前面的一篇文章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.Draw