openGL之幾何變換(繪製球體)---openGL學習筆記(六)
阿新 • • 發佈:2019-02-06
openGL中的變換包括:
①檢視(modeling)---指定觀察者或者相機位置 GLU.glLookAt() 預設情況下,在透視投影中觀察者是從原點向Z軸負方向看去,也可以自行設定。
②模型(viewing)---在場景中移動物體 包含移動、旋轉、縮放
③模型檢視(modelview)---描述製圖和模型變換的對偶性 例如:觀察者靠近物體,與物體放置靠近觀察者,所呈現的效果是一樣的。
④投影---(projection)改變可視區域的大小以及重新設定形狀 glFrustum() 包含正交投影(也叫平行投影,沒有遠近概念)和透視投影(有深度概念)等。
⑤視口---(viewport)偽變換,對視窗上的最終輸出進行縮放
乘以模型檢視矩陣 乘以投影矩陣 應用透視除法 視口變換
①檢視(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; //r0、r1為圓心引向兩個臨近切片部分表面的兩條線 (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; //r0、r1為圓心引向兩個臨近切片部分表面的兩條線 (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); } } }