2012年12月30日 星期日

Terrain Vertex Color

Vertex color如果我們希望有透明水彩的樣子,會透到舊的Vertex color的話,就會用顏色做lerp,會有強度做為參數,調整與舊有的顏色做加總,公式如下:
strength * color + (1 - strength) * last time color = Final Vertex color
其實就是alpha blending的公式。(上面的數值應該都保持0~1之間)

最後跟我們一般的Diffuse做相乘(應該很少會讓地表的vertex color直接覆蓋掉Diffuse)。

那因為做這種乘色的動作,通常都會降低整個顏色的亮度,所以應該還要有個亮度筆刷,其實這種亮度應該要說是最後顏色的強度,也就是在跟Diffuse做相乘時,還會再乘個數值,讓最後的顏色結果整個再提亮。公式如下:
strength * Brightness + (1 - strength) * last time Brightness  = Final Brightness
FinalVertex color * Diffuse *  Final Brightness = Final Diffuse
這邊從公式就看的出來,如果要提亮的話,Brightness一定要是要大於1的,當然小於1的話還是可以把它刷的更暗。
(如果改成FinalVertex color * Diffuse + Final Brightness這樣的話,看起來會有點像是粉筆筆刷的感覺,兩者就差在粉筆筆刷亮度提高後會容易看不到最後Diffuse的顏色)


這邊要注意的是資料儲存的問題,可以看到Color data in shader這篇。因為通常我們使用地表筆刷刷地表的話,一定會有內外圈,內圈和外圈中間通常是做smooth的動作,所以如果我們使用Unsigned integer來儲存資料的話,那經過smooth(如何做Smooth的動作可看到
Circle shape radial blur這篇)的話,一定會卡掉一些資料,那整個刷出來的感覺就會變成一圈一圈的,不會是一個模糊的放射狀。所以最後儲存的結果就要看到底是要存成一個image還是一個自訂binary格式。如果是image,因為最後一定是存成unsigned integer,一定還是有數值誤差的問題。那如果存成自訂binary格式的缺點就是不能編輯,彈性不夠高。




2012年12月29日 星期六

Color data in shader

一般送進shader的color stream,如果要省顯示卡記憶體空間的話,可以考慮使用unsigned integer的方式送進去。通常空間會有三倍的差距。

目前不知道format在DirectX會叫什麼,但是Gamebryo的話,在使用color stream時element format就叫F_NORMUINT8_4,並且在Gamebryo的預設Material class內會有個SetupPackingRequirements這個function,專門用來設定套用這個Material的Mesh有哪些stream可以送到shader內(這樣的好處是不管Mesh有哪些資料,只要套用到自訂的Material就可以決定說我們只要用Mesh的哪些資料,這樣就不用全部送到shader內浪費顯卡記憶體空間),記得在這邊設定Shader declaration的話要使用SPTYPE_UBYTECOLOR。

然後在shader裡面的color register宣告還是可以宣告成float4,shader會自動幫忙正規化到0~1之間。

這邊要注意使用unsigned integer的動作,可能會有數值誤差的問題。如果color資料需要事先在CPU內計算,並且儲存的格式是unsigned integer,而且color資料會在CPU內先用一些浮點數運算,因為要轉成unsigned int而去掉小數點的數值,然後最後送到GPU內,並且在GPU內又要使用color做運算。注意這邊在GPU的時候拿到的數值,就是已經被去掉部分小數,又轉成float的數值了,這樣的值一定會比整個過程都用float資料來做運算處理的值來的有差距,所以最後出來的畫面一定有落差。

2012年12月1日 星期六

C# with Unmanaged c++ debug setting

如果起始專案是C#的話,而用到其他的專案有些是Unmanaged c++的DLL,預設如果想下中斷點在這些Unmanaged c++的專案,是無法使用的。得在起始專案的屬性內設定->Debug頁面->Enabled Unmanaged C++ debugging。

2012年10月9日 星期二

Debug DLL

Compiler DLL還是會產生.pdb檔,這個pdb檔會在compiler時候產生,做為Debug時可看到Source code之用。通常pdb檔放在跟dll檔同一個資料夾,就會自己去抓到。但是如果放在別的資料夾就要去設定symbol file放在哪裡。
設定方式可以參考到這:
http://msdn.microsoft.com/en-us/library/x54fht41.aspx

VS2008的從MSDN的說明來看,是在Tools->Options->Debugging->Symbols裡面設好就可以了。
另外看有沒有設到正確的抓到PDB檔,可以在VS Debug執行時,Debug->Windows->Modules內可以看到所有DLL載入的情況。

2012年9月19日 星期三

NiMesh composition


NiMesh are the array of NiDataStreamRef objects that contain all varying attributes of the mesh (vertices, indices, etc)

Element - An element is an array of data that supplies a single input variable (varying or uniform) to a shader program. Each stream element defines a format and an offset where a particular element may be found for a given stream. Multiple elements may be interleaved(插入) into a single stream.
這邊指的是因為我們的NiDataStream是一連串的資料,假設有十組頂點資料,每組內的資料包含了當前的頂點位置,法向量,貼圖座標,前面這三項我們稱為Element

Region A Region defines a contiguous subset of a stream. The elements must be the same across all regions in a stream. Every region contains the following data:
-the base offset into each stream
-the active number of elements for the Region
Region指的就是data stream內的某部分資料範圍

Mesh裡面是很多個data stream ref所組成的。
似乎一個mesh裡可以用好多個,不過我想通常剛好分成各個Sematic各一個stream。應該很少會有相同的Sematicdata stream。不過看起來好像可以多個相同的Semantic data stream。可能主要是共用頂點資料八。但是一個mesh裡只可以有一個USAGE_VERTEX_INDEX 用途的stream

Semantic就是送到Shader內,他要知道目前送來的資料是頂點位置,法向量,還是貼圖座標,這個information就是Semantic
Semantic index:指的像是TEXCOORDINATE0, 1, 2, 3這種暫存器的index

NiDataStreamCreateDataStream,然後就可以丟NiDataStreamElementSets就可以了。

NiDataStreamElementDescribes an element of a NiDataStream object, by data type, format, and offset.

NiDataStreamElementLock是用來可以從指定的Mesh取得某一個特定的semantic的資料,並且可以用來抓到meshsub-mesh的某特定semantic資料的起始位置和結束位置。所以要注意它是已經被綁定的。

NiDataStreamPrimitiveLock是用來可以從指定的Mesh取得某一個特定的primitive的資料index資料,像是LineTriangle這種,都是Primitive
Element的話應該可以指定單一某種Semantic的資料全都抓出來的樣子。而Primitive是以Primitive來分,所以Primitive可能會有POSITION and COLOR的資料都有。

NiTStridedRandomAccessIterator 就是用來抓element內的資料的。
Semantic - The elements must be the same across all regions in a stream


NiDataStreamRef是用來記錄一個data stream裡面資料代表什麼資料的範圍,及語意,NiDataStreamRef可以多個對一個data stream


NiDataStreamElement::Type 資料型態int or something
NiDataStreamElement::Format 資料擺放格式,可能是xyzw,或者zyxw這種擺放格式。

2012年7月12日 星期四

QT lib compiler with visual studio

想compiler自己的QT lib以及同時產生出visual studio的專案。先下載qt-win-opensource-4.8.2-vs2008,本身就有附lib檔,但是沒有專案檔。如果想要2005的lib檔以及專案檔就得自己compiler。
目前測試環境:
qt-win-opensource-4.8.2-vs2008
visual studio 2005 professional
window 7 企業版
---------------------------------以下轉載別人的內容
添加系統環境變量
QTDIR=D:\Qt\4.8.1
QMAKESPEC=win32-msvc2005
註:如果是vs2008話改成win32-msvc2008
在PATH裡添加%QTDIR%\bin

打開VS2005命令提示窗口(開始菜單-Microsoft Visual Studio 2005-Visual Studio Tools-Visual Studio 2005 Command Prompt)
在VS2005命令提示窗口(系統的命令窗口不行)下切換到目錄:D:\Qt\4.8.1
輸入命令行: configure -debug-and-release -opensource -platform win32-msvc2005
提示是否接收license的時候輸入y
10分鐘左右的樣子就configure完成了
經常在Linux下編譯安裝軟件的同學對這一步肯定非常熟悉嘍。它的作用就是根據當前系統的具體環境生成一個makefile文件,供之後make用。
註:configure 後面的參數可以根據自己的需要來做調整,去掉不需要的東西,精簡系統,減少make編譯時間。參數說明可以看官方文檔 http://doc.qt.nokia.com/4.7-snapshot/configure-options.html 或者configure –help
configure完成之後就要make了,這是一個漫長的過程。
make步驟有兩個選擇,一是繼續輸入nmake,二是通過並行編譯版的nmake——jom來編譯。
關於jom的介紹可以看這裡,http://labs.qt.nokia.com/2009/03/27/speeding-up-visual-c-qt-builds/, 有人寫了並行版的nmake,可以讓多核cpu來所有的cpu資源都利用起來,加快編譯速度。ftp://ftp.qt.nokia.com/jom/ 這裡下載最新的jom,然後解壓到一個位置,比如說 D:\Qt\jom,然後在make步驟就可以這樣..\jom\jom.exe -j N,這裡的N是cpu個數。
我用nmake編的時候出錯了,後來重新configure了一份,用jom來編,N為4,沒有錯誤,用了不到兩個小時的時間。
----------------------自己實際遇到的問題
DefaultLocalizationStrategy.cpp(327) : 「error C2001: newline in constant」 in building 4.8.0
主要應該是當遇到不支援unicode的程式時,OS會把這個視為什麼語系,因為我們是繁體中文版,所以只要把OS預設的地方改成英文(美國)就好了。
win 7的地方在控制台->地區及語言->系統管理(分頁)->非unicode的語言改成英文(美國)

2012年7月7日 星期六

Homogeneous Matrix Decomposition

主要的用途是Matrix經過許多translate, rotate, scale後的matrix怎麼解析回原來的matrix或數值。目前是沒使用過這種東西,而且也覺得要在程式上實作效率應該蠻慢的,覺得真的要求出來User做了什麼動作,花點空間紀錄最實在。如果真的要的話只知道有種東西QR Decomposition,Wiki查的到這邊只是給自己留著紀錄,也沒實際看懂過。

2012年5月8日 星期二

Gambryo standard material registry global constant

因為繼承gamebryo previewer的standard material來修改gamebryo的shader pipeline,一般我們新增一個global attribute這個動作一般都放在standard material的callback function,像是HandleFinalPixelOutputs function。但是要注意因為這個callback function是在model被render時,如果shader仍然沒有被產生出來,才會呼叫到這一個callback function。所以自然的在gamebryo shader factory global attributes list,都還不知道有一個新增的變數。如果剛好我們在這之前,先用了NiShaderFactory::UpdateGlobalShaderConstant,這樣自然在 global attributes list內,找不到指定變數名來update。如果一定要在callback function被呼叫之前就要有這個  global attribute,應該用 NiShaderFactory::RegisterGlobalShaderConstant。

那為什麼一般像fx的shader就不用這樣,是因為fx本身在code內就寫好有哪些attributes是global了,所以gamebryo去load指定的shader library時就會去找到哪些attributes是global的,把它加到global attributes list。而相反的因為繼承standard material的關係,因為是shade tree的關係,所以shader code是動態產生的,必須等到callback function執行才知道有新的global attributes。

Shader render state annotation

directX fx shader一般有提供一些render state,可以直接在每個pass內決定說要做什麼樣的render方式,像是AlphaBlendEnable = false; ZEnable = false;這類的config。

那要注意的是像gamebryo,它則是要在technique這邊宣告一些東西

technique testTechnique
<
    string Description = "Gamebryo annotation";//這行只是描述而已
    string ShaderName = "testTechnique";//gamebryo這行要match technique的名字
    bool UsesNiRenderState = true;//要設為true, gamebryo才會設定render state
>
{
    pass P0
    {
        .....
        ZEnable = false;
        AlphaBlendEnable = false;
    }
}

2012年4月18日 星期三

Linear Depth Map

一般我們在shader內取得depth的方式是 projected position.z / projected position.w。但是這樣的depth並不是線性分佈的,離鏡頭比較近的東西,Z值變化很大,會吃掉Z值90%的值。也就是說距離鏡頭前10%的部分,如果depth的range是0~1之間的話,就會在0~0.9值在變化,剩下的離鏡頭90%的部分,就只會在0.9~1之間變化。

所以要改成projected position.z / camera far plane dist。

目前原理都不曉得為什麼,所以之後再補上。

2012年4月10日 星期二

Gamebryo NiStandardMaterial Alpha Testing

基本上Gamebryo讀取的Model套用到NiStandardMaterial的話,如果Model要做Alpha Testing或Blending的話,基本上就會使用Model本身的設定。但是可以看到Gamebryo產生出來的shader code,實際上他並沒有在shader內做discard的動作,shader code內的程式都會執行,不過重點在於color register 0的alpha value,實際上Gamebryo內是用這個alpha value來做Alpha testing的。所以當今天用了其他的color register,即使把alpha vlaue都設為1都沒用,因為color 0 alpha testing後去掉的pixel,其他color register相同的pixel也不會有顏色。

所以要注意不要任意的把color 0的alpha value任意設,應該follow model最後的值。

2012年3月28日 星期三

Shader semantics

Shader的constants像是float4x4 world : WORLD,實際上矩陣也是從C++ bind到shader的暫存器上,
但是在變數加上WORLD是用來指定變數要使用哪個常數暫存器,這通常是人家寫好的render engine關鍵字,這樣就可以任意宣告變數名字了。

2012年3月1日 星期四

Build Events and Custom Build Step

在Visual Studio的C++專案右鍵->Properties有個Build Events,是當程式在Compiler時才會發生的Events,可以在各個Event的Command Line加上一些DOS指令,方便我們做一些事情,只要每次compiler時就會自動執行,通常像是複製或刪除檔案之類的。
(在Visual Studio用這種Command Line的好處是可以使用Visual Studio的一些變數($(變數名稱))。)
(詳細Compiler的執行順序可以看到MSDN的介紹)

另外想要每次只要執行程式時,不管有沒有做compiler的動作,都會執行Command Line動作的,可以使用Custom Build Step,詳細使用可以看到MSDN的介紹。只要就是在某個檔案按右鍵->Properties->Custom Build Step,下Command Line的命令,以及Outputs的地方指定一個output file(一定要有值)。執行Command Line的條件是只要當目前有Custom Build Step的檔案有更新,或者output file不存在的話,就會自動執行。

2012年2月24日 星期五

Simulate Switch in Lua

因為Lua不知道為啥沒有Switch的流程控制可以使用,Lua製作團隊應該也是被問到煩死了才寫了Lua switch wiki的吧,因為目前沒有涉略很深,就直接用個最簡單的範例吧。

A simple version of a switch statement can be implemented using a table to map the case value to an action. This is very efficient in Lua since tables are hashed by key value which avoids repetitive if <case> then ... elseif ... end statements.
action = {
  [1] = function (x) print(1) end,
  [2] = function (x) z = 5 end,
  ["nop"] = function (x) print(math.random()) end,
  ["my name"] = function (x) print(x) end,
}
Usage (Note, that in the following example you can also pass parameters to the function called) :-
action[case](params)
example: action["my name"](" is tkdstkdstkds");
This is pseudocode for the above:
switch (caseVariable) 
  case 1: print(1)
  case 2: z=5
  case "nop": print(math.random())
  case "my name": print(" is tkdstkdstkds")
end

2012年2月22日 星期三

Edge Detection--Sobel Filter

Edge Detection最基本的想法就是值的變化程度(gradient magnitude),當前處理的pixel的值與周遭旁邊的值如果有急遽的變化的話,代表這目前的pixel可能是處於影像的邊緣。所以通常會設一個Threshold來決定當值的變化量到一定程度時,當前處裡的pixel就視為邊緣。

最基本的Sobel Filter可以參考到Wiki的Sobel operator這篇。目前自己實作過的也只有Wiki的Formulation所提到的方式,在每個pixel convolution 水平和垂直的矩陣之後的Gx、Gy,最後平方相加再開根號完取得G就是值的變化程度。為什麼可以這樣,主要是因為Sobel Filter的矩陣可以看到所有Element相加一定是為0的,所以如果當前的pixel與周圍相鄰的八個pixel值如果相同的話,做完convolution取得值的變化程度一定是0。

實作程式可以看到真00無雙的這篇。要注意的地方是當矩陣做convolution時超出image的範圍時該怎麼做,通常是那邊的矩陣元素就不要乘了。

這邊只是簡單的虛擬碼,沒考慮矩陣超出image的範圍。重點是要容易了解演算法的結構,簡單的邏輯判斷就不管了。
foreach(pixel)
{
    float pixelValue[9]
    {
          leftup,      up, rightup
            left, current, right
        leftdown,    down, rightdown 
    };
 
    float sumX = 0, sumY = 0;
    for(i = 0; i < 9; i++)
    {
     sumX += pixelValue[i] * horizontalMatrix[i];
     sumY += pixelValue[i] * vertical Matrix[i];
    }
 
    gradient = sqrt(sumX * sumX + sumY * sumY);
 
    // threshold could be percent value for current pixel.
    // and we suppose pixel value range is [0, 1], and follow wiki common 
    // sobel matrix, the threshold range must be [0, 4].
    return (gradient > threshold * pixelValue[4]) ? edge : background;
}