Unity URP 曲面细分学习笔记


学百人时遇到了曲面着色器的内容,有点糊里糊涂,于是上知乎找到了两篇大佬的文章 Unity URP 曲面细分Unity曲面细分笔记,本文只是自己做学习记录使用

1.曲面细分与镶嵌

曲面细分或细分曲面(Subdivision surface)是指一种通过递归算法将一个粗糙的几何网格细化的技术。镶嵌(Tessellation)则是实现曲面细分的具体手段,它能将场景中的几何物体顶点集划分为合适的渲染结构。
曲面细分分为三个阶段:外壳着色器(Hull Shader)、镶嵌器(Tessellator)、域着色器(Domain Shader)。

1.1 外壳着色器 Hull Shader

Hull shader实际上是两个阶段(Phase)组成:常量外壳着色器(Constant Hull Shader)和 控制点外壳着色器(Control point hull shader),两个阶段并行运行。

  • Constant Hull Shader 会对每一个面片进行处理,主要任务是输出网格的曲面细分因子(Tessellation Factor),曲面细分因子用于指导面片细分数。
    • 假如要传入的面片是三角形,那么对于三角形就会有三个曲面细分因子,所以edgeFactor[3],对于内部曲面细分因子,因为三角形是最小的图元所以内部是一个因子(个人理解)insideFactor,如果是矩形的话就是四个边因子和两个内部因子
    • 常量外壳着色器会以面片的所有顶点(或控制点)为输入,所以有三个顶点InputPatch<VertexOut, 3> patch,数字为3
    • SV_PrimitiveID 提供传入面片的ID值,可以用来区分不同的Patch,这样你就可以根据Patch的ID来为每个Patch设置不同的细分因子,或者执行其他依赖于Patch ID的操作。它对于每个图元都是不同的
struct PatchTess{
   
	float edgeFactor[3];
	float insideFactor;
};

PatchTess PatchConstant(InputPatch<VertexOut, 3> patch, uint patchID : SV_PrimitiveID)
{
   
	PatchTess o;
	o.edgeFactor[0] = 4;
    o.edgeFactor[1] = 4; 
    o.edgeFactor[2] = 4;
    o.insideFactor  = 4;
    return o;
}
  • Control Point Hull Shader 用来改变每个输出顶点的位置信息,例如将一个三角形变为三次贝塞尔三角面片
    • domain:面片类型,参数有三角面tri、四角面片quad、等值线isoline
    • partitioning:曲面细分方式,参数有integer、fractional_even、fractional_odd
      • integer,指新顶点的增加指取决于细分的整数部分(等分),图形可能会出现图片
      • fractional_even,向上取最近的偶数n,将整段切割为n-2个相等长度的部分,和两端较短的部分
      • fractional_odd,向上取最近的奇数n,将整段切割为n-2个相等长度的部分,和两端较短的部分
    • outputtopology:细分创建的三角形面片的绕序,参数有顺时针triangle_cw、逆时针triangle_ccw
    • patchconstantfunc:常量外壳着色器的函数名
    • outputcontrolpoints:外壳着色器的执行次数,即生成的控制点个数
    • maxtessfactor:程序会使用到最大的细分因子
    • SV_OutputControlPointID:当前正在操作的控制点索引ID
[domain("tri")]    
[partitioning("integer")]    
[outputtopology("triangle_cw")]   
[patchconstantfunc("PatchConstant")]   
[outputcontrolpoints(3)]             
[maxtessfactor(64.0f)]        
HullOut ControlPoint (InputPatch<VertexOut,3> patch,uint id : SV_OutputControlPointID){
     
    HullOut o;
    o.positionOS = patch[id].positionOS;
    o.texcoord = patch[id].texcoord; 
    return o;
}

常量外壳着色器对每个片元执行一次,输出细分因子等信息;控制点外壳着色器对每个控制点执行一次,并输出对应或衍生的控制点。两个阶段并行运行。

1.2 镶嵌器阶段 Tessellator

这一阶段我们无法对其做出任何控制,全程由硬件控制。在这一阶段,硬件根据之前曲面细分因子对面片做出细分操作。

1.3 域着色器阶段 Domain Shader

就和普通的顶点着色器要做的差不多,我们需要计算每一个控制点的顶点位置等信息。

  • 功能
    • 生成细分顶点,这些顶点是由外壳着色器确定的细分因子和细分模式所生成的
    • 插值属性,根据控制点的属性进行插值,得到细分顶点的属性
    • 输出顶点,域着色器生成最终的顶点数据,并传递给后面的着色器
      在这里插入图片描述
struct HullOut{
   
    float3 positionOS : INTERNALTESSPOS; 
    float2 texcoord : TEXCOORD0;
};

struct DomainOut
{
   
    float4  positionCS      : SV_POSITION;
    float2  texcoord        : TEXCOORD0; 
};

[domain("tri")]      
DomainOut FlatTessDomain (PatchTess tessFactors, const OutputPatch<HullOut,3> patch, float3 bary : SV_DOMAINLOCATION)
{
     
    float3 positionOS = patch[0].positionOS * bary.x + 
    	patch[1].positionOS * bary.y + 
    	patch[2].positionOS * bary.z; 
    float2 texcoord   = patch[0].texcoord * bary.x + 
    	patch[1].texcoord * bary.y + 
    	patch[2].texcoord * bary.z;

    DomainOut output;
    output.positionCS = TransformObjectToHClip(positionOS);
    output.texcoord = texcoord;
    return output; 
}
  • 域着色器输入:接受一个域(Domain)输入,代表了细分后顶点在原始补丁内的位置,这个位置通常用参数空间坐标表示,例如重心坐标(Barycentric Coordinates)(这里的重心坐标,是由硬件在细分过程的一个中间阶段 Tessellator 自动计算得出的)
  • 属性插值:域着色器使用这些参数空间坐标来插值原始控制点的属性。
  • 顶点输出:域着色器输出一个顶点,这个顶点包含计算后的位置和其他属性(如颜色、纹理坐标、法线等)

2.具体实现

2.1 不同的细分策略

2.1.1 平面镶嵌 Flat Tessellation

平面镶嵌只是线性插值位置信息,细分后的图案只比之前多了一些三角面片,单独使用并不能平滑模型。通常和置换贴图配合使用,创建凹凸不平的平面。

Shader "Tessellation/Flat Tessellation"
{
   
    Properties
    {
   
        [NoScaleOffset]_BaseMap ("Base Map", 2D) = "white" {
   }  

        [Header(Tess)][Space]

        [KeywordEnum(integer, fractional_even, fractional_odd)]_Partitioning ("Partitioning Mode", Float) = 0
        [KeywordEnum(triangle_cw, triangle_ccw)]_Outputtopology ("Outputtopology Mode", Float) = 0
        _EdgeFactor ("EdgeFactor", Range(1,8)) = 4 
        _InsideFactor ("InsideFactor", Range(1,8)) = 4 
    }
    SubShader
    {
   
        Tags {
   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值