接着前一篇《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. QT 登录记住密码方法之一:Qt QSettings读写配置文件

    不过本文写的是明文保存,最好还是加密一下,以防文件被非法读取 /**登录初始化的时候处理这部分操作*/ Settings cfg("user.ini",QSettings::Ini ...

  2. NutzWk 5.0.x 微服务分布式版本开发及部署说明

    NutzWk 5.x 已发布一段时间,这段时间基于此版本开发了智慧水务系统(NB-IOT).某物联网平台.某设备租赁平台.某智慧睡眠平台.某智慧园区项目等,开发和部署过程中遇到一些小问题,开这个帖子把 ...

  3. NSProxy应用例子

    动态代理模式的应用很多,特别是在不能修改被代理类的前提下,要对执行某些方法时需要打log或者捕捉异常等处理时,是一个非常方便的方法.只需要少量修改客户端(场景类)代码和添加一个代理类就可以实现,这个符 ...

  4. 死磕salt系列-salt文章目录汇总

    死磕salt系列-salt入门 死磕salt系列-salt配置文件 死磕salt系列-salt grains pillar 配置 死磕salt系列-salt 常用modules 死磕salt系列-sa ...

  5. @SpringBootApplication无法被解析引入

    问题描述:@SpringBootApplication无法被解析引入,导致SpringBoot启动类报错 原因分析:springboot的包冲突了所致 解决方案: 需要删掉 repository\or ...

  6. 关于chrom56版本以后的form标签提交bug

    最近遇到一个情况: 低版本的浏览器i运行如下代码: function query_mission(query_tag) { // 创建form表单 var ip_list = getIpList() ...

  7. the django travel three[form表单验证]

    一:表单验证: 场景:因为浏览器的js可以被禁用,所以需要做后台的输入合法的验证. A:ajax发请求.需要注意的是ajax POST的数据的key值和form表单的里的字段名字一致,否则得不到验证! ...

  8. 如何在C#程序中模拟域帐户进行登录操作 (转载)

    .NET Core .NET Core也支持用PInvoke来调用操作系统底层的Win32函数 首先要在项目中下载Nuget包:System.Security.Principal.Windows 代码 ...

  9. 如何修改sharepoint中alert发送邮件模板

    In my post last week I talked about customizing alert notifications and alert templates. Sometimes y ...

  10. Flask租房项目总结

    该Flask项目历时3天,开发小组6人,目的是开发一个租房web项目,该项目采用前后端分离模式. Flask租房项目总结 分析需求文档,需要完成的功能模块有: 登陆注册 首页展示,首页搜索 详情展示, ...