0

我从以下withpaper NVidia TerrainTessellation WhitePaper实现了具有直径的 LoD 。在章节“船体着色器:镶嵌 LOD”第 7 页中,有一个很好的 LoD 与直径的解释。这里有一个很好的报价:

对于每个面片边缘,着色器计算边缘长度,然后在概念上拟合围绕它的球体。球体投影到屏幕空间中,其屏幕空间直径用于计算边缘的镶嵌因子。

这是我的 HullShader:

// Globals
cbuffer TessellationBuffer // buffer need to be aligned to 16!!
{
    float4 cameraPosition;
    float tessellatedTriSize;
    float3 padding;
    matrix worldMatrix;
    matrix projectionMatrix;
};

// Typedefs
struct HullInputType
{
    float4 position : SV_POSITION;
    float2 tex : TEXCOORD0;
    float3 normal : NORMAL;
};

struct ConstantOutputType
{
    float edges[3] : SV_TessFactor;
    float inside : SV_InsideTessFactor;
};

struct HullOutputType
{
    float4 position : SV_POSITION;
    float2 tex : TEXCOORD0;
    float3 normal : NORMAL;
};

// Rounding function
float roundTo2Decimals(float value)
{
    value *= 100;
    value = round(value); 
    value *= 0.01;

    return value;
}

float calculateLOD(float4 patch_zero_pos, float4 patch_one_pos)//1,3,1,1; 3,3,0,1
{
    float diameter = 0.0f;
    float4 radiusPos;
    float4 patchDirection;

    // Calculates the distance between the patches and fits a sphere around.
    diameter = distance(patch_zero_pos, patch_one_pos); // 2.23607
    float radius = diameter/2; // 1.118035
    patchDirection = normalize(patch_one_pos - patch_zero_pos); // 0.894,0,-0.447,0 direction from base edge_zero

    // Calculate the position of the radiusPos (center of sphere) in the world.
    radiusPos = patch_zero_pos + (patchDirection * radius);//2,3,0.5,1
    radiusPos = mul(radiusPos, worldMatrix); 


    // Get the rectangular points of the sphere to the camera.
    float4 camDirection;
    // Direction from camera to the sphere center.
    camDirection = normalize(radiusPos - cameraPosition); // 0.128,0,0.99,0


    // Calculates the orthonormal basis (sUp,sDown) of a vector camDirection.
    // Find the smallest component of camDirection and set it to 0. swap the two remaining
    // components and negate one of them to find sUp_ which can be used to find sDown.
    float4 sUp_;
    float4 sUp;
    float4 sDown;
    float4 sDownAbs;
    sDownAbs = abs(camDirection);//0.128, 0 ,0.99, 0

    if(sDownAbs.y < sDownAbs.x && sDownAbs.y < sDownAbs.z) { //0.99, 0, 0.128
        sUp_.x = -camDirection.z;
        sUp_.y = 0.0f;
        sUp_.z = camDirection.x;
        sUp_.w = camDirection.w;
    } else if(sDownAbs.z < sDownAbs.x && sDownAbs.z < sDownAbs.y){
        sUp_.x = -camDirection.y;
        sUp_.y = camDirection.x;
        sUp_.z = 0.0f;
        sUp_.w = camDirection.w;
    }else{
        sUp_.x = 0.0f;
        sUp_.y = -camDirection.z;
        sUp_.z = camDirection.y;
        sUp_.w = camDirection.w;
    }

    // simple version
//  sUp_.x = -camDirection.y;
//  sUp_.y = camDirection.x;
//  sUp_.z = camDirection.z;
//  sUp_.w = camDirection.w;

    sUp = sUp_ / length(sUp_); // =(0.99, 0, 0.128,0)/0.99824 = 0.991748,0,0.128226,0
    sDown = radiusPos - (sUp * radius); // 0.891191,3,0.356639,1 = (2,3,0.5,1) - (0.991748,0,0.128226,0)*1.118035
    sUp = radiusPos + (sUp * radius); // = (3.10881,3,0.643361,1)

    // Projects sphere in projection space (2d).
    float4 projectionUp = mul(sUp, projectionMatrix);
    float4 projectionDown = mul(sDown, projectionMatrix);

    // Calculate tessellation factor for this edge according to the diameter on the screen.
    float2 sUp_2;
    sUp_2.x = projectionUp.x;
    sUp_2.y = projectionUp.y;
    float2 sDown_2;
    sDown_2.x = projectionDown.x;
    sDown_2.y = projectionDown.y;

    // Distance between the 2 points in 2D
    float projSphereDiam = distance(sUp_2, sDown_2);

    //Debug
    //return tessellatedTriSize;
    //if(projSphereDiam < 2.0f)
    //  return 1.0f;
    //else if(projSphereDiam < 10.0f)
    //  return 2.0f;
    //else
    //  return 10.0f;

    return projSphereDiam*tessellatedTriSize;
}

// Patch Constant Function
// set/calculate any data constant to entire patch.
// is invoked once per patch
// direction vector w = 0 ; position vector w = 1
// receives as input a patch with 3 control points and each control point is represented by the structure of HullInputType
// patch control point should be displaced vertically, this can significantly affect the distance of the camera
// patchId is an identifier number of the patch generated by the  Input Assembler
ConstantOutputType ColorPatchConstantFunction(InputPatch<HullInputType, 3> inputPatch, uint patchId : SV_PrimitiveID)
{    
    ConstantOutputType output;

    ////ret distance(x, y) Returns a distance scalar between two vectors.
    float ret, retinside;
    retinside = 0.0f;

    float4 patch_zero_pos;//1,3,1,1
    patch_zero_pos = float4(inputPatch[0].position.xyz, 1.0f);

    float4 patch_one_pos;//3,3,0,1
    patch_one_pos = float4(inputPatch[1].position.xyz, 1.0f);

    float4 patch_two_pos;
    patch_two_pos = float4(inputPatch[2].position.xyz, 1.0f);

    // calculate LOD by diametersize of the edges
    ret = calculateLOD(patch_zero_pos, patch_one_pos);
    ret = roundTo2Decimals(ret);// rounding
    output.edges[0] = ret;
    retinside += ret;

    ret = calculateLOD(patch_one_pos, patch_two_pos);
    ret = roundTo2Decimals(ret);// rounding
    output.edges[1] = ret;
    retinside += ret;

    ret = calculateLOD(patch_two_pos, patch_zero_pos);
    ret = roundTo2Decimals(ret);// rounding
    output.edges[2] = ret;
    retinside += ret;

    // Set the tessellation factor for tessallating inside the triangle.
    // see image tessellationOuterInner
    retinside *= 0.333;
    // rounding
    retinside = roundTo2Decimals(retinside);

    output.inside = retinside;


    return output;
}

// Hull Shader
// The hull shader is called for each output control point.
// Trivial pass through
[domain("tri")]
[partitioning("fractional_odd")] //fractional_odd
[outputtopology("triangle_cw")]
[outputcontrolpoints(3)]
[patchconstantfunc("ColorPatchConstantFunction")]

HullOutputType ColorHullShader(InputPatch<HullInputType, 3> patch, uint pointId : SV_OutputControlPointID, uint patchId : SV_PrimitiveID)
{
    HullOutputType output;


    // Set the position for this control point as the output position.
    output.position = patch[pointId].position;

    // Set the input color as the output color.
    output.tex = patch[pointId].tex;
    output.normal = patch[pointId].normal;

    return output;
}

代码的一些图形解释:首先找到两个顶点之间的中心 从“圆” 项目上​​的相机sUp和sDown在Projection空间中 在此处输入图像描述 找到正交基(与相机方向成直角), 用于计算长度以计算镶嵌因子。在此处输入图像描述在此处输入图像描述

问题

镶嵌效果很好。但出于某些测试原因,我让对象旋转,因此我可以查看曲面细分是否也随着旋转而进行。有些我认为它不是100%正确的。看看平面,这个平面旋转了(1.0f,2.0f,0.0f),与深红色相比,浅红色显示出更高的镶嵌因子。绿色是 1.0 的因数。平面顶部应该比底部更详细。 带旋转的 TessellationFactor

我错过了什么?

一些测试用例

如果我删除旋转的东西,它看起来像这样: 在此处输入图像描述

如果我删除旋转并且包含这个简单版本的正交基计算:

// simple version
sUp_.x = -camDirection.y;
sUp_.y = camDirection.x;
sUp_.z = camDirection.z;
sUp_.w = camDirection.w;

它看起来像这样: 在此处输入图像描述

如果我不使用查找向量,这可能是个问题吗?你的 LoD 怎么样?我愿意尝试别的东西...

4

2 回答 2

1

你用的是什么IDE?如果您使用的是 Visual Studio,则应尝试使用 Visual Studio 图形调试器或 PIX,具体取决于您拥有的 VS 版本。

http://msdn.microsoft.com/en-us/library/windows/desktop/bb943994(v=vs.85).aspx

于 2013-05-07T23:22:14.860 回答
1

我使用了世界矩阵而不是视图矩阵。始终使用相机用于旋转或其他转换的矩阵。

于 2013-05-24T08:37:34.440 回答