OpenGL GBuffer法线和深度缓冲问题

时间:2013-09-13 01:22:37

标签: opengl glsl shader deferred-shading

我终于让我的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);

这里是Geometry Pass:

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);
}

如果有必要,我还可以发布sencil通行证和/或所有其他处理。

2 个答案:

答案 0 :(得分:5)

我知道这有点老了但我想让你知道经过多次研究后我得到了一切! 我只想在这里发帖,如果有人遇到类似/相同的问题。

实际上位置图是完全错误的(我的照明模型也是如此,但那是另一个故事:))。正如您已经说过的,我正在使用固定管道功能(这真的很愚蠢)。 实际的失败是我得到了“模型矩阵”

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

实际上是modelmatrix *固定管道中的viewmatrix。所以我将所有内容都改为自定义模型计算,瞧,位置图是正确的。 (您也可以使用固定功能管道并设置具有匹配值的自定义模型矩阵,并将其作为模型矩阵输入到着色器中,但这只是脏的)。 多亏了你,我得到了正确的方法,并设法理解矩阵计算和着色器等更多!

顺便说一下:gbuffer现在看起来像这样(纹理位置实际上是不必要的:)): enter image description here

答案 1 :(得分: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+)技术,它不能很好地运行古老的硬件,我认为你使用了this tutorial形式的东西,它也是现代的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;

并确保pointLightPostion与您决定的空间相同,通过在应用程序中转换它,在CPU端,然后将其传递给已经转换的openGL。

另外,我不明白你为什么这么做

lightColor += diffuse + specular;

不是

lightColor *= diffuse + specular;

通过这种方式,您的灯光中会有一个发光组件,其中包含灯光的颜色以及没有它的漫反射和镜面反射。它似乎不是一个不错的选择,特别是在延迟着色中,您可以轻松地在整个帧上执行环境传递。

希望我能帮到你。太糟糕了,我不使用过剩,我无法建立你的代码。

修改
要将pointLightPostion(我假设已经在世界空间中)转换为视图空间,只需执行

pointLightPostion = (ViewMatrix * glm::vec4(pointLightPostion,1.0f)).xyz;