前言:
前面介紹了使用 Android 編寫 OpenGL ES 應(yīng)用的程序框架,并成功繪制了第一個(gè)OpenGL ES 2.0的小程序,本篇介紹 3D 繪圖的一些基本構(gòu)成要素,基本概念,重要的函數(shù),最終將實(shí)現(xiàn)一個(gè)頂點(diǎn)的繪制。
本文是建立在上一篇文章之上,只修改MyRenderer類,其他部分保持不變,如果你沒有看上一篇文章,請先移步Android OpenGl ES2.0編程_第一個(gè)OpenGL小程序
▲ 基本概念
Vertex (頂點(diǎn))
頂點(diǎn)是3D建模時(shí)用到的最小構(gòu)成元素,頂點(diǎn)定義為兩條或是多條邊交會的地方。在 3D 模型中一個(gè)頂點(diǎn)可以為多條邊,面或是多邊形所共享。一個(gè)頂點(diǎn)也可以代表一個(gè)點(diǎn)光源或是 Camera 的位置。下圖中標(biāo)識為黃色的點(diǎn)為一個(gè)頂點(diǎn)(Vertex)。

三維坐標(biāo)系
三維笛卡兒坐標(biāo)系是在二維笛卡兒坐標(biāo)系的基礎(chǔ)上根據(jù)右手定則增加第三維坐標(biāo)(即Z軸)而形成的。同二維坐標(biāo)系一樣,AutoCAD中的三維坐標(biāo)系有世界坐標(biāo)系WCS(World Coordinate System)和用戶坐標(biāo)系UCS(User Coordinate System)兩種形式。---百度百科
學(xué)習(xí)OpenGL編程,少不了需要了解坐標(biāo)系的概念,這里我們不用理解的那么深,簡單的知道和了解最基本的就好,推薦閱讀三維坐標(biāo)系介紹
在3D繪圖中,坐標(biāo)軸遵循右手法則。三維坐標(biāo)系中,Z軸的正軸方向是根據(jù)右手定則確定的。右手定則也決定三維空間中任一坐標(biāo)軸的正旋轉(zhuǎn)方向。
要標(biāo)注X、Y和Z軸的正軸方向,就將右手背對著屏幕放置,拇指即指向X軸的正方向。伸出食指和中指,如右圖所示,食指指向Y軸的正方向,中指所指示的方向即是Z軸的正方向。
要確定軸的正旋轉(zhuǎn)方向,如右圖所示,用右手的大拇指指向軸的正方向,彎曲手指。那么手指所指示的方向即是軸的正旋轉(zhuǎn)方向。

▲ 點(diǎn)的構(gòu)成
有了之前對坐標(biāo)系的理解,我們知道,在3D繪制中,一個(gè)點(diǎn)包含x、y、z坐標(biāo)。在Android系統(tǒng)中,可以用一個(gè)float數(shù)組、int數(shù)組等來表示一個(gè)點(diǎn)。如下:
private float[] mArrayVertex = { 0f, 0f, 0f };
為了提高性能,通常將這些數(shù)組存放到 java.io 中定義的 Buffer 類中
新建一個(gè)工具類ToolUtils,把獲取Buffer數(shù)據(jù)的代碼封裝如下
public class ToolUtils {
/**
* @param vertexes float 數(shù)組
* @return 獲取浮點(diǎn)形緩沖數(shù)據(jù)
*/
public static FloatBuffer getFloatBuffer(float[] vertexes) {
FloatBuffer buffer;
ByteBuffer vbb = ByteBuffer.allocateDirect(vertexes.length * 4);
vbb.order(ByteOrder.nativeOrder());
buffer = vbb.asFloatBuffer();
//寫入數(shù)組
buffer.put(vertexes);
//設(shè)置默認(rèn)的讀取位置
buffer.position(0);
return buffer;
}
}
▲ 繪制一個(gè)點(diǎn)
這里只需修改MyRenderer類的代碼,其余代碼和之前搭建的基礎(chǔ)框架一樣。MyRenderer.java
public class MyRenderer implements GLSurfaceView.Renderer {
//頂點(diǎn)數(shù)組
private float[] mArrayVertex = { 0f, 0f, 0f };
// 緩沖區(qū)
private FloatBuffer mBuffer;
MyRenderer() {
//獲取浮點(diǎn)形緩沖數(shù)據(jù)
mBuffer = ToolUtils.getFloatBuffer(mArrayVertex);
}
// Surface創(chuàng)建的時(shí)候調(diào)用
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
// 設(shè)置清屏顏色為黑色(rgba)
gl.glClearColor(0f, 0f, 0f, 0f);
}
// Surface改變的的時(shí)候調(diào)用
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
// 設(shè)置OpenGL場景的大小
gl.glViewport(width / 4, width / 2, width / 2, height / 2);
}
// 在Surface上繪制的時(shí)候調(diào)用
@Override
public void onDrawFrame(GL10 gl) {
// 清除屏幕
gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
// 允許設(shè)置頂點(diǎn) // GL10.GL_VERTEX_ARRAY頂點(diǎn)數(shù)組
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
// 設(shè)置頂點(diǎn)
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mBuffer);
//設(shè)置點(diǎn)的顏色為紅色
gl.glColor4f(1f, 0f, 0f, 0f);
//設(shè)置點(diǎn)的大小
gl.glPointSize(100f);
// 繪制點(diǎn)
gl.glDrawArrays(GL10.GL_POINTS, 0, 1);
// 禁止頂點(diǎn)設(shè)置
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
}
}
▲ 啟用相關(guān)功能及配置說明
這里講解部分比較重要的函數(shù),之后還有一些常用API在之后的文章會重點(diǎn)講到
0. glClearColor()
設(shè)置清屏顏色,每次清屏?xí)r,使用該顏色填充整個(gè)屏幕。里面參數(shù)分別代表RGBA,取值范圍為[0,1]而不是[0,255]。例如:
gl.glClearColor(0f, 0f, 0f, 0f);
1. glVertexPointer()
設(shè)置一個(gè)指針,這個(gè)指針指向頂點(diǎn)數(shù)組,后面繪制三角形(或矩形)根據(jù)這里指定的頂點(diǎn)數(shù)組來讀取數(shù)據(jù)
函數(shù)原型:
void glVertexPointer(int size,int type,int stride,Buffer pointer)
參數(shù)解釋
size —— 每個(gè)頂點(diǎn)的坐標(biāo)維數(shù),必須是2, 3 或者4,初始值是4。
type —— 指明每個(gè)頂點(diǎn)坐標(biāo)的數(shù)據(jù)類型,允許的符號常量GL_BYTE, GL_SHORT,GL_FIXED 和GL_FLOAT,初始值為GL_FLOAT。
stride —— 指明連續(xù)頂點(diǎn)間的位偏移,如果為0,頂點(diǎn)被認(rèn)為是緊密壓入矩陣,初始值為0。
pointer —— 指明頂點(diǎn)坐標(biāo)的緩沖區(qū),如果為null,則沒有設(shè)置緩沖區(qū)。
2. glClear()
用來清理緩沖區(qū),并設(shè)置為預(yù)設(shè)值
函數(shù)原型:
glClear(int mask)
mask的值有以下三種
GL_COLOR_BUFFER_BIT —— 表明顏色緩沖區(qū)
GL_DEPTH_BUFFER_BIT —— 表明深度緩沖
GL_STENCIL_BUFFER_BIT —— 表明模型緩沖區(qū)
3. glEnableClientState()
啟用客戶端的某項(xiàng)功能。glEnableClientState和glDisableClientState啟用或禁用客戶端的單個(gè)功能。默認(rèn)的,所有客戶端功能禁用。 array可以是下列符號常量:
GL_COLOR_ARRAY —— 如果啟用,顏色矩陣可以用來寫入以及調(diào)用glDrawArrays方法或者glDrawElements方法時(shí)進(jìn)行渲染。詳見glColorPointer。
GL_NORMAL_ARRAY —— 如果啟用,法線矩陣可以用來寫入以及調(diào)用glDrawArrays方法或者glDrawElements方法時(shí)進(jìn)行渲染。詳見glNormalPointer。
GL_TEXTURE_COORD_ARRAY —— 如果啟用,紋理坐標(biāo)矩陣可以用來寫入以及調(diào)用glDrawArrays方法或者glDrawElements方法時(shí)進(jìn)行渲染。詳見glTexCoordPointer。
GL_VERTEX_ARRAY —— 如果啟用,頂點(diǎn)矩陣可以用來寫入以及調(diào)用glDrawArrays方法或者glDrawElements方法時(shí)進(jìn)行渲染。詳見glVertexPointer。
GL_POINT_SIZE_ARRAY_OES(OES_point_size_arrayextension)——如果啟用,點(diǎn)大小矩陣控制大小以渲染點(diǎn)和點(diǎn)sprites。這時(shí)由glPointSize定義的點(diǎn)大小將被忽略,由點(diǎn)大小矩陣 提供的大小將被用來渲染點(diǎn)和點(diǎn)sprites。詳見glPointSize。
4. glDrawArrays()
繪制數(shù)組里面所有點(diǎn)構(gòu)成的各個(gè)三角片
函數(shù)原型:
void glDrawArrays(
int mode,
int first,
int count
);
參數(shù)解釋
mode:有三種取值
- GL_TRIANGLES:每三個(gè)頂之間繪制三角形,之間不連接
- GL_TRIANGLE_FAN:以V0 V1 V2,V0 V2 V3,V0 V3 V4,……的形式繪制三角形
- GL_TRIANGLE_STRIP:順序在每三個(gè)頂點(diǎn)之間均繪制三角形。這個(gè)方法可以保證從相同的方向上所有三角形均被繪制。以V0 V1 V2 ,V1 V2 V3,V2 V3 V4,……的形式繪制三角形
first:從數(shù)組緩存中的哪一位開始繪制,一般都定義為0
count:頂點(diǎn)的數(shù)量
▲ 最終效果圖
看著雖然是一個(gè)正方形,但實(shí)際它是一個(gè)大小為100f的正方形的點(diǎn)
