OpenGL ES(OpenGL for Embedded Systems)是 OpenGL 三维图形API的子集,针对手机、PDA和游戏主机等嵌入式设备而设计。该API由Khronos集团定义推广,Khronos是一个图形软硬件行业协会,该协会主要关注图形和多媒体方面的开放标准。

go 的 golang.org/x/mobile/gl 这个包 是基于OpenGL ES 2了, 文档在: https://godoc.org/golang.org/x/mobile/gl

Khronos的api文档在https://www.khronos.org/opengles/sdk/docs/man/

以 gomobile 的例子中的 basic 例子(源码在:https://github.com/golang/mobile/tree/master/example)为例,它的执行效果如下,

注意这是在不同长宽比下 mac 下执行效果截图, 放大或者缩小窗口大小,这个三角形会按照比率做自动变化。

 

源码分析如下:

三角形坐标定义:

Vertex (顶点)

顶点是3D建模时用到的最小构成元素,顶点定义为两条或是多条边交会的地方。在3D模型中一个顶点可以为多条边,面或是多边形所共享。一个顶点也可以代表一个点光源或是Camera的位置。

下图定义了三角形的三个顶点和对应的代码实现:

这里把float的坐标转换成 byte数组,它将会传入OpenGl ES的图形处理流程中。

这里定义的不是固定像素尺寸,而是相对屏幕大小的尺寸(屏幕大小是2*2,具体原因后面解释)。

使用 OpenGL 画图形需要定义的项

使用OpenGL ES 2.0画一个定义好的形状需要较多代码,因为你需要提供很多图形渲染流程的细节。具体而言,你必须定义如下几项:

  • 顶点着色器(Vertex Shader):用来渲染形状顶点的OpenGL ES代码。
  • 片段着色器(Fragment Shader):使用颜色或纹理渲染形状表面的OpenGL ES代码。
  • 程式(Program):一个OpenGL ES对象,包含了你希望用来绘制一个或更多图形所要用到的着色器。

你需要至少一个顶点着色器来绘制一个形状,以及一个片段着色器为该形状上色。这些着色器必须被编译然后添加到一个OpenGL ES Program当中,并利用它来绘制形状。

程式(Program)

为了绘制你的图形,你必须编译着色器代码,将它们添加至一个OpenGL ES Program对象中,然后执行链接。在你的绘制对象时,上述步骤就只执行一次。

这里的顶点着色器和片段着色器的具体含义后面分析。

着色器包含了OpenGL Shading Language(GLSL)代码,它必须先被编译然后才能在OpenGL环境中使用。要编译这些代码,需要在你的渲染器类中创建一个辅助方法,我们可以看到 gomobile 帮我们把这个方法封装在CreateProgram 中了 :

下面代码是 golang.org/x/mobile/exp/gl/glutil 的代码。

Note:编译OpenGL ES着色器及链接操作对于CPU周期和处理时间而言,消耗是巨大的,所以你应该避免重复执行这些事情。你应该在构建你的应用时,确保它们只被创建了一次,并且缓存以备后续使用。

传值给OpenGL

尽管我们已经有了数据,OpenGL并不能直接使用它们。OpenGL对它能读取的内存有些限制。你可以按需分配你的顶点数据,但是这些内存对OpenGL并不直接可见。因此,第一步就是分配OpenGL可见的内存,并填充我们的数据。这是通过缓存对象(buffer object,以下简称BO)来实现的。
一个缓存对象,是一个线性数组形式的内存,由OpenGL根据用户请求管理和分配。这块内存的内容可由用户控制,但是用户也仅能间接地控制。可以把buffer object当做GPU内存中的数组。
GPU可以快速读取它,因此在它里面存储数据有性能优势。

前面我们已经有了顶点数据,问题是它在我们的RAM中而不是OpenGL的内存中。要把他搬到OpenGL的内存,需要做上面三行代码:

第一行,创建buffer object。这时候我们还未给他分配任何空间。

第二行,BindBuffer函数将新建的BO绑定到ARRAY_BUFFER上下文中。

第三行,我们通过BufferData函数完成OpenGL中分配空间+数据拷贝的工作。

当这个函数执行完后,BO中就有了顶点数据了。这个函数最后的参数可以是下面值:

  • STATIC_DRAW 保存的数据内容只被程序定义一次,GL绘制命令可以使用多次。本文实例代码用的是这个。
  • DYNAMIC_DRAW 保存的数据内容将被程序重复定义,GL绘制命令可以使用多次。

后面我们看到 triangleData 再也没有被传递给 OpenGL 过,就是因为这个参数的让后面复用了。

管道开关

有了顶点的定义,下面一步就是如何将它们传给OpenGL ES库,OpenGL ES提供一个成为”管道Pipeline”的机制,这个管道定义了一些“开关”来控制OpenGL ES支持的某些功能,缺省情况这些功能是关闭的,如果需要使用OpenGL ES的这些功能,需要明确告知OpenGL “管道”打开所需功能。

对于这个示例,需要告诉OpenGL库打开 Vertex buffer以便使用顶点坐标Buffer。

要注意的使用完某个功能之后,要关闭这个功能以免影响后续操作:

顶点着色器

这个例子中的 vertexShader 就是 顶点着色器

这里的 version 100 是 Android、iOS、WebGL 使用的 OpenGL ES 2.0 对应的GLSL ES版本。参考: http://blog.csdn.net/u013467442/article/details/46765335

uniform vec2 offset;
attribute vec4 position;

是两个输入参数,uniform 标示只读、attribute 标示专用于顶点着色器,只读。

vec2 标示只包含2个浮点的向量,vec4标示包含4个浮点的向量。

gl_Position是顶点着色器裁切空间输出的位置向量。如果你想让屏幕上渲染出东西gl_Position必须使用。否则我们什么都看不到。

注意 gl_Position 的坐标系跟手机屏幕坐标像素的坐标系不一样。它的坐标系是 右手坐标系统。 详见后面。

顶点着色器 position 赋值

GLSL 中的position (attribute 类型)是模型的原始坐标,即这里的 triangleData,

go中 position  变量则是这个位置指针。

对应代码如下:

glctx.GetAttribLocation  返回指定属性变量的位置。

returns the location of an attribute variable.
在 GetAttribLocation 这个函数这里完成了映射捆绑。
 

赋值代码在下面:

有关这个函数的声明如下:

直接给 VertexAttribPointer 赋值是不支持的, 你需要使用 BindBuffer 绑定数据缓存区,然后用 BufferData 来填充数值。本例子在初始化时赋值用了标示 STATIC_DRAW  , 即后面可以复用这个赋值。

Direct use of VertexAttribPointer to load data into OpenGL is not supported via the Go bindings. Instead, use BindBuffer with an ARRAY_BUFFER and then fill it using BufferData.

The stride argument specifies the byte offset between consecutive vertex attributes.

offset 的赋值

GLSL  中 offset 只是 uniform 的类型, 这样的赋值就简单多了。

建立 go 跟 openGL 的关联指针

赋值,直接赋值,没有上面 position 那么多弯弯绕。

顶点的位置计算

计算中用到的几个值:

1、touchX、touchY  触点位置。

默认 touchX、touchY 是在屏幕的正中央,当有 touch 事件发生时,则是屏幕的对应位置。 以屏幕的实际像素为准。

屏幕的坐标系如下图,左上角为原点,向右是X轴,向下是Y轴。

2、sz.WidthPx、sz.HeightPx  屏幕的实际大小尺寸。

对应坐标也如上图。 WidthPx 是宽度、 HeightPx   是长度。

3、传递入 OpenGL 的 offset 值

offset.x 相对屏幕宽度的触点位置 touchX/float32(sz.WidthPx)

offset.y 项目屏幕高度的触点位置 touchY/float32(sz.HeightPx)

它们的值都是 0-1 之间。

4、传递入 OpenGL 的 position 值。

这个值就是 triangleData 的值,只是传递方法有点绕。

5、OpenGL 实际运算的 offset4

这里要做坐标体系的转换。

vec4 offset4 = vec4(2.0*offset.x-1.0, 1.0-2.0*offset.y, 0, 0);

x 放大2倍,

我们会把下面这个基于像素的坐标系(长宽 0到1)

转换成下面OpenGL的坐标系(OpenGL中使用, 长宽 -1到1)。

转换的算法就是上面的,2倍减一, 由于Y轴涉及到翻转,再外面加一个负数。

6、OpenGL 的实际位置

在offset4位置画模型,就是模型的具体实际位置。

gl_Position = position + offset4;

注意,这里我们画的三角形 top left 点 是上面的,而不是向下, 模型我们直接就用的 OpenGL的这个坐标系。

由于坐标系是从 -1到 1, 长度 0.4 就是 1/5 , 我们可以看到画出来的三角形, 长宽 分别是屏幕尺寸的 1/5.

片段着色器

图像颜色的设置

fragmentShader

就是片段着色器

precision用来确定默认精度修饰符,precision mediump float; 基本相当于中等精度。

参考: http://blog.csdn.net/wangyuchun_799/article/details/7752322

uniform vec4 color  只读的,4个浮点的向量 color。
所画图的颜色赋值过程

绑定关系

在应用启动时,完成绑定关系

 

赋值

每次需要绘画时,赋值。
 
 

Render (渲染)

我们已定义好了多边形,下面就要了解如和使用OpenGL ES的API来绘制(渲染)这个多边形了。OpenGL ES提供了两类方法来绘制一个空间几何图形:

DrawArrays 使用VetexBuffer 来绘制,顶点的顺序由vertexBuffer中的顺序指定。

DrawElements 可以重新定义顶点的顺序,顶点的顺序由indices Buffer 指定。

前面我们已定义里顶点数组,因此我们将采用 DrawArrays  来绘制多边形。

同样的顶点,可以定义的几何图形可以有所不同,比如三个顶点,可以代表三个独立的点,也可以表示一个三角形,这就需要使用mode参数 来指明所需绘制的几何图形的基本类型。

有关各个 mode 的几何类型请参考: http://www.imobilebbs.com/wordpress/archives/1512

Coordinate System坐标系

OpenGL使用了右手坐标系统,右手坐标系判断方法:在空间直角坐标系中,让右手拇指指向x轴的正方向,食指指向y轴的正方向,如果中指能指向z轴的正方向,则称这个坐标系为右手直角坐标系。

坐标转换

OpenGL 绘图过程中涉及到 坐标系的转换, 类似下图。

图来自: http://zhangwenli.com/blog/2015/08/28/opengl-matrix-transformations/

使用e.External 避免多次刷屏绘画

if glctx == nil || e.External {
    // As we are actively painting as fast as
    // we can (usually 60 FPS), skip any paint
    // events sent by the system.
    continue
}

显示 fps 调试信息

参考资料:

package gl 文档
https://godoc.org/golang.org/x/mobile/gl

Android OpenGL ES 开发教程 从入门到精通   这里讲的是 OpenGL ES 1.1的知识
http://blog.csdn.net/mapdigit/article/details/7526556

绘制形状
http://hukai.me/android-training-course-in-chinese/graphics/opengl/draw.html

OpenGL ES2 学习教程4——Shader语言
https://segmentfault.com/a/1190000004410579

Go Mobile 例子 basic 源码分析的更多相关文章

  1. Go Mobile 例子 audio 源码分析

    看这个源码分析前,建议先看更简单地例子 basic 的源码分析(http://www.cnblogs.com/ghj1976/p/5183199.html), 一些基础知识本篇将不再提及. audio ...

  2. hadoop自带例子SecondarySort源码分析MapReduce原理

    这里分析MapReduce原理并没用WordCount,目前没用过hadoop也没接触过大数据,感觉,只是感觉,在项目中,如果真的用到了MapReduce那待排序的肯定会更加实用. 先贴上源码 pac ...

  3. golang.org/x/mobile/exp/gl/glutil/glimage.go 源码分析

    看这篇之前,建议先看之前几篇,这几篇是基础. Go Mobile 例子 basic 源码分析 http://www.cnblogs.com/ghj1976/p/5183199.html OpenGL ...

  4. ANTD mobile源码分析 -- popover

    最近的开发中要用到很多的各式各样的组件.但是发现ant design mobile(后面简称ANTDM)里很多的资源.于是就分析一下,学习学习. ANTDM直接使用了typescript,没有用ES2 ...

  5. gRPC源码分析0-导读

    gRPC是Google开源的新一代RPC框架,官网是http://www.grpc.io.正式发布于2016年8月,技术栈非常的新,基于HTTP/2,netty4.1,proto3.虽然目前在工程化方 ...

  6. 【JUC】JDK1.8源码分析之SynchronousQueue(九)

    一.前言 本篇是在分析Executors源码时,发现JUC集合框架中的一个重要类没有分析,SynchronousQueue,该类在线程池中的作用是非常明显的,所以很有必要单独拿出来分析一番,这对于之后 ...

  7. [Tomcat 源码分析系列] (二) : Tomcat 启动脚本-catalina.bat

    概述 Tomcat 的三个最重要的启动脚本: startup.bat catalina.bat setclasspath.bat 上一篇咱们分析了 startup.bat 脚本 这一篇咱们来分析 ca ...

  8. PHP扩展编写、PHP扩展调试、VLD源码分析、基于嵌入式Embed SAPI实现opcode查看

    catalogue . 编译PHP源码 . 扩展结构.优缺点 . 使用PHP原生扩展框架wizard ext_skel编写扩展 . 编译安装VLD . Debug调试VLD . VLD源码分析 . 嵌 ...

  9. [Abp 源码分析]三、依赖注入

    0.简要介绍 在 Abp 框架里面,无时无刻不存在依赖注入,关于依赖注入的作用与好处我就不在这里多加赘述了,网上有很多解释的教程.在 [Abp 源码分析]一.Abp 框架启动流程分析 里面已经说过,A ...

随机推荐

  1. TKinter之文本域与多窗口

    用tkinter做出一个文本框出来,用于写字 代码很简单: #!/usr/bin/env python # _*_ coding:utf-8 _*_ from Tkinter import * roo ...

  2. 可视化SNV安装

    以前也安装过非图形化的SVN SERVER,但大多都需要比较复杂的配置,而且操作不太友好,所以其拥有可视化功能就比较重要了. 好了,开始干活吧. 说明:测试机为开发爬虫用的服务器,配置不高 准备工作: ...

  3. ORA-27101:shared memory realm does not exist的问题

    ORA-27101:shared memory realm does not exist的问题 登陆SQLPlus时出现: ORA-01034:ORACLE not avaiable ORA-2710 ...

  4. flume ng 问题点

    1. 启动Flume,出现脚本错误 错误如下: bin/flume-ng: line 82: syntax error in conditional expression: unexpected to ...

  5. 【mysql】mysql分表和表分区详解

    为什么要分表和分区? 日常开发中我们经常会遇到大表的情况,所谓的大表是指存储了百万级乃至千万级条记录的表.这样的表过于庞大,导致数据库在查询和插入的时候耗时太长,性能低下,如果涉及联合查询的情况,性能 ...

  6. VBA 按照文件类型名称打开一个文件

    Application.GetOpenFilename(fileFilter, fileIndex, fileSelectTitle, button, False) fileFilter: 指定能够被 ...

  7. phonegap–app启动欢迎引导页localstorage

    对一个新的app,一般情况都会添加一个介绍和欢迎的页面来告诉用户app的功能和新的特性. 那么在phonegap项目里面如何添加这样个引导欢迎页. 这里需要注意的是只有app第一次打开的时候才会有,其 ...

  8. IOS开发-手势简单使用及手势不响应处理办法

    1.点击 2.长按 3.拖拽 4.轻扫.捏合.旋转 5.使用手势需要注意的地方 1.注意处理轻扫和拖拽的冲突 //那个时间短的话 就让那个先执行 //处理 拖拽和轻扫 两个手势的冲突 //需要轻扫手势 ...

  9. 【转】一道SQL SERVER DateTime的试题

    学习过上一篇SQL SERVER DateTime精度的文章后.再来做一道题. IF ('2011-07-31 00:00:00.000' BETWEEN '2011-07-01' and '2011 ...

  10. RAC_Oracle集群服务安装Grid Infrastructure(案例)

    2015-01-24 Created By BaoXinjian Thanks and Regards