大家好,欢迎来到IT知识分享网。
曲面细分:将几何体细分为更小的三角形并偏移产生曲面,达到细节丰富的效果。工作都在shader中,进行格式的配置即可。
一、CPU侧
启用了曲面细分,就要把点作为控制点来传输:
1.图元装配阶段
cmdList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_4_CONTROL_POINT_PATCHLIST);
2.PSO定义阶段
opaquePsoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_PATCH; opaquePsoDesc.HS = { reinterpret_cast<BYTE*>(mShaders["tessHS"]->GetBufferPointer()), mShaders["tessHS"]->GetBufferSize() }; opaquePsoDesc.DS = { reinterpret_cast<BYTE*>(mShaders["tessDS"]->GetBufferPointer()), mShaders["tessDS"]->GetBufferSize() };
以上就是在CPU端从设置PSO和绑定渲染流水线的控制点图元设置全过程。其中输入装配可以根据控制点个数具有不同的类型(修改格式而已,从1-32)
二、GPU侧
应用曲面细分之后,顶点着色器处理对象就是单个控制点,可用于对控制点进行调整:
1.顶点着色器
VertexOut VS(VertexIn vin) { VertexOut vout; vout.PosL = vin.PosL;//不调整数据,只做传递用 return vout; }
出了顶点着色器,就进入外壳着色器。
2.外壳着色器
所谓外壳着色器就是一种规则的制定者,共分为两种:常量外壳着色器与控制点外壳着色器。
1).常量外壳着色器HS
如上,指定4个控制点,实际意义就是将4个控制点组成一个单个面片。常量HS的作用就是处理单个面片(处理一个面片就调用一次),输出该面片细分的因子(边缘与内部细分方式):
struct PatchTess { float EdgeTess[4] : SV_TessFactor;//四条边细分因子,几边形就是几条边 float InsideTess[2] : SV_InsideTessFactor;//内部细分因子,三角形一个,四边形两个(横竖) }; PatchTess ConstantHS(InputPatch<VertexOut, 4> patch, uint patchID : SV_PrimitiveID) { PatchTess pt; //各边均分为3等份, pt.EdgeTess[0] = 3; pt.EdgeTess[1] = 3; pt.EdgeTess[2] = 3; pt.EdgeTess[3] = 3; //四边形内部行列数 pt.InsideTess[0] = 3; pt.InsideTess[1] = 3; return pt; }
注意:
- InputPatch<VertexOut, 4> patch表示是由顶点着色器输出的4个控制点构成的一个面片;
- 每个面片具有独立的图元索引:uint patchID : SV_PrimitiveID;
- 返回值 return pt;将细分因子返回至流水线以供调用。
2).控制点外壳着色器HS
以大量的控制点作为输入与输出,每输出一个控制点着色器被调用一次。
龙书对这个部分有大量的叙述,什么PN三角形法,N-patches方法啥的。个人感觉没太大感触,这个控制点外壳着色器,就是细分规则的制定者。
struct HullOut { float3 PosL : POSITION; }; [domain("quad")]//面片类型(quad / tri / isoline) [partitioning("integer")]//剔除小数部分,使用细分因子整数部分,如果考虑小数(fractional_even / fractional_odd) [outputtopology("triangle_cw")]//细分生成的三角形绕序(自动组织定义正反面),对线段细分则:line [outputcontrolpoints(4)]//控制点个数,一个控制点HS执行一次 [patchconstantfunc("ConstantHS")]//指定常量外壳着色器以获得细分因子 [maxtessfactor(64.0f)]//钳制最大细分因子 HullOut HS(InputPatch<VertexOut, 4> p, //同常量HS,从顶点着色器过来 uint i : SV_OutputControlPointID,//控制点ID,单次执行用于索引点 uint patchId : SV_PrimitiveID) { HullOut hout; //更复杂的逻辑,可以调整输出控制点点位置 hout.PosL = p[i].PosL; return hout; }
3.镶嵌化
通过两个HS,我们似乎只是制定了曲面划分的规则,但是控制点还是那些控制点,没有任何变化。那我们的规则到底谁来执行呢?答案就是镶嵌化。
镶嵌化会根据我们指定的规则:
1.ConstantHS部分细分因子以SV_TessFactor,SV_InsideTessFactor标记的内容被系统识别;
2.HS部分通过[]指定了一堆规则
通过硬件在两个控制点之间进行插值,并给出在控制点构成的面片空间下的uv(w)坐标,表示插值生成的新点位置。提供给域着色器使用。
4.域着色器
struct DomainOut { float4 PosH : SV_POSITION;//此时才产生真正用于场景顶点的坐标,代替原顶点着色器部分 }; [domain("quad")]//面片类型,4个控制点的,注意全过程的对应 DomainOut DS(PatchTess patchTess, //虽然这里有曲面细分但是似乎没使用过 float2 uv : SV_DomainLocation, //由镶嵌化对控制点间使用细分因子插值得到的uv(w) const OutputPatch<HullOut, 4> quad)//从HS穿过来的控制点 { DomainOut dout; // 使用uv双线性插值.对于三角形就是uvw的中心坐标插值 float3 v1 = lerp(quad[0].PosL, quad[1].PosL, uv.x); float3 v2 = lerp(quad[2].PosL, quad[3].PosL, uv.x); float3 p = lerp(v1, v2, uv.y); // 偏移使之成为曲面,丰富细节 p.y = 0.3f*( p.z*sin(p.x) + p.x*cos(p.z) ); float4 posW = mul(float4(p, 1.0f), gWorld); dout.PosH = mul(posW, gViewProj); return dout; }
之后交给PS,就好像VS交给PS数据一样处理。
三、基于视点距离的曲面细分例子
这里只分享shader部分代码,因为CPU段顶点配置是非常easy的任务。
struct VertexIn { float3 PosL : POSITION; }; struct VertexOut { float3 PosL : POSITION; }; VertexOut VS(VertexIn vin) { VertexOut vout; vout.PosL = vin.PosL; return vout; } struct PatchTess { float EdgeTess[4] : SV_TessFactor; float InsideTess[2] : SV_InsideTessFactor; }; PatchTess ConstantHS(InputPatch<VertexOut, 4> patch, uint patchID : SV_PrimitiveID) { PatchTess pt; float3 centerL = 0.25f*(patch[0].PosL + patch[1].PosL + patch[2].PosL + patch[3].PosL); float3 centerW = mul(float4(centerL, 1.0f), gWorld).xyz; float d = distance(centerW, gEyePosW); // Tessellate the patch based on distance from the eye such that // the tessellation is 0 if d >= d1 and 64 if d <= d0. The interval // [d0, d1] defines the range we tessellate in. const float d0 = 20.0f; const float d1 = 100.0f; //注意等于1表示不细分,0表示剔除 float tess = max(64.0f*saturate( (d1-d)/(d1-d0) ),1.0); // Uniformly tessellate the patch. pt.EdgeTess[0] = tess; pt.EdgeTess[1] = tess; pt.EdgeTess[2] = tess; pt.EdgeTess[3] = tess; pt.InsideTess[0] = tess; pt.InsideTess[1] = tess; return pt; } struct HullOut { float3 PosL : POSITION; }; [domain("quad")] [partitioning("integer")] [outputtopology("triangle_cw")] [outputcontrolpoints(4)] [patchconstantfunc("ConstantHS")] [maxtessfactor(64.0f)] HullOut HS(InputPatch<VertexOut, 4> p, uint i : SV_OutputControlPointID, uint patchId : SV_PrimitiveID) { HullOut hout; hout.PosL = p[i].PosL; return hout; } struct DomainOut { float4 PosH : SV_POSITION; }; // The domain shader is called for every vertex created by the tessellator. // It is like the vertex shader after tessellation. [domain("quad")] DomainOut DS(PatchTess patchTess, float2 uv : SV_DomainLocation, const OutputPatch<HullOut, 4> quad) { DomainOut dout; // Bilinear interpolation. float3 v1 = lerp(quad[0].PosL, quad[1].PosL, uv.x); float3 v2 = lerp(quad[2].PosL, quad[3].PosL, uv.x); float3 p = lerp(v1, v2, uv.y); // Displacement mapping p.y = 0.3f*( p.z*sin(p.x) + p.x*cos(p.z) ); float4 posW = mul(float4(p, 1.0f), gWorld); dout.PosH = mul(posW, gViewProj); return dout; } float4 PS(DomainOut pin) : SV_Target { return float4(1.0f, 1.0f, 1.0f, 1.0f); }
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/124409.html