重慶分公司,新征程啟航
為企業(yè)提供網(wǎng)站建設(shè)、域名注冊、服務(wù)器等服務(wù)
為企業(yè)提供網(wǎng)站建設(shè)、域名注冊、服務(wù)器等服務(wù)
目錄
10年積累的成都網(wǎng)站建設(shè)、做網(wǎng)站經(jīng)驗,可以快速應(yīng)對客戶對網(wǎng)站的新想法和需求。提供各種問題對應(yīng)的解決方案。讓選擇我們的客戶得到更好、更有力的網(wǎng)絡(luò)服務(wù)。我雖然不認(rèn)識你,你也不認(rèn)識我。但先網(wǎng)站設(shè)計后付款的網(wǎng)站建設(shè)流程,更有東山免費網(wǎng)站建設(shè)讓你可以放心的選擇與我們合作。
零基礎(chǔ) OpenGL ES 學(xué)習(xí)路線推薦 : OpenGL ES 學(xué)習(xí)目錄 >> OpenGL ES 基礎(chǔ)
零基礎(chǔ) OpenGL ES 學(xué)習(xí)路線推薦 : OpenGL ES 學(xué)習(xí)目錄 >> OpenGL ES 特效
零基礎(chǔ) OpenGL ES 學(xué)習(xí)路線推薦 : OpenGL ES 學(xué)習(xí)目錄 >> OpenGL ES 轉(zhuǎn)場
零基礎(chǔ) OpenGL ES 學(xué)習(xí)路線推薦 : OpenGL ES 學(xué)習(xí)目錄 >> OpenGL ES 函數(shù)
零基礎(chǔ) OpenGL ES 學(xué)習(xí)路線推薦 : OpenGL ES 學(xué)習(xí)目錄 >> OpenGL ES GPUImage 使用
零基礎(chǔ) OpenGL ES 學(xué)習(xí)路線推薦 : OpenGL ES 學(xué)習(xí)目錄 >> OpenGL ES GLSL 編程
在《OpenGL ES 名詞解釋一》中已經(jīng)講解了著色器渲染等相關(guān)知識,本篇文章著重講解坐標(biāo)系和矩陣相關(guān)內(nèi)容;
屏幕坐標(biāo)系 的 左下點(0, 1),右下角(1,1) , 左上角(0, 0) , 右上角(1 , 0)
紋理坐標(biāo)系 的 左下點 (0, 0),右下角(1 , 0) , 左上角(0, 1 ), 右上角(1, 1)
頂點坐標(biāo)系 的 左下點(-1, -1),右下角(1,-1) , 左上角(-1, 1) , 右上角(1 , 1)
屏幕坐標(biāo)系 的 左下點(0, 1),右下角(1,1) , 左上角(0, 0) , 右上角(1 , 0)
很多人有一個誤解:認(rèn)為 OpenGL ES 紋理原點在左上角,因為如果繪制時紋理坐標(biāo)設(shè)在左下角,繪制的圖像就是上下倒立;而紋理坐標(biāo)設(shè)制在左上角顯示正常;
原因:圖像默認(rèn)的原點在左上角,而 OpenGL ES 紋理讀取數(shù)據(jù)或者 FBO 讀取數(shù)據(jù)時都是以左下角開始,所以圖像才會出現(xiàn)上下倒立的現(xiàn)象;
解決辦法:
關(guān)于方案三:將圖片上下顛倒可以使用 stb_image 完成
stbi_set_flip_vertically_on_load(true);//開起上下鏡像
假設(shè)一種不透明東西的顏色是 A,另一種透明的東西的顏色是 B ,那么透過 B 去看 A ,看上去的顏色 C 就是 B 和 A 的混合顏色,可以用這個式子來近似,設(shè) B 物體的透明度為 alpha (取值為 0 – 1 ,0 為完全透明,1 為完全不透明)
R(C)=alpha*R(B)+(1-alpha)*R(A)
G(C)=alpha*G(B)+(1-alpha)*G(A)
B(C)=alpha*B(B)+(1-alpha)*B(A)
R(x)、G(x)、B(x)分別指顏色 x 的 RGB 分量。看起來這個東西這么簡單,可是用它實現(xiàn)的效果絕對不簡單,應(yīng)用 alpha 混合技術(shù),可以實現(xiàn)出最眩目的火光、煙霧、陰影、動態(tài)光源等等一切你可以想象的出來的半透明效果。
為向量(x,y,z)定義一個平移矩陣
旋轉(zhuǎn)過程涉及到弧度與角度的轉(zhuǎn)化:
弧度轉(zhuǎn)角度
:角度 = 弧度 * (180.0f / PI)
角度轉(zhuǎn)弧度
:弧度 = 角度 * (PI / 180.0f)
為向量(x,y,z)定義一個縮放矩陣
矩陣組合順序 1:先平移,再旋轉(zhuǎn),最后縮放——— OK
矩陣組合順序 2:先平移,再縮放,最后旋轉(zhuǎn)——— ERROR
矩陣組合順序 3:先縮放,再旋轉(zhuǎn),最后平移——— ERROR
(除了第一種,其他組合順序都是錯誤的)
矩陣組合順序可以參考 glm 官方 demo 案例:
#include // glm::vec3
#include // glm::vec4
#include // glm::mat4
#include // glm::translate, glm::rotate, glm::scale
#include // glm::perspective
#include // glm::pi
glm::mat4 camera(float Translate, glm::vec2 const& Rotate)
{
glm::mat4 Projection = glm::perspective(glm::pi() * 0.25f, 4.0f / 3.0f, 0.1f, 100.f);
glm::mat4 View = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, -Translate));
View = glm::rotate(View, Rotate.y, glm::vec3(-1.0f, 0.0f, 0.0f));
View = glm::rotate(View, Rotate.x, glm::vec3(0.0f, 1.0f, 0.0f));
glm::mat4 Model = glm::scale(glm::mat4(1.0f), glm::vec3(0.5f));
return Projection * View * Model;
}
至于矩陣組合順序為什么是先平移,再旋轉(zhuǎn),最后縮放,后面將專門留一篇文章做詳細(xì)講解!可以關(guān)注學(xué)習(xí)目錄《OpenGL ES 基礎(chǔ)》
由觀察空間到裁剪空間在公式上左乘一個投影矩陣,投影矩陣的產(chǎn)生分為兩種:正交投影和透視投影;
不管是正交投影還是透視投影,最終都是將視景體內(nèi)的物體投影在近平面上,這也是 3D 坐標(biāo)轉(zhuǎn)換到 2D 坐標(biāo)的關(guān)鍵一步。
正投影就是沒有 3D 效果的投影方式,用于顯示 2D 效果;
透視投影就是有 3D 效果的投影方式,用于顯示 3D 效果.
正交投影產(chǎn)生的效果無論你離物體多遠(yuǎn)多近,都不會產(chǎn)生近大遠(yuǎn)小的效果,大致如下圖,視點作為觀察點,視椎體由前后左右上下 6 個面包裹而成,物體在視椎體內(nèi)部,最后投影到 near 近平面,視椎體范圍之外將無法顯示到屏幕上來
正投影就是沒有 3D 效果的投影方式,用于顯示 2D 效果;
透視投影就是有 3D 效果的投影方式,用于顯示 3D 效果.
正交投影矩陣,由 Matrix.ortho 這個方法產(chǎn)生
void orthoM(float[] m, int mOffset,
float left, float right, float bottom, float top,
float near, float far)
可以把近平面看作屏幕,left、right、top、bottom 都是以近平面中心相對的距離,由于手機(jī)屏幕的長寬一般不相等,以短邊為基準(zhǔn) 1 ,長邊取值為長/寬,所以如果一個豎屏的手機(jī)使用這個正交投影產(chǎn)生的矩陣應(yīng)該是:
float ratio = (float)height / width;
Matrix.ortho(projectMatrix,0,-1, 1, -ratio, ratio, 1, 6);
透視投影會產(chǎn)生近大遠(yuǎn)小的效果,正投影就是沒有 3D 效果的投影方式,用于顯示 2D 效果;透視投影就是有 3D 效果的投影方式,用于顯示 3D 效果.產(chǎn)生的視椎體如下圖:
透視投影也有響應(yīng)的函數(shù)產(chǎn)生投影矩陣:
Matrix.frustumM(float[] m, int offset, float left,
float right, float bottom, float top,
float near, float far);
經(jīng)過上述的講解,我們要完成 4 個空間轉(zhuǎn)換,需要用到了 3 個轉(zhuǎn)換矩陣:
從局部空間轉(zhuǎn)換到世界空間,我們需要用到模型矩陣 ModeMatrix ,這個矩陣就是我們通常對物體進(jìn)行 translate 、rorate 換后產(chǎn)生的矩陣
從世界空間到觀察空間,我們需要用到觀察矩陣 ViewMatrix ,這個矩陣可以 setLookAt 方法幫我們生成
從觀察空間到裁剪空間,我們可以用到投影矩陣 ProjectMatrix,使用 ortho 、frustuM 還有 perspectiveM 方法產(chǎn)生投影矩陣
最后以上幾個坐標(biāo)依次左乘我們的定義的坐標(biāo) Position 就可以得到歸一化坐標(biāo)了
所以,總結(jié)出來的公式
//注意順序
gl_Position = ProjectMatrix * ViewMatrix * ModeMatrix * g_Position ;
緩沖區(qū)就是顯存,也被叫做幀緩存,它的作用是用來存儲顯卡芯片處理過或者即將提取的渲染數(shù)據(jù)。如同計算機(jī)的內(nèi)存一樣,顯存是用來存儲要處理的圖形信息的部件。
最終”存活”下來的像素需要被顯示到屏幕上,但是顯示屏幕之前,這些像素是會被先提交在幀緩沖區(qū)的。幀緩存區(qū)的每一存儲單元對應(yīng)屏幕上的一個像素,整個幀緩存區(qū)對應(yīng)一幀圖像。
在下一個刷新頻率到來時,視頻控制器會把幀緩沖區(qū)內(nèi)的內(nèi)容映射到屏幕上。一般采用雙緩沖機(jī)制,存在兩個幀緩沖區(qū)。
VAO (頂點數(shù)組對象:Vertex Array Object)是指頂點數(shù)組對象,主要用于管理 VBO 或 EBO ,減少 glBindBuffer 、glEnableVertexAttribArray、 glVertexAttribPointer 這些調(diào)用操作,高效地實現(xiàn)在頂點數(shù)組配置之間切換。
OpenGL 2.0 有 VBO,沒有 VAO,VAO 是 OpenGL 3.0 才開始支持的,并且在 OpenGL 3.0 中,強(qiáng)制要求綁定一個 VAO 才能開始繪制。
VBO(頂點緩沖區(qū)對象:Vertex Buffer Object)是指把頂點數(shù)據(jù)保存在顯存中,繪制時直接從顯存中取數(shù)據(jù),減少了數(shù)據(jù)傳輸?shù)拈_銷,因為頂點數(shù)據(jù)多了,就是坐標(biāo)的數(shù)據(jù)多了很多的很多組,切換的時候很麻煩,就出現(xiàn)了這個 VAO,綁定對應(yīng)的頂點數(shù)據(jù)
OpenGL 2.0 有 VBO,沒有 VAO,VAO 是 OpenGL 3.0 才開始支持的,并且在 OpenGL 3.0 中,強(qiáng)制要求綁定一個 VAO 才能開始繪制。
**PBO (Pixel Buffer Object)是 OpenGL ES 3.0 的概念(OpenGL 2.0 不支持 PBO ,3.0 支持 PBO),稱為像素緩沖區(qū)對象,**主要被用于異步像素傳輸操作。PBO 僅用于執(zhí)行像素傳輸,不連接到紋理,且與 FBO (幀緩沖區(qū)對象)無關(guān)。PBO 設(shè)計的目的就是快速地向顯卡傳輸數(shù)據(jù),或者從顯卡讀取數(shù)據(jù),我們可以使用它更加高效的讀取屏幕數(shù)據(jù)。
FBO(Frame Buffer Object) 即幀緩沖對象。FBO 有什么作用呢?通常使用 OpenGL ES 經(jīng)過頂點著色器、片元著色器處理之后就通過使用 OpenGL ES 使用的窗口系統(tǒng)提供的幀緩沖區(qū),這樣繪制的結(jié)果是顯示到窗口(屏幕)上。
但是對于有些復(fù)雜的渲染處理,通過多個濾鏡處理,這時中間流程的渲染采樣的結(jié)果就不應(yīng)該直接輸出顯示屏幕,而應(yīng)該等所有處理完成之后再顯示到窗口上。這個時候 FBO 就派上用場了。
FBO 是一個容器,自身不能用于渲染,需要與一些可渲染的緩沖區(qū)綁定在一起,像紋理或者渲染緩沖區(qū)。,它僅且提供了 3 個附著(Attachment),分別是顏色附著、深度附著和模板附著。
**UBO,Uniform Buffer Object 顧名思義,就是一個裝載 uniform 變量數(shù)據(jù)的緩沖區(qū)對象,**本質(zhì)上跟 OpenGL ES 的其他緩沖區(qū)對象沒有區(qū)別,創(chuàng)建方式也大致一致,都是顯存上一塊用于儲存特定數(shù)據(jù)的區(qū)域。
當(dāng)數(shù)據(jù)加載到 UBO ,那么這些數(shù)據(jù)將存儲在 UBO 上,而不再交給著色器程序,所以它們不會占用著色器程序自身的 uniform 存儲空間,UBO 是一種新的從內(nèi)存到顯存的數(shù)據(jù)傳遞方式,另外 UBO 一般需要與 uniform 塊配合使用。
本例將 MVP 變換矩陣設(shè)置為一個 uniform 塊,即我們后面創(chuàng)建的 UBO 中將保存 3 個矩陣。
#version 310 es
layout(location = 0) in vec4 a_position;
layout(location = 1) in vec2 a_texCoord;
layout (std140) uniform MVPMatrix
{
mat4 projection;
mat4 view;
mat4 model;
};
out vec2 v_texCoord;
void main()
{
gl_Position = projection * view * model * a_position;
v_texCoord = a_texCoord;
}
設(shè)置 uniform 塊的綁定點為 0 ,生成一個 UBO 。
GLuint uniformBlockIndex = glGetUniformBlockIndex(m_ProgramObj, "MVPMatrix");
glUniformBlockBinding(m_ProgramObj, uniformBlockIndex, 0);
glGenBuffers(1, &m_UboId);
glBindBuffer(GL_UNIFORM_BUFFER, m_UboId);
glBufferData(GL_UNIFORM_BUFFER, 3 * sizeof(glm::mat4), nullptr, GL_STATIC_DRAW);
繪制的時候更新 Uniform Buffer 的數(shù)據(jù),更新三個矩陣的數(shù)據(jù),注意偏移量。
glBindBuffer(GL_UNIFORM_BUFFER, m_UboId);
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(glm::mat4), &m_ProjectionMatrix[0][0]);
glBufferSubData(GL_UNIFORM_BUFFER, sizeof(glm::mat4), sizeof(glm::mat4), &m_ViewMatrix[0][0]);
glBufferSubData(GL_UNIFORM_BUFFER, 2 *sizeof(glm::mat4), sizeof(glm::mat4), &m_ModelMatrix[0][0]);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
紋理緩沖區(qū)對象,即 TBO(Texture Buffer Object),是 OpenGL ES 3.2 引入的概念,因此在使用時首先要檢查 OpenGL ES 的版本,Android 方面需要保證 API >= 24 。
TBO 需要配合緩沖區(qū)紋理(Buffer Texture)一起使用,Buffer Texture 是一種一維紋理,其存儲數(shù)據(jù)來自紋理緩沖區(qū)對象(TBO),用于允許著色器訪問由緩沖區(qū)對象管理的大型內(nèi)存表。
在 GLSL 中,只能使用 texelFetch 函數(shù)訪問緩沖區(qū)紋理,緩沖區(qū)紋理的采樣器類型為 samplerBuffer 。
生成一個 TBO 的方式跟 VBO 類似,只需要綁定到 GL_TEXTURE_BUFFER ,而生成緩沖區(qū)紋理的方式與普通的 2D 紋理一樣。
//生成一個 Buffer Texture
glGenTextures(1, &m_TboTexId);
float *bigData = new float[BIG_DATA_SIZE];
for (int i = 0; i < BIG_DATA_SIZE; ++i) {
bigData[i] = i * 1.0f;
}
//生成一個 TBO ,并將一個大的數(shù)組上傳至 TBO
glGenBuffers(1, &m_TboId);
glBindBuffer(GL_TEXTURE_BUFFER, m_TboId);
glBufferData(GL_TEXTURE_BUFFER, sizeof(float) * BIG_DATA_SIZE, bigData, GL_STATIC_DRAW);
delete [] bigData;
使用紋理緩沖區(qū)的片段著色器,需要引入擴(kuò)展 texture buffer ,注意版本聲明為 #version 320 es
#version 320 es
#extension GL_EXT_texture_buffer : require
in mediump vec2 v_texCoord;
layout(location = 0) out mediump vec4 outColor;
uniform mediump samplerBuffer u_buffer_tex;
uniform mediump sampler2D u_2d_texture;
uniform mediump int u_BufferSize;
void main()
{
mediump int index = int((v_texCoord.x +v_texCoord.y) /2.0 * float(u_BufferSize - 1));
mediump float value = texelFetch(u_buffer_tex, index).x;
mediump vec4 lightColor = vec4(vec3(vec2(value / float(u_BufferSize - 1)), 0.0), 1.0);
outColor = texture(u_2d_texture, v_texCoord) * lightColor;
}
繪制時如何使用緩沖區(qū)紋理和 TBO
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_BUFFER, m_TboTexId);
glTexBuffer(GL_TEXTURE_BUFFER, GL_R32F, m_TboId);
GLUtils::setInt(m_ProgramObj, "u_buffer_tex", 0);
本文由博客 - 猿說編程 猿說編程 發(fā)布!