2D / 3D 遊戲程式設計入門 使用 XNA 3.0 與 C# 第六章 3D繪出管道流程、著色器與特效檔
本章目的 介紹3D繪出的管道流程 (rendering pipeline)、著色器(shader)、與特效檔(effect)。
3D繪出的管道流程 是一系列的過程,描述如何將3D的模型經由頂點座標矩陣轉換、頂點顏色運算、像素顏色運算、像素貼圖運算等等一連串的處理,最後呈現成可繪出在螢幕的2D圖像
著色器(shader) 是將這個輸出過程的頂點運算與像素運算的部份以High-Level Shading Language (HLSL) 程式語法撰寫,在繪圖卡的GPU上執行的技術
特效檔(effect) 是專案下的一個文字檔案,其內容包括頂點著色器程式、像素著色器程式以及相關設定的配對包裝檔案
3D繪出的管道流程
頂點處理的細部流程
世界座標轉換 世界座標轉換是要將3D模型搬到世界舞台某個方向、位置、以及調整縮放比例的必要運算。 Matrix M1 = Matrix.CreateTranslation(1.0f, 2.0f, 3.0f); // 位移 Matrix M2 = Matrix.CreateRotationX(MathHelper.PiOver2); // 旋轉 Matrix M3 = Matrix.CreateScale(1.5f, 2, 3); // 縮放 Matrix MT = M1 * M2 * M3;
視覺空間轉換 視覺空間轉換是當相機擺置好後,從相機看過去的景象。 Matrix ViewMatrix = Matrix.CreateLookAt( new Vector3(0.0f, 2.0f, 2.0f), // 相機觀測點 Vector3.Zero, // 相機目標點 Vector3.Up); // 相機的 上方向量
背面剪裁 背面剪裁是預先將特定方向描述的三角面作剔除的動作,其目的是節省CPU運算量。通常我們只會要求繪出時,三角面只呈現出正面,而不呈現背面,因為背面是包藏在3D模型物體的內部,正常的狀況下是看不到的 GraphicsDeviceManager graphics; … graphics.GraphicsDevice.RenderState.CullMode = CullMode.CullClockwiseFace; // 將 順時間方向 的三角面剔除 或 = CullMode.CullCounterClockwiseFace; // 將逆時間方向的三角面剔除(內定) = CullMode.None; // 三角面的正反面 都不剔除
燈光值計算 燈光值計算是將頂點依照頂點預設顏色、頂點法向量、光源顏色、光源向量等等因素計算後得到的頂點顏色。 BasicEffect basicEffect = new BasicEffect(graphics.GraphicsDevice, null); basicEffect.DiffuseColor = new Vector3(1.0f, 0, 0); // 紅色 擴散光 basicEffect.AmbientLightColor = new Vector3(0.2f, 0.2f, 0.2f); // 環境光 basicEffect.EmissiveColor = new Vector3(0, 0, 1); // 自發光 basicEffect.SpecularColor = new Vector3(1, 1, 1); // 反射光 basicEffect.DirectionalLight0.Enabled = true; basicEffect.DirectionalLight0.DiffuseColor = new Vector3(1.0f, 1, 1); basicEffect.DirectionalLight0.Direction = new Vector3(1.0f, 0, 0); basicEffect.LightingEnabled = true;
剪輯 剪輯是將位於平頭角椎體之外的基本形狀剔除;位於平頭角椎體之內的基本形狀保留;部份位於平頭角椎體之內的基本形狀切割成保留與剔除兩部份。
投影空間轉換 投影空間轉換是將位於平頭角椎體之內的頂點,依照比例,壓縮成為 2 x 2 x 1 的立方體。所以離鏡頭比較遠的頂點的壓縮移動量就會比較大。 Matrix ProjectMatrix = Matrix.CreatePerspectiveFieldOfView( MathHelper.ToRadians(45.0f), // 視角 1.333f, // 寬高比 1.0f, // 近平面 10000.0f); // 遠平面
視口空間轉換 視口空間轉換是將2 x 2 x 1 的立方體3D投影到視窗2D矩形畫面上的轉換。
填點處理 填點處理的部分主要是處理兩件事情 第一是依照程式的設定產生基本形狀 第二是以補插方式產生許多像素來將基本形狀填滿。
以補插方式填出頂點中的像素
紋理貼圖圖素的混合處
寫入後緩衝區前的幾項測試
頂點著色器以及像素著色器 早期遊戲3D皆使用固定的處理流程,程式終止要給予要設定的參數即可。 近年來由於繪圖卡功能的大幅提昇,繪圖卡已經取代CPU來作頂點處理以及像素處理。 我們要寫出頂點處理的小程式與像素處理的小程式來讓繪圖卡的GPU作運算。 這種可程式化,能讓GPU處理的小程式叫做頂點著色器(Vertex Shader)以及像素著色器(Pixel Shader)。 著色器特效程式目前常用的有微軟的High-Level Shading Language、OpenGL Shading Language和Nvidia公司的Cg語言。
頂點著色器以及像素著色器
特效檔的幾個區塊
特效檔透過全域變數接收主程式的參數值
頂點著色器函數
像素著色器函數
HLSL的基本資料型態 Bool 布林值 true、false int 32 位元 整數 half 16 位元 浮點數 float 32 位元 浮點數 double 64 位元 浮點數
HLSL的資料型態 sampler取樣器 struct結構 texture紋理貼圖
取樣器 取樣器是用來描述如何從紋理貼圖讀出圖素的方式 texture2D diffuseTexture; sampler2D diffuseSampler = sampler_state { Texture = diffuseTexture; MinFilter = Linear; // 線性取樣 MagFilter = Linear; MipFilter = Linear; AddressU = Wrap; //環繞模式 AddressV = Wrap; AddressW = Wrap; }
紋理貼圖的對應座標
紋理圖的定址模式
HLSL的語意
頂點著色器的輸入語意 POSITION頂點座標float4 COLOR頂點顏色float4 NORMAL頂點法向量float4 TEXCOORD頂點的紋理圖UV座標float4 TANGENT頂點的正切向量float4 BINORMAL頂點的binormal向量float4 BLENDINDICES頂點的骨骼混合索引值int4 BLENDWEIGHT頂點的骨骼混合權重float4
頂點著色器的輸出語意 POSITION頂點座標float4 COLOR頂點顏色float4 TEXCOORD頂點的紋理圖UV座標float4 FOG頂點霧化值float
像素著色器的輸入語意 COLOR像素顏色float4 TEXCOORD像素的紋理圖UV座標float4
像素著色器的輸出語意 COLOR像素顏色float4 DEPTH像素的Z軸值(深度值)float
HLSL語言內建函數 dot向量內積 cross向量外積 lerp線性內插 mul矩陣相承 normalize向量單位化 pow指數 eflect反射向量 refract折射向量 saturate將值限制在 0~1 之間 tex2d2D紋理圖素採樣 tex3d3D紋理圖素採樣
3D模型、網格、網格零件、和效果的關係
Effects是全部網格零件的Effect的總集
範例一:使用BasicEffect呈現一個3D模型 在LoadContent將模型內的骨架轉換矩陣拷貝出來 在Draw中一一畫出在模型中的每一個網格(mesh)
範例一:使用BasicEffect呈現一個3D模型 protected override void LoadContent() { // TODO: use this.Content to load your game content here myModel = this.Content.Load<Model>(“monster_01”); // 上載 // 3D 模型 // 將myModel模型內的骨轉換矩陣拷貝出來到一個矩陣 transforms = new Matrix[myModel.Bones.Count]; myModel.CopyAbsoluteBoneTransformsTo(transforms); }
範例一:使用BasicEffect呈現一個3D模型 protected override void Draw(GameTime gameTime) { …..foreach (ModelMesh mesh in myModel.Meshes) { // 設定網格的呈現效果 (世界、觀測、投影矩陣) foreach (BasicEffect effect in mesh.Effects) { effect.World = transforms[mesh.ParentBone.Index] * Matrix.CreateScale(0.01f); // 如果模型太大 就縮小一些 effect.World = effect.World* Matrix.CreateRotationY(modelRotation); effect.View = Matrix.CreateLookAt(new Vector3(0.0f, 1.2f, 1.2f), Vector3.Zero, Vector3.Up); effect.Projection = Matrix.CreatePerspectiveFieldOfView( MathHelper.ToRadians(45.0f), 1.333f, 1.0f, 10000.0f); } // 畫出在 模型 中的 某一個 網格 mesh.Draw(); } base.Draw(gameTime); } } }
範例一:使用BasicEffect呈現一個3D模型 /// protected override void Update(GameTime gameTime) { // Allows the game to exit if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); // 旋轉角度 modelRotation += 0.01f; base.Update(gameTime); }
範例二:使用BasicEffect呈現一個3D模型 /// protected override void Update(GameTime gameTime) { // Allows the game to exit if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); // 旋轉角度 modelRotation += 0.01f; base.Update(gameTime); }
The End