前面所介绍的东西都假设模型有自己的颜色,即认为模型自己发光。其实自然界的大部分物体并不发光。当光线照射到物体上,物体吸收某些颜色的光,反射另一些颜色的光,反射的光的颜色就是我们所看到的物体的颜色。这里的灯光是指光源,在Direct 3D中有4种光源:环境光、定向光源、点光源和聚光灯。反射光被分为3类:环境光、漫反射光和镜面高光。材质描述物体反射光的反射属性。可以用法线来计算光的反射,光照的反射强度等于代表光线的向量和三角形平面法向量的点积。
在Direct3D中,CustomVertex.PositionNormal结构记录了在建模座标系统中的顶点位置和顶点法线,这些顶点还没有经过世界变换、观察变换和投影变换。其所包含的属性主要有:Position表示点在建模座标系统中的位置,Normal表示法线向量。
用定向光源照亮立方体,立方体的每个面必须定义正确方向的法线。可以考虑首先创建立方体的一个平面的模型,同时定义这个面的法线方向。对于立方体的其他面,可以考虑用世界变换将第一个面变换到立方体的相应位置,在变换的过程中其他面也能得到正确的法线方向。最终将得到一个每面都有正确法线的立方体。
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;
Material mtrl;
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.PositionNormal), 6,
dev, 0, CustomVertex.PositionNormal.Format, Pool.Default);
vertexBuffer.Created += new System.EventHandler(this.OnCreateVertexBuffer);
this.OnCreateVertexBuffer(vertexBuffer, null);
mtrl = new Material();
mtrl.Diffuse = System.Drawing.Color.Yellow; //物体的颜色
mtrl.Ambient = System.Drawing.Color.Red; //反射环境光的颜色
}
public void OnResetDevice(object sender, EventArgs e)
{
Device dev = (Device)sender;
dev.RenderState.CullMode = Cull.CounterClockwise; //背面剔除
device.RenderState.ZBufferEnable = true; //打开Z缓冲
device.RenderState.Lighting = true; //打开灯光
mtrl = new Material();
mtrl.Diffuse = System.Drawing.Color.Yellow; //物体的颜色
mtrl.Ambient = System.Drawing.Color.Red; //反射环境光的颜色
SetupLights();
}
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.PositionNormal.Format;
device.Transform.World = Matrix.Translation(0, 0, -1);
//以下和6.6节例子渲染方法Render中内容相同
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.PositionNormal[] verts =
(CustomVertex.PositionNormal[])vertexBuffer.Lock(0, 0);
verts[0].Position = new Vector3(-1.0f, -1.0f, 0.0f); //顶点0位置,注意为Vector3
verts[0].Normal = new Vector3(0, 0, -1); //顶点0法线
verts[1].Position = new Vector3(1.0f, 1.0f, 0.0f); //顶点1位置
verts[1].Normal = new Vector3(0, 0, -1); //顶点1法线
verts[2].Position = new Vector3(1.0f, -1.0f, 0.0f); //顶点2位置
verts[2].Normal = new Vector3(0, 0, -1);
verts[3].Position = new Vector3(-1.0f, -1.0f, 0.0f); //顶点3位置
verts[3].Normal = new Vector3(0, 0, -1); //顶点3法线
verts[4].Position = new Vector3(-1.0f, 1.0f, 0.0f); //顶点4位置
verts[4].Normal = new Vector3(0, 0, -1);
verts[5].Position = new Vector3(1.0f, 1.0f, 0.0f); //顶点5位置
verts[5].Normal = new Vector3(0, 0, -1);
vertexBuffer.Unlock();
}
private void SetupMatrices() //注意世界变换和观察变换参数可能要改变
{
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 SetupLights()
{
device.Material = mtrl;
device.Lights[0].Type = LightType.Directional;
device.Lights[0].Diffuse = System.Drawing.Color.White; //光的颜色为白色
device.Lights[0].Direction = new Vector3(0, -2, 4);//灯光方向从观察者上方指向屏幕下方
device.Lights[0].Update(); //更新灯光设置,创建第一盏灯光
device.Lights[0].Enabled = true; //使设置有效,下句设置环境光为白色
device.RenderState.Ambient = System.Drawing.Color.FromArgb(0x808080);
}
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();
}
}
}
对于点光源有一个Range属性,表示点光源的最大照射范围,光源不能照射到此值以外的物体,点光源没有Direction属性。用点光源照亮空心圆柱体,空心圆柱体在三个方向上旋转,但是点光源只是固定地照射到空心圆柱体的正前方,实现如下:
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;
Material mtrl;
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.PositionNormal), 100, dev,
Usage.WriteOnly, CustomVertex.PositionNormal.Format, Pool.Default);
vertexBuffer.Created += new System.EventHandler(this.OnCreateVertexBuffer);
this.OnCreateVertexBuffer(vertexBuffer, null);
mtrl = new Material();
mtrl.Diffuse = System.Drawing.Color.Red; //物体的颜色
mtrl.Ambient = System.Drawing.Color.White; //反射环境光的颜色
}
public void OnResetDevice(object sender, EventArgs e)
{
Device dev = (Device)sender;
dev.RenderState.CullMode = Cull.None; //取消背面剔除
device.RenderState.ZBufferEnable = true; //打开Z缓冲
device.RenderState.Lighting = true; //打开灯光
}
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(); //开始渲染
SetupLights(); //设置灯光,程序运行期间灯光要改变,必须放在此处
SetupMatrices();
device.SetStreamSource(0, vertexBuffer, 0);
device.VertexFormat = CustomVertex.PositionNormal.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.PositionNormal[] verts =
(CustomVertex.PositionNormal[])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].Normal = new Vector3((float)Math.Sin(theta), 0, (float)Math.Cos(theta));
verts[2 * i + 1].Position = new Vector3((float)Math.Sin(theta), 1, (float)Math.Cos(theta));
verts[2 * i + 1].Normal = new Vector3((float)Math.Sin(theta), 0, (float)Math.Cos(theta));
}
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);
//device.Transform.World = Matrix.RotationY(0);
//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 SetupLights()
{
device.Material = mtrl;
device.Lights[0].Type = LightType.Point;
device.Lights[0].Diffuse = System.Drawing.Color.White;
device.Lights[0].Range = 20.0f;
device.Lights[0].Position = new Vector3(0, 2, -4); //设置灯光位置,注意光线的方向
device.Lights[0].Attenuation1 = 0.2f;
device.Lights[0].Enabled = true; //使设置有效
device.Lights[0].Update(); //更新灯光设置,创建第一盏灯光
device.RenderState.Ambient = System.Drawing.Color.FromArgb(0x808080);
}
private void Form1_Load(object sender, EventArgs e)
{
InitializeGraphics();
this.Show();
Render();
}
}
}
聚光灯光源必须首先设置代表聚光灯光源的Light类对象属性Type=LightType.Spot,表示为聚光灯光源。聚光灯光源和点光源的有些属性是相同的。聚光灯光源照到物体上形成两个圆形,小圆是聚光灯光源直射的结果,显得比较明亮,大圆不是聚光灯光源直射的结果,所有显得比小圆暗。聚光灯照亮三角形,实现如下:
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;
Material mtrl;
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.PositionNormal), 3, dev,
Usage.WriteOnly, CustomVertex.PositionNormal.Format, Pool.Default);
vertexBuffer.Created += new System.EventHandler(this.OnCreateVertexBuffer);
this.OnCreateVertexBuffer(vertexBuffer, null);
mtrl = new Material();
mtrl.Diffuse = System.Drawing.Color.Yellow; //物体的颜色
mtrl.Ambient = System.Drawing.Color.Red; //反射环境光的颜色
}
public void OnResetDevice(object sender, EventArgs e)
{
Device dev = (Device)sender;
dev.RenderState.CullMode = Cull.None; //取消背面剔除
device.RenderState.ZBufferEnable = true; //打开Z缓冲
device.RenderState.Lighting = true; //打开灯光
SetupLights(); //设置灯光,程序运行期间灯光不改变,可以放在此处
}
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.PositionNormal.Format;
device.DrawPrimitives(PrimitiveType.TriangleList, 0, 1);
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.PositionNormal[] verts =//建模,请注意建模的笛卡儿座标原点在右下角。
(CustomVertex.PositionNormal[])vertexBuffer.Lock(0, 0);
verts[0].Position = new Vector3(-1.0f, -1.0f, 0.0f); //顶点0位置,注意为Vector3
verts[0].Normal = new Vector3(0, 0, -1); //顶点0法线,沿Z轴反方向,指向观察者
verts[1].Position = new Vector3(0.0f, 1.0f, 0.0f); //顶点1位置
verts[1].Normal = new Vector3(0, 0, -1);
verts[2].Position = new Vector3(1.0f, -1.0f, 0.0f); //顶点2位置
verts[2].Normal = new Vector3(0, 0, -1);
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 SetupLights()
{
device.Material = mtrl;
device.Lights[0].Type = LightType.Spot;
device.Lights[0].Diffuse = System.Drawing.Color.White;
device.Lights[0].Range = 20.0f;
device.Lights[0].Position = new Vector3(0, 0, -4); //设置灯光位置
device.Lights[0].Direction = new Vector3(0, 0, 4); //设置灯光方向
device.Lights[0].InnerConeAngle = 0.5f;
//device.Lights[0].InnerConeAngle = 0.2f;
device.Lights[0].OuterConeAngle = 1.0f;
//device.Lights[0].OuterConeAngle = 0.5f;
device.Lights[0].Falloff = 1.0f;
device.Lights[0].Attenuation0 = 1.0f;
device.Lights[0].Enabled = true; //使设置有效
device.Lights[0].Update(); //更新灯光设置,创建第一盏灯光
device.RenderState.Ambient = System.Drawing.Color.FromArgb(0x808080);
}
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();
}
}
}
任何一个平面都由若干个三角形组成,光源必须照射到三角形的三个顶点上,三角形才能显出颜色。三角形平面的颜色由三角形三个顶点的颜色决定。如果三角形的顶点没有光源照射到就显示为黑色。如果希望聚光灯照到并在地板上能够看到光圈,那么必须使地板由许多小三角形组成,使聚光灯光源照射到这些小三角形的顶点上,使这些三角形发出颜色,从而显示聚光灯光源照射所形成的圆形光圈。
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;
Material mtrl;
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.PositionNormal), 6, dev,
Usage.WriteOnly, CustomVertex.PositionNormal.Format, Pool.Default);
vertexBuffer.Created += new System.EventHandler(this.OnCreateVertexBuffer);
this.OnCreateVertexBuffer(vertexBuffer, null);
mtrl = new Material();
mtrl.Diffuse = System.Drawing.Color.Yellow; //物体的颜色
mtrl.Ambient = System.Drawing.Color.Red; //反射环境光的颜色
}
public void OnResetDevice(object sender, EventArgs e)
{
Device dev = (Device)sender;
dev.RenderState.CullMode = Cull.None; //取消背面剔除
device.RenderState.ZBufferEnable = true; //打开Z缓冲
device.RenderState.Lighting = true; //打开灯光
SetupLights(); //设置灯光,程序运行期间灯光不改变,可以放在此处
}
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(); //开始渲染
SetupMatrices();
device.SetStreamSource(0, vertexBuffer, 0);
device.VertexFormat = CustomVertex.PositionNormal.Format;
float m = 2.0f, n = 2.0f;
for (int i = 0; i < 10; i++)
{
m = 2.0f;
for (int j = 0; j < 10; j++)
{
device.Transform.World = Matrix.Translation(m, 0, n);
device.DrawPrimitives(PrimitiveType.TriangleList, 0, 2);
m -= 0.4f;
}
n -= 0.4f;
}
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.PositionNormal[] verts =
(CustomVertex.PositionNormal[])vertexBuffer.Lock(0, 0);
verts[0].Position = new Vector3(-0.2f, -2.0f, -0.2f); //顶点0位置,注意为Vector3
verts[0].Normal = new Vector3(0, 1, 0); //顶点0法线,沿Y轴方向
verts[1].Position = new Vector3(-0.2f, -2.0f, 0.2f); //顶点1位置
verts[1].Normal = new Vector3(0, 1, 0);
verts[2].Position = new Vector3(0.2f, -2.0f, 0.2f); //顶点2位置
verts[2].Normal = new Vector3(0, 1, 0);
verts[3].Position = new Vector3(-0.2f, -2.0f, -0.2f); //顶点3位置
verts[3].Normal = new Vector3(0, 1, 0);
verts[4].Position = new Vector3(0.2f, -2.0f, 0.2f); //顶点4位置
verts[4].Normal = new Vector3(0, 1, 0);
verts[5].Position = new Vector3(0.2f, -2.0f, -0.2f); //顶点5位置
verts[5].Normal = new Vector3(0, 1, 0);
vertexBuffer.Unlock();
}
private void SetupMatrices() //修改Device的3个变换
{
device.Transform.World = Matrix.RotationY(0); //世界变换矩阵
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 SetupLights()
{
device.Material = mtrl;
device.Lights[0].Type = LightType.Spot;
device.Lights[0].Diffuse = System.Drawing.Color.White;
device.Lights[0].Range = 20.0f;
device.Lights[0].Position = new Vector3(0, 4, -6); //设置灯光位置
device.Lights[0].Direction = new Vector3(0, -4, 4); //设置灯光方向
device.Lights[0].InnerConeAngle = 0.2f; //值较大时,例如为0.5,地板变为黄色
device.Lights[0].OuterConeAngle = 0.5f; //值较大时,例如为1.0,地板变为黄色
device.Lights[0].Falloff = 1.0f;
device.Lights[0].Attenuation0 = 1.0f;
device.Lights[0].Enabled = true; //使设置有效
device.Lights[0].Update(); //更新灯光设置,创建第一盏灯光
device.RenderState.Ambient = System.Drawing.Color.FromArgb(0x808080);
}
private void Form1_Load(object sender, EventArgs e)
{
InitializeGraphics();
this.Show();
Render();
}
}
}
当光线照射到一个表面非常光滑的金属球时,将在球体的表面形成一块高亮的区域,Direct 3D中用镜面高光来模拟这种现象,SpecularSharpness属性标识,点光源照亮空心圆柱,并增加镜面高光。空心圆柱在三个方向不停地旋转,点光源只是固定地照射到空心圆柱的正前方。
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;
Material mtrl;
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.PositionNormal), 100,
dev, 0, CustomVertex.PositionNormal.Format, Pool.Default);
vertexBuffer.Created += new System.EventHandler(this.OnCreateVertexBuffer);
this.OnCreateVertexBuffer(vertexBuffer, null);
mtrl = new Material();
mtrl.Diffuse = System.Drawing.Color.Yellow; //物体的颜色
mtrl.Ambient = System.Drawing.Color.Red; //反射环境光的颜色
mtrl.Specular = System.Drawing.Color.White;
mtrl.SpecularSharpness = 10.0f;
}
public void OnResetDevice(object sender, EventArgs e)
{
Device dev = (Device)sender;
dev.RenderState.CullMode = Cull.None; //取消背面剔除
device.RenderState.ZBufferEnable = true; //打开Z缓冲
device.RenderState.Lighting = true; //打开灯光
device.RenderState.SpecularEnable = true; //打开反射
SetupLights(); //设置灯光
}
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(); //开始渲染
SetupLights(); //设置灯光,程序运行期间灯光要改变,必须放在此处
SetupMatrices();
device.SetStreamSource(0, vertexBuffer, 0);
device.VertexFormat = CustomVertex.PositionNormal.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.PositionNormal[] verts =
(CustomVertex.PositionNormal[])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].Normal = new Vector3((float)Math.Sin(theta), 0, (float)Math.Cos(theta));
verts[2 * i + 1].Position = new Vector3((float)Math.Sin(theta), 1, (float)Math.Cos(theta));
verts[2 * i + 1].Normal = new Vector3((float)Math.Sin(theta), 0, (float)Math.Cos(theta));
}
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);
//device.Transform.World = Matrix.RotationY(0);
//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 SetupLights()
{
device.Material = mtrl;
device.Lights[0].Type = LightType.Point;
device.Lights[0].Diffuse = System.Drawing.Color.White;
device.Lights[0].Range = 20.0f;
device.Lights[0].Position = new Vector3(0, 2, -4); //设置灯光位置,注意光线的方向
device.Lights[0].Attenuation1 = 0.2f;
device.Lights[0].Specular = System.Drawing.Color.White;
device.Lights[0].Enabled = true;
device.RenderState.Ambient = System.Drawing.Color.FromArgb(0x808080);
}
private void Form1_Load(object sender, EventArgs e)
{
InitializeGraphics();
this.Show();
Render();
}
}
}