如何正确制作阴影贴图的深度立方体贴图?

时间:2014-01-18 21:21:59

标签: c opengl glsl

我编写的代码将我的场景对象渲染为格式为GL_DEPTH_COMPONENT的立方体贴图纹理,然后在着色器中使用此纹理来确定片段是否被直接点亮,以用于阴影目的。但是,我的立方体贴图看起来像黑色。我想我没有充分设置我的FBO或渲染上下文,但却没有看到遗漏的内容。

在兼容性配置文件中使用GL 3.3。

这是我创建FBO和立方体贴图纹理的代码:

    glGenFramebuffers(1, &fboShadow);
    glGenTextures(1, &texShadow);
    glBindTexture(GL_TEXTURE_CUBE_MAP, texShadow);
    for (int sideId = 0; sideId < 6; sideId++) {
        // Make sure GL knows what this is going to be.
        glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + sideId, 0, GL_DEPTH_COMPONENT, 512, 512, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
    }

    // Don't interpolate depth value sampling. Between occluder and occludee there will
    // be an instant jump in depth value, not a linear transition.
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);

    glBindTexture(GL_TEXTURE_CUBE_MAP, 0);

我的完整渲染功能如下所示:

void render() {

    // --- MAKE DEPTH CUBEMAP ---

    // Set shader program for depth testing
    glUseProgram(progShadow);

    // Get the light for which we want to generate a depth cubemap
    PointLight p = pointLights.at(0);

    // Bind our framebuffer for drawing; clean it up
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboShadow);
    glClear(GL_DEPTH_BUFFER_BIT);

    // Make 1:1-ratio, 90-degree view frustum for a 512x512 texture.
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(90.0, 1, 16.0, 16384.0);
    glViewport(0, 0, 512, 512);
    glMatrixMode(GL_MODELVIEW);

    // Set modelview and projection matrix uniforms
    setShadowUniforms();

    // Need 6 renderpasses to complete each side of the cubemap
    for (int sideId = 0; sideId < 6; sideId++) {
        // Attach depth attachment of current framebuffer to level 0 of currently relevant target of texShadow cubemap texture.
        glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_CUBE_MAP_POSITIVE_X + sideId, texShadow, 0);

        // All is fine.
        GLenum status = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
        if (status != GL_FRAMEBUFFER_COMPLETE) {
            std::cout << "Shadow FBO is broken with code " << status << std::endl;
        }

        // Push modelview matrix stack because we need to rotate and move camera every time
        glPushMatrix();

        // This does a switch-case with glRotatefs
        rotateCameraForSide(GL_TEXTURE_CUBE_MAP_POSITIVE_X + sideId);

        // Render from light's position.
        glTranslatef(-p.getX(), -p.getY(), -p.getZ());

        // Render all objects.
        for (ObjectList::iterator it = objectList.begin(); it != objectList.end(); it++) {
            (*it)->render();
        }

        glPopMatrix();
    }


    // --- RENDER SCENE ---

    // Bind default framebuffer
    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    // Setup proper projection matrix with 70 degree vertical FOV and ratio according to window frame dimensions.
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(70.0, ((float)vpWidth) / ((float)vpHeight), 16.0, 16384.0);
    glViewport(0, 0, vpWidth, vpHeight);

    glUseProgram(prog);

    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();

    applyCameraPerspective();

    // My PointLight class has both a position (world space) and renderPosition (camera space) Vec3f variable;
    // The lights' renderPositions get transformed with the modelview matrix by this.
    updateLights();

    // And here, among other things, the lights' camera space coordinates go to the shader.
    setUniforms();

    // Render all objects
    for (ObjectList::iterator it = objectList.begin(); it != objectList.end(); it++) {

        // Object texture goes to texture unit 0
        GLuint usedTexture = glTextureList.find((*it)->getTextureName())->second;
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, usedTexture);
        glUniform1i(textureLoc, 0);

        // Cubemap goes to texture unit 1
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_CUBE_MAP, texShadow);
        glUniform1i(shadowLoc, 1);

        (*it)->render();
    }

    glPopMatrix();
    frameCount++;
}

渲染深度值的着色器程序(“progShadow”)很简单。

顶点着色器:

#version 330

in vec3 position;

uniform mat4 modelViewMatrix, projectionMatrix;

void main() {
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1);
}

片段着色器:

#version 330

void main() {
    // OpenGL sets the depth anyway. Nothing to do here.
}

最终渲染的着色器程序(“prog”)有一个片段着色器,如下所示:

#version 330

#define MAX_LIGHTS 8

in vec3 fragPosition;
in vec3 fragNormal;
in vec2 fragTexCoordinates;
out vec4 fragColor;

uniform sampler2D colorTexture;
uniform samplerCubeShadow shadowCube;

uniform uint activeLightCount;

struct Light {
    vec3 position;
    vec3 diffuse;
    float cAtt;
    float lAtt;
    float qAtt;
};

// Index 0 to (activeLightCount - 1) need to be the active lights.
uniform Light lights[MAX_LIGHTS];

void main() {
    vec3 lightColor = vec3(0, 0, 0);
    vec3 normalFragmentToLight[MAX_LIGHTS];
    float distFragmentToLight[MAX_LIGHTS];
    float distEyeToFragment = length(fragPosition);

    // Accumulate all light in "lightColor" variable
    for (uint i = uint(0); i < activeLightCount; i++) {
        normalFragmentToLight[i] = normalize(lights[i].position - fragPosition);
        distFragmentToLight[i] = distance(fragPosition, lights[i].position);
        float attenuation = (lights[i].cAtt
            + lights[i].lAtt * distFragmentToLight[i]
            + lights[i].qAtt * pow(distFragmentToLight[i], 2.0));

        float dotProduct = dot(fragNormal, normalFragmentToLight[i]);
        lightColor += lights[i].diffuse * max(dotProduct, 0.0) / attenuation;
    }

    // Shadow mapping only for light at index 0 for now.
    float distOccluderToLight = texture(shadowCube, vec4(normalFragmentToLight[0], 1));

    // My geometries use inches as units, hence a large bias of 1
    bool isLit = (distOccluderToLight + 1) < distFragmentToLight[0];
    fragColor = texture2D(colorTexture, fragTexCoordinates) * vec4(lightColor, 1.0f) * int(isLit);
}

我已经确认所有统一的位置变量都设置为正确的值(即不是 -1 )。

值得注意的是,在链接之前我没有调用glBindFragDataLocation()“progShadow”,因为该着色器不应该写入任何颜色值。

在这里看到任何明显错误的东西?

1 个答案:

答案 0 :(得分:3)

对于阴影贴图,深度缓冲内部格式非常重要(太小而且看起来很糟糕,太大而你占用内存带宽)。你应该使用一个大小的格式(例如GL_DEPTH_COMPONENT24)来保证一定的大小,否则实现将选择它想要的任何东西。至于调试立方体贴图阴影贴图,最简单的做法是将场景绘制到每个立方体面并输出颜色而不是深度。然后,当前您尝试使用立方体贴图来采样深度,请将采样颜色写入fragColor。您可以通过这种方式立即排除查看问题。

然而,还有一个更为严重的问题。您使用的是samplerCubeShadow,但尚未为立方体贴图设置GL_TEXTURE_COMPARE_MODE。尝试使用此采样器类型并且没有GL_TEXTURE_COMPARE_MODE = GL_COMPARE_REF_TO_TEXTURE从深度纹理进行采样将产生未定义的结果。即使您确实正确设置了此模式,纹理坐标的第4个分量也会用作深度比较参考 - 1.0 的常量值 NOT 你想要什么。

同样,深度缓冲区不存储线性距离,您无法直接比较此处计算的距离:

distFragmentToLight[i] = distance(fragPosition, lights[i].position);

相反,这样的事情是必要的:

float VectorToDepth (vec3 Vec)
{
    vec3 AbsVec = abs(Vec);
    float LocalZcomp = max(AbsVec.x, max(AbsVec.y, AbsVec.z));

    // Replace f and n with the far and near plane values you used when
    //   you drew your cube map.
    const float f = 2048.0;
    const float n = 1.0;

    float NormZComp = (f+n) / (f-n) - (2*f*n)/(f-n)/LocalZcomp;
    return (NormZComp + 1.0) * 0.5;
}

float LightDepth    = VectorToDepth (fragPosition - lights [i].position);
float depth_compare = texture(shadowCube,vec4(normalFragmentToLight[0],LightDepth));

*借用Omnidirectional shadow mapping with depth cubemap

float VectorToDepth (vec3 Vec)代码

现在depth_compare将是 0.0 (完全在阴影中)和 1.0 (完全没有阴影)之间的值。如果启用了线性纹理过滤,硬件将在4个点采样深度,并可能为您提供2x2 PCF过滤的形式。如果您有最近的纹理过滤,那么它将 1.0 0.0

相关问题