Unity3d之将terrain转化成mesh

时间:2023-03-08 15:34:54

Unity3d中,terrain还是比较耗的,为了优化性能,可能需要将terrain转化成mesh。

现提供一工具,思路是根据terrain高度图生成mesh等。

可参考: http://wiki.unity3d.com/index.php?title=TerrainObjExporter

转载请注明出处:

http://www.cnblogs.com/jietian331/p/5831062.html

代码如下:

 using UnityEditor;
using UnityEngine; public class TerrainToMeshConverter : ScriptableObject
{
[MenuItem("Custom/Convert terrain to mesh")]
static void Init()
{
if (Selection.objects.Length <= )
{
Debug.Log("Selection.objects.Length <= 0");
return;
} var terrainObj = Selection.objects[] as GameObject;
if (terrainObj == null)
{
Debug.Log("terrainObj == null");
return;
} var terrain = terrainObj.GetComponent<Terrain>();
if (terrain == null)
{
Debug.Log("terrain == null");
return;
} var terrainData = terrain.terrainData;
if (terrainData == null)
{
Debug.Log("terrainData == null");
return;
} int vertexCountScale = ; // [dev] 将顶点数稀释 vertexCountScale*vertexCountScale 倍
int w = terrainData.heightmapWidth;
int h = terrainData.heightmapHeight;
Vector3 size = terrainData.size;
float[, ,] alphaMapData = terrainData.GetAlphamaps(, , terrainData.alphamapWidth, terrainData.alphamapHeight);
Vector3 meshScale = new Vector3(size.x / (w - 1f) * vertexCountScale, , size.z / (h - 1f) * vertexCountScale);
Vector2 uvScale = new Vector2(1f / (w - 1f), 1f / (h - 1f)) * vertexCountScale * (size.x / terrainData.splatPrototypes[].tileSize.x); // [dev] 此处有问题,若每个图片大小不一,则出问题。日后改善 w = (w - ) / vertexCountScale + ;
h = (h - ) / vertexCountScale + ;
Vector3[] vertices = new Vector3[w * h];
Vector2[] uvs = new Vector2[w * h];
Vector4[] alphasWeight = new Vector4[w * h]; // [dev] 只支持4张图片 // 顶点,uv,每个顶点每个图片所占比重
for (int i = ; i < w; i++)
{
for (int j = ; j < h; j++)
{
int index = j * w + i;
float z = terrainData.GetHeight(i * vertexCountScale, j * vertexCountScale);
vertices[index] = Vector3.Scale(new Vector3(i, z, j), meshScale);
uvs[index] = Vector2.Scale(new Vector2(i, j), uvScale); // alpha map
int i2 = (int)(i * terrainData.alphamapWidth / (w - 1f));
int j2 = (int)(j * terrainData.alphamapHeight / (h - 1f));
i2 = Mathf.Min(terrainData.alphamapWidth - , i2);
j2 = Mathf.Min(terrainData.alphamapHeight - , j2);
var alpha0 = alphaMapData[j2, i2, ];
var alpha1 = alphaMapData[j2, i2, ];
var alpha2 = alphaMapData[j2, i2, ];
var alpha3 = alphaMapData[j2, i2, ];
alphasWeight[index] = new Vector4(alpha0, alpha1, alpha2, alpha3);
}
} /*
* 三角形
* b c
* *******
* * * *
* * * *
* *******
* a d
*/
int[] triangles = new int[(w - ) * (h - ) * ];
int triangleIndex = ;
for (int i = ; i < w - ; i++)
{
for (int j = ; j < h - ; j++)
{
int a = j * w + i;
int b = (j + ) * w + i;
int c = (j + ) * w + i + ;
int d = j * w + i + ; triangles[triangleIndex++] = a;
triangles[triangleIndex++] = b;
triangles[triangleIndex++] = c; triangles[triangleIndex++] = a;
triangles[triangleIndex++] = c;
triangles[triangleIndex++] = d;
}
} Mesh mesh = new Mesh();
mesh.vertices = vertices;
mesh.uv = uvs;
mesh.triangles = triangles;
mesh.tangents = alphasWeight; // 将地形纹理的比重写入到切线中 string transName = "[dev]MeshFromTerrainData";
var t = terrainObj.transform.parent.Find(transName);
if (t == null)
{
GameObject go = new GameObject(transName, typeof(MeshFilter), typeof(MeshRenderer), typeof(MeshCollider));
t = go.transform;
} // 地形渲染
MeshRenderer mr = t.GetComponent<MeshRenderer>();
Material mat = mr.sharedMaterial;
if (!mat)
mat = new Material(Shader.Find("Custom/Environment/TerrainSimple")); for (int i = ; i < terrainData.splatPrototypes.Length; i++)
{
var sp = terrainData.splatPrototypes[i];
mat.SetTexture("_Texture" + i, sp.texture);
} t.parent = terrainObj.transform.parent;
t.position = terrainObj.transform.position;
t.gameObject.layer = terrainObj.layer;
t.GetComponent<MeshFilter>().sharedMesh = mesh;
t.GetComponent<MeshCollider>().sharedMesh = mesh;
mr.sharedMaterial = mat; t.gameObject.SetActive(true);
terrainObj.SetActive(false); Debug.Log("Convert terrain to mesh finished!");
}
}

TerrainToMeshConverter

渲染地形的shader如下(不支持光照):

 Shader "Custom/Environment/TerrainSimple"
{
Properties
{
_Texture0 ("Texture 1", 2D) = "white" {}
_Texture1 ("Texture 2", 2D) = "white" {}
_Texture2 ("Texture 3", 2D) = "white" {}
_Texture3 ("Texture 4", 2D) = "white" {}
} SubShader
{
Tags { "RenderType" = "Opaque" }
LOD Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag sampler2D _Texture0;
sampler2D _Texture1;
sampler2D _Texture2;
sampler2D _Texture3; struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float4 tangent : TANGENT;
}; struct v2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float4 weight : TEXCOORD1;
}; v2f vert(appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.weight = v.tangent;
o.uv = v.uv;
return o;
} fixed4 frag(v2f i) : SV_TARGET
{
fixed4 t0 = tex2D(_Texture0, i.uv);
fixed4 t1 = tex2D(_Texture1, i.uv);
fixed4 t2 = tex2D(_Texture2, i.uv);
fixed4 t3 = tex2D(_Texture3, i.uv);
fixed4 tex = t0 * i.weight.x + t1 * i.weight.y + t2 * i.weight.z + t3 * i.weight.w;
return tex;
} ENDCG
}
} Fallback "Diffuse"
}

Custom/Environment/TerrainSimple

生成的mesh与原terrain对比如下,左边为mesh,右边为terrain:

Unity3d之将terrain转化成mesh

另提供一支持光照的地形shader:

 // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

 Shader "Custom/Environment/LightedTerrain"
{
Properties
{
_Texture0 ("Texture 1", 2D) = "white" {}
_Texture1 ("Texture 2", 2D) = "white" {}
_Texture2 ("Texture 3", 2D) = "white" {}
_Texture3 ("Texture 4", 2D) = "white" {}
} SubShader
{
Tags { "RenderType" = "Opaque" }
LOD Pass
{
Tags
{
"LightMode" = "ForwardBase"
} CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#pragma multi_compile_fog #include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc" sampler2D _Texture0;
sampler2D _Texture1;
sampler2D _Texture2;
sampler2D _Texture3; struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float4 tangent : TANGENT;
float3 normal : NORMAL;
}; struct v2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float4 weight : TEXCOORD1;
float3 worldPos : TEXCOORD2;
float3 worldNormal : TEXCOORD3;
SHADOW_COORDS()
UNITY_FOG_COORDS()
}; v2f vert(appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.weight = v.tangent;
o.uv = v.uv;
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
TRANSFER_SHADOW(o);
UNITY_TRANSFER_FOG(o, o.pos);
return o;
} fixed4 frag(v2f i) : SV_TARGET
{
fixed4 t0 = tex2D(_Texture0, i.uv);
fixed4 t1 = tex2D(_Texture1, i.uv);
fixed4 t2 = tex2D(_Texture2, i.uv);
fixed4 t3 = tex2D(_Texture3, i.uv);
fixed4 tex = t0 * i.weight.x + t1 * i.weight.y + t2 * i.weight.z + t3 * i.weight.w; fixed3 albedo = tex.rgb; fixed3 ambient = albedo * UNITY_LIGHTMODEL_AMBIENT.rgb; float3 worldLight = normalize(UnityWorldSpaceLightDir(i.worldPos));
float halfLambert = dot(worldLight, i.worldNormal) * 0.5 + 0.5;
fixed3 diffuse = albedo * _LightColor0.rgb * halfLambert; float3 worldView = normalize(UnityWorldSpaceViewDir(i.worldPos));
float3 halfDir = normalize(worldView + worldLight);
fixed3 specular = albedo * _LightColor0.rgb * max(dot(halfDir, i.worldNormal), ); UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
fixed4 col = fixed4(ambient + (diffuse + specular) * atten, tex.a);
UNITY_APPLY_FOG(i.fogCoord, col); return col;
} ENDCG
} Pass
{
Tags
{
"LightMode" = "ForwardAdd"
}
Blend One One CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdadd #include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc" sampler2D _Texture0;
sampler2D _Texture1;
sampler2D _Texture2;
sampler2D _Texture3; struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float4 tangent : TANGENT;
float3 normal : NORMAL;
}; struct v2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float4 weight : TEXCOORD1;
float3 worldPos : TEXCOORD2;
float3 worldNormal : TEXCOORD3;
SHADOW_COORDS()
}; v2f vert(appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.weight = v.tangent;
o.uv = v.uv;
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
TRANSFER_SHADOW(o);
return o;
} fixed4 frag(v2f i) : SV_TARGET
{
fixed4 t0 = tex2D(_Texture0, i.uv);
fixed4 t1 = tex2D(_Texture1, i.uv);
fixed4 t2 = tex2D(_Texture2, i.uv);
fixed4 t3 = tex2D(_Texture3, i.uv);
fixed4 tex = t0 * i.weight.x + t1 * i.weight.y + t2 * i.weight.z + t3 * i.weight.w; fixed3 albedo = tex.rgb; float3 worldLight = normalize(UnityWorldSpaceLightDir(i.worldPos));
float halfLambert = dot(worldLight, i.worldNormal) * 0.5 + 0.5;
fixed3 diffuse = albedo * _LightColor0.rgb * halfLambert; float3 worldView = normalize(UnityWorldSpaceViewDir(i.worldPos));
float3 halfDir = normalize(worldView + worldLight);
fixed3 specular = albedo * _LightColor0.rgb * max(dot(halfDir, i.worldNormal), ); UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
fixed4 col = fixed4((diffuse + specular ) * atten, tex.a); return col;
} ENDCG
}
} Fallback "Diffuse"
}

Custom/Environment/LightedTerrain

光照效果如下:

Unity3d之将terrain转化成mesh