找回密码
 立即注册
查看: 248|回复: 0

unity线性波叠加模拟水体表面

[复制链接]
发表于 2022-7-19 06:11 | 显示全部楼层 |阅读模式
最近看了《UnityShader入门精要》,在顶点动画章节中,有关于河流模拟,对书中样例做了扩充,利用sin波与Gerstner波对水面进行模拟。这里原理与实现都是参考网上有关文章和书籍,在理解原理基础上进行了shader编写,是一篇学习的记录。
一、Sin波

利用正弦函数来表示水面波形。顶点着色器中,构造出在Y方向上变化,Y方向的位置变化可以根据x,z平面上的任意方向。这里首先根据X方向控制平面的起伏变化。


c为整个正弦波左右移动的速度,k和波长有关 。在进行求解漫反射光照和高光反射时,还需要用到顶点法线信息,由于平面随时间呈正弦波形式的变化,这里求解其切线和法线方向。






值的说明的是,由于Y只随X方向起伏变化,Tx=1,Tz=0,法线方向 通过 为0得到。
float3 SinWave(float3 pos,inout float3 tangent,inout float3 normal) {                                
                float waveLen =5;
                float k = 2 * UNITY_PI / waveLen;
                float waveA = 1;
                float waveSpeed = 1;              
                float f = k * (pos.x - _Time.y * waveSpeed);
                pos.y = waveA * sin(f);
                tangent = normalize(float3(1, k * waveA * cos(f), 0));
                normal = float3(-tangent.y, tangent.x, 0);
                return float3(pos.x, pos.y, pos.z);      
            }


二、Gerster波

个人理解是Gerster波可以在波峰处有更好的聚集性,在波谷处又能很好的分散开,参考文章里通过给定了顶点位置关系:


也就是相当于表面每个水分子在x,y方向上都可以组成一个圆,每个水分子x上都加上一个A为直径的圆的坐标。其切线与法线分别为,




float3 Gerstners1(float3 pos,inout float3 tangent, inout float3 normal) {
                float k = 2 * UNITY_PI / 1;
                float waveA = 0.08;
                float f1 = k * (pos.x - 1 * _Time.y);               
                tangent = normalize(float3(
                    1-k*waveA*sin(f1),
                    k*waveA*cos(f1),
                    0
                    ));
                normal = float3(-tangent.y, tangent.x, 0);
                return float3(waveA*cos(f1), waveA *sin(f1),0);              
            }



单个Gerster波的效果

多个Gersterner波叠加的情况,与单个波不同的是法线的计算,




其中 代表了当前波的方向,多个类似的波可以进行叠加。






float3 Gerstners2(float4 wave,float3 pos,inout float3 tangent, inout float3 binormal) {
                float2 d = normalize(wave.xy);
                float wavelength = wave.w;
                float stepness = wave.z;               
                float k = 2 * UNITY_PI / wavelength;
                float wavespeed = sqrt(9.8 / k);
                float waveA = stepness / k;
                float f = k * (dot(d, pos.xz) - wavespeed * _Time.y);

                tangent += float3(-d.x * d.x * (stepness * sin(f)), d.x * stepness * cos(f), -d.x * d.y * (stepness * sin(f)));
                binormal += float3(-d.x * d.y * (stepness * sin(f)), d.y * (stepness * cos(f)), -d.y * d.y * (stepness * sin(f)));
                                                
                return float3(d.x * waveA * cos(f), waveA * sin(f), d.y * waveA * cos(f));
            }



两次Gertner叠加

三、水体折射

为了水体更显真实感,这里在水体中做透明和折射处理。根据《UnityShader入门精要》中10.1.4中介绍,我们在片元着色器中,将上述求得的法线转换到切线空间内,并对屏幕坐标进行扰动,进而形成一种折射效果。具体操作为:

  • 对当前subshader进行声明
"Queue" = "Transparent" "RenderType" = "Opaque"
GrabPass{"_RefractionTex"}其中“Queue”作为Transparent保证在渲染改pass之前其他非透明物体已经被渲染,这样可以保证透过水体看见水下物体。GrabPass定义了一个抓取屏幕的pass。“_Refraction”表示存入哪个纹理中。

  • 对顶点着色器输出进行增加
struct v2f {
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;
                float4 scrPos : TEXCOORD1;
                float3 worldNormal:TEXCOORD2;
                float3 worldPos:TEXCOORD3;
                float3 worldViewDir : TEXCOORD4;
                float3 modelTangent : TEXCOORD5;
                float3 modelBinormal : TEXCOORD6;
                float3 modelNormal : TEXCOORD7;
            };

  • 片元着色器中
//模型空间到切线空间转换矩阵
float3x3 rotation = float3x3(i.modelTangent.xyz, i.modelBinormal.xyz, i.modelNormal.xyz);
//切线空间法向量
fixed3 bump = mul(rotation, i.modelNormal).xyz;
//折射偏移
float2 offset = bump.xy * _Distortion * _RefractionTex_TexelSize.xy;
//屏幕坐标偏移
i.scrPos.xy = offset * i.scrPos.z + i.scrPos.xy;
//对渲染纹理_RefractionTex采样
fixed3 refrCol = tex2D(_RefractionTex, i.scrPos.xy / i.scrPos.w).rgb;看下加了折射效果的水面效果:



带有折射效果

这里去掉了高光反射,这里与书中例子不同的是,这里的法线使我们手动计算得到。书中通过法线纹理进行采样得到。水面反射效果通过立方体纹理添加后,出现大片白色光斑,还有待进一步查找问题原因,对于菲涅尔效果来说,反射效果是必要的。同时折射计算时,是在切线空间下对屏幕坐标进行“扰动”,需要进一步理解,这里也是先挖个坑,后续专门说明。

参考文章:
https://catlikecoding.com/unity/tutorials/flow/waves/
nanayon:水体渲染之Gerstner波形理解与推导
栗野:Unity顶点动画与Gerstner Wave海面模拟
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-9-22 07:06 , Processed in 0.088430 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表