DirectX 3D燈光和材質

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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章