【Unity3D】真实感水体渲染(urp)

您所在的位置:网站首页 unity灯光渲染引擎 【Unity3D】真实感水体渲染(urp)

【Unity3D】真实感水体渲染(urp)

#【Unity3D】真实感水体渲染(urp)| 来源: 网络整理| 查看: 265

记录一下过程就当是整理思路。主要实现的是浅水渲染。想到什么还能修改的地方还会随时更新,欢迎大佬们有想法一起讨论~

版本是unity2022.2.8f1c1

目前的效果:

水面纹理变形

模型用的是系统自带的plane。主要参考了catlikeCoding,Texture Distortion 文章写的非常具体详细了,细节就不赘述了。

Flow Map

通过对flowmap采样,用获取的向量进行uv动画,对平面进行模拟流动。当然还有一个更省的办法是不采样贴图,直接自定义两个向量控制方向和流速,也可以获得不错的效果。

flow map由此获得偏移后的两个uv法线扰动

用获得的uv向量对导数图进行采样,生成新的法线信息。

Derivative Map

用导数图是因为导数图只需要两个通道,并且不需要切线空间信息。不过导数图灵活性较差,所以也写了一个法线贴图版本的:

Normal Map

法线混合没有用文章里的线性混合,是参考了Blending in Detail这篇博客的做法,效果更自然。

光照模型用的自写的pbr,不过其实用blingphong高光和漫反射也够用,pbr就是多加了一点环境光信息,这边就不放代码了。

得到的平面水面波浪

同样参考的是catlikeCoding的Waves

用Gerstner函数进行顶点动画来模拟波浪的起伏。由于位置发生变化,法线也需要更新,函数的导数就是切线,通过求导来获取新的tangent和binormal,然后叉乘获得新的normal:

得到顶点位置增量和新的法线、副法线用三个向量模拟三个方向的波加了三个wave方向以后水下颜色

光线传播到水平面上,一部分会发生反射,另一部分会折射到水底再传播回我们的眼睛。因此水深较浅的地方是能看的到水底的颜色。

折射

到目前为止好像能直接用透明混合搞定,但是由于水底折射会产生成像偏移,这块我们用自带的CameraOpaqueTexture,记得设置一下渲染顺序并且在RenderFeatureAsset里打开一下OpaqueTexture。

偏移用法线分量进行扰动就能获得不错的效果,但是会产生严重的伪像:

用红框圈了一些伪像出来

原因是因为uv偏移后采样到了在水面上的物体,解决办法同样参考了catlike的Looking Through Water,通过对比深度,如果物体在水面上方,物体深度小于水面深度,这时就不用对uv进行偏移了。

记得采样时设置Sampler State为point,不然会产生blend Filter 产生的伪像

处理后如图

好了一些但没有太多

毕竟是基于深度处理的,精度肯定是会有点问题的。还有一种方法就是用rendering feature,在渲染透明物体前只渲染水体并把信息保存到一张图作为mask,基于mask可以很好的判断哪些物体在水面上方。

深度

水越深吸收的光线越多,颜色越深。同理上文,也是用深度进行判断的,深度差越大说明水面离地面越远。本来是用深度直接做线性插值的,不过浅水这样做颜色太深了,这种方法更适合于海水,最后还是用了catlike的雾效模拟水深颜色:

直接和折射混合一下就好,深度也要根据uv偏移重新矫正一下上完色(?)后白沫

白沫(Foam)产生的原理是水包含空气后产生了薄膜,使得光线无法穿透这层薄膜,更多的光线被反射而产生的白色。目前实现了两种白沫,一种是水碰撞后产生的浮沫,一种是浪尖白沫。实现参考了Unity官方的开源项目BoatAttack,用的是Multi Ramp Map的方法。三种密度的foam texture对应三种不同的ramp map,混合以后可以产生比较自然的效果。

图片来自刺客信条3的技术分享

通过比较场景深度和水面深度来得到距离,距离越小说明水面离物体越近。

这边的screenPos.w就是View Space坐标下的-z

foammask决定了白沫是否可见,参考了两个值:一个是深度距离,深度距离越小可能产生白沫,pow函数是为了控制浮沫边缘的衰减程度;另一个是高度,当水面高度高于基准高度时,水面越高白沫越明显。

取得foam颜色后根据mask进行混合有了白沫后的效果

焦散

焦散(Caustic)是由于光线传播到水中进行散射使得光线不均匀的分部产生的效果。物理实现比较麻烦,这边直接用贴图模拟。焦散发生在水底,所以用深度图重建了坐标系,用水底坐标xz作为uv,加上法线扰动可以获得不错的效果。坐标系重建可以看我上一篇文章天君:【Unity3D】DepthTexture重建世界坐标(基于URP)

Causitc Texture有了焦散后的效果反射

其实也做了摄像机平面反射和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,顺序在不透明物体渲染之后

得到的反射效果如图:

出现了新的伪像,很多洞。这是由于我们做的是屏幕空间反射,有些被透视压缩的地方已经信息缺失了,无法被反射到,解决办法就是拿四周的像素信息填补一下。

cspass

现实中的反射由于菲涅尔现象是渐隐的,我们直接拿高度做个渐变的mask就可以

最终效果:

一个平平无奇的镜面反射Stretched reflection

其实已经还凑合了,但是真实世界的反射是这样的:

由于水面凹凸不平会产生方向不同的法线,反射方向也会发生抖动,从而使得反射被拉伸。

目前能想到的解决办法就是,既然是因为各向异性产生的现象,那么采样反射图片的时候就用各向异性采样。具体的做法参考了码塔男:Image based Lighting with Stretched Reflection这位大佬。

通过视线和着色点法线的夹角可以获得当前拉伸程度,夹角越小说明离摄像机越近,拉伸程度越大。根据视线方向重新求出切线和副切线,算出偏导向量,进行各向异性采样,然后和正常采样的图片进行混合:

这边假设了点积一定是大于0的,毕竟负方向视线也不应该能形成平面反射改为各项异性采样以后有了拉伸感

但是各项异性采样成本更高,所以这一步也可以省略或者做个开关。

众所周知反射的时候会有菲涅尔效应。既glancing angle(视线和地平面的夹角)越小,反射效果越明显,所以这一步千万不要忘记算:

加了一个power值控制反射强度

采样反射图时加上法线分量进行扰动,再根据菲尼尔效应得到的反射率,计算出最终的反射值

我写了三种反射,只需要看sspr那一项的就可以了

最后得到的效果如图:

还想做水面交互和浮动的,有空再加了~

毛星云:真实感水体渲染技术总结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