我想爲點光源實現陰影貼圖。基本上我創建了一個幀緩衝區,然後渲染立方體貼圖紋理(這是6次)的每一側的所有陰影腳輪,然後在正常渲染過程中讀取它並確定哪個像素在陰影中。我有幾個問題:OpenGL立方體貼圖幀緩衝區深度比較
爲什麼我必須在深度組件之外還包含一個顏色附件才能讓我的立方體貼圖得到任何東西?我嘗試了沒有顏色附件,它沒有工作。
添加顏色附件後,我可以在立方體貼圖中看到我的影子腳輪,但似乎影子比較是錯誤的。我懷疑那個人在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);
這裏是我的影子的頂點着色器:(僅使用位置屬性)
#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是一個索引到我的燈陣列)
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中。最後,當我比較它,它也在世界空間....爲什麼陰影關閉?看起來好像陰影比它們應該大得多,所以整個場景都被陰影覆蓋,看起來應該是陰影的大小實際上被遮住了。
圖片陰影立方體貼圖的:現場的
圖片(僅直升機將投下陰影):
謝謝!