热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Direct-X学习笔记--顶点缓存绘图

DirectX描述物体使用三角形单元,构成三角形的最基本单位是顶点。DirectX中顶点格式是很灵活的,即我们可以自己定义顶点所包含的信息。除了坐标之外,我们还需要定义其他附加属性,颜色

DirectX描述物体使用三角形单元,构成三角形的最基本单位是顶点。

DirectX中顶点格式是很灵活的,即我们可以自己定义顶点所包含的信息。除了坐标之外,我们还需要定义其他附加属性,颜色属性,法线属性等等。

我们在定义的时候,首先要使用DX的一个宏声明一下我们所定义的顶点包含哪些属性。

//------------绘制图形步骤1.定义灵活顶点格式
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE)//坐标为经过变换的屏幕坐标,顶点的颜色

然后我们就可以定义一个包含我们需要的属性的结构体了:

//------------绘制图形步骤2.根据上面定义的顶点格式,创建一个顶点的结构体
struct stVertex
{
D3DXVECTOR4 vPos;//位置坐标
DWORD dwColor;//颜色
};
然后我们需要声明一个指向顶点缓冲区的指针:

//----------绘制图形步骤3.声明一个顶点缓冲区指针
LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL;


接下来,就可以定义各个顶点的属性了,定义一个我们刚才定义的结构体数组,内容为我们需要的顶点的初值。

//----------绘制图形步骤4.定义一个结构体数组用来给每个顶点赋值
//数组中存储当前程序中顶点的数据
stVertex vertex[] =
{
{D3DXVECTOR4(100.0f, 100.0f, 0.0f, 1.0f), D3DCOLOR_XRGB(255,0,0)},
{D3DXVECTOR4(400.0f, 100.0f, 0.0f, 1.0f), D3DCOLOR_XRGB(0,255,0)},
{D3DXVECTOR4(100.0f, 400.0f, 0.0f, 1.0f), D3DCOLOR_XRGB(0,0,255)}
};

然后,我们需要把上面的顶点的值拷贝到顶点缓冲区中,在此之前我们要先为顶点缓冲区申请一个空间:

//----------绘制图形步骤5.为定点缓冲区分配内存,并将数组中的顶点值拷贝到顶点缓冲区中
//通过设备指针来创建顶点缓冲区,用来存储顶点数据
g_pDevice->CreateVertexBuffer(
sizeof(vertex),//顶点缓冲区大小
D3DUSAGE_WRITEONLY,//顶点缓冲区作用
D3DFVF_CUSTOMVERTEX,//通知系统顶点格式
D3DPOOL_MANAGED,//顶点缓冲区存储位置,此处表示由系统处理
&g_pVB,//返回顶点缓冲区指针
NULL//系统保留参数,NULL
);

void* pVertices = NULL;

//锁定顶点缓冲区,向其中拷贝数据
g_pVB->Lock(
0,//锁定的偏移量
sizeof(vertex),//锁定的大小
&pVertices,//锁定之后存储空间
0//锁定的标识,0
);

//将数组中的内容拷贝到缓冲区中
memcpy(pVertices, vertex, sizeof(vertex));

//解锁
g_pVB->Unlock();

至此,初始化定义部分就完成了,下面就是在渲染的部分添加绘制的代码了:

//----------绘制图形步骤6.设置数据源,设置灵活顶点格式,绘制图元

//设置数据流来源
g_pDevice->SetStreamSource(
0,//数据流管道号(0-15)
g_pVB,//数据来源
0,//数据流偏移量
sizeof(stVertex)//每个数据的字节数大小
);

//通知系统数据格式,以便解析数据
g_pDevice->SetFVF(D3DFVF_CUSTOMVERTEX);

//绘制图元
g_pDevice->DrawPrimitive(
D3DPT_TRIANGLELIST,//三角形列
0,//起始点编号
1//图元数量
);


终于完成了,run一下:





总结一下在D3D中绘制图形的步骤:

1.定义灵活顶点格式

2.根据顶点格式构建一个描述顶点的结构体

3.声明一个顶点缓冲区指针

4.定义一个顶点结构数组用来给每个顶点赋值

5.为顶点缓冲区分配空间并将数组中的顶点数据拷贝到顶点缓冲区

6.设置数据源,设置灵活顶点格式,绘制图元


完整的代码:

// D3DDemo.cpp : 定义应用程序的入口点。
//

#include "stdafx.h"
#include "D3DDemo.h"

#define MAX_LOADSTRING 100

// 全局变量:
HINSTANCE hInst;// 当前实例
TCHAR szTitle[MAX_LOADSTRING];// 标题栏文本
TCHAR szWindowClass[MAX_LOADSTRING];// 主窗口类名

// 此代码模块中包含的函数的前向声明:
HWND g_hWnd;
ATOMMyRegisterClass(HINSTANCE hInstance);
BOOLInitInstance(HINSTANCE, int);
LRESULT CALLBACKWndProc(HWND, UINT, WPARAM, LPARAM);

//---------改造3D窗口需要的内容------------
LPDIRECT3D9 g_pD3D = NULL; //D3D接口指针
LPDIRECT3DDEVICE9 g_pDevice = NULL;//D3D设备指针


//------------绘制图形步骤1.定义灵活顶点格式
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE)//坐标为经过变换的屏幕坐标,顶点的颜色

//------------绘制图形步骤2.根据上面定义的顶点格式,创建一个顶点的结构体
struct stVertex
{
D3DXVECTOR4 vPos;//位置坐标
DWORD dwColor;//颜色
};

//----------绘制图形步骤3.声明一个顶点缓冲区指针
LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL;


//初始化顶点缓冲区
void initVB()
{
//----------绘制图形步骤4.定义一个结构体数组用来给每个顶点赋值
//数组中存储当前程序中顶点的数据
stVertex vertex[] =
{
{D3DXVECTOR4(100.0f, 100.0f, 0.0f, 1.0f), D3DCOLOR_XRGB(255,0,0)},
{D3DXVECTOR4(400.0f, 100.0f, 0.0f, 1.0f), D3DCOLOR_XRGB(0,255,0)},
{D3DXVECTOR4(100.0f, 400.0f, 0.0f, 1.0f), D3DCOLOR_XRGB(0,0,255)}
};

//----------绘制图形步骤5.为定点缓冲区分配内存,并将数组中的顶点值拷贝到顶点缓冲区中
//通过设备指针来创建顶点缓冲区,用来存储顶点数据
g_pDevice->CreateVertexBuffer(
sizeof(vertex),//顶点缓冲区大小
D3DUSAGE_WRITEONLY,//顶点缓冲区作用
D3DFVF_CUSTOMVERTEX,//通知系统顶点格式
D3DPOOL_MANAGED,//顶点缓冲区存储位置,此处表示由系统处理
&g_pVB,//返回顶点缓冲区指针
NULL//系统保留参数,NULL
);

void* pVertices = NULL;

//锁定顶点缓冲区,向其中拷贝数据
g_pVB->Lock(
0,//锁定的偏移量
sizeof(vertex),//锁定的大小
&pVertices,//锁定之后存储空间
0//锁定的标识,0
);

//将数组中的内容拷贝到缓冲区中
memcpy(pVertices, vertex, sizeof(vertex));

//解锁
g_pVB->Unlock();

}

void onCreatD3D()
{
g_pD3D = Direct3DCreate9(D3D_SDK_VERSION);
if (!g_pD3D)
return;

//检测硬件设备能力的方法
/*D3DCAPS9 caps;
ZeroMemory(&caps, sizeof(caps));
g_pD3D->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps);*/

//获得相关信息,屏幕大小,像素点属性
D3DDISPLAYMODE d3ddm;
ZeroMemory(&d3ddm, sizeof(d3ddm));

g_pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm);


//设置全屏模式
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
/*d3dpp.Windowed = false;
d3dpp.BackBufferWidth = d3ddm.Width;
d3dpp.BackBufferHeight = d3ddm.Height;*/

d3dpp.Windowed = true;
d3dpp.BackBufferFormat = d3ddm.Format;
d3dpp.BackBufferCount = 1;

d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;//交换后原缓冲区数据丢弃

//是否开启自动深度模板缓冲
d3dpp.EnableAutoDepthStencil = true;
//当前自动深度模板缓冲的格式
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;//每个像素点有16位的存储空间,存储离摄像机的距离


g_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, g_hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &g_pDevice);

if (!g_pDevice)
return;

//设置渲染状态,设置启用深度值
g_pDevice->SetRenderState(D3DRS_ZENABLE, true);

//设置渲染状态,关闭灯光
g_pDevice->SetRenderState(D3DRS_LIGHTING, false);


}

void onInit()
{
//初始化D3D
onCreatD3D();

//初始化顶点
initVB();
}

void onDestroy()
{
if (!g_pDevice)
g_pDevice->Release();
g_pDevice = NULL;
}

void onLogic(float fElapsedTime)
{

}

void onRender(float fElasedTime)
{
//前两个参数是0和NULL时,清空整个游戏窗口的内容(清的是后台)
//第三个是清除的对象:前面表示清除颜色缓冲区,后面表示清除深度缓冲区,D3DCLEAR_STENCIL清空模板缓冲区
g_pDevice->Clear(0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,100,100), 1.0f, 0);

g_pDevice->BeginScene();

//----------绘制图形步骤6.设置数据源,设置灵活顶点格式,绘制图元

//设置数据流来源
g_pDevice->SetStreamSource(
0,//数据流管道号(0-15)
g_pVB,//数据来源
0,//数据流偏移量
sizeof(stVertex)//每个数据的字节数大小
);

//通知系统数据格式,以便解析数据
g_pDevice->SetFVF(D3DFVF_CUSTOMVERTEX);

//绘制图元
g_pDevice->DrawPrimitive(
D3DPT_TRIANGLELIST,//三角形列
0,//起始点编号
1//图元数量
);

g_pDevice->EndScene();


g_pDevice->Present(NULL, NULL, NULL, NULL);
}


int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);

// TODO: 在此放置代码。
MSG msg;
HACCEL hAccelTable;

// 初始化全局字符串
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_D3DDEMO, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);

// 执行应用程序初始化:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}

hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_D3DDEMO));



ZeroMemory(&msg, sizeof(msg));
while (msg.message != WM_QUIT)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
static DWORD dwTime = timeGetTime();
DWORD dwCurrentTime = timeGetTime();
DWORD dwElapsedTime = dwCurrentTime - dwTime;
float fElapsedTime = dwElapsedTime * 0.001f;

//------------渲染和逻辑部分代码----------
onLogic(fElapsedTime);
onRender(fElapsedTime);
//-----------------------------------------
if (dwElapsedTime <1000 / 60)
{
Sleep(1000/ 60 - dwElapsedTime);
}
dwTime = dwCurrentTime;
}
}

onDestroy();
return (int) msg.wParam;
}



//
// 函数: MyRegisterClass()
//
// 目的: 注册窗口类。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX wcex;

wcex.cbSize = sizeof(WNDCLASSEX);

wcex. CS_VREDRAW;
wcex.lpfnWndProc= WndProc;
wcex.cbClsExtra= 0;
wcex.cbWndExtra= 0;
wcex.hInstance= hInstance;
wcex.hIcon= LoadIcon(hInstance, MAKEINTRESOURCE(IDI_D3DDEMO));
wcex.hCursor= LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground= (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName= MAKEINTRESOURCE(IDC_D3DDEMO);
wcex.lpszClassName= szWindowClass;
wcex.hIcOnSm= LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

return RegisterClassEx(&wcex);
}

//
// 函数: InitInstance(HINSTANCE, int)
//
// 目的: 保存实例句柄并创建主窗口
//
// 注释:
//
// 在此函数中,我们在全局变量中保存实例句柄并
// 创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{

hInst = hInstance; // 将实例句柄存储在全局变量中

g_hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

if (!g_hWnd)
{
return FALSE;
}



SetMenu(g_hWnd, NULL);
ShowWindow(g_hWnd, nCmdShow);
UpdateWindow(g_hWnd);

onInit();

return TRUE;
}

//
// 函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// 目的: 处理主窗口的消息。
//
// WM_COMMAND- 处理应用程序菜单
// WM_PAINT- 绘制主窗口
// WM_DESTROY- 发送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND g_hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{


HDC hdc;

switch (message)
{
case WM_KEYDOWN:
if (wParam == VK_ESCAPE)
PostQuitMessage(0);
break;
case WM_CLOSE:
DestroyWindow(g_hWnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(g_hWnd, message, wParam, lParam);
}
return 0;
}

关于顶点格式的问题(D3DFVF_XYZ和D3DFVF_XYZRHW顶点格式的区别):

在上面的例子中使用的是x,y,z,rhw的顶点格式,定义时为D3DFVF_XYZRHW。

DX中原文是这样写的:

The vertices in the Vertices sample project are transformed. In other words, they are already in 2D window coordinates. This means that the point (0,0) is at the top left corner and the positive x-axis is right and the positive y-axis is down. These vertices are also lit, meaning that they are not using Direct3D lighting but are supplying their own color.

正常情况下,我们使用的三维坐标,是xyz格式的,定义为D3DFVF_XYZ,这种类型的顶点格式,需要进行世界变换,投影变换,视图变换等才能转化到到我们窗口中。而D3DFVF_XYZRHW格式的顶点,是已经经过了这些处理,换句话说,这种顶点格式,直接就是我们屏幕上的坐标,因而对于三维的那些世界变换,取景变换对这种顶点格式无效。x表示横坐标,y表示纵坐标(屏幕左上角为原点,向右为x正方向,向下为y正方向),z坐标被忽略了(貌似关于fog会有用,不过这里我试验了一下,改变z坐标貌似没有什么影响),我们一般设置成0即可,而rhw表示投影空间中顶点所在的齐次点(x, y, z, w)的w坐标的倒数。

注意,D3DFVF_XYZ和D3DFVF_XYZRHW这两种顶点格式不能共存。







推荐阅读
  • 本文详细介绍了 Spark 中的弹性分布式数据集(RDD)及其常见的操作方法,包括 union、intersection、cartesian、subtract、join、cogroup 等转换操作,以及 count、collect、reduce、take、foreach、first、saveAsTextFile 等行动操作。 ... [详细]
  • 本文总结了Java初学者需要掌握的六大核心知识点,帮助你更好地理解和应用Java编程。无论你是刚刚入门还是希望巩固基础,这些知识点都是必不可少的。 ... [详细]
  • 字节流(InputStream和OutputStream),字节流读写文件,字节流的缓冲区,字节缓冲流
    字节流抽象类InputStream和OutputStream是字节流的顶级父类所有的字节输入流都继承自InputStream,所有的输出流都继承子OutputStreamInput ... [详细]
  • 本文探讨了如何利用Java代码获取当前本地操作系统中正在运行的进程列表及其详细信息。通过引入必要的包和类,开发者可以轻松地实现这一功能,为系统监控和管理提供有力支持。示例代码展示了具体实现方法,适用于需要了解系统进程状态的开发人员。 ... [详细]
  • 使用HTML和JavaScript实现视频截图功能
    本文介绍了如何利用HTML和JavaScript实现从远程MP4、本地摄像头及本地上传的MP4文件中截取视频帧,并展示了具体的实现步骤和示例代码。 ... [详细]
  • 兆芯X86 CPU架构的演进与现状(国产CPU系列)
    本文详细介绍了兆芯X86 CPU架构的发展历程,从公司成立背景到关键技术授权,再到具体芯片架构的演进,全面解析了兆芯在国产CPU领域的贡献与挑战。 ... [详细]
  • MySQL初级篇——字符串、日期时间、流程控制函数的相关应用
    文章目录:1.字符串函数2.日期时间函数2.1获取日期时间2.2日期与时间戳的转换2.3获取年月日、时分秒、星期数、天数等函数2.4时间和秒钟的转换2. ... [详细]
  • 本文详细介绍了Java反射机制的基本概念、获取Class对象的方法、反射的主要功能及其在实际开发中的应用。通过具体示例,帮助读者更好地理解和使用Java反射。 ... [详细]
  • [转]doc,ppt,xls文件格式转PDF格式http:blog.csdn.netlee353086articledetails7920355确实好用。需要注意的是#import ... [详细]
  • 开发日志:高效图片压缩与上传技术解析 ... [详细]
  • 本文详细解析了一种实用的函数,用于从URL中提取查询参数。该函数通过处理URL中的搜索部分,能够高效地获取并解析出所需的参数值,适用于各种Web开发场景。 ... [详细]
  • Java Socket 关键参数详解与优化建议
    Java Socket 的 API 虽然被广泛使用,但其关键参数的用途却鲜为人知。本文详细解析了 Java Socket 中的重要参数,如 backlog 参数,它用于控制服务器等待连接请求的队列长度。此外,还探讨了其他参数如 SO_TIMEOUT、SO_REUSEADDR 等的配置方法及其对性能的影响,并提供了优化建议,帮助开发者提升网络通信的稳定性和效率。 ... [详细]
  • 深入剖析Java中SimpleDateFormat在多线程环境下的潜在风险与解决方案
    深入剖析Java中SimpleDateFormat在多线程环境下的潜在风险与解决方案 ... [详细]
  • 在PHP中,高效地分割字符串是一项常见的需求。本文探讨了多种技术,用于在特定字符(如“或”)后进行字符串分割。通过使用正则表达式和内置函数,可以实现更加灵活和高效的字符串处理。例如,可以使用 `preg_split` 函数来实现这一目标,该函数允许指定复杂的分隔符模式,从而提高代码的可读性和性能。此外,文章还介绍了如何优化分割操作以减少内存消耗和提高执行速度。 ... [详细]
  • 使用Maven JAR插件将单个或多个文件及其依赖项合并为一个可引用的JAR包
    本文介绍了如何利用Maven中的maven-assembly-plugin插件将单个或多个Java文件及其依赖项打包成一个可引用的JAR文件。首先,需要创建一个新的Maven项目,并将待打包的Java文件复制到该项目中。通过配置maven-assembly-plugin,可以实现将所有文件及其依赖项合并为一个独立的JAR包,方便在其他项目中引用和使用。此外,该方法还支持自定义装配描述符,以满足不同场景下的需求。 ... [详细]
author-avatar
黄梓恒_
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有