OpenGL - 阴影映射- Tutorial 16 : Shadow mapping - CSDN博客

文章推薦指數: 80 %
投票人數:10人

文章目录Basic shadowmap - shadow map的基础知识Rendering the shadow map - 渲染shadow mapSetting up the rendertarget and the MVP matrix - 设置 ... OpenGL-阴影映射-Tutorial16:Shadowmapping Jave.Lin 于 2020-04-1010:58:24 发布 599 收藏 1 分类专栏: OpenGL 图形 文章标签: shadowmap 原文链接:http://www.opengl-tutorial.org/cn/intermediate-tutorials/tutorial-16-shadow-mapping/#basic-shadowmap 版权 OpenGL 同时被2个专栏收录 58篇文章 6订阅 订阅专栏 图形 49篇文章 0订阅 订阅专栏 文章目录 Basicshadowmap-shadowmap的基础知识Renderingtheshadowmap-渲染shadowmapSettinguptherendertargetandtheMVPmatrix-设置渲染目标和MVP矩阵Theshaders-Shader着色器Result-渲染结果 Usingtheshadowmap-使用shadowmapBasicshader-简单的shaderResult-Shadowacne-渲染结果的阴影粉刺 Problems-问题Shadowacne-阴影粉刺PeterPanning-彼得·潘宁Aliasing-锯齿PCF-靠近边缘百分比滤波PoissonSampling-泊松采样StratifiedPoissonSampling-分层泊松采样 Goingfurther-进一步探索Earlybailing-提前判定Spotlights-聚光灯Pointlights-点光源Combinationofseverallights-多组光源的使用Automaticlightfrustum-自适应的光源视锥体Exponentialshadowmaps-指数分布阴影图Light-spaceperspectiveShadowMaps-光源空间的透视ShadowMapsCascadedshadowmaps-级联阴影图Conclusion-总结 最近在学习数学的一些基础知识,发现内容超级多,有时学累了,还是看看别的,再继续学习,效果会好一些,好了,今天就学习一下OpenGL中实现Shadowmapping的内容,翻译一篇文章。

能力有限,如有错误,欢迎指正。

原文:Tutorial16:Shadowmapping (翻译到一般,不小心刷新了原文网页,然后刚好原文网站维护,一直打不开,404之类的,过了大半天,我每隔一小时刷新一下,翻译过程也是断断续续的,终于2020.04.0918:22又可以打开了,那么继续翻译吧) 在Tutorial15我们学习了如何创建包含静态光的lightmaps。

它能生成非常好的阴影,但对于会动的对象是没用的。

Shadowmap是当今(2016)用于创建动态阴影的方法。

还好的是它的工作方式是非常的简单的。

不好的是,想要它的效果处理好是很难的。

在此教程中,我们将介绍基础的算法知识、缺点,以及实现上的一些技巧来得到更好的效果。

以前(2012)要编写实现shadowmaps还是一个需要深入研究的话题,现在我们将告诉你如何实现,并根据你想要的效果进一步优化你的shadowmap。

Basicshadowmap-shadowmap的基础知识 shadowmap的基础算法包含两个pass。

首先,在光源的视角下渲染场景。

仅计算每个片段的深度。

下一步是,与平常一样的渲染场景,但会多了一步去测试当前的片段是否在阴影中。

“判断在阴影”的检测是非常的简单的。

如果当前的渲染片段比shadowmap上对应的片段的深度距离还要远,这意味着场景中还有其他更靠近光源的对象挡住了。

换句话说,就是当前的片段处于阴影中。

如下图,可能会帮助你理解原理: Renderingtheshadowmap-渲染shadowmap 在次教程中,我们只考虑方向光-光源假设是非常远的,并且光的射线都假设是平行的。

所以,完成shadowmap的渲染是使用正交投影矩阵的。

正交矩阵就像一个透视矩阵一样,但没有透视效果-无论对象的远近,看起来都是一样的。

SettinguptherendertargetandtheMVPmatrix-设置渲染目标和MVP矩阵 在Tutorial14你知道如何渲染场景到一张纹理,让后续的shader可以访问到。

这里我们使用1024x102416位的深度格式的纹理来包含shadowmap纹理。

16位已经够用于shadowmap了。

你可以随意去更改这时配置值。

注意我们使用的是一个深度纹理,而不是深度渲染缓存,因为我们后续还要对它采样。

//Theframebuffer,whichregroups0,1,ormoretextures,and0or1depthbuffer. //创建framebuffer帧缓存对象,并重新编组0个或1个,或是更多的纹理,与0个或1个深度缓存 GLuintFramebufferName=0; glGenFramebuffers(1,&FramebufferName); glBindFramebuffer(GL_FRAMEBUFFER,FramebufferName); //Depthtexture.Slowerthanadepthbuffer,butyoucansampleitlaterinyourshader //深度纹理。

比深度缓存慢一些,但可以用于后续的shader来采样 GLuintdepthTexture; glGenTextures(1,&depthTexture); glBindTexture(GL_TEXTURE_2D,depthTexture); glTexImage2D(GL_TEXTURE_2D,0,GL_DEPTH_COMPONENT16,1024,1024,0,GL_DEPTH_COMPONENT,GL_FLOAT,0); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE); glFramebufferTexture(GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,depthTexture,0); glDrawBuffer(GL_NONE);//Nocolorbufferisdrawnto. //不需要绘制颜色缓存 //Alwayscheckthatourframebufferisok //总是需要检测framebuffer是OK的 if(glCheckFramebufferStatus(GL_FRAMEBUFFER)!=GL_FRAMEBUFFER_COMPLETE) returnfalse; MVP矩阵用于渲染场景用的,它是在光源位置的视角上计算的,如下: 投影矩阵(ProjectMatrix)是一个正交矩阵,它将包含所有的东西在一个轴对齐的盒子中,X,Y,Z轴分别是:(-10,10),(-10,10),(-10,20)的大小。

这些值的设置将会让我们的整个场景总是可见的范围;这些内容在后续的章节会有进一步说明。

视角矩阵(ViewMatrix)用于旋转整个世界到相机空间,光源的方向就是的-Z(你可以重新阅读Tutorial3)世界矩阵(ModelMatrix/WorldMatrix)根据你想要的来设置就好。

glm::vec3lightInvDir=glm::vec3(0.5f,2,2); //ComputetheMVPmatrixfromthelight'spointofview //从光源的视角来计算MVP矩阵 glm::mat4depthProjectionMatrix=glm::ortho(-10,10,-10,10,-10,20); glm::mat4depthViewMatrix=glm::lookAt(lightInvDir,glm::vec3(0,0,0),glm::vec3(0,1,0)); glm::mat4depthModelMatrix=glm::mat4(1.0); glm::mat4depthMVP=depthProjectionMatrix*depthViewMatrix*depthModelMatrix; //Sendourtransformationtothecurrentlyboundshader, //inthe"MVP"uniform //给当前的绑定使用的shader设置uniformMVP glUniformMatrix4fv(depthMatrixID,1,GL_FALSE,&depthMVP[0][0]) Theshaders-Shader着色器 这个pass的shader非常简单。

顶点着色器就只是简单的计算顶点位置到齐次坐标就好了: #version330core //Inputvertexdata,differentforallexecutionsofthisshader. //输入的顶点数据,每个执行的shader都不同 layout(location=0)invec3vertexPosition_modelspace; //Valuesthatstayconstantforthewholemesh. //整个mesh渲染时都是保持不变的常量 uniformmat4depthMVP; voidmain(){ gl_Position=depthMVP*vec4(vertexPosition_modelspace,1); } 片段着色器就更加简单:将片段的深度写入到布局限定符为0的寄存器上(例子中是我们的深度纹理中)。

#version330core //Ouputdata //输出的数据 layout(location=0)outfloatfragmentdepth; voidmain(){ //Notreallyneeded,OpenGLdoesitanyway //其实不需要处理,OpenGL不论如何都会处理的 fragmentdepth=gl_FragCoord.z; } 渲染shadowmap通常是比普通的渲染快两倍的,因为仅仅写入的是低精度的深度,而不是深度与颜色;处于GPU中内存带宽通常是最大的性能状态。

Result-渲染结果 渲染结果的纹理看起来是这样的: 灰暗的颜色意味着一个很小的z值;因此,墙的右上角是比较近于相机的。

相反,白色意味着z=1(在齐次坐标),就是非常远的片段。

Usingtheshadowmap-使用shadowmap Basicshader-简单的shader 现在我们回到shader部分。

每个我们计算的片段,我都必须测试它处于shadowmap纹理的“背后”与否。

为了做到这点,我们需要计算当前片段的位置处于在我们创建shadowmap时的空间下的位置。

所以我们需要变换一次处理:将MVP矩阵与depthMVP矩阵变换一次。

这里有一些技巧。

顶点的坐标在应用depthMVP矩阵变换后我们将得到齐次坐标,它的作为值范围都在[-1,1]之间的;但是纹理的采样都范围都必须是[0,1]范围的。

就这个例子而言,一个片段要处于屏幕中间将会是(0,0)的齐次坐标;但采样纹理的中间的话,UV坐标是(0.5,0.5)。

这个调整可以修正直接渲染时得到的片段着色器中的片段坐标,但可以乘以下面的矩阵可以更高效的处理,就是简单的除以了一个2(就是矩阵对角上的系数:将[-1,1]->变化为[-0.5,0.5])然后在平移他们(矩阵中最后的一行,将:[-0.5,0.5]->变化为[0,1]) (译者jave.lin:如果还看不懂我就简单的描述一下:就是对应 ( ( − 1 , 1 ) × 0.5 ) + 0.5 = ( 0 , 1 ) ((-1,1)\times0.5)+0.5=(0,1) ((−1,1)×0.5)+0.5=(0,1),不过它是在CPU层对depthMVP中处理的,在片段着色器就不用每个片段都再执行一次这个转换了,这就是合理使用矩阵复合运算,并将这些公共部分提取到CPU层的威力) glm::mat4biasMatrix( 0.5,0.0,0.0,0.0, 0.0,0.5,0.0,0.0, 0.0,0.0,0.5,0.0, 0.5,0.5,0.5,1.0 ); glm::mat4depthBiasMVP=biasMatrix*depthMVP; 现在我们可以编写顶点着色器了。

他还是和我们之前的一样,但我们将有2个输出数据而不是1个了: gl_Position是顶点坐标处于camera下的坐标。

ShadowCoord是顶点坐标处于lightspace下的坐标(光源空间下) //Outputpositionofthevertex,inclipspace:MVP*position //输出顶点的坐标,在clipspace下:MVP*position gl_Position=MVP*vec4(vertexPosition_modelspace,1); //Same,butwiththelight'sviewmatrix //同样的,但这是光源视角矩阵下的 ShadowCoord=DepthBiasMVP*vec4(vertexPosition_modelspace,1); 片段着色器是非常简单的: texture(shadowMap,ShadowCoord.xy).z是光源与最近的深度遮挡对象的距离。

ShadowCoord.z是光源与当前渲染片段的距离。

所以,如果当前渲染的片段比shadowmap中深度遮挡对象的距离值大的话,就意味着处于阴影中(或是说:有其他的对象比当前渲染的对象更加靠近与光源): floatvisibility=1.0; if(texture(shadowMap,ShadowCoord.xy).zdrawonlyback-facingtriangles //剔除正面三角->仅绘制背面三角 而正常的渲染场景时(剔除背面) glCullFace(GL_BACK);//Cullback-facingtriangles->drawonlyfront-facingtriangles //剔除背面三角->仅绘制正面三角 这种方式的话,就需要用到bias。

PeterPanning-彼得·潘宁 (译者jave.lin:为何叫:PeterPanning,就是小时候看的:《小飞侠》国外卡通片的主角名字,下面会讲到,就是一个术语) 现在没有阴影粉刺了,但在地板仍然还是有一些错误的着色,导致墙体像是漂浮起来似的(因此术语叫:“PeterPanning”《小飞侠》国外卡通片的主角名字)。

实际上,添加了bias偏移后会让结果更糟糕。

这个问题非常容易修复:避免太薄的几何体。

这有两个优点: 第一:解决了PeterPanning的问题:几何体厚度比你的bias大,就好了。

第二:你可以开回剔除背面来渲染lightmap(或是叫shadowmap),因为现在,有另一个面向光源的多边形了,这个多边形就可以挡住另一面,这就不需要开启背面剔除来渲染了。

(我不知道是我对原文这两点的翻译理解有误,还是这两点的描述本身是很有问题的:第一点,bias是为了处理一个应该全亮的表面出现了acne粉刺的问题,而不是为了解决墙体与地板的看似腾空的问题。

第二点,使用剔除正面的方式来绘制shadowmap,就可以不使用bias的方式来偏移当前绘制的片段深度来消除acne,因为绘制几何体的背面来写入shadowmap后,shadowmap的深度值通常就会比正面的表面要大,只要几何体的厚度比分辨率导致失真的深度差大的话,就可以不使用bias来偏移了。

) 缺点是,你需要绘制更多的三角形了 Aliasing-锯齿 前面用过了两个技巧,但现在我们还是注意到阴影的边界有锯齿。

话句话说,就是一个像素是白色的,然后旁边的另一个是黑色的,它们之间没有平滑过渡。

PCF-靠近边缘百分比滤波 (全称是:PercentageCloserFilter)最简单改进的方式就是调整shadowmap的采样器sampler2DShadow。

然后当你每次对shadowmap进行采样时,其实硬件会采样到附近的纹素,然后比较它们数据,最后使用bilinear滤波后返回[0,1]之间的浮点数。

例如,0.5意味着采样了2个是在阴影里的,和2个是在光照里的。

注意这与depthmap中的单采样不同!比较总是范围true或false;PCF返回的是4个"trueorfalse"的插值。

如你所看到的,阴影的边界平滑了,但还是能看到shadowmap中的纹素块。

PoissonSampling-泊松采样 一个简单方式来处理就是采样N次shadowmap,而不是采样一次。

使用PCF滤波组合,这将会得到很好的结果,就算是少量的N次。

这里的代码是采样4次: for(inti=0;i<4;i++){ if(texture(shadowMap,ShadowCoord.xy+poissonDisk[i]/700.0).z(glm::radians(45.0f),1.0f,2.0f,50.0f); glm::mat4depthViewMatrix=glm::lookAt(lightPos,lightPos-lightInvDir,glm::vec3(0,1,0)); 使用的是透视截头锥体而不是正交椎体。

使用texture2Dproj来得到perspective-divide(透视除法)的结果(查看tutorials3-Matrices教程3-矩阵的脚注) 第二步的在shader中的透视除法。

(查看tutorials3-Matrices教程3-矩阵的脚注。

简单的说,perspectiveprojectionmatrix透视投影矩阵准确来说根本没有透视。

这是由硬件完成的,它是除以投影矩阵变换后的坐标的w的来完成透视的。

在这里,我们在shader在模拟这些变换,所以我们手动的执行透视除法(perspective-divide)。

顺便说说,正交投影矩阵总是得到齐次坐标的w为1,这就是为何没有透视的原因) 这里有两个方法在GLSL中实现。

第二种使用的是内置的textureProj函数,但两种方式的结果都是一样的: if(texture(shadowMap,(ShadowCoord.xy/ShadowCoord.w)).zLightingSettings->MixedLighting->LightingMode->Shadowmask,要勾选了上面的BakedGlobalIllumination才会有这个选项。

该宏在混合光照的时候以及选中的上述选项之后才会为真 LIGHTMAP_SHADOW_MIXING对应设置:ProjectSettings->Quality->Shad.. OpenGL.Shader:12-阴影实现-解决阴影失真 志的csdn博客 10-12 1148 OpenGL.Shader:12-阴影实现-解决阴影失真 紧接上文的内容,那么怎么解决阴影失真的问题呢?这些问题其实都是不可回避的存在,现代技术只能尽量优化效果已达以假乱真的效果。

首先回到深度纹理的函数renderDepthFBO,地板LandShadow其实是可以不参与阴影遮挡的深度测试,从而省去很大一部分遮挡测试。

depthFBO.begin(); { ... [ZZ]ShadowMap weixin_33777877的博客 06-28 120 ShadowMap 如何能够高效的产生更接近真实的阴影一直是视频游戏的一个很有挑战的工作,本文介绍目前所为人熟知的两种阴影技术之一的ShadowMap(阴影图)技术。

ShadowMap技术的概念应该说是最早应用在视频游戏中的阴影实现技术,有着非常高效和快速的特点,在实现阴影的同时只需要相对很小的计算负担。

ShadowMap绘制阴影主要是通过一张额外的阴影贴图... OpenGL核心技术之ShadowMapping 海洋个人博客 02-25 4393 笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,国家专利发明人;已出版书籍:《手把手教你架构3D游戏引擎》电子工业出版社和《Unity3D实战核心技术详解》电子工业出版社等。

CSDN视频网址:http://edu.csdn.net/lecturer/144 实时阴影技术的实现,不论在PC端还是在移动端,都显的非常重要,它也是衡量一个3D引擎渲染能力的一个 “相关推荐”对你有帮助么? 非常没帮助 没帮助 一般 有帮助 非常有帮助 提交 ©️2022CSDN 皮肤主题:深蓝海洋 设计师:CSDN官方博客 返回首页 Jave.Lin CSDN认证博客专家 CSDN认证企业博客 码龄14年 暂无认证 493 原创 9934 周排名 763 总排名 137万+ 访问 等级 1万+ 积分 1266 粉丝 391 获赞 330 评论 1367 收藏 私信 关注 热门文章 如何安装.nupkg文件? 62034 C#FSM(仿Unity中的Mecanim动画系统的状态机) 20747 突然的:图形设备驱动程序错误代码43-(已解决) 20639 C#BehaviorTree--行为树 17447 IMGUI系统-DearImGUI 17334 分类专栏 DCC 6篇 unity 201篇 游戏开发帮助文档-私有 unity-shader 115篇 UnityShader译文 53篇 UnityTimeline译文 62篇 unity搬砖 37篇 Unity私有搬砖 1篇 unity-editor-tools OpenGL 58篇 OpenGLAPI 26篇 Blender 18篇 maya 2篇 C/C++ 22篇 lua 10篇 Raymarching 9篇 git 1篇 工具集收藏 5篇 3DMAX 8篇 IMGUI 4篇 Terminology 6篇 程序员的数学-私有 Architecture 4篇 C# 73篇 Vulkan 1篇 服务端 10篇 理论 46篇 AS3 85篇 图形 49篇 py 11篇 奇思妙想 8篇 jsfl 4篇 Jave'sAS3-Control 19篇 最新评论 Unity-OptimizeMeshData导致的Mesh顶点数据(法线、切线、UV2~8,Color0~7,等)丢失 weixin_43961535: 总结的很详细 UnityShaderPostProcessing-3-深度+法线来边缘检测 JackyWuzx: 谢谢作者,项目的百度网盘提取码是什么啊? IMGUI系统-DearImGUI weixin_40699330: 通过计算来居中,官方也是这个解答 Blender-渲染动画:体积云、动画、运动模糊、Compositor数据输出 Jave.Lin: 运动模糊一般都是需要几个条件:1.相对观察点的像素级偏移,2.快门的速度相对慢一些(让胶片曝光时长长一些,并重复写入采集光信息,从而导致模糊) 所以你可以将运动的物体,相对屏幕移动附体大一些,并将shutter快门调整慢一些,即:MotionBlur中的shutter值调整大一些(这个值影响快门的打开/关闭的时长,这个时长越长,那么意味着快门越慢,就意味着重复曝光的信息越多,就越模糊) Blender-渲染动画:体积云、动画、运动模糊、Compositor数据输出 mineselfQAQ: 想问一下为什么我在做这个案例的时候无法单独为螺旋桨开启运动模糊啊 您愿意向朋友推荐“博客详情页”吗? 强烈不推荐 不推荐 一般般 推荐 强烈推荐 提交 最新文章 Photosohp-实现2DMetaBall、MetaFont Unity-Shader-搬砖日志-ShaderGraph快速出原型,然后手撸shader还原,转换到BRP/URP使用 UnityShader-踩坑记录-修复模拟器模型中不显示,或显示异常,但是PC、手机上显示没问题-uniform变量多下划线开头,比如:__MainTex,__MainText_ST 2022年22篇 2021年82篇 2020年247篇 2019年102篇 2018年39篇 2017年11篇 2016年15篇 2015年11篇 2014年11篇 2013年54篇 2012年93篇 目录 目录 分类专栏 DCC 6篇 unity 201篇 游戏开发帮助文档-私有 unity-shader 115篇 UnityShader译文 53篇 UnityTimeline译文 62篇 unity搬砖 37篇 Unity私有搬砖 1篇 unity-editor-tools OpenGL 58篇 OpenGLAPI 26篇 Blender 18篇 maya 2篇 C/C++ 22篇 lua 10篇 Raymarching 9篇 git 1篇 工具集收藏 5篇 3DMAX 8篇 IMGUI 4篇 Terminology 6篇 程序员的数学-私有 Architecture 4篇 C# 73篇 Vulkan 1篇 服务端 10篇 理论 46篇 AS3 85篇 图形 49篇 py 11篇 奇思妙想 8篇 jsfl 4篇 Jave'sAS3-Control 19篇 目录 实付元 使用余额支付 点击重新获取 扫码支付 钱包余额 0 抵扣说明: 1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。

2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值



請為這篇文章評分?