
承接上一篇
saku一只松鼠:[unity]通过高度图制作一个真实世界的地形zhuanlan.zhihu.com
由于历史的车轮不断前进,松鼠为了不固步自封,在地形插件中更新了可以在SRP下使用的模式。从此使用Lightweight和High Definition Render Pipeline的用户再也不用面对生成的地块一片粉色的局面了。


不过通过这次更新,一个问题也浮出水面:
Unity的这一个新渲染管线目前还处于preview阶段,自带的转换工具只能处理built-in的shader,于是RTM中专门为地图应用而制作的材质全部阵亡了。

不过好消息是,这样的效果借助新推出的图形化shader编辑工具可以轻松愉快地完成。于是,在这一章中,我们使用shaderGraph制作材质,并将它作为模板直接赋予到新生成的地形上。
首先,使用shadergraph需要做几个前置任务:
1.打开package manager,安装Lightweight RP。新建工程的时候使用LWRP模板也可以达到差不多的效果,不过建议之后还是在package manager中将其升级到最新版本。
2. Create > Render Pipeline > Lightweight > Pipeline Asset,之后把新建的asset拖到Project Settings > Graphics中的scriptable render pipeline setting一栏中。(如果是使用模板新建的工程则可以省略这一步。如果遇到报错的话,顺手把player setting中的color space从gamma改为linear)
3.在package manager中,安装shader graph。之后就可以新建shadergraph的文件了。

随意从这三项里选一个最简单的,建立unlit graph。之后随便create一个texture 2d的节点,连线到输出节点的color上,就可以显示这张贴图了。

接下来正文开始。
在使用传统的shaderlab制作依据高度着色的效果,需要两个property来设定高度最高点和最低点的高度。
_heightMax("pos Max",Float) = 10
_heightMin("pos Min",Float) = 0
_colorA ("Color Up",Color) = (0,0,0,1)
_colorB ("Color Down ",Color) = (0,0,0,1)
之后用vertrex.y来计算当前高度在最高点最低点之间的位置,并以此计算两个颜色的插值。
void
为了完成以上工作,我们设置一些节点,并按照公式连线。
首先是第一步

接着第二步,使用上面计算的结果做两个颜色之间的插值。

在这里,其实unity给提供了一个更方便的节点,remap。
根据非官方文档中提供的资料:
//The following example code represents one possible outcome of this node.
void Unity_Remap_float4(float4 In, float2 InMinMax, float2 OutMinMax, out float4 Out)
{
Out = OutMinMax.x + (In - InMinMax.x) * (OutMinMax.y - OutMinMax.x) / (InMinMax.y - InMinMax.x);
}
可以将连线简化,并且不用再受到除0的困扰。


虽然达到了效果,但是在inspector中都没有可调节的选项,这无疑会让人比较厌烦,这时就需要将一部分input的节点convert to property。
找到两个height节点和两个color节点,右键选择转换,它们就会出现在旁边的properties中


除了基于高度着色外,一个不带卫星图的地形显然是没有灵魂的,接下来我们做贴图的部分。
首先在新建一个property,并改名为_MainTex,接着将它拖到工作区。

根据文档中的解释Samples a Texture 2D and returns a Vector 4 color value for use in the shader.要使用texture2D就需要为它配备一个采样器,来将图片转换为RGBA的颜色数据。

接着,用加法或乘法的方式将贴图和颜色融合起来。

就实现了这样的效果

接下来,要让生成一个地形的时候直接附上我们刚刚制作的材质。在前一章使用高度图生成地形的脚本基础上,增加材质模板和贴图的变量。
[SerializeField]
Texture2D Tex;
[SerializeField]
Material Mat;
string propertyname = "_MainTex";
propertyname就使用之前给贴图起的名字。
在生成texture的部分做一点修改:
void DrawTexture(){
if (gameObject.GetComponent <MeshRenderer> ()==null ) {
gameObject.AddComponent<MeshRenderer> ();
}
Material diffuseMap = new Material (Shader.Find (Mat.shader.name));
diffuseMap.CopyPropertiesFromMaterial(Mat);
diffuseMap.SetTexture(propertyname,Tex);
gameObject.GetComponent<Renderer> ().material = diffuseMap;
}
这样就以我们选用的材质的shader生成一个新材质,并复制了原材质的属性,接着将贴图赋给相应的property。

接着,还有一件之前忘记做的事情,为mesh的每个顶点设定一个uv,不然所有uv都是默认值(0,0)的话,是没办法正确显示贴图的。
void setUV()
{
_uvs = new Vector2[(sx+1)*(sy+1)];
float u = 1.0F / sx;
float v = 1.0F / sy;
for (int yy = 0; yy <= sy; yy++)
{
for (int xx = 0; xx <= sx; xx++)
{
int self = xx + yy *(sx+1);
_uvs[self] = new Vector2(xx * u, yy * v);
}
}
}
接着,赋值给mesh。
mesh.uv = _uvs;
以上操作的作用是将所有顶点的uv按顺序均匀分配到0和1之间,即简单的让贴图均匀铺满整个mesh,并将计算结果存入数组。这个0到1的范围就对应了贴图从左下到右上的全部,而如果把范围修改为0~2的话,
float u = 1.0F / sx;
float v = 1.0F / sy;
根据贴图的wrap mode设置,就会得到不同的效果了。

新建一个空物体,挂上脚本并配置好材质和图,然后try一下,就会有带有卫星图的彩色地形生成出来了。
这一章同样出自地形生成插件:
Real Terrain Maker - Asset Storeassetstore.unity.com
好惨啊小本生意被打差评真的闹不住,而且还是问都没问直接打。希望有兴趣的客官在阅读使用说明或者看过demo之后理性购买,能打个5星真的感激不尽,作者哭超凶的。
作者活着,随时可在客(没)服(人)群里提问
这章所用到的资源在这里获取:
sakuraplus/make-terrain-with-google-elevationgithub.com由于使用了shadergraph,这次的需求是unity2019.1.7及以上。
脚本挂在任意物体上即可使用,当然建议使用空物体,不然会造成好玩的影响。
如果出现Texture 'Trr00downloadHeightmap_143' is not readable这样的提示,将图片的import setting设为Read/Write Enabled,同时不要忘记将Power of 2设置为none,filter mode为point,不然在逐像素采样的时候会受到影响。