最近一直都在搞flash3D,好像有点对不起Unity3D的朋友们了。这次简单的写一个动态创建地形网格的脚本给大家分享一下。 这次是第一部分,仅仅实现了通过高度图动态生成地形的部分。假如以后有心情和时间,再来慢慢的补充多通道刷地形材质、动态刷地形和保存高度图等的功能吧。以前我都不喜欢公开脚本源码,都是一个个部分的单独讲解然后让朋友们自己去组合起来的,但最近时间实在是不多,所以还是直接提供源码,然后在源码上面写注释,大家自行的观看吧。源码在最下面。更多内容,请访问狗刨学习网http://unity.gopedu.com
首先直接把脚本拖到某物体上面,运行,就会出现了上图所示的一个面片。这是因为为了方便大家测试,我在start里面调用了下面的SetTerrain方法。这个方法会创建一个默认的地形面板。长宽是100*100,段数是50*50,高度是-10米到10米的。
当然了,我们真正用的时候,是使用重载的SetTerrain方法自己制定长宽高和段数这些数值的,把start里面的方法注释掉就行了。
现在由于没有指定默认的地面材质和高度图,所以我写了个警告提示。
这两个变量就是材质和高度图了,大家可以自行想办法去赋值。我现在写成public只是为了方便赋值测试,最好还是写get/set方法赋值。
还有一点,现在没有做地面的多通道材质,所以只是用了一个默认的shader。以后假如需要混合通道材质,改这个shader。
好,先不管以后了,把一个有草地纹理的材质球付给脚本。
出来了一个草地
我随便的用黑白图刷了一个高度图,然后扔进脚本。
注意的是,作为高度图的图片,是需要设置读写权限的,不然获取不到像素的颜色
出现了一个小小的山坡了。
其实我这个脚本并不是必须针对黑白图的,我随便拿了一张木箱子贴图,一样可以做高度图的,这是因为我是做了灰度处理的,最后获取的是该图片像素点上的灰度值。
看,地形出来了。
实际效果
功能是简单了点,这里提供了一点点的思路,有需要或者有兴趣的朋友可以参考一下做法,自行的扩展吧。
源码:
TerrainManager.cs
using UnityEngine;
using System.Collections;
public class TerrainManager : MonoBehaviour {
//材质和高度图
public Material diffuseMap;
public Texture2D heightMap;
//顶点、uv、索引信息
private Vector3[] vertives;
private Vector2[] uvs;
private int[] triangles;
//生成信息
private Vector2 size;//长宽
private float minHeight = -10;
private float maxHeight = 10;
private Vector2 segment;
private float unitH;
//面片mesh
private GameObject terrain;
// Use this for initialization
void Start () {
//默认生成一个地形,如果不喜欢,注销掉然后用参数生成
SetTerrain();
}
///
/// 生成默认地形
///
public void SetTerrain()
{
SetTerrain(100, 100, 50, 50,-10,10);
}
///
/// 通过参数生成地形
///
/// 地形宽度
/// 地形长度
/// 宽度的段数
/// 长度的段数
/// 最低高度
/// 最高高度
public void SetTerrain(float width, float height, uint segmentX, uint segmentY,int min,int max)
{
Init(width, height, segmentX, segmentY,min,max);
GetVertives();
DrawMesh();
}
///
/// 初始化计算某些值
///
///
///
///
///
///
///
private void Init(float width, float height, uint segmentX, uint segmentY, int min, int max)
{
size = new Vector2(width, height);
maxHeight = max;
minHeight = min;
unitH = maxHeight - minHeight;
segment = new Vector2(segmentX, segmentY);
if (terrain != null)
{
Destroy(terrain);
}
terrain = new GameObject();
terrain.name = "plane";
}
///
/// 绘制网格
///
private void DrawMesh()
{
Mesh mesh = terrain.AddComponent().mesh;
terrain.AddComponent();
if (diffuseMap == null)
{
Debug.LogWarning("No material,Create diffuse!!");
diffuseMap = new Material(Shader.Find("Diffuse"));
}
if (heightMap==null)
{
Debug.LogWarning("No heightMap!!!");
}
terrain.renderer.material = diffuseMap;
//给mesh 赋值
mesh.Clear();
mesh.vertices = vertives;//,pos);
mesh.uv = uvs;
mesh.triangles = triangles;
//重置法线
mesh.RecalculateNormals();
//重置范围
mesh.RecalculateBounds();
}
///
/// 生成顶点信息
///
///
private Vector3[] GetVertives()
{
int sum = Mathf.FloorToInt((segment.x + 1) * (segment.y + 1));
float w = size.x / segment.x;
float h = size.y / segment.y;
int index = 0;
GetUV();
GetTriangles();
vertives = new Vector3[sum];
for (int i = 0; i < segment.y + 1; i++)
{
for (int j = 0; j < segment.x + 1; j++)
{
float tempHeight = 0;
if (heightMap != null)
{
tempHeight = GetHeight(heightMap, uvs[index]);
}
vertives[index] = new Vector3(j * w, tempHeight, i * h);
index++;
}
}
return vertives;
}
///
/// 生成UV信息
///
///
private Vector2[] GetUV()
{
int sum =Mathf.FloorToInt( (segment.x + 1) * (segment.y + 1));
uvs = new Vector2[sum];
float u = 1.0F / segment.x;
float v = 1.0F / segment.y;
uint index = 0;
for (int i = 0; i < segment.y + 1; i++)
{
for (int j = 0; j < segment.x + 1; j++)
{
uvs[index] = new Vector2(j * u, i * v);
index++;
}
}
return uvs;
}
///
/// 生成索引信息
///
///
private int[] GetTriangles()
{
int sum = Mathf.FloorToInt(segment.x * segment.y * 6);
triangles = new int[sum];
uint index = 0;
for (int i = 0; i < segment.y; i++)
{
for (int j = 0; j < segment.x; j++)
{
int role = Mathf.FloorToInt(segment.x) + 1;
int self = j +( i*role);
int next = j + ((i+1) * role);
triangles[index] = self;
triangles[index + 1] = next + 1;
triangles[index + 2] = self + 1;
triangles[index + 3] = self;
triangles[index + 4] = next;
triangles[index + 5] = next + 1;
index += 6;
}
}
return triangles;
}
private float GetHeight(Texture2D texture, Vector2 uv)
{
if (texture != null)
{
//提取灰度。如果强制读取某个通道,可以忽略
Color c = GetColor(texture, uv);
float gray = c.grayscale;//或者可以自己指定灰度提取算法,比如:gray = 0.3F * c.r + 0.59F * c.g + 0.11F * c.b;
float h = unitH * gray;
return h;
}
else
{
return 0;
}
}
///
/// 获取图片上某个点的颜色
///
///
///
///
private Color GetColor(Texture2D texture, Vector2 uv)
{
Color color = texture.GetPixel(Mathf.FloorToInt(texture.width * uv.x), Mathf.FloorToInt(texture.height * uv.y));
return color;
}
///
/// 从外部设置地形的位置坐标
///
///
public void SetPos(Vector3 pos)
{
if (terrain)
{
terrain.transform.position = pos;
}
else
{
SetTerrain();
terrain.transform.position = pos;
}
}
}