(4)应用投影和相机视口

在OpenGL ES环境中,投影和相机视口使你绘制的对象以更接近物理对象的样子呈现,这是通过精确的数学坐标变换实现的。

  • 投影-这种变换跟据所在GLSurfaceView的宽和高调整对象的坐标。如果没有此变换,对象会被不规则的视口扭曲。投射变换一般只需要在OpenGLview创建或发生变化时调用,代码写在renderer的onSurfaceChanged()方法中。
  • 相机视口-此变换基于一个虚拟相机的位置调整对象的坐标。注意OpenGLES并没有定义一个真的相机对象,而是提供了一些工具方法变换绘制对象的显示来模拟一个相机。一个相机视口的变换可能只在创建GLSurfaceView时调用,或跟据用户动作动态调用。

一、OpenGL 理论基础—-变换

(1)投影变换:

投影变换的目的是定义一个视景体。视景体有两种用途。首先,视景体决定了一个物体是如何映射到屏幕上的(即通过透视投影还是正投影)。其次,视景体定义了哪些物体(物体的一部分)被裁剪到最终的图像之外。
1.透视投影
这里写图片描述
透视投影最显著的特征就是透视缩短,物体距离照相机越远,它在最终图像中看上去越小。
在OpenGL中,glFrustum()函数定义了一个平截头体,
void glFrustum(GLdouble left,GLdouble right,GLdouble bottom,GLdouble top,GLdouble near,GLdouble far);
创建一个表示透视视图平截头体的矩阵,并把它与当前矩阵相乘。平截头体的视景体是由这个函数的参数定义的:
(left,bottom,-near)和(right,top,-near)分别制定了近侧裁剪平面左上角和右下角的(x,y,z)坐标。near和far分别表示
从观察点到近侧和远侧裁剪平面的距离,它们的值应该是正的。
同时,也可以用gluPerspective()定义一个平截头体,
void gluPerspective(GLdouble fovy,GLdouble aspect,GLdouble near,GLdouble far);
创建一个表示透视视图平截头体的矩阵,并把它与当前矩阵相乘。fovy是yz平面上视野的角度,它的值必须在[0.0,180.0]的范围之内,aspect是这个平截头体的纵横比,也就是它的宽度除以高度。near 和far与glFrustum()类似。

2.正投影
这里写图片描述
在正投影下,视景体是一个平行的长方体。用更通俗的话说,是一个箱子。和透视投影不同,视景体的两端的大小并没有不同。也就是说,物体和照相机之间的距离并不影响它看上去的样子。在OpenGL中,使用glOrtho()函数创建一个正交平行的视景体。

(2)视图变换

视图变换相当于把照相机固定在某个位置并使它对准场景。在OpenGL中使用gluLookAt()函数指定了视图变换。这个函数的参数照相机(或眼睛)的位置、瞄向以及哪个方向是朝上的。
void gluLookAt(GLdouble eyex,GLdouble eyey,GLdouble eyez,GLdouble centerx,GLdouble centery,GLdouble centerz,GLdouble upx,GLdouble upy,GLdouble upz);
定义了一个试图矩阵,并把它与当前矩阵进行右乘。目标观察点是由eyex,eyey和eyez指定的。centerx、centery和centerz参数指定了视线上的任意一点。upx,upy和upz参数表示哪个方向是朝上的(也就是说在视景体中自底向上的方向)。
默认情况下,照相机位于原点,指向Z轴的负方向,以y轴的正方向为朝上方向。

(3)模型变换

使用模型变换的目的是设置模型的位置和方向。例如,可以对模型进行旋转、移动和缩放,或者联合应用这几种操作。

(4)视口变换

投影变换和视口变换共同决定了场景是如何映射到计算机屏幕的。投影变换指定了映射的发生机制,而视口变换则决定了场景所映射的有效屏幕区域的形状。
在屏幕上打开窗口的任务是由窗口系统而不是OpenGL负责的。但是,在默认情况下,视口被设置为占据打开窗口的整个像素矩形。可以使用glViewPort()函数选择一个更小的绘图区域。例如,可以对窗口进行划分,在同一个窗口中显示分割屏幕的效果,以显示多个视图。
glViewport(GLint x, GLint y, GLsizei width,GLsizei height);
在窗口中定义一个像素矩形,最终的图像将映射到这个矩形中。(x,y)参数指定了视口的左下角,width和height表示这个视口矩形的宽度和高度。在默认情况下,窗口的初始值是(0,0,winWidth,winHeight),其中winWidth和winHeight指定了窗口的大小。
视口的纵横比一般和视景体的纵横比相同。如果这两个纵横比不同,当图像投影到视口时就会变形。

二、定义一个投影

投影变换的数据是在GLSurfaceView.Renderer 类的 onSurfaceChanged() 方法中计算。下面的例子跟据GLSurfaceView 的宽和高,使用Matrix.frustumM()方法计算出了一个投影变换Matrix:

@Override
public void onSurfaceChanged(GL10 unused, int width, int height) {

    //定义视口变换
    GLES20.glViewport(0, 0, width, height);

    float ratio = (float) width / height;

    // 此投影矩阵在onDrawFrame()中将应用到对象的坐标
    Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
}

Android OpenGL ES的投影变换函数:
void frustumM(float[] m, int offset,float left, float right, float bottom, float top,float near, float far);
与OpenGL的类似,只是增加参数offset作为偏移量,增加参数float[] m用来存储投影矩阵。
考虑到视口的纵横比一般和视景体的纵横比一定要相同,要不然会变形,可行的一个方法是把较小的范围固定在[-1,1]内,而按屏幕尺寸的比例调整较大的范围。举例来说,在竖屏情况下,其宽度是720,而高度是1280,因此我们可以把宽度范围限定在[-1,1],并把高度范围调整为[-1280/720,1280/720]或[-1.78,1.78]。同理在横屏模式情况下,把高度范围设为[-1.78,1.78],而把高度范围设为[-1,1]。通过调整已有的坐标空间,最终会改变我们可用的空间。通过这个方法,不论是竖屏模式还是横屏模式,物体看起来就都一样了。
以上代码产生了一个投影矩阵mProjMatrix ,你可以把它在 onDrawFrame() 方法中与一个相机视口变换结合。
注: 只对你的对象应用一个投影变换一般会导致什么也看不到。通常,你必须也对其应用一个视口变换才能看到东西。

三、定义一个相机视口

再定义一个相机视口变换以使对绘制对象的变换处理变得完整。在下面的例子中,使用方法Matrix.setLookAtM()计算相机视口变换,然后结合前面所计算的投影矩阵。结合后的变换矩阵之后传给要绘制的对象。

@Override
public void onDrawFrame(GL10 unused) {
    ...

    // 设置相机的位置(视口矩阵)
    Matrix.setLookAtM(mVMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);

    // 计算投影和视口变换
    Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0);

    // 绘制形状
    mTriangle.draw(mMVPMatrix);
}

Android OpenGL ES的视图变换函数:
void setLookAtM(float[] rm, int rmOffset,float eyeX, float eyeY, float eyeZ,float centerX, float centerY, float centerZ, float upX, float upY,float upZ);
与OpenGL的类似,只是增加参数rmOffset作为偏移量,增加参数float[] rm用来存储投影矩阵。
注:由投影变换的视景体可知,eyeZ的坐标应该在[-3,-7]之间,否则将看不到图形。

四、应用投影和相机视口变换

将经过投影变换和视图变换的结果矩阵并应用到你的形状上:

public void draw(float[] mvpMatrix) { // 传递计算出的变换矩阵
    ...

    // 获得形状的变换矩阵的handle
    mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");

    // 应用投影和视口变换
    GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);

    // 绘制三角形
    GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
    ...
}

至此,对模型的投影和视图变换完成,后面章节会涉及到模型变换。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值