先看看本节教程的结构吧
一,曲面细分的介绍。
这里先链接一篇关于曲面细分的文章
http://blog.youkuaiyun.com/qq_29523119/article/details/52914370,算是对曲面细分大概的科普吧.
这里看此放出D3D11的3D渲染管线图:
看红色画圈部分的都属于“Tessellation(曲面细分阶段)”,分别为:HullShader(HS) Stage,TessellatorStage,DomainShader(DS)Stage,
D3D11程序普通情况下渲染管线的流程是:
加入曲面细分后,渲染流程变为:
放出Shader代码:
ColorShader.fx
/*-------------常量缓存---------------------*/
cbuffer CBMatrix:register(b0)
{
matrix World;
matrix View;
matrix Proj;
};
cbuffer CBTessellation:register(b1)
{
float TessellationAmount;
float3 pad;
};
/*--------------各个阶段输入输出的结构体--------------------*/
//从IA阶段输出的
struct VertexIn
{
float3 Pos:POSITION;
float4 color:COLOR;
};
//从VertexShader输出的
struct HullIn
{
float3 Pos:POSITION;
float4 color:COLOR;
};
//从HullShader输出的
struct DomainIn
{
float3 Pos:POSITION;
float4 color:COLOR;
};
//从DomainShader输出的
struct PixelIn
{
float4 Pos:SV_POSITION;
float4 color:COLOR;
};
/*--------------各个Shader阶段的函数--------------------*/
/*VertexShader*/
HullIn VS(VertexIn ina)
{
HullIn outa;
outa.Pos = ina.Pos;
outa.color = ina.color;
return outa;
}
/*HullShader */
//HullShader用到的从补丁常量函数输出的值 三角形
struct ConstantOutputType
{
float edges[3] : SV_TessFactor;
float inside : SV_InsideTessFactor;
};
//HullShader补丁常量缓存函数
ConstantOutputType ColorPathConstantFunction(InputPatch<HullIn, 3> inputPatch, uint patchId : SV_PrimitiveID)
{
ConstantOutputType outa;
//设置三角形三条边的曲面细分因子
outa.edges[0] = TessellationAmount;
outa.edges[1] = TessellationAmount;
outa.edges[2] = TessellationAmount;
//设置三角形里面的曲面细分因子
outa.inside = TessellationAmount;
return outa;
}
[domain("tri")] //三角形
[partitioning("integer")] //整数
[outputtopology("triangle_cw")] //顺时针
[outputcontrolpoints(3)] //三个控制点
[patchconstantfunc("ColorPathConstantFunction")] // 补丁常量缓存函数名
DomainIn HS(InputPatch<HullIn, 3> patch, uint pointId : SV_OutputControlPointID, uint patchId : SV_PrimitiveID)
{
DomainIn outa;
//设置HullShader输出的顶点坐标
outa.Pos = patch[pointId].Pos;
//设置HullShader输出的颜色
outa.color = patch[pointId].color;
return outa;
}
/*DomainShader*/
[domain("tri")]
PixelIn DS(ConstantOutputType input, float3 uvwCoord : SV_DomainLocation, const OutputPatch<DomainIn, 3> patch)
{
PixelIn outa;
float3 vertexPosition;
//获取新顶点的位置
vertexPosition = uvwCoord.x * patch[0].Pos + uvwCoord.y * patch[1].Pos + uvwCoord.z * patch[2].Pos;
//将坐标变到齐次裁剪空间
outa.Pos = mul(float4(vertexPosition, 1.0f), World);
outa.Pos = mul(outa.Pos, View);
outa.Pos = mul(outa.Pos, Proj);
outa.color = patch[0].color;
return outa;
}
/*PixelShader*/
float4 PS(PixelIn outa) : SV_Target
{
return outa.color;
}
说说上面HullShader和DomainShader关键字
(1) domain:补丁类型(the patch type),实参可以是tri,quad,或者isoline
(2)partitioning:指定曲面细分的细分模式,实参可以是integer,fractional_even,fractional_odd
(3)outputtopology:通过曲面细分新创建的三角形的绕线方向
(a) triangle_cw:顺时针绕线方向
(b)triangle_ccw:逆时针绕线方向
(c)line: 线性曲面细分
(4) outputcontrolpoints:HullShader(外壳着色器)执行的次数,每次输出一个控制点.系统值SV_OutputControlPointID给予了一个下标值,这个下标值标识了HullShader(外壳着色器)正在处理的输出控制点(0utputControlpoint)
(5)patchconstantfunc(补丁常量函数):HullShader常量函数的函数名字,为一个字符串
(6)maxtessfactor:驱动的控制线索指定了你的着色器可以使用的最大的曲面细分因子。如果知道这个值的上限,可以通过硬件进行潜在的优化,因此这个因子指明了曲面细分需要使用多少资源。被D3D11硬件支持最大的曲面细分因子为64.
三角形补丁曲面细分的例子(Triangle Patch Tessellation examples):
四边形补丁曲面细分的例子(Quad Patch Tessellation examples):
创建VertexShader,PixelShader,HullShader,DomainShader
bool ColorShaderClass::InitializeShader(ID3D11Device* d3dDevice, HWND hwnd, WCHAR* VSFileName, WCHAR* PSFileName, WCHAR* HSFileName, WCHAR* DSFileName)
{
HRESULT result;
ID3D10Blob* errorMessage;
ID3D10Blob* VertexShaderBuffer;
ID3D10Blob* PixelShaderBuffer;
ID3D10Blob* HullShaderBuffer;
ID3D10Blob* DomainShaderBuffer;
//第一,初始化参数
errorMessage = NULL;
VertexShaderBuffer=NULL;
PixelShaderBuffer=NULL;
//第二,编译VertexShader代码,并创建VertexShader
result = D3DX11CompileFromFile(VSFileName, NULL, NULL, "VS", "vs_5_0", D3DCOMPILE_ENABLE_STRICTNESS, 0, NULL, &VertexShaderBuffer, &errorMessage, NULL);
if (FAILED(result))
{
//存在错误信息
if (errorMessage)
{
OutputShaderErrorMessage(errorMessage, hwnd, VSFileName);
}
//不存在错误信息,也就是没有找到Shader文件
else
{
MessageBox(hwnd, L"can not find VS file", L"error", MB_OK);
}
}
HR(d3dDevice->CreateVertexShader(VertexShaderBuffer->GetBufferPointer(),VertexShaderBuffer->GetBufferSize(),NULL,&md3dVertexShader));
//第三,编译HullShader,并创建HullShader
result = D3DX11CompileFromFile(HSFileName, NULL, NULL, "HS", "hs_5_0", D3DCOMPILE_ENABLE_STRICTNESS, 0, NULL, &HullShaderBuffer, &errorMessage, NULL);
if (FAILED(result))
{
//存在错误信息
if (errorMessage)
{
OutputShaderErrorMessage(errorMessage, hwnd, HSFileName);
}
//不存在错误信息,也就是没有找到Shader文件
else
{
MessageBox(hwnd, L"can not find HS file", L"error", MB_OK);
}
}
HR(d3dDevice->CreateHullShader(HullShaderBuffer->GetBufferPointer(), HullShaderBuffer->GetBufferSize(), NULL, &md3dHullShader));
//第四,编译HullShader,并创建DomainShader
result = D3DX11CompileFromFile(DSFileName, NULL, NULL, "DS", "ds_5_0", D3DCOMPILE_ENABLE_STRICTNESS, 0, NULL, &DomainShaderBuffer, &errorMessage, NULL);
if (FAILED(result))
{
//存在错误信息
if (errorMessage)
{
OutputShaderErrorMessage(errorMessage, hwnd, DSFileName);
}
//不存在错误信息,也就是没有找到Shader文件
else
{
MessageBox(hwnd, L"can not find DS file", L"error", MB_OK);
}
}
HR(d3dDevice->CreateDomainShader(DomainShaderBuffer->GetBufferPointer(), DomainShaderBuffer->GetBufferSize(), NULL, &md3dDomainShader));
//第五,编译PixelShader,并创建PixelShader
result = D3DX11CompileFromFile(PSFileName, NULL, NULL, "PS", "ps_5_0", D3DCOMPILE_ENABLE_STRICTNESS, 0, NULL, &PixelShaderBuffer, &errorMessage, NULL);
if (FAILED(result))
{
//存在错误信息
if (errorMessage)
{
OutputShaderErrorMessage(errorMessage, hwnd, PSFileName);
}
//不存在错误信息,也就是没有找到Shader文件
else
{
MessageBox(hwnd, L"can not find PS file", L"error", MB_OK);
}
}
HR(d3dDevice->CreatePixelShader(PixelShaderBuffer->GetBufferPointer(), PixelShaderBuffer->GetBufferSize(), NULL, &md3dPixelShader))
//第六,填充输入布局形容结构体,创建输入布局
D3D11_INPUT_ELEMENT_DESC VertexInputLayout[] =
{
{ "POSITION",0,DXGI_FORMAT_R32G32B32_FLOAT,0,0,D3D11_INPUT_PER_VERTEX_DATA,0 }, // 96位即12个字节
{ "COLOR",0,DXGI_FORMAT_R32G32B32A32_FLOAT,0,12,D3D11_INPUT_PER_VERTEX_DATA,0 },
};
unsigned int numElements = sizeof(VertexInputLayout) / sizeof(VertexInputLayout[0]); //布局数量
HR(d3dDevice->CreateInputLayout(VertexInputLayout, numElements, VertexShaderBuffer->GetBufferPointer(), VertexShaderBuffer->GetBufferSize(), &md3dInputLayout));
//第七,释放VertexShaderBuffer和PixelShaderBuffer
VertexShaderBuffer->Release();
VertexShaderBuffer = NULL;
PixelShaderBuffer->Release();
PixelShaderBuffer = NULL;
//第八,设置矩阵(常量)缓存形容结构体,并创建矩阵常量缓存
D3D11_BUFFER_DESC matrixBufferDesc;
ZeroMemory(&matrixBufferDesc, sizeof(matrixBufferDesc));
matrixBufferDesc.Usage = D3D11_USAGE_DEFAULT;
matrixBufferDesc.ByteWidth = sizeof(CBMatrix); //结构体大小,必须为16字节倍数
matrixBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
matrixBufferDesc.CPUAccessFlags = 0;
HR(d3dDevice->CreateBuffer(&matrixBufferDesc, NULL, &mCBMatrixBuffer));
//第九,设置曲面细分(常量)缓存形容结构体,并创建曲面细分常量缓存
D3D11_BUFFER_DESC TesselationBufferDesc;
ZeroMemory(&TesselationBufferDesc, sizeof(TesselationBufferDesc));
TesselationBufferDesc.Usage = D3D11_USAGE_DEFAULT;
TesselationBufferDesc.ByteWidth = sizeof(CBTessellation); //结构体大小,必须为16字节倍数
TesselationBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
TesselationBufferDesc.CPUAccessFlags = 0;
HR(d3dDevice->CreateBuffer(&TesselationBufferDesc, NULL, &mCBTessellationBuffer));
return true;
}
设定
VertexShader,PixelShader,HullShader,DomainShader
void ColorShaderClass::RenderShader(ID3D11DeviceContext* deviceContext, int indexCount)
{
//设置顶点输入布局
deviceContext->IASetInputLayout(md3dInputLayout);
//设置VertexShader,PixelShader,hullShader,DomainShader
deviceContext->VSSetShader(md3dVertexShader, NULL, 0);
deviceContext->PSSetShader(md3dPixelShader, NULL, 0);
deviceContext->HSSetShader(md3dHullShader, NULL, 0);
deviceContext->DSSetShader(md3dDomainShader, NULL, 0);
//渲染三角形
deviceContext->DrawIndexed(indexCount, 0, 0);
}
这里我的EdgeTess和InsideTess都是4
程序运行图:
下面放出我的源代码链接: