曲面细分——通过Hull Shader和Domain Shader画甜甜圈

457 阅读2分钟

曲面细分画甜甜圈

image.png 给外壳着色器传入中心坐标,方向向量,外径与内径

在外壳着色器中生成一个四面体网格

Hull Shader

#include "Tessellation.hlsli"[domain("quad")]
[partitioning("fractional_even")]
[outputtopology("triangle_cw")]
[outputcontrolpoints(4)]
[patchconstantfunc("QuadConstantHS")]
[maxtessfactor(64.0f)]
float3 HS(
InputPatch<VertexOut, 2> patch,
uint i : SV_OutputControlPointID,
uint patchId : SV_PrimitiveID) : POSITION
{
    float3 center = patch[0].posL.xyz;
    float3 dir = patch[1].posL.xyz;
    float r = patch[0].posL.w;
    float R = patch[1].posL.w;
    float d = R - r;
​
    float PI = 3.14159265;
    float C = 2 * PI*(r+d/2);
    
    if (i == 0)
        return dir;//传入方向 从其方向推导此点的位置
    if (i == 1)
        return float3(C/2, d / 2, 0) + center;
    if (i == 2)
        return float3(-C / 2, -d / 2, 0) + center;
    if (i == 3)
        return float3(C / 2, -d / 2, 0) + center;
​
    
    return float3(0,0,0);
}
​

不太优雅的是为了生成坐标使用了分支,为将方向向量传入,占用第一个顶点通过其它顶点来推测其位置

其实,顶点位置并不影响镶嵌器阶段的顶点镶嵌,因为镶嵌器生成的是相对坐标:三点权重或是UV坐标

Domain Shader

#include "Tessellation.hlsli"[domain("quad")]
float4 DS(QuadPatchTess patchTess,
    float2 uv : SV_DomainLocation,
    const OutputPatch<HullOut, 4> quad) : SV_POSITION
{
    float3 dir = normalize(quad[0].posL);
    
    float3 p1 = quad[1].posL;
    float3 p2 = quad[2].posL;
    float3 p3 = quad[3].posL;
    float3 p0 = float3(p2.x, p1.y, p1.z);
    // 双线性插值
    float3 v1 = lerp(p0, p1, uv.x);
    float3 v2 = lerp(p2, p3, uv.x);
    float3 p = lerp(v1, v2, uv.y);
    
    //中心
    float3 center = (p0 + p1 + p2 + p3) * 0.25f;
    //移动到原点
    p -= center;
    
    float d = length(p0 - p2);
    
    float PI = 3.14159265;
    float C = length(p1 - p0);
    float R = C / (2 * PI);
    
    float theta1 = p.y * 2 * PI / d;
    float rho1 = d / 2;
    p = float3(p.x, rho1 * sin(theta1), rho1 * cos(theta1));
    
    float theta2 = p.x / R;
    float rho2 = R + p.z;
    p = float3(rho2 * cos(theta2), p.y, rho2 * sin(theta2));
​
    
    //旋转
    float phi = acos(dir.y);
    float theta = acos(dir.z);
    
    float3x3 rot2 = float3x3(
        float3(cos(theta), 0, -sin(theta)),
        float3(0, 1, 0),
        float3(sin(theta), 0, cos(theta))
    );
    
    float3x3 rot1 = float3x3(
        float3(1, 0, 0),
        float3(0, cos(phi), sin(phi)),
        float3(0, -sin(phi), cos(phi))
    );
    
    float3x3 rot = mul(rot1, rot2);
    
    p = mul(p, rot);
    //位移
    p+=center;
    
    float4 posH = mul(float4(p, 1.0f), g_WorldViewProj);
    
    return posH;
}
​

构造甜甜圈(圆面环)可以使用参数方程

x = (R+rcosv)cosu\\ y = (R+rcosv)sinu\\ z = rsinv\

\

在此中使用了变换构造甜甜圈(圆面环)本质上是一样的

    //卷变换
    float theta1 = p.y * 2 * PI / d;
    float rho1 = d / 2;
    p = float3(p.x, rho1 * sin(theta1), rho1 * cos(theta1));
    //环变换
    float theta2 = p.x / R;
    float rho2 = R + p.z;
    p = float3(rho2 * cos(theta2), p.y, rho2 * sin(theta2));

image.png

image.png

image.png