OpenGL GBuffer法线和深度缓冲问题

3

我终于让我的GBuffer工作了(其实并没有),但现在我遇到了一些奇怪的问题,而且我无法找出原因。

当我将法线纹理绘制到屏幕上时,法线总是向我显示(蓝色始终指向相机)。 我不知道如何正确解释它,所以这里有一些截图:

(我认为这就是我的光照通道看起来很奇怪的原因)

这是我创建GBuffer的方式:

glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);

// generate texture object
glGenTextures(GBUFFER_NUM_TEXTURES, textures);
for (unsigned int i = 0; i < 4; i++)
{
    glBindTexture(GL_TEXTURE_2D, textures[i]);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0, GL_RGBA, GL_FLOAT, 0);
    glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, textures[i], 0);

    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}

// generate depth texture object
glGenTextures(1, &depthStencilTexture);
glBindTexture(GL_TEXTURE_2D, depthStencilTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT,NULL);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthStencilTexture, 0);

// generate output texture object
glGenTextures(1, &outputTexture);
glBindTexture(GL_TEXTURE_2D, outputTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, width, height, 0, GL_RGB, GL_FLOAT, NULL);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT4, GL_TEXTURE_2D, outputTexture, 0);

GLenum Status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
assert(Status == GL_FRAMEBUFFER_COMPLETE);

glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);

这里是几何通道:

glEnable(GL_DEPTH_TEST);
glDepthMask(true);
glCullFace(GL_BACK);

glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);

GLenum DrawBuffers[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3};
glDrawBuffers(GBUFFER_NUM_TEXTURES, DrawBuffers);

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glLoadIdentity();
camera.Render();

geoProgram->Use();

GLfloat mat[16];

glPushMatrix();
glTranslatef(0,0,-20);
glRotatef(rot, 1.0, 0, 0);
glGetFloatv(GL_MODELVIEW_MATRIX,mat);
glUniformMatrix4fv(worldMatrixLocation, 1, GL_FALSE, mat);
glutSolidCube(5);
glPopMatrix();

glPushMatrix();
glTranslatef(0,0,0);
glGetFloatv(GL_MODELVIEW_MATRIX,mat);
glUniformMatrix4fv(worldMatrixLocation, 1, GL_FALSE, mat);
gluSphere(sphere, 3.0, 20, 20);
glPopMatrix();

glDepthMask(false);
glDisable(GL_DEPTH_TEST);

以下是几何通道着色器:

[Vertex]
varying vec3 normal;
varying vec4 position;
uniform mat4 worldMatrix;

void main( void )
{       
    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
    position = worldMatrix * gl_Vertex;
    normal = (worldMatrix * vec4(gl_Normal, 0.0)).xyz;
    gl_TexCoord[0]=gl_MultiTexCoord0;
}


[Fragment]
varying vec3 normal;
varying vec4 position;

void main( void )
{
    gl_FragData[0] = vec4(0.5, 0.5, 0.5, 1);//gl_Color;
    gl_FragData[1] = position;
    gl_FragData[2] = vec4(normalize(normal),0);
    gl_FragData[3] = vec4(gl_TexCoord[0].st, 0, 0);
}

很抱歉问题比较长,包含了代码片段,但是我不知道接下来该怎么做,我检查了其他GBuffer实现,但是找不到错误。

//编辑:

好的,看起来你是对的,问题并不在于GBuffer,而是在于灯光通道。我已经尝试过很多变化,但是无法使其正常工作 :(

以下是灯光通道的代码:

[vs]
void main( void )
{
   gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}

[fs]
uniform sampler2D colorMap;
uniform sampler2D positionMap;
uniform sampler2D normalMap;
uniform sampler2D texcoordMap;

uniform vec2 screenSize;
uniform vec3 pointLightPostion;
uniform vec3 pointLightColor;
uniform float pointLightRadius;


void main( void )
{
    float lightDiffuse = 0.5;
    float lightSpecular = 0.7;
    vec3 lightAttenuation = vec3(1.4, 0.045, 0.00075);

    vec2 TexCoord = gl_FragCoord.xy / screenSize.xy;
    vec3 WorldPos = texture2D(positionMap, TexCoord).xyz;
    vec3 Color = texture(colorMap, TexCoord).xyz;
    vec3 normal = texture(normalMap, TexCoord).xyz;
    normal = normalize(normal);

    vec3 lightVector = WorldPos - pointLightPostion;
    float dist = length(lightVector);
    lightVector = normalize(lightVector);

    float nDotL = max(dot(normal, lightVector), 0.0);
    vec3 halfVector = normalize(lightVector - WorldPos);
    float nDotHV = max(dot(normal, halfVector), 0.0);

    vec3 lightColor = pointLightColor;
    vec3 diffuse =  lightDiffuse * nDotL;
    vec3 specular = lightSpecular * pow(nDotHV, 1.0) * nDotL;
    lightColor += diffuse + specular;

    float attenuation = clamp(1.0 / (lightAttenuation.x + lightAttenuation.y * dist + lightAttenuation.z * dist * dist), 0.0, 1.0);


    gl_FragColor = vec4(vec3(Color * lightColor * attenuation), 1.0);
}

如果需要,我也可以发布扫描的原始图像及其他处理结果。

右上角是纹理位置,左下角是漫反射。目前我只为球体设置了纹理位置,漫反射只是单色的。 - C0dR
2
法线始终指向你,因为它们在视图空间中,如果这是你的问题?如果您不通过“worldMatrix”将它们乘以,则始终可以在世界空间中获取它们。但是,然后您需要对其进行偏置和缩放,因为一些法线将指向负方向。 - Andon M. Coleman
即使有图片,我仍然不知道你实际遇到了什么问题(更不用说正确的图像实际上编码了什么)。对我来说,普通图像看起来相当合理。 - Christian Rau
可能“我的光照通道看起来很奇怪”的问题在于光照通道,因为大家看来你的G通道没什么问题。注意你使用的空间。你同时为位置和法向使用相同的矩阵,所以它们都处于视图空间(这很好)。因此,在你的光照通道中,所有东西都必须处于视图空间中(例如灯光的位置)。 - darius
没帮上什么忙?请向我们展示您的输出和用于计算光照的着色器。否则这只是一场徒劳的追逐 :) - Andon M. Coleman
显示剩余10条评论
2个回答

5

我知道这个问题有点老了,但是经过大量的研究,我终于让所有的东西都能正常工作了! 我想在这里发布一下,如果有人遇到类似或相同的问题。

实际上,位置映射完全错误(我的光照模型也是如此,但那是另一个故事 :))。就像你已经说过的那样,我一直在使用固定管线函数(这真的很愚蠢)。 真正的失败是我使用以下代码获取“modelmatrix”:

glGetFloatv(GL_MODELVIEW_MATRIX,mat);
glUniformMatrix4fv(worldMatrixLocation, 1, GL_FALSE, mat);

实际上,这是固定管线中的模型矩阵*视图矩阵。所以我改变了所有内容,使用自定义模型计算,位置地图就正确了。(当然,你也可以使用固定函数管线并设置具有匹配值的自定义模型矩阵,并将其作为模型矩阵输入到着色器中,但那只是肮脏的)。
感谢您的帮助,我找到了正确的方法,成功理解了矩阵计算和着色器等等!
顺便说一下:gbuffer现在看起来像这样(实际上纹理位置是不必要的): enter image description here

做得好 ;). 纹理位置确实是不必要的。在学习延迟技术的下一步是不存储位置,而是从深度中获取它。这可以节省带宽,您可以获得更多的帧数或材料信息。 - darius
已完成。现在正在计算视图空间位置并重写照明模型,使其使用视图空间而非世界空间。 - C0dR

2

看了你提供的解决方案,从评论中发布的链接来看,首先吸引我眼球的是: 在光照通道的顶点着色器中,你这样做:

viewSpace = gl_ModelViewMatrix * gl_Vertex;

然后在片段着色器中进行以下操作:
vec3 lightPos = pointLightPostion * viewSpace;
vec3 WorldPos = texture2D(positionMap, TexCoord) * viewSpace;
vec3 normal = texture2D(normalMap, TexCoord) * viewSpace;

这完全没有意义。
现在,您正在使用延迟渲染技术,这是一种典型的现代OpenGL(3.2+)技术,不适用于古老的硬件。我认为您使用了来自此教程的内容,该教程也是现代OpenGL,那么为什么要使用glPushMatrix和那种旧的东西呢?很遗憾,我从未学过旧版OpenGL,因此我并不总是确定我正确地理解了您的代码。
顺便说一下,回到几何通道。在顶点着色器中,您执行以下操作:
position = gl_ModelViewMatrix * gl_Vertex;
normal = (modelMatrix * vec4(gl_Normal,0.0)).xyz;

但是你在视图空间中有位置,而在模型空间中有法线。(如果您传递给着色器的modelMatrix确实是模型矩阵,因为从您的截图中看起来法线似乎在视图空间中)。 另外要注意,如果法线不是在视图空间中,而是在模型空间中,则必须对其进行偏置和缩放,normal = 0.5f *(modelMatrix * vec4(gl_Normal,0.0))。xyz +1.0f;。 我只会选

position = gl_ModelViewMatrix * gl_Vertex;
normal = (gl_ModelViewMatrix * vec4(gl_Normal,0.0)).xyz;

记住,重要的是在同一个空间中同时拥有位置和法线。您可以使用世界空间或视图空间,但请坚持您的选择。 在光照通道中,只需执行

vec3 WorldPos = texture2D(positionMap, TexCoord).rgb;
vec3 normal = texture2D(normalMap, TexCoord).rgb;
vec3 lightVector = WorldPos - pointLightPostion;

请确保pointLightPosition与您在应用程序的CPU端进行了转换,并已传递给OpenGL,已经转换到相同的空间。

此外,我不明白您为什么这样做

lightColor += diffuse + specular;

而不是

lightColor *= diffuse + specular;

这样,您的照明中就会有一种发光组件,带有您的灯光颜色和漫反射和镜面反射,没有它的话就没有。这似乎不是一个好的选择,特别是在延迟着色中,在整个框架上可以轻松执行环境通道。
希望我能帮上忙。可惜我不使用glut,不能构建您的代码。
编辑 要将pointLightPosition(我假设已经在世界空间中)转换为视图空间,只需执行
pointLightPostion = (ViewMatrix * glm::vec4(pointLightPostion,1.0f)).xyz;

好的,如果我按照你的建议去做,我会得到视图空间中的法线和位置。那么我该如何把pointLightPosition转换成视图空间呢? - C0dR
1
@C0dR 现在,这很基础:你应该学习这些内容。你在openGL中绝对需要它。我会编辑我的答案并提供适用于你特定情况的代码片段。 - darius
好的,非常感谢。我会查看您发送的链接,因为它仍然无法正常工作。我想对于延迟渲染来说,现在对我来说还为时过早 :( - C0dR
但我发现如何在世界空间中设置法线。嗯,多亏了你,我能够理解比以前更多的内容。无论如何,我就是不能让那该死的点光源工作起来... - C0dR
1
@C0dR 是的,延迟渲染可能有些棘手。很抱歉我现在无法为您提供更多帮助。但首先,我建议您花些时间学习如何避免过时的内容(如glPushMAtrix等),并加强您的数学知识。完成一些教程:这不会花费太多时间,但绝对是值得的。 - darius

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接