1.写在前面
好久没有写博客了,最近面试不太顺利,认识到应该把学习心得或者说是结果都落实到博客上来,一来加深印象,二来有利于自我展示.
本片博客的内容是讲地形纹理混合,是关于手游sgl大地图的shader实现.
slg大地图,如cok,是很大的.在渲染时,只渲染屏幕周围的一部分.
在渲染屏幕地形时,会提供一组地形数据,shader会根据地形数据对地形进行混合.
2.混合方法
混合使用的方法是非常常见的纹理混合.
基本原理是为shader提供多张可以选用的地形贴图.然后根据一张alpha贴图或者其它方式来决定纹理的如何混合.即选用哪个层级的地形纹理进行渲染.
3.纹理选择
前文有提到,本shader是在slg游戏中做地形混合的.
在手游slg中,如cok,地形只是渲染在一个平面上的.
如下图所示,其中每一个小的方格可以代表一个地形单位.
而每一个小的地形单位,可以用一个标记位来表示地形所使用的纹理的种类.
5.选择纹理混合信息标识
那么要怎么告诉shader在渲染时要使用哪个纹理呢,
本文所使用的方法是通过顶点色.
在Unity当中,为每一个Mesh默认提供了一个顶点数组,一个三角形数组和一个顶点颜色数组,值得一提的是每一个Mesh还都拥有四个UV数组.
顶点色有rgba四个通道,再加上1-(rgba的和)刚好可以用来表示五层纹理的混合参数.其中1-(rgba和)用来表示默认层纹理的混合参数.
6.设置顶点颜色
决定了采用顶点色来表示混合参数之后,我们还要决定把哪些顶点设置颜色.
以本文所用的square地形为例,每一个小的地形Cell都有两个三角形,四个顶点组成,那么既然如此,我们只需要把这四个顶点的顶点色中,代表该地块纹理的通道值+1即可.
在遍历完所有地块,设置完定点后,需要对定点颜色执行约束操作,按比例缩放,保证和值不超过1.
将得到的顶点色赋值给mesh即可.
7.效果示意:
8.参考
https://zhuanlan.zhihu.com/p/26383778
https://www.cnblogs.com/luxishi/p/6670487.html
9.源码:
shader:
1 Shader "SlpatMap/TerrainLab" 2 { 3 Properties 4 { 5 _Splat0 ("_SlpatTex0", 2D) = "white" {} 6 _Splat1 ("_SlpatTex1", 2D) = "white" {} 7 _Splat2 ("_SlpatTex2", 2D) = "white" {} 8 _Splat3 ("_SlpatTex3", 2D) = "white" {} 9 _Splat4 ("_SlpatTex4", 2D) = "white" {} 10 } 11 SubShader 12 { 13 Tags { "RenderType"="Opaque" } 14 LOD 100 15 16 Pass 17 { 18 CGPROGRAM 19 #pragma vertex vert 20 #pragma fragment frag 21 22 #include "UnityCG.cginc" 23 24 struct appdata 25 { 26 float4 vertex : POSITION; 27 float2 texcoord : TEXCOORD0; 28 fixed4 color : COLOR; 29 }; 30 31 struct v2f 32 { 33 float4 vertex : SV_POSITION; 34 float2 uv[5] : TEXCOORD0; 35 fixed4 color : COLOR0; 36 fixed color2 : COLOR1; 37 }; 38 39 40 sampler2D _Splat0; 41 sampler2D _Splat1; 42 sampler2D _Splat2; 43 sampler2D _Splat3; 44 sampler2D _Splat4; 45 46 float4 _Splat0_ST; 47 float4 _Splat1_ST; 48 float4 _Splat2_ST; 49 float4 _Splat3_ST; 50 float4 _Splat4_ST; 51 52 v2f vert (appdata v) 53 { 54 v2f o; 55 o.vertex = UnityObjectToClipPos(v.vertex); 56 o.uv[0] = TRANSFORM_TEX(v.texcoord, _Splat0); 57 o.uv[1] = TRANSFORM_TEX(v.texcoord, _Splat1); 58 o.uv[2] = TRANSFORM_TEX(v.texcoord, _Splat2); 59 o.uv[3] = TRANSFORM_TEX(v.texcoord, _Splat3); 60 o.uv[4] = TRANSFORM_TEX(v.texcoord, _Splat4); 61 o.color = v.color; 62 o.color2 = (1 - (v.color.r + v.color.g + v.color.b + v.color.a)); 63 return o; 64 } 65 66 fixed4 frag (v2f i) : SV_Target 67 { 68 fixed3 zero = fixed3(0, 0, 0); 69 fixed3 splat1Col = i.color.r <= 0.001 ? zero : tex2D(_Splat0, i.uv[0]).xyz * i.color.r; 70 fixed3 splat2Col = i.color.g <= 0.001 ? zero : tex2D(_Splat1, i.uv[1]).xyz * i.color.g; 71 fixed3 splat3Col = i.color.b <= 0.001 ? zero : tex2D(_Splat2, i.uv[2]).xyz * i.color.b; 72 fixed3 splat4Col = i.color.a <= 0.001 ? zero : tex2D(_Splat3, i.uv[3]).xyz * i.color.a; 73 fixed3 splat5Col = i.color2 <= 0.001 ? zero : tex2D(_Splat4, i.uv[4]).xyz * i.color2; 74 fixed4 col; 75 col.xyz = splat1Col + splat2Col + splat3Col + splat4Col + splat5Col; 76 col.w = 1; 77 return col; 78 } 79 ENDCG 80 } 81 } 82 }
c#代码:
1 using System.Collections.Generic; 2 using UnityEngine; 3 4 public class SquarePlaneComponent : MonoBehaviour 5 { 6 7 static int[,] terrainType = new int[,]{ 8 {0,0,0,1,1,1,1,1,1,1}, 9 {1,1,1,1,1,1,1,1,1,1}, 10 {1,1,1,1,1,1,1,1,1,1}, 11 {1,1,2,2,1,1,1,1,1,1}, 12 {1,1,2,2,3,3,3,3,3,1}, 13 {1,1,1,2,1,1,1,1,1,1}, 14 {1,1,1,1,1,1,1,1,1,0}, 15 {1,1,1,1,1,1,1,1,1,0}, 16 {1,1,1,1,1,1,1,1,1,0}, 17 {1,1,1,1,1,1,1,1,1,1}, 18 19 }; 20 public int rows = 10; 21 22 public int cols = 10; 23 24 public MeshRenderer meshRenderer; 25 26 public MeshFilter meshFilter; 27 28 29 public int[] vertGridMappingArr; 30 31 [ContextMenu("UpdateColorInfo")] 32 public void UpdateUVInfo() 33 { 34 var vertices = meshFilter.sharedMesh.vertices; 35 var colors = new Color[vertices.Length]; 36 var uv = meshFilter.sharedMesh.uv; 37 var colorsV = new Vector4[vertices.Length]; 38 var VRowLength = rows + 1; 39 40 var col = terrainType.GetLength(0); 41 var row = terrainType.GetLength(1); 42 43 for (int i = 0; i < col; i++) 44 { 45 for (int j = 0; j < row; j++) 46 { 47 var index = (i + 1) * VRowLength - j - 1; 48 var leftTop = index; 49 var rightTop = index - 1; 50 var leftBottom = index + VRowLength; 51 var rightBottom = index + VRowLength - 1; 52 53 var indexList = new List<int>() { leftTop, rightTop, leftBottom, rightBottom }; 54 foreach (var t in indexList) 55 { 56 colorsV[t] = Set(colorsV[t], terrainType[i, j]); 57 } 58 } 59 } 60 61 for (int i = 0; i < vertices.Length && i < colors.Length; i++) 62 { 63 var cv = colorsV[i] / colorsV[i].magnitude; 64 colors[i] = new Color(cv.x, cv.y, cv.z, cv.w); 65 } 66 meshFilter.sharedMesh.colors = colors; 67 } 68 69 public Vector4 Set(Vector4 v, int type) 70 { 71 switch (type) 72 { 73 case 0: 74 v.x += 1; 75 break; 76 case 1: 77 v.y += 1; 78 break; 79 case 2: 80 v.z += 1; 81 break; 82 case 3: 83 v.w += 1; 84 break; 85 } 86 return v; 87 } 88 }
原文地址:https://www.cnblogs.com/WongSiuming/p/11018866.html