2018年3月26日 星期一

ViewMatrix的產生

ViewMatrix的產生

前言

  最近在debug繪圖引擎時發現矩陣乘出來的值不如預期,但繪圖的結果竟然是對的!這情形在debug後發現是ViewMatrix的產生有問題,這裡就記錄下來。

內容

  首先矩陣的排列如下
矩陣的排列
在Translate的部分(3_0、3_1與3_2)每一個都乘上"-1",接著是Rotation的部分,這個部分要做Transpose!如果這部分沒做Transpose再加上從Quaternion到Matrix的function寫反了,就會發生繪圖的結果正確,但直接用Vector乘矩陣的結果卻是錯的狀況。

2018年3月19日 星期一

閉包用法的陷阱

閉包用法的陷阱

前言

  最近有個關於閉包的bug,找了相當的久才找到,這裡做個紀錄。


內容

  首先先看以下程式碼
var obj={
  name:"MyObj",
  data:[1,2,3,4,5]
};
function printObjectData(data){
  return function(){
    console.log(data);
  };
}
//
var printFun = printObjectData(obj.data);
printFun();
obj.data = [];
console.log("Clear data!");
printFun();

最後console的結果如下
[1, 2, 3, 4, 5]
Clear data!
[1, 2, 3, 4, 5]

可以看到最後一次的printFun()竟然沒有清除資料!本以為可能是javascript的array是pass by value造成的,但實際試驗了一下javascript會發現array是pass by reference,那這到底是什麼問題呢?實際上是這樣的,printFun在從 printObjectData()取得function時,確實是得到obj.data,但問題是接下來的clear,也就是"obj.data = []",這個array的清除有問題,這裡確實清除了obj.data,但閉包裡的data還是舊的array,所以會發現第二次的printFun()依舊是原本的array,問題是說"obj.data = []"的意思其實不是clear一個array,而是將obj.data指向一個空array。如果要修正問題改成以下

var obj={
  name:"MyObj",
  data:[1,2,3,4,5]
};
function printObjectData(obj){
  return function(){
    console.log(obj.data);
  };
}
//
var printFun = printObjectData(obj);
printFun();
obj.data=[];
console.log("Clear data!");
printFun();

2018年3月12日 星期一

Viewport與Scissor

Viewport與Scissor

前言

  這兩個功能常常被搞混,最近在做API的整合,順便把Direct3D與OpenGL的差異實驗一下,在這邊記錄下來。

內容

  Viewport與Scissor的參數都是Rectangle,所以常常考混,這邊就來說說兩者的差異。Viewport的功能是把RenderSpace(寬與高都是-1~+1的空間)透過Rectangle取得實際在RenderTarget的繪製空間,具體的投射如下圖
Viewport的座標投射
Scissor的功能是定義在Rectangle內的Pixel才能被繪製,就這麼簡單,跟Viewport不同的是Scissor不用考慮RenderSpace,就只是把在Rectangle內Pixel留下來,所以Scissor無法做到畫面比例的調整。
  在來說說這兩個功能在Direct3D與OpenGL的差異。在Rectangle的座標是有差異的,具體的差異如下圖
Rectangle的座標差異
另一個差異在clear,Direct3D的Viewport與Scissor對clear都是無效的!也就是不論Viewport與Scissor設了什麼,clear還是清除整個RenderTarget。Opengl的Viewport也對clear無效,但Opengl的Scissor對clear是有效的!也就是說可以利用Scissor來指定要clear的區域。

2018年3月5日 星期一

點光源與平行光

點光源與平行光

前言

  最近實作了點光源與平行光,以前都看別人寫好的光源,現在自己做的時候發現有些觀念跟我想的不一樣,在此做個紀錄。


內容

  在光的Diffuse的部分只需計算Lambert's cosine law,也就利用光的方向與三角形的Normal做點積(dot),以前都沒發現實際上還要再加上一個"負號"才正確,想想點積後的結果是"1"的時候,表示光與normal同一方向,這時應該得到"-1",再想想點積後的結果是"-1"的時後,表示光與normal相反方向,這時應該得到"1",這樣剛好會差一個負號,這點請注意!再來要剃除負號的部分,讓光只會增亮而不會變暗,所以只需算diffuse的平行光計算如下

  diffuse = (light's intensity) * clamp(dot(lightDir,Normal) * -1, 0.0, 1.0)

接著是Specular的部分,需要一個Light與Normal的反射向量與鏡頭所看的方向兩個做點積,這裡要注意Reflect()的光方向要是"相反的",如下圖示
Specular的計算
計算完後強度再利用pow()來控制"範圍",接著再利用intensity來控制"亮度",所以Specular的計算如下

  Specular = pow(clamp(dot(reflect(-LightDir,Normal),viewDir.xyz),0.0,1.0),(powIntensity) ) *                              (light's intensity)

接著是點光源與平行光的差異,平行光不需計算Specular的部分,所以傳入Shader計算時,只須傳一個方向,點光源在Shader計算時傳的是"光源的位置",在diffuse 與Specular 的計算須要的"光的方向"是透過"光源的位置"與"目前計算的Pixel的位置"兩者來取得方向,並且點光源還需要多控制一個"範圍"。


參考資料

Lambert's cosine law