环境:windows8.1,vs2013,directx11
曲面细分主要增加三角形的数量,使得表面和边缘更平滑和精细。 多年来,置换纹理映射是高端渲染器独有的功能,如RenderMan,而那时的实时的程序接口,如OpenGL和DirectX,则缺少对这个技术的支持。Direct3D11添加了曲面细分功能技术,使得置换纹理映射的实现变得容易。
原理:
实现这个功能的着色器主要包括外壳着色器、曲面细分阶段和域着色器阶段。
1、外壳着色阶段
外壳着色器阶段主要为每个片元进行曲面细分因子的设置和片元控制点的输入。
如片元为三角形时,需要设置三个边的细分因子和三角形内部的细分因子以及三角形输入的三个控制点。
2、曲面细分
这个阶段是固定渲染阶段,并不需要编程。这个阶段主要根据外壳作色器的曲面细分因子对三角形进行细分,并输出细分后每一点想相对输入片元三个点的相对权重坐标xyz。
3、域着色阶段
这个阶段主要根据外壳着色器的片元控制点信息和曲面细分输出的相对权重坐标信息对曲面细分输出的每一点进行坐标计算以及法向量计算。输入的片元三个顶点也是会输出的(三角形片元的定点,此时的权重为0,0,1或者0,1,0或者1,0,0)。
(其中输出的纹理坐标,我觉得应为相对权重坐标)
例子:
利用VS的立方体模板工程进行修改,实现三角形的曲面细分。
(1)vertex shader
vertex shader不作处理只是简单的重新输出坐标,删除原来的color字段,删除了常量buffer,把常量buffer移动到了
// Per-vertex data used as input to the vertex shader. struct VertexShaderInput { float3 pos : POSITION; }; // Per-pixel color data passed through the pixel shader. struct PixelShaderInput { float4 pos : SV_POSITION; }; // Simple shader to do vertex processing on the GPU. PixelShaderInput main(VertexShaderInput input) { PixelShaderInput output; float4 pos = float4(input.pos, 1.0f); output.pos = pos; return output; }(2)外壳着色器
添加一个外壳着色器。vs基本都添加好了相应的代码,基本不需修改。
struct VS_CONTROL_POINT_OUTPUT { float4 vPosition : WORLDPOS; // TODO: change/add other stuff }; // Output control point struct HS_CONTROL_POINT_OUTPUT { float4 vPosition : WORLDPOS; }; // Output patch constant data. struct HS_CONSTANT_DATA_OUTPUT { float EdgeTessFactor[3] : SV_TessFactor; // e.g. would be [4] for a quad domain float InsideTessFactor : SV_InsideTessFactor; // e.g. would be Inside[2] for a quad domain // TODO: change/add other stuff }; #define NUM_CONTROL_POINTS 3 // Patch Constant Function HS_CONSTANT_DATA_OUTPUT CalcHSPatchConstants( InputPatch<VS_CONTROL_POINT_OUTPUT, NUM_CONTROL_POINTS> ip, uint PatchID : SV_PrimitiveID) { HS_CONSTANT_DATA_OUTPUT Output; // Insert code to compute Output here Output.EdgeTessFactor[0] = Output.EdgeTessFactor[1] = Output.EdgeTessFactor[2] = 15; // e.g. could calculate dynamic tessellation factors instead Output.InsideTessFactor = 15; return Output; } [domain("tri")] [partitioning("fractional_odd")] [outputtopology("triangle_cw")] [outputcontrolpoints(3)] [patchconstantfunc("CalcHSPatchConstants")] HS_CONTROL_POINT_OUTPUT main( InputPatch<VS_CONTROL_POINT_OUTPUT, NUM_CONTROL_POINTS> ip, uint i : SV_OutputControlPointID, uint PatchID : SV_PrimitiveID ) { HS_CONTROL_POINT_OUTPUT Output; // Insert code to compute Output here Output.vPosition = ip[i].vPosition; return Output; }
添加一个域着色器,vs生成的内容基本都有。域着色器需要进行坐标变换,进行了一些修改。
// A constant buffer that stores the three basic column-major matrices for composing geometry. cbuffer ModelViewProjectionConstantBuffer : register(b0) { matrix model; matrix view; matrix projection; }; struct DS_OUTPUT { float4 vPosition : SV_POSITION; // TODO: change/add other stuff }; // Output control point struct HS_CONTROL_POINT_OUTPUT { float4 vPosition : WORLDPOS; }; // Output patch constant data. struct HS_CONSTANT_DATA_OUTPUT { float EdgeTessFactor[3] : SV_TessFactor; // e.g. would be [4] for a quad domain float InsideTessFactor : SV_InsideTessFactor; // e.g. would be Inside[2] for a quad domain // TODO: change/add other stuff }; #define NUM_CONTROL_POINTS 3 [domain("tri")] DS_OUTPUT main( HS_CONSTANT_DATA_OUTPUT input, float3 domain : SV_DomainLocation, const OutputPatch<HS_CONTROL_POINT_OUTPUT, NUM_CONTROL_POINTS> patch) { DS_OUTPUT Output; Output.vPosition = patch[0].vPosition*domain.x+patch[1].vPosition*domain.y+patch[2].vPosition*domain.z; Output.vPosition.w = 1; float4 pos = Output.vPosition; // Transform the vertex position into projected space. pos = mul(pos, model); pos = mul(pos, view); pos = mul(pos, projection); Output.vPosition = pos; return Output; }
(4)像素着色器
// Per-pixel color data passed through the pixel shader. struct PixelShaderInput { float4 pos : SV_POSITION; }; // A pass-through function for the (interpolated) color data. float4 main(PixelShaderInput input) : SV_TARGET { return float4(1.0f,0.0f,0.0f, 1.0f); }
(5)代码部分
a、需要修改相应的结构体(shaderstructs.h中)
struct VertexPositionColor { DirectX::XMFLOAT3 pos; };
auto loadHSTask = DX::ReadDataAsync(L"SimpleHullShader.cso"); auto loadDSTask = DX::ReadDataAsync(L"SimpleDomainShader.cso"); auto createHSTask = loadHSTask.then([this](const std::vector<byte>& fileData) { DX::ThrowIfFailed( m_deviceResources->GetD3DDevice()->CreateHullShader( &fileData[0], fileData.size(), nullptr, &m_hullShader ) ); }); auto createDSTask = loadDSTask.then([this](const std::vector<byte>& fileData) { DX::ThrowIfFailed( m_deviceResources->GetD3DDevice()->CreateDomainShader( &fileData[0], fileData.size(), nullptr, &m_domainShader ) ); });
<pre name="code" class="cpp">c、修改顶点描述和数据(CreateDeviceDependentResources()函数中)
static const D3D11_INPUT_ELEMENT_DESC vertexDesc [] = { { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, }; static const VertexPositionColor cubeVertices[] = { { XMFLOAT3(10.0f, 0.0f, 0.0f) }, { XMFLOAT3(0.0f, 10.0f, 0.0f)}, { XMFLOAT3(-10.0f, 0.0f, 0.0f) }, }; static const unsigned short cubeIndices [] = { 0,2,1, // -x };d、修改观察矩阵(CreateWindowSizeDependentResources()函数中)
static const XMVECTORF32 eye = { 0.0f, 0.0f, 30.0f, 0.0f }; static const XMVECTORF32 at = { 0.0f, -0.1f, 0.0f, 0.0f }; static const XMVECTORF32 up = { 0.0f, 1.0f, 0.0f, 0.0f }; XMStoreFloat4x4(&m_constantBufferData.view, XMMatrixTranspose(XMMatrixLookAtRH(eye, at, up)));
主要不想让它和立方体一样在那边旋转
XMStoreFloat4x4(&m_constantBufferData.model, XMMatrixTranspose(XMMatrixRotationY(0)));
void Sample3DSceneRenderer::SetFillMode(D3D11_FILL_MODE mode) { auto context = m_deviceResources->GetD3DDeviceContext(); auto device = m_deviceResources->GetD3DDevice(); D3D11_RASTERIZER_DESC rasterDesc; rasterDesc.AntialiasedLineEnable = false; rasterDesc.CullMode = D3D11_CULL_NONE; rasterDesc.DepthBias = 0; rasterDesc.DepthBiasClamp = 0.0f; rasterDesc.DepthClipEnable = true; rasterDesc.FillMode = mode; //实体模式D3D11_FILL_SOLID,线框模式D3D11_FILL_WIREFRAME rasterDesc.FrontCounterClockwise = false; rasterDesc.MultisampleEnable = false; rasterDesc.ScissorEnable = false; rasterDesc.SlopeScaledDepthBias = 0.0f; // 创建光栅化状态 DX::ThrowIfFailed( device->CreateRasterizerState( &rasterDesc, &m_rasterState ) ); //设置光栅化状态,使其生效 context->RSSetState(m_rasterState.Get()); }
context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST);要设置D3D11_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST方式。还有D3D11_PRIMITIVE_TOPOLOGY_32_CONTROL_POINT_PATCHLIST的方式,主要我们输入的三角形是三个顶点,所以采用D3D11_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST。
auto context = m_deviceResources->GetD3DDeviceContext(); // Prepare the constant buffer to send it to the graphics device. context->UpdateSubresource( m_constantBuffer.Get(), 0, NULL, &m_constantBufferData, 0, 0 ); SetFillMode(D3D11_FILL_WIREFRAME); // Each vertex is one instance of the VertexPositionColor struct. UINT stride = sizeof(VertexPositionColor); UINT offset = 0; context->IASetVertexBuffers( 0, 1, m_vertexBuffer.GetAddressOf(), &stride, &offset ); context->IASetIndexBuffer( m_indexBuffer.Get(), DXGI_FORMAT_R16_UINT, // Each index is one 16-bit unsigned integer (short). 0 ); context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST); context->IASetInputLayout(m_inputLayout.Get()); // Attach our vertex shader. context->VSSetShader( m_vertexShader.Get(), nullptr, 0 ); context->GSSetShader( nullptr, nullptr, 0 ); context->HSSetShader( m_hullShader.Get(), nullptr, 0 ); context->DSSetShader( m_domainShader.Get(), nullptr, 0 ); context->DSSetConstantBuffers( 0, 1, m_constantBuffer.GetAddressOf() ); // Attach our pixel shader. context->PSSetShader( m_pixelShader.Get(), nullptr, 0 );
Microsoft::WRL::ComPtr<ID3D11DomainShader> m_domainShader; Microsoft::WRL::ComPtr<ID3D11HullShader> m_hullShader; Microsoft::WRL::ComPtr<ID3D11RasterizerState> m_rasterState; //渲染状态 void SetFillMode(D3D11_FILL_MODE mode);4、效果图
示例程序下载地址点击打开链接