如何正确制作用于阴影贴图的深度立方体贴图? [英] How to correctly make a depth cubemap for shadow mapping?

查看:211
本文介绍了如何正确制作用于阴影贴图的深度立方体贴图?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经编写了代码,将场景对象渲染为格式为GL_DEPTH_COMPONENT的立方体贴图纹理,然后在着色器中使用此纹理来确定片段是否被直接照明,以进行阴影处理.但是,我的立方体贴图似乎是黑色的.我想我没有设置足够的FBO或渲染上下文,但是看不到缺少的东西.

I have written code to render my scene objects to a cubemap texture of format GL_DEPTH_COMPONENT and then use this texture in a shader to determine whether a fragment is being directly lit or not, for shadowing purposes. However, my cubemap appears to come out as black. I suppose I am not setting up my FBO or rendering context sufficiently, but fail to see what is missing.

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

Using GL 3.3 in compatibility profile.

    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")的着色器程序很简单.

The shader program for rendering depth values ("progShadow") is simple.

#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 ).

I have verified that all uniform location variables are set to a proper value (i.e. not -1).

可能值得注意的是,在链接它之前,我不对"progShadow"进行任何调用,因为该着色器不应写入任何颜色值.

It might be worth noting I do no call to glBindFragDataLocation() for "progShadow" prior to linking it, because no color value should be written by that shader.

看到这里明显有什么问题吗?

See anything obviously wrong here?

推荐答案

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

For shadow maps, depth buffer internal format is pretty important (too small and things look awful, too large and you eat memory bandwidth). You should use a sized format (e.g. GL_DEPTH_COMPONENT24) to guarantee a certain size, otherwise the implementation will pick whatever it wants. As for debugging a cubemap shadow map, the easiest thing to do is actually to draw the scene into each cube face and output color instead of depth. Then, where you currently try to use the cubemap to sample depth, write the sampled color to fragColor instead. You can rule out view issues immediately this way.

但是,还有另一个更严重的问题.您正在使用samplerCubeShadow,但尚未为多维数据集映射设置GL_TEXTURE_COMPARE_MODE.尝试从具有这种采样器类型且不具有GL_TEXTURE_COMPARE_MODE = GL_COMPARE_REF_TO_TEXTURE的深度纹理采样将产生不确定的结果.即使您已正确设置此模式,纹理坐标的第4个分量也将用作深度比较参考- 1.0 的恒定值为 NOT 您想要的.

There is another much more serious issue, however. You are using samplerCubeShadow, but you have not set GL_TEXTURE_COMPARE_MODE for your cube map. Attempting to sample from a depth texture with this sampler type and without GL_TEXTURE_COMPARE_MODE = GL_COMPARE_REF_TO_TEXTURE will produce undefined results. Even if you did have this mode set properly, the 4th component of the texture coordinates are used as the depth comparison reference -- a constant value of 1.0 is NOT what you want.

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

Likewise, the depth buffer does not store linear distance, you cannot directly compare the distance you computed here:

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

* float VectorToDepth (vec3 Vec)的代码来自>具有深度立方体贴图的全向阴影贴图

* Code for float VectorToDepth (vec3 Vec)borrowed from Omnidirectional shadow mapping with depth cubemap

现在depth_compare的值将介于 0.0 (完全在阴影中)和 1.0 (完全在阴影中)之间.如果启用了线性纹理过滤,则硬件将在4个点采样深度,并可能会提供2x2 PCF过滤的形式.如果您使用的是最接近的纹理过滤,则该过滤将为 1.0 0.0 .

Now depth_compare will be a value between 0.0 (completely in shadow) and 1.0 (completely out of shadow). If you have linear texture filtering enabled, the hardware will sample the depth at 4 points and may give you a form of 2x2 PCF filtering. If you have nearest texture filtering, then it will either be 1.0 or 0.0.

这篇关于如何正确制作用于阴影贴图的深度立方体贴图?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆