看这篇之前,建议先看之前几篇,这几篇是基础。

Go Mobile 例子 basic 源码分析
http://www.cnblogs.com/ghj1976/p/5183199.html

OpenGL ES 着色语言
http://www.cnblogs.com/ghj1976/p/5180895.html

仿射变换矩阵(这是glimage包最核心的矩阵算法变换逻辑基础知识)
http://www.cnblogs.com/ghj1976/p/5199086.html

glutil 包中重要的类是 Image 类, 它实现了 *image.RGBA 和 OpenGL纹理图形之间的桥梁,而Image类中最关键的就是完成绘图,我们重点也是分析这个函数。

绘图用的颜色着色器

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

varying 标示是从顶点着色器传递过来的变量。 vec2  2个浮点的向量  UV

uniform 只读变量  sampler2D

sampler2D是个啥?其实在CG中,sampler2D就是和texture所绑定的一个数据容器接口。这个说法还是太复杂了,简单理解的话,所谓加载以后的texture(贴图)说白了不过是一块内存存储的,使用了RGB(也许还有A)通道,且每个通道8bits的数据。而具体地想知道像素与坐标的对应关系,以及获取这些数据,我们总不能一次一次去自己计算内存地址或者偏移,因此可以通过sampler2D来对贴图进行操作。更简单地理解,sampler2D就是GLSL中的2D贴图的类型,相应的,还有sampler1D,sampler3D,samplerCube等等格式。

texture2D(textureSample, UV) 通过texture2D函数我们可以得到一个纹素(texel),这是一个纹理图片中的像素。函数参数分别为simpler2D以及纹理坐标。

参考: http://blog.csdn.net/racehorse/article/details/6664717

绘图用的顶点着色器

参数类型说明:

  • uniform 只读的变量
  • mat3  3*3 的浮点矩阵。
  • attribute 专用于顶点着色器的只读变量
  • vec3 三个浮点的向量、 vec2 2个浮点的向量。
  • varying 标示是要从顶点着色器传递的变量,具体变量名字是UV, 后面有UV的计算公式。

gl_Position 相关计算

gl_Position 变量是一个四维 (vec4) 变量,包含顶点的 x、y、z 和 w 值。上面代码中 w 值恒定为 1,其他值为 mvp * p 计算出来。

mvp 是坐标转换的矩阵,具体计算逻辑请看后面。

p是 z 轴恒定为1 的 三维变量, 其他两维 由 pos 参数传入。

pos 是由 quadXYCoords 传入的,它是 OpenGl 坐标系的四个顶点坐标。

UV 顶点对应颜色计算

在颜色着色器中, 颜色和位置的对应关系由UV传入。 UV 的计算在顶点着色器中完成计算。

UV 由 uvp 转换矩阵 * 具体坐标 inUV 计算而来。

inUV 传递的值是由 quadUVCoords 传入的,它是 手机屏幕坐标系的坐标四个顶点。

绘图函数参数分析

绘制具体图我们用的下面封装的函数
golang.org/x/mobile/exp/gl/glutil/glimage.go

// Draw draws the srcBounds part of the image onto a parallelogram, defined by
// three of its corners, in the current GL framebuffer.
func (img *Image) Draw(sz size.Event, topLeft, topRight, bottomLeft geom.Point, srcBounds image.Rectangle) {

这个函数通过给定的三个点(左上角、右上角、左下角)来画出一个平行四边形。

  • img  要绘制的图片类。
  • 这个函数的第一个参数 sz size.Event 是游戏屏幕的大小。
  • topLeft, topRight, bottomLeft geom.Point  是确定平行四边形的三个点。
  • srcBounds image.Rectangle 是要绘图的位置边界值,即在哪里绘制这个图。 通过两个点 Min, Max Point 来定位。

这个函数输出参数的坐标系是手机屏幕的坐标系,如下图:

在 golang.org/x/mobile/geom 包中有 pixel 和 pt 的转换函数。

px是像素,屏幕中最小元素单位。
pt是磅数,1英寸为72磅。字体大小的单位一般用磅数。

传递给OpenGL的 mvp 矩阵值的计算

We are drawing a parallelogram PQRS, defined by three of its corners, onto the entire GL framebuffer ABCD.
我们在整个OpenGL帧缓冲ABCD中画一个由三个角确定的平行四边形 PQRS。

The two quads may actually be equal, but in the general case, PQRS can be smaller, and PQRS is not necessarily axis-aligned.
这两个四边形实际上是相等的,但一般情况下PQRS会更小,而且PQRS跟坐标轴并不对齐。

There are two co-ordinate spaces: geom space and framebuffer space.
这里我们会用到2个坐标系, geom坐标系和 Framebuffer 坐标系。

In geom space, the ABCD rectangle is:
在 geom 坐标系中, ABCD的矩形坐标如下:

(0, 0)                               (geom.Width, 0)
    (0, geom.Height)       (geom.Width, geom.Height)

and the PQRS quad is:  PQRS 的坐标是:

(topLeft.X,    topLeft.Y)                         (topRight.X, topRight.Y)
    (bottomLeft.X, bottomLeft.Y)           (implicit,   implicit)

In framebuffer space, the ABCD rectangle is:
在 framebuffer 坐标系下,ABCD 矩形坐标是

(-1, +1) (+1, +1)
    (-1, -1)  (+1, -1)

First of all, convert from geom space to framebuffer space.
首先,我们需要从 geom 坐标系转变成 framebuffer 坐标系
For later convenience, we divide everything by 2 here: px2 is half of the P.X co-ordinate (in framebuffer space).
为了后面方便起见,我们这里2分一切,px2是PX坐标系实际坐标的一半(framebuffer坐标系)

px2 := -0.5 + float32(topLeft.X/sz.WidthPt)
py2 := +0.5 - float32(topLeft.Y/sz.HeightPt)
qx2 := -0.5 + float32(topRight.X/sz.WidthPt)
qy2 := +0.5 - float32(topRight.Y/sz.HeightPt)
sx2 := -0.5 + float32(bottomLeft.X/sz.WidthPt)
sy2 := +0.5 - float32(bottomLeft.Y/sz.HeightPt)

这个坐标转换请看下图:

Next, solve for the affine transformation matrix
下一步,解决 仿射变换矩阵 (仿射变换(Affine Transformation))

有关 仿射变换矩阵 的知识请参看: http://www.cnblogs.com/ghj1976/p/5199086.html

我们现在要通过计算 A点通过一个矩阵转换成 P 点坐标, B点转换成 Q点坐标, D点转换成 S点坐标,如下图:

这个转换肯定是一个 仿射变换 。 

A点的坐标是 (-1,+1)   P 的坐标是 (2*PX2, 2*PY2)  ,这个转换可以用下面公式表示。

同理有 B –> Q , D->S 的 公式。

从公式中提取6个表达式如下:

-a00 + a01 + a02 = 2*px2
-a10 + a11 + a12 = 2*py2
+a00 + a01 + a02 = 2*qx2
+a10 + a11 + a12 = 2*qy2
-a00 - a01 + a02 = 2*sx2
-a10 - a11 + a12 = 2*sy2

通过合并运算,可以推理得到

a00 = qx2 - px2
a01 = px2 - sx2
a02 = qx2 + sx2
a10 = qy2 - py2
a11 = py2 - sy2
a12 = qy2 + sy2

即mvp这个转换矩阵应该是

传递给OpenGL的uvp 的矩阵值的计算

这个的计算逻辑过程跟 mvp的过程一样, 不过是 屏幕坐标系 跟

贴图纹理的坐标是类似的, 不过在纹理坐标系中, ABCD 的坐标是:

        //    (0,0) (1,0)
        //    (0,1) (1,1)

PQRS 四个轴跟坐标系是对齐的。如下图效果所示:

由于是轴对齐的, 所以存在:

px=sx     qy=py

跟上面一样做 A到P的转换  和  B到Q的转换。 矩阵运算如下:

最后可以得到这个转换矩阵为,这也是 uvp 传入的值。

给 OpenGL 传递顶点数据

下面这三行代码经常一起出现,含义解释如下:

    glctx.BindBuffer(gl.ARRAY_BUFFER, glimage.quadXY)
    glctx.EnableVertexAttribArray(glimage.pos)
    glctx.VertexAttribPointer(glimage.pos, 2, gl.FLOAT, false, 0, 0)
BindBuffer(target Enum, b Buffer)
的第一个参数是表明buffer的类型,gl.ARRAY_BUFFER表明存储vertex data。
绑定了buffer后,我们需要为buffer传值,传值在之前完成 BufferData 这里完成。
 
EnableVertexAttribArray(a Attrib)

激活该属性

VertexAttribPointer(dst Attrib, size int, ty Enum, normalized bool, stride, offset int)

  • dst: 需要绑定的属性的索引值。
  • size: 表示buffer中几个值代表一个vertex。
  • ty: 表示buffer中数据的类型,一般有FIXED, BYTE, UNSIGNED_BYTE, FLOAT, SHORT, UNSIGNED_SHORT。
  • normalized: 这个参数可以设为true或者false,它涉及到数据转换。一般我们都将它设为false。
  • stride: 如果为0,表示buffer中的数据是按顺序存储。
  • offset: buffer的偏移值,如果设置了则会从Offset的位置开始。

整个这个过程可以用下图表示,图来自:  https://github.com/fem-d/webGL/blob/master/blog/WebGL%E5%9F%BA%E7%A1%80%E5%AD%A6%E4%B9%A0%E7%AF%87%EF%BC%88Lesson%202%EF%BC%89.md

https://github.com/fem-d/webGL/tree/master/blog

数据流程图

顶点数据传递流程图

颜色渲染数据传递流程图

纹理操作

BindTexture(target Enum, t Texture)

为了把一个名为texture的纹理对象绑定为一个2D纹理对象。  p.glctx.BindTexture(gl.TEXTURE_2D, img.gltex)

TexImage2D(target Enum, level int, width, height int, format Enum, ty Enum, data []byte)

    • target   操作的目标类型,设为 GL_TEXTURE_2D 即可
    • level   纹理的级别,这里设置成了0 。表示多级分辨率的纹理图像的级数,若只有一种分辨率,则level设为0。
    • width和height ---- 给出了纹理图像的长度和宽度,纹理映射的最大尺寸依赖于OpenGL,但它至少必须是使用64x64(若带边界为66x66),若width和height设置为0,则纹理映射有效地关闭。
必须是2的n次方。纹理图片至少要支持64个材质元素的宽度
    • format和ty ---- 描述了纹理映射的格式和数据类型
//format    像素数据的颜色格式,必须和internalformatt取值必须相同。可选的值有
// GL_ALPHA,
// GL_RGB,
// GL_RGBA,
// GL_LUMINANCE,
// GL_LUMINANCE_ALPHA 等几种。 //type 指定像素数据的数据类型。可以使用的值有
// GL_UNSIGNED_BYTE,
// GL_UNSIGNED_SHORT_5_6_5,
// GL_UNSIGNED_SHORT_4_4_4_4,
// GL_UNSIGNED_SHORT_5_5_5_1
  • data 告诉OpenGL纹理数据的来源
TexParameteri(target, pname Enum, param int)
指定纹理格式。这里包括纹理横向和纵向的重复方式,这里我们看到设置了4个。
    p.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
    p.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
    p.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
    p.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)

TEXTURE_MAG_FILTER
TEXTURE_MIN_FILTER

当图片缩放时就需要重新计算每一像素的颜色,而缩放后的图片和原图的像素是无法对上的,因此颜色的分配就有很多算法。最基本的两种算法是“就近取色(NEAREST)”和“线性插值(LINEAR)”,这也是“TEXTURE_MAG_FILTER”和“TEXTURE_MIN_FILTER”的值。下面咱也来说说这个两个值。
  NEAREST:把原图在缩放后映射过去的像素直接使用,也就是对映射坐标取近似值,让每一个坐标都对应到像素上。这个做法可以确保颜色表不被破坏,缩放后使用的颜色表绝不会超出原图的颜色表。而且计算量小,所以速度很快。
  LINEAR:取映射坐标最接近的几个像素,把他们的颜色做平均值计算。这样缩放后颜色表就会和原图的不同,不过由于取了平均值,使得颜色的过度更加自然,可以有效的消除锯齿。但是由于需要计算颜色平均值,计算量就变高的,速度就稍为逊色。

下面是效果图,这个东西比较精细,可能很难看清,仔细看文字的边缘

“TEXTURE_WRAP_S”和“TEXTURE_WRAP_T”。这两个东西是用来指定图片平铺的。“TEXTURE_WRAP_S”是横向的设置,“TEXTURE_WRAP_T”是纵向的设置。他们有三个值:
    REPEAT
    MIRRORED_REPEAT
    CLAMP_TO_EDGE

这三个值很容易理解的,测试也很容易看出结果。贴图默认都是平铺的,也就是第一个值。如果我们需要不平铺就可以设置成“CLAMP_TO_EDGE”,就像CSS中的“no-repeat”一样。中间“MIRRORED_REPEAT”也是平铺,只不过是正反交替的。看下面的截图就很容易明白。

参考: https://www.web-tinker.com/article/20163.html

TexSubImage2D(target Enum, level int, x, y, width, height int, format, ty Enum, data []byte)

writes a subregion of a 2D texture image.
 

参考:

OpenGL ES 2 封装库说明
https://godoc.org/golang.org/x/mobile/gl

glutil 包说明文档
https://godoc.org/golang.org/x/mobile/exp/gl/glutil

WebGL基础学习篇
https://github.com/fem-d/webGL/blob/master/blog/WebGL%E5%9F%BA%E7%A1%80%E5%AD%A6%E4%B9%A0%E7%AF%87%EF%BC%88Lesson%202%EF%BC%89.md

golang.org/x/mobile/exp/gl/glutil/glimage.go 源码分析的更多相关文章

  1. Go Mobile 例子 basic 源码分析

    OpenGL ES(OpenGL for Embedded Systems)是 OpenGL 三维图形API的子集,针对手机.PDA和游戏主机等嵌入式设备而设计.该API由Khronos集团定义推广, ...

  2. Go Mobile 例子 audio 源码分析

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

  3. ANTD mobile源码分析 -- popover

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

  4. Golang package轻量级KV数据缓存——go-cache源码分析

    作者:Moon-Light-Dream 出处:https://www.cnblogs.com/Moon-Light-Dream/ 转载:欢迎转载,但未经作者同意,必须保留此段声明:必须在文章中给出原文 ...

  5. golang thrift 源码分析,服务器和客户端究竟是如何工作的

    首先编写thrift文件(rpcserver.thrift),运行thrift --gen go rpcserver.thrift,生成代码 namespace go rpc service RpcS ...

  6. golang中container/list包源码分析

    golang源码包中container/list实际上是一个双向链表 提供链表的一些基本操作,下面就结合定义和接口进行下说明 1. 定义 // Element is an element of a l ...

  7. golang中container/heap包源码分析

    学习golang难免需要分析源码包中一些实现,下面就来说说container/heap包的源码 heap的实现使用到了小根堆,下面先对堆做个简单说明 1. 堆概念 堆是一种经过排序的完全二叉树,其中任 ...

  8. Golang源码分析之目录详解

    开源项目「go home」聚焦Go语言技术栈与面试题,以协助Gopher登上更大的舞台,欢迎go home~ 导读 学习Go语言源码的第一步就是了解先了解它的目录结构,你对它的源码目录了解多少呢? 目 ...

  9. golang之websocket 源码分析

    下载go的websocket包. 1. 通过google官方的方法, 需要hg来同步代码. 由于墙的原因, 还需要设置代理. 比较麻烦 2. http://gopm.io/ 通过该网站下载, 这是go ...

随机推荐

  1. Response.Redirect在新窗口打开网页

    来自:http://www.woosky.net/show.asp?id=761 Respose.Write("<script language='javascript'>win ...

  2. python数据类型之list

    1.append:增加元素到列表尾部 L.append(object) -> None -- append object to end 2.clear:清空列表中所有元素 3.count:返回列 ...

  3. Swift使用Alamofire实现网络请求

    Alamofire是一个用Swift编写的HTTP网络库,由此前热门开源项目AFNetworking的的作者mattt开发,可非常简单地用于异步网络通信. 要获取最新版本的 Alamofire,前往h ...

  4. javascript模块化编程(AMD规范的加载器)

    关于AMD规范可以参考阮一峰的这篇文章Javascript模块化编程(二):AMD规范 简单来说,AMD规范就是异步方式加载模块的一种方式,避免因为模块加载过慢而导致浏览器“假死”. 先贴一个学习地址 ...

  5. java 金额计算,商业计算 double不精确问题 BigDecimal,Double保留两位小数方法

    解决办法================== http://blog.javaxxz.com/?p=763 一提到Java里面的商业计算,我们都知道不能用float和double,因为他们无法 进行精 ...

  6. SIM卡里的文件

    SIM卡里的所有文件按树来组织:主文件MF(Master File)——每一块SIM卡只有一个唯一的主文件, 其他所有文件都是它的子孙, 主文件只有文件头,里面存放着整个SIM卡的控制和管理信息专用文 ...

  7. Neutron分析(7)—— neutron-l3-agent HA solutions

    1. keepalived vrrp/conntrackd High availability features will be implemented as extensions or driver ...

  8. visual studio 编译时 出现 Files 的值 乱码

    参考了:http://blog.163.com/jiang_tao_2010/blog/static/121126890201031031337332/ 最近在做程序时,在生成解决方法过程中,电脑出现 ...

  9. bzoj3035: 导弹防御塔

    Description Freda的城堡——“Freda,城堡外发现了一些入侵者!”“喵...刚刚探究完了城堡建设的方案数,我要歇一会儿嘛lala~”“可是入侵者已经接近城堡了呀!”“别担心,rain ...

  10. 折腾Ipython

    1. 用easy_install安装吧 [root@host python]# easy_install IPython Searching for IPython Reading https://p ...