续Rendering 15
之前的程序,只考虑聚光灯的shading计算。
现在开始考虑其他两种光源类型。
首先考虑到Spot作用域并不是作用于全部的像素点,而是只对那些自己光范围内的fragment起作用。
所以这里就需要去找到这个所谓的光照范围内。
通常对于聚光灯,需要使用一个pyramid的包围盒。
有了包围盒之后,其实我们就只需要把他映射到我们的屏幕上,那么他就会覆盖一定的区域。而这个所覆盖区域的像素,我们只需要处理这些就可以。
具体来说,之前用directional light他是渲染一个quad,而现在就是渲染一个背面裁剪的pyramid。
首先这里需要开启深度测试,因为聚光灯如果pyramid的一些范围被前面的物体挡住了,那么这部分直接不用考虑了。
因为就算聚光灯发挥作用也是照到别人被裁减的背面。没必要计算。
然后通过深度测试,背面裁剪等等的步骤,再投影以下,pyramid就变成了屏幕上的一片区域。
只有这一片区域的像素我们需要计算。其他区域要么是范围根本到不了,要么是照射别人的背面摄像机也看不到,直接被裁剪掉了。
那么我们也就只需要对着这个剪影的所有的顶点,搞到他们的ray,然后对于这个区域内部的所有像素的ray直接通过插值得到。
这里有个问题,就是插值得到的这些ray,只能是pyramid表面的啊,好像没啥用。
获得之后,还是老套路,直接通过depth值来得到他们的世界坐标。(注意关于该区域的像素是否处在聚光灯照射范围内,这里通过深度是无法判断的
,所以我们还是reconstruct世界空间,然后拿到世界坐标再判断,然后基本上就和之前的聚光灯计算连接起来了,就一样了。)
这边就是对这个pyramid渲染的过程。
相关的选项配置:
这里使用模板缓冲有一些限制的地方。就是当pyramid把近平面给包含进去之后。
这里说了两个方法,其实就是:
注意这里红框中的内容。
这里对于pyramid进行render,也就是对pyramid投影之后的像素进行光照的shading计算。
这里把前面给留着,后面给cull,然后如果是它前面有东西,那肯定就不用考虑了。因为在他前面说明像素是在光范围之外的,这个肯定就不用执行当前光的shading计算。
然后这里会搞一个什么模板测试来限制这个drawing 到pyramid内的所有fragment。注意这里limit to的翻译
然后再看第二种方法:
这里第一句说什么inside,outside真的是谜之操作,但好在有第二说清楚了,就是rendering backface,也就是不再渲染前面了,
而是渲染后面。
注意上边的cull 已经写成了front,那么只有当这个back face前面有东西的时候,才会去考虑当前像素的shading计算,
因为他是back face,后面的东西肯定不用考虑。
所以前面有东西,才可能需要shading。
所以两种方法,一个是考虑front face 后面的所有fragment,这样肯定能够包含所有的fragments inside of pyramid。
另一个是考虑back face 前面的所有fragment,也肯定能够包含所有的fragments inside of pyramid。
But it ends up rendering too many fragments, as normally hidden parts of the pyramid now also get rendered. So it's only done when necessary.
这一句没弄明白,什么叫做normal hidden parts,哪有隐藏这一说。
好像有点明白了,因为上边说了两个方法之间会进行一个切换,那么为什么会切换呢,而且重要的是上边的图
下面标记了一个close to the camera。
其实就是灯和camera之间距离近了,因为近了,多数情况下就代表,灯光和摄像机之间的物体就少了。
少了意味着我们在做排除的时候,排除之后剩下的就少了。(我们只考虑back face前面的就是在排除后面的所有。)
那么工作量就会少了。但是有一个问题就是hidden的出现了。因为我们靠近了,也就意味着那个pyramid变得大了
覆盖的fragment多了,所以也就有了上边的too many fragment。
另外当我们距离远的时候,灯后面的东西就相对少一些,我们这时候就可以考虑cull back,然后front 后面的所有像素去考虑是否shading计算。
其实从这里的很多的说法可以看出,他是真的是把render geometry和render light分开了。这里render pyramid其实就是在render lighting。
补充一点,注意上边俩图的深度测试模式也是不同的,所以这里他们就是利用深度测试来排除的pyramid前面或者后面的
所有的像素。然后对于剩下的那些,可能有些确实并不在pyramid中,这里好像也没考虑排除,就直接对这剩下的所有开始
渲染了,因为这个考虑前向渲染的时候考虑过,就是他会裁剪一部分减少着色的物体个数,这里就是减少fragment个数,然后
同时对于过渡的问题,衰减中会做到的。
其实说白了就是如果没有裁剪,当出了范围,衰减也会让他变成0的。所以这里可以不考虑的。
我们想要在刚刚的基础上加上其他的光源类型,我们就得对已经实现了的directional light代码做一个条件判断。
然后把一些不同光源类型之间互通的代码给放在判断外面。
比如这里Directional
这里注意,因为关于阴影边界处fade out的行为是每一个光源都需要的,所以这里直接就可以放在判断外。
然后在获取ray的时候,这里由于传递过来的normal不再是存储ray了。
至于为啥不把ray存储在normal里面了,是因为之前那个给了摄像机的field of view 和那些近远平面相关的数据就可以预计算出来。
但是到了pyramid就不行了,他的位置深度啥的都是随机的,没法提前准备。所以就需要derive the rays from vertex position。
我们需要自己去计算ray。需要转换到view空间下。
但是这里的转换后的坐标为何要进行一个反转xy。说什么wrong orientation。
这块其实就是根据当前渲染的是quad还是pyramid来选择ray数据从哪获取。
cookie这块,只需要注意,聚光灯的光照空间是一个透视空间,所以在获取采样坐标的时候,需要的是投影之后的xy。
这里已经投影了,但是没有透视除法,所以只需要让xy透视除法一下,就可以得到采样坐标了。
当然这里还有一个点,就是我们投影的时候看的都是z为负数那一部分,正数那一部分也会投影,但是这里我们需要给忽略
因为那些实际上是在摄像机的后面,他们的衰减值直接为0.
另外这里w是小于0时,atten保持不变。为啥不是大于0时。
在投影变换的结果中,w分量都是存储了-z的。不应该是z为负数的,也就是-z为正数的留下了么??
这里的衰减就是一个单纯的距离衰减,然后使用距离来采样贴图。 他这里得到距离平方之后,进行了一个scaled by the light's range
对于阴影部分,这里就空间变换然后采样。
说了一句,
we can use UnitySampleShadowmap to take care of the details of sampling hard or soft shadows.
没太明白。。。。
关于点光源部分,这个和聚光灯就有很多东西一样了,一样的部分直接重用,不一样的这里主要有cookie和阴影。
阴影需要cube采用。
最后一部分的一个优化,完全没看出来,哪里提升效率了。
这里的优化很可能是是编译器参与的,因为这种分支,他可能会被编译器先执行,然后因为这里如果执行了,赋值了,
那么说明前边无论对他怎么改都没有意义,都可以忽略了。(不确定~~)
————————————————————————————————————————————————————————————————————————————————————————————————————————————
Rendering 16
这里可以打开lightmap,改成烘焙,这里虽说是间接烘焙,但是这个操作按钮其实是直接和间接光都进行一个烘焙。
将光改为bake之后,其实对于动态物体来说这个光就是跟没有一样,不会直接影响动态物体,但是间接影响还是会的,因为
这些灯光可能影响天空球,天空球的间接光影响物体。
这里说出了一个关于烘焙灯光的弊端,就是没有高光反射,主要就是因为高光反射和观察者位置有关系,所以没办法提前计算出来。
所以当高光物体的时候,用这招效果就不好了,所以往往使用的时候都是实时和烘焙一起用。
这里没太看明白这个directional Mode
这里可以看到所有的baked的图。
关于trade off。
虽然丢失了高光,但是是多了环境光的:
关于一些光子bounce效果。
还有自发光的效果。
这里有一个AO的话题,就是这个AO是我们人造的一个效果。做一些视觉上的补充完善。
这种情况下效果更加真实,但是它和这个自发光结合一块就寄了:
这里可以找到它。
————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
URP
右键copyshader,可以直接获取到shader graph对应的代码
找个地方粘贴就可以了。
全部折叠的快捷键,Ctrl+k,ctrl+0.
这个操作看代码有用
这个shader使用之后的效果就是bug红,但是并不代表有错误,代表走了fallback。
可以通过名字在package找到它的代码,就是输出的一个颜色。
基础知识:
【百人计划】图形 6.1 风格化渲染概述_哔哩哔哩_bilibili
UE4卡通渲染基础教程 Part4:Paint Filter - 知乎 (zhihu.com)
Unity卡通渲染总结 NPR Toon Shading - 知乎 (zhihu.com)
效果实现:
Unity复现《重力眩晕2》中的渲染技术 - 知乎 (zhihu.com)
(120条消息) 米哈游:《八重樱 · 桃源恋歌》短片制作技术分享_wangchewen的博客-CSDN博客
白话笔记——卡通渲染篇(实时渲染) - 知乎 (zhihu.com)
【GAD翻译馆】火影忍者疾风传视觉进化技术和制作方法介绍-腾讯游戏学堂 (qq.com)
(120条消息) unity中int转float_原神截帧分析(持续作案中...)_weixin_39634985的博客-CSDN博客
[Unity 活动]-游戏专场|从手机走向主机 -《原神》主机版渲染技术分享_哔哩哔哩_bilibili
从零开始的URP仿原神角色渲染 - 知乎 (zhihu.com)
【01】Unity URP 卡通渲染 原神角色渲染记录-Diffuse: Ramp + AO + Double Shadow - 知乎 (zhihu.com)
原神角色渲染Shader分析还原 - 知乎 (zhihu.com)
Cel-shading 赛璐璐风格的画风。
前俩,二阶,过渡是直接的,所以颜色跨越不要太大。
第三个就更多的色阶,过渡更加的自然。
第四个就有机融合,可以看出既有PBR,又有NPR,关于融合是一个难点。
美术上的描边。
第一个描边,效果很差,顶多用来选中模型的边缘高亮效果。
第二个,主要思路是:先渲染前面,然后再把前面给cull了,剩下的back face,把这个back face 的网格给进行一个扩充,这样在这里啊交界地方就会出现描边了。(使用较多,实现有很多细节问题)
(需要平滑法线,因为沿着法线扩充,如果不是平滑法线会出现法线的断裂。)
两种方式:一个是直接DCC操作,另外一个是各种什么算法。
这里还有问题,就是由于近大远小的问题,所以在扩充的时候,可能会导致描边的粗细有别。可以弄到屏幕空间来修正。
如果想要不同的部位有一个不同的描边颜色,这里可以活用顶点色。或者通过alpha控制描边粗细,描边强度等。
下面是基于后处理的描边:
根据场景深度这个有一个问题,就是会出现全屏幕都描边。这个就需要把要描边的东西搞一个遮罩出来。
然后这里使用到一个卷积,但是可能由于精度不够,对于内部一些描边可能不够,所以可以用它专门做外部的描边,就需要自定义深度,然后还有一些问题,说什么用取整解决。
然后场景法线。这个内描边好像不错,但是外部就不够了。 可以结合backface。或者结合
还有ID图描边。