kanzi&opengl雜談

最近有幸總結了一下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的部分這裏只是簡單介紹。我們只需知道整個工作流程即可。

 

  1. 準備頂點數據:

在CPU中進行。這裏會定義一些VAO和VBO,他們包含了每個一些頂點數據的信息,在shader中的體現即attribute。

在kanzi中,我們定義的EmptyNode2D或者Image2D...在這部分的體現即會準備不重複的4個點即重複的6個點,構成2個三角形。然後會將這些點綁定到VAO或者VBO中,發送給GPU。

 

  1. Vertex Processing

調用Vertex Shader處理頂點數據信息。這個階段會接受傳遞過來的attribute數據作爲輸入,然後通過一些轉換後輸出。每個頂點都是1對1。

Kanzi中我們可以看到attribute屬性的有kzPosition,kzTextureCoordinate0及其他。

 

  1. Primitive Assembly

圖元裝配:即把vertex shader輸出的頂點數據集合到一起,並把它組合成一個圖元的過程。這個輸出的類型由用戶決定,一般輸出的是三角形,這個取決於你drawcall的類型。

 

  1. Geometry Shading: 這個目前不做介紹,它即對Primitive Assembly輸出的圖元處理後再輸出圖元。

 

  1. Rasterization

光柵化:一系列處理後的圖元會被光柵化爲一系列的‘候選像素點’。

  1. Fragment Processing

調用Fragment shader處理像素點。處理後我們只需要得到out的像素即gl_FragColor。

  1. Pre-Fragment :
  2. 生成frame buffer後GPU顯示出來。

 

這部分東西很多,而我們應該瞭解的東西總結一下:

  1. Fragment Shader處理像素點,故一些運算如果可以放到Vertex shader中最好。
  2. 變量的屬性:

Attribute:輸入的定點屬性,無法改變。

Uniform:用戶定義的屬性,可在外部改變。

Varying:在vertex shader和fragment shader傳遞變量

  1. Float精度

highp:

mediump:

lowp:

使用默認精度可加precision mediump float;

  1. 着色器儘量簡單。

 

二:brush及Material

Kanzi中,如果需要對自帶的2D節點添加材質,則需要一箇中介brush。

如果我們需要在一個Node2D上畫出某個東西(顏色,材質或者圖片),我們需要藉助一個brush來操作。

僞代碼:

  1. 使用Material Brush:

Material::Create();

Shader::Create()

Shader->InitializeFromMemory()

Material->setShader()

MaterialBrush::Create(Material)

BrushResource = kzuBrushResourceCreateFromBrush(MaterialBrush)

Node->setBackGroundBrush(BrushResorce)

 

  1. Color Brush和TextureBrush同理。

 

故思考:如果我們需要提高開機速度,那麼如果這個節點剛好有Material或者其他,那麼我們可將這部分的內容拆開放到後面處理。測試,添加material和不加material加載速度確實是有差異的。

 

 

材質:通俗的將就是材質決定我們想要的物體呈現什麼樣子。可以理解爲是編譯shader後生成的一個實例,材質的屬性就是shader uniform開放的變量。所以我們通過材質的屬性可以達到我們想要的效果。

 

在kanzi中,shader這部分我們無法輸入attribute,所以在我們操作範圍內只能去改變out color。在其中,我們可以添加uniform去控制我們想要達到的效果。

在編寫shader過程中,考慮到幀率因素,有以下優化方案:

  1. 較少甚至禁用 uniform varying的if或者for。
  2. 由於gpu中的運算時並行運算,所以在有if...else判斷中,會根據最慢的執行計算,所以我們需減少if...else執行時間。
  3. GPU中計算實際爲矩陣運算,而float在計算時相當於也會轉換爲四維矩陣的運算,所以我們應當減少運算的次數。
  4. 如果是透明或者純色,我們可直接使用color,減少取紋理。
  5. 除法運算儘量轉爲乘法運算。
  6. 在運算過程中,注意一些複雜的運算符,比如pow,exp,sin,cos,log,tan等。
  7. Float的精度可根據實際情況使用精度低的類型
  8. 在計算一些頂點等信息時,能放到vertext中的儘量放到vertextshader中運算。
  9. 在運算過程中,避免有所依賴。這樣並行運算可能會是同步的

 

擴展:

  1. Blend Mode:
    1. Opaque:不開啓混融效果
    2. Automatic:根據Project屬性的預設值選擇Premultiplied或者Non-Premultiplied,默認是Premultiplied.
    3. Premultiplied:實際調用glBlendFunc(GL_ONE,GL_ONE_MINUS_SRC_ALPHA)
    4. Non-Premultiplied:調用glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
    5. Mixed:調用glBlendSeparateFunc(SRC_ALPHA, ONE_MINUS_SRC_ALPHA, ONE, ONE_MINUS_SRC_ALPHA)
    6. Additive:調用(GL_ONE, GL_ONE):實際就是兩種顏色相加,比如(1,0,0)和(0,1,0)相glBlendFunc加得到(1,1,0)
  2. BlendIntensity:
    1. .rgba*BlendIntensity
    2. .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默認不啓用模板測試,如果啓用模板測試,一般有如下步驟:

  1. 啓用模板緩衝的寫入
  2. 渲染物體,更新模板緩衝的內容
  3. 禁用模板緩衝的寫入
  4. 渲染其他物體,根據模板緩衝的內容丟棄特定的片段。

 

在每次迭代之前清除模板緩衝。

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入手還有多重採樣。

 

轉載請註明出處:https://blog.csdn.net/allen807733144

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章