[小明学Shader]15.基于Grid的地形混合shader

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

时间: 2024-10-03 00:59:27

[小明学Shader]15.基于Grid的地形混合shader的相关文章

[小明学Shader]14.热扭曲效果

1.代码 Shader "AAAA/HeatIsland" { Properties { _MainTex ("Base (RGB)", 2D) = "white" {} _OffsetTex ("Shui (RGB)", 2D) = "white" {} _ClipTex ("Clip (RGB)", 2D) = "white" {} _Speed("速度

[小明学Shader]9.顶点方法(Vertex Function)和顶点色

渲染过程中,Shader总是优先对每个顶点进行计算(这里如果我们用顶点方法,那么我们的顶点方法就会被调用);顶点计算出后按照一定的格式输出给下面的流程,其中的某一步就是我们放置的surfa方法,它接收的输入可以进行一定的控制(我们将用顶点方法输出). 整个 shader 相关流程如下: 1. shader 准备对一个顶点进行计算.因我们自定义顶点方法,因此 vert 被调? 2. vert 的第一参数决定该顶点需要带进来哪些属性,我们得到这个属性里的顶点色,把这个顶点色放到 Input 结构中,

[小明学Shader]2.理解Shader和Material的关系

每一个Material都要选择一个使用的Shader.每一个Material都会存储自己的值 我们新建一个混色的Shader,再分别创建四个Material,其效果如下: Shader代码如下: Shader"Custom/11.25/2"{ Properties{ _Value("Cofficient",Range(0,10))=2.5 _AmbientColor("Ambient Color",color)=(1,1,1,1) _Emissi

[小明学Shader]4.自定义光照----RampTexture

上一篇讲了自定义光照的HalfLambert,这一次和它有些类似,但是计算的时候,不再用方向和atten相乘了. 将光入射方向与法相的点乘积HalfLam作为坐标,找到RampTexture中(HalfLam,HalfLam)点的rgb值作为混合值与s.的Albedo和入射光颜色相乘. 代码如下: Shader"Custom/11.26/2"{ Properties{ _RampTex("Ramp Texture",2D)="white"{} _

[小明学shader高级篇----后期效果]1.Graphics的两个方法

一,介绍 Render to Texture即渲染到纹理的特性,是一种高级操作,利用这种特性,我们可以实现各种各样难以在普通渲染过程中实现的华丽效果. 想要做屏幕的后期效果,就必须使用Graphics的Blit和BlitMultiTap方法.和相机的Render,RenderWithShader方法的不同之处在于,Graphics的这两个方法都是在屏幕上又做了一个和屏幕大小一样的平面,对此平面

[小明学算法]5.常用排序算法

#include<iostream> using namespace std; void Print(int arr[], int length) { for (int i = 0; i < length; i++) { cout << arr[i] << " "; } cout << endl; } int partition(int a[], int low, int height) { cout << "

[小明学算法]4.Dijkstra(迪杰斯特拉)算法

1.定义概念 Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径.主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止.值得注意的是,该算法要求图中不存在负权边. 2.基本思想 设置顶点集合S,初始时,S中仅含有起始点,把从起始点到u且中间只经过S中顶点的路称为从起始点到u的特殊路径,并用数组dist记录当前每个顶点所对应的最短特殊路径的长度.Dijkstra算法每次从S外取出对应dist值最小的节点u,将其添加到S中,并对所有与u点直接相连

[小明学算法]6.字符串匹配算法---KMP

1.简介  字符串匹配就是看看那字符串b是不是字符串a的子串.常用的Knuth-Morris-Pratt 算法,又称KMP算法. 2.主要思想 当patter在某一位置与string匹配失败时,我们除了知道从string的这个位置进行匹配失败这个结果外,是否可以从前面的匹配中获得更多的信息呢.即当前匹配点匹配失败之后,向右滑动的距离是可以提前计算出来的. 3.举例 abcabcabcdef   --------- string abcabcdef         --------- patter

nyist oj 19 擅长排列的小明(dfs搜索+STL)

擅长排列的小明 时间限制:1000 ms  |  内存限制:65535 KB 难度:4 描述 小明十分聪明,而且十分擅长排列计算.比如给小明一个数字5,他能立刻给出1-5按字典序的全排列,如果你想为难他,在这5个数字中选出几个数字让他继续全排列,那么你就错了,他同样的很擅长.现在需要你写一个程序来验证擅长排列的小明到底对不对. 输入 第一行输入整数N(1<N<10)表示多少组测试数据, 每组测试数据第一行两个整数 n m (1<n<9,0<m<=n) 输出 在1-n中选