永远相信美好的事情即将发生 😊!

RenderFeature实现雾效(URP)

分享 Mavis 13℃ 0评论

说实话看了很多次的renderfeature的代码感觉还是迷迷糊糊的

先看一下高度雾的对比效果吧:

Before
After

同时实现的还有深度雾,以及雾效的三种算法(在入门精要里讲雾的时候说过)

ok,下面就先讲讲怎么创建renderfeature吧。

先创建一个:

创建好后就可以看到这么一个东西:

密密麻麻的注释我想我就不用翻译了,创建好以后,在渲染管线里就可以添加了,如果是第一次创建,应该只能看到我红圈圈出来的两个,上面打钩应用的那些也是应该没有的:

添加进行以后,除了一个名字,啥也看不到:

作为一个可以灵活运用在渲染管线各个阶段的工具,这显然还缺少了可以手动setting的部分,接下来添加一下(对于本次的雾效,只需要添加两个属性):

再创建一个fog材质一个fog.shader,把材质赋值到上面的material中,后处理的时候就可以使用到该材质的shader 去渲染啦,关于renderfeature更多的内容大家还是看大神们的笔记吧,我理解的还不是很透彻就不在这乱说了。

弄清楚雾效的计算:

我博客里学习入门精要的时候记的笔记中关于雾效的实现挺清楚的,这里就搬一张图过来

总结一下要实现三种雾效,基本上需要四个参数:起始值、结束值、浓度、当前值

基于高度的线性雾的公式在上面给出来了,由此也可以得到另外的基于高度的指数雾的公式,知道高度雾以后,计算深度雾又是同样的道理。

所以只要实现出基于高度的线性雾,其他的就都不是问题了。

如何实现出来呢?首先上面公式中的f计算出来的就是该像素点的雾效值,其次起始值、结束值、浓度这三个值应该是开放的属性以供调整雾的效果,那么唯一需要计算的就是当前值应该是多少,图中说了,这个值应该是世界坐标下的高度y,选取y的原因很简单,因为这是基于高度的雾效,跟深度或者其他的值没有关系,而选取世界空间下而不是其他空间下的原因是,真实世界中的雾不会因为视角的移动变化而发生位置上的移动。所以必须是基于世界空间去计算。

对于后处理来说,我们需要计算的是每一个像素的值,我们得到的UV也是屏幕UV,对于一个像素来说,是没有世界坐标下的信息的,所以我们需要重建该像素点所指示的片元在世界空间下的坐标,这部分的转化在上面给出的链接中讲的已经很清楚了,后面的代码注释中也讲解了,这里就不重复了,既然计算出来世界坐标下的y值,那么带入公式就ok了。

突然发现自己好像讲了一堆废话,直接上renderfeature脚本中的代码吧:

//重建像素的世界坐标:float4 worldPos=_WorldSpaceCameraPos + linearDepth * interpolateRay
Camera camera = renderingData.cameraData.camera;//获取相机
Matrix4x4 frustumCorners = Matrix4x4.identity;//4*4的单位矩阵
float fov = camera.fieldOfView;//相机的fov值(度数)
float near = camera.nearClipPlane;//相机的近平面
float aspect = camera.aspect;//相机屏幕的宽长比
//Deg2Rad:角度转弧度
float halfHeight = near * Mathf.Tan(fov * 0.5f * Mathf.Deg2Rad);//计算出近平面高度的一半
Vector3 toRight = camera.transform.right * halfHeight * aspect;//得到近平面中心点向右的向量(相机空间的右侧,不是世界空间)
Vector3 toTop = camera.transform.up * halfHeight;//得到近平面中心点向上的向量(相机空间的上方,不是世界空间)
Vector3 topLeft = camera.transform.forward * near + toTop - toRight;//相机到近平面左上角的向量
//interpolateRay在顶点片元着色器中根据像素所在象限确定顶点使用哪个,然后片元着色器会自动插值得到相机到每个像素的向量X
//由于深度图采样得到的深度是线性深度不是相机与点的欧氏距离,所以需要根据相似三角形来计算出欧氏距离:depth/dist=Near/|X|
//dist =( |X|  / Near )* depth
//所以相机到像素世界坐标点的实际距离是:dist* normalize(X) =( |X|  / Near )* depth * normalize(X);
//Near都相同,所以直接在这里先进行计算: dist* normalize(X) = depth * ((|X|/Near)* normalize(X) );
float scale = topLeft.magnitude / near;// |X|/Near
topLeft.Normalize();
topLeft *= scale;
Vector3 topRight = camera.transform.forward * near + toRight + toTop;//相机到近平面右上角的向量
topRight.Normalize();
topRight *= scale;
Vector3 bottomLeft = camera.transform.forward * near - toTop - toRight;//相机到近平面左下角的向量
bottomLeft.Normalize();
bottomLeft *= scale;
Vector3 bottomRight = camera.transform.forward * near + toRight - toTop;//相机到近平面右下角的向量
bottomRight.Normalize();
bottomRight *= scale;
frustumCorners.SetRow(0, bottomLeft);
frustumCorners.SetRow(1, bottomRight);
frustumCorners.SetRow(2, topRight);
frustumCorners.SetRow(3, topLeft);
material.SetMatrix("_FrustumCornersRay", frustumCorners);

上面这部分代码在Execute方法中执行就可以成功把矩阵信息传递到material使用的shader中了,在shader中直接使用set时候的字符串名字就可以获取到这张图。

然后在shader中还需要注意,如果要实现深度雾,还需要得到深度图,深度图在urp渲染管线的设置中可以直接打个勾就可以拿到:

目前我使用的版本还不支持深度法线图,不过如果做效果要用到的话,可以通过renderfeature的方法自己渲染一张

因为雾效其实关键就是把重建世界坐标的矩阵传递到shader,其他部分都不是难题,套个公式就可以实现的。那我就不再多说了,直接上每种方式的效果图吧(截图后加速了,原图草动的很慢的,这样可以看到雾参数的调节效果,基于高度的主要看树,基于深度的主要看草……录的时候忘记把树挪到中间了,将就看吧):

基于高度的线性雾
基于高度的指数雾
基于高度的指数平方雾
基于深度的线性雾
基于深度的指数雾
基于深度的指数平方雾

ok,总算贴完这些图了,因为指数的变化太快了,所以可能效果看起来没有那么明显,不过仔细看还是能看出效果的,通篇下来感觉啥也没讲呢……可能是我觉得入门精要里面讲的够详细了,哈哈哈哈,大家有需要的还是去好好看看入门精要或者我的笔记或者其他大佬的解释吧。


Mavis , 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:RenderFeature实现雾效(URP)
喜欢 (0)
发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址