zoukankan      html  css  js  c++  java
  • water effect(1)

      在GPU Gems1的第一小节中,可以看到通过Gerstner wave function来模拟海洋的波浪效果,同时在Unity自带的Water(Pro)效果中,FX/Water4中的部分水波效果正是通过该公式来模拟的。

      首先来看看FX-Water4.shader的代码。

        Tags {"RenderType"="Transparent" "Queue"="Transparent"}
    
        Lod 500
        ColorMask RGB
        
        GrabPass { "_RefractionTex" }
        Pass {    
                #pragma vertex vert
                #pragma fragment frag    
        }
    }
    
    Subshader 
    {     
        Tags {"RenderType"="Transparent" "Queue"="Transparent"}
        
        Lod 300
        ColorMask RGB
        
        Pass { 
                #pragma vertex vert300
                #pragma fragment frag300      
        }    
    }
    Subshader 
    {     
        Tags {"RenderType"="Transparent" "Queue"="Transparent"}
        
        Lod 200
        ColorMask RGB
        
        Pass {
                #pragma vertex vert200
                #pragma fragment frag200
        }    
    }
    
    Fallback "Transparent/Diffuse"
    其中包含三个subshader,具体执行哪一个Subshader,是通过waterBase.cs来设置sharedMaterial.shader.maximumLOD设置的。我们从low quality开始分析,顶点着色器的代码如下。
        v2f_simple vert200(appdata_full v)
        { 
            v2f_simple o;
            //计算出顶点的世界坐标
            half3 worldSpaceVertex = mul(_Object2World, v.vertex).xyz;
            half2 tileableUv = worldSpaceVertex.xz;
                    
            //通过设置的 _BumpDirection.xy值来模拟第一个方向,zw值模拟另一个方向, _BumpTiling.xy模拟第一个方向的速率,zw模拟第二个方向的变化速率。
            o.bumpCoords.xyzw = (tileableUv.xyxy + _Time.xxxx * _BumpDirection.xyzw) * _BumpTiling.xyzw;    
    
           //viewInterpolator是用来计算相机到该顶点的方向。
            o.viewInterpolator.xyz = worldSpaceVertex-_WorldSpaceCameraPos;
            
            //pos暂时无用。
            o.pos = mul(UNITY_MATRIX_MVP,  v.vertex);
            
            o.viewInterpolator.w = 1;//GetDistanceFadeout(ComputeScreenPos(o.pos).w, DISTANCE_SCALE); 
        
            return o;
    
        }                                                                                                                

     在片元着色器中,我们可以看到法线的计算是通过PerPixelNormal函数实现的,在WaterInclude.cginc中可以找到该函数。

    inline half3 PerPixelNormal(sampler2D bumpMap, half4 coords, half3 vertexNormal, half bumpStrength) 
    {
    //计算normal map中两个不同方向的值。
        half4 bump = tex2D(bumpMap, coords.xy) + tex2D(bumpMap, coords.zw);
    
    //计算出法线方向
        bump.xy = bump.wy - half2(1.0, 1.0);
    
    //通过bumpStrength来增大x,z相对于Y的比例,即增加纹理的凹凸感,在unity 中可以看到,bumpStrength越大,水面反光效果越明显。
        half3 worldNormal = vertexNormal + bump.xxy * bumpStrength * half3(1,0,1);
    
    //归一化计算。
        return normalize(worldNormal);
    } 

     得到法线方向后,具体的片元着色器代码如下。

        half4 frag200( v2f_simple i ) : SV_Target
        {        
    //法线计算
            half3 worldNormal = PerPixelNormal(_BumpMap, i.bumpCoords, half3(0,1,0), PER_PIXEL_DISPLACE);
    
    //归一化视角向量
            half3 viewVector = normalize(i.viewInterpolator.xyz);
    
    //暂时无用
            half3 reflectVector = normalize(reflect(viewVector, worldNormal));
    
    //半角向量计算,减少计算光线与法线之间的计算量(Jim Blinn),与binnPhong高光模型计算一致          
            half3 h = normalize ((_WorldLightDir.xyz) + viewVector.xyz);
    
            float nh = max (0, dot (worldNormal, -h));
            float spec = max(0.0,pow (nh, _Shininess));    
    
    //通过Fresnel参数控制缩放
            worldNormal.xz *= _FresnelScale;    
    
    //计算出fresnel反射率。
            half refl2Refr = Fresnel(viewVector, worldNormal, FRESNEL_BIAS, FRESNEL_POWER);    
    
            half4 baseColor = _BaseColor;
    
    //通过fresnel反射率混合折射与反射颜色。
            baseColor = lerp(baseColor, _ReflectionColor, saturate(refl2Refr * 2.0));
    
    //fresnel反射率越大,alpha越大,水下物品越看不清。
            baseColor.a = saturate(2.0 * refl2Refr + 0.5);
    
    //与高光混合,最终颜色计算。
            baseColor.rgb += spec * _SpecularColor.rgb;
            return baseColor;    
        }

    核心是fresnel反射率的计算,fresnel的基本原理是视线与法线的角度越小,即与平面越垂直,反射越小,折射越大,水下的物品越清晰,视线与平面越平行,反射越大,水下物品越看不清。在WaterINclude.cginc中有简易的fresnel的计算公式。

    inline half Fresnel(half3 viewVector, half3 worldNormal, half bias, half power)
    {
    //夹角的计算。
        half facing =  clamp(1.0-max(dot(-viewVector, worldNormal), 0.0), 0.0,1.0);
    //反射率的计算。    
        half refl2Refr = saturate(bias+(1.0-bias) * pow(facing,power));    
        return refl2Refr;    
    }

      这一节主要是通过UV纹理的变化来模拟简易水波,但无法在相同的normal map下实现大波浪似的效果。下一节主要介绍Gerstner wave function如何更实现高级的水波效果。

  • 相关阅读:
    Docker删除某个容器时失败解决方案
    Docker搭建redis
    Django优雅集成MongoDB
    MongoDB学习笔记:文档Crud Shell
    MongoDB学习笔记:MongoDB 数据库的命名、设计规范
    MongoDB学习笔记:快速入门
    MongoDB学习笔记:Python 操作MongoDB
    在Docker中安装MongoDB
    Linux 挂载盘
    java中Array/List/Map/Object与Json互相转换详解(转载)
  • 原文地址:https://www.cnblogs.com/VanHu/p/4890495.html
Copyright © 2011-2022 走看看