接着前一篇《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. 破解myeclipse10失败的一个奇葩原因

    昨天开发用的myeclipse10突然弹窗提示我要激活,我清楚的记得安装时候已经破解并且看到激活信息了. 翻遍搜索出来的文章,改systemid之类的也试过了,问题依旧存在,很是绝望. 今早过来机灵了 ...

  2. CSS3 新增颜色表示方式

    一.CSS1&2颜色表示方式(W3C标准) 1.Color name  颜色名称方式(用颜色关键字表示对应的颜色.) 例如:red(红色).blue(蓝色).pink(粉色) 优点:方便快捷而 ...

  3. JSF标签之f:facet 的用法

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/zkn_CS_DN_2013/article/details/33717091 f:facet标签用来 ...

  4. 20145203盖泽双《Java程序设计》第三周学习总结

    20145203盖泽双<Java程序设计>第三周学习总结 教材学习内容总结 1.两个基本的标准类:java.util.Scanner与java.math.BigDecimal. 2.Big ...

  5. c++ 堆和栈以及区别

    c++中内存分成5个区:堆.栈.自由存储区.全局\静态存储区.常量存储区 栈是一种连续存储的数据结构,具有先进后出的性质.堆是一种非连续的树形存储数据结构,每个节点有一个值,整棵树是经过排序的,特点是 ...

  6. 学习笔记——并行编程Parallel

    Parallel 并行运算 参考资料:http://www.cnblogs.com/woxpp/p/3925094.html 1.并行运算 使用Parallel并行运算时,跟task很像,相当于tas ...

  7. nagios 在nrpe中自定义脚本

    监控第三方端口(22000) #!/bin/bash#author:xiaoweige#check 140 22000 result=`sleep 1|telnet 10.2.1.140 22000| ...

  8. Ural 1183 Brackets Sequence(区间DP+记忆化搜索)

    题目地址:Ural 1183 最终把这题给A了.. .拖拉了好长时间,.. 自己想还是想不出来,正好紫书上有这题. d[i][j]为输入序列从下标i到下标j最少须要加多少括号才干成为合法序列.0< ...

  9. C到C++的快速教程

    1.头文件: C++头文件不是以.h结尾,C语言中的标准库文件如math,h,stdio.h在C++中被命名为cmath,cstdio 2.命名空间: 为防止名字冲突(出现同名),C++引入名字空间( ...

  10. Linux—文件命令之touch命令

    下面总结一下对于文件的操作命令: satat命令:用于显示文件的详细信息,包括文件.设备.gid.各种时间等. 命令格式:stat filename touch 的两个功能: 1.新建文件,如需建立特 ...