【问题标题】:OpenGL GBuffer normals and depth buffer issuesOpenGL GBuffer 法线和深度缓冲区问题
【发布时间】:2013-09-13 01:22:37
【问题描述】:

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

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

【问题讨论】:

  • 右上角是纹理位置,左下角是反照率。目前我只为球体设置了纹理位置,反照率只是纯色
  • 好吧,我觉得一切正常。实际问题是什么?顺便说一句,如果您想减少必须渲染到的缓冲区数量(节省内存并提高帧速率),您应该将反照率系数写入普通缓冲区的 alpha 通道。并且不要费心将法线/反照率存储在 FP16 中,RGBA8 应该适用于除位置之外的所有内容。
  • 法线总是指向你,因为它们在视图空间中,如果这是你的问题?如果您不费心将它们乘以“worldMatrix”,您总是可以将它们放在世界空间中。但是你需要对它们进行偏置和缩放,因为一些法线会指向负方向。
  • 即使有图片,我仍然没有遇到您实际遇到的问题(撇开正确的图像实际编码的内容不谈)。正常的图像对我来说看起来很合理。
  • 可能“我的光照通道看起来很奇怪的问题”在光照通道中,因为你的 G 通道在每个人看来都很好。请注意您正在使用的空间。您对位置和法线使用相同的矩阵,因此它们都在视图空间中(这很好)。因此,在您的光照通道中,所有内容都必须在视图空间中(例如灯光的位置)。

标签: opengl glsl shader deferred-shading


【解决方案1】:

我知道这有点老了,但我想让你知道,经过大量研究后,我一切正常! 如果有人遇到类似/相同的问题,我只是想在这里发布。

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

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

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

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

【讨论】:

  • 干得好;)。纹理位置确实是不必要的。学习延迟技术的下一步不是存储位置,而是从深度获取它。它节省了带宽,您可以使用该带宽为您的材料获取更多帧或更多信息。
  • 现在就这样做了。现在从深度计算视图空间位置并重写光照模型,使其使用视图空间而不是世界空间。
【解决方案2】:

查看您的解决方案,从您在 cmets 中发布的链接中,我首先想到的是: 在光照通道顶点着色器中,您可以这样做

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;

这样,您的照明中就会有一个自发光组件,其中包含您的光的颜色以及没有它的漫反射和镜面反射。这似乎不是一个好的选择,尤其是在延迟着色中,您可以轻松地在整个帧上执行环境通道。

希望我能帮上忙。太糟糕了,我不使用 glut,我无法构建你的代码。

编辑
要将pointLightPostion(我假设它已经在世界空间中)转换为视图空间,只需这样做

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

【讨论】:

  • 好吧,如果我按照你的建议去做,我就有了视图空间中的法线和位置。那么如何让pointLightPosition查看空间呢?
  • @C0dR 现在,这很基础:你应该学习this stuff。 openGL 完全需要它。将针对您的具体情况使用 sn-p 编辑我的答案。
  • 好的,非常感谢。我会查看您发送的链接,因为它仍然无法正常工作。我想延迟渲染对我来说还为时过早:(
  • 但我发现了如何在世界空间中设置法线。嗯,多亏了你,我比以前明白了很多。无论如何我不能让那个该死的点灯工作......
  • @C0dR 是的,延迟渲染可能很棘手。很抱歉,我现在不能帮助你更多。但首先,我建议你花点时间学习如何避免被弃用的东西(glPushMAtrix 等),并加强你的数学。完成一些教程:不会花费太多时间,而且完全值得。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-31
  • 1970-01-01
  • 1970-01-01
  • 2015-12-13
  • 1970-01-01
相关资源
最近更新 更多