Unity5.6大规模地形—地形资源构建(3)
来源:互联网 发布:淘宝上出现奇怪的字 编辑:程序博客网 时间:2024/04/20 00:50
在上一篇http://blog.csdn.net/fcauto2012/article/details/72377565中,我们主要讨论了如何给地形资源TerrainData类型添加从WorldMachine中导出的贴图。事实上,这种一块地形使用整张贴图的效果并不是很理想,地形尺寸比较大的时候不能很好地控制细节效果。一种更加常见的做法是使用多种贴图材质,应用splatmap进行混合,本文将讨论如何从WorldMachine中导出splatmap,并在Unity中加以使用。另外,我们将会对World Machine进行一个简单介绍。
1. World Machine简介
个人认为World Machine是一款非常优秀的地形生成软件,不仅能够实现一些真实地形的特效,而且熟练掌握后开发速度会非常快,简直是游戏基础地形制作的利器!也许你从Asset Store上下载过各种Perlin Noise、Erosion的插件,应用Unity自带的Terrain引擎不断地刷啊刷~~但是在World Machine里,这些特性不仅全部可以实现,而且你将采用的是这样的方式:
每一个复杂的功能都被封装成了一个模块,是不是觉得有些像MATLAB/Simulink?在WM里制作地形感觉不太像是在做美工,更像是在编程。下面是我按照YouTube上一个教程制作的地形:
是的,WM不仅可以生成高度,还可以添加颜色!对于开发者,可能更加关注如何导出到引擎中,WM支持各种格式的heightmap和texture导出,支持分块导出(tile)。这样一款专业的地形制作软件,并不像Photoshop或者3dmax那样“体型庞大”,安装完毕也仅仅占用不到40MB空间而已,对配置也没啥要求(如果配置比较好在最终build时会快些)。总之,我向大家强烈推荐这个软件,目前官网上只提供beta版本不支持高分辨率也不支持分块导出,大家可以到我的下载频道获取professional版,还有一些Youtube上的教程会很有帮助。那么下面我们来讨论如何在WM中制作splatmap。
2. Splatmap!
Splatmap的概念在上一篇有介绍,当时由于没有应用,理解得并不是很好。splatmap(和alphamap、splatalpha我感觉是一个意思)相当于一个“mask”,它使用RGBA中的几个或所有通道,来控制不同纹理的分布,达到多个纹理在同一地形上的混合。那么首先我们要决定RGBA各个通道所对应的区域,一般来讲,可以根据高度、坡度等特征来分配。对于上述地形,我在WM中进行了如下设计:
我对RGB三个通道使用了坡度和高度,而对A(alpha)通道使用了高度选择。在WM中只需这样拖动几个模块进行简单设置,如果在Unity中就免不了要写许多代码或者下载一些插件了。导出得到的splatmap如图:
实际结果应该是右边那幅,因为WM的SelectHeight模块比较奇葩的特性,alpha通道太过耀眼了...其实导入到Unity中之后更像左边这张没有alpha通道的。下面我们就为RGBA四个通道分别分配一张纹理,这里就先用Standard Assets里面的了:
R和G通道选择的基本是山地,我想让它们带有不同的岩石效果;而B通道基本是平地,我要让它带有草地的效果;而A通道是河流和湖泊,以后要为它们制作水效果,这里先用泥土效果的纹理代替。然而到了这里我们发现Unity Editor其实并没有设置splatmap的功能,因此我们又要自行拓展了~WM官网有一个不错的工具,链接戳这里http://www.world-machine.com/learn.php?page=workflow&workflow=wfunity,但如果使用5.0以上版本需要进行一点修改,看这里https://www.reddit.com/r/Unity3D/comments/601tm/importing_world_machine_splatmap_into_unity_55/。下面上我自己写的C#代码:
using System.Collections;using System.Collections.Generic;using UnityEngine;using UnityEditor;using System.IO;using System;public class ImportTerrain : MonoBehaviour{ [MenuItem("AssetDatabase/ImportTerrain")] static void GenerateTerrain() { string terrainDataPath = "Assets/MyTerrain/MyTerrain.asset"; //路径 //创建TerrainData对象 TerrainData terrainData = (TerrainData)AssetDatabase.LoadAssetAtPath( terrainDataPath, typeof(TerrainData)); if (!terrainData) { terrainData = new TerrainData(); AssetDatabase.CreateAsset(terrainData, terrainDataPath); } //地形设置 terrainData.heightmapResolution = 1025; //要先设置这个参数,再设置terrainSize Vector3 terrainSize = new Vector3(2400, 400, 2400); //地形的大小,最高、最低点的差值 terrainData.size = terrainSize; float[,] height = new float[1025, 1025]; //地形高度数组 //读取高度文件 FileInfo hmFile = new FileInfo(@"Assets/MyTerrain/output.r16"); FileStream hmFs = hmFile.OpenRead(); const int BYTESIZE = 1025 * 1025 * 2; byte[] hmB = new byte[BYTESIZE]; hmFs.Read(hmB, 0, BYTESIZE); //读取.r16字节流 hmFs.Close(); //赋值 int i = 0; for (int x = 0; x < 1025; x++) { for (int y = 0; y < 1025; y++) { //注意两点: //Windows字节序 //右手系转换到左手系 height[1024-x, y] = (hmB[i++] + hmB[i++] * 256.0f) / 65535.0f; } } terrainData.SetHeights(0, 0, height); //texture! //加贴图 Vector2 texSize = new Vector2(80, 80); string terrainTexPath = "Assets/Standard Assets/Environment/TerrainAssets/SurfaceTextures/"; //Cliff string tex0Path = terrainTexPath + "CliffAlbedoSpecular.psd"; Texture2D tex0 = AssetDatabase.LoadAssetAtPath(tex0Path, typeof(Texture2D)) as Texture2D; SplatPrototype splat0 = new SplatPrototype(); splat0.texture = tex0; splat0.tileSize = texSize; //Rock string tex1Path = terrainTexPath + "GrassRockyAlbedo.psd"; Texture2D tex1 = AssetDatabase.LoadAssetAtPath(tex1Path, typeof(Texture2D)) as Texture2D; SplatPrototype splat1 = new SplatPrototype(); splat1.texture = tex1; splat1.tileSize = texSize; //Plane string tex2Path = terrainTexPath + "GrassHillAlbedo.psd"; Texture2D tex2 = AssetDatabase.LoadAssetAtPath(tex2Path, typeof(Texture2D)) as Texture2D; SplatPrototype splat2 = new SplatPrototype(); splat2.texture = tex2; splat2.tileSize = texSize; //River string tex3Path = terrainTexPath + "MudRockyAlbedoSpecular.bmp"; Texture2D tex3 = AssetDatabase.LoadAssetAtPath(tex3Path, typeof(Texture2D)) as Texture2D; SplatPrototype splat3 = new SplatPrototype(); splat3.texture = tex3; splat3.tileSize = texSize; terrainData.splatPrototypes = new SplatPrototype[] { splat0, splat1, splat2, splat3 }; terrainData.RefreshPrototypes(); //alphamap设置 terrainData.alphamapResolution = 1024; string splatmapPath = "Assets/MyTerrain/splatA.png"; Texture2D splatmap = AssetDatabase.LoadAssetAtPath(splatmapPath, typeof(Texture2D)) as Texture2D; float[, ,] alpha = new float[1024, 1024, 4]; for (int x = 0; x < 1024; x++) { for (int y = 0; y < 1024; y++) { alpha[y, x, 0] = splatmap.GetPixel(x, y).r; alpha[y, x, 1] = splatmap.GetPixel(x, y).g; alpha[y, x, 2] = splatmap.GetPixel(x, y).b; alpha[y, x, 3] = splatmap.GetPixel(x, y).a; } } terrainData.SetAlphamaps(0, 0, alpha); //一切都是为了这个方法... }}
和之前的一样,也是Editor脚本,创建一个AssetDataBase->ImportTerrain的选项,而且//texture!注释之前的代码基本都是重复的,我们重点从这里开始看。在上一篇里,我们介绍了TerrainData中SplatPrototype这一重要成员,所有应用的贴图都要放在这里,不同的是这里我们放了4张texture,分别对应RGBA四个通道。设置alphamap的核心方法是TerrainData的SetAlphamaps方法,关键参数是alpha三维数组。它的前两维表示了splatmap的大小,最后一维表示layer数目,也就是所使用的splatmap的通道数,这里我们使用RGBA所以应为4。由于Unity脚本并未提供更加简便的方式,这里我们只能一个像素一个像素地设置,还好Texture2D类型为我们直接提供了获取RGBA值的方法,需要知道每一个值都是0~1的float类型(0代表黑色,1代表白色)。这里要注意的是,SplatPrototype中贴图的数目需要和alpha的通道数对应,如果前者比后者少,那么程序会报错。效果如下:
- Unity5.6大规模地形—地形资源构建(3)
- Unity5.6大规模地形—地形资源的创建(1)
- Unity5.6大规模地形—地形资源的创建(2)
- unity5地形lightmap阴影bug
- 地形的构建ogre地形shader 析解
- 地形的构建ogre地形shader 析解
- 构建地形系统
- 构建地形系统
- 3D地形编程——之地形射线查询(修正)
- 地形建模(一)——TIN地形的生成
- 大规模地形渲染--消除裂缝
- opengles构建过程纹理地形
- Direct3D---三维地形的构建
- unity3d笔记(3)——地形创建
- Unity3D 游戏引擎之构建3D游戏世界的基本地形(四)
- Unity3D For iPhone游戏引擎之构建3D游戏的基本地形(四)
- 摸爬滚打DirectX11_day10——三维地形的构建
- ArcEngine下SceneControl叠加影像数据(构建三维地形)
- Github常用指令
- 南阳oj116
- 【怎样写代码】复杂对象的组装与创建 -- 建造者模式(五):关于Director的进一步讨论
- 用正则表达式校验时间格式的正确性
- ubuntu常用命令
- Unity5.6大规模地形—地形资源构建(3)
- win10 软件界面模糊解决办法
- hdu 4447 Yuanfang, What Do You Think?
- 巴什博弈威佐夫博弈
- 安装macports的麻烦问题
- ThinkPhp excel 导入导出 demo
- Java8 lambda表达式10个示例
- bzoj1798 [AHOI2009]维护序列(线段树)
- dos2unix指令