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();
        }
    }
}


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