前言
前面提到過,在OpenGL ES 世界里面是沒有正方形和圓形的,只有點(diǎn)、線、三角形。三角形是OpenGL ES提供的最復(fù)雜的土元單位。序偶一我們要繪制填充正方形和圓形就需要利用三角形來充實(shí)。
正方形
正方形的構(gòu)建比較簡單,可以用兩個(gè)三角形組成。當(dāng)然,你也可以用很多三角形去合成一個(gè)正方形,只要你樂意。如下圖所示,我們可以按照123組成的三角形和134組成的三角形,兩個(gè)拼成一個(gè)正方形。

可以設(shè)置正方形的坐標(biāo)數(shù)組為:
private float triangleCoors[] = {
-0.5f, 0.5f, 0.0f, // top left 0
-0.5f, -0.5f, 0.0f, // bottom left 1
0.5f, -0.5f, 0.0f, // bottom right 2
0.5f, 0.5f, 0.0f // top right 3
};
顏色
private float color[] = {
1.0f , 1.0f , 1.0f , 1.0f
};
根據(jù)上圖1繪制三角形坐標(biāo)的順序
private short index[] = {
0,1,2,0,2,3
};
頂點(diǎn)著色器
private final String vertextShaderCode =
"attribute vec4 vPosition;" +
"uniform mat4 vMatrix;" +
"void main() {" +
" gl_Position = vMatrix * vPosition;" +
"}";
vPosition:接收頂點(diǎn)坐標(biāo)
vMatrix:變換矩陣,作用上篇文章已經(jīng)說過
片元著色器
private final String fragmentShaderCode =
"precision mediump float;" +
"uniform vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}";
vColor:接收顏色
onSurfaceCreated 方法
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config)
{
//將背景設(shè)置為灰色
GLES20.glClearColor(0.0f,0.0f,0.0f,1.0f);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
//申請底層空間 頂點(diǎn)坐標(biāo)
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(triangleCoors.length * 4);
byteBuffer.order(ByteOrder.nativeOrder());
//將坐標(biāo)數(shù)據(jù)轉(zhuǎn)換為FloatBuffer,用以傳入給OpenGL ES程序
vertexBuffer = byteBuffer.asFloatBuffer();
//將三角形坐標(biāo)傳入FloatBuffer
vertexBuffer.put(triangleCoors);
vertexBuffer.position(0);
//申請底層空間 坐標(biāo)順序
ByteBuffer indexBuffer = ByteBuffer.allocateDirect(index.length * 2);
indexBuffer.order(ByteOrder.nativeOrder());
indexShortBuffer = indexBuffer.asShortBuffer();
indexShortBuffer.put(index);
indexShortBuffer.position(0);
//創(chuàng)建頂點(diǎn)著色器程序
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertextShaderCode);
//創(chuàng)建片元著色器程序
int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
if (vertexShader == 0 || fragmentShader == 0)
{
return;
}
//創(chuàng)建一個(gè)空的OpenGL ES程序
program = GLES20.glCreateProgram();
//將頂點(diǎn)著色器加入程序
GLES20.glAttachShader(program, vertexShader);
//將片元著色器加入程序
GLES20.glAttachShader(program, fragmentShader);
//連接到著色器程序中
GLES20.glLinkProgram(program);
//使用程序
GLES20.glUseProgram(program);
}
onSurfaceChanged方法
@Override
public void onSurfaceChanged(GL10 gl, int width, int height)
{
GLES20.glViewport(0,0,width,height);
float ratio = (float) width/height;
//設(shè)置透視矩陣
Matrix.frustumM(mProjectMatrix,0,-ratio,ratio,-1,1,3,7);
//設(shè)置相機(jī)位置
Matrix.setLookAtM(mViewMatrix,0,0,0,7.0f,0,0,0,0,1.0f,0);
//計(jì)算變換矩陣
Matrix.multiplyMM(mMVPMatrix,0,mProjectMatrix,0,mViewMatrix,0);
}
onDrawFrame 方法
@Override
public void onDrawFrame(GL10 gl)
{
if(program == 0)
return;
//獲取變換矩陣vMatrix成員句柄
int vMatrix = GLES20.glGetUniformLocation(program,"vMatrix");
//設(shè)置vMatrix的值
GLES20.glUniformMatrix4fv(vMatrix,1,false,mMVPMatrix,0);
//獲取頂點(diǎn)著色器的vPosition成員句柄
int vPosition = GLES20.glGetAttribLocation(program, "vPosition");
//啟用vPosition句柄
GLES20.glEnableVertexAttribArray(vPosition);
//傳的坐標(biāo)數(shù)據(jù)
GLES20.glVertexAttribPointer(vPosition,3,GLES20.GL_FLOAT,false,3*4, vertexBuffer);
//獲取頂點(diǎn)著色器的vColor成員句柄
int aColor = GLES20.glGetUniformLocation(program, "vColor");
//設(shè)置繪制三角形的顏色
GLES20.glUniform4fv(aColor, 1, color, 0);
//繪制三角形
GLES20.glDrawElements(GLES20.GL_TRIANGLES,index.length,GLES20.GL_UNSIGNED_SHORT,indexShortBuffer);
//禁止頂點(diǎn)數(shù)組的句柄
GLES20.glDisableVertexAttribArray(vPosition);
}

圓形
圓形的構(gòu)建,相對復(fù)雜一點(diǎn),我們可以把圓形看成一個(gè)正多邊形,邊越多,圓越平滑。如下圖所示,分別為正六邊形、正八邊形、正十六邊形和正一百邊形。

以六邊形為例,由012、023,034、045、056、061六個(gè)三角形,更多變形同樣如此。
利用簡單的數(shù)學(xué)知識(shí),即可得到,以多邊形中心建立直角坐標(biāo)系,得到n變形的頂點(diǎn)坐標(biāo)為L:
private float[] createPositions()
{
ArrayList<Float> data = new ArrayList<>();
data.add(1.0f); //設(shè)置圓心坐標(biāo)
data.add(1.0f);
data.add(0.0f);
float angDegSpan = 360f / n;
for (float i = 0; i < 360 + angDegSpan; i += angDegSpan)
{
data.add((float) (radius * Math.sin(i * Math.PI / 180f)));
data.add((float) (radius * Math.cos(i * Math.PI / 180f)));
data.add(0.0f);
}
float[] f = new float[data.size()];
for (int i = 0; i < f.length; i++)
{
f[i] = data.get(i);
}
return f;
}
createPositions方法得到頂點(diǎn)坐標(biāo)數(shù)組,剩下的工作就和三角形的繪制基本相同,唯一不同的地方是需要修改:
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 1);
為:
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, shapePos.length/3);
public static native void glDrawArrays(
int mode, //表示繪制方式
int first, //表示偏移量
int count //頂點(diǎn)個(gè)數(shù)
);
繪制方式有:
GL_POINTS //將傳入的頂點(diǎn)坐標(biāo)作為單獨(dú)的點(diǎn)繪制
GL_LINES //將傳入的頂點(diǎn)坐標(biāo)作為單獨(dú)線條繪制,ABCDEF六個(gè)頂點(diǎn),繪制AB、CD、EF三條線
GL_LINE_LOOP // 將傳入的頂點(diǎn)作為閉合折線繪制 ,ABCD四個(gè)頂點(diǎn),繪制AB、BC、CD、DA四條線
GL_LINE_STRIP //將傳入的頂點(diǎn)作為折線繪制,ABCD四個(gè)頂點(diǎn),繪制AB、BC、CD三條線
GL_TRIANGLES //將傳入的頂點(diǎn)作為單獨(dú)的三角形繪制,ABCDEF六個(gè)頂點(diǎn),繪制ABC、DEF兩個(gè)三角形
GL_TRIANGLE_STRIP //將傳入的頂點(diǎn)作為三角條帶繪制,ABCDEF六個(gè)頂點(diǎn),繪制ABC、BCD、CDE、DEF四個(gè)三角形
GL_TRIANGLE_FAN //將傳入的頂作為扇面繪制,ABCDEF六個(gè)頂點(diǎn),繪制ABC、ACD、ADE、AEF四個(gè)三角形
顏色
private float color[] = {
1.0f , 1.0f , 1.0f , 1.0f
};
頂點(diǎn)著色器
private final String vertextShaderCode =
"attribute vec4 vPosition;" +
"uniform mat4 vMatrix;" +
"void main() {" +
" gl_Position = vMatrix * vPosition;" +
"}";
vPosition:接收頂點(diǎn)坐標(biāo)
vMatrix:變換矩陣,作用上篇文章已經(jīng)說過
片元著色器
private final String fragmentShaderCode =
"precision mediump float;" +
"uniform vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}";
vColor:接收顏色
onSurfaceCreated 方法
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config)
{
//將背景設(shè)置為灰色
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
//圓頂點(diǎn)坐標(biāo)
shapePos = createPositions();
//申請底層空間
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(shapePos.length * 4);
byteBuffer.order(ByteOrder.nativeOrder());
//將坐標(biāo)數(shù)據(jù)轉(zhuǎn)換為FloatBuffer,用以傳入給OpenGL ES程序
vertexBuffer = byteBuffer.asFloatBuffer();
//將三角形坐標(biāo)傳入FloatBuffer
vertexBuffer.put(shapePos);
vertexBuffer.position(0);
//申請底層空間
ByteBuffer colorBuffer = ByteBuffer.allocateDirect(color.length * 4);
colorBuffer.order(ByteOrder.nativeOrder());
colorFloatBuffer = colorBuffer.asFloatBuffer();
colorFloatBuffer.put(color);
colorFloatBuffer.position(0);
//創(chuàng)建頂點(diǎn)著色器程序
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertextShaderCode);
//創(chuàng)建片元著色器程序
int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
if (vertexShader == 0 || fragmentShader == 0)
{
return;
}
//創(chuàng)建一個(gè)空的OpenGL ES程序
program = GLES20.glCreateProgram();
//將頂點(diǎn)著色器加入程序
GLES20.glAttachShader(program, vertexShader);
//將片元著色器加入程序
GLES20.glAttachShader(program, fragmentShader);
//連接到著色器程序中
GLES20.glLinkProgram(program);
//使用程序
GLES20.glUseProgram(program);
}
onSurfaceChanged 方法
@Override
public void onSurfaceChanged(GL10 gl, int width, int height)
{
GLES20.glViewport(0, 0, width, height);
float ratio = (float) width / height;
//設(shè)置透視矩陣
Matrix.frustumM(mProjectMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
//設(shè)置相機(jī)位置
Matrix.setLookAtM(mViewMatrix, 0, 0, 0, 7.0f, 0, 0, 0, 0, 1.0f, 0);
//計(jì)算變換矩陣
Matrix.multiplyMM(mMVPMatrix, 0, mProjectMatrix, 0, mViewMatrix, 0);
}
onDrawFrame 方法
@Override
public void onDrawFrame(GL10 gl)
{
if (program == 0)
return;
//獲取變換矩陣vMatrix成員句柄
int vMatrix = GLES20.glGetUniformLocation(program, "vMatrix");
//設(shè)置vMatrix的值
GLES20.glUniformMatrix4fv(vMatrix, 1, false, mMVPMatrix, 0);
//獲取頂點(diǎn)著色器的vPosition成員句柄
int vPosition = GLES20.glGetAttribLocation(program, "vPosition");
//啟用vPosition句柄
GLES20.glEnableVertexAttribArray(vPosition);
//傳的坐標(biāo)數(shù)據(jù)
GLES20.glVertexAttribPointer(vPosition, 3, GLES20.GL_FLOAT, false, 3 * 4, vertexBuffer);
//獲取頂點(diǎn)著色器的vColor成員句柄
int aColor = GLES20.glGetUniformLocation(program, "vColor");
//設(shè)置繪制三角形的顏色
GLES20.glUniform4fv(aColor, 1, color, 0);
//繪制三角形
// GLES20.glDrawArrays(GLES20.GL_TRIANGLES,0,4);
// GLES20.glDrawElements(GLES20.GL_TRIANGLES,index.length,GLES20.GL_UNSIGNED_SHORT,indexShortBuffer);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 1, shapePos.length / 3);
//禁止頂點(diǎn)數(shù)組的句柄
GLES20.glDisableVertexAttribArray(vPosition);
}

繪制總結(jié)
GL_TRIANGLE_STRIP
由上面我們可以知道,GL_TRIANGLE_STRIP的方式繪制連續(xù)的三角形,比直接用GL_TRIANGLES的方式少好幾個(gè)頂點(diǎn),效率會(huì)高很多。另外,GL_TRIANGLE_STRIP并不只能繪制連續(xù)的三角形構(gòu)造的物體,我們只需要將不需要重復(fù)繪制的點(diǎn)重復(fù)兩次即可。比如,傳入ABCDEEFFGH坐標(biāo),就會(huì)得到ABC、BCD、CDE以及FGH四個(gè)三角形。
GL_TRIANGLE_FAN
扇面繪制是以第一個(gè)為零點(diǎn)進(jìn)行繪制,通常我們繪制圓形,圓錐的錐面都會(huì)使用到,值得注意的是,最后一個(gè)點(diǎn)的左邊應(yīng)當(dāng)與第二個(gè)重合,在計(jì)算的時(shí)候,起始角度為0度,重點(diǎn)角度包含360度。
頂點(diǎn)法和索引法
GLES20.glDrawArrays,也就是頂點(diǎn)法,是根據(jù)傳入的頂點(diǎn)順序進(jìn)行繪制的。
GLES20.glDrawElements,稱之為索引法,是根據(jù)索引序列,在頂點(diǎn)序列中找到對應(yīng)的頂點(diǎn),并根據(jù)繪制的方式,組成相應(yīng)的圖元進(jìn)行繪制。
頂點(diǎn)法擁有的繪制方式,所引發(fā)也都有。相對于頂點(diǎn)發(fā)在復(fù)雜圖形的繪制中無法避免大量頂點(diǎn)重復(fù)的情況,所引發(fā)可以相對頂點(diǎn)發(fā)減少很多重復(fù)頂點(diǎn)占用的空間。