2018年4月2日 星期一

PerspectiveMatrix的種類

PerspectiveMatrix的種類

前言

  在整合Direct3D與OpenGL前,我一直認為PerspectiveMatrix只有分左手與右手差別,但在實際整合後發現Direct3D與OpenGL的深度範圍是不一樣的,所以還要再區分一次,最後你會發現PerspectiveMatrix有4種,在此將它紀錄下來。

內容

  PerspectiveMatrix的種類在入門時,因為只要考慮一個繪圖函示庫(Direct3D或OpenGL),所以只要分成左手與右手兩種,但是當需要整合Direct3D與OpenGL時就要考慮到深度範圍不一樣的問題,Direct3D的深度範圍是 0 ~ +1,OpenGL的深度範圍是 -1 ~ +1,因此種類會分為以下:
1.左手,深度範圍是 0 ~ +1
2.右手,深度範圍是 0 ~ +1
3.左手,深度範圍是 -1 ~ +1
4.右手,深度範圍是 -1 ~ +1
在整合引擎時,當你下達產生一個PerspectiveMatrix時,會產生一種呢?首先整合引擎的座標本身就有預設好是左手與右手,但深度範圍就要分函示庫了,這裡整合引擎時要想出一套辦法知道目前使用的函示庫來決定要產生哪一種PerspectiveMatrix。
  接著來說怎麼產生這4種PerspectiveMatrix,Direct3D的部分可以查詢
D3DXMatrixPerspectiveFovLHD3DXMatrixPerspectiveFovRH,說明的下方有矩陣的產生,接著是OpenGL的部分就麻煩了點,必須要看code,首先要有glm(OpenGL Mathematics),它是OpenSource,下載後下列檔案看code怎麼產生:
(glm's root directory)\glm\glm\gtc\matrix_transform.inl

參考資料

OpenGL Mathematics
Projection Transform

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

2018年2月26日 星期一

關於Transform的矩陣乘法

關於Transform的矩陣乘法
前言
  這個部分印象寫了不少次,但每次寫的時候就是無法把細節全記起來,這次就在這裡記錄下來。

內容
  由於矩陣有分左乘與右乘,以下的說明皆為"左乘"。首先是TRS矩陣,使用位移(transition)、旋轉(rotation)與比例(scale)矩陣所組成,組成的順序如下

  TRS_Matrix = ScaleMatrix * RotationMatrix * TransitionMatrix

再來,如果考量到有Parent的Transform,組成的順序如下

  WorldMatrix = TRS_Matrix(child) * TRS_Matrix(parent) * (More parent's TRS_Matrix)...

有以上兩種規則組成的矩陣就是WorldMatrix,如果要將Transfrom的內容應用到Shader就要考慮ViewMatrix與ProjectionMatrix,組成的順序如下

  WVPMatrix = WorldMatrix * ViewMatrix * ProjectionMatrix

這樣的就可以組成WVPMatrix。
 


2018年2月19日 星期一

關於wglCreateContextAttribsARB()的問題

關於wglCreateContextAttribsARB()的問題
前言
  wglCreateContextAttribsARB()是可以選擇context的版本的CreateContext(),在create方面沒問題,但在wglMakeCurrent()後,會發現畫面再也畫不出任何東西,可是畫面卻是有清除的!這個問題以前一直不知道是什麼問題,最近有空就來Debug一下。

內容
  首先畫面是有清除的,表示glClear()有效的,但"Hello Trangle的範例"執行後會發現三角形畫不出來!
glGetError()也沒有得到錯誤訊息。以前都一直以為是wglCreateContextAttribsARB()的參數沒下好,又或是wglMakeCurrent()的時機的問題,讓我一直找不出這個問題的答案。在Debug中發現當版本被調到3.1後遽然就畫得出來了!但到了3.2以後的版本都繪畫不出來,這時才開始懷疑可能不是wglCreateContextAttribsARB()的問題,看了一下"Hello Trangle的範例"的程式碼,在OpenGL的設置有glUseProgram()、glEnableVertexAttribArray()、glBindBuffer()與glVertexAttribPointer(),看來沒什麼問題,但在google關於OpenGL3.2的範例後發現裡面多了一個glBindVertexArray()!這是OpenGL3.2多的Vertex Array Object(VAO),在我之前對VAO的認知是這個東西可用可不用,但我錯了,在我把VAO加入後,"Hello Trangle的範例"就可以順利畫出三角形了,原來在OpenGL3.2以後,VAO是"必要"流程,而不是可選擇的流程,真是學習了。

參考資料
Tutorial2: VAOs, VBOs, Vertex and Fragment Shaders (C / SDL)