OpenGL ES入门(使用指南)

opengl es 同时被 2 个专栏收录
1 篇文章 0 订阅
1 篇文章 0 订阅

转载地址:https://www.ict528.com/wpozv3sz3srrtywpoq1qvuyqooqxz1usvwr2uqoo.html。

OpenGL ES 入门 一、前言 OpenGL ES 是 Khronos Group 创 建 的 一 系 列 API 中 的 一 种 ( 官 方 组 织 是 : http://www.khronos.org/) 。

在桌面计算机上有两套标准的 3DAPI : Direct3D 和 OpenGL。

Direct3D 实际上是运行在 windows 操作系统上的标准 3DAPI,而 OpenGL 则是跨平台的, 适用于 Linux、多种 UNIX、MAC OS X 和 windows。

由于 OpenGL 得到了广范围的认可, 所以,基于嵌入式的 3DAPI—OpenGL ES 也就应运而生。

沃 Phone 使用的芯片高通 7227,它能很好的对 OpenGL ES 的支持,了解 OpenGL ES 的种种特性,不仅能开发出很好的适用于沃 Phone 的 3D 游戏、3D 应用等。

借助于 OpenGL ES 的平台无关性,只要稍微修改 EGL,理论上就可以将开发的 3D 游戏、3D 应用移植到任 何支持 OpenGL ES 的平台上去。

本篇文档就从零开始,深入简出,跟大家介绍一下 OpenGL ES 的原理和开发。

OpenGL ES 简介 什么是 OpenGL ES OpenGL ES 是一套适用于手持嵌入式设备的 3DAPI。

比如手机、PDA、汽车、航空等等上 面都可以使用到 OpenGL ES。

OpenGL ES 是免授权费的、跨平台的、功能完善的 2D 和 3D 图形应用程序接口 API,它是桌面 OpenGL 的子集,是从 OpenGL 裁剪定制而来的。

由于手 持设备的相关局限性,OpenGL ES 相对于 OpenGL 不可避免的进行了相关的精简。

去除了 OpenGL 中比如 glBegin/glEnd,四边形(GL_QUADS)、多边形(GL_POLYGONS)等复杂图元 等许多非绝对必要的特性。

但是 OpenGL 方面的很多知识,OpenGL ES 都是可以借鉴的。

OpenGL ES 其实是一个状态机(State machine),它保存一种状态直至其改变。

每个状态都有 本身默认的缺省值,可以通过相关的查询和设置函数进行相关的查询和设置。

大多数的 OpenGL ES 的应用都采用的是相同的操作顺序,这一系列的处理阶段被称作 OpenGL ES 的渲染管线(pipeline)。

二、OpenGL ES 的处理流程 2.1 固定渲染管线流程: 1) Primitive Processing: 这一步是图元运算过程,所谓图元,其实就是一个点集。

在 OpenGL ES 中,所有的物体, 几何元素最终都是以顶点的形式表述的。

一般来说,这些顶点将会产生三角形、直线或点。

它做的工作就是将顶点给顶点处理器进行处理。

顶点的数据包括顶点的位置 (空间坐 标)、大小、颜色、顶点的法向量(用于光照计算)、纹理坐标(可能有多个)等等。

  1. Transform and Lighting: 这一步是转换和光照过程。

其中 Transform 是通过模型、视图、投影变换矩阵,将所有的顶 点坐标变换成人眼坐标系下的一致坐标。

变换矩阵同样会改变物体的顶点法向量。

如果激活 了纹理,还可以进行纹理坐标转换,以及自动纹理坐标的生产。

Lighting 处理的就是光照部 分,它会利用光源、材质、转换后的顶点位置和法向量计算每个顶点的颜色值。

  1. Primitive Assembly: 图元装配过程。

管线中这个流程是对所有的点数据进行点线面等基础图元的组装。

这个过程 会对所有的图元进行剪切和筛选。

对于不在视区空间中的部分进行剪切, 对于不可见的面进 行筛选。

  1. Resterizar:

光栅化。

光栅化的过程就是对所有的经过 Primitive Assembly 图元转换成屏幕上可以显示的 二维 Fragment(片元)。

片元和将要显示的像素一一对应。

  1. Texture Environment: 纹理处理。

利用纹理坐标来进行纹理的相关处理。

  1. Colour Sum: 颜色叠加。

根据纹理颜色等相关属性确定最终的顶点颜色。

  1. Fog: 雾。

雾化处理。

  1. Alpha Test: Alpha 测试。

判断某些片元是否抛弃。

比如可以规定 Alpha 小于 0.2 的片元就需要抛弃。

  1. Depth Stencil: 深度测试和模板测试。

深度测试需要一个深度缓冲区,是在后面的会被在前的遮盖,需要抛 弃。

模板测试需要一个模板缓冲区,也就是模板缓冲区中为每个像素保存了一个“模板值” , 当像素需要进行模板测试时,将设定的模板参考值与该像素的“模板值”进行比较,符合条 件的通过测试,不符合条件的则被丢弃,不进行绘制。

条件的设置与 Alpha 测试中的条件设 置相似。

但注意 Alpha 测试中是用浮点数来进行比较,而模板测试则是用整数来进行比较。

  1. Color Buffer blend: 跟颜色缓冲区进行混合。

最终生成的片元颜色需要跟颜色缓冲区中本来的进行混合(也可以 理解成为跟背景混合),以生成最终的颜色。

  1. Dither: 抖动。

在可用颜色数量较少的系统中,可能需要对颜色值进行抖动,在适当损失颜色质量的 情况下增加可使用的颜色数量。

  1. Frame Buffer: 最终结果就写进了 Frame Buffer。

一个流程就算结束了。

2.2 可编程渲染管线流程 对比固定渲染管线流程图和可编程渲染管线流程图, 可以看出来, 大部分的流程都是一样的, 只是可编程将固定中的几个功能合并了,新增了两个新的流程,Vertex Shader 和 Fragment Shader。

也就是着色器(shader) 作为 OpenGL ES2.0 的一部分,着色器允许应用程序显式地指定处理顶点和片断时候所执行 的操作。

Shader language 简介: 编写 OpenGL ES 程序使用的着色器类似于使用基于编译器的语言 (比如 C 语言) 编写程序。

我们需要用编译器来分析程序,检查它所存在的错误,并把它转换成为目标代码。

接着,在 链接阶段,链接器把一组目标文件组合在一起,形成一个可执行的程序。

着色器的创建流程 如下: 对于每个着色器对象: 1. 创建一个着色器对象。

  1. 把着色器源代码编译为目标代码。

  2. 验证这个着色器已经成功通过编译。

然后,为了把多个着色器对象链接到一个着色器程序中,需要: 1. 创建一个着色器程序。

  1. 把适当的着色器对象连接到这个着色器程序中。

  2. 链接着色器程序。

  3. 验证着色器程序链接成功。

  4. 使用着色器进行顶点或片段处理。

  1. Vertex Shader: 包含了固定渲染管线中的 Transform and lighting 的所有操作。

  2. Fragment Shader: 包含了固定渲染管线中的纹理处理、颜色叠加、雾、alpha 测试等内容。

具体着色器语言和两个着色器的使用,可见后续的 sample 分析。

三、EGL 简介 OpenGL 实现跨平台的功能,在不同的操作系统上需要不同的类似适配层的内容,比如在 Windows 操作系统上需要 WGL。

同样的,OpenGL ES 是一个平台中立的图形库,在它能够 工作前,需要与一个实际的窗口关联起来,但是,与 OpenGL 不一样的是,OpenGL 是每个 窗口系统需要一个与之对应的适配层,Windows 需要 WGL,X-Window 需要 xgl,Mac OS 需要 agl。

而 OpenGL ES 的这层,是统一的一个标准。

这个标准就是 EGL。

(一) 初识 EGL EGL 是介于 RenderAPI(比如 OpenGL ES 和 OpenVG)和本地基础系统的一套接口。

里面涉及 了 OpenGL ES 和 OpenVG 的一些相关描述, 所以需要和 OpenGL ES 和 OpenVG 文档一起阅 读。

EGL 使用 OpenGLES 的命名习惯来命名函数入口和宏定义。

具体的接口和相关宏定义 可以参见 egl.h。

(二) EGL 的使用 1. 获取 Display: Display 代表的是显示器,有的系统上有多个显示器,也就会有多个 display。

获得 Display 需 要 调 用 EGLDisplay eglGetDisplay(EGLNativeDisplayType display_id); , 参 数 一 般 为 EGL_DEFAULT_DISPLAY。

该参数的实际意义是平台相关的,比如在 windows 平台上,一 般返回的就是 DC。

沃 Phone 上就是 TDC。

  1. 初始化 egl: 获得了 Display 后,调用 EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor);该函数会进行一些相关的内部初始化工作。

我们可以通过这个函数获得 egl 的版本 号。

  1. 选择 Config: Config 实际就是 FrameBuffer 的参数,在 Windows 下对应于 PixelFormat,在 X-Window 下 对 应 Visual 。

可 以 用 函 数 EGLBoolean eglChooseConfig(EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config);, 其中 attrib_list 是 以 EGL_NONE 结束的参数数组,通常以 id,value 依次存放,对于个别标识性的属性可以只 有 id,没有 value。

另一个办法是用 EGLBoolean eglGetConfigs(EGLDisplay dpy, EGLConfig *configs, EGLint config_size, EGLint *num_config); 来获得所有 config。

这两个函数都会返回 不多于 config_size 个 Config, 结果保存在 configs[]中, 系统的总 Config 个数保存在 num_config 中。

可以利用 eglGetConfig()中间两个参数为 0 来查询系统支持的 Config 总个数。

Config 有 众多的 Attribute,这些 Attribute 决定 FrameBuffer 的格式和能力,通过 eglGetConfigAttrib () 来读取,但不能修改。

  1. 构造 Surface: 有了 Config,就可以开始构造 Surface 了。

Surface 实际上就是一个 FrameBuffer。

通过函数 EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config,EGLNativeWindowType win, const EGLint *attrib_list)可以创建一个 Surface。

系统通常

还支持另外两种 Surface:PixmapSurface 和 PBufferSurface,这两种都不是可显示的 Surface, PixmapSurface 是保存在系统内存中的位图,PBuffer 则是保存在显存中的帧。

Surface 也有 一 些 attribute , 基 本 上 都 可 以 故 名 思 意 , EGL_HEIGHT EGL_WIDTH EGL_LARGEST_PBUFFER EGL_TEXTURE_FORMAT EGL_TEXTURE_TARGET EGL_MIPMAP_TEXTURE EGL_MIPMAP_LEVEL , 通 过 eglSurfaceAttrib() 设 置 、 eglQuerySurface()读取。

  1. 创建 Context: OpenGL ES 的 pipeline 从程序的角度看就是一个状态机,有当前的颜色、纹理坐标、变换矩 阵、 渲染模式等一大堆状态, 这些状态作用于程序提交的顶点坐标等图元从而形成帧缓冲内 的像素。

在 OpenGL ES 的编程接口中,Context 就代表这个状态机,程序的主要工作就是向 Context 提 供 图 元 、 设 置 状 态 , 偶 尔 也 从 Context 里 获 取 一 些 信 息 。

用 EGLContext eglCreateContext(EGLDisplay dpy, EGLSurface write, EGLSurface read, EGLContext * share_list)来创建一个 Context。

  1. 绘制: 应用程序通过 OpenGL API 进行绘制,一帧完成之后,调用 eglSwapBuffers(EGLDisplay dpy, EGLContext ctx)来显示。

OpenGL ES 的 HelloWorld [cpp] view plain copy 在 CODE 上查看代码片派生到我的代码片 // 头文件包含 #include “TG3.h” #include #include #include “OGLES2Hello********.h” // 宏定义 #define VERTEX_ARRAY 0 // 定义 Display、config、surface、context EGLDisplay eglDisplay = 0; EGLConfig eglConfig = 0; EGLSurface eglSurface = 0; EGLContext eglContext = 0; EGLNativeWindowType eglWindow = 0; // 沃 Phone 窗口指针 extern TWindow *g_pThis; bool TestEGLError() { //eglGetError 返回上一个 egl 中的错误, 用户在每个 egl 函数调用结束都需要调用这个函 数。

EGLint iErr = eglGetError(); if (iErr != EGL_SUCCESS)

{ return false; } return true; } bool CreateEGLContext() { // 第一步:获得或者创建一个可以用于 OpenGL ES 输出的 EGLNativeWindowType eglWindow = (EGLNativeWindowType)g_pThis; // 第 二 步 : 获 得 默 认 的 Display 。

通 常 我 们 只 有 一 块 屏 幕 , 参 数 传 EGL_DEFAULT_DISPLAY 就可以了。

eglDisplay = eglGetDisplay((EGLNativeDisplayType) EGL_DEFAULT_DISPLAY); //第三步:初始化 EGL,如果我们不想要版本号,后两个参数也可以传 NULL 进去。

EGLint iMajorVersion, iMinorVersion; if (!eglInitialize(eglDisplay, &iMajorVersion, &iMinorVersion)) { return false; } //第四步:指定需要的配置属性,一个 EGL 的配置描述了 Surfaces 上像素的格式信息。

当前我们要的是 Windows 的 surface,在屏幕上是可见的,以 EGL_NONE 结尾。

const EGLint pi32ConfigAttribs[] = { EGL_LEVEL, 0, EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_NATIVE_RENDERABLE, EGL_FALSE, EGL_DEPTH_SIZE, EGL_DONT_CARE, EGL_NONE }; //第五步:寻找一个符合所有要求的配置,我们需要的只是其中一个,所以可以限制 config 的个数为 1。

int iConfigs; if (!eglChooseConfig(eglDisplay, pi32ConfigAttribs, &eglConfig, 1, &iConfigs) || (iConfigs != 1)) { return false; }

//第六步:创建一个可画的 surface。

这里创建时可见的 windows surface。

Pixmaps 和 pbuffers 都是不可见的。

eglSurface = eglCreateWindowSurface(eglDisplay, eglConfig, eglWindow, NULL); if(eglSurface == EGL_NO_SURFACE) { eglGetError(); // Clear error eglSurface = eglCreateWindowSurface(eglDisplay, eglConfig, NULL, NULL); } if (!TestEGLError()) { return false; } //第七步:创建 Context。

我们 OpenGL ES 的资源,比如纹理只有在这个 context 里是可 见的。

// 绑定 API (可以是 OpenGLES 或者 OpenVG) eglBindAPI(EGL_OPENGL_ES_API); EGLint ai32ContextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; eglContext = eglCreateContext(eglDisplay, eglConfig, NULL, ai32ContextAttribs); if (!TestEGLError()) { return false; } //第八步:将创建的 context 绑定到当前的线程,使用我们创建的 surface 进行读和写。

Context 绑定到线程上,你就可以不用担心其他的进程影响你的 OpenGL ES 应用程序。

eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext); if (!TestEGLError()) { return false; } return true; } bool Render() { //第九步:使用 OpenGL ES API 画一些东西。

到这里,所有的东西都已准备就绪,我们 做好了往屏幕上画东西的准备。

  • 0
    点赞
  • 0
    评论
  • 3
    收藏
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 博客之星2020 设计师:CY__ 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值