水波效果

水波效果指在计算机图形学中模拟水面波纹的视觉效果,通常用于游戏、动画或者其他虚拟场景中。主要用于体现水体的动态感,比如水的波动、反射、折射、透明等,可以让人感觉像真实的水一样流动闪耀。

核心特点就是:

  • 动态波纹
  • 光学特性(反射、折射、菲涅耳效应)
  • 透明度

1、基本原理

水波效果可以基于【带法线纹理的玻璃效果】进行修改,通过添加噪声法线纹理结合Shader内置时间变量实现水波动态效果,加入菲涅耳计算公式实现水面的光学特性

关键点:

  • 噪声纹理的使用

可以利用沃利噪声(细胞噪声)生成的噪声纹理灰度图,在Unity中将该噪声纹理灰度图作为高度图使用,用它代表水面的法线信息,只需要在Unity中将该灰度图设置为Normal map,并勾选Create from Grayscale后应用即可

  • 动态效果的实现

自定义两个属性,代表水平面x和y轴的速度。在片元着色器中利用Shader内置时间参数 _Time.y 得到累积速度变化。然后用该速度变量从噪声法线纹理中进行两次采样,再讲两次采样的结果相加得到扰动后的法线,最后用该法线处理折射、反射、菲涅耳效果,这样看起来就会有动态效果了
该算法是图形学前辈们总结的高效的模拟流动感的算法,水波、火焰、玻璃折射都可以用

  • 菲涅耳公式的运用

2、实现

Shader "ShaderProj/21/WaterWave"
{
    Properties
    {
        _MainTex("MainTex", 2D) = ""{}
        _BumpMap("BumpMap", 2D) = ""{}
        _Cube("Cubemap", Cube) = ""{}
        //控制折射扭曲程度的变量
        _Distortion("Distortion", Range(0,10)) = 0

        // 水波水平和数值速度偏移的属性 
        _WaveXSpeed("WaveXSpeed", Range(-0.1, 0.1)) = 0.01
        _WaveYSpeed("WaveYSpeed", Range(-0.1, 0.1)) = 0.01

        _FresnelScale("FresnelScale", Range(0,1)) = 1
    }
    SubShader 
    {
        Tags{"RenderType"="Opaque" "Queue"="Transparent"}
        GrabPass{}

        Pass
        {
            Tags{"LightMode"="ForwardBase"}
            
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            #include "Lighting.cginc"

            sampler2D _MainTex;
            float4 _MainTex_ST;
            sampler2D _BumpMap;
            float4 _BumpMap_ST;
            samplerCUBE _Cube;
            sampler2D _GrabTexture;
            float _Distortion;
            fixed _WaveXSpeed;
            fixed _WaveYSpeed;
            float _FresnelScale;

            struct v2f
            {
                float4 pos:SV_POSITION;
                float4 grabPos:TEXCOORD0;
                float4 uv:TEXCOORD1;
                float4 TtoW0:TEXCOORD3;
                float4 TtoW1:TEXCOORD4;
                float4 TtoW2:TEXCOORD5;
            };

            v2f vert(appdata_full v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.grabPos = ComputeGrabScreenPos(o.pos);
                o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
                o.uv.zw = TRANSFORM_TEX(v.texcoord, _BumpMap);
                //计算反射光向量
                float3 worldNormal = UnityObjectToWorldNormal(v.normal);
                fixed3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                float3 worldTangent = UnityObjectToWorldDir(v.tangent);
                float3 worldBinormal = cross(normalize(worldTangent), normalize(worldNormal)) * v.tangent.w;

                o.TtoW0 = float4(worldTangent.x, worldBinormal.x,  worldNormal.x, worldPos.x);
                o.TtoW1 = float4(worldTangent.y, worldBinormal.y,  worldNormal.y, worldPos.y);
                o.TtoW2 = float4(worldTangent.z, worldBinormal.z,  worldNormal.z, worldPos.z);

                return o;
            }

            fixed4 frag(v2f i):SV_TARGET
            {
                float3 worldPos = float3(i.TtoW0.w, i.TtoW1.w, i.TtoW2.w);
                fixed3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));

                float2 speed = _Time.y * float2(_WaveXSpeed, _WaveYSpeed);
                fixed3 bump1 = UnpackNormal(tex2D(_BumpMap, i.uv.zw + speed)).rgb;
                fixed3 bump2 = UnpackNormal(tex2D(_BumpMap, i.uv.zw - speed)).rgb;
                fixed3 bump = normalize(bump1 + bump2);
                float3 worldNormal = float3(dot(i.TtoW0.xyz, bump), dot(i.TtoW1.xyz, bump), dot(i.TtoW2.xyz, bump));

                fixed4 mainTex = tex2D(_MainTex, i.uv + speed);
                float3 refl = reflect(-viewDir, worldNormal);
                fixed4 reflColor = texCUBE(_Cube, refl) * mainTex;
                
                //折射相关的颜色
                float2 offset = bump.xy * _Distortion;
                i.grabPos.xy = offset*i.grabPos.z + i.grabPos.xy; 

                fixed2 screenUV = i.grabPos.xy / i.grabPos.w;
                fixed4 grabColor = tex2D(_GrabTexture, screenUV);

                fixed fresnel = _FresnelScale + (1 - _FresnelScale) * pow(1 - dot(normalize(viewDir), normalize(worldNormal)), 5);
                float4 color = lerp(reflColor, grabColor, 1 - fresnel);

                return color;
            }

            ENDCG
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值