将光源视角的深度贴图应用于摄像机视角的渲染

server/2025/1/11 22:03:29/

将光源视角的深度贴图应用于摄像机视角的渲染是阴影映射(Shadow Mapping)技术的核心步骤之一。这个过程涉及到将摄像机视角下的片段坐标转换到光源视角下,并使用深度贴图来判断这些片段是否处于阴影中。

1. 生成光源视角的深度贴图
首先,我们需要从光源的视角渲染场景,生成一个深度贴图。这个深度贴图记录了从光源到场景中每个可见点的距离(即深度值)。具体步骤如下:

设置光源视角:我们将摄像机位置设置为光源的位置,并将摄像机的方向指向场景。这样,我们可以从光源的视角渲染场景。
创建帧缓冲对象(FBO):为了存储深度信息,我们需要创建一个帧缓冲对象(Framebuffer Object, FBO),并将一个深度纹理附加到该 FBO 上。
渲染深度贴图:在这个视角下,我们只渲染场景的深度信息,而不是颜色信息。每个像素的深度值表示从光源到该像素对应场景点的距离。这些深度值被存储在一个纹理中,这就是所谓的“深度贴图”或“阴影贴图”。

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


2. 计算光源的视图和投影矩阵
为了将摄像机视角下的片段转换到光源视角下,我们需要计算光源的视图矩阵和投影矩阵。这些矩阵用于将世界坐标转换为光源视角下的裁剪空间坐标。

视图矩阵:使用 glm::lookAt 函数来计算光源的视图矩阵。这个函数需要三个参数:光源的位置、目标点(通常是场景的中心)和上方向向量(通常是 (0, 1, 0))。
投影矩阵:根据光源的类型选择合适的投影矩阵。对于平行光(如定向光),通常使用正交投影矩阵(glm::ortho);对于点光源或聚光灯,通常使用透视投影矩阵(glm::perspective)。

glm::mat4 lightView = glm::lookAt(lightPos, glm::vec3(0.0f), glm::vec3(0.0, 1.0, 0.0));
glm::mat4 lightProjection = glm::ortho(-10.0f, 10.0f, -10.0f, 10.0f, near_plane, far_plane);
glm::mat4 lightSpaceMatrix = lightProjection * lightView;


3. 将摄像机视角下的片段转换到光源视角下
在摄像机视角下渲染场景时,我们需要将每个片段的坐标从摄像机视角转换到光源视角。具体步骤如下:

将片段从屏幕空间转换到世界空间:使用摄像机的逆投影矩阵和逆视图矩阵将片段从屏幕空间转换到世界空间。
将片段从世界空间转换到光源视角下的裁剪空间:使用光源的视图矩阵和投影矩阵将世界空间中的坐标转换到光源视角下的裁剪空间。
将片段从裁剪空间转换到归一化设备坐标(NDC):通过透视除法(即将 x, y, z 分量除以 w 分量)将裁剪空间中的坐标转换为 NDC。
将 NDC 转换到纹理坐标系:将 NDC 中的坐标从 [-1, 1] 映射到 [0, 1],以便可以在深度贴图中进行采样。
 

// 在片元着色器中
void main() {// 将片段位置从世界空间转换到光源视角下的裁剪空间vec4 fragPosLightSpace = lightSpaceMatrix * vec4(WorldPos, 1.0);// 执行透视除法,将裁剪空间坐标转换为 NDCvec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;// 将 NDC 坐标从 [-1, 1] 映射到 [0, 1]projCoords = projCoords * 0.5 + 0.5;// 从深度贴图中采样深度值float closestDepth = texture(shadowMap, projCoords.xy).r;float currentDepth = projCoords.z;// 计算阴影因子float shadow = currentDepth > closestDepth ? 1.0 : 0.0;// 应用阴影因子到最终颜色FragColor = mix(color, shadowColor, shadow);
}

处理自阴影问题
当物体自身遮挡自己时,可能会出现自阴影问题。为了避免这种情况,可以在计算阴影时引入一个小偏移量(Bias),以防止物体表面的深度值与深度贴图中的深度值过于接近。偏移量的大小可以根据物体表面的法线方向和光源方向之间的夹角进行调整。

float bias = max(0.05 * (1.0 - dot(normal, lightDir)), 0.005);
float shadow = currentDepth - bias > closestDepth ? 1.0 : 0.0;


http://www.ppmy.cn/server/157587.html

相关文章

景芯SOC设计实战

终身辅导、一对一辅导,手把手教您完成SoC全流程设计,从入门到进阶,带您掌握SoC芯片架构、算法、设计、验证、DFT、后端及低功耗全流程!直播视频不定期升级!让您快速超越同龄人! 景芯团队主打文档服务器实战…

Go语言中的接收器(Receiver)详解

在 Go 语言中,接收器(Receiver) 是指在方法声明中与方法绑定的对象。它是 Go 语言实现面向对象编程(OOP)特性的核心之一。接收器的作用是将方法绑定到某个类型的实例(值或者指针),让…

多租户架构是什么?

多租户架构(Multi-Tenant Architecture)是一种软件架构模型,在这种架构下,一个应用程序可以同时为多个不同的“租户”提供服务,每个租户的数据和设置是隔离的,但都共享同一份软件和硬件资源。每个租户可以视…

《操作系统真象还原》第十二章(一) —— 系统调用

本章节所有代码托管在miniOS 章节任务介绍 任务简介 上一节,我们实现了用户进程的创建,至此,我们成功将操作系统的使用权给用户进行了开放,用户至此可以创建进程然后实现自己的功能,但用户实现的功能最终还是要在操…

macOS安装nvm

新建一个文件夹,使用git将nvm给clone下来 git clone https://github.com/nvm-sh/nvm.git 使用vim编辑~/.bash_profile文件(没有就新建)添加以下代码 export NVM_DIR"$HOME/.nvm" [ -s "$NVM_DIR/nvm.sh" ] &&…

解决Docker冲突问题

错误:docker-ce-cli conflicts with 2:docker-1.13.1-210.git7d71120.el7.centos.x86_64 错误:docker-ce conflicts with 2:docker-1.13.1-210.git7d71120.el7.centos.x86_64 您可以尝试添加 --skip-broken 选项来解决该问题 您可以尝试执行:…

模式识别-Ch2-高斯下判别函数

高斯密度下的判别函数 高斯分布 在给定均值和方差的所有分布中,正态分布的熵最大根据Central Limit Theorem(中心极限定理),大量独立随机变量之和趋近正态分布实际环境中,很多类别的特征分布趋近正态分布 多元正态分布: x [ x …

中学综合素质笔记3

第一章职业理念 第三节 教师观 考情提示: 单选题材料分析题 学习要求: 理解、 识记、 运用 (一)教师职业角色的转变(单选材料分析) 从教师与学生的关系看——对学生 新课程要求教师应该是学生学习的引…