Direct3D基础

来源:互联网 发布:mac final cutpro x 编辑:程序博客网 时间:2024/05/16 14:29

最近在公司里实习,有个项目的接合点需要用到三维处理的东西,还是比较麻烦的,虽然之前也学过一点图形学的理论知识,但都是皮毛,研究得也不深入。所以趁现在把一些基本的概念拾起来,开发平台用微软的DirectX框架并结合C#,很多东西都是现学的。下面给出几个基本的概念。

图形卡显卡,计算机和显示器之间的接口。很多图形卡都有自己的处理器,称为GPU图形处理器。GPU是针对图形和图像所需要的计算进行过优化的专用处理器,与CPU并行工作。每个图形卡都有自己的显存,即计算机系统的显卡内存。其实一些桌面版的3D游戏,只是经过处理器进行大量的数学计算,将3D物体转换为具有立体感的2D图形,在计算机显示器屏幕上显示。

计算机通过图形卡控制显示器,图形卡能够控制显示器屏幕每一点的颜色。所采用的方法是,在图形卡的显存中分配一个区域,区域中每一个单元存储的颜色值跟屏幕中每一点的颜色一一对应,即如果程序修改了这个显存区域中一个单元中存储的颜色值,也就修改了和其对应屏幕点的颜色。如果直接修改显存区域中的数据,图像会发生抖动,因为显示器是按照行进行扫描的。为了避免抖动,一般在后备缓存区(back buffer)中修改图像,修改完成后再把后备缓存区中的图像送到屏幕显示区中显示。将3D物体转换为具有立体感的平面图形,实际上是将3D物体投影到XY平面上,Z轴表示3D物体距离观察者的距离。在投影的过程中,前面的物体可能会遮挡后面的物体,也就是说只有在Z轴方向上最靠近观察者的物体才嫩被投影到XY平面上,这里用到一个称为深度缓存区(Z-buffer,depthbuffer,w-buffer)的东西,它的每一个单元记录了每个投影到XY平面的点到观察者的距离。新的3D物体要投影到XY平面,首先要计算该点到观察者的距离,然后和深度缓存区中相应单元的值进行比较,只有小于时,才能进行投影,Direct 3D把这个操作称为深度测试。把3D物体在2D显示器中显示的过程叫渲染

上面的东西都是概念上的,其实从开发者的角度来看,上面所有的东西都是被封装成相应的类、函数、参数等。在Direct 3D中,Device类是所有绘图操作必须使用的类。可以把这个类的对象假想成真实的图形卡。Device类把真实的图形卡从具体的硬件中抽象出来,在类中定义一组通用函数,这些函数直接操作图形卡硬件。通过调用统一标准的Device类可以操作每一种图形卡,这样做的好处就是既保证了应用程序和硬件无关,又能直接控制硬件,加快应用程序的运行速度。下面给出一个Direct3D基本例子,显示蓝色屏幕背景。

安装的.NetFramework为4.0,x86架构,程序配置文件为:

<?xml version="1.0"?><configuration>  <startup useLegacyV2RuntimeActivationPolicy="true">    <supportedRuntime version="v4.0"/>  </startup></configuration>

using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Windows.Forms;using Microsoft.DirectX;using Microsoft.DirectX.Direct3D;namespace Direct3D{    public partial class Form1 : Form    {        private Device device = null;        public Form1()        {            InitializeComponent();        }        public bool InitializeGraphics()        {            try            {                PresentParameters presentParams = new PresentParameters();                presentParams.Windowed = true;//不是全屏显示,在一个窗口显示                presentParams.SwapEffect = SwapEffect.Discard; //后备缓存交换的方式                presentParams.EnableAutoDepthStencil = true; //允许使用自动深度模板测试                //深度缓冲区单元为16位二进制数                presentParams.AutoDepthStencilFormat = DepthFormat.D16;                device = new Device(0, DeviceType.Hardware, this,  //建立设备类对象          CreateFlags.SoftwareVertexProcessing, presentParams);                //设置设备重置事件(device.DeviceReset)事件函数为this.OnResetDevice                device.DeviceReset += new System.EventHandler(this.OnResetDevice);                this.OnCreateDevice(device, null);//自定义方法,初始化Device的工作放到这个方法中                this.OnResetDevice(device, null);//调用设备重置事件(device.DeviceReset)事件函数            }//设备重置事件函数要设置Device参数,初始函数中必须调用该函数            catch (DirectXException)            {                return false;            }            return true;        }        public void OnCreateDevice(object sender, EventArgs e)        { }        public void OnResetDevice(object sender, EventArgs e)        {            Render();        }        public void Render()//渲染方法,本方法没有任何渲染代码,可认为是渲染方法的框架        {            if (device == null) //如果未建立设备对象,退出                return;            //下边函数将显示区域初始化为蓝色,第1个参数指定要初始化目标窗口包括深度缓冲区            //第2个参数是我们所要填充的颜色。第3、第4个参数一般为1.0f, 0。            device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, System.Drawing.Color.Blue, 1.0f, 0);            device.BeginScene();//开始渲染            //渲染代码必须放在device.BeginScene()和device.Present()之间            device.EndScene();//渲染结束            device.Present();//更新显示区域,把后备缓存的3D图形送到屏幕显示区中显示        }        private void Form1_Load(object sender, EventArgs e)        {            InitializeGraphics();            Show();            Render();         }    }}

利用透视图的原理,可以在显示器屏幕上直接绘制具有立体感的平面图形。具体就是预先根据透视原理人工计算出3D物体在计算机显示器屏幕的显示坐标,然后再利用此坐标进行绘制。在Direct 3D中,CustomVertex.TransformedColored结构记录了已经根据透视原理计算后的顶点。

下面绘制一个静止的三角形,过程类似。

using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Windows.Forms;using Microsoft.DirectX;using Microsoft.DirectX.Direct3D;namespace Triangle{    public partial class Form1 : Form    {        private Device device = null;        CustomVertex.TransformedColored[] verts;        public Form1()        {            InitializeComponent();        }        public bool InitializeGraphics()        {            try            {                PresentParameters presentParams = new PresentParameters();                presentParams.Windowed = true;//不是全屏显示,在一个窗口显示                presentParams.SwapEffect = SwapEffect.Discard; //后备缓存交换的方式                presentParams.EnableAutoDepthStencil = true; //允许使用自动深度模板测试                //深度缓冲区单元为16位二进制数                presentParams.AutoDepthStencilFormat = DepthFormat.D16;                device = new Device(0, DeviceType.Hardware, this,  //建立设备类对象          CreateFlags.SoftwareVertexProcessing, presentParams);                //设置设备重置事件(device.DeviceReset)事件函数为this.OnResetDevice                device.DeviceReset += new System.EventHandler(this.OnResetDevice);                this.OnCreateDevice(device, null);//自定义方法,初始化Device的工作放到这个方法中                this.OnResetDevice(device, null);//调用设备重置事件(device.DeviceReset)事件函数            }//设备重置事件函数要设置Device参数,初始函数中必须调用该函数            catch (DirectXException)            {                return false;            }            return true;        }        public void OnCreateDevice(object sender, EventArgs e)        {            verts = new CustomVertex.TransformedColored[3];            verts[0].Position = new Vector4(150.0f, 50.0f, 0.5f, 1.0f);//三角形的第1个顶点坐标            verts[0].Color = Color.Aqua.ToArgb();//三角形的第1个顶点颜色            verts[1].Position = new Vector4(250.0f, 250.0f, 0.5f, 1.0f); //第2个顶点坐标            verts[1].Color = Color.Brown.ToArgb();            verts[2].Position = new Vector4(50.0f, 250.0f, 0.5f, 1.0f); //第3个顶点坐标            verts[2].Color = Color.LightPink.ToArgb();        }        public void OnResetDevice(object sender, EventArgs e)        {            Render();        }        public void Render()//渲染方法,本方法没有任何渲染代码,可认为是渲染方法的框架        {            if (device == null) //如果未建立设备对象,退出                return;            //下边函数将显示区域初始化为蓝色,第1个参数指定要初始化目标窗口            //第2个参数是我们所要填充的颜色。第3、第4个参数一般为1.0f, 0。            device.Clear(ClearFlags.Target | ClearFlags.ZBuffer,        System.Drawing.Color.Blue, 1.0f, 0);            device.BeginScene();//开始渲染            //渲染代码必须放在device.BeginScene()和device.Present()之间            device.VertexFormat = CustomVertex.TransformedColored.Format; //渲染代码            device.DrawUserPrimitives(PrimitiveType.TriangleList, 1, verts);            device.EndScene();//渲染结束            device.Present();//更新显示区域,把后备缓存的3D图形送到图形卡的显存中显示        }        private void Form1_Load(object sender, EventArgs e)        {            InitializeGraphics();            Show();            Render();        }    }}

DrawUserPrimitives方法的第一个参数primitiveType可以取不同的值,可以绘制点、线段和三角形。

如果顶点的数组放到图形卡的显存中,将能极大地增加绘制图形的速度。可以用VertexBuffer类为数组申请存储空间,指定顶点数组的存放位置。如果3D程序运行在窗口模式,可能有多个运行程序共同占有计算机的图形卡和显存。窗体改变大小、窗体最小化后再最大化、全屏模式和窗体模式之间的切换等情况发生时,Device对象被重新设置,所以放到图形卡显存中顶点数组的数据可能会丢失,必须恢复VertexBuffer缓存区中的数据。比如说,最小化后程序使用的显存将被其他程序占用,所以在最大化后必须重建VertexBuffer类对象中顶点数组的数据。在需要重建VertexBuffer类对象时,系统产生CreateVertexBuffer事件,通知程序在事件处理函数中重建VertexBuffer类对象中的数据。

using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Windows.Forms;using Microsoft.DirectX;using Microsoft.DirectX.Direct3D;namespace VertexBufferTest{    public partial class Form1 : Form    {        private Device device = null;        bool pause = false;        VertexBuffer vertexBuffer = null;        public Form1()        {            InitializeComponent();        }        public bool InitializeGraphics()        {            try            {                PresentParameters presentParams = new PresentParameters();                presentParams.Windowed = true;//不是全屏显示,在一个窗口显示                presentParams.SwapEffect = SwapEffect.Discard; //后备缓存交换的方式                presentParams.EnableAutoDepthStencil = true; //允许使用自动深度模板测试                //深度缓冲区单元为16位二进制数                presentParams.AutoDepthStencilFormat = DepthFormat.D16;                device = new Device(0, DeviceType.Hardware, this,  //建立设备类对象          CreateFlags.SoftwareVertexProcessing, presentParams);                //设置设备重置事件(device.DeviceReset)事件函数为this.OnResetDevice                device.DeviceReset += new System.EventHandler(this.OnResetDevice);                this.OnCreateDevice(device, null);//自定义方法,初始化Device的工作放到这个方法中                this.OnResetDevice(device, null);//调用设备重置事件(device.DeviceReset)事件函数            }//设备重置事件函数要设置Device参数,初始函数中必须调用该函数            catch (DirectXException)            {                return false;            }            return true;        }        public void OnCreateDevice(object sender, EventArgs e)        {            Device dev = (Device)sender;            vertexBuffer = new VertexBuffer(typeof(CustomVertex.TransformedColored),            3, dev, 0, CustomVertex.TransformedColored.Format, Pool.Default);            //事件的预订,指定OnCreateVertexBuffer函数是vertexBuffer.Created事件函数            vertexBuffer.Created += new System.EventHandler(this.OnCreateVertexBuffer);            this.OnCreateVertexBuffer(vertexBuffer, null); //创建顶点数组        }        public void OnResetDevice(object sender, EventArgs e)        { }        public void Render()//渲染方法,本方法没有任何渲染代码,可认为是渲染方法的框架        {            if (device == null) //如果未建立设备对象,退出                return;            if (pause)                return;            device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, System.Drawing.Color.Blue, 1.0f, 0);            device.BeginScene();//开始渲染            device.SetStreamSource(0, vertexBuffer, 0); //使用vertexBuffer中定义的顶点            device.VertexFormat = CustomVertex.TransformedColored.Format;//顶点格式            device.DrawPrimitives(PrimitiveType.TriangleList, 0, 1);            device.EndScene();//渲染结束            device.Present();//更新显示区域,把后备缓存的D图形送到图形卡的显存中显示        }        public void OnCreateVertexBuffer(object sender, EventArgs e)        {            CustomVertex.TransformedColored[] verts =                                (CustomVertex.TransformedColored[])vertexBuffer.Lock(0, 0);            verts[0].X = 150;            verts[0].Y = 50; //顶点0位置            verts[0].Z = 0.5f;            verts[0].Rhw = 1;            verts[0].Color = System.Drawing.Color.Aqua.ToArgb();//顶点0颜色            verts[1].X = 250;            verts[1].Y = 250;            verts[1].Z = 0.5f;            verts[1].Rhw = 1;            verts[1].Color = System.Drawing.Color.Brown.ToArgb();            verts[2].X = 50;            verts[2].Y = 250;            verts[2].Z = 0.5f;            verts[2].Rhw = 1;            verts[2].Color = System.Drawing.Color.LightPink.ToArgb();            vertexBuffer.Unlock();        }        private void Form1_Load(object sender, EventArgs e)//重写Load事件        {            InitializeGraphics();            Show();            Render();        }        private void Form1_Paint(object sender, PaintEventArgs e) //重写Paint事件        {            this.Render();        }        private void Form1_Resize(object sender, EventArgs e) //重写Resize事件        {            pause = ((this.WindowState == FormWindowState.Minimized) || !this.Visible);        }    }}

using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Windows.Forms;using Microsoft.DirectX;using Microsoft.DirectX.Direct3D;namespace 静止正方体{    public partial class Form1 : Form    {        private Device device = null;        bool pause = false;        VertexBuffer vertexBuffer = null;        public Form1()        {            InitializeComponent();        }        public bool InitializeGraphics()        {            try            {                PresentParameters presentParams = new PresentParameters();                presentParams.Windowed = true;//不是全屏显示,在一个窗口显示                presentParams.SwapEffect = SwapEffect.Discard; //后备缓存交换的方式                presentParams.EnableAutoDepthStencil = true; //允许使用自动深度模板测试                //深度缓冲区单元为16位二进制数                presentParams.AutoDepthStencilFormat = DepthFormat.D16;                device = new Device(0, DeviceType.Hardware, this,  //建立设备类对象          CreateFlags.SoftwareVertexProcessing, presentParams);                //设置设备重置事件(device.DeviceReset)事件函数为this.OnResetDevice                device.DeviceReset += new System.EventHandler(this.OnResetDevice);                this.OnCreateDevice(device, null);//自定义方法,初始化Device的工作放到这个方法中                this.OnResetDevice(device, null);//调用设备重置事件(device.DeviceReset)事件函数            }//设备重置事件函数要设置Device参数,初始函数中必须调用该函数            catch (DirectXException)            {                return false;            }            return true;        }        public void OnCreateDevice(object sender, EventArgs e)        {            Device dev = (Device)sender;            vertexBuffer = new VertexBuffer(typeof(CustomVertex.TransformedColored), 18,            dev, 0, CustomVertex.TransformedColored.Format, Pool.Default);            vertexBuffer.Created += new System.EventHandler(this.OnCreateVertexBuffer);            this.OnCreateVertexBuffer(vertexBuffer, null);        }        public void OnResetDevice(object sender, EventArgs e)        { }        public void Render()//渲染方法,本方法没有任何渲染代码,可认为是渲染方法的框架        {            if (device == null) //如果未建立设备对象,退出                return;            if (pause)                return;            device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.LightBlue, 1.0f, 0);            device.BeginScene();//开始渲染            device.SetStreamSource(0, vertexBuffer, 0);            device.VertexFormat = CustomVertex.TransformedColored.Format;            device.DrawPrimitives(PrimitiveType.TriangleList, 0, 6);            device.EndScene();//渲染结束            device.Present();//更新显示区域,把后备缓存的D图形送到图形卡的显存中显示        }        public void OnCreateVertexBuffer(object sender, EventArgs e)        {            CustomVertex.TransformedColored[] verts =                        (CustomVertex.TransformedColored[])vertexBuffer.Lock(0, 0);            verts[0].Position = new Vector4(100.0f, 50.0f, 0.5f, 1.0f);            verts[0].Color = Color.Red.ToArgb();            verts[1].Position = new Vector4(200.0f, 50.0f, 0.5f, 1.0f);            verts[1].Color = Color.Red.ToArgb();            verts[2].Position = new Vector4(50.0f, 100.0f, 0.5f, 1.0f);            verts[2].Color = Color.Red.ToArgb();            verts[3].Position = new Vector4(50.0f, 100.0f, 0.5f, 1.0f);            verts[3].Color = Color.Red.ToArgb();            verts[4].Position = new Vector4(200.0f, 50.0f, 0.5f, 1.0f);            verts[4].Color = Color.Red.ToArgb();            verts[5].Position = new Vector4(150.0f, 100.0f, 0.5f, 1.0f);            verts[5].Color = Color.Red.ToArgb();            verts[6].Position = new Vector4(50.0f, 100.0f, 0.5f, 1.0f);            verts[6].Color = Color.Green.ToArgb();            verts[7].Position = new Vector4(150.0f, 100.0f, 0.5f, 1.0f);            verts[7].Color = Color.Green.ToArgb();            verts[8].Position = new Vector4(50.0f, 200.0f, 0.5f, 1.0f);            verts[8].Color = Color.Green.ToArgb();            verts[9].Position = new Vector4(50.0f, 200.0f, 0.5f, 1.0f);            verts[9].Color = Color.Green.ToArgb();            verts[10].Position = new Vector4(150.0f, 100.0f, 0.5f, 1.0f);            verts[10].Color = Color.Green.ToArgb();            verts[11].Position = new Vector4(150.0f, 200.0f, 0.5f, 1.0f);            verts[11].Color = Color.Green.ToArgb();            verts[12].Position = new Vector4(150.0f, 100.0f, 0.5f, 1.0f);            verts[12].Color = Color.Yellow.ToArgb();            verts[13].Position = new Vector4(200.0f, 50.0f, 0.5f, 1.0f);            verts[13].Color = Color.Yellow.ToArgb();            verts[14].Position = new Vector4(150.0f, 200.0f, 0.5f, 1.0f);            verts[14].Color = Color.Yellow.ToArgb();            verts[15].Position = new Vector4(150.0f, 200.0f, 0.5f, 1.0f);            verts[15].Color = Color.Yellow.ToArgb();            verts[16].Position = new Vector4(200.0f, 50.0f, 0.5f, 1.0f);            verts[16].Color = Color.Yellow.ToArgb();            verts[17].Position = new Vector4(200.0f, 150.0f, 0.5f, 1.0f);            verts[17].Color = Color.Yellow.ToArgb();            vertexBuffer.Unlock();        }        private void Form1_Load(object sender, EventArgs e)        {            InitializeGraphics();            Show();            Render();        }        private void Form1_Paint(object sender, PaintEventArgs e)        {            this.Render();        }        private void Form1_Resize(object sender, EventArgs e)        {            pause = ((this.WindowState == FormWindowState.Minimized) || !this.Visible);        }    }}


任何一个平面都可以由若干个三角形组成,用三个顶点定义三角形平面,Direct 3D在渲染着两个三角形时,左手笛卡尔坐标系默认情况下按顺时针方向绘制三角形,即只显示按顺时针绘制的三角形,这就是背面剔除。Device属性的RenderState.CullMode控制背面剔除方式。


2 0