DirectX 3D 之C 开发

DirectX 3D是Microsoft DirectX API中的一部分,它是一个集成了2D和3D绘制功能的多媒体API。它通过提供各种图形或多媒体功能,为开发人员提供了一套强大的工具来开发基于Windows平台的游戏和其他交互式的应用程序。

下面我们就来详细介绍如何使用DirectX 3D进行C语言开发,并通过案例说明其应用。

1. DirectX 3D 的基础概念

在开始学习 DirectX 3D 的 C 开发之前,我们先介绍一些基础概念。

1.1 DirectX 3D 的对象模型

DirectX 3D 的对象模型通常包含以下几个部分:

- 图形设备:该对象代表允许你在平台上进行图形操作的硬件设备,它可以是一个GPU(图形处理单元)或者一个软件渲染器。

- 设备上下文:该对象代表了你进行图形操作时所需要的状态信息和一些辅助信息的集合,这些状态和辅助信息都会保存在该上下文对象中。

- 顶点缓冲区对象:该对象是将流经它的几何数据进行缓存的对象。通常情况下,我们的几何数据都是由一些顶点和索引数组来表示的,这些数据会被拿来构建物体并渲染出来。

- 纹理对象:该对象是用于存储纹理数据的对象,纹理数据是指建立在一个特定的几何体表面上的图像数据。如果你所构建的几何数据需要进行纹理贴图操作的话,那么你就需要使用纹理对象了。

- 渲染目标对象:该对象代表你要将渲染的输出数据渲染到哪个平面上。在 Direct3D 中,你可以将你的输出数据渲染到一个纹理表面上,或是直接渲染到屏幕上。

1.2 顶点定义和索引缓冲区

你现在需要知道的一个重要概念是顶点定义和索引缓冲区,这也是在 Direct3D 中表示几何数据的标准方式。在 Direct3D 中,一个三角形(或任意多边形)可以通过一组三个顶点来定义。每个顶点都是由一个三维坐标和一些附加信息(如法线、纹理坐标等)组成的。

而索引缓冲区是指一个数组,该数组的每个元素都指向顶点缓冲区中的一个顶点。使用索引缓冲区可以显着地减少在顶点数组中存储和绘制相同顶点的重复数据。

1.3 渲染管道

图形或多媒体数据经过渲染管道(Pipeline)之后,才会最终被显示出来。渲染管道可以从图形入手,将这些图形经过多个阶段的处理和变换、绘制、填充等等操作后,最终输出给显示器。

Direct3D 中的渲染管道是有三个主要部分组成的:输入装配、顶点着色器和像素着色器。放置在输入装配之前的是一些用于转换几何体位置和大小以及光源和摄像机位置的变换对象。

2. 在Visual Studio中创建 Direct3D 应用程序

首先,我们需要在Visual Studio 2017/2019中安装 DirectX SDK。在此之后,打开Visual Studio并单击“新建项目”按钮,选择“Win32 控制台应用程序”。接着,给你的项目一个名称,然后单击“确定”按钮。

在下一个屏幕上,选择“空项目”并确保选中了“下列一个或多个模板”。

在新建项目之后,我们要将 DirectX SDK 添加到我们的项目中。打开解决方案资源管理器,右键单击项目,在上下文菜单中选择“属性”,在属性页中选择“VC ++属性”,然后选择“VC ++目录”并单击“编辑”按钮。

在弹出的“编辑用户Macros”窗口中,为DXSDK\_DIR添加一个新的用户宏,用于保存 DirectX SDK 的安装路径,例如:$(ProjectDir)\bin\d3dx11.

现在,我们已经配置了DirectX SDK,接下来,我们要添加 DirectX 库和头文件,并在程序中进行引用。

首先,在解决方案资源管理器中,选择我们项目中的“源文件”文件夹,并在此文件夹下添加一个新的 C++ 文件。该文件将是我们用于编写 Direct3D 应用程序的主文件,现在,我们在其中添加以下代码:

#include

#include

#include

#include

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)

{

HWND hWnd;

WNDCLASSEX wc;

ZeroMemory(&wc, sizeof(WNDCLASSEX));

wc.cbSize = sizeof(WNDCLASSEX);

wc.style = CS_HREDRAW | CS_VREDRAW;

wc.lpfnWndProc = WndProc;

wc.hInstance = hInstance;

wc.hCursor = LoadCursor(NULL, IDC_ARROW);

wc.lpszClassName = L"WindowClass";

RegisterClassEx(&wc);

RECT wr = {0, 0, 800, 600};

AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE);

hWnd = CreateWindowEx(NULL, L"WindowClass", L"Our Direct3D Window", WS_OVERLAPPEDWINDOW, 300, 300, wr.right - wr.left, wr.bottom - wr.top, NULL, NULL, hInstance, NULL);

ShowWindow(hWnd, nCmdShow);

return 0;

}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

{

switch (message)

{

case WM_DESTROY:

PostQuitMessage(0);

break;

default:

return DefWindowProc(hWnd, message, wParam, lParam);

}

return 0;

}

上述代码创建了一个 Windows 窗口并使用 Direct3D 引擎来进行渲染。我们已经做好了解决方案的初始设置,我们现在要开始进行在 Direct3D 中实现 2D 和 3D 图形的编程。

3. 使用DirectX 3D在C语言中进行2D和3D图形编程

3.1 创建设备

使用 Direct3D API,我们首先需要创建 Direct3D 设备,这样我们才能够实际地绘制图形。Direct3D 设备是一个实际的物理设备,它可以是硬件设备(如显卡)或者软件渲染器,为了创建设备,你需要用户计算机上的一个 Direct3D 适配器,或者称为显卡。接下来,我们来看一个使用 Direct3D SDK 创建设备的示例代码:

void InitD3D(HWND hWnd)

{

// 创建一个 DXGI 工厂对象

IDXGIFactory *factory;

CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)&factory);

// 创建一个 DXGI 适配器对象

IDXGIAdapter *adapter;

factory->EnumAdapters(0, &adapter);

// 创建 Direct3D 设备和设备上下文对象

D3D11CreateDevice(

adapter, // DirectX 适配器(显卡)

D3D_DRIVER_TYPE_UNKNOWN, // 指定渲染驱动器类型

NULL, // 填充默认特性级别

NULL, // 填充默认特性级别

NULL, // 特性级别数组大小

0, // 特性级别数组大小

D3D11_SDK_VERSION, // Direct3D SDK 版本

&device, // 输出 Direct3D 设备对象

NULL, // 输出 D3D_FEATURE_LEVEL(特性级别)

&deviceContext); // 输出 Direct3D 设备上下文对象

// 释放不再需要的适配器和工厂对象

adapter->Release();

factory->Release();

// 取得 Direct3D 处理器的 Direct3D 11 渲染目标视图

ID3D11Texture2D* pBackBuffer;

swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer);

device->CreateRenderTargetView(pBackBuffer, NULL, &picpixel);

pBackBuffer->Release();

// 设置渲染目标视图

deviceContext->OMSetRenderTargets(1, &picpixel, NULL);

}

3.2 渲染图形

渲染图形使用 Direct3D 11,我们需要把图形放入特定的顶点缓冲区中,调用 Direct3D 方法进行绘制。

以下是一个使用 Direct3D SDK 渲染三角形的简单示例:

// 顶点数据

D3D11_BUFFER_DESC bd;

ZeroMemory(&bd, sizeof(bd));

bd.Usage = D3D11_USAGE_DYNAMIC;

bd.ByteWidth = sizeof(Vertex) * 3;

bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;

bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;

device->CreateBuffer(&bd, NULL, &vertexBuffer);

// 定义顶点数据

Vertex vertices[] =

{

{ 0.0f, 0.5f, 0.0f },

{ 0.45f, -0.5f, 0.0f },

{ -0.45f, -0.5f, 0.0f }

};

// 将顶点数据复制到顶点缓冲区中

D3D11_MAPPED_SUBRESOURCE ms;

deviceContext->Map(vertexBuffer, NULL, D3D11_MAP_WRITE_DISCARD, NULL, &ms);

memcpy(ms.pData, vertices, sizeof(vertices));

deviceContext->Unmap(vertexBuffer, NULL);

// 将缓冲区激活到渲染管线中

UINT stride = sizeof(Vertex);

UINT offset = 0;

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

// 设置拓扑类型

deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

// 绘制顶点缓存

deviceContext->Draw(3, 0);

3.3 纹理贴图

我们还可以添加纹理贴图来使用 Direct3D 渲染更复杂的场景。在 Direct3D 中创建纹理贴图的示例代码如下:

// 加载纹理图片

D3DX11CreateShaderResourceViewFromFile(device, "texture.jpg", NULL, NULL, &texture, NULL);

// 设置着色器在渲染时使用的纹理贴图

deviceContext->PSSetShaderResources(0, 1, &texture);

// 渲染顶点缓冲区

deviceContext->Draw(3, 0);

4. 案例说明

下面我们将介绍一个使用 DirectX 3D 在 C 中实现的 3D 小球游戏,它主要涉及以下内容:

- 如何通过设备和设备上下文对象创建 Direct3D 设备。

- 如何使用缓冲区和索引缓冲区表示 3D 几何体。

- 如何设置仪器和光照来影响 3D 几何体的外观。

- 如何通过 Direct3D 11 渲染管道来渲染 3D 几何体。

该小球游戏的源代码可以在 Github 上找到,下面是一些关键代码的解释:

创建设备

该游戏使用 D3D11CreateDevice() 函数将适配器、驱动器类型和特性级别作为参数,然后通过将传递的参数与 Direct3D SDK 关联,创建一个 Direct3D 渲染设备和一个设备上下文对象。

D3D_FEATURE_LEVEL featureLevels[] =

{

D3D_FEATURE_LEVEL_11_0,

D3D_FEATURE_LEVEL_10_1,

D3D_FEATURE_LEVEL_10_0

};

// 创建 Direct3D 设备和设备上下文对象

D3D11CreateDevice(

adapter, // DirectX 适配器(显卡)

D3D_DRIVER_TYPE_UNKNOWN, // 指定渲染驱动器类型

NULL, // 填充默认特性级别

NULL, // 填充默认特性级别

featureLevels, // 特性级别数组

ARRAYSIZE(featureLevels), // 特性级别数组大小

D3D11_SDK_VERSION, // Direct3D SDK 版本

&device, // 输出 Direct3D 设备对象

&selectedFeatureLevel, // 输出 D3D_FEATURE_LEVEL(特性级别)

&deviceContext); // 输出 Direct3D 设备上下文对象

初始化缓冲区及索引缓冲区

该游戏使用 DXGI_MODE_ROTATION_ROTATE90\_DEG,创建顶点缓冲区和索引缓冲区,并将 3D 网格编码到该缓冲区中,这样它们就可以在游戏开始时快速加载。

初始化顶点缓冲区和索引缓冲区的代码如下:

// 3D网格、索引和顶点数量

numMeshes = 1;

numIndices = sizeof(indices) / sizeof(WORD);

numVertices = sizeof(vertices) / sizeof(Vertex);

// 设置缓冲区描述符

D3D11_BUFFER_DESC bufferDesc;

ZeroMemory(&bufferDesc, sizeof(bufferDesc));

bufferDesc.Usage = D3D11_USAGE_DEFAULT;

bufferDesc.ByteWidth = sizeof(Vertex) * numVertices;

bufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;

// 设置子资源数据描述符

D3D11_SUBRESOURCE_DATA initData;

ZeroMemory(&initData, sizeof(initData));

initData.pSysMem = vertices;

// 创建顶点缓冲区

device->CreateBuffer(&bufferDesc, &initData, &vertexBuffer);

// 设置缓冲区描述符

ZeroMemory(&bufferDesc, sizeof(bufferDesc));

bufferDesc.Usage = D3D11_USAGE_DEFAULT;

bufferDesc.ByteWidth = sizeof(WORD) * numIndices;

bufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;

// 设置子资源数据描述符

ZeroMemory(&initData, sizeof(initData));

initData.pSysMem = indices;

// 创建索引缓冲区

device->CreateBuffer(&bufferDesc, &initData, &indexBuffer);

设置水平仪及光照

该游戏使用采样器和顶点着色器创建混合着色器效果以支持光照,并使用不同的光源类型和颜色来影响小球的外观。

// 设置采样器描述符

D3D11_SAMPLER_DESC samplerDesc;

ZeroMemory(&samplerDesc, sizeof(samplerDesc));

samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;

samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;

samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;

samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;

samplerDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;

samplerDesc.MinLOD = 0;

samplerDesc.MaxLOD = D3D11_FLOAT32_MAX;

// 创建采样器状态对象

device->CreateSamplerState(&samplerDesc, &samplerState);

// 设置 Shader Resources

deviceContext->PSSetShaderResources(0, 1, &textureView);

// 设置光源属性

constantBufferData.lightVector = XMFLOAT3(-2.0f, 1.0f, -2.0f);

constantBufferData.diffuseColor = XMFLOAT4(0.6f, 0.6f, 0.9f, 1.0f 如果你喜欢我们三七知识分享网站的文章, 欢迎您分享或收藏知识分享网站文章 欢迎您到我们的网站逛逛喔!https://www.37seo.cn/

点赞(9) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿
发表
评论
返回
顶部