zoukankan      html  css  js  c++  java
  • 如何做一个水面扭曲效果

    如何做一个水面扭曲效果

    本文参考教程,并加上自己的一些心得体会。所谓的扭曲效果,就是将给定的纹理贴图进行采样,并随着时间进行流动,使得每个点的采样路径不完全一样。为了做到这一点,首先我们需要一张flow map,用来保存每个点的采样方向,然后根据时间偏移:

    float2 flowVector = tex2D(_FlowMap, i.uv);
    float2 uv = i.uv + flowVector * _Time.y;
    fixed4 col = tex2D(_MainTex, uv);
    return col;
    

    运行查看,发现随着时间推移,画面越来越细碎,这时我们需要让表现有个周期性,就是画面能够在一个周期回到初始的状态。

    我们可以使用frac函数来对时间进行约束,使得周期控制在1秒:

    float2 uv = i.uv + flowVector * frac(_Time.y);
    

    这样画面总会在1s后回到初始的状态,但是会带来新的问题,就是回到初始状态是一个跳变的过程,会让画面闪一下:

    为了解决这个问题,我们可以加上淡入淡出的效果来缓解这个过渡:

    float progress = frac(_Time.y);
    float2 uv = i.uv + flowVector * progress;
    float w = 1 - 2 * abs(progress - 0.5);
    fixed4 col = tex2D(_MainTex, uv) * w;
    

    通过观察,我们发现这个淡入淡出是整个画面一起的,有些单调,可以让它们的步调稍稍不一致。因为我们的flow map只用了rg两个通道,这里可以再使用a通道来代表每个采样点的步调偏移(其实就是a通道放了一张noise map):

    float4 flowSample = tex2D(_FlowMap, i.uv);
    float2 flowVector = flowSample.rg * 2 - 1;
    float flowNoise = flowSample.a;
    float progress = frac(_Time.y + flowNoise);
    float2 uv = i.uv + flowVector * progress;
    float w = 1 - 2 * abs(progress - 0.5);
    fixed4 col = tex2D(_MainTex, uv) * w;
    

    接下来,我们希望把单次扰动的效果进行叠加,同时使用两次扰动,当然两次扰动的步调是不同的,在时间上存在一个相位差。这里我们将其设置为0.5,使得上面w的权重之和为1:

    float3 flowUVW(float2 uv, float offset)
    {
        float4 flowSample = tex2D(_FlowMap, uv);
        float2 flowVector = flowSample.rg * 2 - 1;
        float flowNoise = flowSample.a;
        float progress = frac(_Time.y + flowNoise + offset);
        uv = uv + flowVector * progress;
        float w = 1 - 2 * abs(progress - 0.5);
    
        return float3(uv, w);
    }
    
    fixed4 frag (v2f i) : SV_Target
    {
        float3 uvwa = flowUVW(i.uv, 0);
        float3 uvwb = flowUVW(i.uv, 0.5);
        fixed4 cola = tex2D(_MainTex, uvwa.xy) * uvwa.z;
        fixed4 colb = tex2D(_MainTex, uvwb.xy) * uvwb.z;
        return cola + colb;
    }
    

    但这样看上去周期重复的感觉很明显,为了淡化这种表现,我们可以再给uv加上偏移参数,让采样的uv需要过很久的时间才会重复:

    uv = uv + float2(_UJump, _VJump) * (_Time.y - progress);
    

    接下来,我们还可以给flow map加上tiling,加上参数控制uv随时间偏移的速度,控制从flow map中采样的方向向量强弱程度:

    float progress = frac(_Time.y * _Speed + flowNoise + offset);
    uv += flowVector * progress;
    uv += float2(_UJump, _VJump) * (_Time.y - progress);
    uv *= _Tiling;
    

    另外,我们可以控制初始采样的偏移,使得当w达到峰值时,即采样权重最大时,对应的uv偏移到一个可以控制的位置:

    uv += flowVector * (progress + _FlowOffset);
    

    最后,我们为水面加上法线信息,这里使用了derivative map来计算水面的法线和高度信息。derivative map的ag通道保存了高度在两个切线方向上的导数,b通道保存了原始的高度。在此基础上还可以继续调制水面的高度,让其与flow map的方向向量强弱挂钩(流动越强,波浪越大,高度越大)。我们用flow map的b通道来保存这一信息,算出水面的高度:

    float flowSpeed = flowSample.b * _FlowStrength;
    float finalHeightScale = flowSpeed * _HeightScaleModulated + _HeightScale;
    

    然后,我们的derivative map保存了高度在两个切线方向上的导数,在切线空间,高度对应的实际上是N法线这条轴,两个切线方向向量分别为(1, 0, x),(0, 1, y),那么叉乘即可得到法线为(-x, -y, 1)。

    float3 flowNormal(float4 flowSample)
    {
        float3 normal = flowSample.agb;
        normal.xy = -(normal.xy * 2 - 1);
        normal.z = 1;
        return normal;
    }
    
    float3 normala = flowNormal(tex2D(_DerivHeightMap, uvwa.xy)) * uvwa.z * finalHeightScale;
    float3 normalb = flowNormal(tex2D(_DerivHeightMap, uvwb.xy)) * uvwb.z * finalHeightScale;
    float3 normal = normalize(normala + normalb);
    

    原教程用的surface shader,这里将其转成vert/frag shader,删去了没用的代码,得到最终的效果如下:

    完整shader代码如下:

    Shader "Custom/DistortionFlowShader"
    {
        Properties
        {
            _MainTex ("Texture", 2D) = "white" {}
            [NoScaleOffset] _FlowMap ("Flow (RG, A noise)", 2D) = "black" {}
            [NoScaleOffset] _DerivHeightMap ("Deriv (AG) Height (B)", 2D) = "black" {}
            _Color ("Color", Color) = (1,1,1,1)
            _UJump ("U jump per phase", Range(-0.25, 0.25)) = 0.25
    		_VJump ("V jump per phase", Range(-0.25, 0.25)) = 0.25
            _Tiling ("Tiling", Float) = 1
            _Speed ("Speed", Float) = 1
            _FlowStrength ("Flow Strength", Float) = 1
            _FlowOffset ("Flow Offset", Float) = 0
            _HeightScale ("Height Scale, Constant", Float) = 0.25
    		_HeightScaleModulated ("Height Scale, Modulated", Float) = 0.75
            _Glossiness ("Smoothness", Range(0,1)) = 0.5
    		_Metallic ("Metallic", Range(0,1)) = 0.0
        }
        SubShader
        {
            Tags { "RenderType"="Opaque" }
            LOD 100
    
            Pass
            {
                CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
    
                #include "UnityCG.cginc"
                #include "Lighting.cginc"
                #include "UnityPBSLighting.cginc"
                #include "AutoLight.cginc"
    
                struct appdata
                {
                    float4 vertex : POSITION;
                    float3 normal : NORMAL;
                    float4 tangent : TANGENT;
                    float2 uv : TEXCOORD0;
                };
    
                struct v2f
                {
                    float2 uv : TEXCOORD0;
                    float4 vertex : SV_POSITION;
                    float3 normal : NORMAL;
                    float4 tangent : TANGENT;
                    float3 viewDir : TEXCOORD1;
                    half3 sh : TEXCOORD2; // SH
                    float4 worldPos : TEXCOORD3;
                    UNITY_SHADOW_COORDS(4)
                };
    
                sampler2D _MainTex;
                float4 _MainTex_ST;
                sampler2D _FlowMap;
                sampler2D _DerivHeightMap;
                fixed4 _Color;
                float _UJump;
                float _VJump;
                float _Tiling;
                float _Speed;
                float _FlowStrength;
                float _FlowOffset;
                float _HeightScale;
                float _HeightScaleModulated;
                half _Glossiness;
    		    half _Metallic;
    
                float3 flowUVW(float4 flowSample, float2 uv, float offset)
                {
                    float2 flowVector = (flowSample.rg * 2 - 1) * _FlowStrength;
                    float flowNoise = flowSample.a;
                    float flowVar = _Time.y * _Speed + flowNoise + offset;
                    float progress = frac(flowVar);
                    uv += flowVector * (progress + _FlowOffset);
                    uv += float2(_UJump, _VJump) * (flowVar - progress);
                    uv *= _Tiling;
                    float w = 1 - 2 * abs(progress - 0.5);
    
                    return float3(uv, w);
                }
    
                float3 flowNormal(float4 flowSample)
                {
                    float3 normal = flowSample.agb;
                    normal.xy = -(normal.xy * 2 - 1);
                    normal.z = 1;
                    return normal;
                }
    
                v2f vert (appdata v)
                {
                    v2f o;
                    o.vertex = UnityObjectToClipPos(v.vertex);
                    o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                    o.normal = v.normal;
                    o.tangent = v.tangent;
                    o.viewDir = WorldSpaceViewDir(v.vertex);
                    o.worldPos = v.vertex;
                    return o;
                }
    
                fixed4 frag (v2f i) : SV_Target
                {
                    float4 flowSample = tex2D(_FlowMap, i.uv);
                    float flowSpeed = flowSample.b * _FlowStrength;
                    float finalHeightScale = flowSpeed * _HeightScaleModulated + _HeightScale;
                    float3 uvwa = flowUVW(flowSample, i.uv, 0);
                    float3 uvwb = flowUVW(flowSample, i.uv, 0.5);
                    fixed4 cola = tex2D(_MainTex, uvwa.xy) * uvwa.z;
                    fixed4 colb = tex2D(_MainTex, uvwb.xy) * uvwb.z;
                    float3 normala = flowNormal(tex2D(_DerivHeightMap, uvwa.xy)) * uvwa.z * finalHeightScale;
                    float3 normalb = flowNormal(tex2D(_DerivHeightMap, uvwb.xy)) * uvwb.z * finalHeightScale;
                    float3 tangentNormal = normalize(normala + normalb);
                    fixed4 texColor = (cola + colb) * _Color;
    
                    float3 normal = normalize(i.normal);
                    float4 tangent = normalize(i.tangent);
                    float3 binormal = cross(normal, tangent) * tangent.w;
                    float3x3 tangentToLocal = {
                        tangent.x,  binormal.x, normal.x,
                        tangent.y,  binormal.y, normal.y,
                        tangent.z,  binormal.z, normal.z
                    };
    
                    float3 worldNormal = normalize(UnityObjectToWorldNormal(mul(tangentToLocal, tangentNormal)));
    
                    fixed4 c = 0;
                    fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
                    float3 worldViewDir = normalize(i.viewDir);
                    UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos)
    
                    UnityGI gi;
                    UNITY_INITIALIZE_OUTPUT(UnityGI, gi);
                    gi.indirect.diffuse = 0;
                    gi.indirect.specular = 0;
                    gi.light.color = _LightColor0.rgb;
                    gi.light.dir = lightDir;
    
                    UnityGIInput giInput;
                    UNITY_INITIALIZE_OUTPUT(UnityGIInput, giInput);
                    giInput.light = gi.light;
                    giInput.worldPos = i.worldPos;
                    giInput.worldViewDir = worldViewDir;
                    giInput.atten = atten;
                    giInput.probeHDR[0] = unity_SpecCube0_HDR;
                    giInput.probeHDR[1] = unity_SpecCube1_HDR;
                    #if defined(UNITY_SPECCUBE_BLENDING) || defined(UNITY_SPECCUBE_BOX_PROJECTION)
                        giInput.boxMin[0] = unity_SpecCube0_BoxMin;
                    #endif
    
                    SurfaceOutputStandard o;
                    o.Albedo = texColor.rgb;
                    o.Metallic = _Metallic;
    			    o.Smoothness = _Glossiness;
                    o.Emission = 0.0;
                    o.Alpha = texColor.a;
                    o.Occlusion = 1.0;
                    o.Normal = worldNormal;
    
                    LightingStandard_GI(o, giInput, gi);
                    c += LightingStandard (o, worldViewDir, gi);
                    UNITY_OPAQUE_ALPHA(c.a);
                    return c;
                }
                ENDCG
            }
        }
    }
    
    
  • 相关阅读:
    2017秋-软件工程第三次作业(3)
    第二周例行总结
    2017秋-软件工程第二次作业
    2017秋-软件工程第一次作业
    ORA-01502: 索引或这类索引的分区处于不可用状态
    Merge into使用详解
    SQL2008中Merge的用法
    system表空间用满解决
    Oracle:ORA-00604: 递归 SQL 级别 1 出现错误
    AIX系统上压缩与解压文件
  • 原文地址:https://www.cnblogs.com/back-to-the-past/p/13475241.html
Copyright © 2011-2022 走看看