Question

I implemented the LoD with diameter from following withpaper NVidia TerrainTessellation WhitePaper. In Chapter "Hull Shader:Tessellation LOD" Page 7 there is a very good explenantion of the LoD with diameter. Here a good quote:

For each patch edge, the shader computes the edge length and then conceptually fits a sphere around it. The sphere is projected into screen space and its screen space diameter is used to compute the tessellation factor for the edge.

Here my 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;
}

Some graphical explenation to the code: First find the Center between the two vertices enter image description here Find orthogonal basis (rectangular to the camera direction) from the camera on the "circle" enter image description here project sUp and sDown in Projection space for calculating the length to calculate the tessellation factor. enter image description here

The Problem

The Tessellation worked fine. But for some testing reason I let the object rotate, so I can see if the tessellation is going with the rotation aswell. Some how I think it is not 100% correct. Look at the Plane, this plane is rotated by (1.0f, 2.0f, 0.0f) and the ligther red is to show higher tessellation factors compared to the darker red. the green color are factors of 1.0. It should be more detailed on the top of the plane, than on the bottom. TessellationFactor with Rotation

What am I missing?

Some test cases

If I remove rotation stuff it looks like this: enter image description here

If I remove rotation and I'm including this simple version of orthogonale base calculation:

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

it looks like this: enter image description here

Could it be a problem, if I'm not using a lookUp Vector? How are you doing LoD? I'm open trying something else...

Was it helpful?

Solution 2

I used the world matrix instead the view matrix. Always use the matrix the camera is using for rotations or other transformations.

OTHER TIPS

What IDE are you using? If you're using Visual Studio, you should try Visual Studio Graphics Debugger or PIX, depending on the version of VS that you have.

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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top