OpenGL 3D世界

3D应用程序开发中,一项很重要的工作就是对场景中的物体进行各种投影与变换。相比于OpenGL ES 1.x的封闭模式,OpenGL ES 2.0在变换方面采取了开放模式,其API中不再提供完成各种变换的方法,变换所有的矩阵都由开发人员直接提供给渲染管线。进行投影与变换时,需要一些线性代数的知识。

OpenGL映屏坐标系

在Android系统中,普通View的坐标系是把屏幕的左上角当作坐标原点,向右是x的正方向,向下是y的正方向。OpenGL映屏坐标系是标准的数学坐标系(笛卡尔坐标系),坐标原点是屏幕的左下方。
向右是x的正方向,向上是y的正方向。如下图:

坐标对比.png

OpenGL统一视图

OpenGL使用一个规格化正方形来表示统一视图,其实就是一个(-1.0,-1.0,1.0,1.0)的Rect.


Screen Shot 2018-12-16 at 4.23.08 PM.png

所有图像都是首先映射到这个统一视图中,然后等比例投影到手机的屏幕上。当然也可以理解,所有屏的大小就是一个规格化的(-1.0,-1.0,1.0,1.0)的Rect.

摄像机的设置

在真实的世界,我们用眼睛看物体,眼睛的位置、姿态的不同,就算是对同一场景进行观察,我们所看到景像也是不一样的。在OpenGL虚拟世界中,会虚拟一个摄像机,来表示人眼观察世界。摄像机的设置来模拟人眼的位置、姿态等信息。

摄像机的设置主要需要给出3方面的信息,包括摄像机的位置、观察的方向以及up方向。

Screen Shot 2018-12-16 at 4.51.06 PM.png
  • 摄像机的位置很容易理解,可以用3D空间的坐标表示。

  • 摄像机的观察的方向可以理解为摄像机镜头的指向,可以用摄像机的位置和观察目标点确定的向量表示。

  • 摄像机的up方向可以理解摄像机为了确定视角的向量。

人眼观察现实世界,也是这样的,如下图:

Screen Shot 2018-12-16 at 10.53.38 PM.png

从上图可以看出,OpenGL虚拟世界模拟现实世界时,摄像机的位置、朝向、up方向可以有很多不同的组合。同样的位置可以有不同的朝向、不同的up方向;不同的位置也可以具有相同的朝向,相同的up方向等。

OpenGL可以通过调用Matrix类的setLookAtM方法来完成对摄像机的设置,代码如下:


Matrix.setLookAtM (
mVMatrix,   // 摄像机矩阵
0,                // 起始偏移量
cx,cy,cz,     // 摄像机的位置 x、y、z的坐标
tx,ty,tz,       // 观察目标点 x、y、z的坐标
upx,upy,upz // up向量在x、y、z轴上的分量
);

投影方式

在《OpenGL ES基础》的介绍中,我们知道在图元装配之后光栅化阶段前,首先需要将虚拟3D世界中的物体投影到二维平面上。OpenGL ES 2.0中常用的投影模式有两种,分别为正交投影与透视投影。

投影概念

OpenGL中,根据应用程序提供的投影矩阵,管线会确定一个可视空间区域,称之为视景体。视景体是由6个平面确定的,这6个平面分别为:上平面(up)、下平面(down)、左平面(left) 、右平面(right)、远平面(far)、近平面(near)。

Screen Shot 2018-12-16 at 11.24.39 PM.png

场景中处于视景体内的的物体会被投影到近平面上,视景体外面的物体将被裁剪掉。然后再将近平面的上投影出内容映射到屏幕上的视口中。

视景体_20190103203905.jpg

正交投影

正交投影是平行投影的一种,其投影线(物体的顶点与近平面上投影点的连线)是平行的,因此它的视景体为长方体,投影到近平面上的图形就是物体的平面镜像。

正交投影的视景体如下图:

Screen Shot 2018-12-16 at 11.46.08 PM.png

正交投影的图形效果如下图:

Screen Shot 2018-12-16 at 11.47.45 PM.png

设置正交投影的代码:

Matrix.orthoM (
mProjectMatrix,           // 投影矩阵
0,                              // 起始偏移量
left,right,                      // near平面的left、right
bottom,top,                  // near平面的bottom、top
near,far                       // near平面、far平面与视点的距离
);

透视投影

现实世界中人眼观察物体会有“近大远小”的效果,因此要想开发出更加真实的场景,仅使用正交投影是远远不够的,这时可以采用透视投影。透视投影的投影线是不平行的,他们相交于视点。通过透视投影,可以产生现实世界中“近大远小”的效果,大部分3D图形采用的都是透视投影。

透视投影的视景体是锥形体。上面介绍投影概念时,使用的就是透视投影。视景体不再复画,投影效果如下:

透视投影效果.png

透视投影的设置代码:

Matrix.frustumM (
mProjectionMatrix,             // 投影矩阵
0 ,                                    // 起始偏移量
left,right,                             // near平面的left、right
bottom,top,                         // near平面的bottom、�top
near,far                              // near平面、far平面与视点的矩离
) ;

Matrix变换

在《OpenGL ES基础》中说过,数字图像的本质就是一个像素矩阵。因此对数字图像的处理,可以理解为是对矩阵的各种变换。Matrix变换是非常重要的。

Matrix变换的数学知识

Matrix变换都是通过将表示点坐标的向量与特定的变换矩阵相乘完成的,进行变换时,三维空间中点的位置需要表示成齐次坐标形式。所谓齐次坐标形式也就是在x、y、z 3个坐标值后面增加第四个量w,未变换时w值一般为1。这样设计是为了计算方便。

例如:点P(Px,Py,Pz,1)与一个矩阵相乘即可完成一次变换,得到变换后的点Q(Qx,Qy,Qz,1)。

矩阵变换.png

当矩阵M中的元素取适当的值时,等式Q=MP就会有其特殊的几何意义,例如,可以将三维空间中的点P平移、旋转、缩放到点Q。这些变换的具体信息就存放在矩阵M中,因此通常矩阵M为变换矩阵。当对一个图像进行变换时,让图像的所有像素点乘以一个变换矩阵,就是可以得到变换后的另一个图像。

平移变换

平移变换.png

缩放变换

缩放变换.png

放转变换

介绍旋转变换的矩阵格式前,首先需要知道绕坐标轴或任意轴旋转的一些规则。OpenGL中,旋转角度的正负可以用右手螺旋定则来确定。螺旋定则:右手握住旋转轴,使大姆指指向旋转轴的正方向,4指环绕的方向即为旋转的正方向,也就是旋转角度为正值。下图为绕任意轴r的旋转Φ的矩阵:

1342760515_3105.jpg

具体推导过程可以参考:

http://www.cppblog.com/lovedday/archive/2008/01/12/41031.html

一般情况下,对绕任意轴的旋转变化,可以拆分为绕x、y、z轴的旋转变化的组合。

操作代码

/**
     * Translates matrix m by x, y, and z in place.
     *
     * @param m matrix
     * @param mOffset index into m where the matrix starts
     * @param x translation factor x
     * @param y translation factor y
     * @param z translation factor z
     */
    public static void translateM(
            float[] m, int mOffset,
            float x, float y, float z) {
        for (int i=0 ; i<4 ; i++) {
            int mi = mOffset + i;
            m[12 + mi] += m[mi] * x + m[4 + mi] * y + m[8 + mi] * z;
        }
    }

/**
     * Scales matrix m in place by sx, sy, and sz.
     *
     * @param m matrix to scale
     * @param mOffset index into m where the matrix starts
     * @param x scale factor x
     * @param y scale factor y
     * @param z scale factor z
     */

public static void scaleM(float[] m, int mOffset,
            float x, float y, float z) {
        for (int i=0 ; i<4 ; i++) {
            int mi = mOffset + i;
            m[     mi] *= x;
            m[ 4 + mi] *= y;
            m[ 8 + mi] *= z;
        }
    }

/**
     * Rotates matrix m in place by angle a (in degrees)
     * around the axis (x, y, z).
     *
     * @param m source matrix
     * @param mOffset index into m where the matrix starts
     * @param a angle to rotate in degrees
     * @param x X axis component
     * @param y Y axis component
     * @param z Z axis component
     */

public static void rotateM(float[] m, int mOffset,
            float a, float x, float y, float z) {
        synchronized(sTemp) {
            setRotateM(sTemp, 0, a, x, y, z);
            multiplyMM(sTemp, 16, m, mOffset, sTemp, 0);
            System.arraycopy(sTemp, 16, m, mOffset, 16);
        }
    }

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容