接着前一篇《WebGL实现sprite精灵效果的GUI控件》,我们继续开发我们的数字系统GUI控件,因为这套数字系统是基于sprite效果的,所以数字随相机转动而旋转(永远面对相机),随场景缩放而逆向缩放(数字在屏幕上看上去大小不变)。实现sprite效果的核心方法在前一篇文章里已经详细说明,这里不再赘述,本文要讨论的是如何将用户输入的数字文本转变成GUI控件的数字贴图。请看demo。

我们能清楚地看到,在角度测量模式下,我们动态地绘制了两条边的长度数字贴图和角度大小的数字贴图。对于我们来说,计算边长和夹角是非常简单的工作,但怎么把结果数字转变成对应的图片呢,这里鲫鱼是通过uv坐标和数字图片一一映射实现的。其实原理非常简单,鲫鱼有一张包含0~9和小数点,角度的°的图片png,用户输入是一串包含0~9,小数点和°的字符串,鲫鱼将每一个字符都绑定图片的uv坐标,这样构造出的函数输入是字符串,返回就是对应输入字符串的uv坐标数组。我们知道构造纹理texture的贴图就是利用图片的uv坐标来定位图片在texture上的位置信息,利用这个原理,鲫鱼拿回的uv数组就完成了将一个一个数字贴到一片显式数字的矩形模型中,用户看到的就会是一片显式一串数字的矩形(当然矩形是透明的,用户只能看到数字)。

  以上是原理的解释,我们先来看一下数字的png图片长啥样。

这张就是包含数字和小数点和°的png图片,注意它是正方形的,且尺寸是2的幂次方。这两点前者是为了优化webgl的材质渲染,后者是为了能被webgl的材质所识别。为了做成正方形,我们用空白填充。好了,接下来我们来看看代码,关于uv贴图的映射和texture绑定图片,我们先来看uv映射。

 /**
* 字库
* */
let TextImage = function(){
this._library = {
ZERO_UV:[0, 1, 0, 0.75, 0.25, 1, 0.25, 0.75],
ONE_UV:[0, 0.75, 0, 0.5, 0.25, 0.75, 0.25, 0.5],
TWO_UV:[0, 0.5, 0, 0.25, 0.25, 0.5, 0.25, 0.25],
THREE_UV:[0, 0.25, 0, 0, 0.25, 0.25, 0.25, 0],
FOUR_UV:[0.25, 1, 0.25, 0.75, 0.5, 1, 0.5, 0.75],
FIVE_UV:[0.25, 0.75, 0.25, 0.5, 0.5, 0.75, 0.5, 0.5],
SIX_UV:[0.25, 0.5, 0.25, 0.25, 0.5, 0.5, 0.5, 0.25],
SEVEN_UV:[0.25, 0.25, 0.25, 0, 0.5, 0.25, 0.5, 0],
EIGHT_UV:[0.5, 1, 0.5, 0.75, 0.75, 1, 0.75, 0.75],
NINE_UV:[0.5, 0.75, 0.5, 0.5, 0.75, 0.75, 0.75, 0.5],
DOT_UV:[0.5, 0.5, 0.5, 0.25, 0.75, 0.5, 0.75, 0.25],
DEGREE_UV:[0.5, 0.25, 0.5, 0, 0.75, 0.25, 0.75, 0]
};
}; TextImage.prototype.constructor = TextImage;
TextImage.prototype = { /**
* 通过输入文字返回图片,小数点后保留3位
* text:输入的文字
* */
getImagesByText:function(text){
text = this.changeUnit(0.001, text);
text = this.keepEffectNum(3, text);
text = text.toString();
//逐字符匹配图片
let imgUVs = [];
for(let i=0; i<text.length; i++){
let imgUV = this.match(text[i]);
imgUVs = imgUVs.concat(imgUV);
}
return imgUVs;
}, /**
* 通过输入角度返回图片,小数点后保留3位
* angle:输入的文字
* */
getAngleImagesByText:function(angle){
angle = this.keepEffectNum(3, angle);
angle = angle.toString();
angle = angle + "#";
//逐字符匹配图片
let imgUVs = [];
for(let i=0; i<angle.length; i++){
let imgUV = this.match(angle[i]);
imgUVs = imgUVs.concat(imgUV);
}
return imgUVs;
}, /**
* 单位换算
* ratio:换算率
* text:输入的值
* */
changeUnit:function(ratio, text){
return ratio * text;
}, /**
* 小数点后保存n位
* effect:有效数字
* text:原始数字
* */
keepEffectNum:function(effect, text){
return text.toFixed(effect);
}, /**
* 匹配字符和图片
* char:字符
* */
match:function(char){
let imgUV = undefined;
if(char === "0"){
imgUV = this._library.ZERO_UV;
}else if(char === "1"){
imgUV = this._library.ONE_UV;
}else if(char === "2"){
imgUV = this._library.TWO_UV;
}else if(char === "3"){
imgUV = this._library.THREE_UV;
}else if(char === "4"){
imgUV = this._library.FOUR_UV;
}else if(char === "5"){
imgUV = this._library.FIVE_UV;
}else if(char === "6"){
imgUV = this._library.SIX_UV;
}else if(char === "7"){
imgUV = this._library.SEVEN_UV;
}else if(char === "8"){
imgUV = this._library.EIGHT_UV;
}else if(char === "9"){
imgUV = this._library.NINE_UV;
}else if(char === "."){
imgUV = this._library.DOT_UV;
}else if(char === "#"){
imgUV = this._library.DEGREE_UV;
}
return imgUV;
}
}; module.exports = TextImage;

  我们看到这个TextImage类拥有一个this._library字库,其中每一个数字都绑定了一串uv坐标,即图片中每一个数字的左上角->左下角->右下角->右上角逆时针绕向的4组坐标值。在match函数中通过函数输入参数的字符来返回对应的uv坐标数组。这就是数字绑定uv的原理。再来看我们拿到uv数组怎么绑定到材质对象中去。请看下面代码。

 /**
* 创建几何
* viewer:视图对像
* textNode:文字节点
* width:宽
* height:高
* position:位置坐标
* imgUVs:图片uv数组
* texture:数字纹理
* */
addGeometry:function(viewer, textNode, width, height, position, imgUVs, texture){
//顶点缓存
let w = width;
let h = height;
//缩放比
let scaleRatio = 1;
scaleRatio = this.againstScale(position, viewer);
w = w*scaleRatio;
h = h*scaleRatio;
//顶点数组
let vertices = [];
//首先确定有几张图片
let imgNum = imgUVs.length/8;
if(imgNum !== 0){
for(let i=0; i<imgNum; i++){
vertices.push(w*i, h, 0, w*i, 0, 0, w*(i+1), h, 0, w*(i+1), 0, 0);
}
}
let array = new Float32Array(vertices);
let vertexBuffer = new BufferArray(BufferArray.ARRAY_BUFFER, array, 3);
//索引缓存
let indices = [];
if(imgNum !== 0){
for(let i=0; i<imgNum; i++){
indices.push(4*i, 4*i+1, 4*i+3, 4*i+3, 4*i+2, 4*i);
}
}
let index = new Int8Array(indices);
let indexBuffer = new BufferArray(BufferArray.ELEMENT_ARRAY_BUFFER, index, index.length);
//绘制图元
let prim = new DrawElements(Primitives.TRIANGLES, indexBuffer);
//几何对象
let geom = new Geometry();
geom.setBufferArray('Vertex', vertexBuffer);
geom.setPrimitive(prim);
//纹理坐标
let uv = new Float32Array(imgUVs);
let uvBuffer = new BufferArray(BufferArray.ARRAY_BUFFER, uv, 2);
geom.setBufferArray('Texture', uvBuffer);
//将texture加入geometry
geom.getStateSet(true).addAttribute(texture, StateAttribute.OVERRIDE);
//图片背景透明
let bf = new BlendFunc(BlendFunc.SRC_ALPHA, BlendFunc.ONE_MINUS_SRC_ALPHA);
geom.getStateSet(true).addAttribute(bf, StateAttribute.OVERRIDE);
//几何对象加入根节点
textNode.addChild(geom);
//将textNode的位置平移到position位置
let translateMat = Mat4.MemoryPool.alloc();
Mat4.fromTranslation(translateMat, position);
Mat4.copy(textNode._matrix, translateMat);
//根据主相机视口调整模型旋转,保证文字总是面向相机
this.computeMatrix4MainCamera(textNode._matrix, viewer);
//析构
Mat4.MemoryPool.free(translateMat);
},

我们看到,我们的uv转成BufferArray后被geometry对象所接收,let uv = new Float32Array(imgUVs); let uvBuffer = new BufferArray(BufferArray.ARRAY_BUFFER, uv, 2); geom.setBufferArray('Texture', uvBuffer);通过这三行代码,我们的几何体对象里就包含了材质信息,鲫鱼接下来很欢快地发现,数字贴图完整地绘制到模型节点中去了。
  以上就是对数字GUI组建的完整说明,谢谢同学们的关注与支持,鲫鱼和大家一起进步,鲫鱼和同学们下周再见。

  本文系原创,如需引用,请注明出处:https://www.cnblogs.com/ccentry/p/10322832.html

WebGL之sprite精灵效果显式数字贴图的更多相关文章

  1. WebGL实现sprite精灵效果的GUI控件

    threejs已经有了sprite插件,这就方便了three的用户,直接可以使用threejs的sprite插件来制作GUI模型.sprite插件是阿里的lasoy老师改造过的,这个很厉害,要学习一哈 ...

  2. GC与显式内存管理

    C++复兴的话题至今已被鼓吹两年有余,Herb Sutter和Bjarne Stroustrup等大牛们也为C++带来了大步伐的革新.然而,从这两年的效果而言,C++的复兴并没有发生.一方面随着世界经 ...

  3. 显式锁(三)读写锁ReadWriteLock

    前言:   上一篇文章,已经很详细地介绍了 显式锁Lock 以及 其常用的实现方式- - ReetrantLock(重入锁),本文将介绍另一种显式锁 - - 读写锁ReadWriteLock.    ...

  4. 浅析SQL查询语句未显式指定排序方式,无法保证同样的查询每次排序结果都一致的原因

    本文出处:http://www.cnblogs.com/wy123/p/6189100.html 标题有点拗口,来源于一个开发人员遇到的实际问题 先抛出问题:一个查询没有明确指定排序方式,那么,第二次 ...

  5. 并发编程 19—— 显式的Conditon 对象

    Java并发编程实践 目录 并发编程 01—— ThreadLocal 并发编程 02—— ConcurrentHashMap 并发编程 03—— 阻塞队列和生产者-消费者模式 并发编程 04—— 闭 ...

  6. (转载)Android理解:显式和隐式Intent

    Intent分两种:显式(Explicit intent)和隐式(Implicit intent). 一.显式(设置Component) 显式,即直接指定需要打开的activity对应的类. 以下多种 ...

  7. 【PL/SQL练习】显式游标

    cursor --------需要用户先定义,在select时,可以用于处理多行记录 1.declare  声明一个游标 2.open cursor (隐式游标自动open) 3.fetch curs ...

  8. c# implicit explicit关键字(隐式和显式数据类型转换)

    implicit关键字用于声明隐式的用户定义类型转换运算符.(explicit反之)explicit则用于显示转换用户自定义类型.static implicit operator target_typ ...

  9. IOS动画隐式,显式,翻页

    //  ViewController.m //  IOS动画0817 // //  Created by 张艳锋 on 15/8/17. //  Copyright (c) 2015年 张艳锋. Al ...

随机推荐

  1. VRSProcess(二)

    1._beginthreadex再谈 Windows操作系统提供了这样的一种解决方案——每个线程都将拥有自己专用的一块内存区域来供标准C运行库中所有有需要的函数使用.而且这块内存区域的创建就是由C/C ...

  2. JVM源码分析之堆外内存完全解读

    JVM源码分析之堆外内存完全解读   寒泉子 2016-01-15 17:26:16 浏览6837 评论0 阿里技术协会 摘要: 概述 广义的堆外内存 说到堆外内存,那大家肯定想到堆内内存,这也是我们 ...

  3. 关于Oracle11g R2的学习笔记

    进来由于工作需要开始有SQLServer向Oracle转型学习,想把学习写到这里作为记录和备忘.  Oracle 11g R2下面都简称:Oracle     第一:Oracle的安装 在安装过程没有 ...

  4. 【原创】python内存泄漏以及python flask框架莫名coredump

    1.python内存泄漏 今天在看服务器上的进程时,用top查的时候,发现一个一直跑的脚本程序内存竟然达到了1.6G,这个脚本我有印象,一开始仅占用20M左右,显然是内存泄漏了. 用gc和objgra ...

  5. JavaScript的DOM操作获取元素周边大小

    一.clientLeft 和 clientTop 这组属性可以获取元素设置了左边框和上边框的大小,目前只提供了 Left 和 Top 这组,并没有提供 Right 和 Bottom. <scri ...

  6. Java 持久化发展历程

  7. 4-1 R语言函数 lapply

    #lapply函数 #可以循环处理列表中的每一个元素 #lapply(参数):lapply(列表,函数/函数名,其他参数) #总是返回一个列表 #sapply:简化结果 #结果列表元素长度均为1,返回 ...

  8. jdk8环境下,添加重复注解的美好体验

    为了实现业务层缓存,定义了几个注解:@Cache.able.@Cache.put.@Cache.del 分别实现对业务方法的 缓存检测.缓存插入 和 缓存清除. public @interface C ...

  9. numpy的array数据类型(创建)

    import numpy as np # 创建 # 创建一维数组 a = np.array([1, 2, 3]) print(a) ''' [1 2 3] ''' # 创建多维数组 b = np.ar ...

  10. 【转】 Android xml中 @和?区别,style和attr小结

    引用资源时,使用@还是?的区别,例如在设置style的时候既可以使用@也可以使用? style="?android:attr/progressBarStyleHorizontal" ...