[OpenGL] 点光源阴影(万向阴影贴图)

本章节源码 点击此处 文档持续更新

一 为什么采用点透视投影

透视投影:

  • 由于点光源是一个点向四周发散的光线,所以这将导致点光源会以不同的角度到达场景中的不同表面,造成近大远小的效果,所以要采用透视投影矩阵来处理点光源的阴影,透视投影能够正确反映这种随着距离增加而大小和亮度逐渐变化的现象。
  • 使用透视投影来渲染点光源的阴影,可以准确地模拟出光源的真实效果,即光源距离物体越远,投射在地面或物体上的阴影边缘越模糊,产生自然的衰减和透视缩放效果。

二 阴影计算思路

  • 对于点光源的阴影计算,我们计算方式还是和平行光产生的阴影计算方式是相同的,
  • 从光的透视图生成一个深度贴图,基于当前fragment位置来对深度贴图采样,然后用储存的深度值和每个fragment进行对比,看看它是否在阴影中。
  • 但是对于点光源要值考虑的一点是,它是从任何方向都会发散的,那么就需要对整个场景中点光源处的六个方向都进行深度贴图的采样。

三 深度贴图生成

对于点光源的深度贴图,我们需要在上下左右前后六个方向都生成深度贴图

3.1 渲染场景生成

  • 我们可以在CPU端,渲染深度贴图时,进行6个不同方向的渲染,具体思路就是提供分别由视点方向看下前后左右上下6个方向的观察矩阵,并进行6次深度贴图的渲染,这样就会在深度缓冲中生成了不同方向的深度贴图。
  • 但是这种会导致在CPU端进行了多次的渲染调用,这会很消耗CPU的性能。所以
for(int i = 0; i < 6; i++)
{GLuint face = GL_TEXTURE_CUBE_MAP_POSITIVE_X + i;glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, face, depthCubemap, 0);/* 伪代码: 也就是视角矩阵 */BindViewMatrix(lightViewMatrices[i]);RenderScene();  
}

3.2 几何着色器生成

我们可以利用几何着色器的功能,在一次渲染过程中就完成多个方向上的深度立方体贴图。我们生成一个深度缓冲,后续利用这个深度缓冲来进行深度值的对比。

  • 首先我们需要准备立方体深度深度缓冲
  • 正常情况下,我们把立方体贴图纹理的一个面附加到帧缓冲对象上,渲染场景6次,每次将帧缓冲的深度缓冲目标改成不同立方体贴图面。由于我们将使用一个几何着色器,它允许我们把所有面在一个过程渲染,我们可以使用glFramebufferTexture直接把立方体贴图附加成帧缓冲的深度附件
    // 创建一个帧缓冲对象glGenFramebuffers(1,&depthCubeMapFBO);// 创建一个立方体贴图glGenTextures(1,&depthCubeMap);// 绑定纹理 并设置每个方向上纹理格式glBindTexture(GL_TEXTURE_CUBE_MAP, depthCubeMap);for (unsigned int i = 0; i < 6; ++i)glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_DEPTH_COMPONENT,SHADOW_WIDTH, SHADOW_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);// 设置纹理的过滤方式。glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);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);// 将创建的立方体贴图提供给帧缓冲作为深度附件glBindFramebuffer(GL_FRAMEBUFFER,depthCubeMapFBO);// 将纹理附件depthCubeMap作为深度缓冲(GL_DEPTH_ATTACHMENT)绑定到帧缓冲对象上glFramebufferTexture(GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,depthCubeMap,0);// 别禁用了当前帧缓冲对象的颜色绘制和读取功能,使得后续的渲染和像素// 读取操作不涉及任何颜色数据。这在进行深度测试、模板测试、只关注非颜色附件的渲染任务等场景中是合理的// 显示告诉OpenGL不适用颜色进行渲染glDrawBuffer(GL_NONE);glReadBuffer(GL_NONE);if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)qDebug() << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << endl;glBindFramebuffer(GL_FRAMEBUFFER,defaultFramebufferObject());
  • 其次我们在PaintGL中生成立方体深度缓冲
  • perspective的视野参数:设置为90度。90度我们才能保证视野足够大到可以合适地填满立方体贴图的一个面,立方体贴图的所有面都能与其他面在边缘对齐。
    QMatrix4x4 shadowProj;QMatrix4x4 shadowView;float near_plane = 1.0f, far_plane = 25.0f;// 定义一个透视投影 这个透视投影矩阵是深度缓冲中的裁剪空间 并且这个并不会在每个方向上改变,改变的只是观察矩阵shadowProj.perspective(90.0f, (float)SHADOW_WIDTH / (float)SHADOW_HEIGHT, near_plane, far_plane);// 准备6个不同方向的观察矩阵std::vector<QMatrix4x4> shadowTransforms;shadowView.lookAt(lightPos, lightPos + QVector3D( 1.0, 0.0, 0.0), QVector3D(0.0,-1.0, 0.0));shadowTransforms.push_back(shadowProj * shadowView);    shadowView.setToIdentity();shadowView.lookAt(lightPos, lightPos + QVector3D(-1.0, 0.0, 0.0), QVector3D(0.0,-1.0, 0.0));shadowTransforms.push_back(shadowProj * shadowView);    shadowView.setToIdentity();shadowView.lookAt(lightPos, lightPos + QVector3D( 0.0, 1.0, 0.0), QVector3D(0.0, 0.0, 1.0));shadowTransforms.push_back(shadowProj * shadowView);    shadowView.setToIdentity();shadowView.lookAt(lightPos, lightPos + QVector3D( 0.0,-1.0, 0.0), QVector3D(0.0, 0.0,-1.0));shadowTransforms.push_back(shadowProj * shadowView);    shadowView.setToIdentity();shadowView.lookAt(lightPos, lightPos + QVector3D( 0.0, 0.0, 1.0), QVector3D(0.0,-1.0, 0.0));shadowTransforms.push_back(shadowProj * shadowView);    shadowView.setToIdentity();shadowView.lookAt(lightPos, lightPos + QVector3D( 0.0, 0.0,-1.0), QVector3D(0.0,-1.0, 0.0));shadowTransforms.push_back(shadowProj * shadowView);// 设置绘制的视窗大小,因为这个是绘制在缓冲中的,并不是真正的绘制在屏幕上面的,所以我们最好保持它和深度纹理贴图采样的分辨率一样是最好的glViewport(0, 0, SHADOW_WIDTH, SHADOW_HEIGHT);// 将当前帧缓冲区绑定到depthCubeMapFBO上glBindFramebuffer(GL_FRAMEBUFFER, depthCubeMapFBO);// 清除当前缓冲区的信息glClear(GL_DEPTH_BUFFER_BIT);// 绑定深度缓冲的shaderdepthProgramObject.bind();// 将6个观察矩阵传递给GPU端for (unsigned int i = 0; i < 6; ++i){std::string str="shadowMatrices[" + std::to_string(i) + "]";depthProgramObject.setUniformValue(str.c_str(), shadowTransforms[i]);}// 将远平面传递给GPU端depthProgramObject.setUniformValue("far_plane", far_plane);// 将点光源的位置传递给GPU端depthProgramObject.setUniformValue("lightPos", lightPos);// 渲染场景renderScene(&depthProgramObject);// 将帧缓冲区绑定到默认的缓冲区对象上,这样才能让后续的绘制绘制到屏幕上面glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebufferObject());depthProgramObject.release();// 绑定shadershaderProgramObject.bind();// 生成透视投影,这个透视投影矩阵是真正的视角的裁剪空间projection.perspective(m_camera.Zoom,(float)width()/(float)height(),_near,_far);// 获得摄像机的观察视角矩阵view = m_camera.GetViewMatrix();// 设置视窗大小,这里是真实的绘制到屏幕上面,所以要保持和openGL的绘制窗口相同。glViewport(0, 0, width(), height());// 传递数据到GPU端shaderProgramObject.setUniformValue("far_plane", far_plane);shaderProgramObject.setUniformValue("lightPos", lightPos);shaderProgramObject.setUniformValue("shadows", true);shaderProgramObject.setUniformValue("projection", projection);shaderProgramObject.setUniformValue("view", view);shaderProgramObject.setUniformValue("viewPos",m_camera.Position);shaderProgramObject.setUniformValue("depthCubeMap",1);// 绑定纹理单元glActiveTexture(GL_TEXTURE1);// 将纹理绑定到纹理单元上glBindTexture(GL_TEXTURE_CUBE_MAP, depthCubeMap);renderScene(&shaderProgramObject);shaderProgramObject.release();
  • (深度缓冲的)顶点着色器:我们只需要把世界坐标传递过去即可。
#version 330 core
layout (location = 0) in vec3 aPos;uniform mat4 model;void main()
{gl_Position =  model * vec4(aPos, 1.0);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/13204.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

华为正式放弃高通芯片 | 百能云芯

5月15日&#xff0c;据外媒最新报道&#xff0c;高通公司正式确认&#xff0c;华为已无需依赖其处理器供应。 在出口许可被正式吊销前&#xff0c;高通的首席财务官已公开表示&#xff0c;预计明年与华为之间的芯片销售将为零&#xff0c;因为华为决定不再从高通购买4G芯片。 报…

[AI智能摄像头]RV1126部署yolov5并加速

导出onnx模型 yolov5官方地址 git clone https://github.com/ultralytics/yolov5 利用官方命令导出python export.py --weights yolov5n.pt --include onnx 利用代码导出 import os import sys os.chdir(sys.path[0]) import onnx import torch sys.path.append(..) from m…

在微信小程序项目中安装和使用 Vant 组件库

vant Wwapp 小程序开发组件库官网 Vant Weapp - 轻量、可靠的小程序 UI 组件库 安装 Vant 组件库 1.在微信小程序项目文件目录的空白位置右键&#xff0c;选择在外部终端窗口中打开 2在命令行输入如下命令&#xff08;在项目中创建包管理配置文件 package.json&#xff09; …

Service Worker的生命周期和全局对象和API

Service Worker的生命周期和全局对象和API 当我们注册了Service Worker后&#xff0c;它会经历生命周期的各个阶段&#xff0c;同时会触发相应的事件。整个生命周期包括了&#xff1a;installing --> installed --> activating --> activated --> redundant。当Se…

【ARMv8/v9 系统寄存器 6 -- EL 异常等级判定寄存器 CurrentEL 使用详细将介绍】

文章目录 ARMv8/v9 EL 等级获取EL 等级获取函数实现EL 等级获取测试 ARMv8/v9 EL 等级获取 下面这个宏定义是用于ARMv8/v9架构下&#xff0c;通过汇编语言检查当前执行在哪个异常级别&#xff08;Exception Level&#xff0c;EL&#xff09;并据此跳转到不同的标签。 异常级别…

svn批量解锁

问题 svn对文件进行checkout之后&#xff0c;先进行lock&#xff0c;之后再去更改&#xff0c;最后进行Commit操作&#xff1b; 上述为我们通过svn管理代码的正常方式&#xff0c;但总会有其他现象发生&#xff1b; 如果我们非正常操作&#xff0c;批量锁所有的svn文件&#x…

阿里云 服务之前设置的密钥登陆,关闭了密码登录,现在打开密码登录

通过网页远程链接 切换用户 sudo -i 输入vim /etc/ssh/sshd_config 进入配置文件 找到 将这一项设置为yes 重启系统 systemctl restart sshd.service

vivo X100s发布,搭载最新天玑9300+平台

在沉寂了半年后&#xff0c;vivo终于发布了新的旗舰产品。相较于前代的X100&#xff0c;X100s作为小迭代也有不少让人眼前一亮的地方&#xff0c;下面就让我们一同来了解下吧。 外观方面&#xff0c;虽然vivo X100s相较于X100没有大改&#xff0c;但却十分具有质感。以“青云”…

每周一算法:恰好经过K条边的最短路

题目描述 牛站 给定一张由 M M M 条边构成的无向图&#xff0c;点的编号为 1 ∼ 1000 1\sim 1000 1∼1000 之间的整数。 求从起点 S S S 到终点 E E E 恰好经过 K K K 条边&#xff08;可以重复经过&#xff09;的最短路。 注意: 数据保证一定有解。 输入格式 第 1 …

[牛客网]——C语言刷题day3

答案&#xff1a;A 解析&#xff1a; A.表示将数组a的首地址赋值给指针变量p B.将一个int型变量直接赋值给一个int型的指针是不行的 C.道理同B D.j2是一个右值&#xff0c;右值是不能进行取地址操作的 #include <iostream> using namespace std;#define N 7 int fun…

前端基础知识-ES6扩展运算符(快速实现数组添加新元素、字符串转为数组、对象添加新属性)

前言&#xff1a; 扩展运算符又称为Rest运算符&#xff0c;可以实现数组、对象、字符串在语法层面上的展开&#xff0c;达到简化语法的目的&#xff0c;使得我们提高开发效率 主要用法&#xff1a; 在需要解构的变量前加三个点&#xff08;...xxx&#xff09; 具体示例&…

快速查看字符对应的ASCII码

1、借助gdb查看 打印字符串用双引号括起来打印单个字符用单引号括起来x 表示十六机制d 表示十进制t 表示二进制 2、借助二进制查看软件 第一步&#xff1a;把要查看的字符保存到文本文件中第二步&#xff1a;借助二进制查看工具&#xff08;比如&#xff1a;Hex Editor Neo&am…

网络安全护网行动:形式主义还是真有价值?

中国每年都投入大量人力物力进行护网行动&#xff0c;如网络攻防演练、黑客技术研究等。有人质疑这些行动是否只是形式主义&#xff0c;缺乏真正的价值。然而&#xff0c;本文将深入解释这些护网行动的原因&#xff0c;并阐明其对信息安全发展的真实价值。 网络信息安全问题的…

自养号测评实战指南:Shopee、Lazada销量翻倍不再是难题

对于速卖通、亚马逊、eBay、敦煌网、SHEIN、Lazada、虾皮等平台的卖家而言&#xff0c;提高店铺流量并转化为实际销量是共同追求的目标。在这个过程中&#xff0c;自养号进行产品测评显得尤为重要。通过精心策划和执行的测评活动&#xff0c;卖家不仅能够显著增加产品的销量&am…

Google Chrome 设备工具栏原理

1.不同预览模式 2.计算出缩放比 3.固定滚动偏移 关键代码&#xff1a; overview&#xff1a; ratioW getChildRect().width / getParentRect().width ratioH getChildRect().height / getParentRect().height maxRatio max(ratioW, ratioH) if(maxRatio < 1) return 1 …

计算机网络实验3:路由器安全防控配置

实验目的和要求 理解标准IP访问控制列表的原理及功能理解CHAP、DHCP配置原理了解家用式无线路由配置方法实验项目内容 标准IP访问控制列表配置 CHAP验证路由器上配置DHCP网络地址转换NAT配置无线路由实现实验环境 1. 硬件&#xff1a;PC机&#xff1b; 2. 软件&#xff1a;W…

【35分钟掌握金融风控策略29】贷中模型调额调价策略

目录 贷中客户风险管理和客户运营体系 用信审批策略 用信审批策略决策流与策略类型 贷中预警策略 对存量客户进行风险评级 基于客户的风险评级为客户匹配相应的风险缓释措施和建议 调额策略 基于定额策略的调额策略 基于客户在贷中的风险表现的调额策略 调价策略 存…

【计算机毕业设计】springboot城市公交运营管理系统

二十一世纪我们的社会进入了信息时代&#xff0c; 信息管理系统的建立&#xff0c;大大提高了人们信息化水平。传统的管理方式对时间、地点的限制太多&#xff0c;而在线管理系统刚好能满足这些需求&#xff0c;在线管理系统突破了传统管理方式的局限性。于是本文针对这一需求设…

【校园生活小程序_超详细部署】

校园生活小程序 1 完整小程序源码2 运行环境3 初次运行3.1 启动后端程序3.1.1 导入项目&#xff0c;找到项目的pom.xml文件&#xff0c;点击ok进行打开。3.1.2 创建数据库并插入内容 3.1.3 配置项目结构信息3.1.4 配置Tomcat服务器3.1.5 正式启动后端项目3.1.6出现BUG3.1.7 解决…

Android实践:查看Activity信息

问题&#xff1a;本地Android SDK的monitor无法正常运行&#xff0c;看不了进程相关信息&#xff0c;确认当前显示Activity十分不便 解决办法&#xff1a;使用adb shell指令可以快速查看 命令&#xff1a; adb shell dumpsys activity activities 这个命令用于获取Android设…