DirectX11-流输出阶段

流输出阶段

我们通过渲染管线的流输出阶段让GPU将几何着色器输出的顶点集合写入到指定的顶点缓冲区,如下图所示。
在这里插入图片描述
1.用来接收流输出的顶点集合的顶点缓冲区描述符需要指定D3D11_BIND_STREAM_OUTPUT绑定标记。比如

	// 设置顶点缓冲区描述
	D3D11_BUFFER_DESC vbd;
	ZeroMemory(&vbd, sizeof(vbd));
	vbd.Usage = D3D11_USAGE_DEFAULT;	// 这里需要允许流输出阶段通过GPU写入
	vbd.ByteWidth = sizeof vertices;
	vbd.BindFlags = D3D11_BIND_VERTEX_BUFFER | D3D11_BIND_STREAM_OUTPUT;	// 需要额外添加流输出标签
	vbd.CPUAccessFlags = 0;
	// 新建顶点缓冲区
	D3D11_SUBRESOURCE_DATA InitData;
	ZeroMemory(&InitData, sizeof(InitData));
	InitData.pSysMem = vertices;
	HR(m_pd3dDevice->CreateBuffer(&vbd, &InitData, m_pVertexBuffers[0].ReleaseAndGetAddressOf()));

2.绑定时需要注意,同一个顶点缓冲区不能同时绑定的输入装配阶段和流输出阶段。所以先恢复默认的流输出阶段和输入装配阶段,然后指定流输出的指定缓冲区。

// 先恢复流输出默认设置,防止顶点缓冲区同时绑定在输入和输出阶段
UINT stride = sizeof(VertexPosColor);
UINT offset = 0;
ID3D11Buffer * nullBuffer = nullptr;
m_pd3dImmediateContext->SOSetTargets(1, &nullBuffer, &offset);
// ...
m_pd3dImmediateContext->IASetInputLayout(mVertexPosColorLayout.Get());
// ...
m_pd3dImmediateContext->SOSetTargets(1, vertexBufferOut.GetAddressOf(), &offset);

3.创建几何着色器时需要填充结构体D3D11_SO_DECLARATION_ENTRY,指定哪些数据流向哪个输出槽。

typedef struct D3D11_SO_DECLARATION_ENTRY {
  UINT   Stream;            // 输出流索引,从0开始
  LPCSTR SemanticName;      // 语义名
  UINT   SemanticIndex;     // 语义索引
  BYTE   StartComponent;    // 从第几个分量(xyzw)开始,只能取0-3
  BYTE   ComponentCount;    // 分量的输出数目,只能取1-4
  BYTE   OutputSlot;        // 输出槽索引,只能取0-3
};

这里对应的是索引为0的流输出对象,输出给绑定在索引为0的输出槽的顶点缓冲区,先输出语义为POSITION的向量中的xyz分量,然后输出COLOR整个向量。这样一个输出的顶点就和原来的顶点一致了。

const D3D11_SO_DECLARATION_ENTRY posColorLayout[2] = {
	{ 0, "POSITION", 0, 0, 3, 0 },
	{ 0, "COLOR", 0, 0, 4, 0 }
};
  1. 创建几何着色器有所不同。
//原接口
HRESULT ID3D11Device::CreateGeometryShaderWithStreamOutput(
  const void                       *pShaderBytecode,    // [In]编译好的着色器字节码
  SIZE_T                           BytecodeLength,      // [In]字节码长度
  const D3D11_SO_DECLARATION_ENTRY *pSODeclaration,     // [In]D3D11_SO_DECLARATION_ENTRY的数组
  UINT                             NumEntries,          // [In]入口总数
  const UINT                       *pBufferStrides,     // [In]一个数组包含了每个绑定到流输出的缓冲区中顶点字节大小
  UINT                             NumStrides,          // [In]上面数组的元素数目
  UINT                             RasterizedStream,    // [In]按索引指定哪个流输出对象用于传递到光栅化阶段
  ID3D11ClassLinkage               *pClassLinkage,      // [In]忽略
  ID3D11GeometryShader             **ppGeometryShader   // [Out]创建好的几何着色器
);

//调用示例
const D3D11_SO_DECLARATION_ENTRY posColorLayout[2] = {
	{ 0, "POSITION", 0, 0, 3, 0 },
	{ 0, "COLOR", 0, 0, 4, 0 }
};

HR(device->CreateGeometryShaderWithStreamOutput(blob->GetBufferPointer(), blob->GetBufferSize(), posColorLayout, ARRAYSIZE(posColorLayout),
	&stridePosColor, 1, D3D11_SO_NO_RASTERIZED_STREAM, nullptr, m_pTriangleSOGS.GetAddressOf()));

5.这样绘制的时候就可以将几何着色器的顶点集合输出到指定的顶点缓冲区中,然后再次作为输入的顶点缓冲区重新绘制。示例如下:

	// 初始化所有顶点缓冲区
	for (int i = 1; i < 7; ++i)
	{
		vbd.ByteWidth *= 3;
		HR(m_pd3dDevice->CreateBuffer(&vbd, nullptr, m_pVertexBuffers[i].ReleaseAndGetAddressOf()));
		m_BasicEffect.SetStreamOutputSplitedTriangle(m_pd3dImmediateContext.Get(), m_pVertexBuffers[i - 1].Get(), m_pVertexBuffers[i].Get());
		// 第一次绘制需要调用一般绘制指令,之后就可以使用DrawAuto了
		if (i == 1)
		{
			m_pd3dImmediateContext->Draw(m_InitVertexCounts, 0);
		}
		else
		{
			m_pd3dImmediateContext->DrawAuto();
		}

	}

void BasicEffect::SetStreamOutputSplitedTriangle(ID3D11DeviceContext * deviceContext, ID3D11Buffer * vertexBufferIn, ID3D11Buffer * vertexBufferOut)
{
	// 先恢复流输出默认设置,防止顶点缓冲区同时绑定在输入和输出阶段
	UINT stride = sizeof(VertexPosColor);
	UINT offset = 0;
	ID3D11Buffer * nullBuffer = nullptr;
	deviceContext->SOSetTargets(1, &nullBuffer, &offset);

	deviceContext->IASetInputLayout(nullptr);
	deviceContext->SOSetTargets(0, nullptr, &offset);

	deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
	deviceContext->IASetInputLayout(pImpl->m_pVertexPosColorLayout.Get());

	deviceContext->IASetVertexBuffers(0, 1, &vertexBufferIn, &stride, &offset);

	deviceContext->VSSetShader(pImpl->m_pTriangleSOVS.Get(), nullptr, 0);
	deviceContext->GSSetShader(pImpl->m_pTriangleSOGS.Get(), nullptr, 0);

	deviceContext->SOSetTargets(1, &vertexBufferOut, &offset);

	deviceContext->RSSetState(nullptr);
	deviceContext->PSSetShader(nullptr, nullptr, 0);
}

drawAtuo 的过程如下图所示
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xhh-cy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值