420 likes | 655 Views
Visual C++ 高级编程. 第七讲 进入 3D 世界. 什么是 DirectX SDK ?. 微软的 DirectX 软件开发工具包( SDK )提供了一套优秀的应用程序编程接口( APIs ),这个编程接口可以提供给你开发高质量、实时的应用程序所需要的各种资源。 DirectX 技术的出现将极大的有助于发展下一代多媒体应用程序和电脑游戏。. DirectX SDK 的主要组件. DirectDraw :通过直接访问显示硬件来提供高级的图象处理能力。
E N D
Visual C++高级编程 第七讲 进入3D世界
什么是DirectX SDK ? • 微软的DirectX软件开发工具包(SDK)提供了一套优秀的应用程序编程接口(APIs),这个编程接口可以提供给你开发高质量、实时的应用程序所需要的各种资源。DirectX技术的出现将极大的有助于发展下一代多媒体应用程序和电脑游戏。
DirectX SDK的主要组件 • DirectDraw :通过直接访问显示硬件来提供高级的图象处理能力。 • DirectSound :它提供了软硬件的低延迟声音混频(low_latency sound mixing)和回放(Playback),硬件加速,以及直接访问音频设备的能力。 • DirectPlay :它明确的提供了通用环境连接能力(generalized communication capabilities),来简化你应用程序之间的通讯服务。 • Direct3D :它为主流的桌上型计算机和Internet用户提供实时的、交互的3D技术。 • DirectInput :它简化你的应用程序访问鼠标、键盘和操纵杆设备的能力。
DirectX的特色 • 直接读写内存 DDRAW可以直接读写显存,并利用“切换页”的功能提高图形显示的性能。 • 支持硬件加速 DirectX对象建立时,程序自动查询可使用的硬件。提供HAL和HEL两种模式。 • 网络联机功能 DirectPlay可以程序员更容易地开发多人联机游戏。并支持各种通讯协议。
COM • COM 就是 Component Object Model( 组件对象模型)。 • COM 接口和C++的抽象类相似,COM描述了一套符号和语法而非实现过程。 • DX 就提供了一套完整的设计3D游戏的库。使用DX时,不必直接接触硬件,而由DX进行代理。 • 使用 COM 时应该注意,必须在程序结束前释放所有的 COM 对象(或接口)。而且,释放它们的顺序应该和创建它们的顺序相反。
设置DirectX开发环境 • 步骤1:安装DirectX SDK; • 步骤2:设置应用程序中的头文件目录; • 步骤3:添加所需的头文件; • 步骤4:设置项目链接时所需的库文件,方法有两种: • 1、应用程序中使用 #pragma comment(lib,“xxxx.lib”) (推荐) • 2、在项目设置中添加库文件
Direct3D的特色 WIN32 Application WIN32 Application Direct3D API GDI Hal Device Device Driver Interface(DDI) Graphics Hardware
硬件抽象层 • Direct3D通过硬件抽象层(HAL)提供设备无关性。HAL是一个设备相关的接口,由设备制造商提供,HAL可以是显示驱动程序的一部分,或者是一个单独的动态链接库。 • HAL通过驱动程序开发人员定义的私有接口与显示驱动程序进行通信。应用程序则通过Direct3D API来实现显示。 • Direct3D选择HAL设备时,可以充分利用硬件的特性,如硬件加速等。
建立3D环境 • 一个标准的3D框架可以分为以下几个部分: • 创建Direct3D接口和设备接口。 • WinMain:Win32程序的入口点,代码会从这里开始执行。这是我们注册、创建、显示窗口的地方。然后,我们要初始化Direct3D并进入游戏循环(Game Loop)。 • WinProc:这是应用程序的消息处理过程。Windows发给我们的程序的消息,都要有它处理。 • InitD3D:这个函数是用来初始化Direct3D的。首先,创建IDirect3D9对象,通过这个对象我们得到了当前屏幕的显示模式。然后,根据刚刚我们获取的信息(显示模式),创建了兼容的设备。
建立3D环境 • Render:首先,我们清除了后缓冲区准备作画。然后,调用了BeginScene函数来告知DX我们要作画了。结束了绘制后我们调用EndScene来告知DX我们画完了。最后,我们调用了Present来完成关键的一步:翻动后缓冲区到前缓冲区(屏幕)。这时,用户就能看到我们画的东西了。 • CleanUp:我们在此做清洁工作:释放所有的被创建的对象 。
建立3D环境 #include <d3d9.h> LPDIRECT3D9 g_pD3D=NULL; LPDIRECT3DDEVICE9 g_pd3dDevice=NULL; 导入头文件 声明接口指针 注册窗口类 创建DIRECT3D9对象,获取显示卡 的显示模式,然后根据这些信息创建 DIRECT3D9设备。 初始化D3D设备 显示窗口 利用DIRECT3D9设备进行场景的绘制。 消息循环 释放所有对象 程序结束 建立3D环境
绘制3D几何图形 • 所有的 3D 图形都是由三角形构成的。 • 顶点:顶点就是3D空间中的一个点。在3D空间中,你可以用三个顶点来指定一个三角形。 • 灵活顶点格式:是用来描述顶点属性的一种格式,而这种格式是可以由我们自定义的 。顶点属性一般有:X,Y,Z值,以及颜色和亮度等。 • 顶点缓冲:顶点缓冲就是一块用于保存顶点的内存缓冲区。 • 颜色:在DirectX中,如果我们要指定一种颜色,我们可以用D3DCOLOR_XRGB宏。
绘制3D几何图形 • 坐标系:3D 迪卡尔坐标系统 • 背面拣选:这所有的三角形面,面向我们的面将会被渲染出来(可见),否则将不被渲染(不可见,被拣选出来了)。
绘制3D几何图形 • 3D环境:三个矩阵——世界矩阵 、视图矩阵 、投影矩阵 。 • 创建模型:即定义3D图形的所有顶点属性。 • 设备数据源:当3D模型设置好后,我们就可以绑定该顶点缓存区到设备数据源。进行3D图形的渲染。
矩阵变换 矩 阵 变 换 的 基 本 公 式 缩小或放大
矩阵变换的实现 D3DXMatrixMultiply(&mat2, & mat1, &mat0); //矩阵相乘, mat1* mat0-> mat2 D3DXMATRIX *D3DXMatrixTranslation( //矩阵平移 D3DXMATRIX* pOut, //输出 FLOAT x, FLOAT y, FLOAT z ); D3DXMATRIX *D3DXMatrixRotationX( //矩阵旋转 D3DXMATRIX* pOut, //输出 FLOAT Angle // 角度. ); D3DXMATRIX *D3DXMatrixScaling( //矩阵缩放 D3DXMATRIX* pOut, //输出 FLOAT sx, FLOAT sy, FLOAT sz ); Device->SetTransform(D3DTS_XXXX, &XXXXX);
顶点格式 • Direct3D让我们可以灵活的构造自己的顶点格式。换句话说,我们可以自己定义顶点的成员。 • 为了创建一个自定义的顶点结构,我们首先要创建一个包含能存放我们选择的顶点数据的结构 . struct ColorVertex { float _x, _y, _z; DWORD _color; }; struct NormalTexVertex { float _x, _y, _z; float _nx, _ny, _nz; float _u, _v; }; 包含了位置 和颜色信息 包含了位置 法线向量 纹理坐标
#define FVF_COLOR (D3DFVF_XYZ | D3DFVF_DIFFUSE) 顶点结构表明它包含位置和颜色属性 #define FVF_NORMAL_TEX (D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1) 顶点结构表明它包含了位置,法线向量,纹理坐标的属性 灵活顶点格式(FVF) 旋转的三角形
图元 • 图元就是设备所支持的原始的类型。它包括:点列、线列、线代、三角形列、三角形带 和 三角扇形。 Triangle Strips (三角形带)
立方体的绘制 • 三角形带: • 立方体的分割:
3D物体的绘制 g_pD3DDevice->BeginScene(); //Rendering our triangle g_pD3DDevice->SetStreamSource(0, g_pVertexBuffer, sizeof(CUSTOMVERTEX)); g_pD3DDevice->SetVFV(D3DFVF_CUSTOMVERTEX); g_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1); //End the scene g_pD3DDevice->EndScene(); 设置源缓冲区 设置顶点格式 绘制图元 旋转的三角棱
索引缓冲 • 索引缓冲就是内存中的一块用于索引顶点的区域。 • 当渲染场景时,DX会对每个顶点都进行许多计算,例如灯光、变幻等等,这样,运算量会很大。但是我们总是希望让DX做最少的运算以增加程序的效率,因此,我们需要把顶点的数目减到最少。
索引缓冲例子 在上面的例子中,我们只用了4个顶点就定义 了一个正方形,实际节约了2个顶点。
索引缓冲的实现 LPDIRECT3DINDEXBUFFER9 g_pIB; //定义 WORD indices[]={0,2,1,0,3,2,0,1,3,1,2,3}; //定义数组 g_pd3dDevice->CreateIndexBuffer(sizeof(indices), 0, D3DFMT_INDEX16, D3DPOOL_DEFAULT, &g_pIB, NULL); //创建索引缓冲 void *pIndices; g_pIB->Lock( 0,sizeof(indices),(void**)&pIndices,0); memcpy( pIndices,indices,sizeof(indices) ); //拷贝索引值 g_pIB->Unlock(); g_pd3dDevice->SetIndices(g_pIB); //设置索引缓冲 g_pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0,0,4,0,4); //输出
灯光 • 可以模拟出真实世界中一样的光线反射效果,受计算机计算能力的限制,只能有限地进行模拟。 • 环境光(ambient)的投射 • 直射光的投射(DIRECT) • 阴影 • 在3D环境里,阴影分为:平面阴影和内插(GOURAUD)阴影 • 平面阴影: • 在每一个表而后第一个顶点上进行颜色与打光的计算。效果较差,但速度很快 • 内插阴影:在每个表面上,利用三个顶点的颜色值进行内插计算,这种效果有点像渐变的模式。但计算量较大,速度慢
灯光 • 2.光的属性: • 光的投射方式: • 漫射(DIFFUSE) • 反射(SPECULAR) • 环境光(AMBIENT) • 光的类型 • 点光源,无方向 • 聚光灯,方向性较强 • 指定光,有方向,但没位置,如太阳光等 • 3.光的色彩属性 • 扩散(Diffuse Color):当物体被光照射时,所能反射的光 • 环绕(Ambient Color) • 放射(Emissiv Color) • 反射(Specular Color)
灯光属性 • 想要不同的光可以指定不同的属性,不是所有类型的光都拥有下面的这些属性: • Position (位置)这是在3D空间中光源的位置,例如(0, 10, 0)。 • Direction (方向)这是一个向量(例如(0, 1, 0)),表示从光源发射出来的光的方向。 • Range (范围)这是光从光源发出后所能到达的最大距离,范围以外的物体将不会接受到此光源的光。 • Attenuation (衰减)在光源到它的范围之间,光会逐渐衰减,这是很正常的。如果想要,你也可以指定让光不衰减,或反而越来越亮。 • Diffuse Light (漫反射)光的漫反射颜色。漫射光是分散的,但仍然具有方向,不像环境光没有方向。 • Ambient Light (环境光)这是环境光的颜色,它是通用的背景光,它是分散的且没有方向和光源,它充满整个场景。
灯光类型 • Ambient Light (环境光)可以为场景指定通用的环境光,与其它灯光互不影响,用红、绿、蓝三个分量来设置颜色。 • Point Light (点光源)它没有方向(因为向全部方向发出),但是有颜色、范围和衰减。例如电灯. • Directional Light (直射光)直射光具有颜色,没有位置。场景里的所有对象都会从同样的方向接收到同样的光。直射光也没有范围和衰减。例如太阳光. • Spotlight (聚光灯)它具有位置、方向、范围、发光内径和发光外径属性,光照强度还会随距离而衰减。例如:手电筒.
设置灯光 • 打开光照处理: SetRenderState( D3DRS_LIGHTING, TRUE ); • 创建灯光: D3DLIGHT9 light; ::ZeroMemory( &light, sizeof(D3DLIGHT9) ); light.Type = D3DLIGHT_DIRECTIONAL; //灯光类型 light.Diffuse.r = 1.0f; light.Diffuse.g = 1.0f; light.Diffuse.b = 1.0f; light.Direction = D3DXVECTOR3( -1.0f, -1.0f, 0.0f ); light.Range = 1000.0f;
材质 • 材质:用于描述对象(三角形)的反光性能,使它看起来有没有光泽和带有什么颜色。 • 下面是描述材质的一些设置: • Diffuse Reflection (漫反射)物体的漫反射颜色,能使物体看起来是什么颜色。 • Ambient Reflection (环境反射)这也是一个颜色值,用于描述对象反射环境光的数量。你甚至可以指定它根本不反射环境光,这意味着它将不可见,除非接收到其它类型的光。 • Specular Reflection and Power (镜面反射与高光强度)镜面反射的值,可以用镜面反射和高光强度设置对象,使它看起来发亮。 • Emission (放射)使物体自发光,但不会影响到场景内的其它物体。
设置材质 D3DMATERIAL9 mtrl; ::ZeroMemory( &mtrl, sizeof(D3DMATERIAL9) ); mtrl.Diffuse.r = mtrl.Ambient.r = 1.0f; mtrl.Diffuse.g = mtrl.Ambient.g = 1.0f; mtrl.Diffuse.b = mtrl.Ambient.b = 1.0f; mtrl.Diffuse.a = mtrl.Ambient.a = 1.0f; mtrl.Specular.r =mtrl.Emissive.r= 0.0f; mtrl.Specular.g =mtrl.Emissive.g= 0.0f; mtrl.Specular.b =mtrl.Emissive.b= 0.0f; mtrl.Specular.a =mtrl.Emissive.a= 0.0f; 灯光与材质
顶点法线 • Direct3D需要知道顶点法线以便它能够确定灯光照射到物体表面的角度,并且一旦计算了每个顶点的灯光,Direct3D需要知道每个顶点的表面方向。注意顶点法线不一定和面法线相同。 • 为了描述顶点的顶点法线,我们必须更新原来的顶点结构 .(如前叙) 阴影的实现
纹理 • 纹理 :赋于某物体一定的纹理属性 • 多种贴图方式(参见:3DMAX里的材质与贴图演示) • 纹理的基本概念 • 纹理坐标:纹理坐标的作用是在纹理上指定一个点,因为纹理是2D的,所以我们仅需要两个值 • U V值:取值范围:0-1.0f • 纹理映射 :把纹理应用到对象上,这个过程就叫做“纹理映射”。在此过程中,纹理坐标会被映射到顶点上,所以,顶点将额外增加两个值:U和V。
纹理过滤 • 最近点取样: • 过滤的效果不是很真实,但它的运算速度最快 • 在匹配大小时,对材质进行同比放大,因此较为粗糙.但因为速度快,因此常准备一些较大型的材质图做为贴图用. • 线性过滤 • 将与像素点相邻的最近四个像素点之间做加权平均的运算. • 也有失真,但比最近点取样要好 • Mipmap过滤: • 采用智能贴图技术,离观者近的,给高的细节,远的,则给出较粗的细节,这样,符合人们的观察习惯,又保证了精度 • 缺点:计算时占用较多的系统资源 • 非线性过滤 • 效果最好,但也最浪费系统资源
设置纹理 Struct CUSTOMVERTEX { FLOAT x, y, z; DWORD colour; FLOAT tu, tv; //纹理坐标 }; #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1) //顶点格式 LPDIRECT3DTEXTURE9 m_pTexture; //定义纹理 D3DXCreateTextureFromFile(g_pd3dDevice, szTextureFilePath, &m_pTexture)) //加载纹理, szTextureFilePath为纹理图片 为每个顶点设置纹理坐标
模型 • 1.构成三角形的方法 • 基础:任何3D模型(含平面模型)都是由三角形这个基本的几何图形来构成。 • 三角形清单: • 在使用三角形之前,要先进行顶点的定义。 • 特点:一个三角形由三个点决定,N个三角形则需要3*N个顶点。
三角形带: • 对于连成一片的三角形,进行重复使用 • 使用一个三角形的后两个顶点与新的顶点构成新的三角形。 • 三角形扇: • 以一个顶点为基准,以基他顶点来构成多个三角形。 两个三角形构成一个四边形,但只有4个顶点。
2 加载 .X 文件 • 方法: • 调用D3D的D3DXLoadMeshFromX 函数将 .X文件中的顶点数据读入到顶点缓冲区。 • 加载必要的材质。 • D3DXLoadMeshFromX函数原型 • HRESULT WINAPI D3DXLoadMeshFromX( LPCTSTR pFilename, • DWORD Options, • LPDIRECT3DDEVICE9 pD3DDevice, • LPD3DXBUFFER *ppAdjacency, • LPD3DXBUFFER *ppMaterials, • LPD3DXBUFFER *ppEffectInstances, • DWORD *pNumMaterials, • LPD3DXMESH *ppMesh ); 绘制模型文件