简体   繁体   English

OpenGL Cubemap FrameBuffer深度比较

[英]OpenGL Cubemap FrameBuffer Depth Comparison

I am trying to implement shadow maps for point lights. 我正在尝试为点光源实现阴影贴图。 Basically I'm creating a framebuffer and then render all shadow casters on each side of a cubemap texture (which is 6 times) and then read it in the regular rendering pass and determine which pixel is in shadow. 基本上,我先创建一个帧缓冲区,然后在立方体贴图纹理的每一面渲染所有阴影投射器(是6次),然后在常规渲染过程中读取它,并确定阴影中有哪个像素。 I have several questions: 我有几个问题:

  1. Why do I have to include a color attachment in addition to a depth component in order for my cubemap to get anything rendered to? 为什么要在立方体贴图上渲染任何内容,除了深度组件外,还必须包括颜色附件? I tried it without the color attachment and it did not work. 我尝试了没有颜色附件的情况,但无法正常工作。

  2. After adding the color attachment, I can see my shadow casters in the cubemap but it seems the shadow comparison is wrong. 添加颜色附件后,我可以在立方体贴图中看到我的阴影脚轮,但是阴影比较似乎是错误的。 I am suspecting that one is in NDC while the other isn't. 我怀疑其中一个在NDC中,而另一个不在。

Here's how I initialize my framebuffer containing the shadow cubemap: 这是初始化包含阴影立方体贴图的帧缓冲区的方法:

// Create the depth buffer
    glGenTextures(1, &mDepthTextureID);
    glBindTexture(GL_TEXTURE_2D, mDepthTextureID);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glBindTexture(GL_TEXTURE_2D, 0);

    //Create the cubemap texture
    glGenTextures(1, &mCubemapTextureID);
    glBindTexture(GL_TEXTURE_CUBE_MAP, mCubemapTextureID);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    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);
    for (GLuint i = 0; i < 6; ++i)
    {
        glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_R32F, width, height, 0, GL_RED, GL_FLOAT, 0);
    }
    glBindTexture(GL_TEXTURE_CUBE_MAP, 0);

    //Create the framebuffer and attach the cubemap texture to it
    glGenFramebuffers(1, &mFrameBufferObjectID);
    glBindFramebuffer(GL_FRAMEBUFFER, mFrameBufferObjectID);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, mDepthTextureID, 0);

    //Disable writes to the color buffer
    glDrawBuffer(GL_NONE);
    //Disable reads from the color buffer
    glReadBuffer(GL_NONE);

    GLenum Status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
    if (Status != GL_FRAMEBUFFER_COMPLETE)
    {
        switch(Status)
        {
            case GL_FRAMEBUFFER_UNSUPPORTED:
                printf("FrameBuffer unsupported error");
                return false;
                break;
            case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
                printf("FrameBuffer incomplete attachement");
                return false;
                break;
            default:
                printf("GLShadowCubemap error, status: 0x%x\n", Status);
                return false;
        }
    }

    //Unbind this
    glBindFramebuffer(GL_FRAMEBUFFER, 0);

Here's my shadow's vertex shader: (Only the Position attribute is used) 这是我的阴影的顶点着色器:(仅使用Position属性)

#version 330 core

layout (location = 0) in vec3 Position;
layout (location = 1) in vec3 Normal;
layout (location = 2) in vec2 TexCoord;
layout (location = 3) in vec3 Tangent;

uniform mat4 gModelMatrix;
uniform mat4 gModelViewProjectionMatrix;

out vec3 WorldPosition;

/*
 * Below needs a GS and using layered rendering
void main()
{
    gl_Position = gModelMatrix * vec4(Position, 1.0);
}
*/

void main()
{
    vec4 pos4 = vec4(Position, 1.0);
    gl_Position = gModelViewProjectionMatrix * pos4;
    WorldPosition = (gModelMatrix * pos4).xyz;
}

Here's my shadow fragment shader: 这是我的阴影片段着色器:

#version 330 core

in vec3 WorldPosition;

uniform vec3 gLightPosition;

out float Fragment;

void main()
{
    // get distance between fragment and light source
    float dist_to_light = length(WorldPosition - gLightPosition);
    //gl_FragDepth = dist_to_light;
    Fragment = dist_to_light;
}

Additional question here: 此处的其他问题:

I saw that many have said that overriding gl_FragDepth is a bad idea. 我看到很多人都说重写gl_FragDepth是一个坏主意。 I kind of know why but what's strange here is that if I were to override the gl_FragDepth manually, nothing gets written to the cubemap. 我有点知道为什么,但是奇怪的是,如果我要手动覆盖gl_FragDepth,则不会有任何内容写入立方体贴图。 Why? 为什么?

Here's how I render all the regular stuff (the variable i is an index to my lights array) 这是我渲染所有常规内容的方式(变量i是我的lights数组的索引)

    mShadowCubemapFBOs[i].ViewportChange();
    mShadowMapTechnique.SetLightPosition(light.Position);
    const float shadow_aspect = (static_cast<float>(mShadowWidth) / mShadowHeight);
    const mat4 shadow_projection_matrix = glm::perspective(90.f, shadow_aspect, 1.f, mShadowFarPlane);
    const vector<MeshComponent>& meshes = ComponentManager::Instance().GetMeshComponentPool().GetPool();
                for(int layer = 0; layer < 6; ++layer)
                {
                    GLenum cubemap_face = GL_TEXTURE_CUBE_MAP_POSITIVE_X + layer;
                    mShadowCubemapFBOs[i].Bind(cubemap_face);
                    glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
                    for(const MeshComponent& mesh : meshes)
                    {
//the transform_component is referenced ahead of time.
                        const mat4 model_transform = transform_component->GetTransformMatrix();
                                 mShadowMapTechnique.SetModelViewProjectionMatrix(light.Position, cubemap_face, shadow_projection_matrix, model_transform);
                        mShadowMapTechnique.SetModelMatrix(model_transform);
                        mesh.Render();
                    }
                }

Finally here's the regular rendering shader: 最后是常规渲染着色器:

#version 330 core

const int MAX_LIGHTS = 8;
const int LIGHT_TYPE_DIRECTIONAL = 0;
const int LIGHT_TYPE_POINT = 1;
const int LIGHT_TYPE_SPOT = 2;

in vec2 TexCoord0;
in vec3 WorldNormal0;
in vec3 WorldPos0;
in vec3 WorldTangent0;

out vec4 FragmentColor;

struct Material
{
    vec4 Emissive;
    vec4 Ambient;
    vec4 Diffuse;
    vec4 Specular;
    float SpecularPower;
    bool UseTexture;
};

struct Light
{
    vec3 Position;
    vec3 Direction;
    vec4 Color;                 //RGBA
    float SpotAngle;
    float ConstantAttenuation;
    float LinearAttenuation;
    float QuadraticAttenuation;
    int LightType;
    samplerCube ShadowMap;  //Cubemap shadows
    bool Enabled;
};

struct LightingResult
{
    vec4 Diffuse;
    vec4 Specular;
};

uniform Material gMaterial;
uniform Light gLights[MAX_LIGHTS];
uniform sampler2D gTextureSampler0;
uniform sampler2D gNormalMap;
uniform bool gEnableNormalMap;
uniform vec3 gEyeWorldPos;

float CalculateShadowFactor(vec3 frag_pos, Light light)
{
    vec3 fragment_to_light = frag_pos - light.Position;
    float sample_distance = texture(light.ShadowMap, fragment_to_light).r;
    float distance = length(fragment_to_light);
    if (distance < sample_distance + 0.001)
    {
        return 1.0; // Inside the light
    }
    else
    {
        return 0.5; // Inside the shadow
    }
}

//L - Light direction vector from pixel to light source
//N - Normal at the pixel
vec4 CalculateDiffuse(Light light, vec3 L, vec3 N)
{
    float n_dot_l = max(0, dot(N, L));
    return light.Color * n_dot_l;
}

//V - View vector
//L - Light direction vector from pixel to light source
//N - Normal at the pixel
vec4 CalculateSpecular(Light light, vec3 V, vec3 L, vec3 N)
{
    //Phong lighting
    vec3 R = normalize(reflect(-L, N));
    float r_dot_v = max(0, dot(R, V));
    return light.Color * pow(r_dot_v, max(0.4, gMaterial.SpecularPower));
}

float CalculateAttenuation(Light light, float distance)
{
    return 1.0 / (light.ConstantAttenuation + light.LinearAttenuation * distance + light.QuadraticAttenuation * distance * distance);
}

//V - View vector
//P - Position of pixel
//N - Normal of pixel
LightingResult CalculatePointLight(Light light, vec3 V, vec3 P, vec3 N)
{
    LightingResult result;
    result.Diffuse = vec4(0.0, 0.0, 0.0, 1.0);
    result.Specular = vec4(0.0, 0.0, 0.0, 1.0);

    vec3 L = light.Position - P;
    float distance = length(L);
    L = normalize(L);

    float attenuation = CalculateAttenuation( light, distance );
    result.Diffuse = CalculateDiffuse(light, L, N) * attenuation;
    result.Specular = CalculateSpecular(light, V, L, N) * attenuation;

    return result;
}

//V - View vector
//P - Position of pixel
//N - Normal of pixel
LightingResult CalculateDirectionalLight(Light light, vec3 V, vec3 P, vec3 N)
{
    LightingResult result;
    result.Diffuse = vec4(0.0, 0.0, 0.0, 1.0);
    result.Specular = vec4(0.0, 0.0, 0.0, 1.0);

    vec3 L = -light.Direction;

    result.Diffuse = CalculateDiffuse(light, L, N);
    result.Specular = CalculateSpecular(light, V, L, N);

    return result;
}

//L - Light vector
//Smoothness increases as angle gets larger
float CalculateSpotCone(Light light, vec3 L)
{
    //cos are in radians
    float min_cos = cos(light.SpotAngle);
    float max_cos = (min_cos + 1.0f) / 2.0f;
    float cos_angle = dot(light.Direction, -L); //negated L such that as we move towards the edge, intensity decreases
    return smoothstep(min_cos, max_cos, cos_angle);
}

//V - View vector
//P - Position of pixel
//N - Normal of pixel
LightingResult CalculateSpotLight(Light light, vec3 V, vec3 P, vec3 N)
{
    LightingResult result;
    result.Diffuse = vec4(0.0, 0.0, 0.0, 1.0);
    result.Specular = vec4(0.0, 0.0, 0.0, 1.0);

    vec3 L = light.Position - P;
    float distance = length(L);
    L = normalize(L);

    float attenuation = CalculateAttenuation(light, distance);
    float spot_intensity = CalculateSpotCone(light, L);
    result.Diffuse = CalculateDiffuse(light, L, N) * attenuation * spot_intensity;
    result.Specular = CalculateSpecular(light, V, L, N) * attenuation * spot_intensity;

    return result;
}

//P - Position of pixel
//N - Normal of pixel
LightingResult CalculateLighting(vec3 P, vec3 N)
{
    vec3 V = normalize(gEyeWorldPos - P);

    LightingResult total_result;
    total_result.Diffuse = vec4(0, 0, 0, 1.0);
    total_result.Specular = vec4(0, 0, 0, 1.0);

    for(int i = 0; i < MAX_LIGHTS; ++i)
    {
        if(!gLights[i].Enabled)
        {
            continue;
        }

        LightingResult result;
        result.Diffuse = vec4(0, 0, 0, 1.0);
        result.Specular = vec4(0, 0, 0, 1.0);
        float shadow_factor = 1.0;

        switch(gLights[i].LightType)
        {
            case LIGHT_TYPE_DIRECTIONAL:
                result = CalculateDirectionalLight(gLights[i], V, P, N);
                break;
            case LIGHT_TYPE_POINT:
                result = CalculatePointLight(gLights[i], V, P, N);
                shadow_factor = CalculateShadowFactor(P, gLights[i]);
                break;
            case LIGHT_TYPE_SPOT:
                result = CalculateSpotLight(gLights[i], V, P, N);
                shadow_factor = CalculateShadowFactor(P, gLights[i]);
                break;
        }
        total_result.Diffuse += (result.Diffuse * shadow_factor);
        total_result.Specular += (result.Specular * shadow_factor);
    }

    total_result.Diffuse = clamp(total_result.Diffuse, 0, 1);
    total_result.Specular = clamp(total_result.Specular, 0, 1);

    return total_result;
}

vec3 CalculateNormalMapNormal()
{
    vec3 normal = normalize(WorldNormal0);
    vec3 tangent = normalize(WorldTangent0);
    tangent = normalize(tangent - dot(tangent, normal) * normal);   //remove components from the normal vector. This is needed for non-uniform scaling
    vec3 bi_tangent = cross(tangent, normal);
    vec3 bump_map = texture(gNormalMap, TexCoord0).xyz;
    bump_map = 2.0 * bump_map - vec3(1.0, 1.0, 1.0);    //Remaps the values
    mat3 TBN = mat3(tangent, bi_tangent, normal);
    vec3 actual_normal = TBN * bump_map;
    return normalize(actual_normal);
}

void main()
{
    vec3 pixel_normal = normalize(WorldNormal0);
    vec4 texture_color = vec4(0, 0, 0, 1);

    if(gMaterial.UseTexture)
    {
        texture_color = texture( gTextureSampler0, TexCoord0 );
    }

    if(gEnableNormalMap)
    {
        pixel_normal = CalculateNormalMapNormal();
    }

    LightingResult light_result = CalculateLighting(WorldPos0, pixel_normal);
    vec4 diffuse_color = gMaterial.Diffuse * light_result.Diffuse;
    vec4 specular_color = gMaterial.Specular * light_result.Specular;

    FragmentColor = (gMaterial.Emissive + gMaterial.Ambient + diffuse_color + specular_color) * texture_color;
    //FragmentColor = texture_color;

    //temp test
    //vec3 fragment_to_light = WorldPos0 - gLights[1].Position;
    //FragmentColor = vec4(vec3(texture(gLights[1].ShadowMap, fragment_to_light).r / gFarPlane), 1.0);
}

What am I doing wrong? 我究竟做错了什么? I see that I am storing the distance from fragment to light in world space and it is written to a color buffer (not the depth buffer) and so it shouldn't be in NDC. 我看到我将片段到光的距离存储在世界空间中,并将其写入颜色缓冲区(而不是深度缓冲区)中,因此它不应位于NDC中。 Finally when I am comparing it, it's also in world space .... Why are the shadows off? 最后,当我进行比较时,它也在世界空间中……为什么阴影消失了? It appears as if the shadows are way larger than they should be so the entire scene is covered with shadow and it appears that what should be the size of shadow is actually covered in light. 看起来好像阴影比实际应有的要大得多,因此整个场景都被阴影覆盖,并且似乎应该用光实际上覆盖了阴影的大小。

Picture of the shadow cubemap: 阴影立方体贴图的图片:

在此处输入图片说明

Picture of the scene (only the helicopter will cast shadow): 场景图片(只有直升机会投射阴影):

在此处输入图片说明

Thanks! 谢谢!

After some debugging, I found out my problems: 经过一些调试后,我发现了我的问题:

  1. glPerspective takes fov as radians, not degrees even though it's documentation says it's only in radians if FORCE_RADIANS is defined (I did not define that) glPerspective将fov表示为弧度,而不是度,即使它的文档说如果定义了FORCE_RADIANS,则仅以弧度表示(我没有定义)

  2. The cubemap for shadow require the clear color to be (FLT_MAX, FLT_MAX, FLT_MAX, 1.0) such that everything is NOT in shadow by default. 阴影的立方体贴图要求透明色为(FLT_MAX,FLT_MAX,FLT_MAX,1.0),这样默认情况下所有内容都不处于阴影中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM