d3d12龙书阅读----绘制几何体(上)

这篇具有很好参考价值的文章主要介绍了d3d12龙书阅读----绘制几何体(上)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

d3d12龙书阅读----绘制几何体(上)

本节主要介绍了构建一个简单的彩色立方体所需流程与重要的api
下面主要结合立方体代码分析本节相关知识

顶点

输入装配器阶段的输入
首先,我们需要定义立方体的八个顶点
顶点结构体:

struct Vertex
{
    XMFLOAT3 Pos;
    XMFLOAT4 Color;
};

当然,对于更复杂的情况,我们不仅要定义顶点的位置与颜色,还要包括法线向量、纹理x坐标、纹理y坐标等等
但在这里情形比较简单
之后,我们还需要定义一个顶点结构体描述子数组,被称为输入布局描述
数组中的每个成员与顶点结构体的成员一一对应,同时也与顶点着色器中的参数对应:

std::vector<D3D12_INPUT_ELEMENT_DESC> mInputLayout;

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 }
};

//顶点着色器
struct VertexIn
{
	float3 PosL  : POSITION;
    float4 Color : COLOR;
};

D3D12_INPUT_ELEMENT_DESC的定义与参数说明可见:
https://learn.microsoft.com/zh-cn/windows/win32/api/d3d12/ns-d3d12-d3d12_input_element_desc
接着,我们还需要为顶点创建顶点缓冲区,与第四章内容创建深度缓冲区的步骤相似,我们首先要填写D3D12_RESOURCE_DESC结构体描述缓冲区资源,然后使用CreateCommittedResource 方法,创建资源与一个堆,并把资源上传到堆中。

CreateCommittedResource 方法的参数说明可见:
https://learn.microsoft.com/zh-cn/windows/win32/api/d3d12/nf-d3d12-id3d12device-createcommittedresource
其中有三个参数在本节中很重要
一个是D3D12_HEAP_PROPERTIES *pHeapProperties
一个是D3D12_RESOURCE_DESC *pDesc
一个是D3D12_RESOURCE_STATES

D3D12_RESOURCE_STATES代表着资源状态
在d3d的初始化中我们提到这样可以防止资源冒险 比如在读的状态在写资源等等
详细的资源种类可见:
https://learn.microsoft.com/zh-cn/windows/win32/api/d3d12/ne-d3d12-d3d12_resource_states
D3D12_HEAP_PROPERTIES是一个结构体:

d3d12龙书阅读----绘制几何体(上)
其中D3D12_HEAP_TYPE的类型主要有以下几种:

d3d12龙书阅读----绘制几何体(上)

D3D12_RESOURCE_DESC 与 D3D12_HEAP_PROPERTIES的创建 这里分别借用了CD3DX12_HEAP_PROPERTIES 与 CD3DX12_RESOURCE_DESC两种变体方法来简化缓冲区的创建过程:

ThrowIfFailed(device->CreateCommittedResource(
    //默认堆 
    &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
    D3D12_HEAP_FLAG_NONE,
    //bytesize 代表缓冲区所占字节数
    &CD3DX12_RESOURCE_DESC::Buffer(byteSize),
    //common状态
	D3D12_RESOURCE_STATE_COMMON,
    nullptr,
    IID_PPV_ARGS(defaultBuffer.GetAddressOf())));

让我们回到创建顶点缓冲区上来,当我们想要为树木、地形等默认几何体(每一帧都不会发生变化的结合体)来创建顶点缓冲区时,常常选择默认堆来优化性能,当顶点缓冲区初始化完毕后,只有gpu需要从中读取数据来绘制几何体。但是在初始化缓冲区时,需要cpu向默认堆中的顶点缓冲区写入数据,这是我们就需要一个上传堆作为中介,为此本节编写了CreateDefaultBuffer函数:

Microsoft::WRL::ComPtr<ID3D12Resource> d3dUtil::CreateDefaultBuffer(
    ID3D12Device* device,
    ID3D12GraphicsCommandList* cmdList,
    const void* initData,
    UINT64 byteSize,
    Microsoft::WRL::ComPtr<ID3D12Resource>& uploadBuffer)
{
    //创建缓冲区资源
    ComPtr<ID3D12Resource> defaultBuffer;
    ThrowIfFailed(device->CreateCommittedResource(
        &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
        D3D12_HEAP_FLAG_NONE,
        &CD3DX12_RESOURCE_DESC::Buffer(byteSize),
		D3D12_RESOURCE_STATE_COMMON,
        nullptr,
        IID_PPV_ARGS(defaultBuffer.GetAddressOf())));

    //创建上传堆 作为中介
    ThrowIfFailed(device->CreateCommittedResource(
        //上传堆
        &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD),
		D3D12_HEAP_FLAG_NONE,
        &CD3DX12_RESOURCE_DESC::Buffer(byteSize),
        //上传堆所需要的启动状态
		D3D12_RESOURCE_STATE_GENERIC_READ,
        nullptr,
        IID_PPV_ARGS(uploadBuffer.GetAddressOf())));


    // 描述我们要传入默认堆的数据
    D3D12_SUBRESOURCE_DATA subResourceData = {};
    subResourceData.pData = initData;
    subResourceData.RowPitch = byteSize;
    subResourceData.SlicePitch = subResourceData.RowPitch;

    //转换资源状态  将数据复制给上传堆 上传堆再复制到默认堆
	cmdList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(defaultBuffer.Get(), 
		D3D12_RESOURCE_STATE_COMMON, 
        //资源处于复制目标状态
        D3D12_RESOURCE_STATE_COPY_DEST));
    UpdateSubresources<1>(cmdList, defaultBuffer.Get(), uploadBuffer.Get(), 0, 0, 1, &subResourceData);
	cmdList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(defaultBuffer.Get(),
		D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_GENERIC_READ));
    return defaultBuffer;
}

整个创建顶点缓冲区的流程如下:
d3d12龙书阅读----绘制几何体(上)

然后我们还需要为其创建视图(无需为其创建描述符堆) 以及将其绑定到渲染流水线上的输入槽,这样就可以向输入装配器传入顶点数据:

D3D12_VERTEX_BUFFER_VIEW VertexBufferView()const
{
	D3D12_VERTEX_BUFFER_VIEW vbv;
    //虚拟地址 使用函数即可获得
	vbv.BufferLocation = VertexBufferGPU->GetGPUVirtualAddress();
    //顶点缓冲区所占字节大小
	vbv.StrideInBytes = VertexByteStride;
    //每个顶点数据所占字节大小
	vbv.SizeInBytes = VertexBufferByteSize;

	return vbv;
}
//0 代表绑定第0个输入槽 共有16个
//1 代表顶点缓冲区的数量为1
mCommandList->IASetVertexBuffers(0, 1, &mBoxGeo->VertexBufferView());

最后绘制顶点:

定义图元拓扑类型
 mCommandList->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

d3d12龙书阅读----绘制几何体(上)

索引

索引缓冲区的创建过程和顶点的过程很类似:

定义索引
std::array<std::uint16_t, 36> indices =
{
	// front face
	0, 1, 2,
	0, 2, 3,

	// back face
	4, 6, 5,
	4, 7, 6,

	// left face
	4, 5, 1,
	4, 1, 0,

	// right face
	3, 2, 6,
	3, 6, 7,

	// top face
	1, 5, 6,
	1, 6, 2,

	// bottom face
	4, 0, 3,
	4, 3, 7
};
//索引缓冲区大小
const UINT ibByteSize = (UINT)indices.size() * sizeof(std::uint16_t);
//定义默认堆 与 上传堆
Microsoft::WRL::ComPtr<ID3D12Resource> IndexBufferGPU = nullptr;
Microsoft::WRL::ComPtr<ID3D12Resource> IndexBufferUploader = nullptr;
//初始化索引缓冲区
mBoxGeo->IndexBufferGPU = d3dUtil::CreateDefaultBuffer(md3dDevice.Get(),
	mCommandList.Get(), indices.data(), ibByteSize, mBoxGeo->IndexBufferUploader);
//创建视图 绑定到渲染流水线
D3D12_INDEX_BUFFER_VIEW IndexBufferView()const
{
	D3D12_INDEX_BUFFER_VIEW ibv;
	ibv.BufferLocation = IndexBufferGPU->GetGPUVirtualAddress();
	ibv.Format = IndexFormat;
	ibv.SizeInBytes = IndexBufferByteSize;

	return ibv;
}
mCommandList->IASetIndexBuffer(&mBoxGeo->IndexBufferView());
//绘制顶点
mCommandList->DrawIndexedInstanced(
		mBoxGeo->DrawArgs["box"].IndexCount, 
		1, 0, 0, 0);

注意在上述过程中我们采用索引来绘制顶点 而不是像上一部分那样使用DrawInstanced 参数解释如下:
d3d12龙书阅读----绘制几何体(上)

顶点着色器

顶点着色器代码如下

//cbuffer 代表常量缓冲区 b0存储资源的寄存器
cbuffer cbPerObject : register(b0)
{
    //从局部空间转换到齐次裁剪空间
	float4x4 gWorldViewProj; 
};

//顶点着色器输入 
//冒号后面的是参数语义
//要和之前提到的输入布局描述对应 同时也要与顶点着色器的输入参数对应
//冒号签名的是自定义的数据成员的名称 叫做输入签名
struct VertexIn
{
	float3 PosL  : POSITION;
    float4 Color : COLOR;
};
//顶点着色器输出 语义作为下一步几何着色器或者像素着色器的输入参数
struct VertexOut
{
	float4 PosH  : SV_POSITION;
    float4 Color : COLOR;
};

VertexOut VS(VertexIn vin)
{
	VertexOut vout;
	
	//转换到齐次裁剪空间
    //mul 有向量矩阵 或者矩阵矩阵乘法的多个重载版本
    //透视除法步骤是交由硬件处理 人为无需编写代码
	vout.PosH = mul(float4(vin.PosL, 1.0f), gWorldViewProj);
	
	// 直接将输入颜色传递给像素着色器
    vout.Color = vin.Color;
    
    return vout;
}

不同寄存器存储不同类型资源如下:
d3d12龙书阅读----绘制几何体(上)
由于使用的着色器语言 HLSL没有 引用或者指针 所以返回多条数据 可以使用结构体的形式 在HLSL中所有函数都是内联的

注意上述代码的语义都是特定的 比如SV_POSITION就代表着存储着齐次裁剪空间的顶点位置信息 其余语义说明可见:
https://learn.microsoft.com/zh-cn/windows/win32/direct3dhlsl/dx-graphics-hlsl-semantics

还有一个地方注意的是 顶点着色器中使用的数据必须要都在之前的顶点结构体中定义(当然还有输入布局描述)但是我们定义的顶点结构体数据可以更多 必须是一个包含关系

像素着色器

对顶点着色器输出的数据 进行插值 在不使用几何着色器的情况下 插值的结果作为像素着色器的输入
这里还强调了一下pixel fragment 与 pixel的区别 像素着色器的输入是像素片段 而像素是已经通过深度测试 模版测试等等 最终绘制到屏幕上去的像素
d3d还提到 由于硬件优化的原因 有些像素片段 进行early-z之后就已经被筛除 但是有可能像素着色器中对像素片段的深度值进行了改变 此时就不能进行early-z 因为像素片段的最终深度值尚未确定

本节的像素着色器的代码很简单,直接输出颜色:
函数参数列表之后的SV_Target语义表示 输出的格式应该与渲染目标的格式相匹配

float4 PS(VertexOut pin) : SV_Target
{
    return pin.Color;
}

着色器编译

ComPtr<ID3DBlob> mvsByteCode = nullptr;
ComPtr<ID3DBlob> mpsByteCode = nullptr;
mvsByteCode = d3dUtil::CompileShader(L"Shaders\\color.hlsl", nullptr, "VS", "vs_5_0");
mpsByteCode = d3dUtil::CompileShader(L"Shaders\\color.hlsl", nullptr, "PS", "ps_5_0");

ComPtr<ID3DBlob> d3dUtil::CompileShader(
	const std::wstring& filename,
	const D3D_SHADER_MACRO* defines,
	const std::string& entrypoint,
	const std::string& target)
{
	UINT compileFlags = 0;
#if defined(DEBUG) || defined(_DEBUG)  
	compileFlags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
#endif

	HRESULT hr = S_OK;

	ComPtr<ID3DBlob> byteCode = nullptr;
	ComPtr<ID3DBlob> errors;
	hr = D3DCompileFromFile(filename.c_str(), defines, D3D_COMPILE_STANDARD_FILE_INCLUDE,
		entrypoint.c_str(), target.c_str(), compileFlags, 0, &byteCode, &errors);

	if(errors != nullptr)
		OutputDebugStringA((char*)errors->GetBufferPointer());

	ThrowIfFailed(hr);

	return byteCode;
}

其中比较重要的参数有
文件名 比如:L"Shaders\color.hlsl" 这里的类型是wstring 因此要使用L
着色器的入口点 VS/PS
着色器版本 vs_5_0等等
d3d12龙书阅读----绘制几何体(上)
这里简要介绍了一下ID3DBlob这个类型:
d3d12龙书阅读----绘制几何体(上)
我在知乎看到一个回答介绍的更为详细:
https://zhuanlan.zhihu.com/p/304352552
下面引用如下

Blob(binary large object),二进制大对象。ID3DBlob则是DX12内建的一种存放较为庞大的二进制对象。在GPU上面,我们对于大部分资源的描述一般都是用地址起点(address starting point)加上对象内存容量(object memory)来描述并且确定某一对象资源
因为其资源内存容量较为庞大的特点,这些资源大多数都不能直接上传到GPU,而是首先在CPU预处理成Blob,然后再上传绑定到GPU上面,才能供GPU使用
上传的对象包括但不限于顶点数据(Vertex data),索引数据(Index data),材质(Texture)等,还包括我们着色器程序(shader)。即我们写的HLSL(high level shader language)程序,需要在CPU端通过预处理和编译才能上传到GPU端供GPU读取并且执行

常量缓冲区

常量缓冲区也是一种GPU资源(ID3D12Resource),但是常量缓冲区是CPU每帧都要更新一次,比如摄像机如果每帧都在移动,那么常量缓冲区每帧都需要更新其中的视图矩阵,所以我们需要将常量缓冲区创建到一个上传堆而非默认堆,这样我们就可以从cpu端更新常量。

下面让我们来看看示例程序中是如何创建常量缓冲区的
首先,定义常量缓冲区结构体:

struct ObjectConstants
{
    XMFLOAT4X4 WorldViewProj = MathHelper::Identity4x4();
};

我们可以看到目前里面只定义了视图矩阵

其次,定义了上传缓冲区的辅助类UploadBuffer.h
注意该辅助类主要用于需要提交到上传堆的gpu资源,而我们之前有一个用于创建默认堆的辅助函数:

template<typename T>
class UploadBuffer
{
public:
    //参数说明 
	//elementCount表示ObjectConstants的数量
	//isConstantBuffer表示是否为要创建常量缓冲区
    UploadBuffer(ID3D12Device* device, UINT elementCount, bool isConstantBuffer) : 
        mIsConstantBuffer(isConstantBuffer)
    {
        mElementByteSize = sizeof(T);
        
		//如果为常量缓冲区,重新计算ObjectConstants结构体的大小
        if(isConstantBuffer)
            mElementByteSize = d3dUtil::CalcConstantBufferByteSize(sizeof(T));
        //创建gpu资源(常量缓冲区) 与 一个上传堆 并把资源提交到堆上
        ThrowIfFailed(device->CreateCommittedResource(
            &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD),
            D3D12_HEAP_FLAG_NONE,
            &CD3DX12_RESOURCE_DESC::Buffer(mElementByteSize*elementCount),
			D3D12_RESOURCE_STATE_GENERIC_READ,
            nullptr,
            IID_PPV_ARGS(&mUploadBuffer)));
        
		//使用map方法,在cpu端分配一块虚拟地址范围,用来映射gpu的资源
        ThrowIfFailed(mUploadBuffer->Map(0, nullptr, reinterpret_cast<void**>(&mMappedData)));

    }

    UploadBuffer(const UploadBuffer& rhs) = delete;
    UploadBuffer& operator=(const UploadBuffer& rhs) = delete;
    ~UploadBuffer()
    {
		//调用unmap取消对gpu资源的映射
        if(mUploadBuffer != nullptr)
            mUploadBuffer->Unmap(0, nullptr);

        mMappedData = nullptr;
    }
    //获取gpu资源
    ID3D12Resource* Resource()const
    {
        return mUploadBuffer.Get();
    }
    //从cpu端更新常量缓冲区中的内容
    void CopyData(int elementIndex, const T& data)
    {
        memcpy(&mMappedData[elementIndex*mElementByteSize], &data, sizeof(T));
    }

private:
    Microsoft::WRL::ComPtr<ID3D12Resource> mUploadBuffer;
    BYTE* mMappedData = nullptr;
    UINT mElementByteSize = 0;
    bool mIsConstantBuffer = false;
};

创建常量缓冲区 我们可以使用如下代码:

std::unique_ptr<UploadBuffer<ObjectConstants>> mObjectCB = nullptr;
定义常量缓冲区存储的是ObjectConstants类型数据 数量为1
mObjectCB = std::make_unique<UploadBuffer<ObjectConstants>>(md3dDevice.Get(), 1, true);

上述代码中 我们可以看到UploadBuffer这个类是使用了模版 这意味着该方法不仅可以创建常量缓冲区资源 也可以创建其它使用上传堆的gpu资源

同时上述代码中在获取ObjectConstants的大小时,我们可以看到使用了d3dUtil::CalcConstantBufferByteSize的方法,该方法代码如下:

static UINT CalcConstantBufferByteSize(UINT byteSize)
{
    // Example: Suppose byteSize = 300.
    // (300 + 255) & ~255
    // 555 & ~255
    // 0x022B & ~0x00ff
    // 0x022B & 0xff00
    // 0x0200
    // 512
    return (byteSize + 255) & ~255;
}

这是因为常量缓冲区的大小必须是硬件最小分配空间的整数倍(通常是256b) 这是因为硬件只能按照这样的规格来查看常量数据,所以要对常量缓冲区的数组进行填充字节

然后,我们还需要创建相应的描述符来将资源绑定到渲染流水线上,和之前顶点缓冲区描述符以及索引不同,我们要为常量缓冲区描述符创建描述堆,然后再创建描述符:

//创建cbv描述符堆
void BoxApp::BuildDescriptorHeaps()
{
    D3D12_DESCRIPTOR_HEAP_DESC cbvHeapDesc;
    cbvHeapDesc.NumDescriptors = 1;
    cbvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
    cbvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
	cbvHeapDesc.NodeMask = 0;
    ThrowIfFailed(md3dDevice->CreateDescriptorHeap(&cbvHeapDesc,
        IID_PPV_ARGS(&mCbvHeap)));
}

//计算第i个物体ObjectConstants的起始内存位置 与大小
UINT objCBByteSize = d3dUtil::CalcConstantBufferByteSize(sizeof(ObjectConstants));
D3D12_GPU_VIRTUAL_ADDRESS cbAddress = mObjectCB->Resource()->GetGPUVirtualAddress();
int boxCBufIndex = 0;
cbAddress += boxCBufIndex*objCBByteSize;

//填写描述符 创建视图
D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc;
cbvDesc.BufferLocation = cbAddress;
cbvDesc.SizeInBytes = d3dUtil::CalcConstantBufferByteSize(sizeof(ObjectConstants));

md3dDevice->CreateConstantBufferView(
	&cbvDesc,
	mCbvHeap->GetCPUDescriptorHandleForHeapStart());

根签名与描述符表

根签名的作用是,定义绑定到渲染流水线上的资源,与对应的着色器的输入寄存器的映射关系,从而可以被着色器程序访问。
不同的绘制调用可能用到一组不同的着色器程序,这就意味着用到不同的根签名。
在d3d中,根签名使用ID3DRootSignature接口来表示,并且由一组描述绘制调用过程中着色器所需资源的根参数定义而成
根参数可以是根常量、根描述符或者描述符表。在本章中,我们只是简要了解根签名,详细的介绍将在下一章中展开,本章只使用了描述符表,即描述符堆中存有描述符的一块连续区域
下面根据代码简要分析:

void BoxApp::BuildRootSignature()
{

	// 根参数
	CD3DX12_ROOT_PARAMETER slotRootParameter[1];

	// 创建一个cbv的描述符表
	CD3DX12_DESCRIPTOR_RANGE cbvTable;
	cbvTable.Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 
    1, //描述符数量
    0 //绑定到b0寄存器);
	slotRootParameter[0].InitAsDescriptorTable(1, &cbvTable);

	// 根签名由一组根参数构成
	CD3DX12_ROOT_SIGNATURE_DESC rootSigDesc(1, slotRootParameter, 0, nullptr, 
		D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);

	// 创建根签名  必须要先将根签名的描述布局通过ID3DBlob序列化才能传入创建根签名的方法
	ComPtr<ID3DBlob> serializedRootSig = nullptr;
	ComPtr<ID3DBlob> errorBlob = nullptr;
	HRESULT hr = D3D12SerializeRootSignature(&rootSigDesc, D3D_ROOT_SIGNATURE_VERSION_1,
		serializedRootSig.GetAddressOf(), errorBlob.GetAddressOf());

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

	ThrowIfFailed(md3dDevice->CreateRootSignature(
		0,
		serializedRootSig->GetBufferPointer(),
		serializedRootSig->GetBufferSize(),
		IID_PPV_ARGS(&mRootSignature)));
}

然后还要通过命令列表设置cbv堆与根签名,再通过设置描述符表绑定资源:

ID3D12DescriptorHeap* descriptorHeaps[] = { mCbvHeap.Get() };
mCommandList->SetDescriptorHeaps(_countof(descriptorHeaps), descriptorHeaps);
mCommandList->SetGraphicsRootSignature(mRootSignature.Get());
mCommandList->SetGraphicsRootDescriptorTable(0, mCbvHeap->GetGPUDescriptorHandleForHeapStart());

一些关于根签名的注意事项:
d3d12龙书阅读----绘制几何体(上)

配置光栅器状态与流水线状态对象

大多数控制图形流水线状态对象被统称为流水线状态对象PSO,用接口ID3D12PipelineState表示
创建其的代码如下:

void BoxApp::BuildPSO()
{
    D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc;
    ZeroMemory(&psoDesc, sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC));
    //绑定输入布局
    psoDesc.InputLayout = { mInputLayout.data(), (UINT)mInputLayout.size() };
    //根签名
    psoDesc.pRootSignature = mRootSignature.Get();
    //顶点着色器
    psoDesc.VS = 
	{ 
		reinterpret_cast<BYTE*>(mvsByteCode->GetBufferPointer()), 
		mvsByteCode->GetBufferSize() 
	};
    //像素着色器
    psoDesc.PS = 
	{ 
		reinterpret_cast<BYTE*>(mpsByteCode->GetBufferPointer()), 
		mpsByteCode->GetBufferSize() 
	};
    //填写光栅器状态 这里使用默认值创建
    psoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
    psoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);
    psoDesc.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC(D3D12_DEFAULT);
    psoDesc.SampleMask = UINT_MAX;
    psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
    psoDesc.NumRenderTargets = 1;
    psoDesc.RTVFormats[0] = mBackBufferFormat;
    psoDesc.SampleDesc.Count = m4xMsaaState ? 4 : 1;
    psoDesc.SampleDesc.Quality = m4xMsaaState ? (m4xMsaaQuality - 1) : 0;
    psoDesc.DSVFormat = mDepthStencilFormat;
    ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&mPSO)));
}

描述符的详细属性可查看微软文档文章来源地址https://www.toymoban.com/news/detail-843806.html

到了这里,关于d3d12龙书阅读----绘制几何体(上)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 一、D3D12学习笔记——初始化Direct3D

    工厂类IDXGIFactory4,这个类有两个作用: 1.枚举适配器(显卡); 2.创建交换链 这个类对象的创建如下: 用这个对象mdxgiFactory枚举我们可以使用的显卡等适配器: 对于一个选定的适配器pIAdapter,拿着它去创建设备 IID_PPV_ARGS这个宏实际包含了两个东西,uuid的COM ID和对象的指针

    2024年02月10日
    浏览(13)
  • DirectX12(D3D12)基础教程(二十一)—— PBR:IBL 的数学原理(1/5)

      2023年来了,令人闹心伤身的疫情也暂告一段落了。感慨之余,其实我也挺惆怅,这个系列教程还能继续下去吗?或者我自己还能坚持多久,我不知道。因为我也天天徘徊在失业的边缘,年纪大了被人嫌弃,学历低被人嫌弃,身体稍差也被人嫌弃,忽然发现我已不是当初那

    2023年04月08日
    浏览(16)
  • DirectX12(D3D12)基础教程(二十一)—— PBR:IBL 的数学原理(5/5)镜面反射积分项2及光照合成

    3.5.4、根据 Epic 近似假设进一步拆分积分项为两部分之积   通过之前的步骤,实际上以及得到了我们想要的镜面反射项的蒙特卡洛积分重要性采样的形式,并且根据我们的假设认为视方向等于法线方向,实际上以及可以编码实现这个积分计算过程,而且依据假设我们不再需

    2023年04月08日
    浏览(9)
  • VTK 几何体连通区域分析 vtkPolyDataConnectivityFilter

    前言:    vtkPolyDataConnectivityFilter 使用过,但网上没有看到完事的教程;这里整理一下;   提取数据集中连通的多边形数据。 该类是一个滤波器,提取cell(区域) - 拥有公共点或者满足某个阈值 该类在提取连通区域时候有如下6种模式:    1 )提取数据集中的最大(最多

    2024年02月02日
    浏览(15)
  • Three.js--》几何体顶点知识讲解

    目录 几何体顶点位置数据 点线定义几何体顶点数据 网格模型定义几何体顶点数据 顶点法线数据 实现阵列立方体与相机适配 常见几何体简介 几何体的旋转、缩放、平移方法 本篇文章主要讲解几何体的顶点概念,相对偏底层一些,不过掌握之后会更加深入理解three.js的几何体

    2024年02月02日
    浏览(8)
  • Three.js之几何体、高光材质、渲染器设置、gui

    阵列立方体和相机适配体验 Threejs常见几何体简介 … gui.js库(可视化改变三维场景) 注:基于Three.js v0.155.0 长方体:BoxGeometry 球体:SphereGeometry 圆柱:CylinderGeometry 矩形平面:PlaneGeometry 圆形平面:CircleGeometry 高光网格材质:MeshPhongMaterial(shininess、specular) WebGL渲染器设置:

    2024年02月11日
    浏览(10)
  • 【ThreeJS基础教程-初识Threejs】1.6各种各样的几何体

    本段内容会写在0篇以外所有的,本人所编写的Threejs教程中 对,学习ThreeJS有捷径 当你有哪个函数不懂的时候,第一时间去翻一翻文档 当你有哪个效果不会做的时候,第一时间去翻一翻所有的案例,也许就能找到你想要的效果 最重要的一点,就是,绝对不要怕问问题,越怕找

    2024年02月08日
    浏览(12)
  • Three.js——scene场景、几何体位置旋转缩放、正射投影相机、透视投影相机

    个人简介 👀 个人主页: 前端杂货铺 🙋‍♂️ 学习方向: 主攻前端方向,正逐渐往全干发展 📃 个人状态: 研发工程师,现效力于中国工业软件事业 🚀 人生格言: 积跬步至千里,积小流成江海 🥇 推荐学习:🍍前端面试宝典 🍉Vue2 🍋Vue3 🍓Vue2/3项目实战 🥝Node.js🍒

    2024年04月14日
    浏览(7)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包