Loading [MathJax]/jax/input/TeX/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >OpenGL-投影和摄像机

OpenGL-投影和摄像机

作者头像
MelonTeam
发布于 2018-01-08 08:00:43
发布于 2018-01-08 08:00:43
3.4K00
代码可运行
举报
文章被收录于专栏:MelonTeam专栏MelonTeam专栏
运行总次数:0
代码可运行

1.OpenGL中的摄像机、视景体、近平面

OpenGL的摄像机和现实世界中的人眼很相似,都有一个三维的坐标表示位置,眼睛的朝向和视野范围,位置和眼睛朝向不同,所观察到的物体的形态就会有所不同,视野范围则规定了只有在该范围的物体才会进入人的视线,超出视野范围的部分就无法被观察到(人总不可能观察到耳朵两边和后脑勺的物体吧。) 所以,在OpenGL中的摄像机看来,是这样观察物体的:

摄像机视角看近平面:

left、right、bottom、top四条边规定了近平面的大小,near为近平面距离摄像机坐标的距离,far为远平面距离摄像机的距离,这六个变量围成的立方体就是摄像机的可视范围:视景体,物体只有在视景体里面的部分才会被显示出来投影到近平面上。该图为透视投影的案例,投影在近平面的影像会产生近大远小的效果。

2.坐标系

手机屏幕坐标系

二维坐标系,左上角为原点,X,Y轴正方向分别为右和下,XY取值范围为屏幕分辨率。

OpenGL世界坐标系

三维坐标系,X正方向为右,Y正方向为上,Z正方向朝向我们。 注意:摄像机位置,投影坐标都是基于世界坐标系设置的。

3.两种投影方式

正交投影

说明:视点为摄像机的位置;离视点较近,垂直于观察方向向量的平面为近平面,离视点较远,垂直于观察方向向量的平面为远平面, 代码调用:使用Matrix.orthoM()来设置正交投影。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    /**
     * @param m 生成的投影矩阵,float[4*4]
     * @param mOffset 填充时候起始的偏移量
     * @param left  近平面left边的x坐标
     * @param right 近平面right边的x坐标
     * @param bottom  近平面bottom边的y坐标
     * @param top   近平面top边的y坐标
     * @param near  近平面距离摄像机的距离
     * @param far   远平面距离摄像机的距离
     */
public static void orthoM(float[] m, int mOffset,
        float left, float right, float bottom, float top,
        float near, float far) {
}

透视投影

特点:透视投影的图已经在上面给出了,它的投影线是不平行的,最终相交于视点处,所以会有近大远小的效果。 代码调用:使用Matrix.frustumM()来设置透视投影。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
* 参数含义同正交投影
*/
public static void frustumM(float[] m, int offset,
            float left, float right, float bottom, float top,
            float near, float far) {
}

left,right,bottom,top,near,far坐标确定

红点为摄像机位置(eyeX,eyeY,eyeZ) = (0, 0, 3)。 蓝色三角形为被观察的物体(为了方便画图没有用立体图形,但是一个道理,立方体的区别就是顶点z坐标非0了),绿色长方体为视景体,此时三角形全部在视景体内。 近平面各坐标: left=-1,right=1,top=2,bottom=-2, 近平面z坐标 = eyeZ - near=2, 远平面z坐标 = eyeZ - far = -2

near、far的取值范围规定:

  • 正交投影时,摄像机可位于视景体中间,此时near < 0,far > 0,近平面位于视点后面(Z轴正方向),远平面位于视点前面(Z轴负方向)
  • 正交投影时,视景体也可位于视点后面(Z轴正方向),此时near < 0, far < 0
  • 正交投影时,far 和 near没有规定的大小关系,既可以far > near 也可以 far < near,只要物体在视景体内都可以被观察到。
  • 透视投影时,摄像机必须位于视景体前面:eyeZ>近平面Z坐标 && eyeZ > 远平面Z坐标,即:eyeZ > (eyeZ - near) && eyeZ > (eyeZ - far)。

4.设置摄像机位置

代码调用:使用Matrix.setLookAtM()来设置摄像机位置。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    /**
     *
     * @param rm 生成的摄像机矩阵,float[16]
     * @param rmOffset 填充时候的起始偏移量
     * @param eyeX 摄像机x坐标
     * @param eyeY 摄像机y坐标
     * @param eyeZ 摄像机z坐标
     * @param centerX 观察目标点的x坐标
     * @param centerY 观察目标点的y坐标
     * @param centerZ 观察目标点的z坐标
     * @param upX 摄像机up向量在x上的分量
     * @param upY 摄像机up向量在y上的分量
     * @param upZ 摄像机up向量在z上的分量
     */
    public static void setLookAtM(float[] rm, int rmOffset,
            float eyeX, float eyeY, float eyeZ,
            float centerX, float centerY, float centerZ, float upX, float upY,
            float upZ) {
    }

eyeX,eyeY,eyeZ:摄像机坐标。 centerX,centerY,centerZ:观察点坐标,和摄像机坐标一起决定了摄像机的观察方向,即向量(centerX - eyeX, centerY - eyeY, centerZ - eyeZ)。观察方向不朝向视景体是无法看到的。 upX,upY,upZ:摄像机up向量。相对于人眼观察物体中,人头的朝向,头的朝向影响了最后的成像。同样以图来说明:

当up向量为Y的正方向时,正如我们头顶对着天花板,所以观察到的物体是正的,投影在近平面的样子就是正的,如右图。

当up向量为X正方向时,正如我们向右90度歪着脑袋去看这个三角形,看到的三角形就会是向左旋转了90度的三角形。 再比如up向量如果为Z轴正方向,就相当于仰着头去看这个三角形,但是因为我们的up向量和观察方向平行了,所以我们什么也看不到,就比如仰着头去看你眼前的物体时,你什么也看不到。 所以在设置up向量时,一般总是设置为(0,1,0),这是大多数观察时头朝上的方向。注意:up向量的大小无关紧要,有意义的只有方向。 引用一段网上的解释:

第一组eyex, eyey,eyez 相机在世界搜索坐标的位置 第二组centerx,centery,centerz 相机镜头对准的物体在世界坐标的位置 第三组upx,upy,upz 相机向上的方向在世界坐标中的方向 第一组眼睛就相当于你的头在一个三维坐标中的具体坐标。 第二组就是你眼睛要看的物体的坐标。 第三组就是你的头的方向。 如果你把upx=0;upz=0;upy=1,那么说明你的头是正常人一样的方向,如果upy=-1那么就相当于你是倒立的。 如果upx=1;upz=0;upy=0;那么相当于我们看的是右边,如果upx=-1,就相当于看的左边。 如果upx=0;upz=1;upy=0;相当于我们看的是屏幕朝我们的方向,如果upz=-1,相当于我们看的是屏幕向里的方向。

5.变换流程

一个物体的顶点,是在世界坐标系中被定义的,是怎么样转为为在手机屏幕上显示的坐标的呢,OpenGL中有一系列的变换流程,涉及到了6种不同的空间: 物体空间:物体空间坐标系是在物体的几何中心,相对于物体本身而言的。 世界空间:世界空间一开始有介绍过,是物体在最终的3D场景中的的位置坐标对应的坐标系空间,通过代码设置的物体顶点坐标,摄像机坐标,投影平面的left,right等坐标,都是相对于世界空间的。 摄像机空间:物体经过摄像机观察后,进入摄像机空间,该空间坐标系中,摄像机位于原点,视线沿Z轴负方向,Y轴方向与UP向量一致。 剪裁空间:物体即使被摄像机观察到进入了摄像机空间,如果有的部分位于视景体外部,也是看不到的,所以被摄像机观察到的,同时位于视景体外部的部分裁去,留下在视景体内部的物体部分,这部分构成了剪裁空间。 标准设备空间:将剪裁空间内的物体进行透视除法后得到的就是在标准设备空间的物体,需要注意的是OpenGL中标准设备空间三个轴的坐标范围都是[-1,1]。 实际窗口空间:就是视口,一般使用GLES20.glViewport(int x, int y, int width, int height)设置,通常来说是SurfaceView的大小。

  • 物体空间->世界空间 乘以物体变换矩阵,比如将三角形先旋转30°再平移(0, 1, 2),这样按照操作顺序生成的矩阵就是物体的变换矩阵。
代码语言:js
AI代码解释
复制

 public void drawSelf() {   ...   //生成变换矩阵  
 Matrix.setRotateM(mMMartrix, 0, 0, 0, 1, 0);      
 Matrix.rotateM(mMMartrix, 0, xAngle, 1, 0, 0); 
 ... }
 
  • 世界空间->摄像机空间 乘以摄像机矩阵。
  • 摄像机空间->剪裁空间 乘以投影矩阵,乘完后,物体就已经被投影在近平面上了,此时物体各个顶点的坐标不再是三维,而是二维,是对应在近平面上的位置。 /** * 传入物体变换矩阵,得到最终最终变换矩阵,送入渲染管线 * [@param]( "@param" ) spec 物体的变换矩阵 */ public static float[] getFinalMatrix(float[] spec) { mMVPMatrix = new float[16]; Matrix.multiplyMM(mMVPMatrix, 0, mCameraMatrix, 0, spec, 0); //乘以摄像机矩阵 Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mMVPMatrix, 0);//乘以投影矩阵 return mMVPMatrix; }

用户可以操作的为以上三个步骤,一旦物体投影到近平面上后,之后的步骤就由渲染管线自动完成。

  • 剪裁空间->标准设备空间 经过透视除法,将近平面上的物体顶点坐标化为标准设备空间中[-1,1]坐标。
  • 标准设备空间->实际窗口空间(视口) 将标准设备空间的XY平面[-1,1]的坐标转换为位于实际窗口中的XY像素坐标。

在视景体内的物体是先投影到近平面,再到标准设备,最终显示到视口的,所以近平面的宽高非常重要,因为一旦近平面的宽高比出现了问题,那么物体就会被拉伸变形。一般会保持近平面的宽高比和视口的宽高比相等。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
    GLES20.glViewport(0, 0, width, height); //设置视口
    float ratio = (float)width / height;
    MatrixState.setProjectFrustum(-ratio, ratio, -1, 1, 2, 10); 
    MatrixState.setCamera(  
            0, 0, 3,
            0f, 0f, 0f,
            0f, -1f, 0f);
}

代码中,视口大小为width、height,宽高比为ratio,所以设置近平面的left = -ratio, right = ratio, bottom = -1, top = 1,近平面 width = 2ratio, height = 2, width / height = ratio,即为视口宽高比。当然,设置近平面位置也需要考虑需要显示的物体的顶点坐标,如果近平面太小,导致视景体太小无法完全包住观察的物体的话,也就无法观察出来了。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2017-08-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
OpenGL 学习系列---观察矩阵
在 OpenGL 投影矩阵 这篇文章中,讲述了 OpenGL 坐标系统中的投影矩阵,有两种类型的投影矩阵,分别是正交投影和透视投影。
音视频开发进阶
2019/07/25
2.5K0
【OpenGL ES】 Android OpenGL ES -- 透视投影 和 正交投影
博客地址 : http://blog.csdn.net/shulianghan/article/details/46680803
韩曙亮
2023/03/27
2.7K0
【OpenGL ES】 Android OpenGL ES  -- 透视投影 和 正交投影
OpenGL ES 2.0 (iOS)[04]:坐标空间 与 OpenGL ES 2 3D空间
第一次变换 模型变换(Model Transforms):就是指从模型空间转换到世界空间的过程
半纸渊
2018/09/04
1.9K0
OpenGL ES 2.0 (iOS)[04]:坐标空间 与 OpenGL ES 2 3D空间
【Android 音视频开发打怪升级:OpenGL渲染视频画面篇】二、使用OpenGL渲染视频画面
在第一篇文章【音视频基础知识】文章中,就介绍过,视频其实就是一张张图片组成的,在上文【初步了解OpenGL ES】中,介绍了如何通过OpenGL渲染一张图片,可以猜想到,视频的渲染和图片的渲染应该是差不多的。话不多说,马上就来看看。
开发的猫
2020/04/02
2.4K0
【Android 音视频开发打怪升级:OpenGL渲染视频画面篇】二、使用OpenGL渲染视频画面
OpenGLES讲解稿
今天我们讲一下OpenGL与OpenGL在移动端的应用 OpenGL,Open Graphics Library,开放式图形库,就是一个库,与我们平时使用的三方库差不多。 OpenGL在移动端的表现形式为OpenGLES(OpenGL for Embedded Systems),是 OpenGL 三维图形 API 的子集,针对手机、PDA和游戏主机等嵌入式设备而设计。
清墨
2020/01/15
1.1K0
OpenGLES讲解稿
附加实验2 OpenGL变换综合练习
理解掌握OpenGL程序的投影变换,能正确使用投影变换函数,实现正投影与透视投影。
步行者08
2018/10/09
1.5K0
[OpenGL]OpenGL坐标系及坐标转换
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ouyangshima/article/details/25135009
用户1148525
2019/06/11
4.4K0
​OpenGL 学习系列---投影矩阵
OpenGL 在观察空间转换到裁剪空间时,需要用到投影矩阵。而在着色器脚本中,也需要提供一个投影矩阵给对应的 u_ProjectionMatrix变量。
音视频开发进阶
2019/07/25
1.2K0
OpenGL ES实践教程(三)镜子效果
教程 OpenGLES实践教程1-Demo01-AVPlayer OpenGL ES实践教程2-Demo02-摄像头采集数据和渲染 其他教程请移步OpenGL ES文集,这一篇介绍帧缓存、Eye坐标系、OpenGL ES调试技巧。 核心思路 1、定义两个着色器,mBaseEffect用于渲染四棱锥,包括渲染到屏幕和自定义帧缓存;mMirrorEffect用于渲染镜子。 2、渲染mBaseEffect到自定义帧缓存,设置mMirrorEffect纹理为自定义帧缓存,渲染mMirrorEffect的镜子效
落影
2018/04/27
1.6K0
OpenGL ES实践教程(三)镜子效果
​OpenGL 学习系列---坐标系统
在前面绘制基本图形中,遇到了很明显的问题,圆形不像圆形,正多边形不像正多边形?就像下面图形一样:
音视频开发进阶
2019/07/25
1.4K0
python绘制六角星外廓_Python之OpenGL笔记(32):正交投影画六角星
从日常生活的经验中可以很容易地了解到,随着摄像机位置、姿态的不同,就算是对同一个场景进行拍摄,得到的画面也是迥然不同的。
用户7886150
2021/01/19
8260
机械版CG 实验3 变换参考实例
#include <GL/glut.h> #include <stdlib.h> static int shoulder = 0, elbow = 0;//shoulder:肩部角度,elbow: 肘部角度
步行者08
2018/10/09
4650
Android多媒体之GLES2战记第四集--移形换影
这也就是m12,m13,m14,m15为什么特别,m0,m1,m2,m3为什么和x息息相关
张风捷特烈
2019/01/28
5990
Android多媒体之GLES2战记第四集--移形换影
变换(Transform)(2)-坐标空间变换
在刚接触图形学,看games101课程时,观察变换与投影变换就给我了相当大的麻烦,同样的(l, r, t, b, n, f)参数,网上每个人给出来的矩阵形式有所不同,让我永远分不清。随着学习资源的丰富以及自己稍微有了些成长,这篇文章终于解决了这个困惑。
Zero Two
2024/08/24
2110
OpenGL与OpenGL在移动端的应用
OpenGL首先我们从字面意思来理解:Open Graphics Library,开放的图形库,图形库自然是处理图形的,所以简单来说OpenGL就是用来处理图形的一个三方库。 稍微技术流一点,作如下解释:是用于渲染2D,3D矢量图形的跨语言、跨平台的应用程序编程接口(API)。
清墨
2019/11/15
2.9K0
OpenGL与OpenGL在移动端的应用
OpenGL ES 投影和坐标[转]
http://blog.csdn.net/liyuanjinglyj/article/details/46624901
jerrypxiao
2021/02/22
1.1K0
OpenGL ES 投影和坐标[转]
终端图像处理系列 - OpenGL ES 2.0 - 3D基础(矩阵投影)
Overview 移动设备的屏幕是二维平面,要想把一个三维场景渲染在手机二维屏幕上,需要利用OpenGL中的矩阵投射,将三维空间中的点映射到二维平面上。三维矩阵的相关知识是学习OpenGL最重要的课程之一。 线性代数 学习OpenGL三维投射知识之前,我们得事先了解下一些基础的线性代数知识,如向量运算,矩阵运算。 向量运算 向量: 指一个同时具有大小和方向的几何对象,因常常以箭头符号表示以区别于其它量而得名。 向量加减 向量的加(减)法定义是分量的相加(减),即将一个向量中的每一个分量加上(减去)另一个向量
天天P图攻城狮
2018/02/02
2.6K0
终端图像处理系列 - OpenGL ES 2.0 - 3D基础(矩阵投影)
NDK OpenGLES3.0 开发(八):坐标系统
我们知道 OpenGL 坐标系中每个顶点的 x,y,z 坐标都应该在 -1.0 到 1.0 之间,超出这个坐标范围的顶点都将不可见。
字节流动
2020/06/03
1.5K0
透视投影变换矩阵推导_矩阵的投影
http://www.codeguru.com/cpp/misc/misc/math/article.php/c10123__1/Deriving-Projection-Matrices.htm,由于本人能力有限,有译的不明白的地方大家可以参考原文,谢谢^-^!
全栈程序员站长
2022/11/10
1.7K0
透视投影变换矩阵推导_矩阵的投影
从零开始学习3D可视化之摄像机投影方式
我们看到的3D 画面其实是计算机把三维空间中的物体从世界坐标系通过各种复杂的计算投影到屏幕坐标系,并显示在视口中。再说的简单一些,就相当于拿着手机拍了一张照片,放到了窗口里面显示出来。
thingjs
2021/07/12
1K0
相关推荐
OpenGL 学习系列---观察矩阵
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验