Unity3d中,terrain还是比较耗的,为了优化性能,可能需要将terrain转化成mesh。
现提供一工具,思路是根据terrain高度图生成mesh等。
可参考: http://wiki.unity3d.com/index.php?title=TerrainObjExporter
转载请注明出处:
http://www.cnblogs.com/jietian331/p/5831062.html
代码如下:
1 using UnityEditor; 2 using UnityEngine; 3 4 public class TerrainToMeshConverter : ScriptableObject 5 { 6 [MenuItem("Custom/Convert terrain to mesh")] 7 static void Init() 8 { 9 if (Selection.objects.Length <= 0) 10 { 11 Debug.Log("Selection.objects.Length <= 0"); 12 return; 13 } 14 15 var terrainObj = Selection.objects[0] as GameObject; 16 if (terrainObj == null) 17 { 18 Debug.Log("terrainObj == null"); 19 return; 20 } 21 22 var terrain = terrainObj.GetComponent<Terrain>(); 23 if (terrain == null) 24 { 25 Debug.Log("terrain == null"); 26 return; 27 } 28 29 var terrainData = terrain.terrainData; 30 if (terrainData == null) 31 { 32 Debug.Log("terrainData == null"); 33 return; 34 } 35 36 int vertexCountScale = 4; // [dev] 将顶点数稀释 vertexCountScale*vertexCountScale 倍 37 int w = terrainData.heightmapWidth; 38 int h = terrainData.heightmapHeight; 39 Vector3 size = terrainData.size; 40 float[, ,] alphaMapData = terrainData.GetAlphamaps(0, 0, terrainData.alphamapWidth, terrainData.alphamapHeight); 41 Vector3 meshScale = new Vector3(size.x / (w - 1f) * vertexCountScale, 1, size.z / (h - 1f) * vertexCountScale); 42 Vector2 uvScale = new Vector2(1f / (w - 1f), 1f / (h - 1f)) * vertexCountScale * (size.x / terrainData.splatPrototypes[0].tileSize.x); // [dev] 此处有问题,若每个图片大小不一,则出问题。日后改善 43 44 w = (w - 1) / vertexCountScale + 1; 45 h = (h - 1) / vertexCountScale + 1; 46 Vector3[] vertices = new Vector3[w * h]; 47 Vector2[] uvs = new Vector2[w * h]; 48 Vector4[] alphasWeight = new Vector4[w * h]; // [dev] 只支持4张图片 49 50 // 顶点,uv,每个顶点每个图片所占比重 51 for (int i = 0; i < w; i++) 52 { 53 for (int j = 0; j < h; j++) 54 { 55 int index = j * w + i; 56 float z = terrainData.GetHeight(i * vertexCountScale, j * vertexCountScale); 57 vertices[index] = Vector3.Scale(new Vector3(i, z, j), meshScale); 58 uvs[index] = Vector2.Scale(new Vector2(i, j), uvScale); 59 60 // alpha map 61 int i2 = (int)(i * terrainData.alphamapWidth / (w - 1f)); 62 int j2 = (int)(j * terrainData.alphamapHeight / (h - 1f)); 63 i2 = Mathf.Min(terrainData.alphamapWidth - 1, i2); 64 j2 = Mathf.Min(terrainData.alphamapHeight - 1, j2); 65 var alpha0 = alphaMapData[j2, i2, 0]; 66 var alpha1 = alphaMapData[j2, i2, 1]; 67 var alpha2 = alphaMapData[j2, i2, 2]; 68 var alpha3 = alphaMapData[j2, i2, 3]; 69 alphasWeight[index] = new Vector4(alpha0, alpha1, alpha2, alpha3); 70 } 71 } 72 73 /* 74 * 三角形 75 * b c 76 * ******* 77 * * * * 78 * * * * 79 * ******* 80 * a d 81 */ 82 int[] triangles = new int[(w - 1) * (h - 1) * 6]; 83 int triangleIndex = 0; 84 for (int i = 0; i < w - 1; i++) 85 { 86 for (int j = 0; j < h - 1; j++) 87 { 88 int a = j * w + i; 89 int b = (j + 1) * w + i; 90 int c = (j + 1) * w + i + 1; 91 int d = j * w + i + 1; 92 93 triangles[triangleIndex++] = a; 94 triangles[triangleIndex++] = b; 95 triangles[triangleIndex++] = c; 96 97 triangles[triangleIndex++] = a; 98 triangles[triangleIndex++] = c; 99 triangles[triangleIndex++] = d; 100 } 101 } 102 103 Mesh mesh = new Mesh(); 104 mesh.vertices = vertices; 105 mesh.uv = uvs; 106 mesh.triangles = triangles; 107 mesh.tangents = alphasWeight; // 将地形纹理的比重写入到切线中 108 109 string transName = "[dev]MeshFromTerrainData"; 110 var t = terrainObj.transform.parent.Find(transName); 111 if (t == null) 112 { 113 GameObject go = new GameObject(transName, typeof(MeshFilter), typeof(MeshRenderer), typeof(MeshCollider)); 114 t = go.transform; 115 } 116 117 // 地形渲染 118 MeshRenderer mr = t.GetComponent<MeshRenderer>(); 119 Material mat = mr.sharedMaterial; 120 if (!mat) 121 mat = new Material(Shader.Find("Custom/Environment/TerrainSimple")); 122 123 for (int i = 0; i < terrainData.splatPrototypes.Length; i++) 124 { 125 var sp = terrainData.splatPrototypes[i]; 126 mat.SetTexture("_Texture" + i, sp.texture); 127 } 128 129 t.parent = terrainObj.transform.parent; 130 t.position = terrainObj.transform.position; 131 t.gameObject.layer = terrainObj.layer; 132 t.GetComponent<MeshFilter>().sharedMesh = mesh; 133 t.GetComponent<MeshCollider>().sharedMesh = mesh; 134 mr.sharedMaterial = mat; 135 136 t.gameObject.SetActive(true); 137 terrainObj.SetActive(false); 138 139 Debug.Log("Convert terrain to mesh finished!"); 140 } 141 }
渲染地形的shader如下(不支持光照):
1 Shader "Custom/Environment/TerrainSimple" 2 { 3 Properties 4 { 5 _Texture0 ("Texture 1", 2D) = "white" {} 6 _Texture1 ("Texture 2", 2D) = "white" {} 7 _Texture2 ("Texture 3", 2D) = "white" {} 8 _Texture3 ("Texture 4", 2D) = "white" {} 9 } 10 11 SubShader 12 { 13 Tags { "RenderType" = "Opaque" } 14 LOD 200 15 16 Pass 17 { 18 CGPROGRAM 19 #pragma vertex vert 20 #pragma fragment frag 21 22 sampler2D _Texture0; 23 sampler2D _Texture1; 24 sampler2D _Texture2; 25 sampler2D _Texture3; 26 27 struct appdata 28 { 29 float4 vertex : POSITION; 30 float2 uv : TEXCOORD0; 31 float4 tangent : TANGENT; 32 }; 33 34 struct v2f 35 { 36 float4 pos : SV_POSITION; 37 float2 uv : TEXCOORD0; 38 float4 weight : TEXCOORD1; 39 }; 40 41 v2f vert(appdata v) 42 { 43 v2f o; 44 o.pos = UnityObjectToClipPos(v.vertex); 45 o.weight = v.tangent; 46 o.uv = v.uv; 47 return o; 48 } 49 50 fixed4 frag(v2f i) : SV_TARGET 51 { 52 fixed4 t0 = tex2D(_Texture0, i.uv); 53 fixed4 t1 = tex2D(_Texture1, i.uv); 54 fixed4 t2 = tex2D(_Texture2, i.uv); 55 fixed4 t3 = tex2D(_Texture3, i.uv); 56 fixed4 tex = t0 * i.weight.x + t1 * i.weight.y + t2 * i.weight.z + t3 * i.weight.w; 57 return tex; 58 } 59 60 ENDCG 61 } 62 } 63 64 Fallback "Diffuse" 65 }
生成的mesh与原terrain对比如下,左边为mesh,右边为terrain:
另提供一支持光照的地形shader:
1 // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)' 2 3 Shader "Custom/Environment/LightedTerrain" 4 { 5 Properties 6 { 7 _Texture0 ("Texture 1", 2D) = "white" {} 8 _Texture1 ("Texture 2", 2D) = "white" {} 9 _Texture2 ("Texture 3", 2D) = "white" {} 10 _Texture3 ("Texture 4", 2D) = "white" {} 11 } 12 13 SubShader 14 { 15 Tags { "RenderType" = "Opaque" } 16 LOD 200 17 18 Pass 19 { 20 Tags 21 { 22 "LightMode" = "ForwardBase" 23 } 24 25 CGPROGRAM 26 #pragma vertex vert 27 #pragma fragment frag 28 #pragma multi_compile_fwdbase 29 #pragma multi_compile_fog 30 31 #include "UnityCG.cginc" 32 #include "Lighting.cginc" 33 #include "AutoLight.cginc" 34 35 sampler2D _Texture0; 36 sampler2D _Texture1; 37 sampler2D _Texture2; 38 sampler2D _Texture3; 39 40 struct appdata 41 { 42 float4 vertex : POSITION; 43 float2 uv : TEXCOORD0; 44 float4 tangent : TANGENT; 45 float3 normal : NORMAL; 46 }; 47 48 struct v2f 49 { 50 float4 pos : SV_POSITION; 51 float2 uv : TEXCOORD0; 52 float4 weight : TEXCOORD1; 53 float3 worldPos : TEXCOORD2; 54 float3 worldNormal : TEXCOORD3; 55 SHADOW_COORDS(4) 56 UNITY_FOG_COORDS(5) 57 }; 58 59 v2f vert(appdata v) 60 { 61 v2f o; 62 o.pos = UnityObjectToClipPos(v.vertex); 63 o.weight = v.tangent; 64 o.uv = v.uv; 65 o.worldPos = mul(unity_ObjectToWorld, v.vertex); 66 o.worldNormal = UnityObjectToWorldNormal(v.normal); 67 TRANSFER_SHADOW(o); 68 UNITY_TRANSFER_FOG(o, o.pos); 69 return o; 70 } 71 72 fixed4 frag(v2f i) : SV_TARGET 73 { 74 fixed4 t0 = tex2D(_Texture0, i.uv); 75 fixed4 t1 = tex2D(_Texture1, i.uv); 76 fixed4 t2 = tex2D(_Texture2, i.uv); 77 fixed4 t3 = tex2D(_Texture3, i.uv); 78 fixed4 tex = t0 * i.weight.x + t1 * i.weight.y + t2 * i.weight.z + t3 * i.weight.w; 79 80 fixed3 albedo = tex.rgb; 81 82 fixed3 ambient = albedo * UNITY_LIGHTMODEL_AMBIENT.rgb; 83 84 float3 worldLight = normalize(UnityWorldSpaceLightDir(i.worldPos)); 85 float halfLambert = dot(worldLight, i.worldNormal) * 0.5 + 0.5; 86 fixed3 diffuse = albedo * _LightColor0.rgb * halfLambert; 87 88 float3 worldView = normalize(UnityWorldSpaceViewDir(i.worldPos)); 89 float3 halfDir = normalize(worldView + worldLight); 90 fixed3 specular = albedo * _LightColor0.rgb * max(dot(halfDir, i.worldNormal), 0); 91 92 UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos); 93 fixed4 col = fixed4(ambient + (diffuse + specular) * atten, tex.a); 94 UNITY_APPLY_FOG(i.fogCoord, col); 95 96 return col; 97 } 98 99 ENDCG 100 } 101 102 Pass 103 { 104 Tags 105 { 106 "LightMode" = "ForwardAdd" 107 } 108 Blend One One 109 110 CGPROGRAM 111 #pragma vertex vert 112 #pragma fragment frag 113 #pragma multi_compile_fwdadd 114 115 #include "UnityCG.cginc" 116 #include "Lighting.cginc" 117 #include "AutoLight.cginc" 118 119 sampler2D _Texture0; 120 sampler2D _Texture1; 121 sampler2D _Texture2; 122 sampler2D _Texture3; 123 124 struct appdata 125 { 126 float4 vertex : POSITION; 127 float2 uv : TEXCOORD0; 128 float4 tangent : TANGENT; 129 float3 normal : NORMAL; 130 }; 131 132 struct v2f 133 { 134 float4 pos : SV_POSITION; 135 float2 uv : TEXCOORD0; 136 float4 weight : TEXCOORD1; 137 float3 worldPos : TEXCOORD2; 138 float3 worldNormal : TEXCOORD3; 139 SHADOW_COORDS(4) 140 }; 141 142 v2f vert(appdata v) 143 { 144 v2f o; 145 o.pos = UnityObjectToClipPos(v.vertex); 146 o.weight = v.tangent; 147 o.uv = v.uv; 148 o.worldPos = mul(unity_ObjectToWorld, v.vertex); 149 o.worldNormal = UnityObjectToWorldNormal(v.normal); 150 TRANSFER_SHADOW(o); 151 return o; 152 } 153 154 fixed4 frag(v2f i) : SV_TARGET 155 { 156 fixed4 t0 = tex2D(_Texture0, i.uv); 157 fixed4 t1 = tex2D(_Texture1, i.uv); 158 fixed4 t2 = tex2D(_Texture2, i.uv); 159 fixed4 t3 = tex2D(_Texture3, i.uv); 160 fixed4 tex = t0 * i.weight.x + t1 * i.weight.y + t2 * i.weight.z + t3 * i.weight.w; 161 162 fixed3 albedo = tex.rgb; 163 164 float3 worldLight = normalize(UnityWorldSpaceLightDir(i.worldPos)); 165 float halfLambert = dot(worldLight, i.worldNormal) * 0.5 + 0.5; 166 fixed3 diffuse = albedo * _LightColor0.rgb * halfLambert; 167 168 float3 worldView = normalize(UnityWorldSpaceViewDir(i.worldPos)); 169 float3 halfDir = normalize(worldView + worldLight); 170 fixed3 specular = albedo * _LightColor0.rgb * max(dot(halfDir, i.worldNormal), 0); 171 172 UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos); 173 fixed4 col = fixed4((diffuse + specular ) * atten, tex.a); 174 175 return col; 176 } 177 178 ENDCG 179 } 180 } 181 182 Fallback "Diffuse" 183 }
光照效果如下: