阴影映射总结

7 篇文章 1 订阅
2 篇文章 0 订阅

阴影映射技术,也就是在场景中产生物体阴影的技术。增加场景的真实感,和空间感。

主要思路:
首先,我们以光的位置作为视角进行场景的渲染,由于在程序中设置了深度缓冲,所以被遮挡的物体,或者物体的一部分不会被看到,也就是说看不到的地方,就是场景中物体的阴影。我们可以使用深度缓冲技术,获得每个fragment的深度值(一般为0到1之间),阴影部分的深度值肯定比对应位置的遮挡物的深度值大。获得的同时,把深度值信息存储到纹理中,也就是阴影贴图。

题外1:深度缓冲记录的是每个像素的深度值,当两个物体相互遮掩,深度值记录的是没被遮掩的像素的深度值。

其次,在程序运行时,判断一个fragment的深度值(在T空间中的z值),与阴影贴图相应的像素位置进行比较,如果大于该相应位置的深度值,说明在阴影中,可以把该像素点的值设置为黑色。如下图所示。
这里写图片描述

我们先得使用T(下面有介绍)把P变换到光源的坐标空间里。既然点P是从光的透视图中看到的,它的z坐标就对应于它的深度,例子中这个值是0.9。使用点P在光源的坐标空间的坐标,我们可以索引深度贴图,来获得从光的视角中最近的可见深度,结果是点C,最近的深度是0.4。因为索引深度贴图的结果是一个小于点P的深度,我们可以断定P被挡住了,它在阴影中了。

具体实现细节
首先创建一个帧缓存空间,并且创建一个纹理指向这个缓存空间。这样这个空间的数据,既可以当作缓存的数据,也可以当作纹理贴图的数据。

//创建一个帧缓存对象
GLuint depthMapFBO;
glGenFramebuffers(1, &depthMapFBO);
//创建一个纹理对象
const GLuint SHADOW_WIDTH = 1024, SHADOW_HEIGHT = 1024; 
GLuint depthMap; 
glGenTextures(1, &depthMap); 
glBindTexture(GL_TEXTURE_2D, depthMap); 
glTexImage2D(GL_TEXTURE_2D,0,GL_DEPTH_COMPONENT,SHADOW_WIDTH,SHADOW_HEIGHT,0,GL_DEPTH_COMPONENT,GL_FLOAT,NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
//将纹理与帧缓存联系在一起
glBindFramebuffer(GL_FRAMEBUFFER,depthMapFBO);
glFramebufferTexture2D(GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,GL_TEXTURE_2D,depthMap, 0);
glDrawBuffer(GL_NONE); 
glReadBuffer(GL_NONE); 
glBindFramebuffer(GL_FRAMEBUFFER, 0);

然后是以光源位置作为视角渲染场景,需要用到将世界坐标系转化为光源空间坐标系,这里需要生成一个转变矩阵T。具体如下。

GLfloat near_plane = 1.0f, far_plane = 7.5f; 
glm::mat4 lightProjection = glm::ortho(-10.0f, 10.0f, -10.0f, 10.0f, near_plane, far_plane);
glm::mat4 lightView = glm::lookAt(glm::vec(-2.0f, 4.0f, -1.0f), glm::vec3(0.0f), glm::vec3(1.0));
glm::mat4 lightSpaceMatrix = lightProjection * lightView;

然后渲染场景。即获得场景中的深度信息,生成深度贴图。

glViewport(0,0,SHADOW_WIDTH,SHADOW_HEIGHT);
glBindFramebuffer(GL_FRAMEBUFFER,depthMapFBO);
glClear(GL_DEPTH_BUFFER_BIT); 
.....
//创建T矩阵,并将T矩阵传递到着色器中
.....

.....
//绘制场景
..... 
glBindFramebuffer(GL_FRAMEBUFFER, 0);

仅仅用来生成深度贴图的着色器,并不需要太多设置,仅仅获取物体坐标,并将物体坐标从世界坐标转化为T坐标。
顶点着色器。

#version 330 core 
layout (location = 0) in vec3 position; 
uniform mat4 lightSpaceMatrix; 
uniform mat4 model; 
void main() 
{ 
    gl_Position = lightSpaceMatrix * model * vec4(position, 1.0f); 
}

片段着色器。

#version 330 core 
void main() 
{  
    gl_FragDepth = gl_FragCoord.z; 
}

接下来就是渲染阴影了。为每个片段着色时,需要先将该片段转化为T空间上的坐标,将T空间坐标的z值与深度贴图相应像素的值做比较,如果大于深度贴图的值,说明该片段在阴影中。则在片段着色器中可以渲染成黑色。
顶点着色器。

#version 330 core 
layout (location = 0) in vec3 position; 
layout (location = 1) in vec3 normal; 
layout (location = 2) in vec2 texCoords; 
out vec2 TexCoords; 
out VS_OUT { 
vec3 FragPos; 
vec3 Normal; 
vec2 TexCoords; 
vec4 FragPosLightSpace; 
} vs_out; 
uniform mat4 projection;
uniform mat4 view; 
uniform mat4 model; 
uniform mat4 lightSpaceMatrix; 
void main() 
{ 
gl_Position = projection * view * model * vec4(position, 1.0f); vs_out.FragPos = vec3(model * vec4(position, 1.0)); 
vs_out.Normal = transpose(inverse(mat3(model))) * normal; 
vs_out.TexCoords = texCoords; 
vs_out.FragPosLightSpace = lightSpaceMatrix * vec4(vs_out.FragPos, 1.0); 
}

片段着色器。

#version 330 core 
out vec4 FragColor; 
in VS_OUT { 
vec3 FragPos; 
vec3 Normal; 
vec2 TexCoords; 
vec4 FragPosLightSpace; 
} fs_in; 
uniform sampler2D diffuseTexture; 
uniform sampler2D shadowMap; 
uniform vec3 lightPos;
uniform vec3 viewPos; 
float ShadowCalculation(vec4 fragPosLightSpace) 
{ 
// 执行透视除法 
vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; 
// 变换到[0,1]的范围 
projCoords = projCoords * 0.5 + 0.5; 
// 取得最近点的深度(使用[0,1]范围下的fragPosLight当坐标) 
float closestDepth = texture(shadowMap, projCoords.xy).r; 
// 取得当前片元在光源视角下的深度 
float currentDepth = projCoords.z; 
// 检查当前片元是否在阴影中 
float shadow = currentDepth > closestDepth ? 1.0 : 0.0; 
return shadow;
} 
void main() 
{ 
vec3 color = texture(diffuseTexture, fs_in.TexCoords).rgb; 
vec3 normal = normalize(fs_in.Normal); 
vec3 lightColor = vec3(1.0); 
// Ambient 
vec3 ambient = 0.15 * color; 
// Diffuse 
vec3 lightDir = normalize(lightPos - fs_in.FragPos); 
float diff = max(dot(lightDir, normal), 0.0); 
vec3 diffuse = diff * lightColor; 
// Specular 
vec3 viewDir = normalize(viewPos - fs_in.FragPos); 
vec3 reflectDir = reflect(-lightDir, normal); 
float spec = 0.0; 
vec3 halfwayDir = normalize(lightDir + viewDir); 
spec = pow(max(dot(normal, halfwayDir), 0.0), 64.0); 
vec3 specular = spec * lightColor; 
// 计算阴影 
float shadow = ShadowCalculation(fs_in.FragPosLightSpace); 
vec3 lighting = (ambient + (1.0 - shadow) * (diffuse + specular)) * color; FragColor = vec4(lighting, 1.0f); 
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
计算机图形学阴影技术的发展历程可以总结为以下几个阶段[^1]: 1. 平面阴影技术:早期的计算机图形学阴影技术主要是基于平面阴影的计算。这种技术通过计算物体与光源之间的相对位置关系,确定物体上的每个像素是否处于阴影中。常见的平面阴影技术包括平面投影和阴影贴图。 2. 体积阴影技术:随着计算机图形学的发展,研究者们开始探索更加真实的阴影效果。体积阴影技术通过模拟光线在物体内部的传播和散射,实现更加逼真的阴影效果。常见的体积阴影技术包括体积光照、体积光线追踪和体积阴影贴图。 3. 实时阴影技术:随着计算机硬件性能的提升,实时阴影技术得到了广泛应用。实时阴影技术要求在实时渲染的情况下计算阴影效果,因此需要高效的算法和优化技术。常见的实时阴影技术包括阴影映射阴影体积贴图和屏幕空间阴影。 4. 光线追踪阴影技术:光线追踪是一种基于物理模型的渲染技术,可以实现高度真实的阴影效果。光线追踪阴影技术通过追踪光线在场景中的传播路径,计算出每个像素的阴影信息。这种技术需要大量的计算资源,因此在实时应用中较少使用,主要用于离线渲染和影视制作等领域。 以上是计算机图形学阴影技术发展的几个阶段。随着计算机硬件和算法的不断进步,我们可以期待未来更加逼真和高效的阴影效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值