簡體   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