繁体   English   中英

OpenGL Cubemap FrameBuffer深度比较

[英]OpenGL Cubemap FrameBuffer Depth Comparison

我正在尝试为点光源实现阴影贴图。 基本上,我先创建一个帧缓冲区,然后在立方体贴图纹理的每一面渲染所有阴影投射器(是6次),然后在常规渲染过程中读取它,并确定阴影中有哪个像素。 我有几个问题:

  1. 为什么要在立方体贴图上渲染任何内容,除了深度组件外,还必须包括颜色附件? 我尝试了没有颜色附件的情况,但无法正常工作。

  2. 添加颜色附件后,我可以在立方体贴图中看到我的阴影脚轮,但是阴影比较似乎是错误的。 我怀疑其中一个在NDC中,而另一个不在。

这是初始化包含阴影立方体贴图的帧缓冲区的方法:

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

这是我的阴影的顶点着色器:(仅使用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;
}

这是我的阴影片段着色器:

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

此处的其他问题:

我看到很多人都说重写gl_FragDepth是一个坏主意。 我有点知道为什么,但是奇怪的是,如果我要手动覆盖gl_FragDepth,则不会有任何内容写入立方体贴图。 为什么?

这是我渲染所有常规内容的方式(变量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();
                    }
                }

最后是常规渲染着色器:

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

我究竟做错了什么? 我看到我将片段到光的距离存储在世界空间中,并将其写入颜色缓冲区(而不是深度缓冲区)中,因此它不应位于NDC中。 最后,当我进行比较时,它也在世界空间中……为什么阴影消失了? 看起来好像阴影比实际应有的要大得多,因此整个场景都被阴影覆盖,并且似乎应该用光实际上覆盖了阴影的大小。

阴影立方体贴图的图片:

在此处输入图片说明

场景图片(只有直升机会投射阴影):

在此处输入图片说明

谢谢!

经过一些调试后,我发现了我的问题:

  1. glPerspective将fov表示为弧度,而不是度,即使它的文档说如果定义了FORCE_RADIANS,则仅以弧度表示(我没有定义)

  2. 阴影的立方体贴图要求透明色为(FLT_MAX,FLT_MAX,FLT_MAX,1.0),这样默认情况下所有内容都不处于阴影中。

暂无
暂无

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

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