DX11(三)-绘制一个三角形

05/29/2020
06/02/2020 后续补充-练习题

//引用库文件来编译hlsl文件
pragma comment(lib,"D3DCompiler.lib") //着色器编译器

编译着色器文件(.hlsl的文件)

运行期间编译着色器,生成字节码

HRESULT D3DCompileFromFile(
	LPCWSTR pFileName,
	CONST D3D_SHADER_MACRO* pDefines,
	ID3DInclude* pInclude,	//是否允许hlsl文件添加include
	LPCSTR pEntrypoint,		//入口函数名,比如"VS","PS","CS",表示
	LPCSTR pTarget,			//使用着色器模型,比如"vs_5_)"
	UINT Flags1,
	UINT Flags2,
	ID3DBlob** ppCode,		//获得着色器二进制块
	ID3DBlob** ppErrorMsgs	//错误信息的二级制块
);
  • 入口函数名与hlsl文件中的着色器名字必须保持一致
//Triangle.hlsl
VertexOut VS(VertexIn vIn){...} // 函数名与入口名字保持一致
  • 着色器模型:如果显卡支持特性等级是11.0,使用Shader Model 5.0
  • ppCode 表示返回的二级制文件
//运行期编译.hlsl文件
ID3DBlob** ppBlobOut; //返回的二进制文件
ID3DBlob* errorBlob;//错误的Blob
hr = D3DCompileFromFile("Triangle_VS.hlsl",nullptr,D3D_COMPILE_STANDARD_FILE_INCLUDE,
						"VS",dwShaderFlags,0,ppBlobOut,&errorBlob); //获得一个编译好的着色器文件(.cso)

编译器产生对象,并在运行期加载(.cso文件)

visual studio 2017 自带hlsl的编译器,可以在编译的时候生成cso文件,然后再运行期加载

//运行期加载函数
HRESULT = S_OK;
//传递一个编译好的hlsl文件,文件名以(.cso)结尾
ID3DBlob** ppBlobOut; //返回的二进制文件
D3DReadFileBlob(cosFileNameInOut,ppBlobOut) == S_OK;

Device设备

创建着色器

HRESULT ID3D11Device::CreateVertexShader(
	const void *pShaderBytecode,		//着色器字节码
	SIZE_T BytecodeLength,				//字节码长度
	ID3D11ClassLinkage* pClassLinkage,	//
	ID3D11VertexShader **ppVertexShader	//获取顶点着色器
);
ComPtr<ID3DBlob> blob;
ComPtr<ID3D11VertexBuffer> mVertexShader;
md3dDevice->CreateVertexShader(blob->GetBufferPointer(),blob->GetBufferSize(),
								nullptr,mVertexShader.GetAddressOf());

绑定输入布局

在HLSL中,用于输入结构体为:

//着色器文件HLSL
struct Vertexin
{
	float3 pos:POSITION;
	float4 color:COLOR;
};
#include <directxmath.h> //XMFLOAT3 数学库
//C++文件中
struct VertexPosColor
{
	DirectX::XMFLOAT3 pos;
	DirectX::XMFLOAT4 colr;
	static const D3D11_INPUT_ELEMENT_DESC inputLayer[2]; //静态资源,可以提前赋值
};

如何建立对应关系

顶点缓冲区是二进制文件(Blob),为了建立C++结构体与HLSL结构体的对应关系,需要使用ID3D11InputLayer

  • 输入布局来描述每一个成员的用途,语义,大小等信息
  • C++的static const D3D11_INPUT_ELEMENT_DESC inputLayer[2]不属于结构体的成员,是为了方便描述布局关系的

描述输入布局

typedef struct D3D11_INPUT_ELEMENT_DESC
{
	LPCSTR SemanticName;	//语义名
	UINT SemanticIndex;		//语义索引
	DXGI_FORMAT Format;		//数据格式
	UINT InputSlot;			//输入槽索引(0-15)
	UIINT AlignedByteOffset;//初始位置
	D3D11_INPUT_CLASSIFICATION InputSlotClass;	//输入类型
	UINT InstanceDataStepRate;	//忽略
} D3D11_INPUT_ELEMENT_DESC;
inputLayer[0] = 
{
	{
		"POSITION", 						//看一下HLSL结构体
		0,
		DXGI_FORMAT_R32G32B32_FLOAT,		//数据存储方式,3个Float的类型值
		0,
		0,
		D3D11_INPUT_PER_VERTEX_DATA,
		0
	},
}
inputLayer[1] = 
{
	{
		"COLOR",  
		0,
		DXGI_FORMAT_R32G32B32A32_FLOAT,		//4个Float的类型
		0,
		12,  								//初始位置 字节偏移量
		D3D11_INPUT_PER_VERTEX_DATA,
		0
	},
}

创建输入布局 CreateInputLayer

HRESULT ID3D11Device::CreateInputLayer(
	const D3D11_INPUT_ELEMENT_DESC* pInputElementDescs,		//输入布局描述
	UINT NumElements,										//上述数组元素个数,2个
	const void* pShaderBytecodeWithInputSignature,			//着色器字节码
	ID3D11InputLayer** ppInputLayout						//获取输入布局
);

顶点缓冲区(Vertex Buffer)

  • 顶点缓冲区把顶点数组以ID3D11Buffer的形式提供给输入装配阶段,即需要把顶点数组存储到顶点缓冲区

创建顶点缓冲区

描述缓冲区

typedef struct D3D11_BUFFER_DESC
{
	UINT ByteWidth;				//数据字节数
	D3D11_USAGE Usage;			//CPU和GPU读写权限
	UINT BindFlags;				//缓冲区类型标志
	UINT CPUAccessFlags;		//CPU读写权限指定
	UINT MiscFlags;				//忽略
	UINT StructureByteStride;	//忽略
};

//初始化三角形三个点的位置和颜色,顺时针选择点
VertexPosColor vertices[] = 
{
	
	{XMFLOAT3(0.0f,0.5f,0.5f),XMFLOAT4(0.0f,1.0f,0.0f,1.0f)}, //上顶点,颜色绿色 不透明
	{XMFLOAT3(0.5f,-0.5f,0.5f),XMFLOAT4(0.0f,0.0f,1.0f,1.0f)}, //右顶点,颜色蓝色 不透明
	{XMFLOAT3(-0.5f,-0.5f,0.5f),XMFLOAT4(1.0f,1.0f,0.0f,1.0f)}, //左顶点,颜色红色 不透明
};

D3D11_BUFFER_DESC vbd;
ZeroMemory(&vbd,sizeof(vbd));
vbd.ByteUsage = D3D11_USAGE_IMMUTABLE; //GPU只读,CPU不可写也不可读
vbd.ByteWidth = sizeof(vertices);
vbd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vbd.CPUACCESSFlags = 0;

指定初始化数据

D3D11_SUBRESOURCE_DATA initData;
ZeroMemory(&initData,sizeof(initData));
initData.pSysMem = vertices;

创建缓冲区

将顶点数据以缓冲区的形式提供给输入装配阶段

//描述顶点缓冲区
//指定数据
//获得顶点缓冲区
d3dDevice->CreateVertexBuffer(&vbd,&initData,mVertexShader->GetAddressOf());

Device Context阶段

装配阶段设置缓冲区,即布置到渲染管道

注意: 类似给渲染管线绑定资源的一切方法,即Set方法,在绑定之后就会一直生效,而不是说仅能够使用一次。所以,以后如果你需要用别的特效去绘制当前物体,就要重新绑定好渲染管线所需要的一切资源。

void ID3D11DeviceContext::IASetVertexBuffers(
	UINT StartSlot,			//输入槽索引
	UINT NumBuffers,		//缓冲区数目
	ID3D11Buffer *const *ppVertexBuffers,	//指向缓冲区数组的指针
	const UINT* pStrides,		//一个数组,规定了对所有缓冲区每次读取的字节数分别是多少
	const UINT* pOffsets		//数组,规定了对所有缓冲区的初始字节偏移量
);

UINT stride = sizeof(VertexPosColor);
UINT offset = 0;
d3dImmediateDevice->IASetVertexBuffers(0,1,mVertexBuffer.GetAddressOf(),&stride,&offset);

只要绘制的内容不变,只需要设置一次,如果绘制不同的内容或者效果,需要重新设置

输入装配阶段设置图元类型

void ID3D11DeviceContext::IASetPrimitivetopology(D3D11_PRIMITIVE_TOPOLOGY topology);

给渲染管线某一个着色器阶段设置对应的着色器

void ID3D11DeviceContext::VSSetShader(
	ID3D11VertexShader* pVertexShader, //着色器
	ID3D11ClassInstance* const*ppClassInstance,
	UINT NumClassInstance
);

总结

创建着色器

  • 编译顶点着色器(D3DCompileFromFile)
  • 创建顶点着色器(CreateVetexShader)
  • 建立C++与HLSL中的对应关系,又叫创建并绑定顶点布局(CreateInputLayout)
  • 编译像素着色器,ID3DBlob释放掉之前顶点着色器,在获取新的字节码(D3DCompileFromFile)
  • 创建像素着色器(CreatePixelShader)

创建资源

  • 创建顶点缓冲区 - 将顶点数据以缓冲区的形式提供给输入装配阶段
    • 设置三角性三个顶点坐标(vertices[3])
    • 描述缓冲区: 设置内存大小,sizeof(vertices); D3D11_BUFFER_DESC(Buffer Description)
    • 指定要用的初始化数据vertices,即三角形三个顶点的实际数据;D3D11_SUBRESOURCE_DATA(子资源数据)
    • 最后创建缓冲区(CreateBuffer)
  • 渲染管线输入装配阶段设置顶点缓冲区 (IASetVertexBuffers)
  • 输入装配装配阶段设置图元类型 (IASetPrimitiveTopology)
  • 输入装配阶段设置输入布局 (IASetInputLayout)
  • 给渲染管线某一个着色器阶段设置对应的着色器 (VSSetShader,PSSetShader)
  • 绘图 (Draw/DrawIndexed)

XMFloat3类

DirectX命名空间下的一个结构体,一个很简单向量类

struct XMFLOAT3
{
    float x;
    float y;
    float z;

    XMFLOAT3() = default;

    XMFLOAT3(const XMFLOAT3&) = default;
    XMFLOAT3& operator=(const XMFLOAT3&) = default;

    XMFLOAT3(XMFLOAT3&&) = default;
    XMFLOAT3& operator=(XMFLOAT3&&) = default;

    XM_CONSTEXPR XMFLOAT3(float _x, float _y, float _z) : x(_x), y(_y), z(_z) {}
    explicit XMFLOAT3(_In_reads_(3) const float *pArray) : x(pArray[0]), y(pArray[1]), z(pArray[2]) {}
};

跨平台的考量

//可以替换XMFLoat3
struct Vector3 {
		float x;
		float y;
		float z;
		Vector3(float x1,float y1,float z1):x(x1),y(y1),z(z1) {}
	};

后续补充

画一个矩形用六个点

顺时针选点画图

//   V0,V1,V2 第一个三角形
// 	 V1,V3,V2 第二个三角形
//   V0                   V1
//    ---------------------
//    |                   |
//    |                   |
//    |                   |
//    ---------------------
//   V2                   V3
VertexPosColor vertices[] =
{
	{ XMFLOAT3(-0.5f, 0.5f, 0.5f), XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f) },
	{ XMFLOAT3(0.5f, 0.5f, 0.5f), XMFLOAT4(0.0f, 0.0f, 1.0f, 1.0f) },
	{ XMFLOAT3(-0.5f, -0.5f, 0.5f), XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f) },

	{ XMFLOAT3(0.5f, 0.5f, 0.5f), XMFLOAT4(0.0f, 0.0f, 1.0f, 1.0f) },
	{ XMFLOAT3(0.5f, -0.5f, 0.5f), XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f) },
	{ XMFLOAT3(-0.5f, -0.5f, 0.5f), XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f) }
};
	
//图元类型为 Triangle List
m_pd3dImmediateContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

画一个矩形用四个点

//   V0                   V1
//    ---------------------
//    |                   |
//    |                   |
//    |                   |
//    ---------------------
//   V2                   V3
VertexPosColor vertices[] =
	{
		{ XMFLOAT3(-0.5f, 0.5f, 0.5f), XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f) },
		{ XMFLOAT3(0.5f, 0.5f, 0.5f), XMFLOAT4(0.0f, 0.0f, 1.0f, 1.0f) },
		{ XMFLOAT3(-0.5f, -0.5f, 0.5f), XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f) },
		{ XMFLOAT3(0.5f, -0.5f, 0.5f), XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f) }

	};

//需要更改图元类型为Triangle strip
m_pd3dImmediateContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);

两个顶点缓冲区分别绘制不同的物体

两个顶点缓冲区

	ComPtr<ID3D11Buffer> m_pVertexBuffer1;		// 顶点缓冲区1
	ComPtr<ID3D11Buffer> m_pVertexBuffer2;		// 顶点缓冲区2
	

顶点缓冲区存储不同的坐标

// 矩形6点坐标
	VertexPosColor vertices[] =
	{
		{ XMFLOAT3(-0.5f, 0.5f, 0.5f), XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f) },
		{ XMFLOAT3(0.5f, 0.5f, 0.5f), XMFLOAT4(0.0f, 0.0f, 1.0f, 1.0f) },
		{ XMFLOAT3(-0.5f, -0.5f, 0.5f), XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f) },

		{ XMFLOAT3(0.5f, 0.5f, 0.5f), XMFLOAT4(0.0f, 0.0f, 1.0f, 1.0f) },
		{ XMFLOAT3(0.5f, -0.5f, 0.5f), XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f) },
		{ XMFLOAT3(-0.5f, -0.5f, 0.5f), XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f) }


	};

	// 设置顶点缓冲区描述
	D3D11_BUFFER_DESC vbd;
	ZeroMemory(&vbd, sizeof(vbd));
	vbd.Usage = D3D11_USAGE_IMMUTABLE;
	vbd.ByteWidth = sizeof vertices;
	vbd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	vbd.CPUAccessFlags = 0;
	// 新建顶点缓冲区
	D3D11_SUBRESOURCE_DATA InitData;
	ZeroMemory(&InitData, sizeof(InitData));
	InitData.pSysMem = vertices; //指向数组第一个元素
	HR(m_pd3dDevice->CreateBuffer(&vbd, &InitData, m_pVertexBuffer1.GetAddressOf()));

	// 设置三角形顶点
	VertexPosColor vertices2[] =
	{
		{ XMFLOAT3(0.5f, 0.5f, 0.5f), XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f) },
		{ XMFLOAT3(0.8f, 0.5f, 0.5f), XMFLOAT4(0.0f, 0.0f, 1.0f, 1.0f) },
		{ XMFLOAT3(0.5f, -0.5f, 0.5f), XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f) }

	};
	// 设置顶点缓冲区描述,可以共享之前的顶点描述,只需要更改顶点占用空间与实际数据
	vbd.ByteWidth = sizeof vertices2;
	InitData.pSysMem = vertices2;
	HR(m_pd3dDevice->CreateBuffer(&vbd, &InitData, m_pVertexBuffer2.GetAddressOf()));

后期将会使用vector容器存储顶点数据,而不是普通的数组。

注意点

因为渲染管线中各个部分的设置方法一经调用就会立即生效。然而如果需要绘制不同的内容或者效果,则需要在绘制前给渲染管线绑定好各种所需的资源

例子1:设置顶点缓冲区方法 IASetVertexBuffer

//输入装配阶段的顶点缓冲区设置
	UINT stride = sizeof(VertexPosColor);	// 跨越字节数
	UINT offset = 0;						// 起始偏移量

	m_pd3dImmediateContext->IASetVertexBuffers(0, 1, m_pVertexBuffer1.GetAddressOf(), &stride, &offset);
	m_pd3dImmediateContext->Draw(6, 0); //由于立即生效,所以需要再下次设置之前,画出矩阵

	m_pd3dImmediateContext->IASetVertexBuffers(0, 1, m_pVertexBuffer2.GetAddressOf(), &stride, &offset);
	m_pd3dImmediateContext->Draw(3, 0);

例子2:图元类型 IASetPrimitiveTopology

	//新设置图元类型 Line Strip
	m_pd3dImmediateContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP);
	m_pd3dImmediateContext->IASetVertexBuffers(0, 1, m_pVertexBuffer1.GetAddressOf(), &stride, &offset);
	m_pd3dImmediateContext->Draw(6, 0);
	
	m_pd3dImmediateContext->IASetVertexBuffers(0, 1, m_pVertexBuffer2.GetAddressOf(), &stride, &offset);
	m_pd3dImmediateContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); 	//Triangle List, 放在Draw之前
	m_pd3dImmediateContext->Draw(3, 0);

结果:
图元类型不一样

渲染过程(图解)

现在用到的渲染管道不多,主要是输入装配阶段,顶点着色器阶段,像素着色器阶段
简单的渲染过程

D3DDevice 创建

先编译hlsl文件后创建着色器,顶点布局,顶点缓冲区,创建顺序无所谓
Vertex Shader
Input Layout
Pixel Shader
Vertex Buffer

D3DDeviceContext

绑定或者设置上面创建好的着色器,输入布局,缓冲区到渲染管道的不同阶段:
Set Parts

Introduction to Game Programming with DirectX 11
GitHub源码链接(感谢X_Jun)

猜你喜欢

转载自blog.csdn.net/weixin_44200074/article/details/106425950