04 - OpenGL ES学习之透视投影和正交投影

上一篇文章介绍了平移变换,旋转变换,缩放变换三种矩阵,这篇文章来说明下两个重要的变换矩阵:透视矩阵和正交矩阵。要了解这两个矩阵,我们想要了解下面两种投影方式。
在计算机三维图像中,投影可以看作是一种将三维坐标变换为二维坐标的方法,常用到的有正交投影和透视投影。正交投影多用于三维建模(例如建筑的平面图采用的就是正交投影),透视投影则由于和人的视觉系统相似,多用于在二维平面中对三维世界的呈现。

1.透视投影

参考文章:透视投影原理与实现
示例代码

它是为了获得接近真实三维物体的视觉效果而在二维的纸或者画布平面上绘图或者渲染的一种方法,也称为透视图 。它具有消失感、距离感、相同大小的形体呈现出有规律的变化等一系列的透视特性,能逼真地反映形体的空间形象。一个常用的例子就是对于我们人眼来说,同一个物体近大远小的形象。这种投影方式就是为了还原人眼观察世界的效果。

原理:
基本的透视投影模型由视点E和视平面P两部分构成(要求E不在平面P上)。视点可以认为是观察者的位置,也是观察三维世界的角度。视平面就是渲染三维对象透视图的二维平面。如下图所示。对于世界中的任一点X,构造一条起点为E并经过X点的射线R,R与平面P的交点Xp即是X点的透视投影结果。三维世界的物体可以看作是由点集合 { Xi} 构成的,这样依次构造起点为E,并经过点Xi的射线Ri,这些射线与视平面P的交点集合便是三维世界在当前视点的透视图


透视投影基本模型
透视图成像原理

透视投影的标准模型


透视投影标准模型

这里一般取近界面为视平面,实际应用中会取近界面和远界面两个界面之间的范围为可视范围。

下面看一下GLKit中构造透视投影的方法:

GLKMatrix4MakePerspective(<#float fovyRadians#>, <#float aspect#>, <#float nearZ#>, <#float farZ#>)

第一个参数代表视角度,第二个是近截面在Z轴上距离原点的距离,第三个是远截面在Z轴上距离原点的距离。

2.正交投影

参考文章:正交投影推导过程
GLKit中构造正交投影矩阵的方法:

 GLKMatrix4MakeOrtho(<#float left#>, <#float right#>, <#float bottom#>, <#float top#>, <#float nearZ#>, <#float farZ#>)

前面四个表示屏幕的范围,nearZ和farZ表示Z轴的可视范围,不在这个范围内的就被裁剪掉了。

下面看代码的实现:

1.透视投影:

//往Z轴负方向平移1.6个距离
    GLKMatrix4 translate = GLKMatrix4MakeTranslation(0, 0, -1.6);
    
    
    //随着时间绕着Y轴旋转
    GLKMatrix4 rotate = GLKMatrix4MakeRotation(varyFactor, 0, 1, 0);
    
    
    //透视投影
    GLKMatrix4 perspectiveMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(90), view.frame.size.width / view.frame.size.height, 0.2, 8.0);
    
    //先旋转,后平移,最后透视投影(切记顺序)
    self.transformMatrix = GLKMatrix4Multiply(translate, rotate);
    self.transformMatrix = GLKMatrix4Multiply(perspectiveMatrix , self.transformMatrix);
    
    //加载统一变量
    GLuint uniformLocation = glGetUniformLocation(_esContext.program, "transform");
    glUniformMatrix4fv(uniformLocation, 1, GL_FALSE, self.transformMatrix.m);

这里注意一点,farZ和NearZ表示远截面和近界面到原点的距离,这里为正值,但实际的位置在Z的负轴,所以这里我们要对绘制的图元,向 负Z轴平移1.6个单位,这样图元才能展示在界面上,效果如图所示:


透视投影.gif

2.正交投影

代码实现

    _elapsedTime += 2.0;
    float varyFactor = GLKMathDegreesToRadians(_elapsedTime);
    _esContext.width = view.drawableWidth;
    _esContext.height = view.drawableHeight;
    
    // x, y, z, r, g, b,每一行存储一个点的信息,位置和颜色
    static GLfloat triangleData[36] = {
           -0.5f,    0.5f,  0.0f,  1.0f,  0.0f,  0.0f,
           -0.5f,   -0.5f,  0.0f,  0.0f,  1.0f,  0.0f,
            0.5f,   -0.5f,  0.0f,  0.0f,  0.0f,  1.0f,
            0.5f,    0.5f,  0.0f,  1.0f,  0.0f,  0.0f,
        };
    glClear(GL_COLOR_BUFFER_BIT);
    
    //x,y放大200倍,本来是展示的一个(1 * 1)像素点,需要将矩形显示出来
    GLKMatrix4 scale = GLKMatrix4MakeScale(300, 300, 300);
                        
    //随着时间绕着Y轴旋转
    GLKMatrix4 rotate = GLKMatrix4MakeRotation(varyFactor, 0, 1, 0);
        
    CGFloat width = view.frame.size.width / 2.0;
    CGFloat height = view.frame.size.height / 2.0;
    
    //正交投影(可视的Z轴范围是-10.0 ~ 10.0)
    GLKMatrix4 orthoMatrix = GLKMatrix4MakeOrtho(-width, width, -height, height, -10.0, 10.0);
    
    //注意顺序,先旋转,后平移,最后矩阵投影
    self.transformMatrix = GLKMatrix4Multiply(scale, rotate);
    self.transformMatrix = GLKMatrix4Multiply(orthoMatrix, self.transformMatrix);
    
    //加载统一变量
    GLuint uniformLocation = glGetUniformLocation(_esContext.program, "transform");
    glUniformMatrix4fv(uniformLocation, 1, GL_FALSE, self.transformMatrix.m);

    
    //gen Buffer and Bind
    if (_vertexBufferId == 0) {
        glGenBuffers(1, &_vertexBufferId);
        glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferId);
        glBufferData(GL_ARRAY_BUFFER, sizeof(triangleData), triangleData, GL_STATIC_DRAW);
    }
    
    int colorLocation = glGetAttribLocation(_esContext.program, "vcolor");
   
    glEnableVertexAttribArray(GLKVertexAttribPosition);
    glEnableVertexAttribArray(colorLocation);
    
    GLuint offset  = 3 * sizeof(float);
    

    
    //加载顶点属性数据
    glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), NULL);
    glVertexAttribPointer(colorLocation, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (const void *) offset);
    _esContext.drawFunc(&_esContext);
    
    
    glDisableVertexAttribArray(GLKVertexAttribPosition);
    glDisableVertexAttribArray(colorLocation);
}

效果如图:


正交投影.gif
MVP矩阵简介

摘自书中8.2.1节
MVP矩阵用于将3D世界中的物体坐标转换为裁剪坐标,最后裁剪坐标进行视口变换,就变换为屏幕上的坐标了,这两篇文章介绍了模型矩阵(M)和投影矩阵(p),下一篇文章介绍视图矩阵(摄像机),后面就可以正式进入3D渲染的学习了。

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

推荐阅读更多精彩内容