从灰度图到地形图 您所在的位置:网站首页 3dmax模型很卡 从灰度图到地形图

从灰度图到地形图

2023-03-04 11:50| 来源: 网络整理| 查看: 265

大概就是根据一个灰度图,生成一个地形。

分两步来实现吧;首先,用随机数生成地形;然后,根据灰度图生成地形。

小白,没啥基础,所以只能慢慢来。

参考:

【萌新图形学】地形网格生成入门 含动画说明哦_哔哩哔哩_bilibili

【萌新图形学】地形生成下篇——随机大地形与真实地形_哔哩哔哩_bilibili

首先,得有一些基本概念的:

00.一些基本概念 演示

我是个小白,所以,刚开始,来点直观的吧

新建了一个空物体gameobject,手动添加了3样东西给它:

MeshFilter组件

MeshRender组件

考虑到没有材质球会成粉色,所以新建了个默认的材质球给它。

前两个组件是主要的,下面的动图就简单的演示了这两个组件的作用。

顺便提一下,这里有个线框模式显示的开关。

大概有了个朦胧的认识:

MeshFilter里的Mesh可以控制物体的形状

MeshRender负责物体的显示

现在,开始文档里的正式介绍。

Mesh

Unity - Scripting API: Mesh (unity3d.com)

《inherits from Object》

里面按一定规则存着模型的数据,比如顶点什么的。【就是顶点着色器里的那个顶点】

看定义可能比较朦胧,看这个示例代码,就很清楚它是什么了:

MeshFilter

Unity - Manual: Mesh Filter component (unity3d.com)

这个组件,也很简单呐;里面就一个Mesh。。

具体可以这么用:

Unity - Scripting API: MeshFilter.mesh (unity3d.com)

从代码里可以看到,这个组件里的Mesh,就是上面的那个Mesh类

MeshRender

Unity - Manual: Mesh Renderer component (unity3d.com)

材质球,UnityShader,就是拖给这个组件的。再结合它的名字猜一下——它负责把网格画出来

小结

MeshFilter和MeshRender是一对

MeshFilter组件和Mesh类是一对

负责提供数据

如顶点在模型坐标系下的坐标

MeshRender组件和材质球,shader是一对

定义了如何使用数据

比如在片元着色器里把quad给discard成ball,point sprite就是这么来的

后面就是按着视频来了。

01.大小为1的平面

这个是视频里的代码。

结合上面介绍的基本概念,大概知道它在干什么吧。

using System.Collections; using System.Collections.Generic; using UnityEngine; public class Terrian : MonoBehaviour { public float width = 0.1f; MeshRenderer meshRenderer; MeshFilter meshFilter; // 用来存放顶点数据 List verts; List indices; private void Awake() { } private void Start() { verts = new List(); indices = new List(); meshRenderer = GetComponent(); meshFilter = GetComponent(); Generate(); } public void Generate() { ClearMeshData(); // 把数据填写好 AddMeshData(); // 把数据传递给Mesh,生成真正的网格 Mesh mesh = new Mesh(); mesh.vertices = verts.ToArray(); //mesh.uv = uvs.ToArray(); mesh.triangles = indices.ToArray(); mesh.RecalculateNormals(); mesh.RecalculateBounds(); meshFilter.mesh = mesh; } void ClearMeshData() { verts.Clear(); indices.Clear(); } void AddMeshData() { verts.Add(new Vector3(0, 0, 0)); verts.Add(new Vector3(0, 0, 1)); verts.Add(new Vector3(1, 0, 1)); verts.Add(new Vector3(1, 0, 0)); indices.Add(0); indices.Add(1); indices.Add(2); indices.Add(0); indices.Add(2); indices.Add(3); } }

在上面的那个演示的基础上,把它拖给空物体,就可以了。

02.更大规模的平面 灌数据到Mesh的原理

主要就俩数组,一个是顶点,一个是索引。

视频里这个图挺好的。

顶点数据 索引数据

稍微复杂一点,因为顶点是单独的,这个是相互关联的。

很形象的图,涉及到二维逻辑地址和一维物理地址的换算。

从特殊到一般:

最终应用【顺序是比较重要的,因为单面剔除,cull on,cull off之类的】

试一试

修改前:

void AddMeshData() { verts.Add(new Vector3(0, 0, 0)); verts.Add(new Vector3(0, 0, 1)); verts.Add(new Vector3(1, 0, 1)); verts.Add(new Vector3(1, 0, 0)); indices.Add(0); indices.Add(1); indices.Add(2); indices.Add(0); indices.Add(2); indices.Add(3); }

修改后

void AddMeshData() { int N = 10; //01填充顶点数据 for (int z = 0; z < N; ++z)//按先x后z的顶点排列顺序,所以最外层的循环是z不是x { for(int x = 0; x < N; ++x) { Vector3 temp = new Vector3(x, 0, z); verts.Add(temp); } } //02填充索引数据 for(int z = 0; z < N - 1; ++z) { for(int x = 0; x < N - 1; ++x) { int index_lb = z * N + x;//index of the left bottom vertex. lb = left bottom int index_lt = (z + 1) * N + x; int index_rt = (z + 1) * N + x + 1; int index_rb = z * N + x + 1; indices.Add(index_lb);indices.Add(index_lt);indices.Add(index_rt); indices.Add(index_rt);indices.Add(index_rb);indices.Add(index_lb); } } }

结果,符合预期;在原点那里放了个cube,作参照。

03.从平面到地形

这个不难,加一行

void AddMeshData() { int N = 10; //01填充顶点数据 for (int z = 0; z < N; ++z)//按先x后z的顶点排列顺序,所以先循环的是z { for(int x = 0; x < N; ++x) { float height = Random.Range(0.1f, 1.0f);//随机加个高度 Vector3 temp = new Vector3(x, height, z); verts.Add(temp); } } //02填充索引数据 for(int z = 0; z < N - 1; ++z) { for(int x = 0; x < N - 1; ++x) { int index_lb = z * N + x;//index of the left bottom vertex. lb = left bottom int index_lt = (z + 1) * N + x; int index_rt = (z + 1) * N + x + 1; int index_rb = z * N + x + 1; indices.Add(index_lb);indices.Add(index_lt);indices.Add(index_rt); indices.Add(index_rt);indices.Add(index_rb);indices.Add(index_lb); } } }

结果,有高度起伏了。

这个是10*10规模的,更大的规模也是一样的。计算机擅长重复。

04.从随机数到灰度图 准备

首先,得有个地形灰度图;这里用的是这个:

其次,得能从C#脚本里读到纹理的值。

Unity - Scripting API: Texture2D (unity3d.com)

GetPixel函数

Unity - Scripting API: Texture2D.GetPixel (unity3d.com)

解释的很详细了:

从下图可以看出,这个xy不是归一化后的uv:

这个返回值color,倒是归一化的:Unity - Scripting API: Color (unity3d.com)

视频里用的是更快的这个函数:

Unity - Scripting API: Texture2D.GetPixels32 (unity3d.com)

但是,我是小白,能搞出来就已经是极限了,哪里还顾得上什么性能问题。

试一试

代码

接着改那个函数就行

void AddMeshData() {     int N = 100;     //01填充顶点数据     for (int z = 0; z < N; ++z)//按先x后z的顶点排列顺序,所以先循环的是z     {     for(int x = 0; x < N; ++x)     {     int u = Mathf.FloorToInt(1.0f * x / N * texture2dHeightMap.width);//没归一化的uv     int v = Mathf.FloorToInt(1.0f * z / N * texture2dHeightMap.height);     float grayValue = texture2dHeightMap.GetPixel(u,v).grayscale;         float height = grayValue*heightRatio;//灰度值范围是[0,1],所以得缩放一下     Vector3 temp = new Vector3(x, height, z);     verts.Add(temp);     }     }     //02填充索引数据     for(int z = 0; z < N - 1; ++z)     {     for(int x = 0; x < N - 1; ++x)     {             ……//这部分是没改,省略     }     } }

符合预期吧

完整的代码

新建一个空物体,上面添加上MeshFilter,MeshRender两个组件,然后把这个脚本拖给这个空物体,再点击play,大概就行了。

using System.Collections; using System.Collections.Generic; using UnityEngine; public class Terrian : MonoBehaviour { public Texture2D texture2dHeightMap; [Range(1,100)] public float heightRatio = 30.0f;//一个系数,控制地形总体的高度的 MeshRenderer meshRenderer; MeshFilter meshFilter; // 用来存放顶点数据 List verts; List indices; private void Awake() { } private void Start() { verts = new List(); indices = new List(); meshRenderer = GetComponent(); meshFilter = GetComponent(); } private void Update() { Generate(); } public void Generate() { ClearMeshData(); // 把数据填写好 AddMeshData(); // 把数据传递给Mesh,生成真正的网格 Mesh mesh = new Mesh(); mesh.vertices = verts.ToArray(); mesh.triangles = indices.ToArray(); mesh.RecalculateNormals(); mesh.RecalculateBounds(); meshFilter.mesh = mesh; } void ClearMeshData() { verts.Clear(); indices.Clear(); } void AddMeshData() { int N = 100; //01填充顶点数据 for (int z = 0; z < N; ++z)//按先x后z的顶点排列顺序,所以先循环的是z { for(int x = 0; x < N; ++x) { int u = Mathf.FloorToInt(1.0f * x / N * texture2dHeightMap.width); int v = Mathf.FloorToInt(1.0f * z / N * texture2dHeightMap.height); float grayValue = texture2dHeightMap.GetPixel(u,v).grayscale; float height = grayValue*heightRatio; Vector3 temp = new Vector3(x, height, z); verts.Add(temp); } } //02填充索引数据 for(int z = 0; z < N - 1; ++z) { for(int x = 0; x < N - 1; ++x) { int index_lb = z * N + x;//index of the left bottom vertex. lb = left bottom int index_lt = (z + 1) * N + x; int index_rt = (z + 1) * N + x + 1; int index_rb = z * N + x + 1; indices.Add(index_lb);indices.Add(index_lt);indices.Add(index_rt); indices.Add(index_rt);indices.Add(index_rb);indices.Add(index_lb); } } } } 后记

还想搞搞颜色,低的用绿色,高的用红色,中间用渐变色;更好的可视化一下;

但是,那个可能得写着色器。那很明显不是我这种小白干的来的。所以,忽略。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有