前面的一篇文章Direct 3D基础介绍了一些基本概念,叙述了如何在显示器上直接绘制具有立体感的2D图形。上面的方法是不现实的,因为预先根据透视原理人工计算出3D物体在显示屏幕上显示的坐标然后再绘制的这种方式如果涉及从不同角度观察的3D物体的话,需要计算的次数会很多。Direct 3D实现3D所采用的方法是首先设计一个仿真真实3D物体的立体模型,然后由计算机根据透视原理计算出每一个角度模型显示在计算机显示器屏幕上的坐标和每个面的颜色,然后显示出来。
把3D场景中的所有3D物体在2D显示器上显示出来的过程叫做渲染。这一过程与使用照相机照相类似。最终将3D物体的3D坐标转化为2D显示器平面坐标及颜色在平面显示器显示出来。渲染过程将对3D场景中的所有3D物体进行世界、观察和投影坐标变换,最终将3D物体的3D坐标变换为2D显示器平面坐标及颜色在平面显示器显示出来。三个坐标变换中的每个变换都可以用一个Matrix结构中的4行4列仿射矩阵来表示,记录3D场景中的所有3D物体的平移、缩放、旋转等操作。渲染前需要进行建模,即制作被渲染的3D物体模型,用来搭建3D场景。一个3D物体一般包括若干曲面,任意曲面都可以由若干三角形平面组成,一个三角形平面由三角形的三个顶点确定。所以3D物体模型就是用顶点定义的3D物体。在建模阶段需要定义3D物体所有顶点的位置以及属性。建模可以在程序中完成,也可以用专用软件比如说3D Max完成后再导进去。每个模型都有自己的坐标系统,称为建模坐标系统,坐标系统所代表空间称为建模空间。建模必须首先定义3D模型以及建模坐标所表示的顶点坐标,这些顶点经过三个变换,最终转换为平面显示器坐标,同时,还可能定义3D模型的其他元素,比如说颜色、面的法线等。
世界变换:使用建模创建的3D模型搭建3D场景。观察变换:也称为取景变换,从场景的世界空间中取得感兴趣的部分场景。投影变换:将摄像机空间中的3D模型转换为能够在显示器屏幕上显示的具有立体感的平面图形。
要在屏幕中显示一个三角形,三角形的三个顶点必须要经过三个变换——世界、观察和投影变换。当3D程序的运行状态发生变化时,比如说窗口状态和全屏状态切换,Device类对象的参数必然要发生变化,这些参数必须被重新设置。一般情况下,可以在OnResetDevice和Render方法中修改Device类对象的参数。在程序中不改变的Device类对象的参数一般在OnResetDevice方法中修改;在程序中需要改变的Device类对象的参数一般要在Render方法中修改,这样做可以加快渲染的速度。为了使三角形旋转,每次渲染三角形之前,必须根据三角形在世界空间中的新位置为其指定新的世界变换矩阵。
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; float Angle = 0, ViewZ = -6.0f; 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.PositionColored), 3, 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) { Device dev = (Device)sender; dev.RenderState.CullMode = Cull.None; //取消背面剔除 dev.RenderState.Lighting = false; //取消灯光 SetupMatrices(); //在程序运行期间,Device的3个变换不改变,因此放在此处 } public void Render() //渲染方法,本方法没有任何渲染代码,可认为是渲染方法的框架 { if (device == null) //如果未建立设备对象,退出 return; if (pause) return; device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Blue, 1.0f, 0); device.BeginScene(); //开始渲染 SetupMatrices(); //在程序运行期间,Device的2个变换参数要改变,因此放在此处 device.SetStreamSource(0, vertexBuffer, 0); device.VertexFormat = CustomVertex.PositionColored.Format; device.DrawPrimitives(PrimitiveType.TriangleList, 0, 1); device.EndScene(); //渲染结束 device.Present(); //更新显示区域,把后备缓存的图形送到图形卡的显存中显示 } 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); } public void OnCreateVertexBuffer(object sender, EventArgs e) { CustomVertex.PositionColored[] verts = //建模 (CustomVertex.PositionColored[])vertexBuffer.Lock(0, 0); verts[0].Position = new Vector3(-1.0f, -1.0f, 0.0f); //顶点0位置,注意为Vector3 verts[0].Color = System.Drawing.Color.Aqua.ToArgb(); //顶点0颜色 verts[1].Position = new Vector3(1.0f, -1.0f, 0.0f); //顶点1位置 verts[1].Color = System.Drawing.Color.Brown.ToArgb(); verts[2].Position = new Vector3(0.0f, 1.0f, 0.0f); //顶点2位置 verts[2].Color = System.Drawing.Color.LightPink.ToArgb(); vertexBuffer.Unlock(); } private void SetupMatrices() //修改Device的3个变换 { int iTime = Environment.TickCount % 1000; Angle = iTime * (2.0f * (float)Math.PI) / 1000.0f; device.Transform.World = Matrix.RotationY(Angle); device.Transform.View = Matrix.LookAtLH(new Vector3(0.0f, 3.0f, ViewZ), new Vector3(0.0f, 0.0f, 0.0f), new Vector3(0.0f, 1.0f, 0.0f)); device.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4, 1.0f, 1.0f, 100.0f); } private void Form1_KeyDown(object sender, KeyEventArgs e) { switch (e.KeyCode) //e.KeyCode是键盘每个键的编号 { case Keys.Left: //Keys.Left是左箭头键编号,三角形沿Y轴左转 Angle += 0.1F; break; case Keys.Right: //三角形沿Y轴右转 Angle -= 0.1F; break; case Keys.Down: //三角形离观察者越来越远 ViewZ += 0.1F; break; case Keys.Up: //三角形离观察者越来越近 ViewZ -= 0.1F; break; } } private void Form1_Load(object sender, EventArgs e) { InitializeGraphics(); Show(); Render(); } } }
显示立方体,首先创建立方体模型,设置8个顶点的坐标,首先创建立方体的一个面,其他的面用世界变换将第一个面变换到立方体的相应位置,最终实现立方体。
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; float Angle = 0, ViewZ = -6.0f; 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; //阴影部分是所作修改,正方形有6个顶点 vertexBuffer = new VertexBuffer(typeof(CustomVertex.PositionColored), 6, 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) { Device dev = (Device)sender; //背面剔除方式为只显示顺时针三角形,因为正方体应该只看到外表面 dev.RenderState.CullMode = Cull.CounterClockwise; dev.RenderState.Lighting = false; //取消灯光 } public void Render() //渲染方法,本方法没有任何渲染代码,可认为是渲染方法的框架 { if (device == null) //如果未建立设备对象,退出 return; if (pause) return; device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.LightBlue, 1.0f, 0); device.BeginScene(); //开始渲染 SetupMatrices(); device.SetStreamSource(0, vertexBuffer, 0); device.VertexFormat = CustomVertex.PositionColored.Format; device.Transform.World = Matrix.Translation(0, 0, -1);//沿Z轴向观察者方向移动1个单位 device.DrawPrimitives(PrimitiveType.TriangleList, 0, 2); //绘制正前面 //旋转180度是为了从外侧看,按顺时针方向绘制三角形,因背面剔除打开,内侧不被看到 device.Transform.World = Matrix.RotationY((float)Math.PI) * Matrix.Translation(0, 0, 1); device.DrawPrimitives(PrimitiveType.TriangleList, 0, 2); //绘制正后面 device.Transform.World = Matrix.RotationY(-(float)Math.PI / 2) * Matrix.Translation(1, 0, 0); device.DrawPrimitives(PrimitiveType.TriangleList, 0, 2); //绘制右侧面 device.Transform.World = Matrix.RotationY((float)Math.PI / 2) * Matrix.Translation(-1, 0, 0); device.DrawPrimitives(PrimitiveType.TriangleList, 0, 2); //绘制左侧面 device.Transform.World = Matrix.RotationX((float)Math.PI / 2) * Matrix.Translation(0, 1, 0); device.DrawPrimitives(PrimitiveType.TriangleList, 0, 2); //绘制下面 device.Transform.World = Matrix.RotationX(-(float)Math.PI / 2) * Matrix.Translation(0, -1, 0); device.DrawPrimitives(PrimitiveType.TriangleList, 0, 2); //绘制上面 device.EndScene(); //渲染结束 device.Present(); //更新显示区域,把后备缓存的D图形送到图形卡的显存中显示 } 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); } public void OnCreateVertexBuffer(object sender, EventArgs e) { CustomVertex.PositionColored[] verts = (CustomVertex.PositionColored[])vertexBuffer.Lock(0, 0); verts[0].Position = new Vector3(-1.0f, -1.0f, 0.0f); //顶点0位置,注意为Vector3 verts[0].Color = System.Drawing.Color.Aqua.ToArgb(); //顶点0颜色 verts[1].Position = new Vector3(1.0f, 1.0f, 0.0f); //顶点1位置 verts[1].Color = System.Drawing.Color.Brown.ToArgb(); verts[2].Position = new Vector3(1.0f, -1.0f, 0.0f); //顶点2位置 verts[2].Color = System.Drawing.Color.LightPink.ToArgb(); verts[3].Position = new Vector3(-1.0f, -1.0f, 0.0f); //顶点3位置 verts[3].Color = System.Drawing.Color.Aqua.ToArgb(); //顶点3颜色 verts[4].Position = new Vector3(-1.0f, 1.0f, 0.0f); //顶点4位置 verts[4].Color = System.Drawing.Color.Red.ToArgb(); verts[5].Position = new Vector3(1.0f, 1.0f, 0.0f); //顶点5位置 verts[5].Color = System.Drawing.Color.Brown.ToArgb(); vertexBuffer.Unlock(); } private void SetupMatrices() //修改Device的3个变换 { device.Transform.World = Matrix.RotationY(Angle); //世界变换矩阵,沿Y轴旋转 device.Transform.View = Matrix.LookAtLH(new Vector3(0.0f, 3.0f, ViewZ),//观察变换矩阵 new Vector3(0.0f, 0.0f, 0.0f), new Vector3(0.0f, 1.0f, 0.0f)); device.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4, 1.0f, 1.0f, 100.0f); //投影变换语句仍可以放到OnResetDevice方法中 } private void Form1_KeyDown(object sender, KeyEventArgs e) { switch (e.KeyCode) //e.KeyCode是键盘每个键的编号 { case Keys.Left: //Keys.Left是左箭头键编号,三角形沿Y轴左转 Angle += 0.1F; break; case Keys.Right: //三角形沿Y轴右转 Angle -= 0.1F; break; case Keys.Down: //三角形离观察者越来越远 ViewZ += 0.1F; break; case Keys.Up: //三角形离观察者越来越近 ViewZ -= 0.1F; break; } } private void Form1_Load(object sender, EventArgs e) { InitializeGraphics(); this.Show(); Render(); } } }
也可以使用顶点索引来绘制立方体,可以极大地简化程序设计。
首先定义八个顶点,由于每个面要用两个三角形进行渲染,所以应该用12个三角形进行渲染立方体,在渲染每个面的正方形时,应该保证从每个面的外侧看,渲染这个面的两个三角形应该是顺时针方向。比如说,立方体前面的正方形,A、B、C、D四个点,渲染的顺序是ABC和CBD。
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; float Angle = 0, ViewZ = -6.0f; IndexBuffer indexBuffer = 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; //阴影部分是所作修改,正方体有8个顶点 vertexBuffer = new VertexBuffer(typeof(CustomVertex.PositionColored), 8, dev, 0, CustomVertex.TransformedColored.Format, Pool.Default); indexBuffer = new IndexBuffer(typeof(int), 36, dev, 0, Pool.Default); //顶点索引 vertexBuffer.Created += new System.EventHandler(this.OnCreateVertexBuffer); indexBuffer.Created += new EventHandler(indexBuffer_Created); this.OnCreateVertexBuffer(vertexBuffer, null); this.indexBuffer_Created(indexBuffer, null); } public void OnResetDevice(object sender, EventArgs e) { Device dev = (Device)sender; //背面剔除方式为只显示顺时针三角形,因为正方体应该只看到外表面 dev.RenderState.CullMode = Cull.CounterClockwise; dev.RenderState.Lighting = false; //取消灯光 } public void Render() //渲染方法,本方法没有任何渲染代码,可认为是渲染方法的框架 { if (device == null) //如果未建立设备对象,退出 return; if (pause) return; device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Blue, 1.0f, 0); device.BeginScene(); //开始渲染 SetupMatrices(); device.SetStreamSource(0, vertexBuffer, 0); device.VertexFormat = CustomVertex.PositionColored.Format; device.Indices = indexBuffer; device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 8, 0, 12); device.EndScene(); //渲染结束 device.Present();//更新显示区域,把后备缓存的D图形送到图形卡的显存中显示 } 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); } public void OnCreateVertexBuffer(object sender, EventArgs e) { CustomVertex.PositionColored[] verts = (CustomVertex.PositionColored[])vertexBuffer.Lock(0, 0); verts[0].Position = new Vector3(-1.0f, 1.0f, 1.0f); //顶点0位置,注意为Vector3 verts[0].Color = System.Drawing.Color.Aqua.ToArgb(); //顶点0颜色 verts[1].Position = new Vector3(1.0f, 1.0f, 1.0f); //顶点1位置 verts[1].Color = System.Drawing.Color.Brown.ToArgb(); verts[2].Position = new Vector3(-1.0f, -1.0f, 1.0f); //顶点2位置 verts[2].Color = System.Drawing.Color.LightPink.ToArgb(); verts[3].Position = new Vector3(1.0f, -1.0f, 1.0f); //顶点3位置 verts[3].Color = System.Drawing.Color.Red.ToArgb(); //顶点3颜色 verts[4].Position = new Vector3(-1.0f, 1.0f, -1.0f); //顶点4位置 verts[4].Color = System.Drawing.Color.Green.ToArgb(); verts[5].Position = new Vector3(1.0f, 1.0f, -1.0f); //顶点5位置 verts[5].Color = System.Drawing.Color.Black.ToArgb(); verts[6].Position = new Vector3(-1.0f, -1.0f, -1.0f); //顶点6位置 verts[6].Color = System.Drawing.Color.LightPink.ToArgb(); verts[7].Position = new Vector3(1.0f, -1.0f, -1.0f); //顶点7位置 verts[7].Color = System.Drawing.Color.Red.ToArgb(); vertexBuffer.Unlock(); } private void SetupMatrices() //修改Device的3个变换 { device.Transform.World = Matrix.RotationY(0); //世界变换 Vector3 v1 = new Vector3(0.0f, 0.0f, -5.0f); //下句使v1点分别沿Y轴和X轴旋转 v1.TransformCoordinate(Matrix.RotationYawPitchRoll(Angle, ViewZ, 0)); device.Transform.View = Matrix.LookAtLH(v1, new Vector3(0.0f, 0.0f, 0.0f), new Vector3(0.0f, 1.0f, 0.0f)); //观察变换 device.Transform.Projection = //透视变换 Matrix.PerspectiveFovLH((float)Math.PI / 4, 1.0f, 1.0f, 100.0f); } private void Form1_KeyDown(object sender, KeyEventArgs e) { switch (e.KeyCode) //e.KeyCode是键盘每个键的编号 { case Keys.Left: //Keys.Left是左箭头键编号,三角形沿Y轴左转 Angle += 0.1F; break; case Keys.Right: //三角形沿Y轴右转 Angle -= 0.1F; break; case Keys.Down: //三角形离观察者越来越远 ViewZ += 0.1F; break; case Keys.Up: //三角形离观察者越来越近 ViewZ -= 0.1F; break; } } void indexBuffer_Created(object sender, EventArgs e) { //下面数组每3个数表示一个三角形的索引,每2个三角形绘制1个面, int[] index = //按顺序分别绘制前面、右面、上面、左面、后面和下面 { 4, 5, 6, 5, 7, 6, 5, 1, 7, 7, 1, 3, 4, 0, 1, 4, 1, 5, 2, 0, 4, 2, 4, 6, 3, 1, 0, 3, 0, 2, 2, 6, 7, 2, 7, 3 }; int[] indexV = (int[])indexBuffer.Lock(0, 0); for (int i = 0; i < 36; i++) { indexV[i] = index[i]; } indexBuffer.Unlock(); } private void Form1_Load(object sender, EventArgs e) { InitializeGraphics(); this.Show(); Render(); } } }
任意复杂的曲面都可以由若干三角形定义的平面组成。创建一个空心圆柱体,可以考虑这样分解,将上下两个圆形底面的周长进行n等分,上下相对应的等分点用线连接,将空心圆柱体外表面划分为n个小矩形,每个小矩形由两个三角形平面组成。其实,可以利用建模工具比如说3D Max,创建复杂的立体模型,然后转换为Direct 3D能够识别的.x文件,并将其显示在显示器屏幕上。假设空心圆柱体的两个圆形底面和建模坐标系的XZ平面平行,两个圆形底面的圆心连线和建模坐标的Y轴重合,圆心连线的中点在建模坐标原点,两个圆形底面的Y坐标分别为1和-1。空心圆柱体的柱表面可以近似认为是由若干正方形组成,将两个圆形底面的圆心角平分为50份,那么就可以用50个正方形来近似这个立体空心圆柱体。
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.PositionColored), 100, dev, Usage.WriteOnly, CustomVertex.TransformedColored.Format, Pool.Default); vertexBuffer.Created += new System.EventHandler(this.OnCreateVertexBuffer); this.OnCreateVertexBuffer(vertexBuffer, null); } public void OnResetDevice(object sender, EventArgs e) { Device dev = (Device)sender; dev.RenderState.CullMode = Cull.None; //取消背面剔除 dev.RenderState.Lighting = false; //取消灯光 } public void Render() //渲染方法,本方法没有任何渲染代码,可认为是渲染方法的框架 { if (device == null) //如果未建立设备对象,退出 return; if (pause) return; device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.WhiteSmoke, 1.0f, 0); device.BeginScene();//开始渲染 SetupMatrices(); // device.SetStreamSource(0, vertexBuffer, 0); device.VertexFormat = CustomVertex.PositionColored.Format; device.DrawPrimitives(PrimitiveType.TriangleStrip, 0, (4 * 25) - 2); device.EndScene();//渲染结束 device.Present();//更新显示区域,把后备缓存的D图形送到图形卡的显存中显示 } 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); } public void OnCreateVertexBuffer(object sender, EventArgs e) { CustomVertex.PositionColored[] verts = (CustomVertex.PositionColored[])vertexBuffer.Lock(0, 0); for (int i = 0; i < 50; i++) { float theta = (float)(2 * Math.PI * i) / 49; verts[2 * i].Position = new Vector3((float)Math.Sin(theta), -1, (float)Math.Cos(theta)); verts[2 * i].Color = System.Drawing.Color.LightPink.ToArgb(); verts[2 * i + 1].Position = new Vector3((float)Math.Sin(theta), 1, (float)Math.Cos(theta)); verts[2 * i + 1].Color = System.Drawing.Color.LightPink.ToArgb(); } vertexBuffer.Unlock(); } private void SetupMatrices() { device.Transform.World = Matrix.RotationAxis(new Vector3((float)Math.Cos(Environment.TickCount / 250.0f), 1, (float)Math.Sin(Environment.TickCount / 250.0f)), Environment.TickCount / 3000.0f); device.Transform.View = Matrix.LookAtLH(new Vector3(0.0f, 3.0f, -5.0f), new Vector3(0.0f, 0.0f, 0.0f), new Vector3(0.0f, 1.0f, 0.0f)); device.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4, 1.0f, 1.0f, 100.0f); } private void Form1_Load(object sender, EventArgs e) { InitializeGraphics(); Show(); Render(); } } }