3D系列4.1 开始代码
我们已经在第一个系列教材中学习了创建地形的基础知识,所以开始的代码是基于第一个系列教程的。
因为我不想在LoadContent方法中写太长的代码,所以这个方法中会调用LoadVertices方法初始化顶点和索引。
现在,LoadVertices方法生成了地形的顶点和索引这些顶点包含位置和颜色信息。然后,将它们传递到CalculateNormals方法,这个方法生成正确的法线并添加到顶点中。最后,顶点和索引都存储到顶点缓冲和索引缓冲中优化性能。
在Draw方法中,它们被传递到Colored technique中,这是在Series4Effects.fx文件中定义的,这个文件包含HLSL代码,我们会在后面的章节中扩展这个代码。
我们还要使用一个高度图。
创建一个新项目将下面的代码粘贴到Game1.cs文件中。确保Game1.cs和Program.cs文件的命名空间是相同的!然后创建一个叫做Series4Effects.fx的文件,将下面的HLSL代码粘贴其中。
下面是Game1.cs的代码:
using System; using System.Collections.Generic; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Audio; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.GamerServices; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Net; using Microsoft.Xna.Framework.Storage; namespace XNAseries4 { public struct VertexPositionNormalColored { public Vector3 Position; public Color Color; public Vector3 Normal; public static int SizeInBytes = 7 * 4; public static VertexElement[] VertexElements = new VertexElement[] { new VertexElement( 0, 0, VertexElementFormat.Vector3, VertexElementMethod.Default, VertexElementUsage.Position, 0 ), new VertexElement( 0, sizeof(float) * 3, VertexElementFormat.Color, VertexElementMethod.Default, VertexElementUsage.Color, 0 ), new VertexElement( 0, sizeof(float) * 4, VertexElementFormat.Vector3, VertexElementMethod.Default, VertexElementUsage.Normal, 0 ), }; } public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; GraphicsDevice device; int terrainWidth; int terrainLength; float[,] heightData; VertexBuffer terrainVertexBuffer; IndexBuffer terrainIndexBuffer; VertexDeclaration terrainVertexDeclaration; Effect effect; Matrix viewMatrix; Matrix projectionMatrix; public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; } protected override void Initialize() { graphics.PreferredBackBufferWidth = 500; graphics.PreferredBackBufferHeight = 500; graphics.ApplyChanges(); Window.Title = "Riemer's XNA Tutorials -- Series 4"; base.Initialize(); } protected override void LoadContent() { device = GraphicsDevice; effect = Content.Load<Effect> ("Series4Effects"); viewMatrix = Matrix.CreateLookAt(new Vector3(130, 30, -50), new Vector3(0,0,-40), new Vector3(0, 1, 0)); projectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, device.Viewport.AspectRatio, 0.3f, 1000.0f); LoadVertices(); } private void LoadVertices() { Texture2D heightMap = Content.Load<Texture2D> ("heightmap"); LoadHeightData(heightMap); VertexPositionNormalColored[] terrainVertices = SetUpTerrainVertices(); int[] terrainIndices = SetUpTerrainIndices(); terrainVertices = CalculateNormals(terrainVertices, terrainIndices); CopyToTerrainBuffers(terrainVertices, terrainIndices); terrainVertexDeclaration = new VertexDeclaration(device, VertexPositionNormalColored.VertexElements); } private void LoadHeightData(Texture2D heightMap) { float minimumHeight = float.MaxValue; float maximumHeight = float.MinValue; terrainWidth = heightMap.Width; terrainLength = heightMap.Height; Color[] heightMapColors = new Color[terrainWidth * terrainLength]; heightMap.GetData(heightMapColors); heightData = new float[terrainWidth, terrainLength]; for (int x = 0; x < terrainWidth; x++) for (int y = 0; y < terrainLength; y++) { heightData[x, y] = heightMapColors[x + y * terrainWidth].R; if (heightData[x, y] < minimumHeight) minimumHeight = heightData[x, y]; if (heightData[x, y] > maximumHeight) maximumHeight = heightData[x, y]; } for (int x = 0; x < terrainWidth; x++) for (int y = 0; y < terrainLength; y++) heightData[x, y] = (heightData[x, y] - minimumHeight) / (maximumHeight - minimumHeight) * 30.0f; } private VertexPositionNormalColored[] SetUpTerrainVertices() { VertexPositionNormalColored[] terrainVertices = new VertexPositionNormalColored[terrainWidth * terrainLength]; for (int x = 0; x < terrainWidth; x++) { for (int y = 0; y < terrainLength; y++) { terrainVertices[x + y * terrainWidth].Position = new Vector3(x, heightData[x, y], -y); if (heightData[x, y] < 6) terrainVertices[x + y * terrainWidth].Color = Color.Blue; else if (heightData[x, y] < 15) terrainVertices[x + y * terrainWidth].Color = Color.Green; else if (heightData[x, y] < 25) terrainVertices[x + y * terrainWidth].Color = Color.Brown; else terrainVertices[x + y * terrainWidth].Color = Color.White; } } return terrainVertices; } private int[] SetUpTerrainIndices() { int[] indices = new int[(terrainWidth - 1) * (terrainLength - 1) * 6]; int counter = 0; for (int y = 0; y < terrainLength - 1; y++) { for (int x = 0; x < terrainWidth - 1; x++) { int lowerLeft = x + y * terrainWidth; int lowerRight = (x + 1) + y * terrainWidth; int topLeft = x + (y + 1) * terrainWidth; int topRight = (x + 1) + (y + 1) * terrainWidth; indices[counter++] = topLeft; indices[counter++] = lowerRight; indices[counter++] = lowerLeft; indices[counter++] = topLeft; indices[counter++] = topRight; indices[counter++] = lowerRight; } } return indices; } private VertexPositionNormalColored[] CalculateNormals(VertexPositionNormalColored[] vertices, int[] indices) { for (int i = 0; i < vertices.Length; i++) vertices[i].Normal = new Vector3(0, 0, 0); for (int i = 0; i < indices.Length / 3; i++) { int index1 = indices[i * 3]; int index2 = indices[i * 3 + 1]; int index3 = indices[i * 3 + 2]; Vector3 side1 = vertices[index1].Position - vertices[index3].Position; Vector3 side2 = vertices[index1].Position - vertices[index2].Position; Vector3 normal = Vector3.Cross(side1, side2); vertices[index1].Normal += normal; vertices[index2].Normal += normal; vertices[index3].Normal += normal; } for (int i = 0; i < vertices.Length; i++) vertices[i].Normal.Normalize(); return vertices; } private void CopyToTerrainBuffers(VertexPositionNormalColored[] vertices, int[] indices) { terrainVertexBuffer = new VertexBuffer(device, vertices.Length * VertexPositionNormalColored.SizeInBytes, BufferUsage.WriteOnly); terrainVertexBuffer.SetData(vertices); terrainIndexBuffer = new IndexBuffer(device, typeof(int), indices.Length, BufferUsage.WriteOnly); terrainIndexBuffer.SetData(indices); } protected override void UnloadContent() { } protected override void Update(GameTime gameTime) { if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); base.Update(gameTime); } protected override void Draw(GameTime gameTime) { float time = (float)gameTime.TotalGameTime.TotalMilliseconds / 100.0f; device.RenderState.CullMode = CullMode.None; device.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1.0f, 0); DrawTerrain(viewMatrix); base.Draw(gameTime); } private void DrawTerrain(Matrix currentViewMatrix) { effect.CurrentTechnique = effect.Techniques["Colored"]; Matrix worldMatrix = Matrix.Identity; effect.Parameters["xWorld"].SetValue(worldMatrix); effect.Parameters["xView"].SetValue(currentViewMatrix); effect.Parameters["xProjection"].SetValue(projectionMatrix); effect.Parameters["xEnableLighting"].SetValue(true); effect.Parameters["xAmbient"].SetValue(0.4f); effect.Parameters["xLightDirection"].SetValue(new Vector3(-0.5f, -1, -0.5f)); effect.Begin(); foreach (EffectPass pass in effect.CurrentTechnique.Passes) { pass.Begin(); device.Vertices[0].SetSource(terrainVertexBuffer, 0, VertexPositionNormalColored.SizeInBytes); device.Indices = terrainIndexBuffer; device.VertexDeclaration = terrainVertexDeclaration; int noVertices = terrainVertexBuffer.SizeInBytes / VertexPositionNormalColored.SizeInBytes; int noTriangles = terrainIndexBuffer.SizeInBytes / sizeof(int)/3; device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, noVertices, 0, noTriangles); pass.End(); } effect.End(); } } }
下面是Series4Effects.fx文件的代码:
//---------------------------------------------------- //-- -- //-- www.riemers.net -- //-- Series 4: Advanced terrain -- //-- Shader code -- //-- -- //---------------------------------------------------- //------- Constants -------- float4x4 xView; float4x4 xProjection; float4x4 xWorld; float3 xLightDirection; float xAmbient; bool xEnableLighting; //------- Texture Samplers -------- Texture xTexture; sampler TextureSampler = sampler_state { texture = <xTexture> ; magfilter = LINEAR; minfilter = LINEAR; mipfilter=LINEAR; AddressU = mirror; AddressV = mirror;}; //------- Technique: Colored -------- struct ColVertexToPixel { float4 Position : POSITION; float4 Color : COLOR0; float LightingFactor: TEXCOORD0; }; struct ColPixelToFrame { float4 Color : COLOR0; }; ColVertexToPixel ColoredVS( float4 inPos : POSITION, float4 inColor: COLOR, float3 inNormal: NORMAL) { ColVertexToPixel Output = (ColVertexToPixel)0; float4x4 preViewProjection = mul (xView, xProjection); float4x4 preWorldViewProjection = mul (xWorld, preViewProjection); Output.Position = mul(inPos, preWorldViewProjection); Output.Color = inColor; float3 Normal = normalize(mul(normalize(inNormal), xWorld)); Output.LightingFactor = 1; if (xEnableLighting) Output.LightingFactor = saturate(dot(Normal, -xLightDirection)); return Output; } ColPixelToFrame ColoredPS(ColVertexToPixel PSIn) { ColPixelToFrame Output = (ColPixelToFrame)0; Output.Color = PSIn.Color; Output.Color.rgb *= saturate(PSIn.LightingFactor + xAmbient); return Output; } technique Colored { pass Pass0 { VertexShader = compile vs_1_1 ColoredVS(); PixelShader = compile ps_1_1 ColoredPS(); } } //------- Technique: Textured -------- struct TexVertexToPixel { float4 Position : POSITION; float4 Color : COLOR0; float LightingFactor: TEXCOORD0; float2 TextureCoords: TEXCOORD1; }; struct TexPixelToFrame { float4 Color : COLOR0; }; TexVertexToPixel TexturedVS( float4 inPos : POSITION, float3 inNormal: NORMAL, float2 inTexCoords: TEXCOORD0) { TexVertexToPixel Output = (TexVertexToPixel)0; float4x4 preViewProjection = mul (xView, xProjection); float4x4 preWorldViewProjection = mul (xWorld, preViewProjection); Output.Position = mul(inPos, preWorldViewProjection); Output.TextureCoords = inTexCoords; float3 Normal = normalize(mul(normalize(inNormal), xWorld)); Output.LightingFactor = 1; if (xEnableLighting) Output.LightingFactor = saturate(dot(Normal, -xLightDirection)); return Output; } TexPixelToFrame TexturedPS(TexVertexToPixel PSIn) { TexPixelToFrame Output = (TexPixelToFrame)0; Output.Color = tex2D(TextureSampler, PSIn.TextureCoords); Output.Color.rgb *= saturate(PSIn.LightingFactor + xAmbient); return Output; } technique Textured { pass Pass0 { VertexShader = compile vs_1_1 TexturedVS(); PixelShader = compile ps_1_1 TexturedPS(); } }
发布时间:2009/12/8 下午1:45:23 阅读次数:8618