OpenGl ES 2.0 Learn For Android(一)世界是三角形的
為什么說世界是三角形的呢剑按?常規(guī)我們理解的世界——三維世界,由點(diǎn)線面構(gòu)成。這是一個(gè)比較常規(guī)的認(rèn)知。那么纵朋,理解opengl的世界的時(shí)候,也當(dāng)然由這個(gè)邏輯去想象茄袖。但在實(shí)際繪制的時(shí)候操软,無限這種概念沒法實(shí)際的在計(jì)算機(jī)里進(jìn)行描述。所以opengl的世界宪祥,事實(shí)上由點(diǎn)聂薪、線段、和三角形來組成品山。為什么不是四邊形呢胆建?我想,可能是創(chuàng)始者的強(qiáng)迫癥肘交。希望設(shè)計(jì)是123的邏輯笆载,不是124這種跳過3的邏輯。當(dāng)然涯呻,我只是亂猜凉驻。
前一篇的內(nèi)容在這里。
OpenGl ES 2.0 Learn For Android
1. 三角形的基礎(chǔ)——頂點(diǎn)
即使是世界是三角形構(gòu)成的复罐。但三角形終究也是一個(gè)一個(gè)的點(diǎn)涝登,所以,opengl也是從點(diǎn)開始效诅。
講到點(diǎn)胀滚,自然要講到坐標(biāo)系趟济。opengl使用的是右手三維坐標(biāo)系。
也就是說咽笼,我們拿著手機(jī)顷编,正對(duì)著它。想象里面的繪制坐標(biāo)的話剑刑,向右是X軸正方向媳纬,向上是Y軸正方向,向你面部的位置是Z軸正方向施掏。而且钮惠,這個(gè)世界只能顯示[-1,1]的內(nèi)容(這里的話不一定對(duì),有可能之后來修正)七芭。那么我們現(xiàn)在先只看一個(gè)面素挽,所以可以暫時(shí)忽略Z軸,理解成原點(diǎn)在手機(jī)屏幕正中央狸驳,向右是X軸正方向毁菱,向上是Y軸正方向。
OpenGL作為本地系統(tǒng)庫(kù)是直接運(yùn)行在硬件上的锌历,在Android里,而我們的代碼是運(yùn)行在Dalvik(現(xiàn)在是ART)上峦筒,導(dǎo)致OpenGL無法去讀取我們的數(shù)據(jù)究西,所以有兩種方案解決上述問題:
從Java調(diào)用本地代碼(JNI)。
一般我們調(diào)用GLES20包物喷,就是在后臺(tái)使用JNI卤材。-
把內(nèi)存從Java堆復(fù)制到本地堆。意思就是改變內(nèi)存分配的方式峦失,Java有個(gè)特殊的類集合扇丛,把Java數(shù)據(jù)復(fù)制到本地內(nèi)存中。
內(nèi)存從Java堆復(fù)制到本地堆
FloatBuffer使用方式可以參照下面博客:
java中的float緩沖區(qū)FloatBuffer
對(duì)應(yīng)代碼使用:
private final FloatBuffer mVertexData;
private float[] mTrianglePoints = {-0.5f, -0.5f, 0.5f, -0.5f, 0f, 0.5f};
mVertexData = ByteBuffer
.allocateDirect(mTrianglePoints.length * BYTES_PER_FLOAT)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
mVertexData.put(mTrianglePoints);
現(xiàn)在的話尉辑,我們知道有了點(diǎn)帆精,但是怎么告訴opengl呢?
2. OpenGL:頂點(diǎn)著色器和片段著色器###
一般我們定義好物體的頂點(diǎn)隧魄,被讀取到本地內(nèi)存中卓练,在繪制到屏幕的時(shí)候,需要通過管道進(jìn)行傳輸购啄,這類管道其實(shí)也成為著色器襟企。
著色器:會(huì)告訴GPU如何處理繪制數(shù)據(jù)
在OpenGL中,總共有兩種類型的著色器:
(1)頂點(diǎn)著色器(Vertex Shader)生成每個(gè)頂點(diǎn)的最終位置狮含,針對(duì)每個(gè)頂點(diǎn)顽悼,它都會(huì)執(zhí)行一次曼振;一旦最終位置確定了,OpenGL就可以把這些可見頂點(diǎn)的集合組裝成點(diǎn)蔚龙、直線冰评、以及三角形
(2)片段著色器(Fragment Shader)為組成的點(diǎn)、直線府蛇、三角形的每個(gè)片段生成最終的顏色集索,針對(duì)每個(gè)片段,都會(huì)執(zhí)行一次;一個(gè)片段是一個(gè)小的汇跨、單一顏色的長(zhǎng)方形區(qū)域务荆,類似于計(jì)算機(jī)屏幕上的一個(gè)像素。
一旦最后顏色生成后穷遂,OpenGl就會(huì)把它們寫到一塊稱為幀緩沖區(qū)(frame buffer)的內(nèi)存塊中函匕,然后Android會(huì)把這塊幀緩沖區(qū)顯示到屏幕上。
在代碼里蚪黑,我們可以完成一個(gè)最簡(jiǎn)單的頂點(diǎn)著色器:
private String mVertexShaderCode =
"attribute vec4 a_Position; \n" +
"void main() \n" +
"{ \n" +
" gl_Position = a_Position; \n" +
"} \n";
同樣的盅惜,完成一個(gè)最簡(jiǎn)單的片段著色器:
private String mFragmentShaderCode =
"precision mediump float; \n" +
"uniform vec4 u_Color; \n" +
"void main() \n" +
"{ \n" +
" gl_FragColor = u_Color; \n" +
"}";
關(guān)于attribute
和uniform
,可以參看這篇博客:OpenGL ES 三種類型修飾 uniform attribute varying
顏色是四維向量忌穿,很容易理解(R,G,B,A)抒寂。頂點(diǎn)我們之前說過是三維的,它這里是(X,Y,Z,W)掠剑。W是什么我們之后講屈芜。
3. OpenGL ES的編譯連接過程###
這里要講到,GLES20的接口朴译,都是先生成一個(gè)實(shí)例井佑,拿到一個(gè)句柄,再對(duì)這個(gè)句柄指向的實(shí)例進(jìn)行操作眠寿。熟悉C語(yǔ)言的應(yīng)該會(huì)比較眼熟這個(gè)流程躬翁。java入行的會(huì)感到一點(diǎn)別扭。
1.shader的連接過程:
glCreateShader->glShaderSource->glCompileShader
2.program的生成過程:
glCreateProgram->glAttachShader->glLinkProgram
program是將一個(gè)頂點(diǎn)著色器和一個(gè)片段著色器鏈接在一起生成一個(gè)對(duì)象盯拱。沒有片段著色器盒发,OpenGL就不知道怎么繪制那些組成每個(gè)點(diǎn)、直線和三角形的片段狡逢;沒有頂點(diǎn)著色器迹辐,OpenGL就不知道在哪里繪制這些片段。
//create vertex shader
int vertexShader = compileShader(GL_VERTEX_SHADER, mVertexShaderCode);
//create fragment shader
int fragmentShader = compileShader(GL_FRAGMENT_SHADER, mFragmentShaderCode);
//create program and link two shader
mShaderProgram = linkProgram(vertexShader,fragmentShader);
//use this program
glUseProgram(mShaderProgram);
4. OpenGL ES的賦值繪制過程###
program拿到后甚侣,就是對(duì)變量進(jìn)行賦值繪制了明吩。
uColorLocation = glGetUniformLocation(mShaderProgram, "u_Color");
aPositionLocation = glGetAttribLocation(mShaderProgram, "a_Position");
// Bind our data, specified by the variable vertexData, to the vertex
// attribute at location A_POSITION_LOCATION.
mVertexData.position(0);
glVertexAttribPointer(aPositionLocation, 2, GL_FLOAT,
false, 0,mVertexData);
glEnableVertexAttribArray(aPositionLocation);
glUniform4f(uColorLocation, 1.0f, 1.0f, 1.0f, 1.0f);
上面拿到了"u_Color"
和"a_Position"
對(duì)應(yīng)的變量,并對(duì)"a_Position"
也就是頂點(diǎn)變量進(jìn)行了賦值了一個(gè)頂點(diǎn)數(shù)組殷费,對(duì)"u_Color"
賦值為白色印荔。
在繪制的時(shí)候
@Override
public void onDrawFrame(GL10 glUnused) {
glClear(GL_COLOR_BUFFER_BIT);
glDrawArrays(GL_TRIANGLES, 0, 3);
}
這里是從頂點(diǎn)數(shù)組下標(biāo)0開始低葫,繪制3個(gè)頂點(diǎn)。glDrawArrays
具體我就不介紹了仍律。這里的參數(shù)有些不同嘿悬,可以參看
https://www.khronos.org/registry/OpenGL-Refpages/es2.0/
下面是效果圖。
demo地址:
https://github.com/YueZhiFengMing/LearnOpenGl/tree/master/SecondDrawTriangle
參考資料###
- java中的float緩沖區(qū)FloatBuffer
- OpenGL ES 三種類型修飾 uniform attribute varying
- https://www.khronos.org/registry/OpenGL-Refpages/es2.0/
- 《OpenGL ES應(yīng)用開發(fā)實(shí)踐指南:Android卷》