[DirectX12学习笔记] 曲面细分

  • 注意!本文是在下几年前入门期间所写(young and naive),其中许多表述可能不正确,为防止误导,请各位读者仔细鉴别。

硬件曲面细分


曲面细分阶段

曲面细分包含三个阶段,夹在VS和GS之间,如图

在这里插入图片描述

如果要用硬件曲面细分,那么顶点着色器输出就不再是顶点,而是control point的patch,一个patch包含多个control point,VS输出patch给HS用,这就要一种新的拓扑类型,如下

D3D_PRIMITIVE_TOPOLOGY_1_CONTROL_POINT_PATCHLIST = 33,
D3D_PRIMITIVE_TOPOLOGY_2_CONTROL_POINT_PATCHLIST = 34,
D3D_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST = 35,
D3D_PRIMITIVE_TOPOLOGY_4_CONTROL_POINT_PATCHLIST = 36,
.
.
.
D3D_PRIMITIVE_TOPOLOGY_31_CONTROL_POINT_PATCHLIST = 63,
D3D_PRIMITIVE_TOPOLOGY_32_CONTROL_POINT_PATCHLIST = 64,

此外,如果要传primitive type到ID3D12GraphicsCommandList::IASetPrimitiveTopology,那么PSO也必须设置D3D12_GRAPHICS_PIPELINE_STATE_DESC::PrimitiveTopologyType为D3D12_PRIMITIVE_TOPOLOGY_TYPE_PATCH。

Hull Shader
Hull Shader分Constant Hull Shader和Control Point Hull Shader。下面先介绍Constant Hull Shader,一个例子如下

struct PatchTess
{
   
float EdgeTess[4] : SV_TessFactor;
float InsideTess[2] : SV_InsideTessFactor;
// Additional info you want associated per patch.
};
PatchTess ConstantHS(InputPatch<VertexOut, 4> patch,
uint patchID : SV_PrimitiveID)
{
   
PatchTess pt;
// Uniformly tessellate the patch 3 times.
pt.EdgeTess[0] = 3; // Left edge
pt.EdgeTess[1] = 3; // Top edge
pt.EdgeTess[2] = 3; // Right edge
pt.EdgeTess[3] = 3; // Bottom edge
pt.InsideTess[0] = 3; // u-axis (columns)
pt.InsideTess[1] = 3; // v-axis (rows)
return pt;
}

首先要输出的有两个系统值,SV_TessFactor和SV_InsideTessFactor,前者是边的细分等级,后者是面片中心的细分等级,前者有几条边就要输出几个,后者要输出两个,分别对应的是u和v方向。
然后输入ConstantHS的InputPatch是一个模板类,对应的是顶点着色器的输出,如果这里要用曲面细分的话,就不能在VS中乘以世界和摄像机投影矩阵,而是要留到细分结束后再变换(如果有几何着色器的话就留到几何着色器阶段再变换)。然后这里可以用SV_PrimitiveID获取Primitive的id。
这里可以把细分等级和离摄像机的远近、屏幕覆盖范围、朝向、粗糙度等挂钩,而不是写一个定的值,可以起到优化的效果。

接下来介绍Control Point Hull Shader,这个部分是每个control point都调用一次,因此和顶点着色器类似,只不过对象是control point,在这个阶段我们可以改变曲面的表达形式,比如把输入的三角面(三个control point)变成由包含十个control point的patch控制的贝塞尔曲线输出,等等。
一个例子如下

struct HullOut
{
   
	float3 PosL : POSITION;
};
[domain(“quad”)]
[partitioning(“integer”)]
[outputtopology(“triangle_cw”)]
[outputcontrolpoints(4)]
[patchconstantfunc(“ConstantHS”)]
[maxtessfactor(64.0f)]
HullOut HS(InputPatch<VertexOut, 4> p,
uint i : SV_OutputControlPointID,
uint patchId : SV_PrimitiveID)
{
   
	HullOut hout;
	hout.PosL = p[i].PosL;
	return hout;
}

Control Point HS要定义不少属性,其中domain是patch type,可选的有tri,quad或者isoline。
partitioining是细分模式,integer的细分等级会突变,而fractional_odd或者fractional_even的细分等级会渐变,顶点会逐渐移动直到消失,而不会突然pop出来或者消失。
outputtopology输出的三角面的winding order,有triangle_cw,triangle_ccw,line这三个选项
outputcontrolpoints是输出顶点的数量,也就是hs的执行次数,可以用SV_OutputControlPointID获取当前Control Point的ID。
patchconstantfunc则是constantHS的名字
maxtessfactor是最大细分数量,dx11最高支持到64,这里可以手动设置得更低。

The Tessellation Stage
HullShader结束之后就是细分阶段,这部分由硬件完成,效果如下
在这里插入图片描述
在这里插入图片描述

Domain Shader
域着色器的输入是细分好了的曲面,但是不会是直接给定位置,而是只给uv,我们要做的是根据uv来算出顶点的位置,然后如果没有几何着色器的话,我们需要在这个阶段把顶点变换到屏幕坐标里。
这里说的给定uv是针对四边面,如果用的是三个control point的patch,那么这里给定的是质心坐标系下的uvw。

Quad曲面细分Demo

下面我们实现一个能根据离摄像机距离细分一个四边面的demo,并列出其中关键部分的代码:

编译shader

void BasicTessellationApp::BuildShadersAndInputLayout()
{
   
	mShaders["tessVS"] = d3dUtil::CompileShader(L"Shaders\\Tessellation.hlsl", nullptr, "VS", "vs_5_0");
	mShaders["tessHS"] = d3dUtil::CompileShader(L"Shaders\\Tessellation.hlsl", nullptr, "HS", "hs_5_0");
	mShaders["tessDS"] = d3dUtil::CompileShader(L"Shaders\\Tessellation.hlsl", nullptr, "DS", "ds_5_0");
	mShaders["tessPS"] = d3dUtil::CompileShader(L"Shaders\\Tessellation.hlsl", nullptr, "PS", "ps_5_0");
	
    mInputLayout =
    {
   
        {
    "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }
    };

定义一个quad

void BasicTessellationApp::BuildQuadPatchGeometry()
{
   
    std::array<XMFLOAT3,4> vertices =
	{
   
		XMFLOAT3(-10.0f, 0.0f, +10.0f),
		XMFLOAT3(+10.0f, 0.0f, +10.0f),
		XMFLOAT3(-10.0f, 0.0f, -10.0f),
		XMFLOAT3(+10.0f, 0.0f, -10.0f)
	};

	std::array<std::int16_t, 4> indices = {
    0, 1, 2, 3 };

    const UINT vbByteSize = (UINT)vertices.size() * sizeof(Vertex);
    const UINT ibByteSize = (UINT)indices.size() * sizeof(std::uint16_t);

	auto geo = std::make_unique<MeshGeometry>();
	geo->Name = "quadpatchGeo";

	ThrowIfFailed(D3DCreateBlob(vbByteSize, &geo->VertexBufferCPU));
	CopyMemory(geo->VertexBufferCPU->GetBufferPointer(), vertices.data(), vbByteSize);

	ThrowIfFailed(D3DCreateBlob(ibByteSize, &geo->IndexBufferCPU));
	CopyMemory(geo->IndexBufferCPU->GetBufferPointer(), indices.data(), ibByteSize);

	geo->VertexBufferGPU = d3dUtil::CreateDefaultBuffer(md3dDevice.Get(),
		mCommandList.Get(), vertices.data(), vbByteSize, geo->VertexBufferUploader);

	geo->IndexBufferGPU = d3dUtil::CreateDefaultBuffer(md3dDevice.Get(),
		mCommandList.Get(), indices.data(), ibByteSize, geo->IndexBufferUploader);

	geo->VertexByteStride = sizeof(XMFLOAT3);
	geo->VertexBufferByteSize = vbByteSize;
	geo->IndexFormat = DXGI_FORMAT_R16_UINT;
	geo->IndexBufferByteSize = ibByteSize;

	SubmeshGeometry quadSubmesh;
	quadSubmesh.IndexCount = 4;
	quadSubmesh.StartIndexLocation = 0;
	quadSubmesh.BaseVertexLocation = 0;

	geo->DrawArgs["quadpatch"] = quadSubmesh;

	mGeometries
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值