前言
曲面细分着色器
在unity hdrp中,由于只支持hlsl因此,要实现tessellation的功能,不能与之前shader相同,需要自己手动重写tessellation,那么这部分的代码就主要为了完成在hlsl中重写曲面细分着色器。
资源来源于Microsoft的tessellation教程
pipeline
Hull shader
转换低程度的控制点为patch的控制点。并为tessellation阶段和domain阶段预处理每一个patch的数据。
在unityshader中,对应的hull脚本为:
[maxtessfactor(MAX_TESSELLATION_FACTORS)]
[domain("tri")]
[partitioning("fractional_odd")]
[outputtopology("triangle_cw")]
[patchconstantfunc("HullConstant")]
[outputcontrolpoints(3)]//输出的顶点数,如果需要临近节点,那么需要写多一些
PackedVaryingsToDS MyHull(InputPatch<PackedVaryingsToDS, 3> input, uint id : SV_OutputControlPointID)
{
// Pass-through
return input[id];
}
注意看函数的输入输出。在设置中会要求设定输入输出的顶点数,这些顶点数能够决定是否需要临近顶点的数据,目前是3,那么就是不需要
- shader的输入范围为1-32个控制点,
- 输出1-32个控制点,忽略tessellation控制因子的值。输出的控制点将会提供给domain-shader stage,而patch constant将会提供给domain shader,而曲面细分因子将会提供给domain shader和tessellation shader
- tessellation factor因子细分的程度
- 函数前面的声明是为了提供给tessellation shader:控制点的数量、patch面、曲面细分(partitioning type)的类型.
- 如果tessellation 因子被设置为0或者NAN,那么patch将会被剔除,tessellation stage将会运行失败,domain将会输出错误,细分网格输出为空。
在深层次,hull-stage只处理两部分:控制点的相位,patch-constant的相位(phase),在硬件中是并行处理。
- 控制点相位操作读入每一个控制点,并且生成输出点(被controlpointID)定义
- patch-constant相位操作生成角度细分因子和另一个patch constant。patch-constant phase在同一时间计算,并且只有只读属性。
hull shader的示例:
//前面是什么向tessellation shader的返回值
[patchsize(12)]
[patchconstantfunc(MyPatchConstantFunc)]
//函数体,包含范围的patch和tessellation factor
MyOutPoint main(uint Id : SV_ControlPointID,
InputPatch<MyInPoint, 12> InPts)
{
MyOutPoint result;
...
//处理control point和patch constant
//control-point phase 和patch-constant phase
result = TransformControlPoint( InPts[Id] );
return result;
}
unity hdrp中的hullconstant phase operation
TessellationFactors HullConstant(InputPatch<PackedVaryingsToDS, 3> input)
{
VaryingsToDS varying0 = UnpackVaryingsToDS(input[0]);
VaryingsToDS varying1 = UnpackVaryingsToDS(input[1]);
VaryingsToDS varying2 = UnpackVaryingsToDS(input[2]);
float3 p0 = varying0.vmesh.positionRWS;
float3 p1 = varying1.vmesh.positionRWS;
float3 p2 = varying2.vmesh.positionRWS;
float3 n0 = varying0.vmesh.normalWS;
float3 n1 = varying1.vmesh.normalWS;
float3 n2 = varying2.vmesh.normalWS;
// ref: http://reedbeta.com/blog/tess-quick-ref/
//计算边缘因子
// x - 1->2 edge
// y - 2->0 edge
// z - 0->1 edge
// w - inside tessellation factor
float4 tf = GetTessellationFactors(p0, p1, p2, n0, n1, n2);
TessellationFactors output;
output.edge[0] = min(tf.x, MAX_TESSELLATION_FACTORS);
output.edge[1] = min(tf.y, MAX_TESSELLATION_FACTORS);
output.edge[2] = min(tf.z, MAX_TESSELLATION_FACTORS);
output.inside = min(tf.w, MAX_TESSELLATION_FACTORS);
return output;
}
在hlsl中tessellation是一个固定的阶段,也就是不能实现可编程。tessellation stage的任务是将hull stage传过来的domain(四边形、三角形、线)细分为跟小的对象。曲面细分器将每一个域的控制转换到归一化空间中[0-1],例如一个四边形域细分到一个单元四边形中。
tessellator这时候根据细分因子和分割类型将patch进行处理并输出处理后的uv坐标值。
tessellation分为两个阶段进行处理,
第一个阶段主要处理细分因子,避免因四舍五入出现的精度问题
第二阶段,根据细分的类型进行细分。生成点或者拓扑列表
Type of Partitioning | Range |
---|---|
Fractional_odd | [1…63] |
Fractional_even | TessFactor range: [2…64] |
Integer | TessFactor range: [1…64] |
Pow2 | TessFactor range: [1…64] |
最后一个阶段
Domain-shader stage
根据之前两个阶段生成的控制点数据、patch数据、patch固定值生成顶点位置。
domain-shader包括:
- 一次调用读取一个细分阶段的坐标
- 读取一次hull-shader的控制点
- 输出顶点的坐标
- 输入包括hull-shader的输出:控制点、patch constant数据以及细分因子。(细分因子包括细分阶段使用的原始数据)
经过domainshader处理后,shader输出几何着色器所需要的顶点信息。
需要 hlsl model 5版本的支持。
总结
hlsl中的细分曲面函数,分为三个部分
hull shader-》tessellation stage-》domain shader
hull shader, and domain shader are programable stage in render pipeline.The tessellation stage is a fixed function stage.
In hull stage, patch is inputted to operate and generate other patch and patch constant.
tessellation fixed rounding probrem of tessellation factors,and tiles a canonical domain in a normalized coordinate system.
Domain operate outputs of hull stage and tessellation stage , and output position of vertex
fork my github repository here