在一些大型的3D游戏中,有几个必不可少的元素,比如说天空和大地,这些元素的存在可以增加3D场景的真实感。三维场景中天空和大地场景的模拟其实很简单,这种场景跟古人所说的“天圆地方”有着异曲同工之妙。天空其实就是一个很大的容器,把整个世界都罩在下面,大地就是一个平面,场景中所有的元素都显示在二者所包围的空间中。三维天空的技术主要包括三种类型:一种是平面型天空(Sky Plane),仅用一个平面盖在所有元素的头顶。这种技术有点弱,很容易被识破,真实感也很低,有时还需要用雾来覆盖远景以增加真实感,但是效果和技术含量依然很低;还有一种就是天空穹庐(Sky Dome),有时也称为天空球,即放到场景元素头顶上的是一个曲面,通常都会为一个半球,这种技术的真实性和立体感最强,但是不是目前使用最广泛的方案,通常会涉及到天空无缝衔接的素材匮乏等的问题;另外一种就是天空盒子(Sky Box),即把天空做成一个立方体,所有的元素都罩在其下,这种技术是目前使用最广泛的三维天空模拟技术,在一些高级的应用中,天空盒子的纹理可能同时会用来生成Cube Map,并用之来做水面倒影、云影、反光等很眩的特效,网络上关于这方面的素材也很多,感兴趣的可以自己搜一下。这里主要给出后面两种模型的实现即天空球和天空盒子,引擎框架用的是微软的DirectX,用C#来实现。

天空球

因为最近在研究道路的三维仿真,所以实现的背景就是道路的三维显示,为了增加真实感,在其上罩了一个天空球。关于道路的三维显示,采用了一个很简单的思路,利用传感器采集的道路数据,一般是是BMP格式的,把采集获得的BMP数据的输出作为道路的高程图(Height Map),根据各个点像素值计算道路的高程值(世界坐标系中Y方向上的高度值),建立顶点缓冲和顶点索引缓冲,再进行三角形构网和道路的纹理贴图就OK了。Demo版本的思路暂时是这样的,这里给出Demo版本的实现。这里贴上一张道路的三维网格效果图如下:

天空球的实现更加简单,利用专业的软件3dsMax把天空模型和大地模型做好,导出.x文件,再利用DirectX提供的接口将其加载进去,然后进行相应的坐标系空间转换调整一下就行了。3dsMax下建好的模型如下:

下面再贴上一张DirectX下实现的带有天空球的道路模型:

这里贴上该模型实现的核心代码:

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;
using System.IO;namespace RoadSkydom
{public partial class RoadSkydom : Form{private Device m_device = null;bool pause = false;Mesh skyboxMesh = null;Material skyboxMeshMaterials;Texture[] skyboxMeshTextures;Microsoft.DirectX.Direct3D.Material[] meshMaterials1;float Angle = 0, ViewZ = -5.0f;private float angleY = 0.01f;//定义绕Y轴旋转变量private int mouseLastX, mouseLastY;//记录鼠标按下时的坐标位置private bool isRotateByMouse = false;//记录是否由鼠标控制旋转private bool isMoveByMouse = false;//记录是否由鼠标控制移动private CustomVertex.PositionTextured[] vertices;//定义顶点变量private Texture texture;//定义贴图变量private Material material;//定义材质变量private VertexBuffer vertexBuffer;//定义顶点缓冲变量private IndexBuffer indexBuffer;//定义索引缓冲变量private int[] indices;//定义索引号变量private int xCount = 5, yCount = 4;//定义横向和纵向网格数目private float cellHeight = 1f, cellWidth = 1f;//定义单元的宽度和长度Material bottomMaterial;//底部材质变量Texture bottomTexture;//底部纹理变量VertexBuffer bottomVertexBuffer = null;//保存建立底部正方形的顶点private VertexBuffer borderFrontVertexBuffer = null;private CustomVertex.PositionColored[] borderFrontVertices;//定义前面边缘顶点变量private IndexBuffer borderFrontIndexBuffer;//定义前面边缘顶点索引缓冲变量private int[] borderFrontIndices;//定义前面边缘顶点的索引号变量private VertexBuffer borderBackVertexBuffer = null;private CustomVertex.PositionColored[] borderBackVertices;//定义背面边缘顶点变量private IndexBuffer borderBackIndexBuffer;//定义背面边缘顶点索引缓冲变量private int[] borderBackIndices;//定义背面边缘顶点的索引号变量private VertexBuffer borderLeftVertexBuffer = null;private CustomVertex.PositionColored[] borderLeftVertices;//定义左面边缘顶点变量private IndexBuffer borderLeftIndexBuffer;//定义左面边缘顶点索引缓冲变量private int[] borderLeftIndices;//定义左面边缘顶点的索引号变量private VertexBuffer borderRightVertexBuffer = null;private CustomVertex.PositionColored[] borderRightVertices;//定义右面边缘顶点变量private IndexBuffer borderRightIndexBuffer;//定义右面边缘顶点索引缓冲变量private int[] borderRightIndices;//定义右面边缘顶点的索引号变量public RoadSkydom(){InitializeComponent();}public bool InitializeGraphics(){try{PresentParameters presentParams = new PresentParameters();presentParams.Windowed = true;              //不是全屏显示,在一个窗口显示presentParams.SwapEffect = SwapEffect.Discard;       //后备缓存交换的方式presentParams.EnableAutoDepthStencil = true;            //允许使用自动深度模板测试//深度缓冲区单元为16位二进制数presentParams.AutoDepthStencilFormat = DepthFormat.D16;m_device = new Device(0, DeviceType.Hardware, this,     //建立设备类对象CreateFlags.SoftwareVertexProcessing, presentParams);//设置设备重置事件(device.DeviceReset)事件函数为this.OnResetDevicem_device.DeviceReset += new System.EventHandler(this.OnResetDevice);this.OnCreateDevice(m_device, null);//自定义方法,初始化Device的工作放到这个方法中this.OnResetDevice(m_device, null);//调用设备重置事件(device.DeviceReset)事件函数}      //设备重置事件函数要设置Device参数,初始函数中必须调用该函数catch (DirectXException){return false;}return true;}public void OnCreateDevice(object sender, EventArgs e){Device device = (Device)sender;ExtendedMaterial[] materials = null;//设定运行程序所在目录的上两级目录为当前默认目录Directory.SetCurrentDirectory(Application.StartupPath + @"\..\..\..\");GraphicsStream adjacency;skyboxMesh = Mesh.FromFile("skydom_bottom.x", MeshFlags.Managed, m_device, out adjacency, out materials);if (skyboxMeshTextures == null)         //如果还未设置纹理,为3D图形增加纹理和材质{skyboxMeshTextures = new Texture[materials.Length];             //纹理数组meshMaterials1 = new Material[materials.Length];         //材质数组for (int i = 0; i < materials.Length; i++)                  //读入纹理和材质{meshMaterials1[i] = materials[i].Material3D;meshMaterials1[i].Ambient = meshMaterials1[i].Diffuse;skyboxMeshTextures[i] = TextureLoader.FromFile(m_device,materials[i].TextureFilename);}} //下句优化Mesh,减少属性的状态改变提高渲染速度skyboxMesh.Optimize(MeshFlags.Managed | MeshFlags.OptimizeAttributeSort, adjacency);material = new Material();material.Diffuse = Color.White;material.Specular = Color.LightGray;material.SpecularSharpness = 15.0F;device.Material = material;texture = TextureLoader.FromFile(device, @"F:\\workdir\\VC# Based DirectX\\RoadTexture.jpg");//底部材料和贴图bottomMaterial = new Material();bottomMaterial.Ambient = Color.FromArgb(200, 255, 255, 255);bottomMaterial.Diffuse = Color.FromArgb(200, 255, 255, 255);bottomTexture = TextureLoader.FromFile(device, "F:\\workdir\\VC# Based DirectX\\BottomTexture.bmp");bottomVertexBuffer = new VertexBuffer(typeof(CustomVertex.PositionNormalTextured), 6, device, 0, CustomVertex.PositionNormalTextured.Format, Pool.Default);bottomVertexBuffer.Created += new System.EventHandler(this.OnCreateBottomVertexBuffer);this.OnCreateBottomVertexBuffer(device, null);}//底部顶点public void OnCreateBottomVertexBuffer(object sender, EventArgs e){CustomVertex.PositionNormalTextured[] bottomVerts = (CustomVertex.PositionNormalTextured[])bottomVertexBuffer.Lock(0, 0);//绘制底面正方形的6个顶点string bitmapPath = @"F:\\workdir\\VC# Based DirectX\\RoadHeight.BMP";float minHeight = GetMinHeight(bitmapPath);float xWidth = GetBitMapWidth(bitmapPath);float yHeight = GetBitMapHeight(bitmapPath);bottomVerts[0].Position = new Vector3(-100.0f, minHeight - 5.0f, -100.0f);bottomVerts[0].Normal = new Vector3(0, 0, -1);bottomVerts[0].Tu = 0.0f;//顶点0纹理坐标TubottomVerts[0].Tv = 5.0f;//纹理图片沿Y轴方向重复贴图50次bottomVerts[1].Position = new Vector3(-100.0f, minHeight - 5.0f, yHeight + 100.0f);bottomVerts[1].Normal = new Vector3(0, 0, -1);bottomVerts[1].Tu = 0.0f;bottomVerts[1].Tv = 0.0f;bottomVerts[2].Position = new Vector3(xWidth + 100.0f, minHeight - 5.0f, yHeight + 100.0f);bottomVerts[2].Normal = new Vector3(0, 0, -1);bottomVerts[2].Tu = 5.0f;bottomVerts[2].Tv = 0.0f;bottomVerts[3].Position = new Vector3(-100.0f, minHeight - 5.0f, -100.0f);bottomVerts[3].Normal = new Vector3(0, 0, -1);bottomVerts[3].Tu = 0.0f;bottomVerts[3].Tv = 5.0f;bottomVerts[4].Position = new Vector3(xWidth + 100.0f, minHeight - 5.0f, yHeight + 100.0f);bottomVerts[4].Normal = new Vector3(0, 0, -1);bottomVerts[4].Tu = 5.0f;bottomVerts[4].Tv = 0.0f;bottomVerts[5].Position = new Vector3(xWidth + 100.0f, minHeight - 5.0f, -100.0f);bottomVerts[5].Normal = new Vector3(0, 0, -1);bottomVerts[5].Tu = 5.0f;bottomVerts[5].Tv = 5.0f;bottomVertexBuffer.Unlock();}//避免精度损失public float GetBitMapHeight(string bitmapPath){Bitmap bitmap = new Bitmap(bitmapPath);xCount = (bitmap.Width - 1) / 2;yCount = xCount;cellWidth = bitmap.Width / xCount;cellHeight = bitmap.Height / yCount;return (float)(yCount * cellHeight);}//避免精度损失public float GetBitMapWidth(string bitmapPath){Bitmap bitmap = new Bitmap(bitmapPath);xCount = (bitmap.Width - 1) / 2;yCount = xCount;cellWidth = bitmap.Width / xCount;cellHeight = bitmap.Height / yCount;return (float)(xCount * cellWidth);}//获得高度图Y方向上的最小高度值 public float GetMinHeight(string bitmapPath){float minHeight = 6553500.0f;Bitmap bitmap = new Bitmap(bitmapPath);xCount = (bitmap.Width - 1) / 2;yCount = xCount;cellWidth = bitmap.Width / xCount;cellHeight = bitmap.Height / yCount;for (int i = 0; i < yCount + 1; i++)for (int j = 0; j < xCount + 1; j++){Color color = bitmap.GetPixel((int)(j * cellWidth), (int)(i * cellHeight));float height = float.Parse(color.R.ToString()) +float.Parse(color.G.ToString()) + float.Parse(color.B.ToString());height /= 10;if (height < minHeight)minHeight = height;}return minHeight;}public void OnResetDevice(object sender, EventArgs e){Device device = (Device)sender;device.RenderState.ZBufferEnable = true;            //允许使用深度缓冲device.RenderState.Ambient = System.Drawing.Color.White;//设定环境光为白色device.Lights[0].Type = LightType.Directional;    //设置灯光类型device.Lights[0].Diffuse = Color.White;            //设置灯光颜色device.Lights[0].Direction = new Vector3(0, -1, 0);    //设置灯光位置device.Lights[0].Update();                      //更新灯光设置,创建第一盏灯光device.Lights[0].Enabled = true;                //使设置有效string bitmapPath = @"F:\\workdir\\VC# Based DirectX\\RoadHeight.BMP";Bitmap bitmap = new Bitmap(bitmapPath);xCount = (bitmap.Width - 1) / 2;yCount = xCount;cellWidth = bitmap.Width / xCount;cellHeight = bitmap.Height / yCount;vertexBuffer = new VertexBuffer(typeof(CustomVertex.PositionTextured), (xCount + 1) * (yCount + 1), device,Usage.Dynamic | Usage.WriteOnly, CustomVertex.PositionColored.Format, Pool.Default);vertices = new CustomVertex.PositionTextured[(xCount + 1) * (yCount + 1)];//定义顶点for (int i = 0; i < yCount + 1; i++){for (int j = 0; j < xCount + 1; j++){Color color = bitmap.GetPixel((int)(j * cellWidth), (int)(i * cellHeight));float height = float.Parse(color.R.ToString()) + float.Parse(color.G.ToString()) + float.Parse(color.B.ToString());height /= 10;vertices[j + i * (xCount + 1)].Position = new Vector3(j * cellWidth, height, i * cellHeight);vertices[j + i * (xCount + 1)].Tu = (float)j / (xCount + 1);vertices[j + i * (xCount + 1)].Tv = (float)i / (yCount + 1);}}vertexBuffer.SetData(vertices, 0, LockFlags.None);CamTarget = new Vector3(bitmap.Width / 2, 0f, bitmap.Height / 2);//设置摄像机目标位置indexBuffer = new IndexBuffer(typeof(int), 6 * xCount * yCount, device, Usage.WriteOnly, Pool.Default);indices = new int[6 * xCount * yCount];for (int i = 0; i < yCount; i++){for (int j = 0; j < xCount; j++){indices[6 * (j + i * xCount)] = j + i * (xCount + 1);indices[6 * (j + i * xCount) + 1] = j + (i + 1) * (xCount + 1);indices[6 * (j + i * xCount) + 2] = j + i * (xCount + 1) + 1;indices[6 * (j + i * xCount) + 3] = j + i * (xCount + 1) + 1;indices[6 * (j + i * xCount) + 4] = j + (i + 1) * (xCount + 1);indices[6 * (j + i * xCount) + 5] = j + (i + 1) * (xCount + 1) + 1;}}indexBuffer.SetData(indices, 0, LockFlags.None);/*******第1个四周边界********/float minY = GetMinHeight(bitmapPath) - 5.0f;borderFrontVertexBuffer = new VertexBuffer(typeof(CustomVertex.PositionColored),2 * (xCount + 1),//四周的一个面封闭总共所需要的顶点的数目device,Usage.Dynamic | Usage.WriteOnly,CustomVertex.PositionColored.Format,Pool.Default);borderFrontVertices = new CustomVertex.PositionColored[2 * (xCount + 1)];//定义顶点int k;for (k = 0; k < xCount + 1; k++)//X轴上的点的定义{borderFrontVertices[k].Position = new Vector3(k * cellWidth, minY, 0.0f);borderFrontVertices[k].Color = System.Drawing.Color.Red.ToArgb();}for (; k < 2 * (xCount + 1); k++)//高程图上的边界点的定义{Color color = bitmap.GetPixel((int)((k - xCount - 1) * cellWidth), 0);float height = float.Parse(color.R.ToString()) + float.Parse(color.G.ToString()) + float.Parse(color.B.ToString());height /= 10;borderFrontVertices[k].Position = new Vector3((k - xCount - 1) * cellWidth, height, 0);//i * cellHeight=0borderFrontVertices[k].Color = System.Drawing.Color.Aqua.ToArgb();}borderFrontVertexBuffer.SetData(borderFrontVertices, 0, LockFlags.None);borderFrontIndexBuffer = new IndexBuffer(typeof(int),6 * xCount * 1,device,Usage.WriteOnly,Pool.Default);borderFrontIndices = new int[6 * xCount * 1];//初始化索引顶点for (int j = 0; j < xCount; j++){borderFrontIndices[6 * (j)] = j;borderFrontIndices[6 * (j) + 1] = j + (xCount + 1);borderFrontIndices[6 * (j) + 2] = j + 1;borderFrontIndices[6 * (j) + 3] = j + 1;borderFrontIndices[6 * (j) + 4] = j + (xCount + 1);borderFrontIndices[6 * (j) + 5] = j + (xCount + 1) + 1;}borderFrontIndexBuffer.SetData(borderFrontIndices, 0, LockFlags.None);/**第2个四周边界**/borderBackVertexBuffer = new VertexBuffer(typeof(CustomVertex.PositionColored),2 * (xCount + 1),//四周的一个面封闭总共所需要的顶点的数目device,Usage.Dynamic | Usage.WriteOnly,CustomVertex.PositionColored.Format,Pool.Default);borderBackVertices = new CustomVertex.PositionColored[2 * (xCount + 1)];//定义顶点for (k = 0; k < xCount + 1; k++)//X轴平行方向上的点的定义{borderBackVertices[k].Position = new Vector3(k * cellWidth, minY, yCount * cellHeight);borderBackVertices[k].Color = System.Drawing.Color.Aqua.ToArgb();}for (; k < 2 * (xCount + 1); k++)//高程图上的边界点的定义{Color color = bitmap.GetPixel((int)((k - xCount - 1) * cellWidth), (int)(yCount * cellHeight));//不能直接写死float height = float.Parse(color.R.ToString()) + float.Parse(color.G.ToString()) + float.Parse(color.B.ToString());height /= 10;borderBackVertices[k].Position = new Vector3((k - xCount - 1) * cellWidth, height, yCount * cellHeight);borderBackVertices[k].Color = System.Drawing.Color.Aqua.ToArgb();}borderBackVertexBuffer.SetData(borderBackVertices, 0, LockFlags.None);borderBackIndexBuffer = new IndexBuffer(typeof(int),6 * xCount * 1,device,Usage.WriteOnly,Pool.Default);borderBackIndices = new int[6 * xCount * 1];//初始化索引顶点for (int j = 0; j < xCount; j++){borderBackIndices[6 * (j)] = j;borderBackIndices[6 * (j) + 1] = j + (xCount + 1);borderBackIndices[6 * (j) + 2] = j + 1;borderBackIndices[6 * (j) + 3] = j + 1;borderBackIndices[6 * (j) + 4] = j + (xCount + 1);borderBackIndices[6 * (j) + 5] = j + (xCount + 1) + 1;}borderBackIndexBuffer.SetData(borderBackIndices, 0, LockFlags.None);int diff = yCount - xCount;/**第3个四周边界**/borderLeftVertexBuffer = new VertexBuffer(typeof(CustomVertex.PositionColored),2 * (yCount + 1),//四周的一个面封闭总共所需要的顶点的数目device,Usage.Dynamic | Usage.WriteOnly,CustomVertex.PositionColored.Format,Pool.Default);borderLeftVertices = new CustomVertex.PositionColored[2 * (yCount + 1)];//定义顶点for (k = 0; k < yCount + 1; k++)//Z轴平行方向上的点的定义{borderLeftVertices[k].Position = new Vector3(0, minY, k * cellHeight);borderLeftVertices[k].Color = System.Drawing.Color.Aqua.ToArgb();}for (; k < 2 * (yCount + 1); k++)//高程图上的边界点的定义{Color color = bitmap.GetPixel(0, (int)((k - yCount - 1) * cellHeight));//边界点不能直接传进去写死,容易产生溢出的异常float height = float.Parse(color.R.ToString()) + float.Parse(color.G.ToString()) + float.Parse(color.B.ToString());height /= 10;borderLeftVertices[k].Position = new Vector3(0, height, (k - yCount - 1) * cellHeight);borderLeftVertices[k].Color = System.Drawing.Color.Aqua.ToArgb();}borderLeftVertexBuffer.SetData(borderLeftVertices, 0, LockFlags.None);borderLeftIndexBuffer = new IndexBuffer(typeof(int),6 * yCount * 1,device,Usage.WriteOnly,Pool.Default);borderLeftIndices = new int[6 * yCount * 1];//初始化索引顶点for (int j = 0; j < yCount; j++){borderLeftIndices[6 * (j)] = j;borderLeftIndices[6 * (j) + 1] = j + (yCount + 1);borderLeftIndices[6 * (j) + 2] = j + 1;borderLeftIndices[6 * (j) + 3] = j + 1;borderLeftIndices[6 * (j) + 4] = j + (yCount + 1);borderLeftIndices[6 * (j) + 5] = j + (yCount + 1) + 1;}borderLeftIndexBuffer.SetData(borderLeftIndices, 0, LockFlags.None);//MessageBox.Show(borderLeftIndices[0]+","+borderLeftIndices[1]+","+borderLeftIndices[2]);/**第4个四周边界**/borderRightVertexBuffer = new VertexBuffer(typeof(CustomVertex.PositionColored),2 * (yCount + 1),//四周的一个面封闭总共所需要的顶点的数目device,Usage.Dynamic | Usage.WriteOnly,CustomVertex.PositionColored.Format,Pool.Default);borderRightVertices = new CustomVertex.PositionColored[2 * (yCount + 1)];//定义顶点for (k = 0; k < yCount + 1; k++)//Z轴平行方向上的点的定义{borderRightVertices[k].Position = new Vector3(xCount * cellWidth, minY, k * cellHeight);borderRightVertices[k].Color = System.Drawing.Color.Aqua.ToArgb();}for (; k < 2 * (yCount + 1); k++)//高程图上的边界点的定义{Color color = bitmap.GetPixel((int)(xCount * cellWidth), (int)((k - yCount - 1) * cellHeight));//边界点不能直接传进去写死,容易产生溢出和精度损失float height = float.Parse(color.R.ToString()) + float.Parse(color.G.ToString()) + float.Parse(color.B.ToString());height /= 10;borderRightVertices[k].Position = new Vector3(xCount * cellWidth, height, (k - yCount - 1) * cellHeight);borderRightVertices[k].Color = System.Drawing.Color.Aqua.ToArgb();}borderRightVertexBuffer.SetData(borderRightVertices, 0, LockFlags.None);borderRightIndexBuffer = new IndexBuffer(typeof(int),6 * yCount * 1,device,Usage.WriteOnly,Pool.Default);borderRightIndices = new int[6 * yCount * 1];//初始化索引顶点for (int j = 0; j < yCount; j++){borderRightIndices[6 * (j)] = j;borderRightIndices[6 * (j) + 1] = j + (yCount + 1);borderRightIndices[6 * (j) + 2] = j + 1;borderRightIndices[6 * (j) + 3] = j + 1;borderRightIndices[6 * (j) + 4] = j + (yCount + 1);borderRightIndices[6 * (j) + 5] = j + (yCount + 1) + 1;}borderRightIndexBuffer.SetData(borderRightIndices, 0, LockFlags.None);device.SamplerState[0].MagFilter = TextureFilter.Linear;//使用纹理滤波器进行线性滤波}private Vector3 CamPostion = new Vector3(0.0f, 0.0f, -100.0f);//定义摄像机位置private Vector3 CamTarget = new Vector3(0.0f, 0.0f, 0.0f);//定义摄像机目标位置
//         void SetupMatrices()
//         {
//             m_device.Transform.World = Matrix.RotationX((float)Math.PI/180)*Matrix.Translation(0,-20,0);//世界变换,下调为观察变换矩阵
//             Matrix viewMatrix = Matrix.LookAtLH(CamPostion, CamTarget, new Vector3(0, 1, 0));
//             m_device.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4, this.Width / this.Height, 1.0f, 6500.0f);
//             m_device.Transform.View = viewMatrix;
//         }public void Render(){if (m_device == null)return;m_device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.White, 1.0f, 0);/*SetupMatrices();*/m_device.Transform.World = Matrix.Scaling(8,8,8)*Matrix.Translation(250,0,250);//世界变换,下调为观察变换矩阵m_device.Transform.View = Matrix.LookAtLH(CamPostion, CamTarget, new Vector3(0, 1, 0));m_device.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4, this.Width / this.Height, 1.0f, 6500.0f);m_device.BeginScene();m_device.RenderState.FillMode = FillMode.Solid;m_device.RenderState.CullMode = Cull.None;m_device.RenderState.Lighting = false;m_device.Material = skyboxMeshMaterials;                  //指定设备的材质for (int i = 0; i < meshMaterials1.Length; i++)      //Mesh中可能有多个3D图形,逐一显示{m_device.Material = meshMaterials1[i];        //设定3D图形的材质m_device.SetTexture(0, skyboxMeshTextures[i]);   //设定3D图形的纹理skyboxMesh.DrawSubset(i);}m_device.Transform.World = Matrix.Identity;Matrix viewMatrix = Matrix.LookAtLH(CamPostion, CamTarget, new Vector3(0, 1, 0));m_device.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4,this.Width / this.Height,1.0f,6500.0f);m_device.Transform.View = viewMatrix;m_device.SetTexture(0, texture);//设置贴图m_device.VertexFormat = CustomVertex.PositionTextured.Format;m_device.SetStreamSource(0, vertexBuffer, 0);m_device.Indices = indexBuffer;m_device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, (xCount + 1) * (yCount + 1), 0, indices.Length / 3);//m_device.Transform.World = Matrix.Translation(0.0f, 0.0f, 0.0f);//底部世界变换
//             m_device.Material = bottomMaterial;//底部使用的材质
//
//             m_device.RenderState.DiffuseMaterialSource = ColorSource.Material;
//             m_device.RenderState.AlphaBlendEnable = true;
//             m_device.SetTexture(0, bottomTexture);
//
//             m_device.SetStreamSource(0, bottomVertexBuffer, 0);
//             m_device.VertexFormat = CustomVertex.PositionNormalTextured.Format;
//             m_device.DrawPrimitives(PrimitiveType.TriangleList, 0, 2);/*第1个边界三角形*/m_device.SetStreamSource(0, borderFrontVertexBuffer, 0);m_device.VertexFormat = CustomVertex.PositionColored.Format;m_device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 2 * (xCount + 1), 0, borderFrontIndices.Length / 3);/*第2个边界三角形*/m_device.SetStreamSource(0, borderBackVertexBuffer, 0);m_device.VertexFormat = CustomVertex.PositionColored.Format;m_device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 2 * (xCount + 1), 0, borderBackIndices.Length / 3);/*第3个边界三角形*/m_device.SetStreamSource(0, borderLeftVertexBuffer, 0);m_device.VertexFormat = CustomVertex.PositionColored.Format;m_device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 2 * (yCount + 1), 0, borderLeftIndices.Length / 3);/*第4个边界三角形*/m_device.SetStreamSource(0, borderRightVertexBuffer, 0);m_device.VertexFormat = CustomVertex.PositionColored.Format;m_device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 2 * (yCount + 1), 0, borderRightIndices.Length / 3);m_device.EndScene();m_device.Present();}private void OnKeyDown(object sender, KeyEventArgs e){Vector4 tempV4;Matrix currentView = m_device.Transform.View;//当前摄像机的视图矩阵switch (e.KeyCode){case Keys.Left:CamPostion.Subtract(CamTarget);tempV4 = Vector3.Transform(CamPostion, Matrix.RotationQuaternion(Quaternion.RotationAxis(new Vector3(currentView.M12, currentView.M22, currentView.M32), -angleY)));CamPostion.X = tempV4.X + CamTarget.X;CamPostion.Y = tempV4.Y + CamTarget.Y;CamPostion.Z = tempV4.Z + CamTarget.Z;break;case Keys.Right:CamPostion.Subtract(CamTarget);tempV4 = Vector3.Transform(CamPostion, Matrix.RotationQuaternion(Quaternion.RotationAxis(new Vector3(currentView.M12, currentView.M22, currentView.M32), angleY)));CamPostion.X = tempV4.X + CamTarget.X;CamPostion.Y = tempV4.Y + CamTarget.Y;CamPostion.Z = tempV4.Z + CamTarget.Z;break;case Keys.Up:CamPostion.Subtract(CamTarget);tempV4 = Vector3.Transform(CamPostion, Matrix.RotationQuaternion(Quaternion.RotationAxis(new Vector3(m_device.Transform.View.M11, m_device.Transform.View.M21, m_device.Transform.View.M31), -angleY)));CamPostion.X = tempV4.X + CamTarget.X;CamPostion.Y = tempV4.Y + CamTarget.Y;CamPostion.Z = tempV4.Z + CamTarget.Z;break;case Keys.Down:CamPostion.Subtract(CamTarget);tempV4 = Vector3.Transform(CamPostion, Matrix.RotationQuaternion(Quaternion.RotationAxis(new Vector3(m_device.Transform.View.M11, m_device.Transform.View.M21, m_device.Transform.View.M31), angleY)));CamPostion.X = tempV4.X + CamTarget.X;CamPostion.Y = tempV4.Y + CamTarget.Y;CamPostion.Z = tempV4.Z + CamTarget.Z;break;case Keys.Add:CamPostion.Subtract(CamTarget);CamPostion.Scale(0.95f);CamPostion.Add(CamTarget);break;case Keys.Subtract:CamPostion.Subtract(CamTarget);CamPostion.Scale(1.05f);CamPostion.Add(CamTarget);break;}Matrix viewMatrix = Matrix.LookAtLH(CamPostion, CamTarget, new Vector3(0, 1, 0));m_device.Transform.View = viewMatrix;Render();}private void OnMouseDown(object sender, MouseEventArgs e){if (e.Button == MouseButtons.Left){mouseLastX = e.X;mouseLastY = e.Y;isRotateByMouse = true;}else if (e.Button == MouseButtons.Middle){mouseLastX = e.X;mouseLastY = e.Y;isMoveByMouse = true;}}private void OnMouseUp(object sender, MouseEventArgs e){isRotateByMouse = false;isMoveByMouse = false;}private void OnMouseMove(object sender, MouseEventArgs e){if (isRotateByMouse){Matrix currentView = m_device.Transform.View;//当前摄像机的视图矩阵float tempAngleY = 2 * (float)(e.X - mouseLastX) / this.Width;CamPostion.Subtract(CamTarget);Vector4 tempV4 = Vector3.Transform(CamPostion, Matrix.RotationQuaternion(Quaternion.RotationAxis(new Vector3(currentView.M12, currentView.M22, currentView.M32), tempAngleY)));CamPostion.X = tempV4.X;CamPostion.Y = tempV4.Y;CamPostion.Z = tempV4.Z;float tempAngleX = 4 * (float)(e.Y - mouseLastY) / this.Height;tempV4 = Vector3.Transform(CamPostion, Matrix.RotationQuaternion(Quaternion.RotationAxis(new Vector3(currentView.M11, currentView.M21, currentView.M31), tempAngleX)));CamPostion.X = tempV4.X + CamTarget.X;CamPostion.Y = tempV4.Y + CamTarget.Y;CamPostion.Z = tempV4.Z + CamTarget.Z;Matrix viewMatrix = Matrix.LookAtLH(CamPostion, CamTarget, new Vector3(0, 1, 0));m_device.Transform.View = viewMatrix;mouseLastX = e.X;mouseLastY = e.Y;Render();}else if (isMoveByMouse){Matrix currentView = m_device.Transform.View;//当前摄像机的视图矩阵float moveFactor = 0.01f;CamTarget.X += -moveFactor * ((e.X - mouseLastX) * currentView.M11 - (e.Y - mouseLastY) * currentView.M12);CamTarget.Y += -moveFactor * ((e.X - mouseLastX) * currentView.M21 - (e.Y - mouseLastY) * currentView.M22);CamTarget.Z += -moveFactor * ((e.X - mouseLastX) * currentView.M31 - (e.Y - mouseLastY) * currentView.M32);CamPostion.X += -moveFactor * ((e.X - mouseLastX) * currentView.M11 - (e.Y - mouseLastY) * currentView.M12);CamPostion.Y += -moveFactor * ((e.X - mouseLastX) * currentView.M21 - (e.Y - mouseLastY) * currentView.M22);CamPostion.Z += -moveFactor * ((e.X - mouseLastX) * currentView.M31 - (e.Y - mouseLastY) * currentView.M32);Matrix viewMatrix = Matrix.LookAtLH(CamPostion, CamTarget, new Vector3(0, 1, 0));m_device.Transform.View = viewMatrix;mouseLastX = e.X;mouseLastY = e.Y;Render();}}private void OnMouseWheel(object sender, MouseEventArgs e){float scaleFactor = -(float)e.Delta / 2000 + 1f;CamPostion.Subtract(CamTarget);CamPostion.Scale(scaleFactor);CamPostion.Add(CamTarget);Matrix viewMatrix = Matrix.LookAtLH(CamPostion, CamTarget, new Vector3(0, 1, 0));m_device.Transform.View = viewMatrix;Render();}private void OnLoad(object sender, EventArgs e){InitializeGraphics();this.Show();Render();}private void OnPaint(object sender, PaintEventArgs e){this.Render();}private void OnResize(object sender, EventArgs e){pause = ((this.WindowState == FormWindowState.Minimized) || !this.Visible);}}
}

天空盒子

天空盒子的实现更加简单,这个可以完全通过代码来实现,需要准备几张天空360°无缝衔接的全景图,然后按照一定的顺组组合贴图即可。

建立一个立方体模型,为了增加渲染的效率,可以考虑只绘制一个基准面,然后利用这个基准面进行旋转平移变换就可以得到其他的面,绘制的时候,设置不同的纹理贴图即可。下面贴上几张天空盒子的效果图:

下面附上实现的核心代码:

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 Test
{public partial class SkyBox : Form{private Device m_device = null;bool pause = false;VertexBuffer skyboxVertexBuffer = null;Material skyboxMtrl;float Angle = 0, ViewZ = -6.0f;Texture skyboxFrontTexture = null;//定义skybox模型6个面的纹理变量Texture skyboxBackTexture = null;Texture skyboxTopTexture = null;Texture skyboxBottomTexture = null;Texture skyboxLeftTexture = null;Texture skyboxRightTexture = null;private float angleY = 0.0f;//定义绕Y轴旋转变量private int mouseLastX, mouseLastY;//记录鼠标按下时的坐标位置private bool isRotateByMouse = false;//记录是否由鼠标控制旋转private bool isMoveByMouse = false;//记录是否由鼠标控制移动public SkyBox(){InitializeComponent();}public bool InitializeGraphics(){try{PresentParameters presentParams = new PresentParameters();presentParams.Windowed = true;             //不是全屏显示,在一个窗口显示presentParams.SwapEffect = SwapEffect.Discard;       //后备缓存交换的方式presentParams.EnableAutoDepthStencil = true;            //允许使用自动深度模板测试//深度缓冲区单元为16位二进制数presentParams.AutoDepthStencilFormat = DepthFormat.D16;m_device = new Device(0, DeviceType.Hardware, this, CreateFlags.SoftwareVertexProcessing, presentParams);//设置设备重置事件(device.DeviceReset)事件函数为this.OnResetDevicem_device.DeviceReset += new System.EventHandler(this.OnResetDevice);this.OnCreateDevice(m_device, null);//自定义方法,初始化Device的工作放到这个方法中this.OnResetDevice(m_device, null);//调用设备重置事件(device.DeviceReset)事件函数}       //设备重置事件函数要设置Device参数,初始函数中必须调用该函数catch (DirectXException){return false;}return true;}public void OnCreateDevice(object sender, EventArgs e){Device dev = (Device)sender;skyboxVertexBuffer = new VertexBuffer(typeof(CustomVertex.PositionNormalTextured), 6, dev, 0, CustomVertex.PositionNormalTextured.Format, Pool.Default);skyboxVertexBuffer.Created += new System.EventHandler(this.OnCreateSkyBoxVertexBuffer);this.OnCreateSkyBoxVertexBuffer(skyboxVertexBuffer, null);skyboxMtrl = new Material();skyboxMtrl.Diffuse = System.Drawing.Color.Yellow;        //物体的颜色skyboxMtrl.Ambient = System.Drawing.Color.Red;          //反射环境光的颜色skyboxFrontTexture = TextureLoader.FromFile(dev, @"F:\\workdir\\VC# Based DirectX\\skybox\\neg_z.bmp");skyboxBackTexture = TextureLoader.FromFile(dev, @"F:\\workdir\\VC# Based DirectX\\skybox\\pos_z.bmp");skyboxTopTexture = TextureLoader.FromFile(dev, @"F:\\workdir\\VC# Based DirectX\\skybox\\pos_y.bmp");skyboxBottomTexture = TextureLoader.FromFile(dev, @"F:\\workdir\\VC# Based DirectX\\skybox\\neg_y.bmp");skyboxLeftTexture = TextureLoader.FromFile(dev, @"F:\\workdir\\VC# Based DirectX\\skybox\\pos_x.bmp");skyboxRightTexture = TextureLoader.FromFile(dev, @"F:\\workdir\\VC# Based DirectX\\skybox\\neg_x.bmp");}public void OnCreateSkyBoxVertexBuffer(object sender, EventArgs e){CustomVertex.PositionNormalTextured[] verts =(CustomVertex.PositionNormalTextured[])skyboxVertexBuffer.Lock(0, 0);verts[0].Position = new Vector3(-10.0f, -10.0f, 0.0f);   //顶点0位置verts[0].Normal = new Vector3(0, 0, -1);                //顶点0法线verts[0].Tu = 0.0f;                                     //顶点0纹理坐标Tuverts[0].Tv = 1.0f;verts[1].Position = new Vector3(-10.0f, 10.0f, 0.0f);       //顶点1位置verts[1].Normal = new Vector3(0, 0, -1);                //顶点1法线verts[1].Tu = 0.0f;                                     //顶点0纹理坐标Tuverts[1].Tv = 0.0f;verts[2].Position = new Vector3(10.0f, 10.0f, 0.0f);        //顶点2位置verts[2].Normal = new Vector3(0, 0, -1);verts[2].Tu = 1.0f;                                    //顶点0纹理坐标Tuverts[2].Tv = 0.0f;verts[3].Position = new Vector3(-10.0f, -10.0f, 0.0f);      //顶点3位置verts[3].Normal = new Vector3(0, 0, -1);                //顶点3法线verts[3].Tu = 0.0f;                                     //顶点0纹理坐标Tuverts[3].Tv = 1.0f;verts[4].Position = new Vector3(10.0f, 10.0f, 0.0f);        //顶点4位置verts[4].Normal = new Vector3(0, 0, -1);verts[4].Tu = 1.0f;                                    //顶点0纹理坐标Tuverts[4].Tv = 0.0f;verts[5].Position = new Vector3(10.0f, -10.0f, 0.0f);       //顶点5位置verts[5].Normal = new Vector3(0, 0, -1);verts[5].Tu = 1.0f;                                    //顶点0纹理坐标Tuverts[5].Tv = 1.0f;skyboxVertexBuffer.Unlock();}public void OnResetDevice(object sender, EventArgs e){Device dev = (Device)sender;dev.RenderState.CullMode = Cull.None;     //没有背面剔除m_device.RenderState.ZBufferEnable = true;             //打开Z缓冲m_device.RenderState.Lighting = false;                  //打开灯光//SetupLights();                                      //设置灯光//m_device.SamplerState[0].MagFilter = TextureFilter.Linear;//使用线性滤波,结合处容易出现裂缝}public void Render()       //渲染方法,本方法没有任何渲染代码,可认为是渲染方法的框架{if (m_device == null) //如果未建立设备对象,退出return;if (pause)return;m_device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.WhiteSmoke, 1.0f, 0);m_device.BeginScene();      //开始渲染SetupMatrices();m_device.RenderState.CullMode = Cull.None;m_device.RenderState.FillMode = FillMode.Solid;m_device.SetStreamSource(0, skyboxVertexBuffer, 0);m_device.VertexFormat = CustomVertex.PositionNormalTextured.Format;m_device.SetTexture(0, skyboxFrontTexture);m_device.Transform.World = Matrix.Translation(0, 0, -10);//沿Z轴向观察者方向移动10个单位m_device.DrawPrimitives(PrimitiveType.TriangleList, 0, 2);//正前面m_device.SetTexture(0, skyboxBackTexture);m_device.Transform.World = Matrix.RotationY((float)Math.PI) * Matrix.Translation(0, 0, 10);m_device.DrawPrimitives(PrimitiveType.TriangleList, 0, 2);//正后面m_device.SetTexture(0, skyboxRightTexture);m_device.Transform.World =Matrix.RotationY(-(float)Math.PI / 2) * Matrix.Translation(10, 0, 0);m_device.DrawPrimitives(PrimitiveType.TriangleList, 0, 2);//右侧面m_device.SetTexture(0, skyboxLeftTexture);m_device.Transform.World =Matrix.RotationY((float)Math.PI / 2) * Matrix.Translation(-10, 0, 0);m_device.DrawPrimitives(PrimitiveType.TriangleList, 0, 2);//左侧面m_device.SetTexture(0, skyboxTopTexture);m_device.Transform.World =Matrix.RotationX((float)Math.PI / 2) * Matrix.RotationY(-(float)Math.PI / 2) * Matrix.Translation(0, 10, 0);m_device.DrawPrimitives(PrimitiveType.TriangleList, 0, 2);//上面m_device.SetTexture(0, skyboxBottomTexture);m_device.Transform.World =Matrix.RotationX(-(float)Math.PI / 2) * Matrix.RotationY(-(float)Math.PI/2)*Matrix.Translation(0, -10, 0);m_device.DrawPrimitives(PrimitiveType.TriangleList, 0, 2);//下面m_device.EndScene();         //渲染结束m_device.Present();   //更新显示区域,把后备缓存的D图形送到图形卡的显存中显示}private Vector3 CamPostion = new Vector3(0.0f, 0.0f, -5.0f);//定义摄像机位置private Vector3 CamTarget = new Vector3(0.0f, 0.0f, 0.0f);//定义摄像机目标位置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,
//                             this.Width/this.Height, 1.0f, 100.0f);           //投影变换矩阵Matrix viewMatrix = Matrix.LookAtLH(CamPostion, CamTarget, new Vector3(0, 1, 0));m_device.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4, this.Width / this.Height, 1.0f, 6500.0f);m_device.Transform.View = viewMatrix;}private void SetupLights(){m_device.Material = skyboxMtrl;m_device.Lights[0].Type = LightType.Directional;         //灯光类型m_device.Lights[0].Diffuse = System.Drawing.Color.White; //光的颜色为白色m_device.Lights[0].Direction = new Vector3(0, -2, 4);//灯光方向从观察者上方指向屏幕下方m_device.Lights[0].Update();           //更新灯光设置,创建第一盏灯光m_device.Lights[0].Enabled = true;      //使设置有效,下句设置环境光为白色m_device.RenderState.Ambient = System.Drawing.Color.FromArgb(0x808080);}private void Form1_Load(object sender, EventArgs e){InitializeGraphics();this.Show();Render();}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);}private void OnKeyDown(object sender, KeyEventArgs e){Vector4 tempV4;Matrix currentView = m_device.Transform.View;//当前摄像机的视图矩阵switch (e.KeyCode){case Keys.Left:CamPostion.Subtract(CamTarget);tempV4 = Vector3.Transform(CamPostion, Matrix.RotationQuaternion(Quaternion.RotationAxis(new Vector3(currentView.M12, currentView.M22, currentView.M32), -angleY)));CamPostion.X = tempV4.X + CamTarget.X;CamPostion.Y = tempV4.Y + CamTarget.Y;CamPostion.Z = tempV4.Z + CamTarget.Z;break;case Keys.Right:CamPostion.Subtract(CamTarget);tempV4 = Vector3.Transform(CamPostion, Matrix.RotationQuaternion(Quaternion.RotationAxis(new Vector3(currentView.M12, currentView.M22, currentView.M32), angleY)));CamPostion.X = tempV4.X + CamTarget.X;CamPostion.Y = tempV4.Y + CamTarget.Y;CamPostion.Z = tempV4.Z + CamTarget.Z;break;case Keys.Up:CamPostion.Subtract(CamTarget);tempV4 = Vector3.Transform(CamPostion, Matrix.RotationQuaternion(Quaternion.RotationAxis(new Vector3(m_device.Transform.View.M11, m_device.Transform.View.M21, m_device.Transform.View.M31), -angleY)));CamPostion.X = tempV4.X + CamTarget.X;CamPostion.Y = tempV4.Y + CamTarget.Y;CamPostion.Z = tempV4.Z + CamTarget.Z;break;case Keys.Down:CamPostion.Subtract(CamTarget);tempV4 = Vector3.Transform(CamPostion, Matrix.RotationQuaternion(Quaternion.RotationAxis(new Vector3(m_device.Transform.View.M11, m_device.Transform.View.M21, m_device.Transform.View.M31), angleY)));CamPostion.X = tempV4.X + CamTarget.X;CamPostion.Y = tempV4.Y + CamTarget.Y;CamPostion.Z = tempV4.Z + CamTarget.Z;break;case Keys.Add:CamPostion.Subtract(CamTarget);CamPostion.Scale(0.95f);CamPostion.Add(CamTarget);break;case Keys.Subtract:CamPostion.Subtract(CamTarget);CamPostion.Scale(1.05f);CamPostion.Add(CamTarget);break;}Matrix viewMatrix = Matrix.LookAtLH(CamPostion, CamTarget, new Vector3(0, 1, 0));m_device.Transform.View = viewMatrix;Render();}private void OnMouseDown(object sender, MouseEventArgs e){if (e.Button == MouseButtons.Left){mouseLastX = e.X;mouseLastY = e.Y;isRotateByMouse = true;}else if (e.Button == MouseButtons.Middle){mouseLastX = e.X;mouseLastY = e.Y;isMoveByMouse = true;}}private void OnMouseUp(object sender, MouseEventArgs e){isRotateByMouse = false;isMoveByMouse = false;}private void OnMouseMove(object sender, MouseEventArgs e){if (isRotateByMouse){Matrix currentView = m_device.Transform.View;//当前摄像机的视图矩阵float tempAngleY = 2 * (float)(e.X - mouseLastX) / this.Width;CamPostion.Subtract(CamTarget);Vector4 tempV4 = Vector3.Transform(CamPostion, Matrix.RotationQuaternion(Quaternion.RotationAxis(new Vector3(currentView.M12, currentView.M22, currentView.M32), tempAngleY)));CamPostion.X = tempV4.X;CamPostion.Y = tempV4.Y;CamPostion.Z = tempV4.Z;float tempAngleX = 4 * (float)(e.Y - mouseLastY) / this.Height;tempV4 = Vector3.Transform(CamPostion, Matrix.RotationQuaternion(Quaternion.RotationAxis(new Vector3(currentView.M11, currentView.M21, currentView.M31), tempAngleX)));CamPostion.X = tempV4.X + CamTarget.X;CamPostion.Y = tempV4.Y + CamTarget.Y;CamPostion.Z = tempV4.Z + CamTarget.Z;Matrix viewMatrix = Matrix.LookAtLH(CamPostion, CamTarget, new Vector3(0, 1, 0));m_device.Transform.View = viewMatrix;mouseLastX = e.X;mouseLastY = e.Y;Render();}else if (isMoveByMouse){Matrix currentView = m_device.Transform.View;//当前摄像机的视图矩阵float moveFactor = 0.01f;CamTarget.X += -moveFactor * ((e.X - mouseLastX) * currentView.M11 - (e.Y - mouseLastY) * currentView.M12);CamTarget.Y += -moveFactor * ((e.X - mouseLastX) * currentView.M21 - (e.Y - mouseLastY) * currentView.M22);CamTarget.Z += -moveFactor * ((e.X - mouseLastX) * currentView.M31 - (e.Y - mouseLastY) * currentView.M32);CamPostion.X += -moveFactor * ((e.X - mouseLastX) * currentView.M11 - (e.Y - mouseLastY) * currentView.M12);CamPostion.Y += -moveFactor * ((e.X - mouseLastX) * currentView.M21 - (e.Y - mouseLastY) * currentView.M22);CamPostion.Z += -moveFactor * ((e.X - mouseLastX) * currentView.M31 - (e.Y - mouseLastY) * currentView.M32);Matrix viewMatrix = Matrix.LookAtLH(CamPostion, CamTarget, new Vector3(0, 1, 0));m_device.Transform.View = viewMatrix;mouseLastX = e.X;mouseLastY = e.Y;Render();}}private void OnMouseWheel(object sender, MouseEventArgs e){float scaleFactor = -(float)e.Delta / 2000 + 1f;CamPostion.Subtract(CamTarget);CamPostion.Scale(scaleFactor);CamPostion.Add(CamTarget);Matrix viewMatrix = Matrix.LookAtLH(CamPostion, CamTarget, new Vector3(0, 1, 0));m_device.Transform.View = viewMatrix;Render();}}
}

DirectX天空球和天空盒子模型相关推荐

  1. unity 自定义天空球模型防止被裁剪

    在unity中,有时候不想使用内置的天空球去渲染,那么,我们就会使用一个球体去渲染天空球.为了保证游戏场景模型都放置在天空球内.球体就会放大的很大,那么问题就来了,这样会导致球体超出了相机的可视范围, ...

  2. ue4学习日记4(植被,光照,光束遮挡,天空球)

    目录 植被 种植植被 植被垂直向上 消失距离 删除植被 植被加碰撞 光照 关闭自动曝光 雾气效果 光亮 光束遮挡 体积雾 天空球 调整白天黑夜 白天黑夜-调整太阳高度 是否让颜色根据太阳位置改变 改变 ...

  3. Laya Air+Unity3D双引擎带你做个天空球3D小游戏(下篇)

    本章继续上篇文章内容开始带大家写代码,大家要是把这个3d天空球demo学会了再自己丰富一下玩法加点精美UI其实也可以拿去上线发布哦

  4. 11_ue4天空球的使用

    1.变成黑天 先旋转平行光 90度旋转,让平行光从下往上射,这时地面变黑了. 勾选这个按钮,可以刷新天空球,让太阳跑到光源的上游. 太阳到了地面的下方 天黑了,星星都出来了.  这个选项是根据平行光的 ...

  5. [shader]动态切换天空球

    最终效果 思路 改进之前的全景图,应用到天空球. 利用3d noise算法,生成噪声. 源代码 Shader "QQ/Sky/MultiSky" {Properties{_Colo ...

  6. Unity3D_HDRP项目代码修改天空球

    U3D HDRP项目 代码修改天空球的办法 public Volume volume;public UnityEngine.Rendering.HighDefinition.HDRISky HDRIS ...

  7. [shader]动态天空球

    最终效果 思路 把天空球当成一个球体来处理纹理. 贴图UV动画. 通过渐变颜色和曲线控制昼夜变化. Shader源码 Shader "QQ/Sky" {Properties{_Su ...

  8. unity在没有灯光的作用下实现物体的发光以及去除天空球

    首先在左上角找到Window在里面找到Rendering然后再找到Lighting Settings按钮,进去之后按照下图进行一些设置,则可以实现 上面就是进行一些操作,就可以了. 这个地方就是删除天 ...

  9. three 天空球_紫天学习星球教学:Sky Dome 天空球插件功能使用详解(中文)

    视频作者:TutorialsUp 视频来源:https://youtu.be/Vvq9XTIDJ3k 中文讲解:紫天肖万涛 该视频讲解了 Sky Dome 天空球插件的使用方法,使用该插件可以把真实的 ...

  10. Unity昼夜变换(lightmap+天空球)

    1. 效果图 2. 原理 烘焙出两组lightmap,并在shader中进行采样:最后插值输出混合结果.天空球也是同理可得. 3. 代码 3.1 shader Shader "Unlit/l ...

最新文章

  1. 使用Python,OpenCV进行卡类型及16位卡号数字的OCR
  2. oracle-imp导入小错filesize设置
  3. python经典案例-Python经典实例
  4. Windows 11正式发布,所有用户均可免费升级,还支持安卓应用
  5. jasmine spyOn的单步调试
  6. C#下的两种加密方式MD5和DEC
  7. 《Python学习之路 -- 字符串的方法》
  8. 【转】浅谈TDD、BDD、ATDD、DDD的区别
  9. Map的使用和遍历方法示例
  10. .net5 不支持winform_.NET5.0 单文件发布打包操作深度剖析
  11. 硅谷产品联盟合伙人:每一个伟大产品的背后
  12. 在《寒门状元之死》上,咪蒙贩卖的是什么?
  13. beanstalkd协议解读(中文翻译加个人理解)
  14. 12行代码拿下所有lol皮肤!!Python超简单爬虫【内附详细教学 】
  15. uniapp小程序开发设置系统状态栏高度、全屏背景图设置
  16. HashMap? ConcurrentHashMap? 相信看完这篇没人能难住你!
  17. java ftp 下载 0k_Ftp下载文件大小为0 KB
  18. BT种子文件格式和Bencoding编码
  19. 关联入库表,出库表,统计库存。。。
  20. 超声波测距模块(HC-SR04模块)特点及使用介绍

热门文章

  1. 证件照蓝底变白底的方法
  2. 台式计算机图形设置,如何打开计算机图形设置以提高游戏质量?
  3. 2018-7-4 笔记
  4. 大话设计模式C++实现-第7章-代理模式
  5. 我说Java完全面向对象,老大过来就是一jio
  6. 采用Matlab解决最小曼哈顿图问题
  7. flutter开发的即时聊天应用
  8. 并发编程之CompletableFuture全网最细最全用法(一)
  9. Creo 9.0 基准特征:基准点
  10. 基于微信小程序的基于安卓APP的设计毕设计ADD ME let me help you