曲面细分着色器

  • 1. 背景
  • 2. 曲面细分工作机制
    • 2.1 Hull Shader(外壳着色器)
    • 2.2 Tessellator(曲面细分器)
    • 2.3 Domain Shader(域细分器)
  • 3. 编程开发
    • 3.1 hull shader编程开发
      • 1. 设计输入/输出控制点格式
      • 2. 设计生成面片常数参量数据的函数
      • 3. 设计生成控制点的主函数
    • 3.2 domain shader编程开发

1. 背景

曲面细分建模(Tessellation)是一种通过多边形分解来提升几何逼真度的方法。曲面细分会导致图元和顶点的增加,其主要实现依据是对顶点法向量进行插值,达到几何面中用更细腻的多图元模拟表达曲面图元的效果。曲面细分的好处是:节省原始模型的数据空间,提高模型表达的质量、性能和可靠性
注意,对面片(patch)顶点法向量均一致的平面图元而言,曲面细分并不能提高逼真度,只有针对相邻面片有不同法向量的三角形来描述的模型,曲面细分才能提高逼真度。

曲面细分是渲染管线的一个可选项,DirectX 12中采用了三个部件来协同从流水线(rendering pipeline)上接管流数据进行曲面细分。它们分别是:Hull Shader 外壳着色器, Tessellator 曲面细分器, Domain Shader 域着色器。其中,Hull Shader和Domain Shader可编程,Tessellator是固定阶段不可编程。

Fig1. Pipeline with hardware tessellation

2. 曲面细分工作机制

曲面细分三个部分的作用分别为:
1)Hull Shader: 生成细分输出面片的顶点,更新所有逐顶点或逐面片的属性值; 设置细分层次因数,以控制生成图元的属性值。
2)Tessellator: 对整个图元几何区域创建采样模式,并根据采样模式生成细分的面片图元。
3)Domain Shader: 对每个域采样计算生成的顶点数据,从而使得细分图元能够接入到流水线的下一步。

2.1 Hull Shader(外壳着色器)

以下图为例说明曲面细分中的Hull Shader技术手段。图中黑色粗线表示的是原始模型的面片图元(patch)及其邻接的拓扑顶点,共有5个模型图元。Hull Shader会分别接收每一个图元及其拓扑结构,内部根据法向量等因素生成Bezier曲面的控制点(control points)与曲面细分参数(tess factor, TF)。

Hull Shader完成的工作包括两部分:

  • 对每个输入的图元进行运算,转换初始图元的顶点,生成Bezier拟合曲面的控制点;
  • 为Tessellator和Domain Shader准备需要用到的转换参数,即图元的细分因子参数,相当于图元的预处理过程。

Hull Shader实际上是通过两个并行的过程来实现计算任务的:控制点生成过程图元细分因子参数生成过程

  • Hull Shader依据传入的图元顶点生成新的控制点,并通过产生的系统值SV_OutputControlPointID给生成的顶点赋索引值,这个就是控制点生成过程。
  • 图元细分因子参数生成过程也是对每个图元运行一次。细分因子包括:edgeTessFactor[4] (分别指代left edge, right edge, top edge, bottom edge细分程度)、insideTessFactor[2] (分别指代内部u,v坐标轴细分程度)。

其中,生成的控制点数据和TF将直接传给Domain shader,同时TF也传给tessellator。即TF决定了图元的细分过程,只是这个过程在Tessellator中完成,如果TF值无效,则该图元将被忽略,不进行细分优化建模。

生成控制点数据涉及到的参数有:(也称为control points hull shader参数)

参数名称 含义 可能的取值
domain 细分发生的区域 isoline(等值线)、 tri(三角形)、quad(四边形) 注:不同的domain类型,细分的方式有区别
partitioning 细分模式 integer, fractional_even, fractional_odd, pow2
outputtopology tessellator输出图元的拓扑结构 triangle_cw(顺时针环绕),triangle_ccw(逆时针环绕)
outputcontrolpoints hull shader每个线程生成控制点的数目 不一定与输入数量相同,可以新增控制点
max_tessfactor 最大细分度 告知驱动程序shader用到的细分度,硬件可能会针对这个做出优化。DirectX 11和OpenGL core都支持64
patchconstantfunc 指定生成图元细分因子的函数 如下文中的SetHullConstantsHS函数

图元细分参数包括:(也称为const hull shader参数)

参数名称 含义
edgeTess[0] lefe edge细分程度
edge Tess[1] top edge细分程度
edge Tess[2] right edge细分程度
edge Tess[3] bottom edge细分程度
inside Tess[0] 内部细分程度,u-axis
insde Tess[1] 内部细分程度,v-axis

外侧细分因子负责控制细分区域的周长,内侧细分因子控制细分区域的内部划分方式(区域内水平和垂直方向上各有多少区域存在)。

值得注意的是,
当面片为quad时,edgeTess的长度为4,insideTess的长度为2,分别指定四条边各被分成多少段、内部在横向和纵向各被分为多少段;
当面片为tri时,edgeTess的长度为3,insideTess的长度为2,分别指定三条边各被分成多少段、内部有多少个点;
当面片为isoline时,edgeTess的长度为2,第0个元素指定线段的个数,第1个元素指定线段被分为多少段,insideTess会被忽略。

2.2 Tessellator(曲面细分器)

Tessellator的主要功能是将图元区域分成若干小的图元对象,对每个图元仅运行一次。它首先将输入图元归化为标量化的规范域([0,1]空间),然后根据hull shader提供的细分因子进行曲面细分。它输出uvw标量坐标和新面片的几何拓扑类型到domain shader中。
输入:patch, TF
输出:uvw或uv标量坐标,patch topology

quad: 顶点以uv坐标的形式传给Domain Shader.

tri: 顶点以uvw重心坐标的形式传给Domain Shader.

isoline:顶点以uv坐标的形式传给Domain Shader.
三角形的内侧细分因子t,如果t是一个偶数,那么三角形域的中心(重心坐标)将定位于(1/2, 1/2, 1/2),然后再中心点和周长之间生成(t/2)-1个同心三角形。反之,如果t是一个奇数,那么到周长为止将生成(t/2)-1个同心三角形,但中心点
(重心坐标)不再是一个细分坐标。

曲面细分器内部存在两个阶段:
1.对细分因子进行优化处理,如对细分因子进行四舍五入,过滤非常小的因子,减少或合并因子,使用32bit浮点运行等。
2. 根据细分因子进行曲面细分,它使用16bit定点小数进行运算,这样即可采用硬件加速,也可保障运算精度。

2.3 Domain Shader(域细分器)

域着色器用于计算细分图元的顶点位置,它对Tessellator的每个输出点运行一次,能够只读访问其输出的uvw标量坐标,同时使用hull shader的两项输出数据。所有的顶点信息都会在这里计算,会涉及到大量运算。
需要注意的是,经过domain shader处理后的数据流会丢失图元的邻接拓扑关系,geometry shader将无法从这种经过曲面细分后的数据流中正确提取图元的拓扑关系。因此,在geometry shader需要使用图元拓扑结构的情况下,geometry shader与曲面细分两个过程不能同时使用。

DS输出的数据,可能会先传给GS(Geometry Shader)进行进一步的计算(增加顶点、修改顶点位置,计算顶点属性)。也可以直接(当然首先要进行裁剪和光栅化)传给FS(Fragment Shader)进行片元着色。最后进入输出合并阶段,完成整个渲染管线。

3. 编程开发

3.1 hull shader编程开发

1. 设计输入/输出控制点格式

初始的输入控制点来源于数据流的模型顶点,通常根据细分面片的需要,除了顶点的坐标位置,描述面片方向的顶点法向量、切向量和同样需要关联细分的纹理坐标都需要作为顶点输入数据传给外壳着色器。相关代码如下:

struct HS_POINT
{float3 vPosition:WORLDPOS; //顶点坐标float2 vUV: TEXCOORD0; //纹理坐标float3 vNormal: NORMAL; //法向量
}

经过hull shader运算处理后,输出的数据依然是控制点,数据流的性质没有发生改变。

hull shader还需要面片图元的细分因子参数格式,可采用下面的数据结构:

struct HS_CONSTANT_FACTOR
{float Edges[4]: SV_TessFactor; //边缘细分因子float Inside[2]: SV_InsideTessFactor; //内部细分因子
}

2. 设计生成面片常数参量数据的函数

在对每个面片进行细分之前,需要先对每个面片生成面片描述常量数据。实现这一功能的函数定义如下:

HS_CONSTANT_FACTOR SetHullConstantsHS(InputPatch<HS_POINTS,3> ip, unit PatchID: SV_PrimitiveID)
{HS_CONSTANT_FACTOR output;output.edges[0] = 4;output.edges[1] = 4;output.edges[2] = 4;output.inside = 4;return output;
}

常量生成函数的参数包括以下几个:
1)一个通过SV_PrimitiveID定义的区别标识面片的唯一ID变量PatchID;
2) 输入控制点ip,同上面定义的输入控制点结构一样,如通过ip[0]访问图元的第1个控制点;
3) 最基本的函数实现必须返回后一步Tessellator计算每个面片的TF,如HS_CONSTANT_FACTOR结构。本例中, 将所有的因子都赋值为常量4是最简单的情形,也只可以通过控制点来动态计算这些值。

3. 设计生成控制点的主函数

hull shader的主入口函数除了负责生成控制点外,还负责对整个曲面细分过程进行参数化配置,这主要是通过HLSL修饰关键词来描述。

[domain("tri")] [partitioning("integer")] [outputtopology("triangle_cw")]
[outputcontrolpoints(3)] [patchconstantfunc("SetHullConstantsHS")]
HS_POINT HullShaderMain(InputPatch<HS_POINT, 3> ip, unit i:SV_OutputControlPointID, uint PatchID: SV_PrimitiveID)
{HS_POINT output;output.vPosition = ip[PatchID].vPosition;output.vNormal = ip[PatchID].vNormal;output.vUV = ip[PatchID].vUV;return output;
}

3.2 domain shader编程开发

domain shader的任务是结合原始图元的顶点坐标xyz,将tessellator内部标题化的细分图元顶点坐标uvw反算到世界坐标空间中,保障细分图元的空间坐标与原始图元的一致,与周边其他图元的边进行无缝对接。
domain shader的输入参数包括hull shader输出的控制点数据、TF和区域位置数据。控制点与常量数据的结构定义均应在hull shader中指定并保持一致,如上段代码中的HS_POINT结构定义。
domain shader的输出为细分后顶点,由于domain shader后数据将回归到rendering pipeline的正常数据流上,所以可以根据需要另行指定输出顶点格式,或直接采用顶点着色器的输出顶点格式。

区域位置相当于记载图元在模型上定义的原点位置,以便反算出细分图元的模型坐标。对quad, isoline图元而言,采用float2类型的变量声明就可以,对于tri类型的图元,则要通过float3类型的变量来表示其重心坐标。定位原点可以是图元的顶点或其他特征点,如重心、中点、垂心,但由于在hull shader内部有约束,只需要按其指定的规则声明,声明的系统值变量应以SV_DomainLocation语义关键词来描述,代码如下:
float2 UV: SV_DomainLocation
在主函数前一定要加上对图元类型的域描述,其定义与hull shader一致。如有下面一个主函数:

[domain("tri")]
HS_POINT Domain_Shader(HS_CONSTANT_FACTOR input, float3 uvw: SV_DomainLocation, const outputPatch<HS_POINT, 3> patch)
{HS_POINT output;float3 pos;//基于重心坐标的控制顶点生成pos = uvw.x * patch[0].vPosition + uvw.y * patch[1].vPosition +uvw.z * patch[2].vPosition;//反算新产生的细分顶点在投影空间中的位置output.vPosition = mul(float4(pos, 1.0f), worldViewProjectMatrix);   //统计图元三个顶点实现的纹理坐标填充output.vUV = uvw.x * patch[0].vUV + uvw.y * patch[1].vUV + uvw.z * patch[2].vUV;return output;
}

或者quad类型示例:

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, //patchTess:细分参数float2 uv : SV_DomainLocation, //uv:曲面细分阶段传入的顶点位置信息const OutputPatch<HullOut, 4> quad) //quad:HS传入的patch数据,尖括号的第二个参数与HS的outputcontrolpoints对应
{  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);float4 posW = mul(float4(p, 1.0f), gWorld);dout.PosH = mul(posW, gViewProj);return dout;
}

DirectX 12 曲面细分着色器笔记相关推荐

  1. 曲面细分着色器---细分二维四边形

    openGL系列文章目录 文章目录 openGL系列文章目录 前言 一.曲面细分 二.细分二维四边形 参考 前言 术语Tessellation(镶嵌)是指一大类设计活动,通常是指在平坦的表面上,用各种 ...

  2. 曲面细分着色器与几何着色器

    着色器执行顺序 顶点着色器->曲面细分着色器->几何着色器->片元着色器 曲面细分着色器细分后的顶点属于重心空间. 曲面细分着色器 应用:海浪.雪地等 与置换贴图结合:只使用法线贴图 ...

  3. 曲面细分(subdivision)曲面细分着色器GPU的LOD

    曲面细分是指将一个模型的面合理的分成更多小的面,从而提升模型精度,提高渲染效果 曲面简化是指将一个模型的面合理的合成更少的面,从而降低模型精度,为特定情形下提供使用(如LOD技术) .这一过程是可以在 ...

  4. unity曲面细分着色器详解

     前言:本文翻译自catlikecoding上一篇十分详细的英文blog并修改了几处错误,逐行解释了如何在自己的shader中添加曲面细分支持,并给出了多种计算细分因子的方案以及它们的优缺点. 原文链 ...

  5. UnityShader 曲面细分着色器 生成地形 高度贴图

    什么是曲面细分着色器 如下图 ​ 曲面细分着色器比较官方定义:可以将一个几何体细化为一个球体也能将一根直线无限向曲线逼近 曲面细分着色器将复杂的曲面转换为简单的点,线,三角形等.它分为三部分:曲面细分 ...

  6. Shader学习_曲面细分着色器

    曲面细分 详细介绍 当然除了细分与简化之外,还有另外一种同属一类的操作叫做==曲面规则化(Mesh Regularization)==其所作的便是将三角面都变的尽可能相同,从而也达到提升模型效果的目的 ...

  7. 曲面细分着色器学习记录

    曲面细分着色器学习记录 前言: 今天终于走通了Catlike大佬的曲面细分着色器教程,感慨一番,自己的学习之路还很漫长呢 今天的博客内容只是简单的记录一下Catlike大佬的教程 先上个链接:Catl ...

  8. Shader入门---曲面细分着色器和几何着色器

    Shader入门-曲面细分着色器和几何着色器 前记:学不可以停止-------------------------------mx 基础知识: 曲面细分着色器:可以将一个几何体细化为一个球体也能将一根 ...

  9. Unity Shader:细分着色器(Tessellation Shader)在Unity顶点着色器中的写法以及各参数变量解释

    图1:在Unity内将sphere细分后 图2:在Unity内将sphere细分后 Unity官网关于细分着色器的资料比较少,只有在Surface Shader中使用的例子.我看了下Surface S ...

最新文章

  1. Error running ‘xxx-web-order‘: Unable to open debugger port : java.net.Socke
  2. SparkStreaming读取Socket数据
  3. Docker学习笔记之在开发环境中使用服务发现
  4. SQL Sever 各版本下载 SQL Server 2012下载SQL Server 2008下载SQL Server 2005 下载SQL Server 2000 下载...
  5. 【Tools】Windows安装CMake工具
  6. java listener详解_Java监听器Listener使用详解
  7. Python二级笔记(18,19合集知识点篇)
  8. docker 安装 sqlserver 数据库
  9. 水晶报表之创建子报表
  10. 【机器人】机械臂与动捕Nokov的深入了解
  11. 那年,三支玫瑰的花语是我们每人一支
  12. 电脑端QQ无法打开怎么办
  13. 运筹学那些事,专科学生学习运筹学之运输问题,No.5
  14. 知识分享:移动设备的安全管理策略和方法
  15. 登录邮恰显示服务器登录失败,邮洽邮箱收不到邮件是什么原因?
  16. 桌面远程控制计算机名字和,Win7系统远程控制其他计算机桌面教程
  17. oracle替换手机号中间的4位为*号(姓名,手机号,邮箱脱敏)
  18. 基于MATLAB的进制转换
  19. html5公司年终抽奖程序源码按数字随机抽奖
  20. 清除html中多余代码

热门文章

  1. mysql技能培训_MySQL学习(一)
  2. html+css+支付宝页面,CSS仿支付宝菜单,自己亲手写的
  3. ORACLE问题记录-ORA-01078、LRM-00109处理
  4. HTML5学习资料推荐
  5. Task06|连接|joyfulpandas|组队学习 2022.8月组队学习
  6. 主流mes厂商_国内主流MES厂商排名报告-MES系统哪家好?
  7. 案例:5分钟自动跳转页面 监听单击事件 倒计时跳转
  8. 人工智能原理与实践期末知识点总结
  9. 华为ne40e出现php什么意思,NE80E、NE40E、NE5000E的POS卡端口信息告警的含义及定位方法...
  10. 法宝就在顺其自然的道上