PNPatches学习

                                                                                                             PNPatches学习

2013/5/1                    Simon&Lee

理论学习

通常,为了在游戏中更加丰富模型细节,AMD提出了一种Tessellation的网格拆分技术。经过若干年的发展,微软在DirectX11中引入了这一技术。在基于DirectX11的渲染管线中,增加了Hull-Shader Stage、Tessellator Stage和Domain-Shader Stage三个阶段。

Hull-Shader Stage阶段

Hull-ShaderStage是一个可编程管线阶段,通过对输入的控制点进行计算,生成输出控制点和一些常量数据供tessellation阶段和demain shader阶段使用。

Tessellator Stage阶段

TessellatorStage是一个固定管线阶段,对于开发者一般不需要太多关注该阶段。该阶段主要是根据hull shader阶段的输出数据进行网格拆分,并将细分数据输出到domain shader阶段,以便进行后处理。

Domain-Shader Stage阶段

Domain-ShaderStage也是一个可编程管线阶段,主要是根据tessellator阶段的输出数据和hull shader阶段的ConstantData进行计算,将最终的顶点数据传给PixelShader阶段。

实现细节

1.   C++部分代码

(1)Load资源

首先,读取法线纹理和位移纹理作为网格细分资源。

    ID3D11ShaderResourceView*pDisplacementTextureSRV = NULL;

    ID3D11ShaderResourceView*pNormalTextureSRV = NULL;

 

    // Try toload displacement and normal maps from cache

    {

        D3DX11_IMAGE_INFO imageInfo;

        ID3D11Texture2D*pDisplacementTexture = NULL;

        ID3D11Texture2D*pNormalTexture = NULL;

        WCHAR path[1024];

 

        if(DXUTFindDXSDKMediaFileCch(path, MAX_PATH, L"PNPatches/cache/displacement_map.dds") == S_OK)

        {

            if (D3DX11GetImageInfoFromFile(path, NULL,&imageInfo, NULL) == S_OK)

            {

                D3DX11_IMAGE_LOAD_INFOimageLoadInfo;

               memset(&imageLoadInfo, 0, sizeof(imageLoadInfo));

               imageLoadInfo.Width = imageInfo.Width;

               imageLoadInfo.Height = imageInfo.Height;

               imageLoadInfo.MipLevels = 1;

               imageLoadInfo.Format = imageInfo.Format;

               imageLoadInfo.BindFlags = D3D11_BIND_SHADER_RESOURCE;

                V_RETURN(D3DX11CreateTextureFromFile(pDevice, path,&imageLoadInfo, NULL, (ID3D11Resource**)&pDisplacementTexture, NULL));

 

                D3D11_SHADER_RESOURCE_VIEW_DESC descSRV;

               memset(&descSRV, 0, sizeof(descSRV));

               descSRV.Format = imageInfo.Format;

               descSRV.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;

               descSRV.Texture2D.MipLevels = 1;

                pDevice->CreateShaderResourceView(pDisplacementTexture,&descSRV, &pDisplacementTextureSRV);

 

               pDisplacementTexture->Release();

            }

        }

 

        if(DXUTFindDXSDKMediaFileCch(path, MAX_PATH, L"PNPatches/cache/normal_map.dds") == S_OK)

        {

            if (D3DX11GetImageInfoFromFile(path, NULL,&imageInfo, NULL) == S_OK)

            {

                D3DX11_IMAGE_LOAD_INFOimageLoadInfo;

               memset(&imageLoadInfo, 0, sizeof(imageLoadInfo));

               imageLoadInfo.Width = imageInfo.Width;

               imageLoadInfo.Height = imageInfo.Height;

               imageLoadInfo.MipLevels = 1;

               imageLoadInfo.Format = imageInfo.Format;

               imageLoadInfo.BindFlags = D3D11_BIND_SHADER_RESOURCE;

                V_RETURN(D3DX11CreateTextureFromFile(pDevice, path,&imageLoadInfo, NULL, (ID3D11Resource**)&pNormalTexture, NULL));

 

                D3D11_SHADER_RESOURCE_VIEW_DESC descSRV;

               memset(&descSRV, 0, sizeof(descSRV));

               descSRV.Format = imageInfo.Format;

               descSRV.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;

               descSRV.Texture2D.MipLevels = 1;

                pDevice->CreateShaderResourceView(pNormalTexture,&descSRV, &pNormalTextureSRV);

 

               pNormalTexture->Release();

            }

        }

    }

(2)Render函数

然后,提交Shader寄存器数据,并进行渲染。

void MyMesh::RenderAnimated(ID3D11DeviceContext *pd3dDeviceContext, D3DXMATRIX& mView, D3DXMATRIX& mProj, D3DXVECTOR3& cameraPos, float time)

{

    UpdateView(mView, mProj, cameraPos);

 

    ID3DX11EffectPass*pOverridePass = NULL;

 

    ...

 

    // Constants

    D3DXMATRIX mViewProj =mView * mProj;

g_pEffect->GetVariableByName("g_ModelViewProjectionMatrix")->AsMatrix()->SetMatrix(mViewProj);

   g_pEffect->GetVariableByName("g_CameraPosition")->AsVector()->SetFloatVector(cameraPos);

   g_pEffect->GetVariableByName("g_DisplacementScale")->AsScalar()->SetFloat(m_DisplacementScale);

   

    // Buffers& resources

g_pEffect->GetVariableByName("g_PositionsBuffer")->AsShaderResource()->SetResource(g_pMeshVertexBufferSRV);

g_pEffect->GetVariableByName("g_CoordinatesBuffer")->AsShaderResource()->SetResource(g_pMeshCoordinatesBufferSRV);

g_pEffect->GetVariableByName("g_IndicesBuffer")->AsShaderResource()->SetResource(g_pMeshTrgsIndexBufferSRV);

g_pEffect->GetVariableByName("g_NormalsBuffer")->AsShaderResource()->SetResource(g_pMeshNormalsBufferSRV);

   g_pEffect->GetVariableByName("g_TangentsBuffer")->AsShaderResource()->SetResource(g_pMeshTangentsBufferSRV);

g_pEffect->GetVariableByName("g_CornerCoordinatesBuffer")->AsShaderResource()->SetResource(g_pMeshCornerCoordinatesBufferSRV);

    g_pEffect->GetVariableByName("g_EdgeCoordinatesBuffer")->AsShaderResource()->SetResource(g_pMeshTriEdgeCoordinatesBufferSRV);

   

   g_pEffect->GetVariableByName("g_DisplacementTexture")->AsShaderResource()->SetResource(g_pDisplacementMapSRV);

   g_pEffect->GetVariableByName("g_WSNormalMap")->AsShaderResource()->SetResource(g_pNormalMapSRV);

 

    // Animationstuff

    int bonesNum =m_pCharacterAnimation->GetBonesNum();

    D3DXMATRIX* pFrame =m_pCharacterAnimation->GetFrame(time);

   g_pEffect->GetVariableByName("mAnimationMatrixArray")->AsMatrix()->SetMatrixArray((float*)pFrame, 0,bonesNum);

 

   g_pEffect->GetVariableByName("g_BoneIndicesBuffer")->AsShaderResource()->SetResource(m_pBoneIndicesBufferSRV);

   g_pEffect->GetVariableByName("g_BoneWeightsBuffer")->AsShaderResource()->SetResource(m_pBoneWeightsBufferSRV);

 

    if(m_TessellationFactor != 1.0f)

    {

        g_pEffect->GetVariableByName("g_TessellationFactor")->AsScalar()->SetFloat(m_TessellationFactor* g_TessellationFactorResScale);

        pd3dDeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST);

       g_pEffect->GetTechniqueByName("RenderTessellatedTrianglesAnimated")->GetPassByIndex(0)->Apply(0,pd3dDeviceContext);

    }

    else

    {

       g_pEffect->GetVariableByName("g_TessellationFactor")->AsScalar()->SetFloat(1.0f);

        pd3dDeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

       g_pEffect->GetTechniqueByName("RenderTrianglesAnimated")->GetPassByIndex(0)->Apply(0,pd3dDeviceContext);

    }

   

    if(pOverridePass) pOverridePass->Apply(0, pd3dDeviceContext);

   

    pd3dDeviceContext->Draw(g_MeshTrgIndicesNum,0);

 

    ...

}

 

2.   Shader部分代码

(1)Technique和pass设置

 

technique11 RenderTessellatedTrianglesAnimated

{

    pass p0

    {

        SetRasterizerState(CullBackMS);

        SetDepthStencilState(DepthNormal, 0);

        SetBlendState(NoBlending, float4(0.0f, 0.0f,0.0f, 0.0f), 0xFFFFFFFF);

 

        SetVertexShader(CompileShader(vs_4_0, RenderTessellatedDiffuseVS(true)));

        SetHullShader(CompileShader(hs_5_0, DiffuseHS()));

        SetDomainShader(CompileShader(ds_5_0, DiffuseDS()));

        SetGeometryShader(NULL);

        SetPixelShader(CompileShader(ps_4_0, RenderTessellatedDiffusePS()));

    }

}

(2)VertexShader

 

HSIn_Diffuse RenderTessellatedDiffuseVS(uint vertexID : SV_VertexID, uniform bool renderAnimated = false)

{

    HSIn_Diffuse output;

   

    int2 indices = g_IndicesBuffer.Load(vertexID);

 

    output.position = g_PositionsBuffer.Load(indices.x).xyz;

    output.texCoord = g_CoordinatesBuffer.Load(indices.xy);

   

    float4 normalData = g_NormalsBuffer.Load(indices.x);

    output.normal = normalData.xyz;

 

    float4 tangentData = g_TangentsBuffer.Load(indices.x);

    output.tangent = tangentData.xyz;

 

    if (renderAnimated)

    {

        int4 boneIndices = g_BoneIndicesBuffer.Load(indices.x);

        float4 boneWeights = g_BoneWeightsBuffer.Load(indices.x);

 

        float4x4 combinedAnimMatrix = 0;

 

        [unroll]

        for (int iBone=0; iBone<4; ++iBone)

        {

            combinedAnimMatrix += mAnimationMatrixArray[boneIndices[iBone]] * boneWeights[iBone];

        }

 

        float3 animatedPosition = mul(float4(output.position, 1), combinedAnimMatrix).xyz;

        output.position = mul(animatedPosition, MAnimatedScale);

 

        float3x3 rotationMatrix = (float3x3)combinedAnimMatrix;

 

        float3 animatedNormal = mul(float4(output.normal, 1), rotationMatrix).xyz;

        output.normal = normalize(mul(animatedNormal, MAnimatedScale));

 

        float3 animatedTangent = mul(float4(output.tangent, 1), rotationMatrix).xyz;

        output.tangent = normalize(mul(animatedTangent, MAnimatedScale));

    }

 

#ifdef FIX_THE_SEAMS

    output.cornerCoord = g_CornerCoordinatesBuffer.Load(indices.x);

    output.edgeCoord = g_EdgeCoordinatesBuffer.Load(vertexID);

#endif

 

    return output;

}

(3)HullShader

 

struct HS_CONSTANT_DATA_OUTPUT

{

    float Edges[3]  : SV_TessFactor;

    float Inside    : SV_InsideTessFactor;

#ifdef PN_TRIANGLES

    // Geometrycubic generated control points

    float3 f3B210   : POSITION3;

    float3 f3B120   : POSITION4;

    float3 f3B021   : POSITION5;

    float3 f3B012   : POSITION6;

    float3 f3B102   : POSITION7;

    float3 f3B201   : POSITION8;

    float3 f3B111   : CENTER;

#endif

 

    float sign      : SIGN;

};

 

[domain("tri")]

[partitioning("integer")]

[outputtopology("triangle_cw")]

[outputcontrolpoints(3)]

[patchconstantfunc("DiffuseConstantHS")]

[maxtessfactor(64.0)]

HSIn_Diffuse DiffuseHS( InputPatch<HSIn_Diffuse, 3> inputPatch, uint i : SV_OutputControlPointID)

{

    return inputPatch[i];

}

 

HS_CONSTANT_DATA_OUTPUT DiffuseConstantHS( InputPatch<HSIn_Diffuse, 3> inputPatch)

{   

    HS_CONSTANT_DATA_OUTPUT output;

 

    //tessellation factors are proportional to model space edge length

    for (uint ie = 0; ie < 3; ++ie)

    {

#ifdef MESH_CONSTANT_LOD

       output.Edges[ie] = g_TessellationFactor / (float)512 * (float)64;

#else

       float3 edge = inputPatch[(ie + 1) % 3].position - inputPatch[ie].position;

       float3 vec = (inputPatch[(ie + 1) % 3].position + inputPatch[ie].position) / 2 - g_FrustumOrigin;

       float len = sqrt(dot(edge, edge) / dot(vec, vec));

       output.Edges[(ie + 1) % 3] =max(1,len* g_TessellationFactor);

#endif

    }

 

    // culling

    int culled[4];

    for (int ip = 0; ip < 4; ++ip)

    {

        culled[ip] = 1;

        culled[ip] &= dot(inputPatch[0].position - g_FrustumOrigin, g_FrustumNormals[ip].xyz) > 0;

        culled[ip] &= dot(inputPatch[1].position - g_FrustumOrigin, g_FrustumNormals[ip].xyz) > 0;

        culled[ip] &= dot(inputPatch[2].position - g_FrustumOrigin, g_FrustumNormals[ip].xyz) > 0;

    }

    if (culled[0] || culled[1] || culled[2] || culled[3]) output.Edges[0] = 0;

 

#ifdef PN_TRIANGLES

    // computethe cubic geometry control points

    // edgecontrol points

    output.f3B210 = ( ( 2.0f* inputPatch[0].position ) + inputPatch[1].position - ( dot( ( inputPatch[1].position - inputPatch[0].position ), inputPatch[0].normal ) * inputPatch[0].normal ) ) / 3.0f;

    output.f3B120 = ( ( 2.0f* inputPatch[1].position ) + inputPatch[0].position - ( dot( ( inputPatch[0].position - inputPatch[1].position ), inputPatch[1].normal ) * inputPatch[1].normal ) ) / 3.0f;

    output.f3B021 = ( ( 2.0f* inputPatch[1].position ) + inputPatch[2].position - ( dot( ( inputPatch[2].position - inputPatch[1].position ), inputPatch[1].normal ) * inputPatch[1].normal ) ) / 3.0f;

    output.f3B012 = ( ( 2.0f* inputPatch[2].position ) + inputPatch[1].position - ( dot( ( inputPatch[1].position - inputPatch[2].position ), inputPatch[2].normal ) * inputPatch[2].normal ) ) / 3.0f;

    output.f3B102 = ( ( 2.0f* inputPatch[2].position ) + inputPatch[0].position - ( dot( ( inputPatch[0].position - inputPatch[2].position ), inputPatch[2].normal ) * inputPatch[2].normal ) ) / 3.0f;

    output.f3B201 = ( ( 2.0f* inputPatch[0].position ) + inputPatch[2].position - ( dot( ( inputPatch[2].position - inputPatch[0].position ), inputPatch[0].normal ) * inputPatch[0].normal ) ) / 3.0f;

    // centercontrol point

    float3 f3E = ( output.f3B210 + output.f3B120 + output.f3B021 + output.f3B012 + output.f3B102 + output.f3B201 ) / 6.0f;

    float3 f3V = ( inputPatch[0].position + inputPatch[1].position + inputPatch[2].position ) / 3.0f;

    output.f3B111 = f3E + ( ( f3E - f3V ) / 2.0f );

#endif

 

    output.Inside = (output.Edges[0] + output.Edges[1] + output.Edges[2]) / 3;

 

    float2 t01 = inputPatch[1].texCoord - inputPatch[0].texCoord;

    float2 t02 = inputPatch[2].texCoord - inputPatch[0].texCoord;

    output.sign = t01.x * t02.y - t01.y * t02.x > 0.0f ?1 : -1;

 

    return output;

}

 

(4)DomainShader

 

[domain("tri")]

PSIn_TessellatedDiffuse DiffuseDS( HS_CONSTANT_DATA_OUTPUT input,

                      float3 barycentricCoords : SV_DomainLocation,

                      OutputPatch<HSIn_Diffuse, 3> inputPatch )

{

    PSIn_TessellatedDiffuse output;

 

    float3 coordinates = barycentricCoords;

 

    // Thebarycentric coordinates

    float fU = barycentricCoords.x;

    float fV = barycentricCoords.y;

    float fW = barycentricCoords.z;

 

    //Precompute squares and squares * 3

    float fUU = fU * fU;

    float fVV = fV * fV;

    float fWW = fW * fW;

    float fUU3 = fUU * 3.0f;

    float fVV3 = fVV * 3.0f;

    float fWW3 = fWW * 3.0f;

 

    // Computeposition from cubic control points and barycentric coords

    float3 position = inputPatch[0].position * fWW * fW + inputPatch[1].position * fUU * fU + inputPatch[2].position * fVV * fV +

                    input.f3B210 * fWW3 * fU + input.f3B120 * fW * fUU3 + input.f3B201 * fWW3 * fV + input.f3B021 * fUU3 * fV +

                    input.f3B102 * fW * fVV3 + input.f3B012 * fU * fVV3 + input.f3B111 * 6.0f * fW * fU * fV;

 

    // Computenormal from quadratic control points and barycentric coords

    float3 normal = inputPatch[0].normal * coordinates.z + inputPatch[1].normal * coordinates.x + inputPatch[2].normal * coordinates.y;

    normal = normalize(normal);

 

    float2 texCoord = inputPatch[0].texCoord * coordinates.z + inputPatch[1].texCoord * coordinates.x + inputPatch[2].texCoord * coordinates.y;

 

    float2 displacementTexCoord = texCoord;

 

#ifdef FIX_THE_SEAMS

    // Edgepoint

    if(coordinates.z == 0)

        displacementTexCoord = lerp(inputPatch[1].edgeCoord.xy, inputPatch[1].edgeCoord.zw, coordinates.y);

    else if(coordinates.x == 0)

        displacementTexCoord = lerp(inputPatch[2].edgeCoord.xy, inputPatch[2].edgeCoord.zw, coordinates.z);

    else if(coordinates.y == 0)

        displacementTexCoord = lerp(inputPatch[0].edgeCoord.xy, inputPatch[0].edgeCoord.zw, coordinates.x);

 

    // Cornerpoint

    if(coordinates.z == 1)

        displacementTexCoord = inputPatch[0].cornerCoord;

    else if(coordinates.x == 1)

        displacementTexCoord = inputPatch[1].cornerCoord;

    else if(coordinates.y == 1)

        displacementTexCoord = inputPatch[2].cornerCoord;

#endif

 

#ifndef IGNORE_DISPLACEMENT

    float offset = g_DisplacementTexture.SampleLevel(SamplerLinearClamp, displacementTexCoord, 0).x;

    position += normal * offset;

#endif

 

    float3 tangent = inputPatch[0].tangent * coordinates.z + inputPatch[1].tangent * coordinates.x + inputPatch[2].tangent * coordinates.y;

    tangent = normalize(tangent);

 

    output.position = mul(float4(position, 1.0f), g_ModelViewProjectionMatrix);

#ifdef SMOOTH_TCOORDS

    output.texCoord = displacementTexCoord;

#else

    output.texCoord = texCoord;

#endif

    output.positionWS = position;

    output.normal = normal;

    output.tangent = tangent;

   

    output.sign = input.sign;//inputPatch[0].sign;

 

    return output;

}

(5)PixelShader

 

float4 RenderTessellatedDiffusePS(PSIn_TessellatedDiffuse input) : SV_Target

{

#ifdef FLAT_NORMAL

    float3 dir_x = ddx(input.positionWS);

    float3 dir_y = ddy(input.positionWS);

    float3 normal = normalize(cross(dir_x, dir_y));

    float3 lightDir = normalize(g_CameraPosition - input.positionWS);

#else   

    float3 normal = normalize(input.normal);

    float3 tangent = normalize(input.tangent);

    float3 bitangent = cross(normal, tangent) * input.sign;

    float3x3 tangentBasis = float3x3(tangent, bitangent, normal);

   

    float3 lightDir = normalize(g_CameraPosition - input.positionWS);

    lightDir = normalize(mul(tangentBasis, lightDir));

  

    normal = normalize(g_WSNormalMap.Sample(SamplerLinearClamp, input.texCoord).xyz);

#endif

   

    float dotNL = max(dot(normal, lightDir), 0.0f);

    float diffuse = dotNL * 0.75f +0.25f;

    float specular = pow(dotNL, 100.0f);

 

    float3 diffuseColor = float3(1.0, 0.5,0.35) * 0.75;//* 0.75 + (input.sign * 0.5 + 0.5) * 0.25;

 

    float3 color = diffuseColor * diffuse + specular * 0.25;

   

    return float4(color, 0);

}

 

3.   实时渲染截图

上图为初始的模型,下图为根据法线贴图和位移贴图进行细分得到的模型。

 

 

 

 

 

 

 

 

 

 

(1)正常渲染模式

 

 

 

 

 

 

 

 

 

 

 

(2)网格渲染模式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值