1. 程式人生 > >openGL之幾何變換(繪製球體)---openGL學習筆記(六)

openGL之幾何變換(繪製球體)---openGL學習筆記(六)

openGL中的變換包括:
  ①檢視(modeling)---指定觀察者或者相機位置  GLU.glLookAt() 預設情況下,在透視投影中觀察者是從原點向Z軸負方向看去,也可以自行設定。
  ②模型(viewing)---在場景中移動物體 包含移動、旋轉、縮放
  ③模型檢視(modelview)---描述製圖和模型變換的對偶性 例如:觀察者靠近物體,與物體放置靠近觀察者,所呈現的效果是一樣的。
  ④投影---(projection)改變可視區域的大小以及重新設定形狀  glFrustum() 包含正交投影(也叫平行投影,沒有遠近概念)和透視投影(有深度概念)等。
  ⑤視口---(viewport)偽變換,對視窗上的最終輸出進行縮放

頂點變換的管線:

 
            乘以模型檢視矩陣                    乘以投影矩陣           應用透視除法                        視口變換

  源頂點資料----------------->經過變換的檢視座標------------->剪裁座標--------------->經過規範化的裝置座標------------->視窗座標

我們要繪製一個球體,那麼就要先把球體切開,先水平切成幾部分,把其中的一部分拿出來是一條帶狀,將這部分切開將會是一個平行四邊形帶,如下圖。

需要注意的是:因為openGL中剔除表面時通常剔除背面,而繪製三角形過程中,採用畫三角形帶的方法,如果按照0-->1-->2-->0   1-->2-->3-->1   的方式繪製的話就會導致所繪製的三角形帶一個逆時針,一個順時針,而剔除時,就會把其中一個剔除掉,所以openGL在繪製三角形帶時,實際的做法是:0-->1-->2-->0   2-->1-->3-->2,這樣就可以保證所有的三角形全部為逆時針方向繪製。


那麼這個平行四邊形可以用畫三角形帶的方法畫出來,那麼如果分的份數夠多(微積分的思想),且把每一個部分都以平行四邊形的方式畫出來,那麼球體就可以實現,如下圖:


首先是座標的計算:

假如以球心圓點為座標原點,那麼水平切成的幾個部分的Y座標可以確定,且不同。而在內迴圈繪製每個圓面的時候的X、Z座標是可以確定且不同的,所以,給定圓的半徑R,就能用雙迴圈的方式繪製出球體的各個點座標。

float R = 0.7f;//球的半徑
int statck = 20;//statck:切片----把球體橫向切成幾部分
float statckStep = (float) (Math.PI / statck);//單位角度值
int slice = 50;//縱向切幾部分 float sliceStep = (float) (Math.PI / slice);//水平圓遞增的角度 float r0,r1,x0,x1,y0,y1,z0,z1; //r0r1為圓心引向兩個臨近切片部分表面的兩條線 (x0,y0,z0)(x1,y1,z1)為臨近兩個切面的點。 float alpha0 = 0,alpha1 = 0; //前後兩個角度 float beta = 0; //切片平面上的角度 List<Float> coordsList = new ArrayList<Float>(); //外層迴圈 for( int i = 0;i < statck;i++ ){ alpha0 = (float) (- Math.PI / 2 + (i*statckStep)); alpha1 = (float) (- Math.PI / 2 + ((i+1)*statckStep)); y0 = (float) (R * Math.sin(alpha0)); r0 = (float) (R * Math.cos(alpha0)); y1 = (float) (R * Math.sin(alpha1)); r1 = (float) (R * Math.cos(alpha1)); //迴圈每一層圓 for( int j = 0;j <= (slice * 2);j ++ ){ beta = j * sliceStep; x0 = (float) (r0 * Math.cos(beta)); z0 = -(float) (r0 * Math.sin(beta)); x1 = (float) (r1 * Math.cos(beta)); z1 = -(float) (r1 * Math.sin(beta)); coordsList.add(x0); coordsList.add(y0); coordsList.add(z0); coordsList.add(x1); coordsList.add(y1); coordsList.add(z1); }
座標確定了之後剩下就簡單了,一樣的,先要設定清屏色,設定繪圖顏色,然後指定模型檢視矩陣,載入單位矩陣,放置眼球位置,設定旋轉角度。最後再指定頂點指標,繪製三角形帶。

執行效果圖:

注:為方便觀察,這個圖是用畫線帶的方式繪製的,想要畫球體該用畫三角形帶的方法。

最後附程式碼:

public class MySphereRenderer extends AbstractRenderer{
    @Override
public void onDrawFrame(GL10 gl) {

        gl.glClear(GL10.GL_COLOR_BUFFER_BIT);//設定清屏色
gl.glColor4f(1f, 1f, 1f, 1f);//設定繪圖顏色
gl.glMatrixMode(GL10.GL_MODELVIEW);//模型檢視矩陣
gl.glLoadIdentity();//載入單位矩陣
GLU.gluLookAt(gl, 0, 0, 5, 0, 0, 0, 0, 1, 0);//放置眼球位置
gl.glRotatef(xRotate, 1, 0, 0);//x軸旋轉角度
gl.glRotatef(yRotate,0,1,0);//y軸旋轉角度
/***********************計算球體座標****************************/
float R = 0.7f;//球的半徑
int statck = 20;//statck:切片----把球體橫向切成幾部分
float statckStep = (float) (Math.PI / statck);//單位角度值
int slice = 50;//縱向切幾部分
float sliceStep = (float) (Math.PI / slice);//水平圓遞增的角度
float r0,r1,x0,x1,y0,y1,z0,z1; //r0r1為圓心引向兩個臨近切片部分表面的兩條線 (x0,y0,z0)(x1,y1,z1)為臨近兩個切面的點。
float alpha0 = 0,alpha1 = 0; //前後兩個角度
float beta = 0; //切片平面上的角度
List<Float> coordsList = new ArrayList<Float>();
//外層迴圈
for( int i = 0;i < statck;i++ ){
            alpha0 = (float) (- Math.PI / 2 + (i*statckStep));
alpha1 = (float) (- Math.PI / 2 + ((i+1)*statckStep));
y0 = (float) (R * Math.sin(alpha0));
r0 = (float) (R * Math.cos(alpha0));
y1 = (float) (R * Math.sin(alpha1));
r1 = (float) (R * Math.cos(alpha1));
//迴圈每一層圓
for( int j = 0;j <= (slice * 2);j ++ ){
                beta = j * sliceStep;
x0 = (float) (r0 * Math.cos(beta));
z0 = -(float) (r0 * Math.sin(beta));
x1 = (float) (r1 * Math.cos(beta));
z1 = -(float) (r1 * Math.sin(beta));
coordsList.add(x0);
coordsList.add(y0);
coordsList.add(z0);
coordsList.add(x1);
coordsList.add(y1);
coordsList.add(z1);
}
            //指定頂點指標
gl.glVertexPointer(3,GL10.GL_FLOAT,0, BufferUtils.list2FloatBuffer(coordsList));
//若要畫球體,應以畫三角形帶的方式繪製,為方便觀察,此處用畫線帶的方式
gl.glDrawArrays(GL10.GL_LINE_STRIP,0,coordsList.size() / 3);
}

    }
}