【Unity3D】真实感水体渲染(urp) |
您所在的位置:网站首页 › unity灯光渲染引擎 › 【Unity3D】真实感水体渲染(urp) |
记录一下过程就当是整理思路。主要实现的是浅水渲染。想到什么还能修改的地方还会随时更新,欢迎大佬们有想法一起讨论~ 版本是unity2022.2.8f1c1 目前的效果: 模型用的是系统自带的plane。主要参考了catlikeCoding,Texture Distortion 文章写的非常具体详细了,细节就不赘述了。 Flow Map通过对flowmap采样,用获取的向量进行uv动画,对平面进行模拟流动。当然还有一个更省的办法是不采样贴图,直接自定义两个向量控制方向和流速,也可以获得不错的效果。 用获得的uv向量对导数图进行采样,生成新的法线信息。 用导数图是因为导数图只需要两个通道,并且不需要切线空间信息。不过导数图灵活性较差,所以也写了一个法线贴图版本的: 法线混合没有用文章里的线性混合,是参考了Blending in Detail这篇博客的做法,效果更自然。 光照模型用的自写的pbr,不过其实用blingphong高光和漫反射也够用,pbr就是多加了一点环境光信息,这边就不放代码了。 同样参考的是catlikeCoding的Waves 用Gerstner函数进行顶点动画来模拟波浪的起伏。由于位置发生变化,法线也需要更新,函数的导数就是切线,通过求导来获取新的tangent和binormal,然后叉乘获得新的normal: 光线传播到水平面上,一部分会发生反射,另一部分会折射到水底再传播回我们的眼睛。因此水深较浅的地方是能看的到水底的颜色。 折射到目前为止好像能直接用透明混合搞定,但是由于水底折射会产生成像偏移,这块我们用自带的CameraOpaqueTexture,记得设置一下渲染顺序并且在RenderFeatureAsset里打开一下OpaqueTexture。 偏移用法线分量进行扰动就能获得不错的效果,但是会产生严重的伪像: 原因是因为uv偏移后采样到了在水面上的物体,解决办法同样参考了catlike的Looking Through Water,通过对比深度,如果物体在水面上方,物体深度小于水面深度,这时就不用对uv进行偏移了。 处理后如图 毕竟是基于深度处理的,精度肯定是会有点问题的。还有一种方法就是用rendering feature,在渲染透明物体前只渲染水体并把信息保存到一张图作为mask,基于mask可以很好的判断哪些物体在水面上方。 深度水越深吸收的光线越多,颜色越深。同理上文,也是用深度进行判断的,深度差越大说明水面离地面越远。本来是用深度直接做线性插值的,不过浅水这样做颜色太深了,这种方法更适合于海水,最后还是用了catlike的雾效模拟水深颜色: 白沫(Foam)产生的原理是水包含空气后产生了薄膜,使得光线无法穿透这层薄膜,更多的光线被反射而产生的白色。目前实现了两种白沫,一种是水碰撞后产生的浮沫,一种是浪尖白沫。实现参考了Unity官方的开源项目BoatAttack,用的是Multi Ramp Map的方法。三种密度的foam texture对应三种不同的ramp map,混合以后可以产生比较自然的效果。 通过比较场景深度和水面深度来得到距离,距离越小说明水面离物体越近。 foammask决定了白沫是否可见,参考了两个值:一个是深度距离,深度距离越小可能产生白沫,pow函数是为了控制浮沫边缘的衰减程度;另一个是高度,当水面高度高于基准高度时,水面越高白沫越明显。 焦散(Caustic)是由于光线传播到水中进行散射使得光线不均匀的分部产生的效果。物理实现比较麻烦,这边直接用贴图模拟。焦散发生在水底,所以用深度图重建了坐标系,用水底坐标xz作为uv,加上法线扰动可以获得不错的效果。坐标系重建可以看我上一篇文章天君:【Unity3D】DepthTexture重建世界坐标(基于URP) 其实也做了摄像机平面反射和Screen Space Reflection,但是用下来感觉Screen Space Planar Reflection综合性能和效果是最适合的,所以主要讲讲SSPR。更多的反射可以参考这篇文章,非常详细Unity Shader-反射效果(CubeMap,Reflection Probe,Planar Reflection,Screen Space Reflection) SSPR原理其实很简单,先在屏幕空间重建世界坐标,有了视线方向,根据着色点的法线进行反射,打到的物体位置就是将会被反射的点。而平面反射是镜像的,那么只要根据平面进行翻转,就可以获得反射点的位置。 获得反射点位置以后,只需要把该坐标重新映射回屏幕屏幕空间就可以了。 我会用rendering feature和compute shader来实现,之所以用compute shader是因为fragment shader 位置和颜色是一一对应的,而conpute shader是乱序写入的,也就是说可以在不同位置写入颜色,非常符合我们的需求,性能上compute shader也更省一些。 compute shader 具体代码: 首先重建世界坐标,thread id相当于屏幕坐标,除以render texture的大小把它们映射回[0,1]区间,后面的写法和之前的差不太多就不多说了 采样OpaqueTexuture的颜色,放到反射后的屏幕坐标位置。不过在记录信息前需要先判断一下遮挡关系,同一个反射方向上可能有多个遮挡物,只需要取最前面的遮挡物就可以。 用一个新的texture ReDepthButterRT来记录深度 然后新建一个render pass,顺序在不透明物体渲染之后 得到的反射效果如图: 出现了新的伪像,很多洞。这是由于我们做的是屏幕空间反射,有些被透视压缩的地方已经信息缺失了,无法被反射到,解决办法就是拿四周的像素信息填补一下。 现实中的反射由于菲涅尔现象是渐隐的,我们直接拿高度做个渐变的mask就可以 最终效果: 其实已经还凑合了,但是真实世界的反射是这样的: 由于水面凹凸不平会产生方向不同的法线,反射方向也会发生抖动,从而使得反射被拉伸。 目前能想到的解决办法就是,既然是因为各向异性产生的现象,那么采样反射图片的时候就用各向异性采样。具体的做法参考了码塔男:Image based Lighting with Stretched Reflection这位大佬。 通过视线和着色点法线的夹角可以获得当前拉伸程度,夹角越小说明离摄像机越近,拉伸程度越大。根据视线方向重新求出切线和副切线,算出偏导向量,进行各向异性采样,然后和正常采样的图片进行混合: 但是各项异性采样成本更高,所以这一步也可以省略或者做个开关。 众所周知反射的时候会有菲涅尔效应。既glancing angle(视线和地平面的夹角)越小,反射效果越明显,所以这一步千万不要忘记算: 采样反射图时加上法线分量进行扰动,再根据菲尼尔效应得到的反射率,计算出最终的反射值 最后得到的效果如图: 还想做水面交互和浮动的,有空再加了~ 毛星云:真实感水体渲染技术总结https://catlikecoding.com/unity/tutorials/flow/https://github.com/ColinLeung-NiloCat/UnityURP-MobileScreenSpacePlanarReflectionhttps://advances.realtimerendering.com/s2017/PixelProjectedReflectionsAC_v_1.92_withNotes.pdf |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |