最近有幸總結了一下kanzi中opengl的東西,實際比較重點的也就是shader部分,對於其他部分,這裏也會簡單的說,但是具體的功能作用就要自己下來慢慢研究了
一:引言
本文章主要介紹kanzi和opengl之間的一些問題,可能會比較雜,因爲很多東西也無法預測他們具體是如何結合,故我們會以kanzi爲主,在kanzi的基礎上介紹opengl的一些東西。
通過kanzi help doc中的opengl接口,我們可以瞭解kanzi也是支持modern opengl,故老的opengl接口是不支持的。具體接口可見<system/wrappers/kzs_opengl.h>
這裏的東西比較雜,我們不會具體的說,這裏會分幾個大塊來針對性的說明。
二:具體介紹
一:着色器
一:shader(着色器)
着色器(Shader)是用來實現圖像渲染的,用來替代固定渲染管線的可編輯程序。其中Vertex Shader主要負責頂點的幾何關係等的運算,Pixel Shader(Fragment Shader)主要負責片源顏色等的計算。
着色器替代了傳統的固定渲染管線,可以實現3D圖形學計算中的相關計算,由於其可編輯性,可以實現各種各樣的圖像效果而不用受顯卡的固定渲染管線限制。
目前比較流行的Shader語言除了GLSL之外還有HLSL,RM等,當然,我們目前主要使用GLSL。
Opengl的繪圖管線
當然,這部分opengl的部分這裏只是簡單介紹。我們只需知道整個工作流程即可。
- 準備頂點數據:
在CPU中進行。這裏會定義一些VAO和VBO,他們包含了每個一些頂點數據的信息,在shader中的體現即attribute。
在kanzi中,我們定義的EmptyNode2D或者Image2D...在這部分的體現即會準備不重複的4個點即重複的6個點,構成2個三角形。然後會將這些點綁定到VAO或者VBO中,發送給GPU。
- Vertex Processing:
調用Vertex Shader處理頂點數據信息。這個階段會接受傳遞過來的attribute數據作爲輸入,然後通過一些轉換後輸出。每個頂點都是1對1。
Kanzi中我們可以看到attribute屬性的有kzPosition,kzTextureCoordinate0及其他。
- Primitive Assembly
圖元裝配:即把vertex shader輸出的頂點數據集合到一起,並把它組合成一個圖元的過程。這個輸出的類型由用戶決定,一般輸出的是三角形,這個取決於你drawcall的類型。
- Geometry Shading: 這個目前不做介紹,它即對Primitive Assembly輸出的圖元處理後再輸出圖元。
- Rasterization
光柵化:一系列處理後的圖元會被光柵化爲一系列的‘候選像素點’。
- Fragment Processing
調用Fragment shader處理像素點。處理後我們只需要得到out的像素即gl_FragColor。
- Pre-Fragment :
- 生成frame buffer後GPU顯示出來。
這部分東西很多,而我們應該瞭解的東西總結一下:
- Fragment Shader處理像素點,故一些運算如果可以放到Vertex shader中最好。
- 變量的屬性:
Attribute:輸入的定點屬性,無法改變。
Uniform:用戶定義的屬性,可在外部改變。
Varying:在vertex shader和fragment shader傳遞變量
- Float精度
highp:
mediump:
lowp:
使用默認精度可加precision mediump float;
- 着色器儘量簡單。
二:brush及Material
Kanzi中,如果需要對自帶的2D節點添加材質,則需要一箇中介brush。
如果我們需要在一個Node2D上畫出某個東西(顏色,材質或者圖片),我們需要藉助一個brush來操作。
僞代碼:
- 使用Material Brush:
Material::Create();
Shader::Create()
Shader->InitializeFromMemory()
Material->setShader()
MaterialBrush::Create(Material)
BrushResource = kzuBrushResourceCreateFromBrush(MaterialBrush)
Node->setBackGroundBrush(BrushResorce)
- Color Brush和TextureBrush同理。
故思考:如果我們需要提高開機速度,那麼如果這個節點剛好有Material或者其他,那麼我們可將這部分的內容拆開放到後面處理。測試,添加material和不加material加載速度確實是有差異的。
材質:通俗的將就是材質決定我們想要的物體呈現什麼樣子。可以理解爲是編譯shader後生成的一個實例,材質的屬性就是shader uniform開放的變量。所以我們通過材質的屬性可以達到我們想要的效果。
在kanzi中,shader這部分我們無法輸入attribute,所以在我們操作範圍內只能去改變out color。在其中,我們可以添加uniform去控制我們想要達到的效果。
在編寫shader過程中,考慮到幀率因素,有以下優化方案:
- 較少甚至禁用 uniform varying的if或者for。
- 由於gpu中的運算時並行運算,所以在有if...else判斷中,會根據最慢的執行計算,所以我們需減少if...else執行時間。
- GPU中計算實際爲矩陣運算,而float在計算時相當於也會轉換爲四維矩陣的運算,所以我們應當減少運算的次數。
- 如果是透明或者純色,我們可直接使用color,減少取紋理。
- 除法運算儘量轉爲乘法運算。
- 在運算過程中,注意一些複雜的運算符,比如pow,exp,sin,cos,log,tan等。
- Float的精度可根據實際情況使用精度低的類型
- 在計算一些頂點等信息時,能放到vertext中的儘量放到vertextshader中運算。
- 在運算過程中,避免有所依賴。這樣並行運算可能會是同步的
擴展:
- Blend Mode:
- Opaque:不開啓混融效果
- Automatic:根據Project屬性的預設值選擇Premultiplied或者Non-Premultiplied,默認是Premultiplied.
- Premultiplied:實際調用glBlendFunc(GL_ONE,GL_ONE_MINUS_SRC_ALPHA)
- Non-Premultiplied:調用glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
- Mixed:調用glBlendSeparateFunc(SRC_ALPHA, ONE_MINUS_SRC_ALPHA, ONE, ONE_MINUS_SRC_ALPHA)
- Additive:調用(GL_ONE, GL_ONE):實際就是兩種顏色相加,比如(1,0,0)和(0,1,0)相glBlendFunc加得到(1,1,0)
- BlendIntensity:
- .rgba*BlendIntensity
- .a*BlendIntensity
二:模型
在項目中,很容易看到模型,錶盤,車模等等。先說一下模型的構成:
而一起提供過來的還有一張圖片,這張圖片就是覆蓋在模型表面的紋理。而我們看到的圖片的形狀和模型一般不同,這方面我們不需要關心,模型到底是取得圖片的uv到底在哪也不關我們的事,我們只需要做的就是看後期可能會如何處理它的UV。
例:在項目表盤中,我們很容易看到模型,它到底是怎麼工作的?
如何導入模型不做介紹,我們可以在kanzi中看到mesh管理器中有對應的mesh,每個mesh的信息也可以看到,三角形數量和頂點的數量、fbx名稱、材質等等。
而對於模型的使用和kanzi中3d節點大同小異。
渲染模型流程:
首先,模型除了我們可以操作座標之外,kanzi中我們是改變不了一個mesh內部的頂點結構的,一個mesh在kanzi中的體現就是model3D。所以在通過材質渲染node時,渲染流程即上面所述流程。頂點以attribute在Vertex shader作爲輸入,經過轉換爲世界座標後輸出進行圖元裝配、光柵化等。Vertex中還會包含uv,這個uv即紋理的座標(-1, 1)。那在這部分我們是否可以修改這個mesh的內部結構了?
其次,在fragment中,模型已經是固定的,而類似於錶盤中的掃光實現原理是什麼呢?我們知道在fragment中的運算實際就是計算像素點,將輸出的像素表現在模型對應的位置。所以對於錶盤中的掃光,我們能看到進度的實際是通過控制紋理的uv座標讓顏色顯示出來,而未顯示的部分就是一個vec4(0.0)或其他顏色。
最後,對於模型,三角形越多越細緻,而導致越佔資源;對於紋理,分辨率越大越好,但是影響效率,所以在模型這塊就需取一個適當的值。
三:光照
目前項目中用到光照的東西很少,一般像車模可能會加入光照。
首先光照類型:點光,平行光(太陽光),聚光(手電筒)
這些光在kanzi中都有定義,但是具體實際上也是在shader中去模擬光照的一系列計算。
雖說光是一系列的計算,但是opengl對這些光都有固定的說明。我們這裏會針對kanzi中的東西做一個說明,其實也和opengl類似。
如何使用光不做介紹,我們說說材質中的光照。
Kanzi給我們提供了很多的光照類型的材質:
Phong:只包含光照
PhoneCube:包含光照和環境紋理
PhongTexture:包含光照和紋理
PhongTextureCube:包含光照、紋理和環境紋理。
對於這幾種材質,都有共同屬性就是針對光照的:
Ambient Color:環境光顏色
Diffuse Color:漫反射顏色
Emissive Color:反射顏色
Specular Color:鏡面反射顏色
Specular Exponent:鏡面反射係數
這幾種屬性可改變光的顏色以及一些光的效果。但是很多我們要實現的效果不僅僅是光可以做完的,我們也許在模型表面需要加入材質,於是我們使用PhongTexture,這個是在屬性多了一個Texture屬性,我們需要加入環境的顏色比如水面呈現天空的東西,於是我們再加入Cube。對於Cube,我們下節紋理介紹。
四:紋理
這部分其實也沒什麼東西可說,我們這裏說一下壓縮紋理和環境紋理。
壓縮紋理:目前kanzi支持壓縮的紋理類型:ATC、ETC、PVRTC。
很多時候我們用的都是PNG類型生成的紋理,而使用壓縮紋理往往是資源可能很大或者影響到性能,但是使用壓縮紋理的類型我們需要確定機器是否支持。
支持opengl es2.0(3.0)的是支持ETC1(2)的。我們機器支持的版本是 OpenGL ES 3.0(具體的可通過demo或者kanzi中的opengl接口查看),故我們在使用的時候可壓縮成etc格式的壓縮紋理,其他格式目前不清楚。
在kanzi中找到resource file->image,在對應圖片屬性頁選中要壓縮的目標格式,右鍵創建紋理即可。
環境紋理:遊戲中有天空盒這個東西,但是kanzi中目前是無法使用的,我們可以使用環境紋理。原理:利用環境的顏色/紋理後映射到目的模型上。
環境紋理由六張圖片生成,剛好是構成一個立方體。
在kanzi中創建一個環境紋理:Textures->create cube-map texture,然後再屬性框添加這 六張圖片(注意環境紋理的前後左右上下順序)。
注:arm支持的最大紋理尺寸:8192
kanzi支持導入的最大紋理尺寸:4096
五:3D
項目中3D場景雖然不多,但是基本每個項目多多少少都包含一些,所以粗略的探討一下opengl 3d部分在kanzi中的一些東西。
一:深度測試
深度:該像素點距3d中的攝像機的距離(繪製座標),深度緩存存儲着深度值。
在沒有深度測試時一般都是按照順序繪製,這樣會把某些物體覆蓋,所以深度測試會根據Z值(遠近)來正常顯示。
opengl默認不會啓用深度測試,但是默認開啓深度測試。
glEnable(GL_DEPTH_TEST)
繪製半透明物體不適用。
Kanzi中默認屬性如上圖所示。啓用了深度測試,如果沒有通過深度緩存,則會丟棄該片段,還需在每個渲染迭代之前清除深度緩存,否則仍然在使用上一次的深度值。
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
禁用深度緩衝的寫入在kanzi中沒找到如何做,這種一般使用一個只讀的深度緩衝。
二:Frustum Culling
將不可見的物體提前剔除。
三:Face Cull
面剔除。
剔除背向面,前向面,不啓用剔除。
Opengl默認不啓用面剔除,而kanzi默認啓用背面剔除,意思顯而易見就是背面的面會丟棄不渲染。
四:模板測試
片段着色器處理完一個片段之後,經過深度測試丟棄一些片段,被保留的片段進入模板測試,他可能會丟棄更多。深度測試根據深度緩衝進行,而模板測試也是根據模板緩衝進行。
模板緩衝會首先被清除爲0,之後再模板緩衝中用1填充,然後在場景中的片段只會在模板值爲1時被渲染,剩下的都會被丟棄。
Kanzi默認不啓用模板測試,如果啓用模板測試,一般有如下步驟:
- 啓用模板緩衝的寫入
- 渲染物體,更新模板緩衝的內容
- 禁用模板緩衝的寫入
- 渲染其他物體,根據模板緩衝的內容丟棄特定的片段。
在每次迭代之前清除模板緩衝。
Stencil Fail Operation: 模板測試失敗時的行爲
Stencil Pass Depth Fail Operation: 模板測試通過,但深度測試失敗時採取的行爲
Stencil Pass Depth Pass Operation: 模板測試和深度測試都通過時採取的行爲
行爲具體有下面
Keep:保持當前存儲的模板值
Zero:將模板值設置爲0
Replace:將模板值設置爲參考值(Stencil Function Reference Value)
Increase:如果模板值小於最大值則將模板值加1
IncreaseWrap:如果模板值超過了最大值則歸零
Decrease:如果模板值大於最小值減1
DecreaseWrap:如果模板值小於0則將其設置爲最大值
Invert:按位翻轉當前的模板緩衝值
opengl和kanzi默認的行爲都是keep。
Stencil Function:應用到已存儲的模板值上的選項:
Always:永遠通過測試
Never:永遠不通過測試
Less:在片段模板值小於緩衝的模板值時通過測試
Lequal:在片段模板值小於等於緩衝區的模板值時通過測試
Greater:在片段模板值大於緩衝區的模板值時通過測試
Gequal:在片段模板值大於等於緩衝區的模板值時通過測試
Equal:在片段模板值等於於模板的模板值時通過測試
Notequal:在片段模板值不等於緩衝區的模板值時通過測試
Opengl默認GL_LESS,kanzi默認always。
五:裁剪測試
當啓用scissor test時,會將渲染時的視口做限制,也就是我們處理的像素範圍做了限制。
六:混合
七:抗鋸齒
目前瞭解的在kanzi中做抗鋸齒都是從shader入手還有多重採樣。