XNA 天空盒子
今天终于做了个SkyBox,本来很简单,但是因为之前没有做过,所以做的候还是不是很顺利。
Skyboxr 原理很简单:
将6张图贴在个面上,图实为TextureCUBE。
首先为了省去每次写DEMO时都要重新写Camera的操作的方法,所有这次就做了个Camera操作的类,来封装Camera的常用操作方法:
Code
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.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
namespace CameraLib
{
public class Camera
{
float Fov;
float aspectRatio;
float neer;
float far;
Vector3 mPosi;
public Vector3 mposi{get{return mPosi;}}
Vector3 mTarget;
public Vector3 mtarget { get { return mTarget; } }
Vector3 mUp;
public Vector3 mup { get { return mUp; } }
private Matrix view;
public Matrix View
{
get { return Matrix.CreateLookAt(mPosi, mTarget, mUp); }
}
private Matrix projection;
public Matrix Projection
{
get { return projection = Matrix.CreatePerspectiveFieldOfView(Fov, aspectRatio, neer, far); }
}
public Camera(float fov, float ar, float neer, float far,Vector3 pos,Vector3 targ,Vector3 up)
{
Fov = fov;
this.aspectRatio = ar;
this.neer = neer;
this.far = far;
this.mPosi = pos;
this.mTarget = targ;
this.mUp = up;
UpdateMatrix();
}
Matrix tranMatrix = Matrix.Identity;
public void UpdateMatrix()
{
KeyboardState ks = Keyboard.GetState();
Vector3 movVec = Vector3.Zero;
Vector3 rolVec = Vector3.Zero;
#region 相机平移
if (ks.IsKeyDown(Keys.W))
{
movVec.Z -= 1;
}
if (ks.IsKeyDown(Keys.S))
{
movVec.Z += 1;
}
if (ks.IsKeyDown(Keys.A))
{
movVec.X -= 1;
}
if (ks.IsKeyDown(Keys.D))
{
movVec.X += 1;
}
if (ks.IsKeyDown(Keys.Q))
{
movVec.Y -= 1;
}
if (ks.IsKeyDown(Keys.E))
{
movVec.Y += 1;
}
#endregion
#region 相机旋转
if (ks.IsKeyDown(Keys.Left))
{
rolVec.Y+=0.1f;
}
if (ks.IsKeyDown(Keys.Right))
{
rolVec.Y-=0.1f;
}
if (ks.IsKeyDown(Keys.Up))
{
rolVec.X+=0.1f;
}
if (ks.IsKeyDown(Keys.Down))
{
rolVec.X-=0.1f;
}
if (ks.IsKeyDown(Keys.PageUp))
{
rolVec.Z+=0.1f;
}
if (ks.IsKeyDown(Keys.PageDown))
{
rolVec.Z-=0.1f;
}
#endregion
tranMatrix *= Matrix.CreateRotationX(rolVec.X) * Matrix.CreateRotationY(rolVec.Y) * Matrix.CreateRotationZ(rolVec.Z);
mUp = Vector3.Transform(Vector3.Up, tranMatrix);
mPosi += Vector3.Transform(movVec, tranMatrix);
mTarget = mPosi + Vector3.Transform(Vector3.Forward, tranMatrix);
}
private void cameraTransform(Matrix matrix)
{
view *= matrix;
}
}
}
这个类现在可能还不完善,以后要用的时候再修改吧。
接下来是shader:
Code
float4x4 World;
float4x4 View;
float4x4 Projection;
float3 eyePosition:CameraPosition;
Texture textu;
samplerCUBE textuSampler=sampler_state
{
texture=<textu>;
};
struct vsin
{
float4 position:POSITION0;
//float3 normal:NORMAL0;
float2 texcoord:TEXCOORD0;
};
struct vsout
{
float4 Position : POSITION0;
float3 ViewDirection : TEXCOORD2;
};
vsout vs(vsin input)
{
vsout output;
output.Position=mul(input.position,mul(mul(World,View),Projection));
output.ViewDirection=eyePosition-mul(input.position,World);
return output;
}
struct psin
{
float3 ViewDirection:TEXCOORD2;
};
float4 ps(psin input):COLOR0
{
float3 vd=normalize(input.ViewDirection);
return texCUBE(textuSampler,-vd);
}
technique Technique1
{
pass Pass1
{
VertexShader = compile vs_3_0 vs();
PixelShader = compile ps_3_0 ps();
}
}
注意:这里出来后,当你移动镜头时,可能看到skybox的移动方向可能和实际方向相反。
然后是主程序:
Code
using System;
using System.Collections.Generic;
using System.Linq;
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.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
namespace SkyBox
{
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
CameraLib.Camera camera;
Model skybox;
SpriteFont fnt;
TextureCube texture;
Effect effect;
Matrix modRow=Matrix.Identity;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
camera = new CameraLib.Camera(MathHelper.PiOver4, GraphicsDevice.Viewport.AspectRatio, 1, 1000,
new Vector3(0, 0, 50), Vector3.Forward, Vector3.Up);
base.Initialize();
}
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
skybox = Content.Load<Model>("skybox");
fnt = Content.Load<SpriteFont>("fnt");
texture = Content.Load<TextureCube>("SkyboxTex");
effect = Content.Load<Effect>("effect");
}
protected override void Update(GameTime gameTime)
{
KeyboardState ks = Keyboard.GetState();
if (ks.IsKeyDown(Keys.Escape))
{
Exit();
}
if (ks.IsKeyDown(Keys.NumPad4))
{
modRow *= Matrix.CreateRotationY(0.1f);
}
if (ks.IsKeyDown(Keys.NumPad6))
{
modRow *= Matrix.CreateRotationY(-0.1f);
}
if (ks.IsKeyDown(Keys.NumPad8))
{
modRow *= Matrix.CreateRotationZ(0.1f);
}
if (ks.IsKeyDown(Keys.NumPad5))
{
modRow *= Matrix.CreateRotationZ(-0.1f);
}
if (ks.IsKeyDown(Keys.NumPad7))
{
modRow *= Matrix.CreateRotationX(0.1f);
}
if (ks.IsKeyDown(Keys.NumPad9))
{
modRow *= Matrix.CreateRotationX(-0.1f);
}
camera.UpdateMatrix();
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
effect.Parameters["World"].SetValue(modRow*Matrix.CreateScale(30f));
effect.Parameters["View"].SetValue(camera.View);
effect.Parameters["Projection"].SetValue(camera.Projection);
effect.Parameters["textu"].SetValue(texture);
effect.Parameters["eyePosition"].SetValue(camera.mposi);
foreach(ModelMesh mesh in skybox.Meshes)
{
foreach (ModelMeshPart part in mesh.MeshParts)
{
part.Effect = effect;
}
mesh.Draw();
}
spriteBatch.Begin(SpriteBlendMode.AlphaBlend);
spriteBatch.DrawString(fnt, "Position:" + camera.mposi.ToString() + "\n" +
"Target:" + camera.mtarget.ToString() + "\n" +
"up:" + camera.mup.ToString(), Vector2.Zero, Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
}
}
好了,看下效果:
其实要实现天空效果也不只有一种方法,还可以通过一个"房顶"完成。
通过上面的模型也能实现天空效果。