前面所介绍的东西都假设模型有自己的颜色,即认为模型自己发光。其实自然界的大部分物体并不发光。当光线照射到物体上,物体吸收某些颜色的光,反射另一些颜色的光,反射的光的颜色就是我们所看到的物体的颜色。这里的灯光是指光源,在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(); } } }