要在DX上绘制一个基本图形,大体流程有以下几步

  1. 给出输入布局(主要是描述顶点的格式)
  2. 给出描述几何体的顶点,索引,拓扑图元
  3. 加载并创建各种着色器
  4. 创建索引缓冲,顶点缓冲,以及必要的常量缓冲
  5. 将以上内容绑定到渲染管线,然后绘制

主要针对于步骤2,绘制什么样的几何体应该需要做一下封装,我希望能够用一个结构体描述顶点,通过不同的函数为结构体填充顶点信息,然后根据结构体创建顶点缓冲,索引缓冲。

如下,定义一个结构体Shape3D

struct Shape3DVertex;
struct Shape3D;
#include "AppPublic.h"
struct Shape3DVertex	// 顶点的结构
	DirectX::XMFLOAT3 Pos;    // 仅描述顶点的坐标
struct Shape3D	// 图形的结构体
	Shape3DVertex* VertexArrPtr;
	UINT VertexNum;
	WORD* IndexArrPtr;
	UINT IndexNum;
	~Shape3D()
		delete[] VertexArrPtr;
		delete[] IndexArrPtr;

填充几何体的顶点以及索引

这里分别演示如何创建长方体,圆柱,球体和正棱锥四种图形。声明下面函数

// 创建长方体
bool Shape3DCreateCuboid(float length, float width, float height, Shape3D* pShape);
// 创建圆柱
// diff 用于描述用正 n 边形拟合圆柱,其中 n = diff
bool Shape3DCreateCylinder(float radius, float height, UINT diff, Shape3D* pShape);
// 创建一个球
// radius: 半径
// longitude: 经线的数量
// latitude: 纬线的数量(不包含两极)
// 本算法可优化,做出 1/8 球面的顶点,然后利用对称性
bool Shape3DCreateBall(float radius, UINT longitude, UINT latitude, Shape3D* pShape);
// 创建一个正棱锥图形
bool Shape3DCreateRegularPyramid(float radius, float height, UINT sideNum, Shape3D* pShape);

接着是函数实现

bool Shape3DCreateCuboid(float length, float width, float height, Shape3D * pShape)
	if (nullptr == pShape)
		return false;
	float x = length / 2;
	float y = width / 2;
	float z = height / 2;
	DirectX::XMFLOAT3 poses[] =
		DirectX::XMFLOAT3(-x, +y, +z),
		DirectX::XMFLOAT3(+x, +y, +z),
		DirectX::XMFLOAT3(+x, -y, +z),
		DirectX::XMFLOAT3(-x, -y, +z),
		DirectX::XMFLOAT3(-x, +y, -z),
		DirectX::XMFLOAT3(+x, +y, -z),
		DirectX::XMFLOAT3(+x, -y, -z),
		DirectX::XMFLOAT3(-x, -y, -z),
	WORD indexes[] =
		3, 1, 0,
		2, 1, 3,
		0, 5, 4,
		1, 5, 0,
		3, 4, 7,
		0, 4, 3,
		1, 6, 5,
		2, 6, 1,
		2, 7, 6,
		3, 7, 2,
		6, 4, 5,
		7, 4, 6,
	pShape->IndexNum = ARRAYSIZE(indexes);
	pShape->VertexNum = ARRAYSIZE(poses);
	pShape->IndexArrPtr = new WORD[pShape->IndexNum];
	pShape->VertexArrPtr = new Shape3DVertex[pShape->VertexNum];
	// fill index array
	memcpy(pShape->IndexArrPtr, indexes, sizeof(WORD)*pShape->IndexNum);
	// fill vertex array
	for (UINT i = 0; i < pShape->VertexNum; i++)
		pShape->VertexArrPtr[i] = { poses[i] };
	return true;
bool Shape3DCreateCylinder(float r, float h, UINT n, Shape3D * pShape)
	if (nullptr == pShape)
		return false;
	if (n < 6) n = 6;
	if (n > 64) n = 64;
	pShape->VertexNum = (n + 1) << 1; // n*2+2 个顶点
	pShape->VertexArrPtr = new Shape3DVertex[pShape->VertexNum];
	pShape->IndexNum = (n << 2) * 3; // n*4个三角形
	pShape->IndexArrPtr = new WORD[pShape->IndexNum];
	const float _2pi_div_n = DirectX::XM_PI * 2 / n;
	const float _h_div_2 = h / 2;
	float sint = 0.0f, cost = 0.0f;
	// 填充顶点数组
	for (UINT i = 0; i < n; i++)	// 两个底面圆周的顶点
		sint = sin(_2pi_div_n * i) * r;
		cost = cos(_2pi_div_n * i) * r;
		pShape->VertexArrPtr[(i << 1) + 0] = { {cost, sint, +_h_div_2} };
		pShape->VertexArrPtr[(i << 1) + 1] = { {cost, sint, -_h_div_2} };
	pShape->VertexArrPtr[(n << 1) + 0] = { {0.0f, 0.0f, +_h_div_2 } }; // 两个底面的中心
	pShape->VertexArrPtr[(n << 1) + 1] = { {0.0f, 0.0f, -_h_div_2 } };
	// 填充索引数组
	UINT _index_seq[] =
		1, 2, 0,
		3, 2, 1
	for (UINT i = 0; i < n; i++)
		for (int j = 0; j < 6; j++)
			pShape->IndexArrPtr[i * 6 + j] = (_index_seq[j] + i * 2) % (n << 1);
		pShape->IndexArrPtr[(n + i) * 6 + 0] = n * 2 + 0;
		pShape->IndexArrPtr[(n + i) * 6 + 1] = (i * 2 + 0) % (n << 1);
		pShape->IndexArrPtr[(n + i) * 6 + 2] = (i * 2 + 2) % (n << 1);
		pShape->IndexArrPtr[(n + i) * 6 + 3] = n * 2 + 1;
		pShape->IndexArrPtr[(n + i) * 6 + 4] = (i * 2 + 3) % (n << 1);
		pShape->IndexArrPtr[(n + i) * 6 + 5] = (i * 2 + 1) % (n << 1);
	return true;
bool Shape3DCreateBall(float radius, UINT longitude, UINT latitude, Shape3D * pShape)
	if (nullptr == pShape)
		return false;
	if (longitude < 16) longitude = 16;
	if (latitude < 8) latitude = 8;
	UINT n = longitude * latitude;
	// 顶点数量 = 经纬线交点 + 2个极点
	pShape->VertexNum = n + 2;
	pShape->IndexNum = n * 6;
	pShape->VertexArrPtr = new Shape3DVertex[pShape->VertexNum];
	pShape->IndexArrPtr = new WORD[pShape->IndexNum];
	auto vptr = pShape->VertexArrPtr;
	// 纬线变化的角度(纬度)增量
	// 因为 n 条纬线(不含极点)会将球分割成 n+1 个带状部分
	// 纬度变化从一个极点到另一个极点,可以认为跨越的角度是 180°(π)
	const float delta_lati = DirectX::XM_PI / (latitude + 1);
	// 类似于圆柱,每条纬线要被经线分割
	// 纬线是一个圆周,跨越角度是360°(2π)
	const float delta_long = DirectX::XM_PI * 2 / (longitude);
	for (UINT i = 0; i < latitude; i++)	// 求出每条纬线上的交点
		float radius_i = sin((i + 1)*delta_lati)*radius;	// 经线的半径
		float zval_i = cos((i + 1)*delta_lati)*radius;	// 纬线的半径
		for (UINT j = 0; j < longitude; j++)
			float cost = cos(j * delta_long);
			float sint = sin(j * delta_long);
			vptr[i*longitude + j] = { {cost*radius_i, sint*radius_i, zval_i} };
	vptr[n + 0] = { { 0.0f, 0.0f, +radius } };
	vptr[n + 1] = { { 0.0f, 0.0f, -radius } };
	auto iptr = pShape->IndexArrPtr;
	for (UINT i = 0; i < latitude - 1; i++)
		for (UINT j = 0; j < longitude; j++)
			int x0 = j;
			int x1 = (j + 1) % longitude;
			int y0 = i;
			int y1 = i + 1;
			iptr[0] = y0 * longitude + x0;
			iptr[1] = y1 * longitude + x0;
			iptr[2] = y0 * longitude + x1;
			iptr[3] = y0 * longitude + x1;
			iptr[4] = y1 * longitude + x0;
			iptr[5] = y1 * longitude + x1;
			iptr += 6;
	for (UINT i = 0; i < longitude; i++)
		iptr[0] = n + 0;
		iptr[1] = i;
		iptr[2] = (i + 1) % longitude;
		iptr[3] = n + 1;
		iptr[4] = (i + 1) % longitude + (longitude) * (latitude - 1);
		iptr[5] = i + (longitude) * (latitude - 1);
		iptr += 6;
	return true;
bool Shape3DCreateRegularPyramid(float radius, float height, UINT sideNum, Shape3D * pShape)
	if (nullptr == pShape)
		return false;
	if (sideNum < 3) sideNum = 3;	// 最小必须是正三棱锥
	pShape->VertexNum = sideNum + 2;
	pShape->IndexNum = sideNum * 2 * 3;	// 侧面和底面
	pShape->VertexArrPtr = new Shape3DVertex[pShape->VertexNum];
	pShape->IndexArrPtr = new WORD[pShape->IndexNum];
	const float _2pi_div_side_num = 2 * DirectX::XM_PI / sideNum;
	for (UINT i = 0; i < sideNum; i++)
		float cost = cos(i * _2pi_div_side_num) * radius;
		float sint = sin(i * _2pi_div_side_num) * radius;
		pShape->VertexArrPtr[i] = { {cost, sint, 0.0f} };
	pShape->VertexArrPtr[sideNum + 0] = { {0.0f, 0.0f, height} };
	pShape->VertexArrPtr[sideNum + 1] = { {0.0f, 0.0f, 0.0f} };
	auto iptr = pShape->IndexArrPtr;
	for (UINT i = 0; i < sideNum; i++)
		UINT p0 = i;
		UINT p1 = (i + 1) % sideNum;
		iptr[0] = sideNum + 0;
		iptr[1] = p0;
		iptr[2] = p1;
		iptr[3] = sideNum + 1;
		iptr[4] = p1;
		iptr[5] = p0;
		iptr += 6;
	return true;

创建渲染管线

定义 D3DGeometryTestApp 的类,继承D3DApp

struct CBuffer;
class D3DGeometryTestApp;
#include "D3DApp.h"
using namespace DirectX;
struct CBuffer
	XMMATRIX world;
	XMMATRIX view;
class D3DGeometryTestApp : public D3DApp
private:
	ID3D11InputLayout* m_pInputLayout;
	ID3D11VertexShader* m_pVertexShader;
	ID3D11GeometryShader* m_pGeometryShader;
	ID3D11PixelShader* m_pPixelShader;
	ID3D11Buffer* m_pVertexBuffer;
	ID3D11Buffer* m_pIndexBuffer;
	ID3D11Buffer* m_pConstBuffer;
	UINT m_IBCount;
public:
	D3DGeometryTestApp(HINSTANCE hInstance, HWND hWnd);
	virtual ~D3DGeometryTestApp();
	virtual void Update() override;
	virtual void Render() override;
D3DGeometryTestApp::D3DGeometryTestApp(HINSTANCE hInstance, HWND hWnd)
	:D3DApp(hInstance, hWnd)
	HRESULT hr;
	ID3DBlob* pVSBlob = nullptr;
	ID3DBlob* pGSBlob = nullptr;
	ID3DBlob* pPSBlob = nullptr;
	LPCWSTR filename = L"geomtry.fx";
	hr = ShaderUtil::CompileFromFile(filename, "VS", "vs_4_0", &pVSBlob);
	if (FAILED(hr))
		EXCEPT("Error at compile VS");
	// VS
	hr = m_pD3dDevice->CreateVertexShader(
		pVSBlob->GetBufferPointer(),
		pVSBlob->GetBufferSize(),
		nullptr,
		&m_pVertexShader
	if (FAILED(hr))
		EXCEPT("Error at create VS");
	// PS
	hr = ShaderUtil::CompileFromFile(filename, "PS", "ps_4_0", &pPSBlob);
	if (FAILED(hr))
		EXCEPT("Error at compile PS");
	hr = m_pD3dDevice->CreatePixelShader(
		pPSBlob->GetBufferPointer(),
		pPSBlob->GetBufferSize(),
		nullptr,
		&m_pPixelShader
	if (FAILED(hr))
		EXCEPT("Error at create PS");
	// InputLayout
	D3D11_INPUT_ELEMENT_DESC inputDescs[] =
		{ "POS", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
	UINT numDesc = ARRAYSIZE(inputDescs);
	hr = m_pD3dDevice->CreateInputLayout(
		inputDescs,
		numDesc,
		pVSBlob->GetBufferPointer(),
		pVSBlob->GetBufferSize(),
		&m_pInputLayout
	if (FAILED(hr))
		EXCEPT("Error at create input layout");
	Shape3D shape;
//	Shape3DCreateCuboid(0.8f, 0.6f, 0.6f, &shape);
//	Shape3DCreateCylinder(0.5f, 0.4f, 40, &shape);
	Shape3DCreateBall(0.5f, 40, 20, &shape);
//	Shape3DCreateRegularPyramid(0.4f, 0.4f, 12, &shape);
	// vertex buffer
	D3D11_BUFFER_DESC buffDesc = {};
	D3D11_SUBRESOURCE_DATA initData = {};
	buffDesc.BindFlags = D3D11_BIND_FLAG::D3D11_BIND_VERTEX_BUFFER;
	buffDesc.Usage = D3D11_USAGE::D3D11_USAGE_DEFAULT;
	buffDesc.CPUAccessFlags = 0;
	buffDesc.ByteWidth = sizeof(Shape3DVertex) * shape.VertexNum;
	initData.pSysMem = shape.VertexArrPtr;
	hr = m_pD3dDevice->CreateBuffer(&buffDesc, &initData, &m_pVertexBuffer);
	if (FAILED(hr))
		EXCEPT("Error at create vertex buffer");
	// index buffer
	buffDesc.BindFlags = D3D11_BIND_FLAG::D3D11_BIND_INDEX_BUFFER;
	buffDesc.Usage = D3D11_USAGE::D3D11_USAGE_DEFAULT;
	buffDesc.CPUAccessFlags = 0;
	buffDesc.ByteWidth = sizeof(WORD) * shape.IndexNum;
	initData.pSysMem = shape.IndexArrPtr;
	hr = m_pD3dDevice->CreateBuffer(&buffDesc, &initData, &m_pIndexBuffer);
	if (FAILED(hr))
		EXCEPT("Error at create index buffer");
	m_IBCount = shape.IndexNum;
	// const buffer
	buffDesc.BindFlags = D3D11_BIND_FLAG::D3D11_BIND_CONSTANT_BUFFER;
	buffDesc.Usage = D3D11_USAGE::D3D11_USAGE_DEFAULT;
	buffDesc.CPUAccessFlags = 0;
	buffDesc.ByteWidth = sizeof(CBuffer);
	hr = m_pD3dDevice->CreateBuffer(&buffDesc, nullptr, &m_pConstBuffer);
	if (FAILED(hr))
		EXCEPT("Error at create const buffer");
	if (pVSBlob) pVSBlob->Release();
	if (pGSBlob) pGSBlob->Release();
	if (pPSBlob) pPSBlob->Release();
	ID3D11RasterizerState* pRSState;    // 设置渲染模式为线框
	D3D11_RASTERIZER_DESC rd = {};
	m_pD3dDevice->CreateRasterizerState(&rd, &pRSState);
	rd.CullMode = D3D11_CULL_MODE::D3D11_CULL_BACK;
	rd.FillMode = D3D11_FILL_MODE::D3D11_FILL_WIREFRAME;
	m_pD3dDevice->CreateRasterizerState(&rd, &pRSState);
	m_pImmediateContext->RSSetState(pRSState);
	UINT stride = sizeof(Shape3DVertex);
	UINT offset = 0;
	m_pImmediateContext->IASetInputLayout(m_pInputLayout);
	m_pImmediateContext->IASetVertexBuffers(0, 1, &m_pVertexBuffer, &stride, &offset);
	m_pImmediateContext->IASetIndexBuffer(m_pIndexBuffer, DXGI_FORMAT_R16_UINT, 0);
	m_pImmediateContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
	m_pImmediateContext->VSSetConstantBuffers(0, 1, &m_pConstBuffer);
	m_pImmediateContext->VSSetShader(m_pVertexShader, nullptr, 0);
	m_pImmediateContext->PSSetShader(m_pPixelShader, nullptr, 0);
D3DGeometryTestApp::~D3DGeometryTestApp()
	if (m_pInputLayout) m_pInputLayout->Release();
	if (m_pVertexShader) m_pVertexShader->Release();
	if (m_pGeometryShader) m_pGeometryShader->Release();
	if (m_pPixelShader) m_pPixelShader->Release();
	if (m_pVertexBuffer) m_pVertexBuffer->Release();
	if (m_pIndexBuffer) m_pIndexBuffer->Release();
	if (m_pConstBuffer) m_pConstBuffer->Release();
void D3DGeometryTestApp::Update()
	static float time = 0.0f;
	static CBuffer cb;
	time += 0.0008f;
	cb.world =
		XMMatrixRotationZ(time);
	cb.view =
		XMMatrixLookAtLH({ 2.0f, 0.0f, 1.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 1.0f })*
		XMMatrixPerspectiveFovLH(XM_PIDIV4, 1.33f, 0.01f, 20.f);
	m_pImmediateContext->UpdateSubresource(m_pConstBuffer, 0, nullptr, &cb, 0, 0);
void D3DGeometryTestApp::Render()
	if (!m_pImmediateContext) return;
	m_pImmediateContext->ClearRenderTargetView(m_pRenderTargetView, D3DApp::_bg_color);
	m_pImmediateContext->ClearDepthStencilView(m_pDepthStencilView, D3D11_CLEAR_DEPTH, 1.0f, 0);
	m_pImmediateContext->DrawIndexed(m_IBCount, 0, 0);
	m_pSwapChain->Present(0, 0);

最后还有着色器文件geomtry.fx

cbuffer ConstBuff : register(b0)
    row_major matrix world;
    row_major matrix view;
void VS(
    float3 pos : POS,
    out float4 outPos : SV_Position
    outPos = float4(pos, 1);
    outPos = mul(outPos, world);
    outPos = mul(outPos, view);
void PS(
float4 pos : SV_Position,
    out float4 color : SV_Target
    color = float4(0.6f, 0.6f, 0.6f, 1.0f);    // 简单的单色线框

一个简单的长方体(长宽高设置为相同的值就是正方体了)

一个四棱锥

当n较大时,n棱锥可以用来表示圆锥

注:下面涉及的代码都基于这篇文章的内容 https://blog.csdn.net/Kurozaki_Kun/article/details/86709050绘制过程要在DX上绘制一个基本图形,大体流程有以下几步给出输入布局(主要是描述顶点的格式) 给出描述几何体的顶点,索引,拓扑图元 加载并创建各种着色器 创建索引缓冲,顶点缓冲,以及必要的常量缓冲 将以上内容绑定到渲染管...
锥束CT 几何参数偏差会导致重建图像畸变或图像模糊。针对工程实际中锥束CT 系统初装和应用中的几何参数偏差与漂移,提出了一种改进的基于线框模型的几何参数测量与校正方法。通过对探测器两次不同径向位置的线框模型投影图像进行分析和处理,计算出源-探测器距离及源-旋转中心距离,并确定探测器纵向位置; 通过对模型在两次互为180°的投影图像分析,确定探测器横向位置; 在此基础上,根据模型参数及其投影图像的测量值求解其他几何参数; 最后,用物体投影数据进行带参数重建,获得校正后的重建图像。实验结果表明,该方法能精确求解各几何参数偏差,通过校正可有效减少图像伪影,其主要参数探测器绕其法线旋转角的测量精度可达到0. 03°,探测器沿其行和列方向的平移测量精度分别可达到0. 03 和0. 06 pixel,其抗噪性好精度高。同时,该方法降低了对模型安装精度的严格要求,有较高工程实用价值。
• 练习:xjun 01/02 DirectX11 初始化的习题 • 真作业:用 DX 画一个等六边形 • 申请github账号,用Git管理以后的作业和周记,并上传仓库到Github(推荐使用vs上传),并且要写进文档中,文 档也是评分内容之一 问题描述&解决
资源包含文件:lunwen文档+开题报告+文献综述+外文翻译及原文+答辩PPT+VC源码 本系统主要分为六个模块,声音模块、输入模块、日志模块、数学模块、渲染模块、物体载入模块。这些模块被设计为相互独立,尽量不依赖其它模块。物体载入这一功能勉强算一个模块,这些模块之间的耦合程度,决定了今后进行重用的可能性,模块的功能尽量使其单一化,这样才能在今后独立出使用。 图形的绘制主要会调用DirectX9.0的API来绘制基本的图形。而DirectX9.0基本在windows下,所以本系统在windows下进行开发设计。 详细介绍参考:https://biyezuopin.blog.csdn.net/article/details/125326652
(1)在屏幕中心建立三维坐标系 Oxyz,x 轴水平向右,y 轴铅直向上,z 轴 垂直于屏幕指向观察者。  (2)以三维坐标系 Oxyz 的原点为立方体体心绘制边长为 a 的立方体线框模 (3)使用旋转变换矩阵计算立方体线框模型围绕三维坐标系原点变换前后 的顶点坐标。  (4)使用双缓冲技术在屏幕上绘制三维立方体线框模型的二维正交投影图。  使用键盘方向键旋转立方体线框模型。  (5)使用工具条上的“动画”按钮播放立方体线框模型的旋转动画。  ★ IA——输入组装器(Input Assembler)。用于读取索引和顶点数据并进行组装。 ★ VS——顶点着色器(Vertex Shader)。获取输入的顶点数据,将处理完毕的顶点数据写入下一个阶段。 ★ PA——图元装配器(Pri...