Отложенный конвейер рендеринга, не работающий на графических картах ноутбуков, и странные проблемы с "Model Ghosting" (glBlendFunc)
РЕДАКТИРОВАТЬ: Изменение фрагмента шейдера так, чтобы был возможен только один свет за цикл, решил мою первую проблему. Моя вторая проблема остается в силе.
заранее спасибо за любую помощь, которую вы можете предложить. Я работаю над отложенным конвейером затенения для моего движка рендеринга LWJGL уже пару недель. Хотя я и сумел заставить все работать так, как я ожидал, но после распространения программы среди нескольких знакомых людей у меня начались проблемы. Я постараюсь сделать это как можно короче. Спасибо, что остаетесь со мной.
Я начну с первой из двух проблем в моем названии. На моей машине (AMD Phenom II 965 и Nvidia GTX 480) конечный продукт моего рендерера оказался точно таким, как ожидалось. (Я собирался опубликовать ссылку на изображение, но, поскольку я новый пользователь, я не смог опубликовать более 3 гиперссылок. Но достаточно сказать, что это выглядело так, как и должно быть.)
Это именно то, что я намеревался, так что я, хотя рендер был в порядке. Я отослал это другу (который использовал GT 440), и у них были те же самые результаты.
Вскоре после этого я дал сборку двигателя моему другу, у которого есть ноутбук (с GT 540M). Это было то, что произвел рендерер (игнорируйте счетчик FPS, он не работает):
http://i.imgur.com/DxxFEpy.png
Очевидно, что это совсем не тот результат, которого я ожидал. Я испытывал те же результаты на каждой другой мобильной видеокарте, на которой смог протестировать. После более чем недели, пока я стучал головой по столу, я смог сузить проблему до прохода освещения, где вызывается glBlendFunc. Мой код выглядит следующим образом:
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
List<Float[]>[] listArray = LightData.getInstance().updateLights();
List<Float[]> lightsColor = listArray[1];
List<Float[]> lightsPos = listArray[0];
viewMatrix.setViewMatrix(camera.getTranslation(), camera.getRotation());
glDisable(GL_DEPTH_TEST);
glUseProgram(0);
glCallList(quadList);
FBO.useShader();
FBO.passTextures(); //Just sets the shader uniform values to the correct textures
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_BLEND);
glLoadIdentity();
glBlendFunc(GL_ONE, GL_ONE) ; //using GL_ONE and GL_ONE_MINUS_SRC_ALPHA have the same effect
for (int i = 0; i < lightsPos.size(); i++) {
glClear(GL_DEPTH_BUFFER_BIT);
int uniformLightPosition = glGetUniformLocation(FBO.getShaderID(), "uniformLightPosition");
int uniformLightColor = glGetUniformLocation(FBO.getShaderID(), "uniformLightColor");
int uniformViewMatrix = glGetUniformLocation(FBO.getShaderID(), "uniformViewMatrix");
int uniformAmbient = glGetUniformLocation(FBO.getShaderID(), "uniformAmbient");
glUniform1(uniformLightPosition, Tools.asFloatBuffer(lightsPos.get(i)));
glUniform1(uniformLightColor, Tools.asFloatBuffer(lightsColor.get(i)));
glUniform1f(uniformAmbient, 0.01f);
glUniformMatrix4(uniformViewMatrix, true, viewMatrix.asFloatBuffer());
glCallList(quadList); //is just a display list for a fullscreen quad (using GL_QUADS)
}
glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
Первое, что вы можете заметить, это то, что я рисую квад, а затем очищаю буферы глубины и цвета. Это будет рассмотрено в моем следующем вопросе, хотя я не удивлюсь, если проблема в моем следующем вопросе будет тесно связана с проблемой в этом вопросе. Я почти уверен (99%), что проблема в этом методе, потому что при тестировании с более старой сборкой движка, который поддерживал только один источник света, но все еще использовал отложенный конвейер, я смог получить отличные результаты на каждом компьютере, на котором я тестировал, Еще раз, рендерер работает на каждой настольной видеокарте, которую я тестировал, но не на любой видеокарте ноутбука. Я исключил почти все, кроме этого метода. Возможно, стоит отметить, что мне не удалось использовать текстуру с внутренним форматом, который не GL_RGBA32f
или же GL_RGBA16f
как цель рендеринга. Кто-нибудь видел это раньше, или кто-нибудь может предложить помощь? Я был бы рад, если бы у кого-то была идея, с чего начать искать проблемы на этом этапе, потому что у меня ничего нет. Я был совершенно не в состоянии найти решение самостоятельно.
Затем переходим ко второй проблеме и второму вопросу. В начале последнего блока кода у меня было несколько строк кодов, которые создавали четырехугольник на экране без шейдера:
glDisable(GL_DEPTH_TEST);
glUseProgram(0);
glCallList(quadList);
FBO.useShader();
FBO.passTextures(); //Just sets the shader uniform values to the correct textures
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_BLEND);
Насколько я могу судить, этот код не должен делать абсолютно ничего. Но когда я удаляю рисунок четырехугольника, окно отображает это:
http://i.imgur.com/mkMsP0F.png
Я не знал, как еще это назвать, кроме "призраков", потому что это похоже на призрачное изображение (по крайней мере, так, как мне кажется). Когда я поворачиваю матрицу MV, она искажается в направлении, в котором я вращаюсь, и первый набор источников света (я использую массив из 7) освещает ее, а затем остальные освещают фактическую модель. Я не могу объяснить, почему это происходит, потому что код, который генерирует это изображение, точно такой же, как код выше без glCallList(quadList);
Это означает, что буферы глубины и цвета все еще очищаются прямо перед тем, как я вхожу в цикл. Я не могу объяснить эту проблему вообще. У кого-нибудь есть идея, что не так и как это исправить, или хотя бы идея о том, что не так?
РЕДАКТИРОВАТЬ Я обнаружил, что это происходит только с моделями, которые имеют координаты текстуры. Я не знаю почему.
РЕДАКТИРОВАТЬ Похоже, что когда я ограничиваю количество источников света, разрешенных в каждом шейдере, до 1, двоение изображения становится намного менее заметным, но все еще присутствует, поэтому я предполагаю, что это означает, что один запуск фрагментного шейдера приводит к этим призракам.
Спасибо за ЛЮБУЮ помощь любому из этих двух проблем, это очень ценится. Если у вас есть какие-либо вопросы для меня, просто задавайте, хотя мне может потребоваться некоторое время, чтобы вернуться к вам, я постараюсь вернуться как можно быстрее.
РЕДАКТИРОВАТЬ Сори, я забыл свой код шейдера: вершина GeometryPass:
uniform sampler2D tex;
varying vec3 surfaceNormal;
varying vec3 varyingVertex;
void main() {
vec4 color = vec4(texture2D(tex, gl_TexCoord[1].st));
gl_FragColor = color;
}
Фрагмент GeometryPass:
uniform sampler2D tex;
varying vec3 surfaceNormal;
varying vec3 varyingVertex;
uniform float materialValue;
uniform float specValue;
void main() {
vec4 color = vec4(texture2D(tex, gl_TexCoord[1].st)) ;//vec4(0.25,0.25,0.25,1);
vec4 vertex = vec4(varyingVertex, materialValue);
vec4 normal = vec4(surfaceNormal, specValue);
gl_FragData[0] = color;
gl_FragData[1] = vertex;
gl_FragData[2] = normal;
}
Вершина LightPass Phong:
void main() {
gl_TexCoord[0] = gl_MultiTexCoord0;
gl_Position = gl_ModelViewMatrix * gl_Vertex;
}
Фрагмент фонаря LightPass
uniform sampler2D location;
uniform sampler2D normal;
uniform sampler2D color;
uniform float uniformLightPosition[21];
uniform mat4 uniformViewMatrix;
uniform float uniformLightColor[28];
void main() {
vec4 color = texture2D(color, gl_TexCoord[0].st);
vec4 locationAndMat = texture2D(location, gl_TexCoord[0].st);
vec4 normalAndSpec = texture2D(normal, gl_TexCoord[0].st);
vec3 vertexPosition = locationAndMat.xyz;
vec3 surfaceNormal = normalAndSpec.xyz;
float spec = normalAndSpec.a;
float specA = locationAndMat.a;
vec4 lightColor[7];
int iterator = 0;
for (int i = 0; i<28; i = i+4) {
lightColor[iterator] = vec4(uniformLightColor[i], uniformLightColor[i+1], uniformLightColor[i+2], uniformLightColor[i+3]);
iterator = iterator + 1;
}
vec3 lightPos[7];
iterator = 0;
for (int i = 0; i<21; i = i+3) {
lightPos[iterator] = vec3(uniformLightPosition[i], uniformLightPosition[i+1], uniformLightPosition[i+2]);
lightPos[iterator] = (uniformViewMatrix * vec4(lightPos[iterator],1)).xyz ;
iterator = iterator + 1;
}
vec4 fragData[7];
vec4 endColor;
for (int i = 0; i<7 ; i++) {
if (lightColor[i] != vec4(0,0,0,0) && color != vec4(0,0,0,0)) {
vec3 lightDistance = lightPos[i]-vertexPosition;
float distance = pow((pow(lightDistance.x, 2) + pow(lightDistance.y, 2) + pow(lightDistance.z, 2)), 0.5);
if (distance < lightColor[i].a) {
float att = 1/((-3/800*(lightColor[i].a) + 0.225)*pow(distance, 2));
vec3 lightDirection = normalize(lightDistance);
float diffuseLightIntensity = max(0.0, dot(surfaceNormal, lightDirection));
fragData[i] += (vec4(diffuseLightIntensity,diffuseLightIntensity,diffuseLightIntensity,1));
vec3 reflectionDirection = normalize(reflect(-lightDirection, surfaceNormal));
float specular = max(0.0, dot(reflectionDirection, -normalize(vertexPosition)));
if (diffuseLightIntensity != 0) {
float fspecular = pow(specular, spec);
vec4 fspec = vec4(fspecular*specA, fspecular*specA, fspecular*specA,1);
fragData[i] += fspec;
}
fragData[i] *= lightColor[i];
fragData[i] *= 0.1;
fragData[i].a = 0;
fragData[i] *= att;
endColor += fragData[i];
}
}
}
gl_FragData[0] = endColor * color;
}
1 ответ
Я решил основную проблему! Это было достаточно хорошо для меня. Похоже, проблема, с которой я столкнулся, заключалась в том, что на фрагментный шейдер приходилось много инструкций (из-за цикла for). Когда я настроил шейдер только на один свет, он работал как положено! Я просто использую смешивание, чтобы позаботиться обо всем, как раньше, но больше запускаю шейдер. Недостатком является то, что требуется больше фильтрата, но с другой стороны, он работает на старом оборудовании и ноутбуках.
Я до сих пор не могу понять, что является причиной появления ореолов, но это менее важно для меня, так как у меня плохое решение для этого.