【附源碼】【Android 3D OpenGL】開發之四——投影中的正交與透視【MacroCheng原創】
一、正交與透視
1.1、正交Orthographic (無消失點投影)
正交視圖無法看到一個物體是遠離自己還是正在我們面前。爲什麼?因爲它不會根據距離收縮。所以如果你如果你畫一個固定大小的物體在視點前面,同時畫一個同樣大小的物體在第一個物體的遠後方,你無法說那個物體是第一個。因爲兩個都是一樣的大小,根距離無關。他們不會隨着距離而收縮。
1.2、透視Perspective (有消失點投影)
透視視圖和我們從眼睛看到的視圖是一樣的。例如,一個高個子的人站在你面前,你看上去是很高的。如果這個人站在100米以外,他甚至還沒有你的拇指大。他看上去會隨着距離而縮小,但是我們實際上都知道,它依然是個高個子。這種效果叫做透視。上面例子中提到的兩個物體,第二個物體將會顯示地更小,所以我們可以區分哪個是離我們近的物體,那個是離我們遠的物體。
二、關鍵方法說明
2.1、glMatrixMode(GL_PROJECTION)
指明接下來的兩行代碼將影響projection matrix(投影矩陣)。投影矩陣負責爲我們的場景增加透視。
2.2、glMatrixMode(GL_MODELVIEW)
指明任何新的變換將會影響 modelview matrix(模型觀察矩陣)。模型觀察矩陣中存放了我們的物體訊息。
2.3、gl.glOrthof()
設置我們的視點來做orthographic view。這些參數是爲邊界設定,順序是這樣的:left, right, bottom, top, zNear, zFar。
2.4、glFrustumf()
glFrustumf()函數的參數和glOrthof()的參數略有不同。因爲我們沒有縮小物體,但是我們定義的錐體將被漏斗狀切開。
三、正交和透視效果對比
3.1、正交圖片
跟只有一個金字塔看起來效果是一摸一樣的,但是其實我們有建立10個金字塔的,看下面透視效果就知道了。
3.2、正交的核心語句
gl.glOrthof(-1,1,-1/ratio,1/ratio,0.01f,100.0f);
//gl.glFrustumf(-size, size, -size / ratio, size / ratio, 0.01f, 100.0f);
3.3、透視圖片
3.4、透視核心語句
//gl.glOrthof(-1,1,-1/ratio,1/ratio,0.01f,100.0f);
gl.glFrustumf(-size, size, -size / ratio, size / ratio, 0.01f, 100.0f);
四、OpenGLRenderer.java
因爲其他的都沒動,就動了這個類,所以僅貼出這個類得代碼
package com.macrocheng.opengl3d01;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.opengl.GLSurfaceView;
public class OpenGLRenderer implements GLSurfaceView.Renderer {
private static final String LOG = OpenGLRenderer.class.getSimpleName();
private float red = 0.9f;
private float green = 0.2f;
private float blue = 0.2f;
private Triangle tr;
private float xAngle;
private float yAngle;
private Pyramid py;
private float _width = 320f ;
private float _height = 480f ;
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
// TODO Auto-generated method stub
//tr = new Triangle();
/**
* 指明接下來的兩行代碼將影響projection matrix(投影矩陣)。
* 投影矩陣負責爲我們的場景增加透視。
*/
gl.glMatrixMode(GL10.GL_PROJECTION);
float size = 0.01f*(float)Math.tan(Math.toRadians(45.0)/2);
float ratio = _width/_height;
gl.glOrthof(-1,1,-1/ratio,1/ratio,0.01f,100.0f);
//gl.glFrustumf(-size, size, -size / ratio, size / ratio, 0.01f, 100.0f);
gl.glViewport(0, 0, (int)_width, (int)_height);
/**
* 指明任何新的變換將會影響 modelview matrix(模型觀察矩陣)。
* 模型觀察矩陣中存放了我們的物體訊息。
*/
gl.glMatrixMode(GL10.GL_MODELVIEW);
/**
* 這使OpenGL ES檢查對象的z-order。
* 如果我們沒有enable它,我們將看到最後被繪製的對象一直顯示在最前面。
* 這意味着,及時即使這個物體本來應該被更近更大的物體遮蓋,我們依然可以看到它。
*/
gl.glEnable(GL10.GL_DEPTH_TEST);
py = new Pyramid();
/**
* 決定哪一面可見,讓正面可見
*/
gl.glEnable(GL10.GL_CULL_FACE);
/**
* 逆時針方向的面爲前面,前面就被畫出來
*/
gl.glFrontFace(GL10.GL_CCW);
/**
* 後面就不需要被畫出來
*/
gl.glCullFace(GL10.GL_BACK);
/**
* 設置OpenGL使用vertex數組來畫
*/
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
/**
* 設置顏色來自數組
*/
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
// TODO Auto-generated method stub
_width = width;
_height = height;
gl.glViewport(0, 0, width, height);
}
@Override
public void onDrawFrame(GL10 gl) {
// TODO Auto-generated method stub
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
/**
* 爲了讓顏色變化可見,我們必須調用glClear()以及顏色緩衝的Mask來清空buffer,
* 然後爲我們的底色使用新的底色。
*/
//gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
/**
* 我們通過glClearColor()方法爲底色定義了顏色。
* 底色是在我們能看到的所有東西的後面,所以所有在底色後面的東西都是不可見的。
* 可以想象這種東西爲濃霧,擋住了所有的東西。
*/
gl.glClearColor(red, green, blue, 1.0f);
//清除顏色的Buffer然後讓現實上面我們通過glClearColor來定義的顏色
for(int i=0;i<10;i++)
{
/**
* 重置當前的模型觀察矩陣
* 近似於重置。它將所選的矩陣狀態恢復成其原始狀態
*/
gl.glLoadIdentity();
gl.glTranslatef(0.0f, -1f, -1.0f + -1.5f * i);
/**
* 旋轉,四個參數分別是旋轉度、x軸、y軸、z軸
* 後面三個值來決定圍繞那個軸線來旋轉
*/
gl.glRotatef(xAngle, 1f, 0f, 0f);
gl.glRotatef(yAngle, 0f, 1f, 0f);
/**
* 第一個參數是大小,也是頂點的維數。我們使用的是x,y,z三維座標。
* 第二個參數,GL_FLOAT定義buffer中使用的數據類型。
* 第三個變量是0,是因爲我們的座標是在數組中緊湊的排列的,沒有使用offset。
* 最後,第四個參數頂點緩衝。
*/
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, py.getVertexBuffer());
/**
* 參數4表示RGBA(RGBA剛好是四個值),其餘的幾個參數大家都比較熟悉了。
*/
gl.glColorPointer(4, GL10.GL_FLOAT, 0, py.getColorBuffer());
/**
* 將所有這些元素畫出來。第一個參數定義了什麼樣的圖元將被畫出來。
* 第二個參數定義有多少個元素,
* 第三個是indices使用的數據類型。
* 最後一個是繪製頂點使用的索引緩衝。
*/
gl.glDrawElements(GL10.GL_TRIANGLES, py.getNumberOfPoint(), GL10.GL_UNSIGNED_SHORT, py.getIndexBuffer());
}
}
public float getXAngle() {
return xAngle;
}
public float getYAngle() {
return yAngle;
}
public void setYAngle(float angle) {
yAngle = angle;
}
public void setXAngle(float angle) {
xAngle = angle;
}
/**
* 設置顏色的值
* @param r Red值
* @param g Green值
* @param b Blue值
*/
public void setColor(float r,float g,float b)
{
red = r;
green = g;
blue = b;
}
/**
* 設置三角形的旋轉度
* @param x x軸上的旋轉度
* @param y y軸上的旋轉度
*/
public void setAngle(float x,float y)
{
xAngle = x;
yAngle = y;
}
}