一、曲面细分着色器(Tessellation Shader)
曲面细分(TESS)是利用镶嵌化处理技术对网格中三角形进行细分,以此来增加物体表面上三角形数量,表现出更多的细节。
曲面细分着色器的应用
(1)海浪、雪地等
(2)与置换贴图
TESS的输入与输出
-
输入:Patch,可以看成是多个顶点的集合,包含每个顶点的属性;可以指定一个Patch包含的顶点数以及自己的属性
-
功能:将图元细分(可以是三角形,矩形等)
-
输出:细分后的顶点
TESS流程
-
Hull Shader(细分控制着色器):决定细分的数量(设定Tessellation factor以及Inside Tessellation factor)、对输入的Patch参数进行改变(如果需要)
-
Tessellation Primitive Generation:将图元细分(可以是三角形,矩形等)
-
Domain Shader:对细分后的点进行处理,从重心空间(Barycentric coordinate system)转换到屏幕空间
Hull Shader
TessellationFactors patchConstantFunction (InputPatch<vertexInput, 3> patch)
{
TessellationFactors f;
f.edge[0] = _TessellationUniform;
f.edge[1] = _TessellationUniform;
f.edge[2] = _TessellationUniform;
f.inside = _TessellationUniform;
return f;
}
[UNITY_domain("tri")]
[UNITY_outputcontrolpoints(3)]
[UNITY_outputtopology("triangle_cw")]
[UNITY_partitioning("integer")]
[UNITY_patchconstantfunc("patchConstantFunction")]
vertexInput hull (InputPatch<vertexInput, 3> patch, uint id : SV_OutputControlPointID)
{
return patch[id];
}
重要特性解析:
-
[UNITY_domain("tri")]:指明输入进hull shader的图元是三角形
-
[UNITY_outputcontrolpoints(3)]:为hull shader输出的outputpatch中的顶点数量
-
[UNITY_outputtopology("triangle_cw")]:决定图元的朝向,由组成三角形的三个顶点的顺序所产生的方向决定,cw为clockwise顺时针,ccw为counter clockwise逆时针
-
[UNITY_partitioning("integer")]:决定舍入规则
integer:细分层将被截断在[1,max]范围内,然后取整到下一个整数值。
fractional_odd:数值将被截断在[1,max]范围内,然后取整到下一个奇数整数值n,然后将边界划分为n-2个等长的部分以及2个位于两端的部分。
fractional_oven:数值将被截断在[1,max]范围内,然后取整到下一个偶数整数值n,然后将边界划分为n-2个等长的部分以及2个位于两端的部分。
-
[UNITY_patchconstantfunc("hsconst")]:指明计算factor的方法,然后在方法hsconst中计算每个边的Tessellation factor和内部的Inside Tessellation factor。
Domain Shader
[UNITY_domain("tri")]
vertexOutput domain(TessellationFactors factors, OutputPatch<vertexInput, 3> patch, float3 barycentricCoordinates : SV_DomainLocation)
{
vertexInput v;
#define MY_DOMAIN_PROGRAM_INTERPOLATE(fieldName) v.fieldName = \
patch[0].fieldName * barycentricCoordinates.x + \
patch[1].fieldName * barycentricCoordinates.y + \
patch[2].fieldName * barycentricCoordinates.z;
MY_DOMAIN_PROGRAM_INTERPOLATE(vertex)
MY_DOMAIN_PROGRAM_INTERPOLATE(normal)
MY_DOMAIN_PROGRAM_INTERPOLATE(tangent)
return tessVert(v);
}
重要特性解析:
-
[UNITY_domain(“tri”)]:决定了输入数据SV_DomainLocation的类型,不同类型图元使用不同长度的向量
二、几何着色器(Geometry Shader)
几何着色器的应用
(1)几何动画
(2)草地等(与曲面细分着色器结合)
GS的输入与输出
-
输入:图元(三角形、矩形、线等);根据图元的不同,shader中会出现对应不同数量的顶点
-
输出:图元,一个或多个;需要自己从顶点构建,顺序很重要,同时需要定义最大输出的顶点数
Geo Shader
[maxcertexcount(N)]
void ShaderName (PrimitiveType InputVertexType InputName[NumElements],
inout StreamOutputObjectVertexType) OutputName){
// 几何着色器具体实现
StreamOutputObjectVertexType gout;
OutputName.Append(gout);
OutputName.RestartStrip();
OutputName.Append(gout);
}
解释说明:
1.[maxcertexcount(N)]:用来指定几何着色器单次调用所输出的最大顶点数量
2.PrimitiveType:输入图元的类型,必须对应输入装配阶段的图元拓扑类型
-
point:输入图元拓扑类型为点列表
-
line:输入图元拓扑类型为线列表或线条带
-
triangle:输入的图元拓扑类型为三角形列表或三角形带
-
lineadj:输入的图元拓扑类型为线条列表/带及其邻接图元
-
triangleadj:输入的图元为三角形列表/带及其邻接图元
3.StreamOutputObjectVertexType:流类型(stream type) ,存有一系列顶点,它们定义了几何着色器输出的几何图形
-
PointStream<OutputVertexType>:一系列顶点所定义的点列表
-
LineStream<OutputVertexType>:一系列顶点所定义的线条带
-
TriangleStream<OutputVertexType>:一系列顶点所定义的三角形带
4.Append函数:用来将几何着色器的输出数据追加到一个现有的流中
5.RestartStrip函数:用来结束当前的基元条带,开始一个新的条带;如果当前的条带没有足够的顶点被追加出来以填满基元拓扑结构,那么末端的不完整基元将被丢弃
三、草地应用案例
具体参考:Unity Grass Shader Tutorial (roystan.net)
增加了与草地的互动:
geometryOutput VertexOutput(float3 pos, float2 uv, float3 normal)
{
geometryOutput o;
float3 worldPos = mul(unity_ObjectToWorld, pos);
float dis = distance(worldPos, _PlayerPos);
float forceStrength = saturate((_InteractRadius - dis) * _InteractStrength);
float3 forceDir = normalize(worldPos - _PlayerPos);
float3 force = forceStrength * forceDir;
worldPos.xyz += force;
o.pos = UnityWorldToClipPos(worldPos);
o.uv = uv;
o._ShadowCoord = ComputeScreenPos(o.pos);
o.normal = UnityObjectToWorldNormal(normal);
#if UNITY_PASS_SHADOWCASTER
// Applying the bias prevents artifacts from appearing on the surface.
o.pos = UnityApplyLinearShadowBias(o.pos);
#endif
return o;
}
思路很简单,通过草的顶点位置减去需要交互物体的位置得到草受力的方向,让其顶点在此方向上偏移即可。