Unity中海水仿真渲染教程 VR资源

清风 2017-08-28 17:19:42
海水仿真渲染一直是比较困难的事情,虽然市面上有了各种海水的渲染算法,但是真正做到仿真渲染的少之又少。大多停留在试验阶段,达到了仿真的要求,但是硬件配置要求高,不利于推广。本篇博客给读者介绍的是关于海水的实施渲染以及通过算法实现船只航行轨迹效果,真实的达到了海水的渲染,海水的网格采用了多个面片网格拼接的方式,网格采用的是LOD处理的,这样就优化了效率。同时将海水的绘制以及算法实现放到C++中进行,这样对于复杂算法的实现效率明显提升。先给读者看几幅效果图:
 

  船在海水中航行的轨迹效果图,轨迹是实时绘制的。再来一副海水到岸边产生的泡沫效果图:
 

  除了岸边的效果图外,船在水中周围也会产生泡沫效果。最后一副效果图如下:
 


  海水的反射折射效果。下面开始给读者介绍实现该海水的原理以及核心代码,最后把整个工程奉献给读者。
  第一步:海水网格的实现,海水网格采用的是面片拼接的方式,并且面片采用了LOD运算,其在Unity中的效果如下所示:
 

  从里向外,面片的数量逐步减少,它是根据摄像机的远近处理的,对应的核心代码如下所示:
 
 
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
//update the meshes with the final calculated mesh data 
void updateTiles(int a, int b) { 
   
    if(skipLods) { 
        lodSkip++; 
        if(lodSkip >= lodSkipFrames+1) lodSkip=0; 
    
   
    for (int L0D=a; L0D<b; L0D++) { 
        //if(L0D> 
        //this will skip one update of the tiles higher then Lod0 
        if(L0D>0 && lodSkip==0 && !ticked && skipLods) { break; } 
        //this will skip one update of the LOD0 tiles because they got updated earlier when they should. 
        if(ticked2 && L0D==0) { ticked2=false; continue; } 
  
        #if !NATIVE 
            int den = MyIntPow (2, L0D); 
            int idx = 0; 
   
            for (int y=0; y<g_height; y+=den) { 
                for (int x=0; x<g_width; x+=den) { 
                    int idx2 = g_width * y + x; 
                    verticesLOD[L0D] [idx] = vertices [idx2]; 
                    //lower the far lods to eliminate gaps in the horizon when having big waves 
                    if(L0D>0) { 
                        if(farLodOffset!=0) { 
                            verticesLOD[L0D] [idx].y += flodoffset[L0D] * flodFact; 
                        
                    
                    tangentsLOD[L0D] [idx] = tangents [idx2]; 
                    normalsLOD[L0D] [idx++] = normals [idx2]; 
                }            
            
        #else 
            uocean._updateTilesA(verticesLOD[L0D], vertices, tangentsLOD[L0D], tangents, normalsLOD[L0D], normals, L0D, farLodOffset, flodoffset, flodFact); 
        #endif 
   
        btiles_LOD[L0D].vertices = verticesLOD[L0D]; 
        btiles_LOD[L0D].normals = normalsLOD[L0D]; 
        btiles_LOD[L0D].tangents = tangentsLOD[L0D]; 
    
   
    if(ticked) ticked = false
   
       
void GenerateTiles() { 
   
    int chDist, nmaxLod=0; // Chebychev distance 
       
    for (int y=0; y<tiles; y++) { 
        for (int x=0; x<tiles; x++) { 
            chDist = System.Math.Max (System.Math.Abs (tiles / 2 - y), System.Math.Abs (tiles / 2 - x)); 
            chDist = chDist > 0 ? chDist - 1 : 0; 
            if(nmaxLod<chDist) nmaxLod = chDist; 
        
    
    max_LOD = nmaxLod+1; 
   
    flodoffset = new float[max_LOD+1]; 
    float ffact = farLodOffset/max_LOD; 
    for(int i=0; i<max_LOD+1; i++) { 
        flodoffset[i] = i*ffact; 
    
   
    btiles_LOD = new List<Mesh>(); 
    tiles_LOD = new List<List<Mesh>>(); 
   
    for (int L0D=0; L0D<max_LOD; L0D++) { 
        btiles_LOD.Add(new Mesh()); 
        tiles_LOD.Add (new List<Mesh>()); 
    
   
    GameObject tile; 
   
    int ntl = LayerMask.NameToLayer ("Water"); 
   
    for (int y=0; y<tiles; y++) { 
        for (int x=0; x<tiles; x++) { 
            chDist = System.Math.Max (System.Math.Abs (tiles / 2 - y), System.Math.Abs (tiles / 2 - x)); 
            chDist = chDist > 0 ? chDist - 1 : 0; 
            if(nmaxLod<chDist) nmaxLod = chDist; 
            float cy = y - Mathf.Floor(tiles * 0.5f); 
            float cx = x - Mathf.Floor(tiles * 0.5f); 
            tile = new GameObject ("Lod_"+chDist.ToString()+":"+y.ToString()+"x"+x.ToString()); 
                  
               Vector3 pos=tile.transform.position; 
            pos.x = cx * size.x; 
            pos.y = transform.position.y; 
            pos.z = cy * size.z; 
   
            tile.transform.position=pos; 
            tile.AddComponent <MeshFilter>(); 
            tile.AddComponent <MeshRenderer>(); 
               Renderer renderer = tile.GetComponent<Renderer>(); 
   
            tile.GetComponent<MeshFilter>().mesh = btiles_LOD[chDist]; 
            //tile.isStatic = true; 
   
            //shader/material lod (needs improvement) 
            if(useShaderLods && numberLods>1) { 
                if(numberLods==2) { 
                    if(chDist <= sTilesLod) { if(material) renderer.material = material; } 
                    if(chDist > sTilesLod) { if(material1) renderer.material = material1; } 
                }else if(numberLods==3){ 
                    if(chDist <= sTilesLod ) { if(material) renderer.material = material; } 
                    if(chDist == sTilesLod+1) { if(material1) renderer.material = material1; } 
                    if(chDist > sTilesLod+1) { if(material2) renderer.material = material2; } 
                
            } else
                renderer.material = material; 
            
                  
               //Make child of this object, so we don't clutter up the 
               //scene hierarchy more than necessary. 
               tile.transform.parent = transform; 
           
            //Also we don't want these to be drawn while doing refraction/reflection passes, 
            //so we'll add the to the water layer for easy filtering. 
            tile.layer = ntl; 
   
            tiles_LOD[chDist].Add( tile.GetComponent<MeshFilter>().mesh); 
        }  
    
   
    //enable/disable the fixed disc 
    initDisc(); 
}


  在Unity中生成的效果如下所示:
 
 
  第二步,海水的渲染Shader,根据不同的LOD等级实行不同的Shader渲染,举个例子,LOD等级2的Shader代码如下所示:
 
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
Shader "Mobile/OceanL2"
    Properties { 
        _SurfaceColor ("SurfaceColor", Color) = (1,1,1,1) 
        _WaterColor ("WaterColor", Color) = (1,1,1,1) 
   
        _Specularity ("Specularity", Range(0.01,1)) = 0.3 
        _SpecPower("Specularity Power", Range(0,1)) = 1 
   
        [HideInInspector] _SunColor ("SunColor", Color) = (1,1,0.901,1) 
   
        _Bump ("Bump (RGB)", 2D) = "bump" {} 
        _Size ("UVSize", Float) = 0.015625//this is the best value (1/64) to have the same uv scales of normal and foam maps on all ocean sizes 
        [HideInInspector] _SunDir ("SunDir", Vector) = (0.3, -0.6, -1, 0) 
   
        _FakeUnderwaterColor ("Water Color LOD1", Color) = (0.196, 0.262, 0.196, 1) 
        _DistanceCancellation ("Distance Cancellation", Float) = 2000 
    
       
   
//water bump 
     SubShader { 
        Tags { "RenderType" = "Opaque" "Queue"="Geometry"
        LOD 2 
        Pass { 
            CGPROGRAM 
            #pragma vertex vert 
            #pragma fragment frag 
            //#pragma multi_compile_fog 
            #pragma multi_compile FOGON FOGOFF 
            #pragma multi_compile DCON  DCOFF 
  
              
  
            #pragma target 2.0 
            #include "UnityCG.cginc" 
   
            struct v2f { 
                float4 pos : SV_POSITION; 
                half3 floatVec : TEXCOORD0; 
                float2  bumpTexCoord : TEXCOORD1; 
                //half3  viewDir : TEXCOORD2; 
                half3  lightDir : TEXCOORD2; 
                half2 buv : TEXCOORD3; 
                half3 normViewDir : TEXCOORD4; 
                //UNITY_FOG_COORDS(7) 
                #ifdef FOGON 
                half dist : TEXCOORD5; 
                #ifdef DCON 
                half distCancellation : TEXCOORD6; 
                #endif 
                #endif 
            }; 
   
            half _Size; 
            half4 _SunDir; 
            half4 _FakeUnderwaterColor; 
            #ifdef FOGON 
            uniform half4 unity_FogStart; 
            uniform half4 unity_FogEnd; 
            uniform half4 unity_FogDensity; 
            #ifdef DCON 
            half _DistanceCancellation; 
            #endif 
            #endif 
                          
            v2f vert (appdata_tan v) { 
                v2f o; 
                UNITY_INITIALIZE_OUTPUT(v2f, o); 
   
                o.bumpTexCoord.xy = v.vertex.xz*_Size;///float2(_Size.x, _Size.z)*5; 
                o.pos = mul (UNITY_MATRIX_MVP, v.vertex); 
       
                half3 objSpaceViewDir = ObjSpaceViewDir(v.vertex); 
                half3 binormal = cross( normalize(v.normal), normalize(v.tangent.xyz) ); 
                half3x3 rotation = half3x3( v.tangent.xyz, binormal, v.normal ); 
       
                half3 viewDir = mul(rotation, objSpaceViewDir); 
                o.lightDir = mul(rotation, half3(_SunDir.xyz)); 
   
                o.buv = float2(o.bumpTexCoord.x + _CosTime.x * 0.2, o.bumpTexCoord.y + _SinTime.x * 0.3); 
   
                o.normViewDir = normalize(viewDir); 
   
                o.floatVec = normalize(o.normViewDir - normalize(o.lightDir)); 
  
                #ifdef FOGON 
                //manual fog 
                half fogDif = 1.0/(unity_FogEnd.x - unity_FogStart.x); 
                o.dist = (unity_FogEnd.x - length(o.pos.xyz)) * fogDif; 
                #ifdef DCON 
                o.distCancellation = (unity_FogEnd.x - _DistanceCancellation) * fogDif; 
                #endif 
                #endif 
   
                //autofog 
                //UNITY_TRANSFER_FOG(o, o.pos); 
   
                return o; 
            
   
            sampler2D _Bump; 
            half4 _WaterColor; 
            half4 _SurfaceColor; 
            half _Specularity; 
            half _SpecPower; 
            half4 _SunColor; 
   
            half4 frag (v2f i) : COLOR { 
  
                #ifdef FOGON 
                #ifdef DCON 
                if(i.dist>i.distCancellation){ 
                #endif 
                #endif 
                    half3 tangentNormal0 = (tex2D(_Bump, i.buv.xy) * 2.0) -1; 
                    half3 tangentNormal = normalize(tangentNormal0); 
   
                    half4 result = half4(0, 0, 0, 1); 
                   
                    //half fresnelLookup = dot(tangentNormal,i. normViewDir); 
                    //float bias = 0.06; 
                    //float power = 4.0; 
                    //half fresnelTerm = 0.06 + (1.0-0.06)*pow(1.0 - fresnelLookup, 4.0); 
   
                    half fresnelTerm = 1.0 - saturate(dot (i.normViewDir, tangentNormal0)); 
   
                    half specular = pow(max(dot(i.floatVec,  tangentNormal) , 0.0), 250.0 * _Specularity ) * _SpecPower; 
                   
                    result.rgb = lerp(_WaterColor*_FakeUnderwaterColor, _SunColor.rgb*_SurfaceColor*0.85, fresnelTerm*0.6)  + specular*_SunColor.rgb; 
   
                    //fog 
                    //UNITY_APPLY_FOG(i.fogCoord, result);  
  
                    #ifdef FOGON 
                    //manual fog (linear) (reduces instructions on d3d9) 
                    float ff = saturate(i.dist); 
                    result.rgb = lerp(unity_FogColor.rgb, result.rgb, ff); 
                    #endif 
   
                    return result; 
                #ifdef FOGON 
                #ifdef DCON 
                }else
                    return unity_FogColor; 
                
                #endif 
                #endif 
            
            ENDCG 
               
   
        
    
   
       
}
  渲染的效果图如下所示:
 
  远近海水很有层次感效果。
  第三步:船只在海水中航行的轨迹效果以及随着海水起伏效果实现代码