Unity5.6大规模地形—地形资源的创建(2)

在系列的上一篇中http://blog.csdn.net/fcauto2012/article/details/70767973(以下简称“文1”),我们介绍了一种使用脚本从原始地形资源(heightmap,texture)创建TerrainData的方法。不过我们只实现了heightmap的导入,并没有实现texture的导入。另外,对于扩展Editor功能实现所谓的“批量操作”也没有做过多的阐述。本文我们将讨论以上两个问题。


1. TerrainData资源texture的导入(接上篇)

地形中的texture一般涉及到一个概念,就是splatmap。在接触地形的一开始我就被这个splatmap给搞糊涂了,到现在我也还是没能彻底理解它的概念和用法。以下这段文字是我从维基百科翻译https://en.wikipedia.org/wiki/Texture_splatting过来的,我读过之后有了一些概念。

splatmap 定义在计算机图形学中,texture(纹理) splatting是一项将不同纹理结合的方法。它将一个alphamap(或者叫做weightmap,splat map)添加在当前图层的上方,通过设置这个alphamap部分或者全部透明,来显示出下面的图层。这个术语由Crawfis等人创造。(如下图:)

Unity5.6大规模地形—地形资源的创建(2)_第1张图片

纹理splat技术通常用于计算机游戏中的地形渲染,因此需要多种优化技术。一个潜在的基本原则是每个纹理都拥有自己的alpha通道,因此消耗了大量的内存资源。对此的解决方案是,将多个alpha map整合到一张纹理中,每个通道依次赋予一张map。这样,通过单一的纹理高效地为四种颜色的纹理提供了alphamap。alpha纹理还可以使用比带颜色纹理更低的分辨率,并且带颜色纹理通常可以被平铺(be tiled)。

地形可以被分成块,每块可以拥有自己的纹理。假设有一张纹理只用于地形的某一部分,那么将这个alphamap应用于整个地形就造成了资源浪费,对此要将这个alpha map根据地形的分块也分成对应的alpha map块。

我的理解就是一块地形的某张texture可能有几张splatmap(或者叫alphamap)组成,这个数的上限应该是4,因为一个splatmap只能有RGBA四个通道。这样允许每个地形块使用不同的splatmap,那么就避免了只对某一块地形有用的splatmap扩大到整个地形而造成的资源浪费。显然splatmap是与地形分块相匹配的技术,它本质上还是texture。多通道形式的splatmap可以在worldmachine中制作,不过我们暂时先不做讨论。我们只采用从WM中导出的单个纹理,这样每块地形texture的splatmap就只有一个通道了,在Unity中这个单一通道是R。我们可以通过点击做好的terraindata资源旁边的“播放按钮”看到:那个红色的就是R通道的SplatAlphamap

Unity5.6大规模地形—地形资源的创建(2)_第2张图片

在Unity的TerrainData类型中,用来描述texture的是一个叫做splatPrototypes的成员,它是一个SplatPrototype类型的数组,存储了TerrainData所用到的所有splatmap。因此我们只需要把用到的那张纹理图片加载进来,放进去就好了。代码如下:(直接加在文1的heightmap代码后面)

//texture!
        //alphamap设置
        terrainData.alphamapResolution = 512;
        //加贴图
        string texturePath="Assets/512Test/image_x0_y0.png";
        Texture2D tex = AssetDatabase.LoadAssetAtPath(texturePath, typeof(Texture2D)) as Texture2D;
        SplatPrototype splat = new SplatPrototype();
        splat.texture = tex;
        Vector2 texSize = new Vector2(512, 512);
        splat.tileSize = texSize;
        terrainData.splatPrototypes = new SplatPrototype[] { splat };
        terrainData.RefreshPrototypes();

SplatPrototype的内部成员又包括texture(纹理),normalmap(法线贴图),tileSize(纹理大小)等,我们只需将原始地形资源赋值给这些成员即可。这些成员与文1中方法1的参数设置是一致的,再次体现了这种对应关系。


2. 在Editor中实现“批量操作”的思路

对于文1中“批量操作”的想法,这里参照http://wiki.unity3d.com/index.php/TerrainImporter提供一种思路:

1. 做一个EditorWindow来创建TerrainData,UI功能包括源/目标路径和配置文件的选择等。配置文件可以使用txt或者xml格式,我选择的是xml,因为使用C# .NET的xml类解析起来很方便。同样heightmap解析同样用到了.NET库,可见熟练掌握.NET库对于Unity开发是很重要的。

2. 规定配置文件格式:要包含terraindata资源创建的所有信息,比如地形块数、块大小、高度、文件名称等。

3. 写一个TerrainImporter:根据配置文件格式来解析加载信息,并实现全部terraindata资源的创建。


到此为止,TerrainData资源就全部创建完毕。下一步的工作就是在runtime加载这些资源,并创建Terrain Object了。事实上,单纯的runtime创建Terrain Object并加载地形资源并不是那么困难,使用Terrain类型的一些方法就可以轻松办到,比如下面的代码就实现了对某一块地形的动态加载:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Initialization : MonoBehaviour {

    public static TerrainData td;
	public static GameObject terrain00;
	// Use this for initialization
	void Start () {
		#if UNITY_EDITOR
        td = Resources.Load("tt/myTD00") as TerrainData;
        if (!td)
        {
            Debug.Log("加载terraindata失败!");
        }
        terrain00 = Terrain.CreateTerrainGameObject(td);
        terrain00.transform.position = new Vector3(0, 0, 0);
        terrain00.GetComponent().Flush();
		#endif
	}
	
	// Update is called once per frame
	void Update () {
		
	}
}

核心方法是Terrain类的静态方法CreateTerrainGameObject,将这个脚本挂载到Main Camera下,即可在运行时初始化生成一块地形。另外,我们需要制定加载的策略,以及runtime的内存优化实现。我们将在后面的文章中继续讨论这些问题。


参考文献:

1. “文1”:http://blog.csdn.net/fcauto2012/article/details/70767973

2. 维基百科:https://en.wikipedia.org/wiki/Texture_splatting

3. http://wiki.unity3d.com/index.php/TerrainImporter

4. Unity Script API文档

https://docs.unity3d.com/ScriptReference/SplatPrototype.html

https://docs.unity3d.com/ScriptReference/Terrain.html

你可能感兴趣的:(Unity)