利用控制飛船的變換矩陣,現在它已經在天空中任由我們擺布了,但是總覺得還是差了點什麼,真實性不夠。到底缺少了是什麼呢?
通過仔細觀察,我們可以看到,原來無論天空怎麼旋轉,在飛船船體上都沒有體現光影的變化,沒能隨著太陽的移動產生必要的光線反射。
這塊看起來是一個大問題,飛船的模型我們已經封裝了Ship類,在Draw()方法中通過BasicEffect來渲染輸出。天空我們也是封裝了類,同樣也有它自己的BasicEffect來控制渲染效果。現在要做的就是把兩者結合起來,讓天空的旋轉能夠在船身上形成光線的變化。
回顧前文中學到的光線的知識,在BasicEffect中提供了環境光線、高光、有向光等光源處理,如果能為飛船施加一個有向光,並且該光的方向與天空盒中太陽光的方向相同,那麼就可以先實現真的讓圖片中的太陽發光。而光線的變化,就轉變成讓這速有向光的方向始終保持與旋轉的天空盒中太陽光的方向相一致就可以了。
根據這個思路,首先來為Ship類增加有向光方向這一屬性。
public Vector3 lightDirection { set; get; }
然後改變Ship類中的Draw()方法,為其中的BasicEffect增加有向光參數。代碼如下:
public override void Draw(GameTime gameTime) { model.CopyAbsoluteBoneTransformsTo(modelTransforms); foreach (ModelMesh mesh in model.Meshes) { foreach (BasicEffect effect in mesh.Effects) { effect.World = modelTransforms[mesh.ParentBone.Index] * worldMatrix; effect.View = viewMatrix; effect.Projection = projectionMatrix; effect.LightingEnabled = true; effect.AmbientLightColor = new Vector3(0.35f, 0.35f, 0.35f); effect.DirectionalLight0.Enabled = true; effect.DirectionalLight0.Direction = Vector3.Normalize(lightDirection); effect.DirectionalLight0.DiffuseColor = Color.White.ToVector3(); } mesh.Draw(); } base.Draw(gameTime); }
這樣一來,只要為飛船對象設定光的方向,就可以在船身上做出正確的反射效果了。
接下來再來確定陽光的方向,根據天空盒的圖片和頂點參數,在構建天空盒各三角形的頂點資料時,我們假設是在z軸正方向的盒外看盒子,因此,貼圖中有太陽的部分被貼到了天空盒的面向觀察者這一面,即前面。現在,渲染時我們觀察者的位置是在天空盒的盒子裡,視線指向了z軸的負方向,因此,太陽是在我們的身後,陽光方向可以近似認為指向了z軸的負方向。
這樣確定了陽光的方向以後,在MainScene代碼中修改LoadContent()方法,就可以為ship對象設定光線的方向為(0,0,-1)了,代碼如下:
protected override void LoadContent() { // Create a new SpriteBatch, which can be used to draw textures. spriteBatch = new SpriteBatch(GraphicsDevice); camera = new Camera(this, new Vector3(100,100,100), new Vector3(0, 0, 1), Vector3.Up, MathHelper.PiOver4, GraphicsDevice.Viewport.AspectRatio, 0.1f, 500); basicEffect = new BasicEffect(GraphicsDevice); skyBox.projectionMatrix = camera.projection; skyBox.viewMatrix = Matrix.CreateWorld(new Vector3(0,0,0),new Vector3(0,0,-1),Vector3.Up); ship.projectionMatrix = camera.projection; ship.viewMatrix = camera.view; ship.worldMatrix = Matrix.CreateScale(-0.02f) * Matrix.CreateRotationZ(MathHelper.ToRadians(180)); ship.lightDirection = skyBox.worldMatrix.Forward; ship2.projectionMatrix = camera.projection; ship2.viewMatrix = camera.view; ship2.worldMatrix = Matrix.CreateScale(-0.02f) * Matrix.CreateRotationZ(MathHelper.ToRadians(180))*Matrix.CreateTranslation(new Vector3(50, 0, 0)); ship2.lightDirection = skyBox.worldMatrix.Forward; }
執行程式,會看到飛船船身的光線已經符合天空的環境了。
如果讓光線也能變,那還是要在Update()方法中增加如下代碼。
protected override void Update(GameTime gameTime) { // Allows the game to exit if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); TouchPanel.EnabledGestures = GestureType.Tap; if (TouchPanel.IsGestureAvailable) { GestureSample gestureSample = TouchPanel.ReadGesture(); if (gestureSample.GestureType == GestureType.Tap) { skyBox.worldMatrix *= Matrix.CreateRotationY(MathHelper.ToRadians(5)); ship.lightDirection = skyBox.worldMatrix.Forward; ship2.lightDirection = skyBox.worldMatrix.Forward; } } base.Update(gameTime); }
從這段代碼中可以看出,每當天空盒旋轉的時候,需要將天空盒新的視點座標的向方向量作為有向光的方向,從而讓天空盒的旋轉與光線的旋轉同步,飛船也就會按照對應的光線方向渲染出陽光照射的效果了。
運行結果。
到此為止,我們已經能初步運用XNA提供的3D功能開發應用了,通過運用矩陣可以進行複雜的變換,通過運用向量可以控制方向,這些都是電腦圖形學理論的基礎。其實,前面關於物體的變換也同樣可以應用於攝像機,對攝像機施加平移、旋轉的變換就可以形成電影中最基本的鏡頭推拉搖移效果,從而也讓我們的思想在天空中飛翔。
——歡迎轉載,請註明出處 http://blog.csdn.net/caowenbin ——