图形 3.3 曲面细分与几何着色器 大规模草渲染

曲面细分与几何着色器  大规模草渲染


 

曲面细分与几何着色器的应用

 

曲面细分着色器的应用

 

  曲面细分就是把一条直线进行不断的细分,然后把它和曲线进行逼近,逐渐变成曲线的形状。

  可以使用在海浪、雪地的部分,比如雪地的脚印,也可以通过曲面细分着色器来处理和优化。

  或者和置换贴图进行结合使用。置换贴图能改变物体的形状,使得在边缘也有很强的凹凸感,但是如果模型的面数不够多的情况下,边缘就会十分尖锐突兀。此时便可以使用曲面细分着色器把模型面数变多变细来解决这个问题。

 

  

 

 

几何着色器的应用

 

  应用于常见的几何动画,比如爆破破碎效果。或者应用于草的生成,并可以和曲面细分着色器结合,来获得一个动态调整密度的草地。

 

  

 

 


 

着色器执行顺序

 

 

  Tessellation Shader:曲面细分着色器

  Geometry Shader:几何着色器

然后曲面细分着色器又分成了Hull Shader、Tessellation Primitive Generator、Doamin Shader。其中两个部分是可以控制的。

Hull Shader定义一些细分的参数,Tessellation Primitive Generator是API去处理的,Domain Shader则是曲面细分着色器细分之后的点

 

TESS(曲面细分着色器)的输入与输出

 

  • 输入

  Patch, 可以看成是多个顶点的集合,包含每个顶点的属性,可以  指定一个Patch包含的顶点数以及自己的属性。

  • 功能

  将图元细分(可以是三角形,矩形等)

  • 输出

  细分后的顶点

 

TESS流程

 

  • HULL Shader
    • 决定细分的数量(设定Tessellation factor以及Inside Tessellation factor)
    • 对输入的Patch参数进行改变(如果需要)
  • Tessellation Primitive Generation  

  进行细分操作

  • Domain Shader

  对细分后的点进行处理,从重心空间(Barycentric coordinate system )转换到屏幕空间

 

HULL Shader各参数解析

 

  • Tessellation Factor

  决定将一条边分成几部分:

 

  equal_Spacing   等分

 

  

  fractional_even_spacing   最小为2,向上取最近的偶数

 

 

  fractional_odd_spacing   最小为1,向上取最近的奇数

 

  fractional_even_spacing和fractional_odd_spacing会把周长分为n减2的等长的部分,以及两端的不等长部分

 

  • Inner Tessellation Factor

  说明内部的细分如何被绘制出来。

     

 

GS(几何着色器)的输入与输出

 

  • 输入为图元(三角形,矩形,线等)

  根据图元的不同,shader中会出现对应不同数量的顶点

  • 输出同样为图元

  一个或多个,需要自己从顶点构建,顺序很重要,同时需要定义最大输出的顶点数。

 


 

曲面细分Demo演示

 

  • 将一个Quad细分

  • 与置换贴图的结合

 

示例Shader

  1 Shader "Unlit/TESS2"
  2 {
  3     Properties
  4     {
  5         _MainTex("MainTex",2D) = "white"{}
  6         _DisplacementMap("_DisplacementMap",2D)="gray"{} //置换贴图
  7         [HideInInspector]_DisplacementStrength("DisplacementStrength",Range(0,1)) = 0
  8         _Smoothness("Smoothness",Range(0,5))=0.5
  9         _TessellationUniform("TessellationUniform",Range(1,1024)) = 1
 10     }
 11     SubShader
 12     {
 13         Tags { "RenderType"="Opaque" 
 14                "LightMode"="ForwardBase"}
 15         LOD 100
 16         Pass
 17         {
 18             CGPROGRAM
 19             //定义2个函数 hull domain
 20             #pragma hull hullProgram
 21             #pragma domain ds
 22            
 23             #pragma vertex tessvert
 24             #pragma fragment frag
 25 
 26             #include "UnityCG.cginc"
 27             #include "Lighting.cginc"
 28             //引入曲面细分的头文件
 29             #include "Tessellation.cginc" 
 30 
 31             #pragma target 5.0
 32             float _TessellationUniform;
 33             sampler2D _MainTex;
 34             float4 _MainTex_ST;
 35 
 36             sampler2D _DisplacementMap;
 37             float4 _DisplacementMap_ST;
 38             float _DisplacementStrength;
 39             float _Smoothness;
 40 
 41             struct VertexInput
 42             {
 43                 float4 vertex : POSITION;
 44                 float2 uv : TEXCOORD0;
 45                 float3 normal : NORMAL;
 46                 float4 tangent : TANGENT;
 47             };
 48 
 49             struct VertexOutput
 50             {
 51                 float2 uv : TEXCOORD0;
 52                 float4 pos : SV_POSITION;
 53                 float4 worldPos:TEXCOORD1;
 54                 half3 tspace0 :TEXCOORD2;
 55                 half3 tspace1 :TEXCOORD3;
 56                 half3 tspace2 :TEXCOORD4;
 57             };
 58 
 59             VertexOutput vert (VertexInput v)
 60             //这个函数应用在domain函数中,用来空间转换的函数
 61             {
 62                 VertexOutput o;
 63                 o.uv = TRANSFORM_TEX(v.uv,_MainTex);
 64                 //Displacement 处理置换贴图
 65                 //由于并不是在Fragnent shader中读取图片,GPU无法获取mipmap信息,因此需要使用tex2Dlod来读取图片,使用第四坐标作为mipmap的level,这里取了0
 66                 float Displacement = tex2Dlod(_DisplacementMap,float4(o.uv.xy,0.0,0.0)).g;
 67                 //_DisplacementStrength = frac((_Time) * 5)  * 0.2;
 68                 _DisplacementStrength = abs(frac((_Time) * 3) - 0.5)  * 0.4;
 69                 Displacement = (Displacement-0.5)*_DisplacementStrength;
 70                 v.normal = normalize(v.normal);
 71                 v.vertex.xyz += v.normal * Displacement;
 72 
 73                 o.pos = UnityObjectToClipPos(v.vertex);
 74                 o.worldPos = mul(unity_ObjectToWorld, v.vertex);
 75 
 76                 //计算切线空间转换矩阵
 77                 half3 vNormal = UnityObjectToWorldNormal(v.normal);
 78                 half3 vTangent = UnityObjectToWorldDir(v.tangent.xyz);
 79                 //compute bitangent from cross product of normal and tangent
 80                 half tangentSign = v.tangent.w * unity_WorldTransformParams.w;
 81                 half3 vBitangent = cross(vNormal,vTangent)*tangentSign;
 82                 //output the tangent space matrix
 83                 o.tspace0 = half3(vTangent.x,vBitangent.x,vNormal.x);
 84                 o.tspace1 = half3(vTangent.y,vBitangent.y,vNormal.y);
 85                 o.tspace2 = half3(vTangent.z,vBitangent.z,vNormal.z);
 86                 return o;
 87             }
 88 
 89             //和上面的shader一样 需要额外定义的宏
 90             #ifdef UNITY_CAN_COMPILE_TESSELLATION
 91                 //顶点着色器结构的定义
 92                 struct TessVertex{
 93                     float4 vertex : INTERNALTESSPOS;
 94                     float3 normal : NORMAL;
 95                     float4 tangent : TANGENT;
 96                     float2 uv : TEXCOORD0;
 97                 };
 98 
 99                 struct OutputPatchConstant { 
100                     //不同的图元,该结构会有所不同
101                     //该部分用于Hull Shader里面
102                     //定义了patch的属性
103                     //Tessellation Factor和Inner Tessellation Factor
104                     float edge[3] : SV_TESSFACTOR;
105                     float inside  : SV_INSIDETESSFACTOR;
106                 };
107 
108                 TessVertex tessvert (VertexInput v){
109                     //顶点着色器函数
110                     TessVertex o;
111                     o.vertex  = v.vertex;
112                     o.normal  = v.normal;
113                     o.tangent = v.tangent;
114                     o.uv      = v.uv;
115                     return o;
116                 }
117 
118                 //float _TessellationUniform;
119                 OutputPatchConstant hsconst (InputPatch<TessVertex,3> patch){
120                     //定义曲面细分的参数
121                     OutputPatchConstant o;
122                     o.edge[0] = _TessellationUniform;
123                     o.edge[1] = _TessellationUniform;
124                     o.edge[2] = _TessellationUniform;
125                     o.inside  = _TessellationUniform;
126                     return o;
127                 }
128 
129                 [UNITY_domain("tri")]//确定图元,quad,triangle等
130                 [UNITY_partitioning("fractional_odd")]//拆分edge的规则,equal_spacing,fractional_odd,fractional_even
131                 [UNITY_outputtopology("triangle_cw")]
132                 [UNITY_patchconstantfunc("hsconst")]//一个patch一共有三个点,但是这三个点都共用这个函数
133                 [UNITY_outputcontrolpoints(3)]      //不同的图元会对应不同的控制点
134               
135                 TessVertex hullProgram (InputPatch<TessVertex,3> patch,uint id : SV_OutputControlPointID){
136                     //定义hullshaderV函数
137                     return patch[id];
138                 }
139 
140                 [UNITY_domain("tri")]//同样需要定义图元
141                 VertexOutput ds (OutputPatchConstant tessFactors, const OutputPatch<TessVertex,3>patch,float3 bary :SV_DOMAINLOCATION)
142                 //bary:重心坐标
143                 {
144                     VertexInput v;
145                     v.vertex = patch[0].vertex*bary.x + patch[1].vertex*bary.y + patch[2].vertex*bary.z;
146                     v.tangent = patch[0].tangent*bary.x + patch[1].tangent*bary.y + patch[2].tangent*bary.z;
147                     v.normal = patch[0].normal*bary.x + patch[1].normal*bary.y + patch[2].normal*bary.z;
148                     v.uv = patch[0].uv*bary.x + patch[1].uv*bary.y + patch[2].uv*bary.z;
149 
150                     VertexOutput o = vert (v);
151                     return o;
152                 }
153             #endif
154 
155             float4 frag (VertexOutput i) : SV_Target
156             {
157                 float3 lightDir =_WorldSpaceLightPos0.xyz;
158                 float3 tnormal = UnpackNormal (tex2D (_DisplacementMap, i.uv));
159 
160                 half3 worldNormal;
161                 worldNormal.x=dot(i.tspace0,tnormal);
162                 worldNormal.y= dot (i.tspace1, tnormal);
163                 worldNormal.z=dot (i.tspace2, tnormal);
164 
165                 float3 albedo=tex2D (_MainTex, i.uv). rgb;
166                 float3 lightColor = _LightColor0.rgb;
167                 float3 diffuse = albedo * lightColor * DotClamped(lightDir,worldNormal);
168                 float3 viewDir = normalize (_WorldSpaceCameraPos. xyz-i. worldPos. xyz);
169                 float3 halfVector = normalize(lightDir + viewDir);
170                 float3 specular = albedo * pow (DotClamped (halfVector, worldNormal), _Smoothness * 100);
171                 float3 result = specular + diffuse;
172                 return float4(result, 1.0);
173 
174                 return float4(result,1.0);
175             }
176             ENDCG
177         }
178     }
179     Fallback "Diffuse"
180 }

 


 

 

几何着色器Demo演示

 

  • 根据顶点的法线方向画线

 

  • 根据顶点画三角形

 

 1 Shader "Unlit/GEO1"
 2 {
 3     Properties
 4     {
 5     }
 6     SubShader
 7     {
 8         Tags { "RenderType"="Opaque" }
 9         LOD 100
10 
11         Pass
12         {
13             CGPROGRAM
14             #pragma vertex vert
15             #pragma geometry geom
16             #pragma fragment frag
17             // make fog work
18             #pragma multi_compile_fog
19 
20             #include "UnityCG.cginc"
21 
22             struct appdata
23             {
24                 float4 vertex : POSITION;
25                 float3 normal : NORMAL;
26                 float3 tangent : TANGENT;
27             };
28 
29             //注意这里并不是从顶点直接到片元 而是要先经过几何着色器
30             struct v2g //vert-geo 
31             {      
32                 float4 vertex : SV_POSITION;
33                 float3 normal : NORMAL;
34                 float3 tangent : TANGENT;
35             };
36             
37             struct g2f //geo-frag
38             {
39                 float4 vertex : SV_POSITION;
40                 fixed4 col : COLOR;
41             };
42 
43             v2g vert (appdata v)
44             {
45                 v2g o;
46                 //不作操作
47                 o.vertex = v.vertex;
48                 o.normal = v.normal;
49                 o.tangent = v.tangent;
50                 return o;
51             }
52             
53             [maxvertexcount(3)]
54             void geom(point v2g input[1], inout TriangleStream<g2f> outStream) 
55             {
56                 g2f o;
57  
58                 float3 normalP = input[0].vertex + input[0].normal;
59                 //画三角形
60                 o.vertex = UnityObjectToClipPos(input[0].vertex - input[0].tangent * 0.125);
61                 o.col = fixed4(0.0, 1.0, 0.0, 1.0);
62                 outStream.Append(o);
63 
64                 o.vertex = UnityObjectToClipPos(input[0].vertex + input[0].tangent * 0.125);
65                 o.col = fixed4(0.0, 1.0, 0.0, 1.0);
66                 outStream.Append(o);
67 
68                 o.vertex = UnityObjectToClipPos(normalP);
69                 o.col = fixed4(0.0, 0.0, 1.0, 1.0);
70                 outStream.Append(o);
71             }
72 
73             fixed4 frag (g2f i) : SV_Target
74             {
75                 fixed4 col = i.col;
76                 return col;
77             }
78             ENDCG
79         }
80     }
81 }

 

  • 制作一片草地

 

给三角形上色,使其接近草。

 

 

 

 

随机长度宽度。

 

 

 

 

加入随机转向

 

 

 

 

 

加入曲面细分

 

 

 

 

加入风场

 

 

示例shader

  1 Shader "Unlit/GEO1"
  2 {
  3     Properties
  4     {
  5         _ColorRoot("ColorRoot", color) = (0,1,0,1) //根部颜色
  6         _ColorTop("ColorTop", color) = (0,1,0,1) //尖部颜色
  7 
  8         _WidthScale("WidthScale", Range(0.25,4)) = 1.0 //根部宽度倍率
  9         _WidthRandPow("WidthRandPow", Range(1.0,2.0)) = 1.0//宽度随机强度
 10 
 11         _HeightScale("HeightScale", Range(0.25,4)) = 1.0 //高度倍率
 12         _HeightRandPow("HeightRandPow", Range(1.0,2.0)) = 1.0//高度随机强度
 13 
 14         _RotAngle("RotAngle", Range(0.0,180.0)) = 0.0//旋转值
 15         _RotRandPow("RotRandPow", Range(1.0,2.0)) = 1.0//旋转随机强度
 16 
 17         _TessellationUniform("TessellationUniform",Range(1,16)) = 1//曲面细分强度
 18 
 19         _WindMap("WindMap", 2D) = "white"{}//风场图
 20         _WindSpeed("WindSpeed", Vector) = (0.1, 0.1, 0, 0)//吹风速度
 21         _WindPow("WindPow", Float) = 1//风力强度
 22     }
 23     SubShader
 24     {
 25         Tags { "RenderType"="Opaque" }
 26         LOD 100
 27 
 28         Pass
 29         {
 30             CGPROGRAM
 31             #pragma vertex vert
 32             #pragma geometry geom
 33             #pragma fragment frag
 34             #pragma hull hs
 35             #pragma domain ds
 36             #pragma target 4.6
 37 
 38             #include "UnityCG.cginc"
 39             #include "Lighting.cginc"
 40 
 41             struct appdata
 42             {
 43                 float4 vertex : POSITION;
 44                 float3 normal : NORMAL;
 45                 float3 tangent : TANGENT;
 46             };
 47 
 48             struct v2g //vert-tess
 49             {      
 50                 float4 vertex : SV_POSITION;
 51                 float3 normal : NORMAL;
 52                 float3 tangent : TANGENT;
 53             };
 54             
 55             struct g2f //geo-frag
 56             {
 57                 float4 vertex : SV_POSITION;
 58                 fixed4 col : COLOR;
 59             };
 60 
 61             struct InternalTessInterp_appdata {
 62               float4 vertex : INTERNALTESSPOS;
 63               float3 tangent : TANGENT;
 64               float3 normal : NORMAL;
 65               float2 texcoord : TEXCOORD0;
 66             };
 67 
 68             float4 _ColorRoot;
 69             float4 _ColorTop;
 70 
 71             float _WidthScale;
 72             float _WidthRandPow;
 73             float _HeightScale;
 74             float _HeightRandPow;
 75             float _RotAngle;
 76             float _RotRandPow;
 77 
 78             float _TessellationUniform;
 79 
 80             sampler2D _WindMap;
 81             float4 _WindMap_ST;
 82             vector _WindSpeed;
 83             float _WindPow;
 84             v2g vert (appdata v)
 85             {
 86                 v2g o;
 87                 //不作操作
 88                 o.vertex = v.vertex;
 89                 o.normal = v.normal;
 90                 o.tangent = v.tangent;
 91                 return o;
 92             }
 93 
 94             UnityTessellationFactors hsconst (InputPatch<v2g,3> v) {
 95               UnityTessellationFactors o;
 96               //_TessellationUniform = frac(_Time * 5)  * 32;
 97               o.edge[0] = _TessellationUniform; 
 98               o.edge[1] = _TessellationUniform; 
 99               o.edge[2] = _TessellationUniform; 
100               o.inside = _TessellationUniform;
101               return o;
102             }
103 
104             [UNITY_domain("tri")]
105             [UNITY_partitioning("fractional_odd")]
106             [UNITY_outputtopology("triangle_cw")]
107             [UNITY_patchconstantfunc("hsconst")]
108             [UNITY_outputcontrolpoints(3)]
109             v2g hs (InputPatch<v2g,3> v, uint id : SV_OutputControlPointID) {
110               return v[id];
111             }
112 
113             [UNITY_domain("tri")]
114             v2g ds (UnityTessellationFactors tessFactors, const OutputPatch<v2g,3> vi, float3 bary : SV_DomainLocation) {
115               appdata v;
116 
117               v.vertex = vi[0].vertex*bary.x + vi[1].vertex*bary.y + vi[2].vertex*bary.z;
118               v.tangent = vi[0].tangent*bary.x + vi[1].tangent*bary.y + vi[2].tangent*bary.z;
119               v.normal = vi[0].normal*bary.x + vi[1].normal*bary.y + vi[2].normal*bary.z;
120 
121               v2g o = vert (v);
122               return o;
123             }
124 
125             [maxvertexcount(3)]
126             void geom(point v2g input[1], inout TriangleStream<g2f> outStream) 
127             {
128                 g2f o;
129 
130                 _WidthScale = _WidthScale * lerp(_WidthScale / _WidthRandPow, _WidthScale * _WidthRandPow, abs(sin(input[0].vertex.x * _WidthRandPow + cos(input[0].vertex.z))));
131                 _HeightScale = _HeightScale * lerp(_HeightScale / _HeightRandPow, _HeightScale * _HeightRandPow, abs(frac(fmod(input[0].vertex.x,_HeightRandPow) + (input[0].vertex.z - _HeightRandPow) * _HeightRandPow)));
132 
133                 float3 normalP = input[0].vertex + input[0].normal * _HeightScale;
134                 
135                 float3 BinT = cross(input[0].normal, input[0].tangent);
136                 _RotAngle = _RotAngle + 180 * (_RotRandPow - 1) * lerp(0, 180, abs(cos(input[0].vertex.x + cos(input[0].vertex.z))));
137                 float3 RotTangent = input[0].tangent * cos(_RotAngle / 3.141592);
138                 float3 RotBinTangent = BinT * sin(_RotAngle / 3.141592);
139                 float3 RotValue = (RotTangent + RotBinTangent)  * 0.125 * _WidthScale;
140 
141                 //风场
142                 float2 uv = input[0].vertex.xz * _WindMap_ST.xy + _WindMap_ST.zw + _WindSpeed * _Time.x;
143                 float2 windTex = (tex2Dlod(_WindMap, float4(uv, 0, 0)).xy * 2 - 1) * _WindPow;
144                 float3 wind = normalize(float3(windTex.x, windTex.y, 0));
145 
146                 //画三角形
147                 o.vertex = UnityObjectToClipPos(input[0].vertex - RotValue);
148                 o.col = _ColorRoot;
149                 outStream.Append(o);
150 
151                 o.vertex = UnityObjectToClipPos(input[0].vertex + RotValue);
152                 o.col = _ColorRoot;
153                 outStream.Append(o);
154 
155                 o.vertex = UnityObjectToClipPos(normalP + wind);
156                 o.col = _ColorTop;
157                 outStream.Append(o);
158             }
159 
160             fixed4 frag (g2f i) : SV_Target
161             {
162                 fixed4 col = i.col;
163                 return col;
164             }
165             ENDCG
166         }
167     }
168 }

 


 

 

参考链接

 

【技术美术百人计划】图形 3.3 曲面细分与几何着色器 大规模草渲染

LearnOpenGL-CN-高级OpenGL几何着色器

【Unity Shader】使用Geometry Shader进行大片草地的实时渲染

UnityShader_使用几何着色器实现草坪效果(简易)

 

跳转回百人合集

posted @ 2022-04-27 01:17  anesu  阅读(262)  评论(0编辑  收藏  举报