前面所介紹的東西都假設模型有自己的顏色,即認爲模型自己發光。其實自然界的大部分物體並不發光。當光線照射到物體上,物體吸收某些顏色的光,反射另一些顏色的光,反射的光的顏色就是我們所看到的物體的顏色。這裏的燈光是指光源,在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();
}
}
}