【附源码】【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;
}
}