之前文章中,我們繪制了三角形、正方形、圓形、立方星,今天我們將回執(zhí)圓錐、圓柱、體。能夠繪制這些基本的常規(guī)幾何形體后,其他常見的幾何形體的繪制對(duì)我們來說基本沒問題了。
圓錐
由之前的文章,我們應(yīng)該知道了,OpenGL ES中物體的繪制重點(diǎn)就是在于把這個(gè)物體表面分解成三角形,分解成功后,繪制自然不成問題了。圓錐我們很容易就想到把它拆解成一個(gè)圓形和一個(gè)錐面,錐面的頂點(diǎn)跟圓形的圓點(diǎn),除了錐面的中心點(diǎn)的坐標(biāo)有了“高度”,其他的完全相同。圓形在之前文章已經(jīng)繪制過,那么錐面其實(shí)對(duì)我們來說也是小事。
錐面頂點(diǎn)坐標(biāo)
其實(shí)錐面頂點(diǎn)坐標(biāo)也就是圓形中,給圓心相對(duì)圓邊增加高度,使之形成錐面。
ArrayList<Float> data = new ArrayList<>();
data.add(0.0f);
data.add(0.0f);
data.add(2.0f); //給圓心相對(duì)圓邊增加高度,使之形成錐面
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);
}
我們按照繪制圓形的方式,繪制出錐面,然后再在這個(gè)錐面的地步繪制一個(gè)圓形,這樣我們就可以得到一個(gè)圓錐了:

從上圖中可以看出,我們繪制的并不是同樣的顏色,如果使用同樣的顏色,很難看出圓錐的立體效果。這種顏色怎么實(shí)現(xiàn)的呢?我們來看它的頂點(diǎn)著色器:
private final String vertextShaderCode =
"attribute vec4 vPosition;" +
"uniform mat4 vMatrix;" +
"varying vec4 vColor;" +
"void main() {" +
" gl_Position = vMatrix * vPosition;" +
" if(vPosition.z!=0.0){"+
" vColor=vec4(0.0,0.0,0.0,1.0);"+
" }else{"+
" vColor=vec4(0.9,0.9,0.9,1.0);"+
" }"+
"}";
在頂點(diǎn)著色器中并沒有傳入顏色,而是在程序中直接判斷進(jìn)行賦值,當(dāng)然頂點(diǎn)顏色和定邊顏色也可以由外面?zhèn)魅搿?/p>
具體代碼:
public class MySixthRender implements GLSurfaceView.Renderer
{
private final String vertextShaderCode =
"attribute vec4 vPosition;" +
"uniform mat4 vMatrix;" +
"varying vec4 vColor;" +
"void main() {" +
" gl_Position = vMatrix * vPosition;" +
" if(vPosition.z!=0.0){"+
" vColor=vec4(0.0,0.0,0.0,1.0);"+
" }else{"+
" vColor=vec4(0.9,0.9,0.9,1.0);"+
" }"+
"}";
private final String fragmentShaderCode =
"precision mediump float;" +
"varying vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}";
private float color[] = {
1.0f, 1.0f, 1.0f, 1.0f
};
private int program;
private FloatBuffer vertexBuffer1;
private FloatBuffer vertexBuffer2;
//投影矩陣
private final float[] mProjectMatrix = new float[16];
//相機(jī)位置矩陣
private final float[] mViewMatrix = new float[16];
//計(jì)算變換矩陣
private final float[] mMVPMatrix = new float[16];
private float radius = 1.0f; //半徑
private int n = 3; //切割份數(shù)
private float[] shapePos;
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config)
{
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
//將背景設(shè)置為灰色
GLES20.glClearColor(0.5f,0.2f,1.0f,1.0f);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT| GLES20.GL_DEPTH_BUFFER_BIT);
//錐面所有坐標(biāo)
shapePos = createPositions();
//申請(qǐng)底層空間
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(shapePos.length * 4);
byteBuffer.order(ByteOrder.nativeOrder());
//將坐標(biāo)數(shù)據(jù)轉(zhuǎn)換為FloatBuffer,用以傳入給OpenGL ES程序
vertexBuffer1 = byteBuffer.asFloatBuffer();
//將三角形坐標(biāo)傳入FloatBuffer
vertexBuffer1.put(shapePos);
vertexBuffer1.position(0);
//圓形所有坐標(biāo)
ByteBuffer byteBuffer2 = ByteBuffer.allocateDirect(shapePos.length * 4);
byteBuffer2.order(ByteOrder.nativeOrder());
//將坐標(biāo)數(shù)據(jù)轉(zhuǎn)換為FloatBuffer,用以傳入給OpenGL ES程序
vertexBuffer2 = byteBuffer2.asFloatBuffer();
//將三角形坐標(biāo)傳入FloatBuffer
shapePos[2] = 0.0f; //改變錐面頂點(diǎn)高度,變?yōu)閳A心坐標(biāo)
vertexBuffer2.put(shapePos);
vertexBuffer2.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);
}
@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, 20);
//設(shè)置相機(jī)位置
Matrix.setLookAtM(mViewMatrix , 0, 1.0f, -10.0f, -4.0f, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
//計(jì)算變換矩陣
Matrix.multiplyMM(mMVPMatrix, 0, mProjectMatrix, 0, mViewMatrix, 0);
}
@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);
//傳的圓錐所有頂點(diǎn)坐標(biāo)數(shù)據(jù)
GLES20.glVertexAttribPointer(vPosition, 3, GLES20.GL_FLOAT, false, 3 * 4, vertexBuffer1);
//繪制
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, shapePos.length / 3);
//傳的圓形所有頂點(diǎn)坐標(biāo)數(shù)據(jù)
GLES20.glVertexAttribPointer(vPosition, 3, GLES20.GL_FLOAT, false, 3 * 4, vertexBuffer2);
//繪制圓形
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, shapePos.length / 3);
//禁止頂點(diǎn)數(shù)組的句柄
GLES20.glDisableVertexAttribArray(vPosition);
}
public int loadShader(int type, String shaderCode)
{
//創(chuàng)建空的著色器
int shader = GLES20.glCreateShader(type);
//將著色器程序加到著色器中
GLES20.glShaderSource(shader, shaderCode);
//編譯色器程序
GLES20.glCompileShader(shader);
return shader;
}
private float[] createPositions()
{
ArrayList<Float> data = new ArrayList<>();
data.add(0.0f);
data.add(0.0f);
data.add(2.0f); //給圓心相對(duì)圓邊增加高度,使之形成錐面
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;
}
}
圓柱
圓柱與圓錐類似,我們可以把圓柱拆分成上下圓面,加上一個(gè)圓筒。圓筒我們之前也沒有畫過,它怎么拆解成三角形呢?我們可以如同拆解東思路來理解圓柱,想想正三棱柱,正八棱柱,正一百棱柱。。。棱越多,就越圓滑,與圓柱越接近,然后再把每個(gè)棱面(矩形)拆分成兩個(gè)三角形就ok。圓筒面的所有頂點(diǎn)為:
ArrayList<Float> cylinder = new ArrayList<>();
float angDegSpan = 360f / n;
for (float i = 0; i < 360 + angDegSpan; i += angDegSpan)
{
float x = (float) (radius * Math.sin(i * Math.PI / 180f));
float y = (float) (radius * Math.cos(i * Math.PI / 180f));
//圓筒坐標(biāo)
cylinder.add(x);
cylinder.add(y);
cylinder.add(2.0f);
cylinder.add(x);
cylinder.add(y);
cylinder.add(0.0f);
}
cylinderShapePos = new float[cylinder.size()];
for (int i = 0; i < cylinderShapePos.length; i++)
{
cylinderShapePos[i] = cylinder.get(i);
}
頂部圓所有頂點(diǎn)坐標(biāo)為:
ArrayList<Float> top = new ArrayList<>();
top.add(0.0f);
top.add(0.0f);
top.add(2.0f);
float angDegSpan = 360f / n;
for (float i = 0; i < 360 + angDegSpan; i += angDegSpan)
{
float x = (float) (radius * Math.sin(i * Math.PI / 180f));
float y = (float) (radius * Math.cos(i * Math.PI / 180f));
//頂部圓形坐標(biāo)
top.add(x);
top.add(y);
top.add(2.0f);
}
topShapePos = new float[top.size()];
for (int i = 0; i < topShapePos.length; i++)
{
topShapePos[i] = top.get(i);
}
底部圓所有頂點(diǎn)坐標(biāo)為:
ArrayList<Float> bottom = new ArrayList<>();
bottom.add(0.0f);
bottom.add(0.0f);
bottom.add(0.0f);
float angDegSpan = 360f / n;
for (float i = 0; i < 360 + angDegSpan; i += angDegSpan)
{
float x = (float) (radius * Math.sin(i * Math.PI / 180f));
float y = (float) (radius * Math.cos(i * Math.PI / 180f));
//底下圓形坐標(biāo)
bottom.add(x);
bottom.add(y);
bottom.add(0.0f);
}
bottomShapePos = new float[bottom.size()];
for (int i = 0; i < bottomShapePos.length; i++)
{
bottomShapePos[i] = bottom.get(i);
}
這樣就可以繪制圓柱了,繪制圓筒面時(shí)要注意繪制方式是GL_TRIANGLE_STRIP。這樣我們就可以得到一個(gè)圓柱了:

具體代碼:
public class MySeventhRender implements GLSurfaceView.Renderer
{
private final String vertextShaderCode =
"attribute vec4 vPosition;" +
"uniform mat4 vMatrix;" +
"varying vec4 vColor;" +
"void main() {" +
" gl_Position = vMatrix * vPosition;" +
" if(vPosition.z!=0.0){"+
" vColor=vec4(0.0,0.0,0.0,1.0);"+
" }else{"+
" vColor=vec4(0.7,0.7,0.7,1.0);"+
" }"+
"}";
private final String fragmentShaderCode =
"precision mediump float;" +
"varying vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}";
private int program;
private FloatBuffer cylinderVertexBuffer;
private FloatBuffer bottomVertexBuffer;
//投影矩陣
private final float[] mProjectMatrix = new float[16];
//相機(jī)位置矩陣
private final float[] mViewMatrix = new float[16];
//計(jì)算變換矩陣
private final float[] mMVPMatrix = new float[16];
private float radius = 1.0f; //半徑
private int n = 360; //切割份數(shù)
private float[] cylinderShapePos;
private float[] bottomShapePos;
private float[] topShapePos;
private FloatBuffer topVertexBuffer;
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config)
{
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
//將背景設(shè)置為灰色
GLES20.glClearColor(0.5f,0.2f,1.0f,1.0f);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT| GLES20.GL_DEPTH_BUFFER_BIT);
//頂點(diǎn)坐標(biāo)
createPositions();
//申請(qǐng)底層空間
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(cylinderShapePos.length * 4);
byteBuffer.order(ByteOrder.nativeOrder());
//將坐標(biāo)數(shù)據(jù)轉(zhuǎn)換為FloatBuffer,用以傳入給OpenGL ES程序
cylinderVertexBuffer = byteBuffer.asFloatBuffer();
//將三角形坐標(biāo)傳入FloatBuffer
cylinderVertexBuffer.put(cylinderShapePos);
cylinderVertexBuffer.position(0);
ByteBuffer bottomByteBuffer = ByteBuffer.allocateDirect(bottomShapePos.length * 4);
bottomByteBuffer.order(ByteOrder.nativeOrder());
//將坐標(biāo)數(shù)據(jù)轉(zhuǎn)換為FloatBuffer,用以傳入給OpenGL ES程序
bottomVertexBuffer = bottomByteBuffer.asFloatBuffer();
//將三角形坐標(biāo)傳入FloatBuffer
bottomVertexBuffer.put(bottomShapePos);
bottomVertexBuffer.position(0);
ByteBuffer topByteBuffer = ByteBuffer.allocateDirect(topShapePos.length * 4);
topByteBuffer.order(ByteOrder.nativeOrder());
//將坐標(biāo)數(shù)據(jù)轉(zhuǎn)換為FloatBuffer,用以傳入給OpenGL ES程序
topVertexBuffer = topByteBuffer.asFloatBuffer();
//將三角形坐標(biāo)傳入FloatBuffer
topVertexBuffer.put(topShapePos);
topVertexBuffer.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);
}
@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, 20);
//設(shè)置相機(jī)位置
Matrix.setLookAtM(mViewMatrix , 0, 1.0f, -10.0f, -4.0f, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
//計(jì)算變換矩陣
Matrix.multiplyMM(mMVPMatrix, 0, mProjectMatrix, 0, mViewMatrix, 0);
}
@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, cylinderVertexBuffer);
//繪制圓筒面
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, cylinderShapePos.length / 3);
//底部圓形的所有坐標(biāo)數(shù)據(jù)
GLES20.glVertexAttribPointer(vPosition, 3, GLES20.GL_FLOAT, false, 3 * 4, bottomVertexBuffer);
//繪制底部圓形
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, bottomShapePos.length / 3);
//頂部圓形的所有坐標(biāo)數(shù)據(jù)
GLES20.glVertexAttribPointer(vPosition, 3, GLES20.GL_FLOAT, false, 3 * 4, topVertexBuffer);
//繪制頂部圓形
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, topShapePos.length / 3);
//禁止頂點(diǎn)數(shù)組的句柄
GLES20.glDisableVertexAttribArray(vPosition);
}
public int loadShader(int type, String shaderCode)
{
//創(chuàng)建空的著色器
int shader = GLES20.glCreateShader(type);
//將著色器程序加到著色器中
GLES20.glShaderSource(shader, shaderCode);
//編譯色器程序
GLES20.glCompileShader(shader);
return shader;
}
private void createPositions()
{
ArrayList<Float> cylinder = new ArrayList<>();
ArrayList<Float> bottom = new ArrayList<>();
ArrayList<Float> top = new ArrayList<>();
bottom.add(0.0f);
bottom.add(0.0f);
bottom.add(0.0f);
top.add(0.0f);
top.add(0.0f);
top.add(2.0f);
float angDegSpan = 360f / n;
for (float i = 0; i < 360 + angDegSpan; i += angDegSpan)
{
float x = (float) (radius * Math.sin(i * Math.PI / 180f));
float y = (float) (radius * Math.cos(i * Math.PI / 180f));
//圓筒坐標(biāo)
cylinder.add(x);
cylinder.add(y);
cylinder.add(2.0f);
cylinder.add(x);
cylinder.add(y);
cylinder.add(0.0f);
//底下圓形坐標(biāo)
bottom.add(x);
bottom.add(y);
bottom.add(0.0f);
//頂部圓形坐標(biāo)
top.add(x);
top.add(y);
top.add(2.0f);
}
cylinderShapePos = new float[cylinder.size()];
for (int i = 0; i < cylinderShapePos.length; i++)
{
cylinderShapePos[i] = cylinder.get(i);
}
bottomShapePos = new float[bottom.size()];
for (int i = 0; i < bottomShapePos.length; i++)
{
bottomShapePos[i] = bottom.get(i);
}
topShapePos = new float[top.size()];
for (int i = 0; i < topShapePos.length; i++)
{
topShapePos[i] = top.get(i);
}
}
}
球
相對(duì)于圓錐圓柱來說,球體的拆解就復(fù)雜許多了,比較常見的拆解方法是將按照經(jīng)緯度拆解和按照正多面體拆解,下圖分別為正多面體和經(jīng)緯度拆解.
正多面體拆解

經(jīng)緯度拆解(每一個(gè)小塊看作一個(gè)矩形,再拆成三角形):

由上圖可以明顯看出,多面體雖然看起來好看點(diǎn),但是還是按照經(jīng)緯度方式來拆解計(jì)算容易點(diǎn),畢竟規(guī)律那么明顯。
球上點(diǎn)的坐標(biāo)
無論是按照經(jīng)緯度拆還是多面體拆,都需要知道球上面點(diǎn)的坐標(biāo),這算是基本的幾何知識(shí)了。以球的中心為坐標(biāo)中心,球的半徑為R的話,那么球上點(diǎn)的坐標(biāo)(x0,y0,z0)為:
x0 = R * cos(a) * sin(b);
y0 = R * sin(a);
z0 = R * cos(a) * cos(b);
其中,a為圓心到點(diǎn)的線段與xz平面的夾角,b為圓心到點(diǎn)的線段在xz平面的投影與z軸的夾角,如圖:

得到頂點(diǎn)后,剩下的工作就和之前繪制其他圖形一樣了。
但是如果繼續(xù)使用圓錐的著色器,我們會(huì)得到這樣的一個(gè)球:

看起來都不太像個(gè)球了,要不是有條白線,這是不是個(gè)球就不好說了。我們需要修啊下頂點(diǎn)著色器,讓它有立體感。把頂點(diǎn)著色器修改為:
private final String vertextShaderCode =
"attribute vec4 vPosition;" +
"uniform mat4 vMatrix;" +
"varying vec4 vColor;" +
"float color;" +
"void main() {" +
" gl_Position = vMatrix * vPosition;" +
" if(vPosition.z>0.0){" +
" color=vPosition.z;" +
" }else{" +
" color=-vPosition.z;" +
" }" +
" vColor=vec4(color,color,color,1.0);" +
"}";
運(yùn)行一下,我們得到的結(jié)果如下圖:

具體代碼:
public class MyEighthRender implements GLSurfaceView.Renderer
{
private final String vertextShaderCode =
"attribute vec4 vPosition;" +
"uniform mat4 vMatrix;" +
"varying vec4 vColor;" +
"float color;" +
"void main() {" +
" gl_Position = vMatrix * vPosition;" +
" if(vPosition.z>0.0){" +
" color=vPosition.z;" +
" }else{" +
" color=-vPosition.z;" +
" }" +
" vColor=vec4(color,color,color,1.0);" +
"}";
private final String fragmentShaderCode =
"precision mediump float;" +
"varying vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}";
private int program;
private FloatBuffer vertexBuffer;
//投影矩陣
private final float[] mProjectMatrix = new float[16];
//相機(jī)位置矩陣
private final float[] mViewMatrix = new float[16];
//計(jì)算變換矩陣
private final float[] mMVPMatrix = new float[16];
private float[] shapePos;
private float step = 1f;
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config)
{
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
//將背景設(shè)置為灰色
GLES20.glClearColor(0.5f, 0.2f, 1.0f, 1.0f);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
//頂點(diǎn)坐標(biāo)
createPositions();
//申請(qǐng)底層空間
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);
//創(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);
}
@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, 20);
//設(shè)置相機(jī)位置
Matrix.setLookAtM(mViewMatrix, 0, 1.0f, -10.0f, -4.0f, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
//計(jì)算變換矩陣
Matrix.multiplyMM(mMVPMatrix, 0, mProjectMatrix, 0, mViewMatrix, 0);
}
@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);
//繪制圓筒面
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, shapePos.length / 3);
//禁止頂點(diǎn)數(shù)組的句柄
GLES20.glDisableVertexAttribArray(vPosition);
}
public int loadShader(int type, String shaderCode)
{
//創(chuàng)建空的著色器
int shader = GLES20.glCreateShader(type);
//將著色器程序加到著色器中
GLES20.glShaderSource(shader, shaderCode);
//編譯色器程序
GLES20.glCompileShader(shader);
return shader;
}
private void createPositions()
{
ArrayList<Float> data = new ArrayList<>();
float r1, r2;
float h1, h2;
float sin, cos;
for (float i = -90; i < 90 + step; i += step)
{
r1 = (float) Math.cos(i * Math.PI / 180.0);
r2 = (float) Math.cos((i + step) * Math.PI / 180.0);
h1 = (float) Math.sin(i * Math.PI / 180.0);
h2 = (float) Math.sin((i + step) * Math.PI / 180.0);
// 固定緯度, 360 度旋轉(zhuǎn)遍歷一條緯線
float step2 = step * 2;
for (float j = 0.0f; j < 360.0f + step; j += step2)
{
cos = (float) Math.cos(j * Math.PI / 180.0);
sin = (float) Math.sin(j * Math.PI / 180.0);
data.add(r2 * cos);
data.add(h2);
data.add(r2 * sin);
data.add(r1 * cos);
data.add(h1);
data.add(r1 * sin);
}
}
shapePos = new float[data.size()];
for (int i = 0; i < shapePos.length; i++)
{
shapePos[i] = data.get(i);
}
}
}