OpenGL基础知识介绍和简单使用

本文介绍了OpenGL基础知识,涵盖上下文、渲染、顶点数组、着色器、纹理、相机与投影等,重点讲解了Android OpenGLES在移动开发中的应用,如绘制三角形、正方形、立方体和3D模型加载,以及关键概念如深度测试和混合。

OpenGL简介

OpenGL (全写Open Graphics Library)是指定义了一个跨编程语言、跨平台的编程接口规格的专业的图形程序接口。它用于三维图像(二维的亦可),是一个功能强大,调用方便的底层图形库。OpenGL在不同的平台上有不同的实现,但是它定义好了专业的程序接口,不同的平台都是遵照该接口来进行实现的,思想完全相同,方法名也是一致的,所以使用时也基本一致,只需要根据不同的语言环境稍有不同而已。OpenGL这套3D图形API从1992年发布的1.0版本到目前最新2014年发布的4.5版本,在众多平台上多有着广泛的使用

OpenGL 专业词解析

1.OpenGL上下文[context]

(1),在应用程序调用任何OpenGL指令之前,需要首先创建一个OpenGL的上下 文,这个上下文是一个非常庞大的状态机,保存了OpenGL中的各种状态,也是OpenGL指令执行的基础;
(2),由于OpanGL上下文是一个巨大的状态机,切换上下文往往会产生较大的开销,但是不同绘制模块可能需要使用完全独立的状态管理,因此,可以在应用程序中分别创建多个不同的上下文,在不同的线程中使用不同的上下文,上下文之间共享纹理,缓冲区等资源,这样的方案,会比反复切换上下文或者大量修改渲染状态更加合理高效

2.渲染

OpenGL渲染的原理是将输入的3d坐标的顶点数据结合纹理信息,和各种渲染状态绘制成屏幕上的2D像素片段的过程。如图
在这里插入图片描述

3.顶点数组和顶点缓冲区

画图一般是先画好骨架,然后再往骨架里面填充颜色,这对与OpenGL也是一样的,顶点数据就是要画的图像的骨架,和现实中不同的是:OpenGL中的图像都是由图元组成的,在OpenGL ES中,有3种类型的图元:点,线,三角形,在那些顶点数据最终存储在哪里呢?开发者可以选择设定的函数指针,再调用绘制方法的时候,直接由内存传入顶点数据,也就是说这部分数据之前是存储在内存当中,被称为顶点数组,而性能更高的做法是提前分配一块显存,将顶点数据预先传入当中,这部分显存,就被成为顶点缓冲区。顶点指的是我们在绘制一个图形时,它的顶点位置数据,而这个数据可以直接存储在数组中或者将其缓存到GPU内存中。

4.着色器程序Shader

OpenGL在处理shader时,和其他编译器一样,通过编译,链接等步骤,生成了着色器程序(glProgram),着色器程序同时包含顶点着色器和片段着色器的运算逻辑,在OpenGL进行绘制的时候,首先有顶点着色器对传入的顶点数据进行运算,再通过图元装配,将顶点装换为图元,然后进行光栅化,将图元这种矢量图形,转化为删格化数据,最后,将删格化数据传入片段着色器进行运算,片段着色器会对删格化数据中的每一个像素进行运算,并决定像素的颜色;

5.顶点着色器(VertexShader)

一般用来处理图形每个顶点变化【旋转、平移、投影等】;
顶点着色器是OpenGL中用于计算顶点属性的程序,顶点着色器是逐顶点运算的程序,也就是说每个顶点数据都会执行一次顶点着色器,当然这是并行的,并且顶点着色器运行中无法访问其他顶点的数据
一般来说典型的需要计算的顶点属性主要包括坐标变换,逐顶点光照运算等待,顶点坐标由自身坐标装换到归一化坐标系的运算,就是在这里发生的
![在这里插入图片描述](https://img-blog.csdnimg.cn/2e075c964aca49d999dcf6b369519135.png

6.几何着色器(GeometryShader)

在顶点和片段着色器之间有一个可选的几何着色器(Geometry Shader),几何着色器的输入是一个图元(如点或三角形)的一组顶点。几何着色器可以在顶点发送到下一着色器阶段之前对它们随意变换。(爆炸效果)

7.片元/片段/像素着色器(FragmentShader)

一般用来处理图形中每个像素点颜色和填充;
片元着色器是OpenGL中用于计算片段(像素)颜色的程序,片段着色器逐像素运算的程序,也就是说每个像素都会执行一次片段着色器,且是并行执行
在这里插入图片描述

8.光栅化

是把顶点数据转换为片元的过程,具有将图转化为一个个栅格组成的图像的作用,特点是每个元素对应的缓冲区的一个像素;
光栅化就是把顶点数据转换为片元的过程,片元中的每个元素对应于帧缓冲区的一个像素;
光栅化其实是一种将几何图元变成二维图像的过程,该过程包含了两部分:
1.决定窗口坐标中的哪些整型删格化区域被基本图元占用,
2.分配一个颜色值和一个深度值到各个区域,光栅化的过程产生的是片元;
把物体的数学描述以及与物体相关的颜色信息转换为屏幕上用于对应位置的像素以及用于填充像素的颜色,这个过程称为光栅化,这是一个将模拟信号转化为离散信号的过程;
粗略地讲:你模型的那些顶点在经过各种矩阵变换后也仅仅是顶点。而由顶点构成的三角形要在屏幕上显示出来,除了需要三个顶点的信息以外,还需要确定构成这个三角形的所有像素的信息
在这里插入图片描述

9.纹理

为了能够把图片纹理(Bitmap)映射到三角形上,我们需要指定三角形的每个顶点各自对应纹理的哪个部分。这样每个顶点就会关联着一个纹理坐标(Texture Coordinate),用来标明该从纹理图像的哪个部分采样(译注:采集片段颜色)。之后在图形的其它片段上进行片段插值(Fragment Interpolation)。
纹理坐标在x和y轴上,范围为0到1之间(注意我们使用的是2D纹理图像)。纹理坐标用浮点数来表示,范围一般从0.0到1.0,左上角坐标为(0.0,0.0),右上角坐标为(1.0,0.0),左下角坐标为(0.0,1.0),右下角坐标为(1.0,1.0),如下图所示:
在这里插入图片描述
左图为纹理图和纹理坐标,右图为顶点图和顶点坐标。
将纹理映射到右边的两个三角形上(也就是一个矩形),需要将纹理坐标指定到正确的顶点上,才能使纹理正确的显示,否则显示出来的纹理会无法显示,或者出现旋转、翻转、错位等情况。
将右图顶点按照V2V1V4V3传入,以三角形条带方式绘制,则纹理坐标应按照V2V1V4V3传入。如果按照V3V4V1V2传入,会得到一个旋转了180度的纹理。如果按照V4V3V2V1传入,则会得到一个左右翻转的纹理

10.深度测试

(1)什么是深度?
深度其实就是该象素点在3d世界中距离摄象机的距离(绘制坐标),深度缓存中存储着每个象素点(绘制在屏幕上的)的深度值!
深度值(Z值)越大,则离摄像机越远。
深度值是存储在深度缓存里面的,我们用深度缓存的位数来衡量深度缓存的精度。深度缓存位数越高,则精确度越高,目前的显卡一般都可支持16位的Z Buffer,一些高级的显卡已经可以支持32位的Z Buffer,但一般用24位Z Buffer就已经足够了。
(2)为什么需要深度?
在不使用深度测试的时候,如果我们先绘制一个距离较近的物体,再绘制距离较远的物体,则距离远的物体因为后绘制,会把距离近的物体覆盖掉,这样的效果并不是我们所希望的。而有了深度缓冲以后,绘制物体的顺序就不那么重要了,都能按照远近(Z值)正常显示,这很关键。
实际上,只要存在深度缓冲区,无论是否启用深度测试,OpenGL在像素被绘制时都会尝试将深度数据写入到缓冲区内,除非调用了glDepthMask(GL_FALSE)来禁止写入。这些深度数据除了用于常规的测试外,还可以有一些有趣的用途,比如绘制阴影等等。
(3)启用深度测试
使用 glEnable(GL_DEPTH_TEST);
在默认情况是将需要绘制的新像素的z值与深度缓冲区中对应位置的z值进行比较,如果比深度缓存中的值小,那么用新像素的颜色值更新帧缓存中对应像素的颜色值。
但是可以使用glDepthFunc(func)来对这种默认测试方式进行修改。
其中参数func的值可以为GL_NEVER(没有处理)、GL_ALWAYS(处理所有)、GL_LESS(小于)、GL_LEQUAL(小于等于)、GL_EQUAL(等于)、GL_GEQUAL(大于等于)、GL_GREATER(大于)或GL_NOTEQUAL(不等于),其中默认值是GL_LESS。
一般来将,使用glDepthFunc(GL_LEQUAL);来表达一般物体之间的遮挡关系。
(4)启用了深度测试,那么这就不适用于同时绘制不透明物体。

11.混合

在测试阶段之后,如果像素依然没有被剔除,那么像素的颜色将会和帧缓冲区中颜色附着上颜色进行混合,混合的算法可以通过OpenGL的函数进行指定,但是OpenGL提供的混合算法有限,如果需要更加复杂的混合算法,一般可以通过像素着色器进行实现,当然性能会比原生的混合算法要差一些。混合是实现物体透明度的一种技术。就是说一个物体的颜色是本身的颜色和它背后其它物体的颜色的不同强度混合

12.GLSL

着色器是使用一种叫GLSL的类C语言写成的。GLSL是为图形计算量身定制的,它包含一些针对向量和矩阵操作的有用特性。着色器的开头总是要声明版本,接着是输入和输出变量、uniform和main函数。每个着色器的入口点都是main函数,在这个函数中我们处理所有的输入变量,并将结果输出到输出变量中。
GLSL中的数据类型主要分为标量、向量、矩阵、采样器、结构体、数组、空类型七种类型:

  1. 标量:标量表示的是只有大小没有方向的量,在GLSL中标量只有bool、int和float三种。对于int,和C一样,可以写为十进制(16)、八进制(020)或者十六进制(0x10)对于标量的运算,我们最需要注意的是精度,防止溢出问题
  2. 向量:向量我们可以看做是数组,在GLSL通常用于储存颜色、坐标等数据,针对维数,可分为二维、三维和四位向量。针对存储的标量类型,可以分为bool、int和float。共有vec2、vec3、vec4,ivec2、ivec3、ivec4、bvec2、bvec3和bvec4九种类型,数组代表维数、i表示int类型、b表示bool类型。
    作为颜色向量时,用rgba表示分量,就如同取数组的中具体数据的索引值。三维颜色向量就用rgb表示分量。比如对于颜色向量vec4 color,color[0]和color.r都表示color向量的第一个值,也就是红色的分量。其他相同。
    作为位置向量时,用xyzw表示分量,xyz分别表示xyz坐标,w表示向量的模。三维坐标向量为xyz表示分量,二维向量为xy表示分量。
    作为纹理向量时,用stpq表示分量,三维用stp表示分量,二维用st表示分量。
  3. 矩阵:在GLSL中矩阵拥有22、33、4*4三种类型的矩阵,分别用mat2、mat3、mat4表示。我们可以把矩阵看做是一个二维数组,也可以用二维数组下表的方式取里面具体位置的值
  4. 采样器:采样器是专门用来对纹理进行采样工作的,在GLSL中一般来说,一个采样器变量表示一副或者一套纹理贴图。所谓的纹理贴图可以理解为我们看到的物体上的皮肤
  5. 结构体:和C语言中的结构体相同,用struct来定义结构体,关于结构体参考C语言中的结构体。
  6. 数组:数组知识也和C中相同,不同的是数组声明时可以不指定大小,但是建议在不必要的情况下,还是指定大小的好
  7. 空类型:空类型用void表示,仅用来声明不返回任何值得函数
    限定符
    attribute :一般用于各个顶点各不相同的量。如顶点颜色、坐标等。
    uniform:一般用于对于3D物体中所有顶点都相同的量。比如统一颜色,统一变换矩阵等。
    varying:表示易变量,一般用于顶点着色器传递到片元着色器的量。
    const:常量。
    浮点精度
    与顶点着色器不同的是,在片元着色器中使用浮点型时,必须指定浮点类型的精度,否则编译会报错。精度有三种,分别为:
    lowp:低精度。8位。
    mediump:中精度。10位。
    highp:高精度。16位。
    内建变量
    顶点着色器的内建变量
    输入变量:
    gl_Position:顶点坐标
    gl_PointSize:点的大小,没有赋值则为默认值1,通常设置绘图为点绘制才有意义。
    片元着色器的内建变量
    输入变量:
    gl_FragCoord:当前片元相对窗口位置所处的坐标。
    gl_FragFacing:bool型,表示是否为属于光栅化生成此片元的对应图元的正面。
    输出变量
    gl_FragColor:当前片元颜色
    gl_FragData:vec4类型的数组。向其写入的信息,供渲染管线的后继过程使用。

13.相机

根据现实生活中的经历我们知道,对一个场景,随着相机的位置、拍摄出来的画面也是不相同。将相机对应于OpenGL的世界,决定相机拍摄的结果(也就是最后屏幕上展示的结果),包括相机位置、相机观察方向以及相机的UP方向。

相机位置:相机的位置是比较好理解的,就是相机在3D空间里面的坐标点。
相机观察方向:相机的观察方向,表示的是相机镜头的朝向,你可以朝前拍、朝后拍、也可以朝左朝右,或者其他的方向。
相机UP方向:相机的UP方向,可以理解为相机顶端指向的方向。比如你把相机斜着拿着,拍出来的照片就是斜着的,你倒着拿着,拍出来的就是倒着的。
Android 设置相机位置的方法:

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上的分量

14.投影

OpenGL 的世界是3D的,但是手机屏幕能够给我展示的终究是一个平面,只不过是在绘制的过程中利用色彩和线条让画面呈现出3D的效果。OpenGL 将这种从3D到2D的转换过程利用投影的方式使计算相对使用者来说变得简单可设置。
OpenGL 中有两种投影方式:即透视投影(perspective projection)和正交投影( orthographic projection)。
1.透视投影的投影线相交于一点,因此投影的结果与原物体的实际大小并不一致,而是会近大远小。因此透视投影更接近于真实世界的投影方式。
2.正交投影是平行投影的一种特殊情形,正交投影的投影线垂直于观察平面。平行投影的投影线相互平行,投影的结果与原物体的大小相等,因此广泛地应用于工程制图等方面。

在这里插入图片描述
Android 设置透视投影的方法:

Matrix.frustumM (float[] m,         //接收透视投影的变换矩阵
                int mOffset,        //变换矩阵的起始位置(偏移量)
                float left,         //相对观察点近面的左边距
                float right,        //相对观察点近面的右边距
                float bottom,       //相对观察点近面的下边距
                float top,          //相对观察点近面的上边距
                float near,         //相对观察点近面距离
                float far)          //相对观察点远面距离

Android 设置正交投影的方法:

Matrix.orthoM (float[] m,           //接收正交投影的变换矩阵
                int mOffset,        //变换矩阵的起始位置(偏移量)
                float left,         //相对观察点近面的左边距
                float right,        //相对观察点近面的右边距
                float bottom,       //相对观察点近面的下边距
                float top,          //相对观察点近面的上边距
                float near,         //相对观察点近面距离
                float far)          //相对观察点远面距离

Android 矩阵相乘的方法:

Matrix.multiplyMM (float[] result, //接收相乘结果
                int resultOffset,  //接收矩阵的起始位置(偏移量)
                float[] lhs,       //左矩阵
                int lhsOffset,     //左矩阵的起始位置(偏移量)
                float[] rhs,       //右矩阵
                int rhsOffset)     //右矩阵的起始位置(偏移量)

OpenGL 和 OpenGl ES 的区别

1.OpenGL ES和OpenGL 的关系

  1. OpenGL ES 是OpenGL 的子集,针对手机、PDA和游戏主机嵌入式设备而设计
  2. OpenGL ES 是从OpenGL 裁剪定制而来的,去除了glBegin/glEnd,四边形(GL_QUADS)、多边形(GL_POLYGONS)等复杂图元等许多非绝对必要的特性,剩下最核心有用的部分。可以理解成是一个在移动平台上能够支持OpenGL 最基本功能的精简规范。

3.OpenGL ES可以做什么

  1. 图片处理。比如图片色调转换、美颜等。
  2. 摄像头预览效果处理。比如美颜相机、恶搞相机等。
  3. 视频处理。摄像头预览效果处理可以,这个自然也不在话下了。
  4. 3D游戏。3D动画等.

4.OpenGL ES版本及Android支持情况

  1. OpenGL ES1.0是基于OpenGL 1.3的,OpenGL ES1.1是基于OpenGL 1.5的。Android 1.0和更高的版本支持这个API规范。OpenGL ES 1.x是针对固定硬件管线的。
  2. OpenGL ES2.0是基于OpenGL 2.0的,不兼容OpenGL ES 1.x。Android 2.2(API 8)和更高的版本支持这个API规范。OpenGL ES 2.x是针对可编程硬件管线的。
  3. OpenGL ES3.0的技术特性几乎完全来自OpenGL 3.x的,向下兼容OpenGL ES 2.x。Android 4.3(API 18)及更高的版本支持这个API规范。
  4. OpenGL ES3.1基本上可以属于OpenGL 4.x的子集,向下兼容OpenGL ES3.0/2.0。Android 5.0(API 21)和更高的版本支持这个API规范。
    在这里插入图片描述
    在这里插入图片描述

Android OpenGl ES 简单使用

点、线、三角形是OpenGL ES世界的图形基础。无论多么复杂的几何物体,在OpenGL ES的世界里都可以用三角形拼成
在这里插入图片描述

绘制一个简单的三角形

  1. 在AndroidManifest.xml文件中设置使用的OpenGL ES的版本:
 <uses-feature android:glEsVersion="0x00020000" android:required="true" />
 // 3.0的版本为0x00030000,3.1的版本为0x00030001。
  1. 创建一个GLSurfaceView用来显示图形,可以自定义继承GLSurfaceView 或者直接使用GLSurfaceView。设置OpenGL ES 2.0 版本和Render
public class OneGlSurfaceView extends GLSurfaceView {
   
   
    private final OneGlRenderer oneGlRenderer;

    public OneGlSurfaceView(Context context) {
   
   
        super (context);
        // 设置EGLContext客户端使用OpenGL ES 2.0 版本
        setEGLContextClientVersion (2);
        oneGlRenderer = new OneGlRenderer (context);
        setRenderer (oneGlRenderer);
    }
}
  1. 创建一个Render,可以自定义实现GLSurfaceView.Renderer接口。图形的具体渲染工作都是在Render中完成的。
public class OneGlRenderer1 implements GLSurfaceView.Renderer {
   
   
    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
   
   
      // 创建
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
   
   
      // 设置大小和位置
    
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值