首页 > 其他分享 >绘制几何体(实现)

绘制几何体(实现)

时间:2022-10-04 22:23:23浏览次数:57  
标签:1.0 实现 void BoxApp 几何体 mCommandList mpBoxGeo D3D12 绘制

概述

  • 本篇运用classD3DApp、vertex、inputLayout、vertexBuffer、indexBuffer、constantBuffer、pixelShader、vertexShader、CompileFromFile、rootSignature、descriptor table、MeshGeometry、psoStateObject绘制几何体
  • image-20221004221454367

Direct3D渲染流程

创建windows窗口

Direct3D初始化

消息循环

渲染图形

应用程序结束,清除COM对象,程序退出

  • 最基础的D3DWinMain流程

image-20221002205003485

header files:


#ifndef _boxheader_
#include"d3dApp.h"
#include"MathHelper.h"
#include"UploadBuffer.h"

using Microsoft::WRL::ComPtr;
using namespace DirectX;
using namespace DirectX::PackedVector;
#endif

//顶点属性
struct Vertex
{
	XMFLOAT3 Pos;
	XMFLOAT4 Color;
};

//常量缓冲区对象
struct ObjectConstants
{
    //透视投影矩阵
	XMFLOAT4X4 WorldViewProj = MathHelper::Identity4x4();
};

class BoxApp : public D3DApp
{
public:
	BoxApp(HINSTANCE hInstance);
	BoxApp(const BoxApp& rhs) = delete;
	BoxApp& operator=(const BoxApp& rhs) = delete;
	~BoxApp();
	
    //D3D资源初始化
	virtual bool Initialize() override;

private:
    //若窗口大小发生改变,一些如深度模板缓冲区、后台缓冲区、视口等D3D属性需要改变
	virtual void OnResize() override;
    //更新透视投影矩阵
	virtual void Update(const GameTimer& gt) override;
    //渲染
	virtual void Draw(const GameTimer& gt) override;

	virtual void onm ouseDown(WPARAM btnState, int x, int y)  override;	//鼠标被按下
	virtual void onm ouseUp(WPARAM btnState, int x, int y) override;		//鼠标被松开
	virtual void onm ouseMove(WPARAM btnState, int x, int y) override;	//鼠标移动

	void BuildDescriptorHeaps();	//创建描述符堆
	void BuildConstantBuffers();	//创建常量缓冲区
	void BuildRootSignature();		//创建根签名
	void BuildPSO();	//创建渲染流水线对象
	void BuildBoxGeometry();		//创建几何辅助体,其中含有顶点缓冲区和索引缓冲区结构体
	void BuildShaderAndInputLayout();	//创建着色器字节码和输入布局

private:

	ComPtr<ID3D12RootSignature> mpRootSignature = nullptr;	//根签名接口
	ComPtr<ID3D12DescriptorHeap> mpCbvHeap = nullptr;	//描述符堆接口

	std::unique_ptr<UploadBuffer<ObjectConstants>> mppObjectCB = nullptr;	//上传缓冲区里含常量对象
	std::unique_ptr<MeshGeometry> mpBoxGeo = nullptr;	//几何辅助体接口

	ComPtr<ID3DBlob> mpvsByteBlob = nullptr;	//vs字节码接口
	ComPtr<ID3DBlob> mppsByteBlob = nullptr;	//ps字节码接口

	std::vector<D3D12_INPUT_ELEMENT_DESC> mInputLayout;		//输入布局数组

	ComPtr<ID3D12PipelineState> mpPSO = nullptr;	//渲染流水线对象接口

	XMFLOAT4X4 mWorld = MathHelper::Identity4x4();		//世界矩阵
	XMFLOAT4X4 mView = MathHelper::Identity4x4();		//观测矩阵
	XMFLOAT4X4 mProj = MathHelper::Identity4x4();		//透视投影矩阵

	float mTheta = 1.5f * XM_PI;	//水平视场角
	float mPhi = XM_PIDIV4;    //垂直视场角
	float mRadius = 5.0f;	//摄像机可视范围半径

	POINT mLastMousePos;	//上一次鼠标的位置
}; 

source files:

WinMain()

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance, PSTR cmdLine, int showCmd)
{
//启动调试层监听内存以及额外的调试功能
#if defined(DEBUG) | defined(_DEBUG)
	_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif
    //生成BoxApp object,BoxApp进行初始化若成功则进入消息循环
	try
	{
		BoxApp theApp(hInstance);
		if (theApp.Initialize() == false)
		{
			return 0;
		}
		return theApp.Run();
	}
    //检测返回的HRESULT值,检测失败则抛出异常,显示调用出错的错误码、函数名、文件名、出错行号
	catch (DxException& e)
	{
		MessageBox(nullptr, e.ToString().c_str(), L"HR Failed", MB_OK);
		return 0;
	}
}

Initialize()

bool BoxApp::Initialize()
{
    //先初始化D3D所需基本资源
	if (D3DApp::Initialize() == false)
	{
		return false;
	}
	
	ThrowIfFailed(mCommandList->Reset(mDirectCmdListAlloc.Get(), nullptr));
	
    //创建Box所需资源
	BuildDescriptorHeaps();
	BuildConstantBuffers();
	BuildRootSignature();
	BuildShaderAndInputLayout();
	BuildBoxGeometry();
	BuildPSO();
	
    //执行初始化命令
    //因为命令加入命令列表后需Close(),引用命令列表需要将其重置,而重置前需要先将其Close()
	ThrowIfFailed(mCommandList->Close());
	ID3D12CommandList* cmdsLists[] = { mCommandList.Get() };
	mCommandQueue->ExecuteCommandLists(_countof(cmdsLists), cmdsLists);

	FlushCommandQueue();

	return true;
}

Run()

  • 依然使用D3DApp基础的消息循环(Run)函数

OnResize()

void BoxApp::OnResize()
{
    //D3D基础OnResize函数
	D3DApp::OnResize();

    //若调整窗口尺寸,则更新纵横比并重新计算透视投影矩阵
	XMMATRIX p = XMMatrixPerspectiveFovLH(0.25 * XM_PI, AspectRatio(), 1.0f, 1000.0f);
	XMStoreFloat4x4(&mProj, p);
}

Update()

  • 位于消息循环
void BoxApp::Update(const GameTimer& gt)
{
    //球坐标转换为笛卡尔坐标
    //因为此处用orbiting camera system,摄像机涉及环绕物体旋转等操作,因此需要先用球坐标表示变换,再转换为笛卡尔坐标表示更方便
	float x = mRadius * sinf(mPhi) * cosf(mTheta);
	float y = mRadius * cosf(mPhi);
	float z = mRadius * sinf(mPhi) * sinf(mTheta);

    //构建观测矩阵
	XMVECTOR pos = XMVectorSet(x, y, z, 1.0f);
	XMVECTOR target = XMVectorZero();
	XMVECTOR up = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);

	XMMATRIX view = XMMatrixLookAtLH(pos, target, up);
	XMStoreFloat4x4(&mView, view);

	XMMATRIX world = XMLoadFloat4x4(&mWorld);
	XMMATRIX proj = XMLoadFloat4x4(&mProj);
	XMMATRIX worldViewProj = world * view * proj;

    //更新常量缓冲区
	ObjectConstants objConstant;
    //转置是因为存储在着色器内需要采用列式存储,采用右乘
    //而在DirectXMath库中的矩阵数据在内存中采用行式存储,采用左乘
	XMStoreFloat4x4(&objConstant.WorldViewProj, XMMatrixTranspose(worldViewProj));
	mppObjectCB->CopyData(0, objConstant);
}

Draw()

void BoxApp::Draw(const GameTimer& gt)
{
    //复用记录命令所用的内存,只有GPU中命令列表执行完后才对其重置
	ThrowIfFailed(mDirectCmdListAlloc->Reset());

    //将命令列表加入命令队列后对其进行重置
	ThrowIfFailed(mCommandList->Reset(mDirectCmdListAlloc.Get(), mpPSO.Get()));

    //设置viewport和scissor rectangle
	mCommandList->RSSetViewports(1, &mScreenViewport);
	mCommandList->RSSetScissorRects(1, &mScissorRect);

    //资源转换:将呈现状态转换为渲染目标状态
	mCommandList->ResourceBarrier(1,
		&CD3DX12_RESOURCE_BARRIER::Transition(CurrentBackBuffer(),
			D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET)
	);

    //将渲染目标设定为指定颜色
	mCommandList->ClearRenderTargetView(
		CurrentBackBufferView(),
		Colors::LightSteelBlue,
		0,
		nullptr
	);
    //清理深度模板缓冲区
	mCommandList->ClearDepthStencilView(
		DepthStencilView(),
		D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL,
		1.0f,
		0,
		0,
		nullptr
	);

    //设置渲染流水线的render target缓冲区和depth stencil缓冲区
	mCommandList->OMSetRenderTargets(
		1,
		&CurrentBackBufferView(),
		true,
		&DepthStencilView()
	);

	ID3D12DescriptorHeap* descriptorHeaps[] = { mpCbvHeap.Get() };
	mCommandList->SetDescriptorHeaps(_countof(descriptorHeaps), descriptorHeaps);

	mCommandList->SetGraphicsRootSignature(mpRootSignature.Get());

	mCommandList->SetGraphicsRootDescriptorTable(0, mpCbvHeap->GetGPUDescriptorHandleForHeapStart());

	mCommandList->IASetVertexBuffers(0, 1, &mpBoxGeo->VertexBufferView());
	mCommandList->IASetIndexBuffer(&mpBoxGeo->IndexBufferView());
	mCommandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

    //绘制顶点
	mCommandList->DrawIndexedInstanced(
		mpBoxGeo->DrawArgs["box"].IndexCount,
		1, 0, 0, 0
	);

    //资源转换:渲染目标到呈现
	mCommandList->ResourceBarrier(
		1,
		&CD3DX12_RESOURCE_BARRIER::Transition(
			CurrentBackBuffer(),
			D3D12_RESOURCE_STATE_RENDER_TARGET,
			D3D12_RESOURCE_STATE_PRESENT
		)
	);

	ThrowIfFailed(mCommandList->Close());

	ID3D12CommandList* cmdLists[] = { mCommandList.Get() };
	mCommandQueue->ExecuteCommandLists(_countof(cmdLists), cmdLists);

    //交换前后台缓冲区
	ThrowIfFailed(mSwapChain->Present(0, 0));
	mCurrBackBuffer = (mCurrBackBuffer + 1) % SwapChainBufferCount;

	FlushCommandQueue();
}

OnMouseDown()

  • 按下鼠标时
void BoxApp::OnMouseDown(WPARAM btnState, int x, int y)
{
    //上次鼠标位置
	mLastMousePos.x = x;
	mLastMousePos.y = y;

	SetCapture(mhMainWnd);	//将鼠标捕获设置为属于当前线程的指定窗口
}

OnMouseUp()

  • 释放当前线程中一个窗口的鼠标捕获,并恢复正常的鼠标输入处理
void BoxApp::OnMouseUp(WPARAM btnState, int x, int y)
{
	ReleaseCapture();	//从当前线程中的窗口释放鼠标捕获,并恢复正常的鼠标输入处理
}

OnMouseMove()

void BoxApp::OnMouseMove(WPARAM btnState, int x, int y)
{
	if ((btnState & MK_LBUTTON) != 0)
	{
		//根据鼠标移动距离计算旋转角度并令每个像素都以此角度的1/4旋转
		float dx = XMConvertToRadians(0.25f * static_cast<float>(x - mLastMousePos.x));
		float dy = XMConvertToRadians(0.25f * static_cast<float>(y - mLastMousePos.y));

		//以鼠标输入更新摄像机绕立方体旋转的角度
		mTheta += dx;
		mPhi += dy;

		//限制垂直视场角范围
		mPhi = MathHelper::Clamp(mPhi, 0.1f, MathHelper::Pi - 0.1f);	//Restricts a value to be within a specified range
	}
	else if((btnState & MK_RBUTTON) != 0 )
	{
		//每个像素按鼠标移动距离的0.005倍缩放
		float dx = 0.005f * static_cast<float>(x - mLastMousePos.x);
		float dy = 0.005f * static_cast<float>(y - mLastMousePos.y);

		//以鼠标输入更新摄像机的可视范围半径
		mRadius += dx - dy;

		//限制可视半径范围
		mRadius = MathHelper::Clamp(mRadius, 3.0f, 15.0f);
	}

	mLastMousePos.x = x;
	mLastMousePos.y = y;
}

BuildDescriptorHeaps()

void BoxApp::BuildDescriptorHeaps()
{
	D3D12_DESCRIPTOR_HEAP_DESC cbvHeapDesc;
	cbvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
	cbvHeapDesc.NumDescriptors = 1;
	cbvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
	cbvHeapDesc.NodeMask = 0;

	ThrowIfFailed(
		md3dDevice->CreateDescriptorHeap(
			&cbvHeapDesc,
			IID_PPV_ARGS(&mpCbvHeap)
		)
	)
}

BuildConstantBuffers()

  • 利用d3dUtil.h内的UploadBuffer类完成上传缓冲区的相关操作
void BoxApp::BuildConstantBuffers()
{
    //利用UploadBuffer构造函数完成计算mElementByteSize、createCommittedResource()、map()
	mppObjectCB = std::make_unique<UploadBuffer<ObjectConstants>>(md3dDevice.Get(), 1, true);

	UINT mppObjectCBSize = d3dUtil::CalcConstantBufferByteSize(sizeof(ObjectConstants));

    //Resource()获得UploadBuffer地址,GetGPUVirtualAddress获得缓冲区起始地址
	D3D12_GPU_VIRTUAL_ADDRESS cbAddress = mppObjectCB->Resource()->GetGPUVirtualAddress();
    
    //偏移到常量缓冲区内第i个物体
	int boxCBufferIndex = 0;
	cbAddress += boxCBufferIndex * mppObjectCBSize;

	D3D12_CONSTANT_BUFFER_VIEW_DESC cbvViewDesc;
	cbvViewDesc.BufferLocation = cbAddress;
	cbvViewDesc.SizeInBytes = mppObjectCBSize;

	md3dDevice->CreateConstantBufferView(
		&cbvViewDesc,
		mpCbvHeap->GetCPUDescriptorHandleForHeapStart()
	);
}

BuildRootSignature()

  • 此处只涉及描述符表
void BoxApp::BuildRootSignature()
{
    //定义根参数
	CD3DX12_ROOT_PARAMETER slotRootParameter[1];

    //定义初始化描述符表并初始化根参数
	CD3DX12_DESCRIPTOR_RANGE cbvTable;
	cbvTable.Init(
		D3D12_DESCRIPTOR_RANGE_TYPE_CBV,
		1,
		0
	);
	slotRootParameter[0].InitAsDescriptorTable(
		1,
		&cbvTable
	);

    //定义根签名描述符
	CD3DX12_ROOT_SIGNATURE_DESC rootSigDesc(
		1,
		slotRootParameter,
		0,
		nullptr,
		D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT
	);

    //根签名的描述布局必须先进行序列化处理(将对象的状态信息转换为可以存储或传输的形式的过程),
    //其转换为以ID3DBlob接口表示的序列化数据格式后,才可将其传入CreateRootSignature方法,正式创建根签名
	ComPtr<ID3DBlob>serializedRootSignature = nullptr;
	ComPtr<ID3DBlob>errorBlob = nullptr;
	auto hr = D3D12SerializeRootSignature(
		&rootSigDesc,
		D3D_ROOT_SIGNATURE_VERSION_1,
		serializedRootSignature.GetAddressOf(),
		errorBlob.GetAddressOf()
	);

	if (errorBlob != nullptr)
	{
		::OutputDebugStringA((char*)errorBlob->GetBufferPointer());
	}
	ThrowIfFailed(hr);

	ThrowIfFailed(md3dDevice->CreateRootSignature(
		0,
		serializedRootSignature->GetBufferPointer(),
		serializedRootSignature->GetBufferSize(),
		IID_PPV_ARGS(&mpRootSignature)
	)
	);
}

BuildShaderAndInputLayout()

void BoxApp::BuildShaderAndInputLayout()
{
	HRESULT hr = S_OK;

    //将着色器程序编译为可移植的字节码给图形驱动程序,驱动程序则会将其重新编译为当前GPU优化的本地指令[ATI1]
	mpvsByteBlob = d3dUtil::CompileShader(
		L"Shaders\\color.hlsl", nullptr, "VS", "vs_5_0"
	);
	mppsByteBlob = d3dUtil::CompileShader(
		L"Shaders\\color.hlsl", nullptr, "PS", "ps_5_0"
	);

    ////D3D12_INPUT_ELEMENT_DESC数组描述顶点结构体对应成员
	mInputLayout =
	{
		{
			"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0,
			D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0
		},
		{
		"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12,
		D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0
		}
	};
}

BuildBoxGeometry()

void BoxApp::BuildBoxGeometry()
{
    //顶点
	std::array<Vertex, 8> vertices =
	{
		//后
		Vertex{XMFLOAT3(-1.0f, 1.0f, 1.0f), XMFLOAT4(Colors::White)},	//左上
		Vertex{XMFLOAT3(-1.0f, -1.0f, 1.0f), XMFLOAT4(Colors::Black)},	//左下
		Vertex{XMFLOAT3(1.0f, 1.0f, 1.0f), XMFLOAT4(Colors::Black)},	//右上
		Vertex{XMFLOAT3(1.0f, -1.0f, 1.0f), XMFLOAT4(Colors::White)},	//右下
		//前
		Vertex{XMFLOAT3(-1.0f, 1.0f, -1.0f), XMFLOAT4(Colors::Black)},	//左上
		Vertex{XMFLOAT3(-1.0f, -1.0f, -1.0f), XMFLOAT4(Colors::White)},	//左下
		Vertex{XMFLOAT3(1.0f, 1.0f, -1.0f), XMFLOAT4(Colors::White)},	//右上
		Vertex{XMFLOAT3(1.0f, -1.0f, -1.0f), XMFLOAT4(Colors::Black)}	//右下
	};

    //按此索引顺序绘制
	std::array<std::uint16_t, 36> indices =
	{
		//前
		5, 4, 6,
		5, 6, 7,

		//后
		1, 0, 2,
		1, 2, 3,

		//左
		1, 0, 4,
		1, 4, 5,

		//右
		3, 2, 6,
		3, 6, 7,

		//上
		4, 0, 2,
		4, 2, 6,

		//下
		5, 1, 3,
		5, 3, 7

	};

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

	mpBoxGeo = std::make_unique<MeshGeometry>();
    //指定此几何体网格集合的名称,如此即可根据此名找到ta
	mpBoxGeo->Name = "boxGeo";

	ThrowIfFailed(D3DCreateBlob(VBSize, &mpBoxGeo->VertexBufferCPU));
	CopyMemory(mpBoxGeo->VertexBufferCPU->GetBufferPointer(), &vertices, VBSize);

	ThrowIfFailed(D3DCreateBlob(IBSize, &mpBoxGeo->IndexBufferCPU));
	CopyMemory(mpBoxGeo->IndexBufferCPU->GetBufferPointer(), &indices, IBSize);

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

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

    //缓冲区相关数据
	mpBoxGeo->VertexByteStride = sizeof(Vertex);
	mpBoxGeo->VertexBufferByteSize = sizeof(vertices);
	mpBoxGeo->IndexFormat = DXGI_FORMAT_R16_UINT;
	mpBoxGeo->IndexBufferByteSize = sizeof(indices);

	SubmeshGeometry submesh
	{
		(UINT)indices.size(),
		0,
		0
	};

	mpBoxGeo->DrawArgs["box"] = submesh;
}

BuildPSO()

void BoxApp::BuildPSO()
{
	D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc;
    //用0填充此内存区域,不让结构的成员数值具有不确定性
	ZeroMemory(&psoDesc, sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC));

	psoDesc.pRootSignature = mpRootSignature.Get();
	psoDesc.VS =
	{
		reinterpret_cast<BYTE*>(mpvsByteBlob->GetBufferPointer()),
		mpvsByteBlob->GetBufferSize()
	};
	psoDesc.PS =
	{
		reinterpret_cast<BYTE*>(mppsByteBlob->GetBufferPointer()),
		mppsByteBlob->GetBufferSize()
	};
	psoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);
	psoDesc.SampleMask = UINT_MAX;
	psoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
	psoDesc.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC(D3D12_DEFAULT);
	psoDesc.InputLayout =
	{
		mInputLayout.data(),
		(UINT)mInputLayout.size()
	};
	psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
	psoDesc.NumRenderTargets = 1;
	psoDesc.RTVFormats[0] = mBackBufferFormat;
	psoDesc.DSVFormat = mDepthStencilFormat;
	psoDesc.SampleDesc.Count = m4xMsaaState ? 4 : 1;
	psoDesc.SampleDesc.Quality = m4xMsaaState ? (m4xMsaaQuality - 1) : 0;
	psoDesc.NodeMask = 0;
	
	ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&mpPSO)));
}

标签:1.0,实现,void,BoxApp,几何体,mCommandList,mpBoxGeo,D3D12,绘制
From: https://www.cnblogs.com/chenglixue/p/16754641.html

相关文章