之前的博客中,我們繪製了三角形、正方形、圓形、立方體,今天我們將繪製圓錐、圓柱和球體。能夠繪製這些基本的常規幾何形體後,其他的常見幾何形體的繪製對於我們來說就基本沒問題了。
繪製圓錐
由之前的博客,我們大家也應該都知道了,OpenGL ES2.0中物體的繪製重點就是在於把這個物體表面分解成三角形,分解成功後,繪製自然就不成問題了。圓錐我們很容易就能想到把它拆解成一個圓形和一個錐面,錐面的頂點與圓形的頂點,除了錐面的中心點的座標有了“高度”,其他的完全相同。圓形在【我的安卓進階之旅】Opengl Es繪製多邊形及圓形(附Github地址)中我們已經繪製過,那麼錐面其實對於我們來說也是小case了:
ArrayList<Float> pos=new ArrayList<>();
pos.add(0.0f);
pos.add(0.0f);
pos.add(height); //給圓心相對圓邊增加高度,使之形成錐面
float angDegSpan=360f/n;
for(float i=0;i<360+angDegSpan;i+=angDegSpan){
pos.add((float) (radius*Math.sin(i*Math.PI/180f)));
pos.add((float)(radius*Math.cos(i*Math.PI/180f)));
pos.add(0.0f);
}
float[] d=new float[pos.size()]; //所有的頂點
for (int i=0;i<d.length;i++){
d[i]=pos.get(i);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
我們按照繪製圓形的方式,繪製出錐面,然後再在這個錐面的底部繪製一個圓形,這樣我們就得到了一個圓錐了:
主要的代碼變化 @Override
public void onDrawFrame(GL10 gl) {
//GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT|GLES20.GL_DEPTH_BUFFER_BIT);
//告知OpenGL所要使用的Program
GLES20.glUseProgram(mProgramId);
//啓用指向三角形頂點數據的句柄
GLES20.glEnableVertexAttribArray(mPositionId);
drawStep1();
drawStep2();
//禁用指向三角形的頂點數據
GLES20.glDisableVertexAttribArray(mPositionId);
}
//繪製錐面
public void drawStep1(){
//綁定三角形的座標數據
GLES20.glVertexAttribPointer(mPositionId, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
VERTEX_STRID, vertexBuffer);
//綁定顏色數據
GLES20.glUniform4fv(mColorId, 1,color , 0);
//指定vMatrix的值
GLES20.glUniformMatrix4fv(mMatrixHandler,1,false,mMVPMatrix,0);
//索引法繪製正方形
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, shapePos.length/3);
}
//繪製底面
public void drawStep2(){
//綁定三角形的座標數據
GLES20.glVertexAttribPointer(mPositionId, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
VERTEX_STRID, vertexBuffer2);
//綁定顏色數據
GLES20.glUniform4fv(mColorId, 1,color , 0);
//指定vMatrix的值
GLES20.glUniformMatrix4fv(mMatrixHandler,1,false,mMVPMatrix,0);
//索引法繪製正方形
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, circlePos.length/3);
}
運行程序
從圖中我們可以看到,我們繪製的是同樣的顏色,很難看出圓錐的立體效果。要達到立體效果,我們來修改它的着色器:
private final String VERTEX_SHADER =
"attribute vec4 vPosition;" +
"uniform mat4 vMatrix;"+
"varying vec4 vColor;"+
"void main() {" +
" gl_Position = vMatrix*vPosition;" +
" if(vPosition.z!=0.0){\n" +
" vColor=vec4(1,1,1,1.0);\n" +
" }else{\n" +
" vColor=vec4(1,1,1,1.0);\n" +
" }"+
"}";
在頂點着色器中,並沒有傳入顏色,而是在程序中直接判斷進行賦值的,當然也有可以頂點顏色和定邊顏色由外面傳入。在着色器中,我們不再是簡單的賦值,而是加入了流程控制。之後,我們在學習下GLSL語言。 我們看下修改後的效果
怎麼樣,看起來是不是更真實了?
繪製圓柱
圓柱的與圓錐類似,我們可以把圓柱拆解成上下兩個圓面,加上一個圓筒。圓筒我們之前也沒畫過,它怎麼拆解成三角形呢?我們可以如同拆圓的思路來理解圓柱,想想正三菱柱、正八菱柱、正一百菱柱……菱越多,就越圓滑與圓柱越接近了,然後再把每個菱面(矩形)拆解成兩個三角形就OK了,拆解的頂點爲:
ArrayList<Float> pos=new ArrayList<>();
float angDegSpan=360f/n;
for(float i=0;i<360+angDegSpan;i+=angDegSpan){
pos.add((float) (radius*Math.sin(i*Math.PI/180f)));
pos.add((float)(radius*Math.cos(i*Math.PI/180f)));
pos.add(height);
pos.add((float) (radius*Math.sin(i*Math.PI/180f)));
pos.add((float)(radius*Math.cos(i*Math.PI/180f)));
pos.add(0.0f);
}
float[] d=new float[pos.size()];
for (int i=0;i<d.length;i++){
d[i]=pos.get(i);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
這樣我們就可以繪製出一個圓筒了,只需要在頂部繪製一個圓,底部繪製一個圓,就得到了一個圓柱了:
繪製球體
相對於圓錐圓柱來說,球體的拆解就複雜了許多,比較常見的拆解方法是將按照經緯度拆解和按照正多面體拆解,下圖分別爲正多面體示意和經緯度拆解示意:
- 正多面體的方法拆解:
- 經緯度的方法拆解(每一個小塊看做一個矩形,再拆成三角形。PS:人懶,不想做圖。):
由圖我們也能看出來,多面體雖然看起來好看點,但是還是按照經緯度的方式來拆解計算容易點,畢竟規律那麼明顯。
球上點的座標
無論是按照經緯度拆還是按照多面體拆,都需要知道球上面點的座標,這算是基本的幾何知識了。以球的中心爲座標中心,球的半徑爲R的話,那麼球上點的座標則爲:
其中,ψ爲圓心到點的線段與xz平面的夾角,λ爲圓心到點的線段在xz平面的投影與z軸的夾角。用圖形表示如下:
拆解頂點
按照經緯度方式拆解球體,得到球體的頂點數組:
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 度旋轉遍歷一條緯線
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);
}
}
float[] f=new float[data.size()];
for(int i=0;i<f.length;i++){
f[i]=data.get(i);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
得到頂點後,剩下的工作就和之前繪製其他圖形一樣了。
修改着色器
如果繼續使用圓錐的着色器,我們會得到這樣一個球:
看起來都不太像個球了,要不是有條白線,這是不是個球就不好說了。我們需要修改下頂點着色器,讓它有立體感。把頂點着色器修改爲:
uniform mat4 vMatrix;
varying vec4 vColor;
attribute vec4 vPosition;
void main(){
gl_Position=vMatrix*vPosition;
float color;
if(vPosition.z>0.0){
color=vPosition.z;
}else{
color=-vPosition.z;
}
vColor=vec4(color,color,color,1.0);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
運行一下,我們得到的運行結果如下,這樣纔好意思說是個球嘛。
源碼
OK,繪製各種簡單的幾何物體到這裏就結束了,現在應該各種常規的幾何形體都攔不到我們了。後面開始講解其他內容了。