OpenGL
OpenGL简介
什么是OpenGL ES?
OpenGL(Open Graphics Library)指定义了一个跨编程语言,跨平台的编程接口规格的专业图形程序接口。用于三维/二维图形,是一个功能强大,调用方便的底层图形库。
OpenGL ES (OpenGL for Embedded Systems)是OpenGL三维图形API的子集,针对手机和游戏主机等嵌入式设备而设计。OpenGL ES相对于OpenGL来说,减少了许多不是必须的方法和数据类型,去掉了不必须的功能,对代价大的功能做了限制,相比OpenGL更为轻量。在OpenGL ES的世界里,没有四边形或多边形,无论多么复杂的图形都是由点,线和三角形组成的,也去除了glBegin / glEnd等方法。
OpenGL ES可以做什么?
OpenGL ES是手机,游戏主机等嵌入式设备三维/二维图形处理的API,其强大的渲染能力使其成为我们在嵌入式设备上进行图形处理的优良选择。常用的场景有:
- 图形绘制。二维/三维
- 图片处理。比如图片色调的转换,美颜等
- 摄像头预览效果处理。比如美颜相机,恶搞相机等
- 视频处理。
- 3D游戏。比如神庙逃亡等
OpenGL ES版本以及Android支持情况
OpenGL ES中的3.x版本都向下兼容OpenGL ES2.0,所以可以选择使用OpenGL ES的2.0版本。Android4.3(API 18)以及更高版本均支持这个API规范。
OpenGL ES中的基本概念
状态机
OpenGL是一个状态机,它维持自己的状态,并根据用户调用的函数来改变自己的状态。根据状态的不同,调用同样的函数也可能产生不同的效果。
OpenGL中,大多数元素都可以用状态来描述,如:
-
颜色,纹理坐标,光源的各种参数…
-
是否启用了光照,是否启用了纹理,是否启用了深度测试…
OpenGL会保持状态,除非我们调用OpenGL函数来改变它,如用glEnableXXX方法开启了一个状态,在以后的渲染中将一直保留并应用这个状态,除非调用glDisableXXX方法以及同类函数来改变该状态或程序退出。
又或者当前颜色是一个状态变量,可以把当前颜色设置为白色,红色或其他任何颜色,在此之后绘制的所有物体都将使用这种颜色,直到把当前颜色设置为其他颜色。
上下文
状态机中的各种颜色都保存在**上下文(context)**中。
通过放置这些状态到context中,context可以跟踪用于渲染的帧缓存,用于几何数据,颜色等的缓存。还会决定是否使用如纹理,光照等功能以及会为渲染定义当前的坐标系统等。并且在多任务的情况下,可以很容易地共享硬件设备,而互不影响各自的状态。上下文的切换会产生较大的开销,但是不同模块的绘制可能需要使用完全独立的状态管理,所以,可以在应用程序中分别创建多个不同的上下文,在不同的线程中使用不同的上下文,上下文之间共享纹理,缓冲区等资源,这样比反复切换上下文或者大量修改渲染状态更加高效。
因此渲染的时候,要制定对应的当前上下文,也就是按要求创建一系列诸如EGLSurface,EGLDisplay等对象,调用glMakeCurrent之后,当前线程便拥有了OpenGL的绘图能力,在此之后就能使用OpenGL绘图等操作。在GLSurfaceView中我们可以看到一个GLThread,也就是GL线程,这一线程内部封装了OpenGL绘制所需要的整个完整的过程,并且按照这个流程正确地执行,也就是具有了OpenGL的绘图能力。
帧缓冲区
OpenGL是图形API,因此可以说所有的运算和结果最终都是需要通过图像进行输出的。那么绘图必然就需要有一块画板,而帧缓冲区就是OpenGL中的画板。但是特别需要注意的是,帧缓冲区不是常规意义缓冲区,它并不是实际存储数据的对象,类似画画的时候,需要在画板上放一块画布,才能实际在画布上进行绘画,这些画布可以是纹理(Texture)或者是渲染缓冲区(RenderBuffer),而放置这些画布的位置被称为帧缓冲区的附着(Attachment)。
附着(Attachment)
附着可以理解为画板上的夹子,夹住了哪个画布,就往对应画布上输出数据。
在帧缓冲区中可以附着3种类型的附着,颜色附着(ColorAttachment),深度附着(DepthAttachment),模板附着(StencilAttachment)。这三种附着对应的存储区域也被称为颜色缓冲区(ColorBuffer),深度缓冲区(DepthBuffer),模板缓冲区(StencilBuffer)。
颜色附着输出绘制图像的颜色数据,也就是平时常见的图像的RGBA数据。如果使用了多渲染目标(Multiple Render Targets)技术,那么颜色附着的数量可能会大于一。
深度附着输出绘制图像的深度数据,深度数据主要在3D渲染中使用,一般用于判断物体的远近来实现遮挡的效果。
模板附着输出模板数据,模板数据是渲染中较为高级的用法,一般用于渲染时进行像素级别的剔除和遮挡效果,常见的应用场景比如三维物体的描边。
纹理映射
纹理是一个用来保存图像色值的 OpenGL ES 缓存,而纹理和渲染缓冲区也是实际存储图像数据的对象,可作为帧缓冲区的附着。
要展现出现实世界中更加真实多彩的物体,绘制出更加真实的物体,就需要用到纹理映射。纹理映射是将2D的纹理映射到3D场景中的立体物体上。
纹理是表示物体表面的一幅或几幅二维图形,也称纹理贴图(texture)。当把纹理按照特定的方式映射到物体表面上的时候,能使物体看上去更加真实。当前流行的图形系统中,纹理绘制已经成为一种必不可少的渲染方法。在理解纹理映射时,可以将纹理看做应用在物体表面的像素颜色。在真实世界中,纹理表示一个对象的颜色、图案以及触觉特征。纹理只表示对象表面的彩色图案,它不能改变对象的几何形式。更进一步的说,它只是一种高强度的计算行为。
渲染管线
在OpenGL中,任何事物都在3D空间中,而屏幕和窗口却是2D像素数组,这导致OpenGL的大部分工作都是关于把3D坐标转变为适应屏幕的2D像素。而3D到2D的坐标处理过程就是由OpenGL的图形渲染管线(Graphics Pipeline,实际上指的是一堆原始图形数据途经一个输送管道,期间经过各种变化处理最终出现在屏幕的过程)管理的。
图形渲染管线可以被划分为两个主要部分:
(1)将3D坐标转换为2D坐标
(2)把2D坐标转变为实际的有颜色的像素
图形渲染管线可以被划分为几个阶段,每个阶段将会把前一个阶段的输出作为输入,所有这些阶段都是高度专门化(它们都有一个特定的函数),并且很容易并行执行。工作过程和车间流水线一致,各模块各司其职又相互依赖。
OpenGL ES采用服务器/客户端编程模型,客户端运行在CPU上,服务端运行在GPU上,调用OpenGL ES函数时,由客户端发送至服务端,并被服务端转换成底层图形硬件支持的绘制命令。
顶点数组和顶点缓冲区
准备好了画布之后,就要开始画图了。画图一般是先画好图像的骨架,然后再往骨架里面填充颜色,这对于OpenGL也是一样的。顶点数据就是要画的图像的骨架,和现实中不同的是,OpenGL中的图像都是由图元组成。在OpenGL ES中,有3种类型的图元:点、线、三角形。那这些顶点数据最终是存储在哪里的呢?开发者可以选择设定函数指针,在调用绘制方法的时候,直接由内存传入顶点数据,也就是说这部分数据之前是存储在内存当中的,被称为顶点数组。而性能更高的做法是,提前分配一块显存,将顶点数据预先传入到显存当中。这部分的显存,就被称为顶点缓冲区。
索引数组和索引缓冲区
索引数组的目的主要是为了实现顶点的复用,在绘制图像时,总是会有一些顶点被多个图元共享,而反复对这个顶点进行运算常常是没有必要的(也有某些特殊场景需要)。因此对通过索引数据,指示OpenGL绘制顶点的顺序,不但能防止顶点的重复运算,也能在不修改顶点数据的情况下,一定程度的重新组合图像。
和顶点数据一样,索引数据也可以以索引数组的形式存储在内存当中,调用绘制函数时传入;或者提前分配一块显存,将索引数据存储在这块显存当中,这块显存就被称为索引缓冲区。同样的,使用缓冲区的方式,性能一般会比直接使用索引数组的方式更加高效。
着色器程序
OpenGL和其他主流图形API一样,已经将固定渲染管线变成了可编程渲染管线,shader提供对图形运算的精细操作,带来了各式各样的处理能力,极度的丰富了图形API所能实现的效果。所以,在OpenGL在实际调用绘制函数之前,还需要指定一个由shader编译成的着色器程序。
常见的着色器有顶点着色器(VertexShader),片元着色器(FragmentShader)/像素着色器(PixelShader),几何着色器(GeometryShader),曲面细分着色器(TessellationShader)。OpenGL ES中只支持顶点着色器和片元着色器两个最基础的着色器。
OpenGL在处理shader时,和其他编译器一样。通过编译、链接等步骤,生成了着色器程序(glProgram),着色器程序同时包含了顶点着色器和片元着色器的运算逻辑。
在OpenGL进行绘制的时候,首先由顶点着色器对传入的顶点数据进行运算。再通过图元装配,将顶点转换为图元。然后进行光栅化,将图元这种矢量图形,转换为栅格化数据。最后,将栅格化数据传入片段着色器中进行运算。片段着色器会对栅格化数据中的每一个像素进行运算,并决定像素的颜色,也可以在这个阶段将某些像素丢弃。其中像素的颜色可以是具体的数值或者是由某种算法计算而来的。如果图元有纹理,就必须用纹理来产生图元的二维渲染图象上每个像素的颜色。
顶点着色器
OpenGL ES中的重要部分,着色器(Shader)是在GPU上运行的小程序,可以通过他们来计算顶点属性,此程序使用OpenGL ES SL语言进行编写,是一个描述顶点或像素特性的简单程序。
对于发送给GPU的每一个顶点,都要执行一次顶点着色器。其功能是把每个顶点在虚拟空间中的三维坐标变换为可以在屏幕上显示的二维坐标,并带有用于z-buffer的深度信息。顶点着色器可以操作的属性有:位置,颜色,纹理坐标,但是不能创建新的顶点,在渲染管线中每个顶点都是独立且并行地被执行。
其输入输出模型如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HlOielyX-1632470982581)(OpenGL分享.assets/vertexShader.png)]
顶点着色器的数据输入主要有两种,统一变量(Uniform)、顶点属性(VertexAttribute)。统一变量在所有顶点运算中是一样的,而顶点属性则是从外部输入的顶点数据中获取,一般在每个顶点运算中都是不同的。
顶点着色器的最重要的任务是执行顶点坐标变换,应用程序中设置的图元顶点坐标通常是针对本地坐标系的。本地坐标系简化了程序中的坐标计算,但是GL并不识别本地坐标系,所以在顶点着色器中要对本地坐标执行模型视图变换,将本地坐标转化为裁剪坐标系的坐标值。
顶点着色器的另一个功能是向后面的片元着色器提供一组可变量(varying)。可变量会在图元装配阶段(图元装配之后,所有3D的图元将被转化为屏幕上的2D图元)之后被执行插值计算,如果是单重采样,其插值点为片段的中心,如果多重采样,其插值点可能为多个采样片段中任意一个位置。易变量可以用来保存插值计算片段的颜色,纹理坐标等信息。
片元着色器
片元着色器计算每个像素的颜色和其它属性,通过应用光照值,凹凸贴图,阴影,镜面高光,半透明等处理来计算像素的颜色并输出。它可以改变像素的深度(z-buffering)或在多个渲染目标被激活的状态下输出多种颜色。一个片元着色器不能产生复杂的效果,因为它只在一个像素上进行操作,而不知道场景的几何形状。片元着色器的主要作用是计算每一个片元最终的颜色值(或者丢弃该片段)。
片元着色器的输入输出模型如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b27u41PR-1632470982583)(OpenGL分享.assets/FragmentShader.png)]
片元着色器的的数据输入主要有三种,统一变量(Uniform)、顶点着色器输入变量(也被称为可变变量varying)、采样器(Sampler)。统一变量的值,在同个OpenGL着色器程序中的顶点着色器和片元着色器中是一致的。片元着色器输入变量在每个像素运算中则一般是不同的,它的值由组成图元的顶点的顶点着色器运算输出的值,根据像素位置进行插值的结果而决定。采样器则是用于从设定好的纹理中,获取纹理的像素颜色的。
在片元着色器之前的阶段,渲染管线都只是在和顶点,图元打交道。在3D图形程序开发中,贴图是最重要的部分,程序可以通过GL命令上传纹理数据至GL内存中,这些纹理可以被片元着色器使用。片元着色器可以根据顶点着色器输出的顶点纹理坐标对纹理进行采样,以计算该片段的颜色值。
另外,片元着色器也是执行光照等高级特效的地方,如可以传给片元着色器一个光源位置和光源颜色,根据公式计算出一个新的颜色值,这样就可以实现光照特效。
着色器语言(Shading Language)
着色器语言是一种高级的图形编程语言,仅适合于GPU编程,其源自于C语言,对于顶点着色器和片元着色器的开发都需要用到着色器语言进行开发,是一种面向过程的语言。
坐标系
OpenGL ES采用的是右手坐标系,选取屏幕中心为原点,从原点到屏幕边缘默认长度为1。向右为X轴正轴方向,向上为Y轴正轴方向,屏幕面垂直向上为Z轴正轴方向。默认情况下,从原点到(1,0,0)的距离与到(0,1,0)的距离在屏幕上展示的并不同。
图形绘制
在OpenGL中,任何复杂的三维模型都是由基本的几何图元:点,线和多边形组成的,所有的图元都是由一系列有顺序的顶点集合来描述的。有了这些图元,就可以建立比较复杂的模型。OpenGL ES2.0的世界里只有点,线,三角形,其它复杂的几何形状都是由三角形构建的,包括正方形,圆形,正方体,球体等。但是更加复杂的物体,就不能使用三角形构建,需要通过加载其他3D建模软件(3DMAX或Maya等)构建的3D模型。
投影
OpenGL ES的世界是3D的,但是手机屏幕能够给我展示的只是一个平面,只不过是在绘制的过程中利用色彩和线条让画面呈现出3D效果。OpenGL ES将这种从3D到2D的转换过程利用投影的方式使计算过程对使用者来说变得相对简单可设置。
OpenGL ES中有两种投影方式:
a. 正交投影,物体大小不会随距离观测点的位置而发生变化
b. 透视投影,距离观测点越远,物体显示越小,反之越大
光照
如果利用直接给出颜色的方式来对3D场景中的物体进行着色渲染,很难使3D场景拥有较强的真实感。一般来说,曲面物体比平面物体更能体现出光照效果,想用数学模型完全模拟真实世界的光照情况使很难的,而OpenGL ES采用的光照模型相对真实世界的光照进行了很大的简化。OpenGL ES2.0中,光照由三种元素组成(三种通道)组成,分别为环境光,镜面光以及散射光。
OpenGL ES工作流程
OpenGL ES2.0的渲染过程为:
读取顶点数据—执行顶点着色器—组装图元—光栅化图元—执行片元着色器—写入帧缓冲区—显示到屏幕
- OpenGL作为本地库直接运行在硬件上,没有虚拟机,也没有垃圾回收或者内存压缩。在Java层定义图像的数据需要能被OpenGL存取,因此需要把内存从Java堆复制到本地堆。
- 顶点着色器是针对每个顶点都会执行的程序,是确定每个顶点的位置。同理片元着色器是针对每个片元都会执行的程序,确定每个片元的颜色。
- 着色器需要进行编译,然后链接到OpenGL程序中。一个OpenGL的程序就是把一个顶点着色器和一个片元着色器链接在一起变成单个对象。
Demo
图形绘制
使用OpenGL绘制图形,主要使用的是Android中的两个基类**GLSurfaceView
和GLSurfaceView.Render
,其中,前者可以看作用来绘制图形的视图容器**,后者是一个渲染类,用于完成该视图中图形绘制和操作的主要逻辑,两者通过OpenGL ES API来创建和操控图形。
-
以三角形的绘制为例,对使用OpenGL绘制图形的流程进行介绍,以及遇到问题的解决方案。步骤如下:
- 在AndroidManifest.xml文件中设置使用OpenGL ES的版本:
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
-
显示图形,需要一个载体,创建显示图形的Activity,利用GLSurfaceView作为显示三角形的View,而图形渲染工作都是在Render(渲染器)中完成的。
-
实现GLSurfaceView.Render,在Render中完成三角形的绘制,具体行为如下:
-
加载顶点和片元着色器
-
确定需要绘制图形的坐标和颜色数据
-
创建program对象,连接顶点和片元着色器,链接program对象
-
设置视图窗口(viewport)
-
将坐标数据以及颜色数据传入OpenGL ES程序中
-
将颜色缓冲区的内容显示到屏幕上
-
三角形绘制核心代码实现
第一步,编写简单的顶点着色器和片元着色器程序
顶点着色器程序:
//根据所设置的顶点数据自动插值后的当前片元的顶点坐标
attribute vec4 vPosition;
void main(){//当前片元最终所处的坐标gl_Position = vPosition;
}
片元着色器程序
//设置float类型默认精度,顶点着色器默认为highp,片元着色器需要我们声明
precision mediump float;
//四维颜色向量,RGBA格式,用于存储用户所设置的三角形颜色,也是用户所设置的颜色数据进行逐片元插值后的值
uniform vec4 vColor;
void main(){//当前片元的最终颜色值gl_FragColor = vColor;
}
其中,gl_Position
和gl_FragColor
都是Shader的内置变量,分别用于顶点位置和片元颜色。与 C 语言类似,main()方法表示入口函数,可以在其上定义函数和变量,然后在main中引用这些变量和函数。定义在函数体以外的叫做全局变量,函数体内定义的为局部变量,变量和函数在使用前必须声明。
着色器中,一般都会声明变量来在程序中使用,但是着色器中还有一些特殊的不需要声明就可以使用的变量,即内置变量。内置变量相当于着色器硬件的输入和输出点,使用者利用这些输入点输入后,就会看到屏幕上的输出,通过输出点可以知道输出的某些数据内容。顶点着色器中和片元着色器中的内置变量是不同的。一下列出一些常用的内置变量和限定符:
顶点着色器的内置变量1.输入变量gl_Position: 顶点坐标gl_PointSize: 点的大小,如果未赋值默认为1,只有在设置绘图为点绘制才有意义。片元着色器的内置变量1.输入变量gl_FragCoord: 当前片元相对窗口位置所处的坐标gl_FragFacing: bool型,表示是否为属于光栅化生成此片元的对应图元的正面2.输出变量gl_FragColor: 当前片元的颜色gl_FragData: vec4类型的数组,向其写入的信息,供渲染管线的后继过程使用GLSL中的限定符与Java限定符类似,放在变量类型之前,并且只能用于全局变量,GLSL中没有默认限定符
GLSL限定符主要有:attribute:一般用于各个顶点各不相同的量,如顶点颜色,坐标等uniform:一般用于对3D物体中所有顶点都相同的量,比如光源位置,统一变换矩阵等varying:表示易变量,一般用于顶点着色器传递到片元着色器的量const:常量
第二步,确定要绘制图形的顶点坐标和颜色
// 数组中坐标的每个顶点对应的坐标数
static final int COORDS_PER_VERTEX = 3;
//顶点数
private final int vertexCount = triangleCoords.length / COORDS_PER_VERTEX;
//顶点在字节缓冲区中的偏移量,每个顶点4个字节
private final int vertexStride = COORDS_PER_VERTEX * 4; //三角形顶点数组
static float triangleCoords[] = {//逆时针顺序,坐标圆心在屏幕中心位置0.0f, 0.622008459f, 0.0f, // 顶点-0.5f, -0.311004243f, 0.0f, // 左下角0.5f, -0.311004243f, 0.0f // 右下角
};//RGBA格式
// 设置颜色,根据三种基色红色R,绿色G,蓝色B的比例设置,最后一个参数为不透明度alpha
float color[] = { 1.0f, 1.0f, 1.0f, 1.0f };
第三步,在Render中实现图形的绘制逻辑。
Render接口有三个方法,分别为:
-
onSurfaceCreated( ),SurfaceView创建时的回调函数,主要用于初始化等操作
在此方法中,创建program对象,连接顶点和片元着色器,链接program对象。
// 将背景设置为灰色
GLES20.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);// 申请底层空间,初始化形状坐标的顶点字节缓冲区
ByteBuffer bb = ByteBuffer.allocateDirect(triangleCoords.length * 4);
// 使用设备硬件的本机字节顺序
bb.order(ByteOrder.nativeOrder());// 从ByteBuffer中创建一个浮点缓冲区,将坐标数据转换为FloatBuffer,用以传入给OpenGL ES程序
vertexBuffer = bb.asFloatBuffer();
// 将坐标添加到FloatBuffer
vertexBuffer.put(triangleCoords);
// 将缓冲区设置为读取第一个坐标
vertexBuffer.position(0); // 在绘制对象构造函数中对着色程序进行编译
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);//创建一个空的OpenGL ES程序,返回新创建程序对象的ID引用
mProgram = GLES20.glCreateProgram();
//将顶点着色器加入到程序
GLES20.glAttachShader(mProgram, vertexShader);
//将片元着色器加入到程序中
GLES20.glAttachShader(mProgram, fragmentShader);
//链接到着色器程序
GLES20.glLinkProgram(mProgram);
要编译着色器程序,在渲染程序类中创建一个编译方法
public static int loadShader(int type, String shaderCode) {//创建顶点着色器对象或片段着色器对象,返回一个非零的引用值int shader = GLES20.glCreateShader(type);//添加源码到着色器程序并对其进行编译GLES20.glShaderSource(shader, shaderCode);GLES20.glCompileShader(shader);return shader;
}
- onSurfaceChanged( ),SurfaceView的宽高变化时的回调函数,主要用于模型视图转换等操作此方法中设置视图窗口。
// 设置视图窗口
GLES20.glViewport(0, 0, width, height);
- onDrawFrame( ),OpenGL渲染每一帧的回调方法,进行实际的绘制操作
//将程序加入到OpenGL ES2.0环境,激活这个着色器程序对象
GLES20.glUseProgram(mProgram);//获取顶点着色器的vPosition成员句柄,也就是要配置的顶点属性对象
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
//以顶点属性位置值作为参考,启用通用顶点属性数组
//默认情况下所有客户端功能都是禁用的,启用后,当调用glDrawArrays或glDrawElements时,通用顶点属性数组中的值将被访问并用于呈现。
GLES20.glEnableVertexAttribArray(mPositionHandle);
//准备三角形的坐标数据,此函数告诉OpenGL该如何解析顶点数据(应用到逐个顶点属性上)
//参数1,指定要修改的通用顶点属性的索引,2,通用顶点属性的组件数量,3,指定数组中每个组件的数据类型
//4,指定定点数据值在被访问时是应该被规范化(GL_TRUE)还是直接转换为定点值(GL_FALSE)
//5,指定连续通用顶点属性之间的字节偏移量。
GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, vertexBuffer);
//获取片元着色器的vColor成员的句柄
mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
//设置绘制三角形的颜色
//参数1,指定要修改的统一变量的位置,2,指定修改元素的数量,3,更新统一变量的数组,4,指针索引
GLES20.glUniform4fv(mColorHandle, 1, color, 0);
//绘制三角形,参数1,绘制的图元类型,2,顶点数组的起始索引,3,打算绘制的顶点数
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
//禁止顶点数组的句柄
GLES20.glDisableVertexAttribArray(mPositionHandle);

可以在这里查看所使用方法的API
OpenGL ES Software Development Kit: https://www.khronos.org/registry/OpenGL-Refpages/es2.0/
相机和投影
至此一个 ‘ 正三角形 ‘ 就已经绘制在屏幕上了,但是发现三角形的显示比例并不是我们所想的正三角形,而且通过旋转屏幕,三角形的比例还会发生变化。这时就需要通过相机视图和投影视图对模型坐标进行转换,使图形可以按照正确的比例进行显示。
-
相机
对于一个场景,随着相机的位置,姿态的不同,拍摄出的画面也是不同的,将相机对应到OpenGL中,决定相机的拍摄结果(最后屏幕上展示的结果)的因素,包括相机位置,观察方向以及UP方向。
- 相机位置:相机在3D空间里面的坐标点。
- 相机观察方向:表示相机镜头的朝向。
- 相机UP方向:可以理解为相机顶端指向的方向,斜着拿相机,拍出的照片就是斜着的。
//OpenGL ES中,可以通过以下方式对相机进行设置 Matrix.setLookAtM (float[] rm, //接收相机变换矩阵int rmOffset, //变换矩阵的起始位置(偏移量)float eyeX,float eyeY, float eyeZ, //相机位置float centerX,float centerY,float centerZ, //观测点位置float upX,float upY,float upZ) //up向量在xyz上的分量
-
投影
将相机看到的3D世界呈现到一个2D平面上,就是投影。投影分为正交投影和透视投影。
- 正交投影,物体呈现出的大小不会随着其距离视点的远近发生变化。
//OpenGL ES中,正交投影的设置 Matrix.orthoM (float[] m, //接收正交投影的变换矩阵int mOffset, //变换矩阵的起始位置(偏移量)float left, //相对观察点近面的左边距float right, //相对观察点近面的右边距float bottom, //相对观察点近面的下边距float top, //相对观察点近面的上边距float near, //相对观察点近面距离float