Direct3D 能让linux运行天涯明月刀等类型3d游戏吗

404 Not Found
404 Not Found&figure&&img src=&https://pic2.zhimg.com/fa84e880732_b.jpg& data-rawwidth=&1618& data-rawheight=&1080& class=&origin_image zh-lightbox-thumb& width=&1618& data-original=&https://pic2.zhimg.com/fa84e880732_r.jpg&&&/figure&&p&在 SIGGRAPH Asia 2014 中,《&a href=&https://link.zhihu.com/?target=http%3A//sa2014.siggraph.org/en/attendees/courses.html%3Fview%3Dsession%26type%3Dcourses%26sessionid%3D36& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&高效地使用大量光源于实时着色(Efficient Real-TimeShading with Many Lights)&/a&》是和游戏技术最接近的课程之一。三位主讲分别是 Ola Olsson(瑞典查尔姆斯理工大学博士)、Emil Persson(Avalanche Studios研究部负责人)、Markus Billeter(瑞典查尔姆斯理工大学博士候选人)。课程内容比较前向、延迟、分块、群组和相关技术实现大量动态光源的渲染管道。由于现时未获演讲稿的电子版本,笔者尽量以记忆及相关文献简单介绍一下本课程的重点。&/p&&p&(标题相片来自 &a href=&https://link.zhihu.com/?target=https%3A//unsplash.com/pixelperfect& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Ryan Pouncy | Unsplash&/a&)&/p&&p&近年,实时渲染管道经历了一连串变革,全动态光源对这些变革有重要影响。传统的前向 渲染(forward rendering)管道在渲染&img src=&https://www.zhihu.com/equation?tex=n& alt=&n& eeimg=&1&&个物体在&img src=&https://www.zhihu.com/equation?tex=m& alt=&m& eeimg=&1&&个光源下着色,需要绘制&img src=&https://www.zhihu.com/equation?tex=%5Cmathrm%7BO%7D%28nm%29& alt=&\mathrm{O}(nm)& eeimg=&1&&次。当需要越来越复杂的游戏场景,更多光源,这种方式成为CPU及GPU的性能瓶颈。在2004年 GDC 出现了延迟渲染(deferred rendering)管道的讨论[4],所有物体都先绘制在一组屏幕空间的缓冲(称为几何缓冲区/G-buffer,见图1),再逐光源对该缓冲着色,复杂度变成&img src=&https://www.zhihu.com/equation?tex=%5Cmathrm%7BO%7D%28n%2Bm%29& alt=&\mathrm{O}(n+m)& eeimg=&1&&。另一种相关的技术在同一届 GDC 中发表,被称为延迟光照(deferred lighing)[3],几年后也被称为前期光照通道(light pre-pass)[2]。通过这类“延迟”方式,已可以大量增加物体和光源的数量,渲染出更复杂的场景。然而,这种延迟方式有几个问题。首先,只能使用统一的材质着色器,而且其参数受限(每个参数需要相应的G-buffer),这对于游戏的画面风格造成很大的限制。第二,在性能上虽然复杂度降低了,但读写G-buffer的内存带宽用量成为了性能瓶颈。由于GPU的计算性能不断提升但显存带宽却提升缓慢,使延迟渲染越来越不适合近代的GPU。第三,不能处理半透明及多重采样抗锯齿(MultiSampling Anti-Aliasing, MSAA)。&/p&&figure&&img src=&https://pic4.zhimg.com/a43e41af1c05e194ce6baa_b.jpg& data-rawwidth=&635& data-rawheight=&156& class=&origin_image zh-lightbox-thumb& width=&635& data-original=&https://pic4.zhimg.com/a43e41af1c05e194ce6baa_r.jpg&&&/figure&&p&图1:延迟渲染的几何缓冲区/G-buffer及渲染结果(维基百科图片)&/p&&p&因为这些因素,近几年催生出另一族渲染管道的方式,称为分块渲染(tiled rendering) [5]。分块渲染有多个变种,先说分块延迟渲染(tiled deferred rendering)。在前面提及,延迟渲染的瓶颈在于读写 G-buffer,在大量光源下,具体瓶颈将位于每个光源对 G-buffer的读取及与颜色缓冲区(color buffer)混合。这里的问题是,每个光源,即使它们的影响范围在屏幕空间上有重疉,因为每个光源是在不同的绘制中进行,所以会重复读取G-buffer中相同位置的数据,计算后以相加混合方式写入颜色缓冲。光源越多,内存带宽用量越大。而分块延迟渲染则是把屏幕分拆成细小的栅格,例如每 32 × 32 象素作为一个分块(tile)。然后,计算每个分块会受到哪些光源影响,把那些光源的索引储存在分块的光源列表里。最后,逐个分块进行着色,对每像素读取 G-buffer 和光源列表及相关的光源信息。因此,G-buffer的数据只会被读取1次且仅1次,写入 color buffer 也是1次且仅1次,大幅降低内存带宽用量。不过,这种方法需要计算 光源会影响哪些分块,这个计算又称为光源剔除(light culling),可以在 CPU 或 GPU(通常以 compute shader 实现)中进行。用GPU计算的好处是,GPU 计算这类工作比 CPU 更快,也减少 CPU/GPU 数据传输。而且,可以计算每个分块的深度范围(depth range),作更有效的剔除。&/p&&p&&figure&&img src=&https://pic2.zhimg.com/49b5ad2a8234edb1af97_b.jpg& data-rawwidth=&634& data-rawheight=&230& class=&origin_image zh-lightbox-thumb& width=&634& data-original=&https://pic2.zhimg.com/49b5ad2a8234edb1af97_r.jpg&&&/figure&图2:分块渲染(图片来自 Olaolss 的&a href=&https://link.zhihu.com/?target=http%3A//www.cse.chalmers.se/%7Eolaolss/main_frame.php%3Fcontents%3Dpublication%26id%3Dtiled_shading& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&网页&/a&)&/p&&p&有趣的是,这种分块方式也可以用于前向渲染,这称为分块前向渲染(tiled forward rendering)[1]。当计算好光源列表,前向渲染时就按屏幕位置读取相关的光源信息去着色。那么,就可以同时解决延迟渲染的各种问题,又不需要像传统前向渲染对物体进行多次绘制。这种分块渲染似乎已经很理想,但如果增加更多光源,而场景又比较空扩,每个分块的光源数量就会变得更多。虽然做了深度范围的优化可缓解这个问题,但如果在视野里有很多深度不连续区域(depth discontinuities),如图3,那么该优化也无能为力了。为了解决这个问题,研究者想 出了不同的新方法。&/p&&figure&&img src=&https://pic4.zhimg.com/5e2ebbfcca66f17_b.jpg& data-rawwidth=&634& data-rawheight=&241& class=&origin_image zh-lightbox-thumb& width=&634& data-original=&https://pic4.zhimg.com/5e2ebbfcca66f17_r.jpg&&&/figure&&p&图3:深度不连续的场景(图片来自[8])&/p&&p&Olsson、Billeter 及他们的导师 Ulf Assarsson 在2012年发表了一种新的改善方式,名为分群组渲染(clustered rendering)[6][7]。这里的群组(cluster)是指分块(tile)在深度上进一步划分,这种体积数据使每个单位储存更少量的光源(图4)。在渲染时,除了考虑着色像素的屏幕坐标,还考虑到深度的坐标,去索引群组并取得光源信息。这个简单的概念优化了光源剔除,使光源数目能进一步提升。&/p&&figure&&img src=&https://pic1.zhimg.com/cd524bc2ec178f2f70feea8b8719111b_b.jpg& data-rawwidth=&634& data-rawheight=&229& class=&origin_image zh-lightbox-thumb& width=&634& data-original=&https://pic1.zhimg.com/cd524bc2ec178f2f70feea8b8719111b_r.jpg&&&/figure&&p&图4:群组渲染(图片来自Olaolss的网页 3)&/p&&p&据游戏业界的Persson称,以他所知,暂时未有已发行游戏使用了群组渲染技术。而他 在Avalanche Studios开发中的《正当防卫3(Just Cause 3)》则使用了此技术[8]。对于实际在游戏中应用群组渲染技术,他表示非常乐观。在开发本作中,他从实际的场景中发现,如果简单地使用指数方式划分深度,较近的范围可能有太多群组,而使效率变差。所以他把最近的群组加深,有效改善此问题。另外,由于该作主要是室外大型场景,远景就不使用真正的动态光照,而仅使用公告板作渲染。&/p&&figure&&img src=&https://pic2.zhimg.com/deddc1eadc3b_b.jpg& data-rawwidth=&634& data-rawheight=&354& class=&origin_image zh-lightbox-thumb& width=&634& data-original=&https://pic2.zhimg.com/deddc1eadc3b_r.jpg&&&/figure&&p&图5:Avalanche 工作室对群组深度分布的优化(图片来自[8])&/p&&p&不过,在光照上还有一个未有完善解决的问题,就是阴影。笔者记得,以前在解决虚拟点 光源模拟全局光照的阴影问题时,曾出现一种名为不完美阴影贴图(imperfect shadow map, ISM)的技术[9]。刚好在这课程的前一天 Square Enix 的德吉雄介也谈到使用ISM于他们的全局渲染管道中[10]。然而,本课程中 Olsson 就介绍了他和 Billeter 及其他同事合作发明的一个方案, 称为虚拟阴影贴图(virtual shadow map),采用类似 id Tech 5 的虚拟纹理(virtual texturing)技术,去动态按阴影采样需求动态分配阴影贴图的纹理空间。其结果也是不错的,虽然使用了较大量的显存,但似乎在这一代游戏机平台上是有可能应用到的。&/p&&p&最后 Billeter 讲述在移动平台上实现多光源的尝试,由于移动设备的内存带宽比PC的问题 更大,使用分块或群组渲染都更有竞争力。可望在近一两年内在高端手机游戏中实际应用。&/p&&h2&参考文献&/h2&&p&[1] Markus Billeter, Ola Olsson, and Ulf Assarsson. Tiled forward shading. GPU Pro 4: Advanced Rendering Techniques, 4:99, 2013.&/p&&p&[2] Wolfgang Engel. Designing a renderer for multiple lights–the light pre-pass renderer. ShaderX7: Advanced rendering techniques, 2009.&/p&&p&[3] Rich Geldreich, Matt Pritchard, and John Brooks. &a href=&https://link.zhihu.com/?target=http%3A//www.tenacioussoftware.com/gdc_2004_deferred_shading.ppt& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Deferred lighting and shading&/a&. In Game Developers Conference, volume2, 2004.&/p&&p&[4] Shawn Hargreaves and Mark Harris. &a href=&https://link.zhihu.com/?target=http%3A//www.shawnhargreaves.com/DeferredShading.pdf& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Deferred shading&/a&. In Game Developers Conference, volume 2, 2004.&/p&&p&[5] Ola Olsson and Ulf Assarsson. &a href=&https://link.zhihu.com/?target=http%3A//www.cse.chalmers.se/%7Eolaolss/get_file.php%3Ffilename%3Dpapers/Improved%2520Ray%2520Hierarchy%2520Alias%2520Free%2520Shadows.pdf& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Tiled shading&/a&. Journal of Graphics, GPU, and Game Tools, 15(4):235–251, 2011.&/p&&p&[6] Ola Olsson, Markus Billeter, and Ulf Assarsson. &a href=&https://link.zhihu.com/?target=http%3A//www.cse.chalmers.se/%7Eolaolss/get_file.php%3Ffilename%3Dpapers/clustered_shading_preprint.pdf& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Clustered deferred and forward shading&/a&. In HPG ’12: Proceedings of the Conference on High Performance Graphics .&/p&&p&[7] Ola Olsson, Markus Billeter, and Ulf Assarsson. &a href=&https://link.zhihu.com/?target=http%3A//www.cse.chalmers.se/%7Eolaolss/get_file.php%3Ffilename%3Dpapers/tiled_shading_siggraph_2012.pdf& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Tiled and clustered forward shading&/a&. In SIGGRAPH ’12: ACM SIGGRAPH 2012 Talks, New York, NY, US- A, 2012. ACM.&/p&&p&[8] Emil Persson and Ola Olsson. &a href=&https://link.zhihu.com/?target=http%3A//www.cse.chalmers.se/%7Eolaolss/get_file.php%3Ffilename%3Dpapers/siggraph_2013.pdf& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Practical clustered deferred and forward shading&/a&. SIGGRAPH Course: Advances in Real-Time Rendering in Games, 2013.&/p&&p&[9] Tobias Ritschel, Thorsten Grosch, Min H Kim, H-P Seidel, Carsten Dachsbacher, and Jan Kautz. &a href=&https://link.zhihu.com/?target=http%3A//people.mpi-inf.mpg.de/%7Eritschel/Papers/ISM.pdf& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Imperfect shadow maps for efficient computation of indirect illumination&/a&. In ACM Transactions on Graphics (TOG), volume 27, page 129. ACM, 2008.&/p&&p&[10] Yusuke Tokuyoshi. &a href=&https://link.zhihu.com/?target=http%3A//www.jp.square-enix.com/info/library/pdf/Virtual%2520Spherical%2520Gaussian%2520Lights%2520for%2520Real-Time%2520Glossy%2520Indirect%2520Illumination.pdf& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Virtual spherical gaussian lights for real-time glossy indirect illumination&/a&. In SIGGRAPH Asia 2014 Technical Briefs, page 17. ACM, 2014.&/p&&p&本文原于日在腾讯内部揭载,获授权公开。&/p&&p&&figure&&img src=&https://pic4.zhimg.com/d5005df8aebb185aa03d5b7fdfe4ce0d_b.jpg& data-rawwidth=&150& data-rawheight=&150& class=&content_image& width=&150&&&/figure&&a href=&https://link.zhihu.com/?target=http%3A//miloyip.com/2014/many-lights/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&SIGGRAPH Asia 2014 见闻之《高效地使用大量光源于实时着色》课程&/a&&/p&
在 SIGGRAPH Asia 2014 中,《》是和游戏技术最接近的课程之一。三位主讲分别是 Ola Olsson(瑞典查尔姆斯理工大学博士)、Emil Persson(Avalanche Studios研究部负责人)、M…
&p&题主说熟悉C#,我最近刚好写了一个C#版的,虽然没有各位大神的高大上,但是比较简单易懂,也放上来给题主参考一下吧~&/p&&p&原文链接:&a href=&//link.zhihu.com/?target=http%3A//blog.csdn.net/aceyan0718/article/details/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&用C#实现一个简易的软件光栅化渲染器&/a&&/p&&br&&p& 这是一个用C#+winform实现的软件光栅化渲染器,今天拿出来与大家分享一下,希望能起到抛砖引玉的作用,给新人一点启发(结构比较简单,注释比较详细^_^),也欢迎司机们拍砖指点和交流~&/p&&p&&strong&目的:&/strong&&/p&&p& 巩固图形编程知识,理解渲染流水线所做的事情。&/p&&p&&strong&实现功能:&/strong&&/p&&p&1、将顶点数据进行一系列处理显示到屏幕上(废话。。。(?_?))&/p&&p&2、线框渲染模式、纹理渲染模式、顶点色模式&/p&&p&3、纹理uv坐标和顶点色等的透视校正插值&/p&&p&4、纹理双线性过滤采样&/p&&p&5、背面消隐&/p&&p&6、Cvv简单裁剪&/p&&p&7、“基础光照模型”(相当于D3D、OpenGL中的固定管线顶点光照)&/p&&p&&strong&截图:&/strong&&/p&&p&光照&/p&&figure&&img src=&https://pic3.zhimg.com/50/c034cd971f4bd480e42b4_b.jpg& data-rawwidth=&784& data-rawheight=&597& class=&origin_image zh-lightbox-thumb& width=&784& data-original=&https://pic3.zhimg.com/50/c034cd971f4bd480e42b4_r.jpg&&&/figure&&p&顶点色&/p&&figure&&img src=&https://pic3.zhimg.com/50/0a7a1411f1acb4d9ffb70_b.jpg& data-rawwidth=&800& data-rawheight=&600& class=&origin_image zh-lightbox-thumb& width=&800& data-original=&https://pic3.zhimg.com/50/0a7a1411f1acb4d9ffb70_r.jpg&&&/figure&&p&线框模式:&/p&&figure&&img src=&https://pic2.zhimg.com/50/a7015aea10209bdc19d7_b.jpg& data-rawwidth=&800& data-rawheight=&600& class=&origin_image zh-lightbox-thumb& width=&800& data-original=&https://pic2.zhimg.com/50/a7015aea10209bdc19d7_r.jpg&&&/figure&&br&&br&&p&&strong&代码放在Github上面:&/strong&&a href=&//link.zhihu.com/?target=https%3A//github.com/aceyan/SimpleSoftwareRenderer& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&GitHub - aceyan/SimpleSoftwareRenderer: 简单的软件渲染by c# winform&/a&&/p&&br&&p&下面我会将一些完成这个渲染器所需要的知识点以及相关资料的链接(多是博文)罗列一下,方便查阅。&/p&&br&零、准备阶段&br&&br&&p&想要实现渲染器首先我们得知道“给定视点、三维物体、光源、照明模式,和纹理等元素,如何绘制一幅二维图像”,这就必须提到一个词“图像绘制管线”(也称绘制流水线),我们得渲染器正是要以软件的形式来模拟这条流水线的运作。&/p&&p&流水线:&a href=&//link.zhihu.com/?target=http%3A//www.cnblogs.com/wonderKK/p/5022226.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&(转)GPU图形绘制管线&/a&&/p&&figure&&img src=&https://pic4.zhimg.com/50/c36c7beee23f04d8ab6c710f_b.jpg& data-rawwidth=&674& data-rawheight=&470& class=&origin_image zh-lightbox-thumb& width=&674& data-original=&https://pic4.zhimg.com/50/c36c7beee23f04d8ab6c710f_r.jpg&&&/figure&&p&理解了绘制管线,我们发现其中充满对矩阵向量等数学工具的运用,那么再实现流水线之前,我们必须先实现这些数学工具,这些数学类网上已经有很多的资料了,但是有几点需要特别说明:&/p&&strong&1、本程序中使用行矩阵与行向量&/strong&&p&相关资料:D3d和openGl矩阵区别&/p&&p&&a href=&//link.zhihu.com/?target=http%3A//blog.csdn.net/Nightmare/article/details/3983724& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&矩阵和向量的乘法顺序&/a&&/p&&strong&2、本程序使用左手坐标系&/strong&&strong&3、实现了color类来方便进行颜色的运算,因为进行光照的时候要使用颜色乘以颜色的计算方法,它跟向量的乘法不同,应该称为“&/strong&Modulate&strong&(调制)”,注意不能用向量乘法的计算公式哦。&/strong&&p&相关资料:&/p&&p&&a href=&https://www.zhihu.com/question//answer/& class=&internal&&在 Cg 的逐像素着色的最后,每一个颜色值相加和每一个颜色相乘从数学角度有什么差别? - Milo Yip 的回答&/a&&/p&&p&@Milo Yip 大神的博文里面有一段讲颜色计算的:&/p&&p&&a href=&//link.zhihu.com/?target=http%3A//www.cnblogs.com/miloyip/archive//1698953.html%& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&用JavaScript玩转计算机图形学(一)光线追踪入门&/a&&/p&&strong&4、矩阵求逆&/strong&&p&由于要进行光照,我们需要变换顶点法线,那么就需要矩阵求逆和求转置的运算。(为什么需要逆转置呢?请看:&a href=&https://www.zhihu.com/question/& class=&internal&&cg语言漫反射光照模型中的worldMatrix_IT是什么意思?是世界变换矩阵的转置的逆? - 游戏开发&/a&)&/p&&p&其中矩阵求逆比较复杂,本程序使用的是伴随矩阵的方法求矩阵的逆, 必须先求出矩阵的行列式和伴随矩阵,这部分需要一定的线性代数基础知识。&/p&4.1矩阵求逆重要定理:&figure&&img src=&https://pic4.zhimg.com/50/946a84fa1d16a28a9d7c_b.jpg& data-rawwidth=&713& data-rawheight=&420& class=&origin_image zh-lightbox-thumb& width=&713& data-original=&https://pic4.zhimg.com/50/946a84fa1d16a28a9d7c_r.jpg&&&/figure&&br&&p&&a href=&//link.zhihu.com/?target=http%3A//wenku.baidu.com/link%3Furl%3Ddga27GT5oSK8SDwHaeqPGL_raHnMLNP54oad7q8wJnMOqEpuT4_UAC-BHAeTsL8vGpYkRuphnXng99TLfHOx8_Nn7WMVycFRlUiMgSS-2Ve& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&逆矩阵的几种求法与解析(很全很经典)_百度文库&/a&&/p&4.2求矩阵行列式&p&本程序使用递归&a href=&//link.zhihu.com/?target=http%3A//lib.csdn.net/base/datastructure& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&算法&/a&求行列式,请查看矩阵行列式的递归定义:&/p&&p&&a href=&//link.zhihu.com/?target=http%3A//wenku.baidu.com/link%3Furl%3D9TSCuxJdfypmsqZLYegHNuKYP1TABFwGT22v-cJut5wenWM-Ll2Bp6zqgDEeIvd9ChV3Laj3czAbdWtjSwaulHHSkUZtrLNcgLqNw2xrjb7& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&3行列式(递归定义)_图文&/a&&/p&4.3伴随矩阵:&p&&a href=&//link.zhihu.com/?target=http%3A//wenku.baidu.com/link%3Furl%3DBVbtO30v7eygbZUloybJtpFkCrpAwypYNyIx7XGX-OItxdWz0w4Y2hpzux729bq4MLKnx0sWwS4mJwm8Nrnf_3zxG9RqzD7LND3mklrwyDe& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&伴随矩阵_百度文库&/a&&/p&&br&&p&实现了数学类,就可以正式进入&strong&流水线&/strong&了:&/p&&br&一、几何阶段&br&&br&&strong&1、顶点从模型空间-----&世界空间&/strong&&p&这一步要生成世界矩阵(简称m),m是一系列平移、选择等变换的组合&/p&&p&如果开启了光照,我们还需要把模型空间的法线信息变换到世界空间,对顶点进行光照计算并保存光照结果颜色,以便在光栅化的时候进行差值和颜色调制(Modulate)&/p&&p&这里实现的“简单光照模型”可以参见《Cg教程_可编程实时图形权威指南》第五章光照&/p&&strong&2、世界空间&/strong&-----&相机空间&p&这一步需要生成视矩阵(简称v)&/p&&p&推导过程:@zdd的博客 &a href=&//link.zhihu.com/?target=http%3A//www.cnblogs.com/graphics/archive//2476413.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&View Transform(视图变换)详解&/a&&/p&&p&上面的推导中&figure&&img src=&https://pic4.zhimg.com/50/0b6ab8c7f4aeb5ba1315b2_b.jpg& data-rawwidth=&279& data-rawheight=&150& class=&content_image& width=&279&&&/figure&写错了&/p&&p&应该是 &/p&&p&
ry rz&/p&&p&[ ux uy uz ]&/p&&p&
dx dy dz&/p&&p&但是推导结果是对的&/p&&strong&3、相机空间---&齐次剪裁空间&/strong&&p&这里要生成投影矩阵,简称p&/p&&p&Dx风格的投影矩阵推导:&/p&&p&&a href=&//link.zhihu.com/?target=http%3A//www.cnblogs.com/graphics/archive//2582119.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&透视投影详解&/a&&/p&&p&本程序使用的投影矩阵:&/p&&figure&&img src=&https://pic3.zhimg.com/50/2b2b9a5299eff50b4ffcf3_b.jpg& data-rawwidth=&423& data-rawheight=&262& class=&origin_image zh-lightbox-thumb& width=&423& data-original=&https://pic3.zhimg.com/50/2b2b9a5299eff50b4ffcf3_r.jpg&&&/figure&&br&&p&视空间的顶点乘以这个矩阵之后被变换到齐次剪裁空间,并且w分量保存着视空间的z信息。&/p&&p&在齐次剪裁空间我们可以对顶点进行简单的裁剪,既将不在&/p&&p&-w &= x &= w&/p&&p&-w&= y &= w&/p&&p&0 &= z &= w&/p&&p&这个范围的顶点剔除掉,不进行渲染。&/p&&p&这里采用的是最简单粗暴的剪裁方法,更加复杂的剪裁还需要生成新的顶点,有很多可以深入挖掘的地方:)&/p&&p&Ps:为了方便后续的透视校正插值,程序中还将1/z 保存在顶点数据中。&/p&&strong&4、进行图元装配,也就是将顶点以一定的顺序组装成三角形(Primitive Assembly && Trianglesetup )&/strong&&p&本程序中使用了顶点索引的方式来组织顶点数据,并且约定以逆时针顺序组织的三角形的法线朝向屏幕外,换句话说就是逆时针顺序组织的三角形看起来是正面。&/p&&p&有了这个约定就可以进行背面消隐:&/p&&p&&a href=&//link.zhihu.com/?target=http%3A//blog.csdn.net/cppyin/article/details/6207206& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&从零实现3D图像引擎:(14)背面消隐的三大陷阱&/a&&/p&&strong&5、透视除法&/strong&&p&
对透视变换得到的含有深度信息(z)的齐次坐标做透视除法。所谓透视除法,&br&
就是把透视变换后的齐次坐标除以(z)。由于透视变换矩阵已经构造好了,当&br&
视锥体内部点经透视处理后的齐次坐标除以(z)后,使得顶点进入&/p&&p&-1&= x &= 1&/p&&p&-1&= y &= 1&/p&&p&0 &= z &= 1
(CVV正方体中)。&br&&/p&&strong&6、映射到视口&/strong&&p&将cvv正方体的顶点根据屏幕大小或者视口大小转换为屏幕坐标&/p&二、光栅化阶段&br&&br&&strong&1、Rasterization光栅化&/strong&&p&光栅化决定哪些像素被几何图元覆盖的过程&/p&1.1 如果选择线框模式,本程序使用的画线方法是&a href=&//link.zhihu.com/?target=http%3A//www.cnblogs.com/gamesky/archive//2648623.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Bresenham快速画直线算法&/a&&p&相关资料:&a href=&//link.zhihu.com/?target=http%3A//www.cnblogs.com/gamesky/archive//2648623.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Bresenham快速画直线算法&/a&&/p&1.2顶点色模式和纹理模式&p&三角形光栅化算法:&a href=&//link.zhihu.com/?target=http%3A//blog.csdn.net/cppyin/article/details/6232453& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&从零实现3D图像引擎:(15)三角形的光栅化&/a&&/p&&p&扫描线填充算法:&a href=&//link.zhihu.com/?target=http%3A//blog.csdn.net/cppyin/article/details/6172211& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&从零实现3D图像引擎:(2)画2D直线不简单&/a&&/p&&p&光栅化过程当中,我们要对uv坐标,顶点颜色、我们保存在顶点信息中的1/z等信息进行透视校正插值:&/p&&p&&a href=&//link.zhihu.com/?target=http%3A//blog.csdn.net/popy007/article/details/5570803& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&深入探索透视纹理映射(下)&/a&&/p&&p&文章中的重要结论:&/p&&p&我们发现s/z、t/z和x’、y’也是线性关系。而我们之前知道1/z和x’、y’是线性关系。则我们得出新的思路:对1/z关于x’、y’插值得到1/z’,然后对s/z、t/z关于x’、y’进行插值得到s’/z’、t’/z’,然后用s’/z’和t’/z’分别除以1/z’,就得到了s’和t’。&/p&&p&这就是为什么我们要保存1/z的原因。&/p&&br&&strong&2、Pixel Operation 像素操作&/strong&2.1消除遮挡面&p&根据zbuff
使用1/z来进行ztest,这样就不用给zbuff初始化一个很大的值了。&/p&2.2 Texture operation 纹理操作,也就是根据像素的纹理坐标,查询对应的纹理值&p&纹理坐标的透视校正: &a href=&//link.zhihu.com/?target=http%3A//blog.csdn.net/popy007/article/details/5556639& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&深入探索透视纹理映射(上)&/a&&/p&&p&纹理采样,双线性纹理过滤:&a href=&//link.zhihu.com/?target=http%3A//dev.gameres.com/Program/Visual/3D/Bilinear.htm& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&纹理映射的双线性插值滤波&/a&&/p&&p&&a href=&//link.zhihu.com/?target=http%3A//blog.csdn.net/i_dovelemon/article/details/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&DirectX (9) 纹理映射&/a&&/p&2.3 Blending&p&本程序暂时没有对alpha的处理,后面应该会更新&/p&2.4:Filtering&p&相当于后处理,就是对即将写入framebuff(帧缓冲)的像素进行一些处理,本程序暂时没有添加。&/p&&p&经历了上面的步骤,把要渲染的像素写到framebuff中,再将framebuff渲染到屏幕上就完成了所有步骤(本程序简单的生成了一个bitmap来作为framebuff),这时你应该可以在屏幕上看到你所渲染的东西。如果看起来不太对,那么进入debug吧。一开始也许会觉得调试这样一个绘制流水线会无从下手,因为其中任何一步的偏差都会使最后的渲染发生错误。&/p&&p&请假了一些大神后找到了不错的调试方法,下面给出一些tips:&/p&&p&1、调试数学库,用笔算来验证结果的正确性&/p&&p&2、调试流水线,可以建立一个简单多边形,比如一个quad,用笔算来验证m、v、p三个矩阵的正确性。光栅化的阶段由于要进行插值,计算量比较大,建议使用心算和单步跟踪的方式来进行调试。&/p&&p&笔算的过程能让你更加了解背后的数学奥秘:)&/p&&p&最后要感谢一下@cppyin,@zdd, &a data-hash=&1e2cccc3ce33& href=&//www.zhihu.com/people/1e2cccc3ce33& class=&member_mention& data-editable=&true& data-title=&@Milo Yip& data-hovercard=&p$b$1e2cccc3ce33&&@Milo Yip&/a& , &a data-hash=&225cbdbbfacce96f55de2f& href=&//www.zhihu.com/people/225cbdbbfacce96f55de2f& class=&member_mention& data-editable=&true& data-title=&@韦易笑& data-hovercard=&p$b$225cbdbbfacce96f55de2f&&@韦易笑&/a& 等大神的文章,给我很大帮助。&/p&&p&特别要推荐 &a data-hash=&225cbdbbfacce96f55de2f& href=&//www.zhihu.com/people/225cbdbbfacce96f55de2f& class=&member_mention& data-editable=&true& data-title=&@韦易笑& data-hovercard=&p$b$225cbdbbfacce96f55de2f&&@韦易笑&/a& 老师写的mini3D:&a href=&//link.zhihu.com/?target=http%3A//www.skywind.me/blog/archives/1498& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Skywind Inside&/a&,让我受益匪浅。 &/p&
题主说熟悉C#,我最近刚好写了一个C#版的,虽然没有各位大神的高大上,但是比较简单易懂,也放上来给题主参考一下吧~原文链接: 这是一个用C#+winform实现的软件光栅化渲染器,今天拿出来与大家分享一下,希望能起到抛砖…
&figure&&img src=&https://pic1.zhimg.com/v2-c75b6b9214aed4b210b2b_b.jpg& data-rawwidth=&1728& data-rawheight=&1080& class=&origin_image zh-lightbox-thumb& width=&1728& data-original=&https://pic1.zhimg.com/v2-c75b6b9214aed4b210b2b_r.jpg&&&/figure&&h2&写在前面&/h2&&p&
专栏断更了这么久, 实在惭愧. 这段时间又是期末考试又是回家过节, 实在是没时间整理干货来分享. 之前说好的不及时更新就赔钱, @Cathy Chen童鞋已经拿到10大洋的红包了. &/p&&p&
不过从今天开始我就有大把的时间来继续研究图形学啦, 因此会保证更新速度的 ~ &/p&&p&
延续了我对Depth Buffer的一贯兴趣, 本文将介绍&b&SSAO&/b&的基本原理及包括&b&Temporal Coherence SSAO, Selective Temporal Filtering SSAO&/b&在内的优化算法. &/p&&br&&h2&何为&SSAO&&/h2&&p&
Screen Space Ambient Occlusion (以下简称SSAO), 屏幕空间环境光遮蔽. 在具体介绍SSAO之前, 本文先介绍更加广义的Ambient Occlusion (AO). &/p&&p&
简单来说, Ambient Occlusion(以下简称&AO&)是一种基于全局照明中的环境光(Ambient Light)参数和环境几何信息来计算场景中任何一点的&b&光照强度系数&/b&的算法. AO描述了表面上的任何一点所接受到的环境光被周围几何体所遮蔽的百分比, 因此使得渲染的结果更加富有层次感, 对比度更高. &figure&&img src=&https://pic4.zhimg.com/v2-40edd773a01ffb69399c75d_b.jpg& data-rawwidth=&652& data-rawheight=&315& class=&origin_image zh-lightbox-thumb& width=&652& data-original=&https://pic4.zhimg.com/v2-40edd773a01ffb69399c75d_r.jpg&&&/figure&&br&&/p&&p&
图片来自Wiki. 因为老人的皱纹处对外界暴露的部分较少, 使用AO后被遮蔽的部分较多, 渲染后显得更加暗一些, 增加了皱纹的层次感和质感. &/p&&p&
AO的计算公式如下: &/p&&img src=&https://www.zhihu.com/equation?tex=AO%28p%2C+n_%7Bp%7D+%29+%3D+%5Cfrac%7B1%7D%7B%5Cpi+%7D+%5Cint_%7B%5COmega+%7D%5E%7B%7D+V%28p%2C+%5Comega+%29max%28n_%7Bp%7D%2A%5Comega%2C+0%29d%5Comega& alt=&AO(p, n_{p} ) = \frac{1}{\pi } \int_{\Omega }^{} V(p, \omega )max(n_{p}*\omega, 0)d\omega& eeimg=&1&&&p&&img src=&https://www.zhihu.com/equation?tex=n_%7Bp%7D+& alt=&n_{p} & eeimg=&1&&代表点&img src=&https://www.zhihu.com/equation?tex=p& alt=&p& eeimg=&1&&的法线, &img src=&https://www.zhihu.com/equation?tex=%5Comega+& alt=&\omega & eeimg=&1&&代表点&img src=&https://www.zhihu.com/equation?tex=p& alt=&p& eeimg=&1&&切平面正方向的任意单位向量, &img src=&https://www.zhihu.com/equation?tex=V%28p%2C+%5Comega+%29& alt=&V(p, \omega )& eeimg=&1&&是可见函数, 如果点&img src=&https://www.zhihu.com/equation?tex=p& alt=&p& eeimg=&1&&在&img src=&https://www.zhihu.com/equation?tex=%5Comega+& alt=&\omega & eeimg=&1&&方向被遮挡则为1, 否则为0. &/p&&p&
由此可见, 计算AO系数是一个颇为昂贵的操作. 一般离线渲染器都会采用Ray-Tracing(光线追踪)或是简化的Ray-Marching(所谓光线行进)算法, 模拟若干条射线以计算遮蔽百分比. 很明显这种方式不可能应用到实时图形渲染中. 尽管目前有一些实时计算AO的新技术, 但是其性能距离普及还有很长的路要走. &/p&&p&&figure&&img src=&https://pic1.zhimg.com/v2-17b9e68effc23f3bd170ebefd94b9c9d_b.jpg& data-rawwidth=&838& data-rawheight=&536& class=&origin_image zh-lightbox-thumb& width=&838& data-original=&https://pic1.zhimg.com/v2-17b9e68effc23f3bd170ebefd94b9c9d_r.jpg&&&/figure&
上图为基于Ray-Tracing的AO计算模型. 红色的射线表示V = 1, 绿色的射线表示V = 0. &/p&&p&
那么我们能否&b&Trade Off&/b&, 用差一点的渲染结果来获得更高的运行效率呢? 答案是肯定的, 而且方法还远不止一种. 本文将重点放在SSAO上. &/p&&p&
顾名思义, &Screen Space&意味着SSAO并不是场景的预处理, 而是屏幕后期处理. 其原理是在片元着色器中对于屏幕上的每个像素模拟若干个位置随机的采样点, 用被遮蔽的采样点数量百分比来&b&近似&/b&表示光照强度系数. &/p&&br&&h2&SSAO的实现&/h2&&p&
SSAO的实现可分为三个步骤: 计算AO, 模糊/滤波, 与Color Buffer混合. &/p&&p&&b&1. 计算AO&/b&&/p&&p&计算AO的核心问题在于&b&如何取采样点&/b&并判断&b&这些采样点是否被遮蔽&/b&. 我们首先解决第一个问题. 在此我们使用一种指向法线方向的半球形采样块(Sample Kernel), 并在采样块中生成采样点. &b&距离原点越远的点, AO贡献越小&/b&. 采样块如下图所示: &/p&&figure&&img src=&https://pic2.zhimg.com/v2-0e8ef56914ab7bdedebbbd23_b.jpg& data-rawwidth=&380& data-rawheight=&218& class=&content_image& width=&380&&&/figure&&br&&p&
那么我们转入第二个问题: 如何判断下图中的采样点遮蔽情况呢? &/p&&p&&figure&&img src=&https://pic1.zhimg.com/v2-eab494f8c71bf1fec82bd9_b.jpg& data-rawwidth=&352& data-rawheight=&184& class=&content_image& width=&352&&&/figure&
(涂黑的点处在几何体表面内部, 因此判定为被遮蔽)&/p&&p&
一种方法是将采样点全部投影到View Plane上, 相当于获取采样点的UV坐标, 并同时获取Depth Buffer中该UV坐标处的深度值. 随后比较采样点的深度和场景中该点的深度. 如果采样点的深度更大, 说明其被场景遮蔽. 最终将所有采样点的AO贡献求和, 即是该点的AO值. 计算公式如下: &/p&&img src=&https://www.zhihu.com/equation?tex=AO_%7Bn%7D%28p%29+%3D+%5Cfrac%7B1%7D%7Bn%7DC%28p%2C+s_%7Bi%7D+%29++& alt=&AO_{n}(p) = \frac{1}{n}C(p, s_{i} )
& eeimg=&1&&&br&&img src=&https://www.zhihu.com/equation?tex=C%28p%2C+s_%7Bi%7D+%29+%3D+V%28p%2C+s_%7Bi%7D+%29cos%28s_%7Bi%7D+-+p%2C+n_%7Bp%7D++%29D%28s_%7Bi%7D+-+p+%29& alt=&C(p, s_{i} ) = V(p, s_{i} )cos(s_{i} - p, n_{p}
)D(s_{i} - p )& eeimg=&1&&&br&&p&
其中, 函数V前面已经介绍过. 函数D是一个[0, 1]之间的单调递减函数, 距离原点越近的采样点对AO的贡献越大. 一般使用指数函数. &/p&&p&&figure&&img src=&https://pic4.zhimg.com/v2-e53ec439e47cb72ef75ce_b.jpg& data-rawwidth=&1336& data-rawheight=&835& class=&origin_image zh-lightbox-thumb& width=&1336& data-original=&https://pic4.zhimg.com/v2-e53ec439e47cb72ef75ce_r.jpg&&&/figure&上图为原图&/p&&p&&figure&&img src=&https://pic1.zhimg.com/v2-425f9d8a84d9fa052ba3f7ace46a1d1a_b.jpg& data-rawwidth=&2880& data-rawheight=&1800& class=&origin_image zh-lightbox-thumb& width=&2880& data-original=&https://pic1.zhimg.com/v2-425f9d8a84d9fa052ba3f7ace46a1d1a_r.jpg&&&/figure&上图为求得的AO值. 颜色越深代表AO越大. &/p&&p&以下是循环采样部分代码(ii为循环变量): &/p&&div class=&highlight&&&pre&&code class=&language-glsl&&&span&&/span&&span class=&n&&half3&/span& &span class=&n&&randomDirection&/span& &span class=&o&&=&/span& &span class=&n&&RandomSample&/span&&span class=&p&&[&/span& &span class=&n&&ii&/span& &span class=&p&&];&/span&
&span class=&n&&float2&/span& &span class=&n&&uv_offset&/span& &span class=&o&&=&/span& &span class=&n&&randomDirection&/span&&span class=&p&&.&/span&&span class=&n&&xy&/span& &span class=&o&&*&/span& &span class=&n&&scale&/span&&span class=&p&&;&/span&
&span class=&k&&float&/span& &span class=&n&&randomDepth&/span& &span class=&o&&=&/span& &span class=&n&&depth&/span& &span class=&o&&-&/span& &span class=&p&&(&/span& &span class=&n&&randomDirection&/span&&span class=&p&&.&/span&&span class=&n&&z&/span& &span class=&o&&*&/span& &span class=&n&&_Radius&/span& &span class=&p&&);&/span&
&span class=&k&&float&/span& &span class=&n&&sampleDepth&/span&&span class=&p&&;&/span&
&span class=&n&&float3&/span& &span class=&n&&sampleNormal&/span&&span class=&p&&;&/span&
&span class=&n&&DecodeDepthNormal&/span& &span class=&p&&(&/span& &span class=&n&&tex2D&/span& &span class=&p&&(&/span& &span class=&n&&_CameraDepthNormalsTexture&/span&&span class=&p&&,&/span& &span class=&n&&i&/span&&span class=&p&&.&/span&&span class=&n&&uv&/span& &span class=&o&&+&/span& &span class=&n&&uv_offset&/span& &span class=&p&&),&/span& &span class=&n&&sampleDepth&/span&&span class=&p&&,&/span& &span class=&n&&sampleNormal&/span& &span class=&p&&);&/span&
&span class=&n&&sampleDepth&/span& &span class=&o&&*=&/span& &span class=&n&&_ProjectionParams&/span&&span class=&p&&.&/span&&span class=&n&&z&/span&&span class=&p&&;&/span&
&span class=&k&&float&/span& &span class=&n&&diff&/span& &span class=&o&&=&/span&
&span class=&n&&saturate&/span&&span class=&p&&(&/span& &span class=&n&&randomDepth&/span& &span class=&o&&-&/span& &span class=&n&&sampleDepth&/span& &span class=&p&&);&/span&
&span class=&k&&if&/span& &span class=&p&&(&/span& &span class=&n&&diff&/span& &span class=&o&&&&/span& &span class=&n&&_ZDiff&/span& &span class=&p&&)&/span&
&span class=&n&&occlusionAmount&/span& &span class=&o&&+=&/span& &span class=&n&&pow&/span& &span class=&p&&(&/span&&span class=&mi&&1&/span& &span class=&o&&-&/span& &span class=&n&&diff&/span&&span class=&p&&,&/span& &span class=&n&&_Attenuation&/span&&span class=&p&&);&/span&
&/code&&/pre&&/div&&p&
到此我们发现了一个问题: 上面求得的AO结果非常不理想. 图中有非常明显的条带状阴影, 给人的感觉像是在图上轻轻地抹了一层均匀的&b&&i&&u&油漆&/u&&/i&&/b&. 产生这种现象的原因很简单 - &b&为了满足实时渲染的性能要求, 我们必须限制采样点的数目. &/b&&/p&&p&
但是, 对于这种现象我们有一个Trick ---- 可以引入&b&噪声&/b&, 将每个采样点以原点法线方向为旋转轴旋转随机的角度. 这样的新采样点会变得极其不规则, 更加离散化. &b&将低频的条纹转化成高频的噪声. &/b&&/p&&div class=&highlight&&&pre&&code class=&language-glsl&&&span&&/span&&span class=&n&&half3&/span& &span class=&n&&randomVector&/span& &span class=&o&&=&/span& &span class=&n&&tex2D&/span& &span class=&p&&(&/span& &span class=&n&&_RandomTexture&/span&&span class=&p&&,&/span& &span class=&n&&i&/span&&span class=&p&&.&/span&&span class=&n&&uv_random&/span& &span class=&p&&).&/span&&span class=&n&&xyz&/span& &span class=&o&&*&/span& &span class=&mf&&2.0&/span& &span class=&o&&-&/span& &span class=&mf&&1.0&/span&&span class=&p&&;&/span&
&span class=&n&&half3&/span& &span class=&n&&randomDirection&/span& &span class=&o&&=&/span& &span class=&n&&reflect&/span& &span class=&p&&(&/span& &span class=&n&&RandomSample&/span&&span class=&p&&[&/span& &span class=&n&&ii&/span& &span class=&p&&],&/span& &span class=&n&&randomVector&/span& &span class=&p&&);&/span&
&/code&&/pre&&/div&&p&&figure&&img src=&https://pic4.zhimg.com/v2-985dfe06f533bc25ee3ce3_b.jpg& data-rawwidth=&2880& data-rawheight=&1800& class=&origin_image zh-lightbox-thumb& width=&2880& data-original=&https://pic4.zhimg.com/v2-985dfe06f533bc25ee3ce3_r.jpg&&&/figure&上图为引入随机噪声后的采样结果. 我们发现&油漆&变成了&&b&&i&&u&沙子&/u&&/i&&/b&&. &/p&&br&&p&&b&2. 模糊/滤波&/b&&/p&&blockquote&&p&&i&&油漆&好还是&沙子&好? &/i&&/p&&p&&i&都不好! &/i&&/p&&/blockquote&&p&
&油漆&显得平淡无奇, &沙子&让人眼花缭乱. 中国人讲究中庸之道, 也就是说 ---- &b&我们需要一个&中频&的AO! &/b&&/p&&p&
在此介绍两种方法. 第一种方法是直接模糊. 比较常用的是高斯模糊. 关于高斯模糊的资料有很多, 本文不再赘述. &/p&&p&
第二种方法在采样原理上和高斯模糊别无二致, 只是采样系数由静态变为动态: 原点与采样点的UV坐标距离, 法线和深度关系共同决定采样系数, 距离越远采样系数越小, 法线和深度的差距越大则采样系数也越大. 这样的模糊使得结果更加趋近于中频, 进一步减弱了闪烁(Flickering)的效果. &/p&&br&&p&3. 与Color Buffer混合. &/p&&p&
一般加入Gamma Correction使得阴影更有层次感, 即最终结果为: &/p&&div class=&highlight&&&pre&&code class=&language-glsl&&&span&&/span&&span class=&n&&tex2D&/span& &span class=&p&&(&/span& &span class=&n&&_MainTex&/span&&span class=&p&&,&/span& &span class=&n&&i&/span&&span class=&p&&.&/span&&span class=&n&&uv&/span& &span class=&p&&)&/span& &span class=&o&&*&/span& &span class=&n&&pow&/span& &span class=&p&&(&/span& &span class=&p&&(&/span& &span class=&mi&&1&/span& &span class=&o&&-&/span& &span class=&n&&occlusion&/span& &span class=&p&&),&/span& &span class=&mf&&2.2&/span& &span class=&p&&);&/span&
&/code&&/pre&&/div&&figure&&img src=&https://pic1.zhimg.com/v2-e2a74d972de2e725fa22b2a_b.jpg& data-rawwidth=&1336& data-rawheight=&835& class=&origin_image zh-lightbox-thumb& width=&1336& data-original=&https://pic1.zhimg.com/v2-e2a74d972de2e725fa22b2a_r.jpg&&&/figure&&p&
上图为SSAO处理后的最终结果. &/p&&br&&h2&SSAO的问题与优化策略&/h2&&p&
SSAO技术的基本原理已经介绍完了, 下面我们来谈谈SSAO可能遇到的问题, 以及相应的解决方案: &/p&&p&&b&1. 采样块的问题&/b&&/p&&p&上文的SSAO实现方案其实是假定了使用Deferred Rendering, 深度和法线都可以非常容易得获取到, 因此我们的半球形采样块可以沿着顶点的法线方向摆放. 但是如果获取法线比较困难, 我们可以将半球形退化成球形, 这也正是2007年&i&Crysis&/i&中SSAO的实现方案. &/p&&p&&figure&&img src=&https://pic2.zhimg.com/v2-ee4a82aa0d9fc507c413_b.jpg& data-rawwidth=&352& data-rawheight=&184& class=&content_image& width=&352&&&/figure&
如上图所示, 采样块的摆放与法线无关, 采样点遍布整个球形中. 这种方法的效果自然不如半球形采样块. &/p&&br&&p&&b&2. 重复计算的问题&/b&&/p&&p&
SSAO最为耗时的操作是模拟多个采样点并计算其AO贡献值, 因此我们应该想办法避免重复计算, 尽量使用以前的结果. 这里可以使用&b&Reverse Reprojection&/b&(反向二次投影), 保存上一帧的AO计算信息, 使得当前帧中相对上一帧没有变化的点可以利用旧的AO信息, 避免重复计算. 这种方式称为SSAO with Temporal Coherence(&b&时间相干性&/b&), 简称为&b&&u&TSSAO&/u&&/b&. 具体的实现方式将在下文中进行阐述. &/p&&br&&p&&b&3. 滤波与精确计算之间的矛盾&/b&&/p&&p&
这也是一个Trade Off ---- 反正运算结果都是要套用滤波来过滤掉噪点, 那么最开始计算的时候就可以想办法在保证质量不受太大影响的前提下, 尽量提升效率. 举一个例子: &/p&&blockquote&假设有两个面饼师傅, 第一个师傅的工作是揉出来5个直径为1.2cm的面球, 第二个师傅的工作是把这5个面球放在一起拍成一个面饼. &/blockquote&&p&
我们不讨论这种工作方式是否合理, 只是在此情况下第一个师傅确实不用对1.2cm吹毛求疵, 只要差不多就行了. &/p&&p&
明白了这个道理, 就会发现可以在计算AO的时候使用&b&&u&降采样&/u&&/b&, 这样能成平方倍地降低SSAO的时间复杂度. 关于降采样网上有很多资料, 我的专栏&a href=&https://zhuanlan.zhihu.com/p/?refer=MeowShader& class=&internal&&第一篇文章&/a&也对此有过介绍. &/p&&br&&h2&TSSAO&/h2&&p&&b&1. Reverse Reprojection&/b&&/p&&p&
交替使用两张Render Texture, 一张代表当前帧, 另一张代表上一帧. 对于当前帧上任何一个Pixel都可以根据其UV坐标重建其世界坐标, 然后根据上一帧的View-Projection矩阵的逆矩阵来转化成上一帧的相应UV坐标. 如果两帧上对应的Pixel的Depth与世界坐标差距不大, 那么当前帧就可以利用上一帧对应Pixel的信息, 免去重复计算. &/p&&p&
对于静态场景我们有如下公式: &/p&&img src=&https://www.zhihu.com/equation?tex=t_%7Bold_%7Bx%5E%7B%27%7D%2C+y%5E%7B%27%7D%2C+z%5E%7B%27%7D+%7D+%7D+%3D+P_%7Bold%7DV_%7Bold%7DV_%7Bnew%7D%5E%7B-1%7D+P_%7Bnew%7D%5E%7B-1%7D+t_%7Bnew_%7Bx%2C+y%2C+z+%7D+%7D& alt=&t_{old_{x^{'}, y^{'}, z^{'} } } = P_{old}V_{old}V_{new}^{-1} P_{new}^{-1} t_{new_{x, y, z } }& eeimg=&1&&&br&&p&
t表示NDC坐标, P表示Projection矩阵, V表示View矩阵. &/p&&div class=&highlight&&&pre&&code class=&language-glsl&&&span&&/span&&span class=&k&&inline&/span& &span class=&n&&float4&/span& &span class=&n&&UV2WorldPos&/span&&span class=&p&&(&/span&&span class=&n&&float2&/span& &span class=&n&&uv&/span&&span class=&p&&,&/span& &span class=&n&&float4x4&/span& &span class=&n&&iv&/span&&span class=&p&&)&/span&
&span class=&p&&{&/span&
&span class=&k&&float&/span& &span class=&n&&depth&/span& &span class=&o&&=&/span& &span class=&n&&SAMPLE_DEPTH_TEXTURE&/span&&span class=&p&&(&/span&&span class=&n&&_CameraDepthTexture&/span&&span class=&p&&,&/span& &span class=&n&&uv&/span&&span class=&p&&);&/span&
&span class=&n&&float2&/span& &span class=&n&&invClipSize&/span& &span class=&o&&=&/span& &span class=&n&&float2&/span&&span class=&p&&(&/span&&span class=&n&&_CurP&/span&&span class=&p&&.&/span&&span class=&n&&_11&/span&&span class=&p&&,&/span& &span class=&n&&_CurP&/span&&span class=&p&&.&/span&&span class=&n&&_22&/span&&span class=&p&&);&/span&
&span class=&n&&float2&/span& &span class=&n&&spos&/span& &span class=&o&&=&/span& &span class=&p&&(&/span&&span class=&n&&uv&/span& &span class=&o&&*&/span& &span class=&mi&&2&/span& &span class=&o&&-&/span& &span class=&mi&&1&/span&&span class=&p&&)&/span& &span class=&o&&/&/span& &span class=&n&&invClipSize&/span&&span class=&p&&;&/span&
&span class=&k&&float&/span& &span class=&n&&depthEye&/span& &span class=&o&&=&/span& &span class=&n&&LinearEyeDepth&/span&&span class=&p&&(&/span&&span class=&n&&depth&/span&&span class=&p&&);&/span&
&span class=&n&&float3&/span& &span class=&n&&vpos&/span& &span class=&o&&=&/span& &span class=&n&&float3&/span&&span class=&p&&(&/span&&span class=&n&&spos&/span&&span class=&p&&,&/span& &span class=&o&&-&/span&&span class=&mi&&1&/span&&span class=&p&&)&/span& &span class=&o&&*&/span& &span class=&n&&depthEye&/span&&span class=&p&&;&/span&
&span class=&k&&return&/span& &span class=&n&&mul&/span&&span class=&p&&(&/span&&span class=&n&&iv&/span&&span class=&p&&,&/span& &span class=&n&&float4&/span&&span class=&p&&(&/span&&span class=&n&&vpos&/span&&span class=&p&&,&/span& &span class=&mf&&1.0&/span&&span class=&p&&));&/span&
&span class=&p&&}&/span&
&span class=&k&&inline&/span& &span class=&n&&float2&/span& &span class=&n&&WorldPos2UV&/span&&span class=&p&&(&/span&&span class=&n&&float4&/span& &span class=&n&&worldPos&/span&&span class=&p&&,&/span& &span class=&n&&float4x4&/span& &span class=&n&&vp&/span&&span class=&p&&)&/span&
&span class=&p&&{&/span&
&span class=&n&&float4&/span& &span class=&n&&vpos&/span& &span class=&o&&=&/span& &span class=&n&&mul&/span&&span class=&p&&(&/span&&span class=&n&&vp&/span&&span class=&p&&,&/span& &span class=&n&&worldPos&/span&&span class=&p&&);&/span&
&span class=&n&&float2&/span& &span class=&n&&projOffset&/span& &span class=&o&&=&/span& &span class=&n&&float2&/span&&span class=&p&&(&/span&&span class=&n&&_CurP&/span&&span class=&p&&.&/span&&span class=&n&&_13&/span&&span class=&p&&,&/span& &span class=&n&&_CurP&/span&&span class=&p&&.&/span&&span class=&n&&_23&/span&&span class=&p&&);&/span&
&span class=&n&&float2&/span& &span class=&n&&screenPos&/span& &span class=&o&&=&/span& &span class=&n&&vpos&/span&&span class=&p&&.&/span&&span class=&n&&xy&/span& &span class=&o&&/&/span& &span class=&n&&vpos&/span&&span class=&p&&.&/span&&span class=&n&&w&/span& &span class=&o&&+&/span& &span class=&n&&projOffset&/span&&span class=&p&&;&/span&
&span class=&k&&return&/span& &span class=&mf&&0.5&/span& &span class=&o&&*&/span& &span class=&n&&screenPos&/span& &span class=&o&&+&/span& &span class=&mf&&0.5&/span&&span class=&p&&;&/span&
&span class=&p&&}&/span&
&span class=&k&&inline&/span& &span class=&n&&float2&/span& &span class=&n&&GetMotionVector&/span&&span class=&p&&(&/span&&span class=&n&&float2&/span& &span class=&n&&uv&/span&&span class=&p&&)&/span&
&span class=&p&&{&/span&
&span class=&n&&float4&/span& &span class=&n&&worldPos&/span& &span class=&o&&=&/span& &span class=&n&&UV2WorldPos&/span&&span class=&p&&(&/span&&span class=&n&&uv&/span&&span class=&p&&,&/span& &span class=&n&&_CurIV&/span&&span class=&p&&);&/span&
&span class=&n&&float2&/span& &span class=&n&&curUv&/span& &span class=&o&&=&/span& &span class=&n&&WorldPos2UV&/span&&span class=&p&&(&/span&&span class=&n&&worldPos&/span&&span class=&p&&,&/span& &span class=&n&&_CurVP&/span&&span class=&p&&);&/span&
&span class=&n&&float2&/span& &span class=&n&&preUv&/span& &span class=&o&&=&/span& &span class=&n&&WorldPos2UV&/span&&span class=&p&&(&/span&&span class=&n&&worldPos&/span&&span class=&p&&,&/span& &span class=&n&&_PrevVP&/span&&span class=&p&&);&/span&
&span class=&k&&return&/span&
&span class=&n&&curUv&/span& &span class=&o&&-&/span& &span class=&n&&preUv&/span&&span class=&p&&;&/span&
&span class=&p&&}&/span&
&/code&&/pre&&/div&&br&&p&
但是对于动态场景上述公式不再成立. 因为SSAO要做到Scene Independent, 同时还必须记录前后两帧的关联, 因此在此我们可以用一个Render Texture单独记录每个像素在前一帧与当前帧的世界坐标的差. 具体的原因详见下文. &/p&&br&&p&&b&2. 新的AO计算公式&/b&&/p&&p&
在TSSAO中我们使用两个Render Texture分别作为上一帧和当前帧的AO Buffer. 首先计算当前帧的AO贡献值: &/p&&img src=&https://www.zhihu.com/equation?tex=C_%7Bt+%2B+1%7D+%28p%29+%3D+%5Cfrac%7B1%7D%7Bk%7D+%5Csum_%7Bi+%3D+n_%7Bt%7D%28p%29+%2B+1+%7D%5E%7Bn_%7Bt%7D%28p%29+%2B+k%7D%7BC%28p%2C+s_%7Bi%7D%29%7D& alt=&C_{t + 1} (p) = \frac{1}{k} \sum_{i = n_{t}(p) + 1 }^{n_{t}(p) + k}{C(p, s_{i})}& eeimg=&1&&&p&&img src=&https://www.zhihu.com/equation?tex=n_%7Bt%7D%28p%29& alt=&n_{t}(p)& eeimg=&1&&表示的是之前已经计算过的采样点数量, k表示每一帧应该计算的采样点数量. &/p&&p&
随后, 利用当前帧和上一帧的AO贡献值共同计算当前帧的AO值: &/p&&img src=&https://www.zhihu.com/equation?tex=AO_%7Bt%2B1%7D%28p%29+%3D+%5Cfrac%7Bn_%7Bt%7D%28p%29AO_%7Bt%7D%28p_%7Bold%7D%29+%2B+kC_%7Bt%2B1%7D%28p%29%7D%7Bn_%7Bt%7D%28p%29%2Bk%7D+& alt=&AO_{t+1}(p) = \frac{n_{t}(p)AO_{t}(p_{old}) + kC_{t+1}(p)}{n_{t}(p)+k} & eeimg=&1&&&br&&p&
最后, 更新&img src=&https://www.zhihu.com/equation?tex=n_%7Bt%7D%28p%29& alt=&n_{t}(p)& eeimg=&1&&的值: &/p&&img src=&https://www.zhihu.com/equation?tex=n_%7Bt%2B1%7D%28p%29%3Dmin%28n_%7Bt%7D%28p%29%2Bk%2C+n_%7Bmax%7D%29& alt=&n_{t+1}(p)=min(n_{t}(p)+k, n_{max})& eeimg=&1&&&p&
在此说明一下为什么要设置n的上限. 这里主要有两个原因, 第一是如果之前的计算结果不老化, 当前帧的AO贡献值会越来越小, 算法的反应会越来越慢. 第二是反向二次投影本身是有误差的, 随着投影次数的增加误差会变得非常大, 因此必须限制被使用的结果数量, 适当舍弃掉过老的数据. &/p&&br&&p&&b&3. 检测不合法像素&/b&&/p&&p&
很容易想到的一个判定条件是深度检测: 如果新旧两个像素的深度差距过大, 那么说明场景 已经改变, 当前像素的AO值已经不正确, 必须全部舍弃. 相对深度关系检测条件如下: &/p&&img src=&https://www.zhihu.com/equation?tex=%7C1+-+%5Cfrac%7Bd_%7Bnew%7D%7D%7Bd_%7Bold%7D%7D+%7C+%3C+%5Cvarepsilon+& alt=&|1 - \frac{d_{new}}{d_{old}} | & \varepsilon & eeimg=&1&&&p&
但是, SSAO考虑的不仅仅是当前点, 还有它周边的&b&环境&/b&. 举一个例子: 在一个静态的地面上放置着一个动态的立方体. 这个立方体随着时间不规则运动. 地面与立方体地面棱边的外交界处的AO值自然明显高出地面上其他点的值, 但是立方体的移动会使得地面上相应区域的AO值不再有效 ---- 虽然地面没有在动, 地面上的点能够通过深度关系检测. &/p&&figure&&img src=&https://pic2.zhimg.com/v2-f384c75ce3b305bece332_b.jpg& data-rawwidth=&400& data-rawheight=&308& class=&content_image& width=&400&&&/figure&&br&&p&
这里我们用到了另一个Trick ---- 我们在计算当前帧AO的循环中可以同时做出以下判定: &/p&&img src=&https://www.zhihu.com/equation?tex=%7C+%7C+s_%7Bi%7D+-+p+%7C+-+%7C+s_%7Bi_%7Bold%7D%7D+-+p_%7Bold%7D+%7C+%7C+%3C+%5Cvarepsilon+& alt=&| | s_{i} - p | - | s_{i_{old}} - p_{old} | | & \varepsilon & eeimg=&1&&&p&
只要有一个采样点不满足上述条件, 则说明其对应原点的周边环境已经发生改变, 其AO值自然也应该重新计算. 对于&img src=&https://www.zhihu.com/equation?tex=S_%7Bi_%7Bold%7D%7D& alt=&S_{i_{old}}& eeimg=&1&&的计算方法也很简单: 根据&img src=&https://www.zhihu.com/equation?tex=s_%7Bi%7D& alt=&s_{i}& eeimg=&1&&和在第一步中记录的世界坐标的差就可以直接得出了. &/p&&p&
只要像素被判定为不合法, 则其&img src=&https://www.zhihu.com/equation?tex=n_%7Bt%7D%28p%29& alt=&n_{t}(p)& eeimg=&1&&会被重置为0, 即之前的所有AO运算结果全部舍弃. &/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&int ww =
if ( abs ( 1 - texelDepth / preDepth ) &= EPS )
float scale = _Parameters.x /
for ( int ii = 0; ii & _SampleC ii++ )
float3 randomDirection = RAND_SAMPLES [ w + ii ];
randomDirection *= -sign ( dot ( texelNormal, randomDirection ) );
randomDirection += texelNormal * 0.3;
float2 uv_offset = randomDirection.xy *
float randomDepth = texelDepth - randomDirection.z * _Parameters.x;
float sampleD
float3 sampleN
GetDepthNormal ( i.uv + uv_offset, sampleDepth, sampleNormal );
sampleDepth *= _ProjectionParams.z;
float diff = randomDepth - sampleD
if ( diff & _Parameters.y )
occlusion += saturate ( cos ( dot ( randomDirection, texelNormal ) ) ) * pow ( 1 - diff, _Parameters.z );
if ( abs ( randomDirection - abs ( randomDirection + GetMotionVector ( i.uv + uv_offset ) - GetMotionVector ( i.uv ) - texelPosition ) ) &= EPS )
&/code&&/pre&&/div&&br&&br&&p&&b&4. 滤波&/b&&/p&&p&
可以在SSAO的滤波基础上加上收敛度的条件. 收敛度定义为: &/p&&img src=&https://www.zhihu.com/equation?tex=conv+%3D+min+%28n_%7Bt%7D%28p%29%2C+n_%7Bmax%7D%29+%2F+n_%7Bmax%7D& alt=&conv = min (n_{t}(p), n_{max}) / n_{max}& eeimg=&1&&&p&
收敛度越大, 说明当前像素越为&安全&, 随着时间的改变越小, 因此采样系数也越大. &/p&&br&&h2&Selective Temporal Filtering (STF)&/h2&&p&
这项技术应用在了BattleField3中. 首先它是基于TSSAO的, 不同的是其AO计算方式: 在当前帧的AO贡献值和历史数据中间做插值. &/p&&p&
这样做带来的一个小问题就是&老化& ---- 旧的数据不能及时清理出去, 这就导致场景移动比较快的时候, AO Buffer会存在鬼影的问题. &/p&&p&
但是一个更为严重的问题是&闪烁& ---- BattleField3的场景中有大量花草树木, 树叶的晃动使得大量像素被频繁检测为失效, 重新计算AO, 这与未失效的部分构成了鲜明的对比. &/p&&p&
DICE的解决方案非常Trick: 他们发现存在鬼影的像素和存在闪烁的像素是互斥的. 因此他们想办法甄别这两种像素, 并对于可能产生鬼影的像素关掉Temporal Filtering. 因此这项技术被称为Selective Temporal Filtering. &/p&&p&
具体的方法是检测连续性: 对于任何一个Pixel, 连续在x或y方向选择两个像素, 判断这三个像素的深度是否连续. 如果连续则可能产生鬼影, 否则可能产生闪烁. &/p&&figure&&img src=&https://pic2.zhimg.com/v2-50abf7a4efaf_b.jpg& data-rawwidth=&1366& data-rawheight=&776& class=&origin_image zh-lightbox-thumb& width=&1366& data-original=&https://pic2.zhimg.com/v2-50abf7a4efaf_r.jpg&&&/figure&&p&&br&
最后将所有闪烁的像素按照4x4放大, 圈定进行Temporal Filter的区域: &/p&&figure&&img src=&https://pic2.zhimg.com/v2-98fc149a1cef5bbcaf53_b.jpg& data-rawwidth=&1336& data-rawheight=&753& class=&origin_image zh-lightbox-thumb& width=&1336& data-original=&https://pic2.zhimg.com/v2-98fc149a1cef5bbcaf53_r.jpg&&&/figure&&figure&&img src=&https://pic3.zhimg.com/v2-a0a41d5ad7facb62aa15_b.jpg& data-rawwidth=&1336& data-rawheight=&751& class=&origin_image zh-lightbox-thumb& width=&1336& data-original=&https://pic3.zhimg.com/v2-a0a41d5ad7facb62aa15_r.jpg&&&/figure&&br&&h2&后记&/h2&&p&
SSAO原理并不复杂, 只是在实际应用场景中会有各种各样的Trick以应对个性化的需要. 文中主要讲解了SSAO的基本原理与TSSAO的优化原理, 并举了BattleField3的STF为例. &/p&&br&&h2&引用&/h2&&br&&p&Nehab, Sander, Lawrence, Tatarchuk, Isidoro.
Accelerating Real-Time Shading with Reverse Reprojection Caching. In ACM
SIGGRAPH/Eurographics Symposium on Graphics Hardware 2007.&/p&&br&&p&Mattausch, Oliver, Daniel Scherzer, and Michael Wimmer. &High‐Quality Screen‐Space Ambient Occlusion using Temporal Coherence.& &i&Computer Graphics Forum&/i&. Vol. 29. No. 8. Blackwell Publishing Ltd, 2010. &/p&&br&&p&BAVOIL L., SAINZ M.: Multi-layer dual-resolution
screen-space ambient occlusion. In SIGGRAPH ’09: SIGGRAPH
2009: Talks (New York, NY, USA, 2009), ACM, pp. 1–1. 3, 7&/p&&br&&p&Screen space ambient occlusion. (2016, December 29). In &i&Wikipedia, The Free Encyclopedia&/i&. Retrieved 19:24, January 28, 2017, from &a class=& wrap external& href=&https://link.zhihu.com/?target=https%3A//en.wikipedia.org/w/index.php%3Ftitle%3DScreen_space_ambient_occlusion%26oldid%3D& target=&_blank& rel=&nofollow noreferrer&&Screen space ambient occlusion&/a&&/p&&p&Bavoil, L., & Sainz, M. (2008). Screen space ambient occlusion. &i&NVIDIA developer information: &a href=&https://link.zhihu.com/?target=http%3A//developers& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&developers&/span&&span class=&invisible&&&/span&&/a&. nvidia. com&/i&, &i&6&/i&.&/p&
写在前面 专栏断更了这么久, 实在惭愧. 这段时间又是期末考试又是回家过节, 实在是没时间整理干货来分享. 之前说好的不及时更新就赔钱, @Cathy Chen童鞋已经拿到10大洋的红包了. 不过从今天开始我就有大把的时间来继续研究图形学啦, 因此会保证更新速度的 ~…
对照CryEngine官方的页面 &a href=&//link.zhihu.com/?target=http%3A//cryengine.com/features/visuals& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&CRYENGINE Visuals&/a& 说一下。完全基于个人知识和理解,本人并没有用过CryEngine,最近也没有再做渲染方面的工作,所以如果有不准确的地方,轻喷。&br&&br&&b&Physically Based Shading &/b&&b& / 基于物理的着色&/b&&br&传统3D游戏着色通常是美术对着一个shader反复调数值,而参数本身并没有真实的物理含义,光的能量也不守恒,只是调整过程中美术人员形成一种“直觉”,达到审美上能接受的程度就可以。使用真实的物理参数,意味着“一定程度”上使用正确的物理单位,光源能量,表面反射,折射,能量吸收等等材质的物理属性是和现实中相对应的。这样允许实景测量BRDF已达到更精确的效果。材质的表现会更真实更统一。&br&扩展阅读:&a href=&//link.zhihu.com/?target=http%3A//blog.selfshadow.com/publications/s2013-shading-course/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&SIGGRAPH 2013 Course: Physically Based Shading in Theory and Practice&/a&&br&&br&&b&Realistic Vegetation Rendering / 真实的植被渲染&/b&&br&植被的特点,一,需要大量频繁的出现,二,经常以群落形式出现,三,需要一定程度上响应物理(不需要很精确),四,细节多,远近拉伸尺度大。这里面既要考虑效果和性能又要考虑美术在布置场景时候的易用性,这中间涉及到大量的优化和一些专门针对植被的算法,所以植被渲染/模拟经常当做一个单独系统来处理。CryEngine在这方面应该是做了大量的工作。&br&扩展阅读:&a href=&//link.zhihu.com/?target=http%3A//www.kevinboulanger.net/grass.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Rendering Grass Terrains in Real-Time with Dynamic Lighting&/a& (不是说CryEngine用了这个,只是感受一下植被系统需要解决的一些特殊问题)&br&&br&&b&Realistic Water System / 真实的水流系统&/b&&br&流体模拟一直是计算机图形中一个很难的问题,水流模拟是其中一个子集。很多能够提供真实流体模拟的系统计算一帧可能都需要10几分钟(其实是上不封顶的),在游戏中完全不可取。所以实时的水流模拟需要各种特殊的优化,而不是真的去解流体的偏微分方程。从早年的动态高度图到后来的浅水方程再到现在GPU上一定程度的物理模拟,然后再和物理,渲染结合起来,这个过程中的复杂度还是很高的。&br&扩展阅读:&a href=&//link.zhihu.com/?target=http%3A//www.cs.columbia.edu/%7Ekeenan/Projects/GPUFluid/& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://www.&/span&&span class=&visible&&cs.columbia.edu/~keenan&/span&&span class=&invisible&&/Projects/GPUFluid/&/span&&span class=&ellipsis&&&/span&&/a& (紧紧是复杂度参考)&br&&br&&b&Real-time Dynamic Water Caustics / 实时动态水流Caustics&/b&&br&(不知道怎么翻译caustics
╮(╯Д╰)╭)&br&类似水流,玻璃球等曲面透明物体在现实中会形成caustics,就是出现在物体后方的不规则光斑。形成原因是光线在曲面经过折射后被聚焦到了一起,导致明暗不一致。在基于raytracing一类的离线渲染器这种效果是很好模拟的,但是传统3D引擎的渲染方法不考虑光线的轨迹,所以模拟caustics一直是一个比较麻烦的问题。实时,动态,多光源的模拟caustics需要很大计算量,而且不同类型水流模拟(瀑布 vs 湖水表面),需要用到不同的方式。CryEngine有一套“统一的”“准确的”造假caustics的方法。&br&&br&&b&Real-time Local Reflections / 实时反射&/b&&br&反射一直是3D引擎比较难处理的问题。一般是在反射平面重新渲染一遍场景,速度慢而且需要反射平面不能有弯曲。CryEngine用的是screen-space local reflection,也是一篇老paper了,不过可能自己做了不少优化。基本思路是利用延迟渲染所保存的屏幕像素的法向量在屏幕空间做一个ray cast,计算反射的内容。这样避免的“平面”的限制,也避免的重新渲染场景。&br&范例实现:&a href=&//link.zhihu.com/?target=http%3A//www.gamedev.net/blog/1323/entry-2254101-real-time-local-reflections/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Real-Time Local Reflections&/a&&br&&br&&b&Controllable Tessellation & Displacement / 可控&/b&&b&曲面&/b&&b&细分和位移&/b&&br&Tessellation和Displacement现在都是相对标准的功能(PC上),有很多不同的技巧,有通过贴图的(displacement mapping),也有硬件支持的(geometry shader)。这类技巧能够有效利用硬件增加物体的细节。所谓“可控”大概就是把各种参数暴露给美术,能够比较方便的控制吧。&br&&br&&b&3D HDR Lens Flares / 3D HDR的镜头炫光&/b&&br&酷炫拽的场景怎么能没有lens flare呢。lens flare形成的原因是强光进入摄像头之后在镜片之间反复反射之后再成像,而强光本身其实是对场景的一种过渡曝光,导致对比度下降。而显示器的亮度区间有限,所以游戏中背景被曝光淹没掉,所谓HDR,就是压缩这个区间,让过渡曝光的部分更清楚一点。&br&扩展阅读:&a href=&//link.zhihu.com/?target=http%3A//resources.mpi-inf.mpg.de/lensflareRendering/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Physically-Based Real-Time Lens Flare Rendering&/a&&br&&br&&b&Multi Anti-Aliasing Modes / 多种抗锯齿模式&/b&&br&说到这儿已经没啥可说了,抗锯齿,老生常谈。本着实时渲染就是抖机灵的原则,抗锯齿简直就是冷笑话大全。&br&扩展阅读:&a href=&//link.zhihu.com/?target=http%3A//iryoku.com/aacourse/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Filtering Approaches for Real-Time Anti-Aliasing&/a&&br&&br&&b&Color Grading / 颜色控制&/b&&br&美图秀秀以后可以说支持color grading。其实就是全屏自定义滤镜。&br&&br&&b&Blend Layer / 融合层&/b&&br&其实就是blend多个贴图到一起... &br&功能文档:&a href=&//link.zhihu.com/?target=http%3A//docs.cryengine.com/display/SDKDOC2/Blend%2BLayer& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Blend Layer - Doc 2. Sandbox Manual&/a&&br&&br&&b&Shadows / 阴影&/b&&br&阴影的算法现在已经比较标准了,CryEngine应该也没有啥革新。现在调整阴影更像一种voodoo,每家都有自己的秘籍,可能参数有点不一样,或者一点点成熟算法的改动,或者加个什么预处理一类的,但是归根结底都差不多。&br&具体技巧可参见图形学的世界名著: &a href=&//link.zhihu.com/?target=http%3A//www.amazon.com/Real-Time-Rendering-Third-Edition-Akenine-Moller/dp//ref%3Dsr_1_1%3Fie%3DUTF8%26qid%3D%26sr%3D8-1%26keywords%3Dreal%2Btime%2Brendering& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Real-Time Rendering, Third Edition: Tomas Akenine-Moller, Eric Haines, Naty Hoffman: 7: Amazon.com: Books&/a&&br&&br&&b&Tiled Deferred Shading / 基于网格的延迟渲染&/b&&br&延迟渲染是比较老的技巧了,也是上面很多功能的基础。所谓Tiled,就是把屏幕切成网格,分格子渲染。这里面的影响主要是性能上的,不细说了。&br&扩展阅读:&a href=&//link.zhihu.com/?target=http%3A//www.cse.chalmers.se/%7Eolaolss/main_frame.php%3Fcontents%3Dpublication%26id%3Dtiled_shading& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Ola Olsson - Chalmers University of Technology&/a&&br&&br&&b&High Quality Motion Blur & Depth of Field / 高质量动作模糊和景深&/b&&br&动作模糊和景深都是现实摄像机的一些“特征”。这个就是对这些功能的模拟。&br&Motion Blur的原因是物体移动速度大于摄像机快门速度,导致物体在同一帧上持续曝光形成一个模糊的影子。准确并且快速的模拟这个有点麻烦。本着实时渲染就是抖机灵的原则,参考中的技巧就是通过每个像素当前的世界坐标和前一帧的世界坐标来模拟像素在两帧之间的“速度”,然后用这个来做blur。&br&Depth of Field是因为摄像机光圈比较大导致曝光时焦点以外物体无法聚焦到底片上而形成的模糊。准确并且快速的模拟这个有点麻烦。本着实时渲染就是抖机灵的原则,一种不准确但是快速的方法是根据深度缓冲里面的值不同程度的模糊每个像素。&br&扩展阅读:&br&动作模糊 &a href=&//link.zhihu.com/?target=http%3A//http.developer.nvidia.com/GPUGems3/gpugems3_ch27.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&GPU Gems 3 - Chapter 27. Motion Blur as a Post-Processing Effect&/a&&br&景深 &a href=&//link.zhihu.com/?target=http%3A//http.developer.nvidia.com/GPUGems/gpugems_ch23.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&GPU Gems - Chapter 23. Depth of Field: A Survey of Techniques&/a&&br&&br&&br&光照那一部分回头再补充,写的好累...&br&&br&CryEngine的技术细节:
&a href=&//link.zhihu.com/?target=http%3A//www.crytek.com/cryengine/presentations/secrets-of-cryengine-3-graphics-technology& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Secrets of CryENGINE 3 Graphics Technology&/a&
对照CryEngine官方的页面
说一下。完全基于个人知识和理解,本人并没有用过CryEngine,最近也没有再做渲染方面的工作,所以如果有不准确的地方,轻喷。 Physically Based Shading
/ 基于物理的着色 传统3D游戏着色通常是美术对着一个sha…
crysis1,2007年发售,之前已经宣传的比较厉害,早先在ubi时候,去看过的同事纷纷以震撼来形容,效果上绝对是非常有话题性,也是显卡公司的宠儿。&br&后来老黄在推shield的时候,也专门提到crysis(不一定是一代了)。&br&然后crytek是很喜欢分享的,所以一些技术,不一定是crytek纯原创,但是是在crytek手里成熟以及发扬广大的,可以说对游戏开发行业产生了深远的影响,下面列一些,也是在开发过程中印象深刻,会学习使用的地方。&br&&b&ssao&/b&:由此行业开始在screenspace系列上去处理ao,产生了很多的探索,也越做越好&br&&figure&&img src=&https://pic3.zhimg.com/50/ce26026dbc6b234edcd0e74_b.jpg& data-rawwidth=&1021& data-rawheight=&649& class=&origin_image zh-lightbox-thumb& width=&1021& data-original=&https://pic3.zhimg.com/50/ce26026dbc6b234edcd0e74_r.jpg&&&/figure&&b&lightshaft&/b&:简单易用效果好,现在很多游戏依旧在使用,前两年才开始逐渐被volume系列取代,不过依旧是性价比超高的技术&br&&figure&&img src=&https://pic4.zhimg.com/50/2edf9a3da31d25d7c14f0cc_b.jpg& data-rawwidth=&1026& data-rawheight=&635& class=&origin_image zh-lightbox-thumb& width=&1026& data-original=&https://pic4.zhimg.com/50/2edf9a3da31d25d7c14f0cc_r.jpg&&&/figure&&b&高质量的海水&/b&&br&理论和工程的典范,或许玩家就是看着挺好的一个水,crytek内部是一个哥们做了很长时间,使用的技术优秀自不必说,早期工作时候,做水,看crytek的水的shader,留下深刻印象,&br&指令上面标注好是编译出来是几条alu汇编指令&br&fft的波形生成,反射,折射,fresnel效果都俱全&br&且带上一定的hack,最后的效果非常的棒。&br&当时自己写的时候,也是把水的标准效果加进去,但就是美感差上一截,看过crytek的理论,工程俱全的shader非常受教。&br&&figure&&img src=&https://pic4.zhimg.com/50/0a2d4a3affc3de80a59569_b.jpg& data-rawwidth=&638& data-rawheight=&358& class=&origin_image zh-lightbox-thumb& width=&638& data-original=&https://pic4.zhimg.com/50/0a2d4a3affc3de80a59569_r.jpg&&&/figure&&br&其余一些技术可能在不同项目里有不同的使用价值,比如motion blur,volumetric技术的做法等等,&br&reference:&br&&a href=&//link.zhihu.com/?target=http%3A//www.crytek.com/download/GDC08_SousaT_CrysisEffects.ppt& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://www.&/span&&span class=&visible&&crytek.com/download/GDC&/span&&span class=&invisible&&08_SousaT_CrysisEffects.ppt&/span&&span class=&ellipsis&&&/span&&/a&&br&&a href=&//link.zhihu.com/?target=http%3A//www.crytek.com/download/SIGGRAPH2007_CrysisDX10.ppt& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://www.&/span&&span class=&visible&&crytek.com/download/SIG&/span&&span class=&invisible&&GRAPH2007_CrysisDX10.ppt&/span&&span class=&ellipsis&&&/span&&/a&&br&&a href=&//link.zhihu.com/?target=http%3A//www.crytek.com/download/Mittring-Finding_NextGen_CryEngine2_Siggraph07.ppt& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://www.&/span&&span class=&visible&&crytek.com/download/Mit&/span&&span class=&invisible&&tring-Finding_NextGen_CryEngine2_Siggraph07.ppt&/span&&span class=&ellipsis&&&/span&&/a&&br&&a href=&//link.zhihu.com/?target=http%3A//www.crytek.com/download/SIGGRAPH2006_RealtimeAtmoFxInGames.ppt& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://www.&/span&&span class=&visible&&crytek.com/download/SIG&/span&&span class=&invisible&&GRAPH2006_RealtimeAtmoFxInGames.ppt&/span&&span class=&ellipsis&&&/span&&/a&
crysis1,2007年发售,之前已经宣传的比较厉害,早先在ubi时候,去看过的同事纷纷以震撼来形容,效果上绝对是非常有话题性,也是显卡公司的宠儿。 后来老黄在推shield的时候,也专门提到crysis(不一定是一代了)。 然后crytek是很喜欢分享的,所以一些技术…
之前在公司内做的培训计划中,基础内容大概有以下这些:&br&&br&三维几何学基础:&br&&ol&&li&三维坐标系统&br&&/li&&li&点与矢量&br&&/li&&li&矩阵与几何变换&br&&/li&&li&四元数与三维旋转&br&&/li&&/ol&&br&实时渲染管道:&br&&ol&&li&应用阶段(场景管理、可见性剔除、分组排序、提交图元)&br&&/li&&li&几何阶段(顶点着色、图元组装、面向剔除、三角形裁剪、透视除法、视区变换)&br&&/li&&li&光栅化阶段(扫瞄转换、scissor/stencil/alpha 测试、alpha 混合)&br&&/li&&/ol&&br&游戏中的光照与阴影:&br&&ol&&li&实时光照分类(正向渲染、延迟渲染、Tile 正向/延迟渲染)&/li&&li&局部光照中的光源(环境光、方向光、点光、聚光、cookie)&br&&/li&&li&阴影(平面阴影、阴影体积、阴影贴图、PCF、VSM、CSM)&br&&/li&&li&全局光照(光照贴图、幅照度环境贴图、球谐函数)&br&&/li&&/ol&&br&材质着色原理与实践:&br&&ol&&li&材质反射模型(渲染方程、BRDF、Lambertian、Phong、Blinn-Phong)&br&&/li&&li&材质着色器编程(环境光、环境遮蔽、发光物体、贴图采样、环境贴图、法线贴图、轮廓光、纹理坐标动画)&br&&/li&&li&特殊着色器(卡通渲染、Kajiya-Kay、??)&br&&/li&&/ol&&br&由浅入深可先看[1],然后[2]。数学方面可参考[3]。&br&&br&[1] 《游戏引擎架构》,叶劲峰译,电子工业出版社,2014&br&[2] Akenine-M?ller, Tomas, Eric Haines, and Naty Hoffman. Real-time rendering Third Edition, CRC Press, 2008.&br&[3] Lengyel, Eric. Mathematics for 3D game programming and computer graphics. Cengage Learning, 2012.
之前在公司内做的培训计划中,基础内容大概有以下这些: 三维几何学基础: 三维坐标系统 点与矢量 矩阵与几何变换 四元数与三维旋转 实时渲染管道: 应用阶段(场景管理、可见性剔除、分组排序、提交图元) 几何阶段(顶点着色、图元组装、面向剔除、三角形…
我也同意没有什么论文是必读的,尤其是所谓“经典”的论文。通常那些经典的技术或者算法,在它们真正普及的时候通常和提出它们的第一篇经典论文比较有比较大的差别。算法本身会多出许多更有意思的变种。拿Shadow Map举例子,后面的PCF,再到VSM,再到PCSS,再到Convolution Shadow Map,Cascaded Shadow map,以及我们最近弄的&a href=&//link.zhihu.com/?target=http%3A//developer.download.nvidia.com/assets/events/GDC15/hybrid_ray_traced_GDC_2015.pdf& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Hybrid Ray Traced Shadow&/a&等等,还有无数的我没有提到的shadow map的变体。每个变体都有自己需要解决的问题,需要掌握的信息量比最初那个算法要大很多。尽管不是每个算法都有必要掌握,但是只有都了解了,才能自己从这些大量的信息的提取出问题的精髓,从而为自己的问题找到最合适的解决方案。&br&而且在学习经典算法的时候,抛开算法逻辑本身,往往更有价值的东西是如何在系统中高效的使用,和系统中其他可能影响到此模块的交互,对算法健壮性的理解(什么时候会fail以及如何处理),高性能的实现和优化,甚至是用户使用时候的体验。这些东西往往在经典论文里都找不到,需要自己实践,和人讨论,积累,来一点一点提高自己的认知。&br&歪了一下楼,硬要我说一篇经典的,我肯定选Eric Veach的thesis:&a href=&//link.zhihu.com/?target=http%3A//graphics.stanford.edu/papers/veach_thesis/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Robust Monte Carlo Methods for Light Transport Simulation&/a&
我也同意没有什么论文是必读的,尤其是所谓“经典”的论文。通常那些经典的技术或者算法,在它们真正普及的时候通常和提出它们的第一篇经典论文比较有比较大的差别。算法本身会多出许多更有意思的变种。拿Shadow Map举例子,后面的PCF,再到VSM,再到PCSS,…
&p&行业里外最有名的是辐射3的火车}

我要回帖

更多关于 3dd103D 3DD103E 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信