采纳着色器在WebGL3D场景中呈现行星表面地形,技术储备指南

By admin in 4858美高梅 on 2019年3月30日

试行指标:依照一定规律生成类地行星地球表面地貌区块,并用合理的法子将地形块显示出来

译序
Three.js是三个光辉的开源WebGL库,WebGL允许JavaScript操作GPU,在浏览器端完成真正含义的3D。不过近期那项技术还处于发展阶段,资料极为紧张,爱好者学习为重要透过Demo源码和Three.js本人的源码来上学。

译序
Three.js是3个英豪的开源WebGL库,WebGL允许JavaScript操作GPU,在浏览器端达成真正意义的3D。但是当前那项技术还处于发展阶段,资料极为紧张,爱好者学习为主要因此Demo源码和Three.js自个儿的源码来学学。

WebGL 是 HTML 5 草案的一部分,能够使得 Canvas 渲染三维场景。WebGL
固然还未有广泛应用,但极具潜力和想象空间。本文是本身就学 WebGL
时梳理知识系统的产物,花点时间整理出来与大家大快朵颐。

涉及文化:Babylon.js引擎应用、着色器编制程序、正态分布、数据处理、canvas像素操作

0.简介
事先小编早已交由了一篇《开头采纳Three.js》。假诺你还尚无读过,你恐怕必要去读一下,本文的根底是在那一篇教程的功底上成功的。

0.简介
事先自身一度提交了一篇《初阶应用Three.js》。要是您还尚无读过,你大概须求去读一下,本文的功底是在那一篇教程的底子上形成的。

示例

WebGL 很酷,有以下 demos 为证:

寻找奥兹国
超跑游戏
泛舟的男孩(Goo
Engine Demo)

github地址:

自家想研究一下着色器。在Three.js支持你免去了诸多辛勤以前,原生WebGL就很优秀了。有的时候,你只怕会想要实现都部队分特定的法力,可能想对表今后您的显示器上的东西钻研得更透彻部分,那么着色器一定会进入你的视野。就算您像自家同样,你也一样希望完结部分比上一篇教程中的基础更为有意思的东西。那篇教程中,小编会讲解Three.js的功底,这一个基础实际上为大家做了累累干燥的行事。
在上马从前自身还要说,那篇教程会有一定多的篇幅在分解着色器的代码,之后会有一篇教程会在着色器代码的基础上腾飞一点,利用着色器去做点什么。那是因为着色器shaders第贰立时上去并不错懂,供给有些解说。
1.三种着色器
WebGL没有稳定的渲染管线,你不能够直接行使二个黑盒子式的着色器(译者注:上个世纪的显卡基本都只援救固定渲染管线);WebGL提供的是可编制程序的管线,那种措施更有力但也更难明白和行使。长途电话短说,可编制程序渲染管线意味着编写程序的人要团结背负获取极限并将它绘制在荧屏上了。着色器是渲染管线的一有个别,有二种着色器:
1.顶点着色器
2.片元着色器
你应当知道的是,那三种着色器都统统运维在显卡的GPU上,我们将急需它们处理的数量从CPU上卸下,装到GPU上,减轻了CPU的清华。现代的GPU对着色器需求的调用的运算类型都做了大幅优化,那样做很值得。
2.顶点着色器
基元形状,比如3个圆球,是由顶点构成的,是吗?顶点着色器被逐一传入那些极端中的3个终极,然后处理它。如何处理每种终端是足以专擅定制的,但终端着色器有二个必做的事,正是为1个名为
gl_Position
的变量赋值,该变量是四个4维数组,表示该终端最后在荧屏上的职分。那作者是个有趣的历程,因为我们其实在研究怎么样将1个三维坐标(一个具有x、y、z值得顶点)转化为,大概说投影到二维的显示屏上。谢天谢地,假使大家采取Three.js之类的工具,大家能够这么福利地走访到
gl_Position 。
3.片元着色器
今昔我们有隐含顶点的三维物体了,以后要将物体投影到二维荧屏上了,但颜色哪个地方去了?纹理和光照呢?那多亏片元着色器要拍卖的。
和顶峰着色器类似,片元着色器有一项必须达成的义务:设置或化解变量
gl_FragColor
,另1个四维浮点变量,也正是片元点最后的颜色。什么是片元?想象二个有所两个极端的三角形,片元正是因此那多少个终端总计后的,全数在三角内部的点。由此,片元值由顶点的值内插生成。固然2个终极的水彩是丁巳革命,相邻顶点的颜料是深青莲,那么大家得以考察到颜色从红棕顶点附近渐变,由深普鲁士蓝变成浅莲红,最后在茜红顶点附近变成黄色。
4.着色器变量
说到着色器变量,有三种:Uniforma,Attributes和Varyings。当自家先是次听到那四个词语时,小编很迷惑,因为它们和自家前边运用的东西完全不般配。但今后,你能够那样敞亮它们:
1.Uniforms变量既能够流传顶点着色器,也得以流传片元着色器,它们包涵了怎么在一切渲染进度中保持不变的变量,比如,三个点光源的职位。
2.Attributes变量对应于各种终端,它们只好够流传顶点着色器中,比如每一种终端都拥有一个颜色。Attributes变量和顶峰的关联是逐一对应的。
3.Varyings变量是在巅峰着色器中定义,并且准备传入给片元着色器的变量。为了保障那点,大家供给保障在八个着色器中变量的档次和命名完全一致。贰个经文的利用是法线向量,因为在盘算光照的时候须要用到法线。
在末端一篇教程中,笔者会使用那二种变量,你也会学习到那两种变量怎么样真正使用起来得。
近来,大家曾经谈过了顶点着色器、片元着色器和三种着色器变量。是时候来看一个我们得以创造的最简单易行的着色器了。
5.Hello World(译者吐槽:能还是不可能不要秀意大利语啊)
那时有三个最简易的终点着色器:

自身想探究一下着色器。在Three.js帮忙您免去了许多烦劳以前,原生WebGL就非常漂亮艳了。有的时候,你只怕会想要完毕都部队分特定的法力,可能想对表未来你的荧屏上的东西钻研得更尖锐部分,那么着色器一定会进入你的视野。假如您像自家同一,你也如出一辙希望实现部分比上一篇教程中的基础越发有意思的东西。这篇教程中,作者会讲解Three.js的根底,那些基础实际上为大家做了诸多干燥的干活。
在发轫从前自身还要说,那篇教程会有非常多的篇幅在分解着色器的代码,之后会有一篇教程会在着色器代码的基本功上发展一点,利用着色器去做点什么。这是因为着色器shaders第2随即上去并不错懂,供给部分演讲。
1.三种着色器
WebGL没有固定的渲染管线,你不恐怕直接利用二个黑盒子式的着色器(译者注:上个世纪的显卡基本都只帮衬固定渲染管线);WebGL提供的是可编程的管线,那种办法更强劲但也更难领悟和平运动用。长话短说,可编制程序渲染管线意味着编写程序的人要协调肩负获取极限并将它绘制在显示器上了。着色器是渲染管线的一部分,有两种着色器:
1.顶点着色器
2.片元着色器
你应当了然的是,这两种着色器都统统运营在显卡的GPU上,大家将必要它们处理的多寡从CPU上卸下,装到GPU上,减轻了CPU的南开。现代的GPU对着色器供给的调用的运算类型都做了大幅优化,那样做很值得。
2.顶点着色器
基元形状,比如七个圆球,是由顶点构成的,是吧?顶点着色器被逐一传入那么些极端中的二个终极,然后处理它。怎样处理各种终端是能够自由定制的,但终端着色器有贰个必做的事,正是为三个名为
gl_Position
的变量赋值,该变量是1个4维数组,表示该终端最后在显示器上的职分。那本身是个有趣的历程,因为大家其实在议论怎样将1个三维坐标(四个具有x、y、z值得顶点)转化为,只怕说投影到二维的显示屏上。谢天谢地,就算大家应用Three.js之类的工具,我们能够如此福利地走访到
gl_Position 。
3.片元着色器
前些天大家有隐含顶点的三维物体了,将来要将物体投影到二维显示器上了,但颜色哪里去了?纹理和光照呢?那正是片元着色器要拍卖的。
和终端着色器类似,片元着色器有一项必须达成的天职:设置或免除变量
gl_FragColor
,另二个四维浮点变量,也正是片元点最后的水彩。什么是片元?想象3个有所多少个极端的三角形,片元正是因此那四个终端总括后的,全部在三角内部的点。由此,片元值由顶点的值内插生成。要是多个终极的颜料是银色,相邻顶点的颜色是灰黄,那么我们得以洞察到颜色从壬申革命顶点附近渐变,由深藕红变成法国红,最后在大青顶点附近变成深紫。
4.着色器变量
说到着色器变量,有两种:Uniforma,Attributes和Varyings。当自家首先次听到那八个词语时,笔者很迷惑,因为它们和作者事先运用的东西完全不兼容。但现行,你可以那样精通它们:
1.Uniforms变量既可以流传顶点着色器,也足以流传片元着色器,它们含有了如何在总体渲染过程中保持不变的变量,比如,2个点光源的职分。
2.Attributes变量对应于每一个终端,它们只好够流传顶点着色器中,比如每一种终端都兼备3个颜色。Attributes变量和顶峰的关联是各种对应的。
3.Varyings变量是在终端着色器中定义,并且准备传入给片元着色器的变量。为了保险那一点,我们需求保险在八个着色器中变量的品种和命名完全一致。3个经典的应用是法线向量,因为在盘算光照的时候须要用到法线。
在背后一篇教程中,作者会使用那三种变量,你也会学习到那三种变量怎么样确实使用起来得。
今昔,大家已经谈过了终点着色器、片元着色器和三种着色器变量。是时候来看一个大家能够创制的最简便易行的着色器了。
5.Hello World(译者吐槽:能还是不能不要秀葡萄牙共和国(República Portuguesa)语啊)
此刻有三个最简易的终极着色器:

本文的对象

本文的料想读者是:不熟知图形学,熟识前端,希望理解或连串学习 WebGL
的同校。

正文不是 WebGL 的概述性小说,也不是完好详细的 WebGL
教程。本文只期待变成一篇供 WebGL 初学者使用的纲领。

一 、在球体网格上海展览中心示纹理的观念艺术:

复制代码 代码如下:

复制代码 代码如下:

Canvas

熟知 Canvas 的同校都明白,Canvas 绘图先要获取绘图上下文:

var context = canvas.getContext('2d');

context上调用各样函数绘制图形,比如:

// 绘制左上角为(0,0),右下角为(50, 50)的矩形
context.fillRect(0, 0, 50, 50);

WebGL 同样需求取得绘图上下文:

var gl = canvas.getContext('webgl'); // 或 experimental-webgl

只是接下去,如若想画四个矩形的话,就没这么不难了。实际上,Canvas
是浏览器封装好的3个制图环境,在实际开始展览绘图操作时,浏览器还是供给调用
OpenGL API。而 WebGL API 大致正是 OpenGL API 未经封装,直接套了一层壳。

Canvas 的越来越多知识,能够参见:

  • JS
    权威指南的
    21.4 节或 JS
    高级程序设计中的
    15 章
  • W3CSchool
  • 阮一峰的 Canvas
    教程

一 、常见的一种星球表面绘制方法是如此的:

/**
* 每一种终端坐标乘以模型视图矩阵在乘以投影矩阵
* 得到在二维荧屏上的坐标
*/
void main() {
gl_Position = projectionMatrix *
modelViewMatrix *
vec4(position,1.0);
}

/**
* 每一个终端坐标乘以模型视图矩阵在乘以投影矩阵
* 获得在二维显示器上的坐标
*/
void main() {
gl_Position = projectionMatrix *
modelViewMatrix *
vec4(position,1.0);
}

矩阵变换

三维模型,从文件中读出来,到绘制在 Canvas 中,经历了频仍坐标变换。

一经有贰个最简便的模型:三角形,三个极端分别为(-1,-1,0),(1,-1,0),(0,1,0)。那八个数据是从文件中读出来的,是三角形最初步的坐标(局地坐标)。如下图所示,右手坐标系。

4858美高梅 1

模型平时不会放在场景的原点,假诺三角形的原点位于(0,0,-1)处,没有转动或缩放,多个极点分别为(-1,-1,-1),(1,-1,-1),(0,1,-1),即世界坐标。

4858美高梅 2

制图三维场景必须钦点2个旁观者,如若观望者位于(0,0,1)处而且看向三角形,那么七个极点相对于观望者的坐标为(-1,-1,-2),(1,-1,-2),(0,1,-2),即视图坐标。

4858美高梅 3

观望者的肉眼是一个点(这是看破投影的前提),水平视角和垂直视角都以90度,视野范围(目力所及)为[0,2]在Z轴上,观看者能够见到的区域是二个四棱台体。

4858美高梅 4

将四棱台体映射为规范立方(CCV,中央为原点,边长为2,边与坐标轴平行)。顶点在
CCV 中的坐标,离它说到底在 Canvas 中的坐标已经很类似了,就算把 CCV
的前表面看成 Canvas,那么最后三角形就画在图中铁红三角形的岗位。

4858美高梅 5

上述变换是用矩阵来进展的。

一些坐标 –(模型变换)-> 世界坐标 –(视图变换)-> 视图坐标
–(投影变换)–> CCV 坐标。

以(0,1,0)为例,它的齐次向量为(0,0,1,1),上述变换的代表经过能够是:

4858美高梅 6

地点多少个矩阵依次是看破投影矩阵,视图矩阵,模型矩阵。七个矩阵的值分别取决于:观望者的视角和视野距离,观望者在世界中的状态(地方和倾向),模型在世界中的状态(地方和动向)。总括的结果是(0,1,1,2),化成齐次坐标是(0,0.5,0.5,1),便是其一点在CCV中的坐标,那么(0,0.5)便是在Canvas中的坐标(认为
Canvas 核心为原点,长度宽度都为2)。

上边出现的(0,0,1,1)是(0,0,1)的齐次向量。齐次向量(x,y,z,w)能够代表三维向量(x,y,z)参预矩阵运算,通俗地说,w
分量为 1 时表示地点,w 分量为 0 时表示位移。

WebGL 没有提供别的关于上述变换的建制,开发者需求亲自计算顶点的 CCV
坐标。

关于坐标变换的越来越多内容,能够参照:

  • 微型总括机图形学中的5-7章
  • 改换矩阵@维基百科
  • 透视投影详解

相比较复杂的是模型变换中的绕任意轴旋转(平常用四元数生成矩阵)和投影变换(上面的例子都没收涉及到)。

有关绕任意轴旋转和四元数,能够参照:

  • 四元数@维基百科
  • 2个老外对四元数公式的辨证

有关齐次向量的越多内容,能够参考。

  • 电脑图形学的5.2节
  • 齐次坐标@维基百科

率先用三角近似的效仿七个圆球网格:

一个最不难易行的片元着色器:

贰个最简便易行的片元着色器:

着色器和光栅化

在 WebGL
中,开发者是由此着色器来形成上述变换的。着色器是运维在显卡中的程序,以
GLSL 语言编写,开发者必要将着色器的源码以字符串的格局传给 WebGL
上下文的有关函数。

着色器有二种,顶点着色器和片元(像素)着色器,它们成对出现。顶点着色器职责是接到顶点的部分坐标,输出
CCV 坐标。CCV
坐标经过光栅化,转化为逐像素的数量,传给片元着色器。片元着色器的天职是规定各样片元的水彩。

终极着色器接收的是 attribute 变量,是逐顶点的数量。顶点着色器输出
varying 变量,也是逐顶点的。逐顶点的 varying
变量数据经过光栅化,成为逐片元的 varying
变量数据,输入片元着色器,片元着色器输出的结果就会议及展览示在 Canvas 上。

4858美高梅 7

着色器功用很多,上述只是基本效用。超越四分之二炫酷的效劳都是依靠着色器的。假使您对着色器完全没有概念,能够试着明亮下一节
hello world 程序中的着色器再回想一下本节。

至于更多着色器的学识,能够参见:

  • GLSL@维基百科
  • WebGL@MSDN

4858美高梅 8

复制代码 代码如下:

复制代码 代码如下:

程序

采纳着色器在WebGL3D场景中呈现行星表面地形,技术储备指南。这一节解释绘制上述情景(三角形)的 WebGL
程序。点本条链接,查看源代码,试图领悟一下。那段代码出自WebGL
Programming
Guide,笔者作了部分改动以适应本文内容。假设一切平日,你看来的相应是上面那样:

4858美高梅 9

演说几点(假如此前不打听 WebGL ,多半会对上面包车型地铁代码疑忌,无碍):

  1. 字符串 VSHADER_SOURCE 和 FSHADER_SOU君越CE
    是终极着色器和片元着色器的源码。能够将着色器明白为有定点输入和出口格式的主次。开发者须要事先编写好着色器,再根据一定格式着色器发送绘图命令。

  2. Part2 将着色器源码编写翻译为 program
    对象:先分别编写翻译顶点着色器和片元着色器,然后连接两者。若是编写翻译源码错误,不会报
    JS 错误,但能够通过其他API(如gl.getShaderInfo等)获取编写翻译状态新闻(成功与否,假如出错的错误新闻)。

    // 顶点着色器
    var vshader = gl.createShader(gl.VERTEX_SHADER);
    gl.shaderSource(vshader, VSHADER_SOURCE);
    gl.compileShader(vshader);
    // 同样新建 fshader
    var program = gl.createProgram();
    gl.attachShader(program, vshader);
    gl.attachShader(program, fshader);
    gl.linkProgram(program);
    
  3. program
    对象必要钦定使用它,才方可向着色器传数据并绘制。复杂的先后日常有多少个program 对 象,(绘制每一帧时)通过切换 program
    对象绘制场景中的分裂功能。

    gl.useProgram(program);
    
  4. Part3 向正在使用的着色器传入数据,包含逐顶点的 attribute
    变量和全局的 uniform 变量。向着色器传入数据必须运用
    ArrayBuffer,而不是例行的 JS 数组。

    var varray = new Float32Array([-1, -1, 0, 1, -1, 0, 0, 1, 0])
    
  5. WebGL API 对 ArrayBuffer
    的操作(填充缓冲区,传入着色器,绘制等)都以通过 gl.A安德拉RAY_BUFFE奥迪Q3举行的。在 WebGL 系统中又很多看似的状态。

    // 只有将 vbuffer 绑定到 gl.ARRAY_BUFFER,才可以填充数据
    gl.bindBuffer(gl.ARRAY_BUFFER, vbuffer);
    // 这里的意思是,向“绑定到 gl.ARRAY_BUFFER”的缓冲区中填充数据
    gl.bufferData(gl.ARRAY_BUFFER, varray, gl.STATIC_DRAW);
    // 获取 a_Position 变量在着色器程序中的位置,参考顶点着色器源码
    var aloc = gl.getAttribLocation(program, 'a_Position');
    // 将 gl.ARRAY_BUFFER 中的数据传入 aloc 表示的变量,即 a_Position
    gl.vertexAttribPointer(aloc, 3, gl.FLOAT, false, 0, 0);
    gl.enableVertexAttribArray(aloc);
    
  6. 向着色器传入矩阵时,是按列存款和储蓄的。能够相比较一下 mmatrix
    和矩阵变换一节中的模型矩阵(第 3 个)。

  7. 终极着色器总括出的 gl_Position 就是 CCV
    中的坐标,比如最上边的终端(天灰)的 gl_Position
    化成齐次坐标正是(0,0.5,0.5,1)。

  8. 向终极着色器传入的只是多个极点的颜色值,而三角形表面包车型客车水彩渐变是由那多少个颜色值内插出的。光栅化不仅会对
    gl_Position 进行,还会对 varying 变量插值。

  9. gl.drawArrays()方法使得缓冲区实行绘图,gl.TTiguanIANGLES
    内定绘制三角形,也能够变动参数绘制点、折线等等。

关于 ArrayBuffer 的详细新闻,能够参见:

  • ArrayBuffer@MDN
  • 阮一峰的 ArrayBuffer
    介绍
  • 张鑫旭的 ArrayBuffer
    介绍

关于 gl.T奥迪Q5IANGLES
等别的绘制形式,能够参照上面那张图或那篇博文。

4858美高梅 10

以此不难场景的代码如下:

/**
* 将任意2个像元色设置为白色
*/
void main() {
gl_FragColor = vec4(1.0, // R
0.0, // G
1.0, // B
1.0); // A
}

/**
* 将任意一个像元色设置为米白
*/
void main() {
gl_FragColor = vec4(1.0, // R
0.0, // G
1.0, // B
1.0); // A
}

深度检查和测试

当八个外表重叠时,前边的模子会遮掩前边的模型。比如其一例子,绘制了多少个交叉的三角(
varray 和 carray 的尺寸变为 18,gl.drawArrays 最终1个参数变为
6)。为了不难,那几个例子去掉了矩阵变换进程,间接向着色器传入 CCV 坐标。

4858美高梅 11

4858美高梅 12

顶点着色器给出了 6 个终端的 gl_Position ,经过光栅化,片元着色器获得了
2X 个片元(假诺 X 为各种三角形的像素个数),每一个片元都离散的 x,y
坐标值,还有 z 值。x,y 坐标就是三角形在 Canvas
上的坐标,但倘诺有几个拥有同样 x,y 坐标的片元同时出现,那么 WebGL
就会取 z 坐标值较小的不胜片元。

在深度检查和测试以前,必须在绘制前拉开一个常量。不然,WebGL 就会奉公守法在 varray
中定义的逐条绘制了,前面包车型客车会覆盖前面包车型地铁。

gl.enable(gl.DEPTH_TEST);

骨子里,WebGL 的逻辑是那般的:依次拍卖片元,假使渲染缓冲区(那里就是Canvas
了)的百般与如今片元对应的像素还未曾绘制时,就把片元的颜色画到渲染缓冲区对应像素里,同时把片元的
z
值缓存在另三个纵深缓冲区的均等地方;假设当前缓冲区的呼应像素已经绘制过了,就去查看深度缓冲区中对应地点的
z 值,假使当前片元 z 值小,就重绘,不然就舍弃当前片元。

WebGL 的那套逻辑,对领会蒙版(前边会说到)有一部分协理。

4858美高梅 134858美高梅 14

那便是整套了。假若今日一向运转以来,你就能够在显示器上观察二个“无光”的粉深草绿形体。不是很复杂,是啊?

那正是全部了。假使今后直接运维以来,你就足以在显示屏上看看一个“无光”的粉铁灰形体。不是很复杂,是吗?

顶点索引

gl.drawArrays()是依据顶点的种种绘制的,而
gl.drawElements()能够令着色器以三个索引数组为顺序绘制顶点。比如其一例子。

4858美高梅 15

此地画了七个三角形,但只用了 5个极点,有一个终端被五个三角形共用。那时要求建立索引数组,数组的每一种成分表示顶点的索引值。将数组填充至gl.ELEMENT_ARRAY,然后调用
gl.drawElements()。

var iarray = new Uint8Array([0,1,2,2,3,4]);
var ibuffer = gl.createBuffer(gl.ARRAY_BUFFER, ibuffer);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, iarray, gl.STATIC_DRAW);
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>演示球体的绘制</title>
 6     <link href="../../CSS/newland.css" rel="stylesheet">
 7     <link href="../../CSS/stat.css" rel="stylesheet">
 8     <script src="../../JS/LIB/babylon.32.all.max.js"></script>
 9     <script src="../../JS/LIB/stat.js"></script>
10 </head>
11 <body>
12 <div id="div_allbase">
13     <canvas id="renderCanvas"></canvas>
14     <div id="fps" style="z-index: 301;"></div>
15 </div>
16 </body>
17 <script>
18     var canvas,engine,scene,gl;
19     canvas = document.getElementById("renderCanvas");
20     engine = new BABYLON.Engine(canvas, true);
21     gl=engine._gl;//决定在这里结合使用原生OpenGL和Babylon.js;
22     scene = new BABYLON.Scene(engine);
23     var divFps = document.getElementById("fps");
24     //全局对象
25     var light0//全局光源
26             ,camera0//主相机
27             ;
28     window.onload=webGLStart;
29     window.addEventListener("resize", function () {
30         engine.resize();
31     });
32     function webGLStart()
33     {
34         gl=engine._gl;
35         createScene();
36         MyBeforeRender();
37     }
38     var createScene = function (engine) {
39         camera0 =new BABYLON.FreeCamera("FreeCamera", new BABYLON.Vector3(0, 0, -20), scene);
40         camera0.attachControl(canvas, true);
41         camera0.speed=0.5;
42         camera0.minZ=0.0001;
43         light0 = new BABYLON.HemisphericLight("Hemi0", new BABYLON.Vector3(0, 1, 0), scene);
44         sphere1=BABYLON.MeshBuilder.CreateSphere("sphere1",{segments:10,diameter:10.0},scene);
45         var groundMaterial = new BABYLON.StandardMaterial("groundMat", scene);
46         groundMaterial.wireframe=true;
47         sphere1.material=groundMaterial;
48 
49     }
50     function MyBeforeRender()
51     {
52         scene.registerBeforeRender(function() {
53             if(scene.isReady())
54             {
55 
56             }
57         });
58         engine.runRenderLoop(function () {
59             engine.hideLoadingUI();
60             if (divFps) {
61                 // Fps
62                 divFps.innerHTML = engine.getFps().toFixed() + " fps";
63             }
64             scene.render();
65         });
66 
67     }
68 </script>
69 </html>

在终极着色器中,大家透过Three.js传入了一部分uniforms变量。有多个4×4的矩阵uniforms变量:模型视图矩阵和投影矩阵。你并不必要太领会那八个矩阵是怎么工作的。简单地说,那八个矩阵描述了三维点坐标如何投影成为二维显示屏上的坐标。

在巅峰着色器中,大家通过Three.js传入了一部分uniforms变量。有七个4×4的矩阵uniforms变量:模型视图矩阵和投影矩阵。你并不须要太领会那多少个矩阵是怎么工作的。简单地说,那八个矩阵描述了三维点坐标怎样投影成为二维显示屏上的坐标。

纹理

attribute
变量不仅能够传递顶点的坐标,还足以传递别的任何逐顶点的数码。比如
HelloTriangle 程序把单个顶点的颜料传入了 a_Color,片元着色器收到
v_Color 后一贯赋给 gl_FragmentColor,就控制了颜色。

attribute
变量还足以协助绘制纹理。绘制纹理的基本原理是,为各类终端钦定三个纹理坐标(在(0,0)与(1,1,)的四方形中),然后传入纹理对象。片元着色器获得的是对应片元的内插后的纹理坐标,就选择那几个纹理坐标去纹理对象上取颜色,再画到片元上。内插后的纹理坐标很可能不正好对应纹理上的某些像素,而是在多少个像素之间(因为一般的图样纹理也是离散),那时只怕会透过周围多少个像素的加权平均算出该像素的值(具体有几各个分歧措施,能够参照)。

比如本条事例。

4858美高梅 16

纹理对象和缓冲区指标很接近:使用 gl 的 API 函数创立,需求绑定至常量
gl.ALANDRAY_BUFFER 和 gl.TEXTURE_2D
,都由此常量对象向里面填入图像和数据。区别的是,纹理对象在绑定时还索要激活1个纹理单元(此处的gl.TEXTURE0),而
WebGL 系统援救的纹路单元个数是很不难的(一般为 8 个)。

var texture = gl.createTexture();
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, textureImage);
var sloc = gl.getUniformLocation(program, 'u_Sampler');
gl.uniform1i(sloc, 0);

片元着色器内注明了 sampler2D 类型的 uniform
变量,通过texture2D函数取样。

precision mediump float;
uniform sampler2D u_Sampler;
varying vec2 v_TexCoord;
void main() {
  gl_FragColor = texture2D(u_Sampler, v_TexCoord);
};

View Code

骨子里,笔者只介绍了这两段简短的代码段。Three.js在您本人的着色器代码前已经将它们加进去了,所以你不要顾虑。实话说,Three.js还加了许多事物在你的代码后边,比如光照数据、节点颜色和节点法向量等等。借使没有Three.js你要亲自创设并安装那几个目的,真的。
6.采取着色器材质

实际上,作者只介绍了那两段简短的代码段。Three.js在您本身的着色器代码前曾经将它们加进去了,所以你不用顾虑。实话说,Three.js还加了好多东西在你的代码前边,比如光照数据、节点颜色和节点法向量等等。如若没有Three.js你要亲自创立并安装那几个指标,真的。
6.用到着色器材质

混合与蒙版

透明效果是用混合机制形成的。混合机制与深度检查和测试类似,也产生在试图向有个别已填写的像素填充颜色时。深度检查和测试通过比较z值来规定像素的水彩,而掺杂机制会将三种颜色混合。比如其一例子。

4858美高梅 17

混合的各种是服从绘制的逐条实行的,假诺绘制的逐条有变化,混合的结果平常也不及。如若模型既有非透明表面又有晶莹剔透表面,绘制透明表面时打开蒙版,其目标是锁定深度缓冲区,因为半晶莹剔透物体后边的物体还能够看出的,假若不这么做,半晶莹剔透物体后边的实体将会被深度检查和测试机制排除。

打开混合的代码如下。gl.blendFunc方法钦定了混合的章程,那里的情趣是,使用源(待混合)颜色的
α 值乘以源颜色,加上 1-[源颜色的 α]乘以指标颜色。

gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);

所谓 α 值,就是颜色的第 4 个轻重。

var carray = new Float32Array([
  1,0,0,0.7,1,0,0,0.7,1,0,0,0.7,
  0,0,1,0.4,0,0,1,0.4,0,0,1,0.4
  ]);

接下来将一张纹理贴图的纹路坐标对应到球体网格中的各个三角形上,具体原理如下:

复制代码 代码如下:

复制代码 代码如下:

浏览器的WebGL系统

WebGL 系统依次组成都部队分在既定规则下相互合作。稍作梳理如下。

4858美高梅 18

那张图相比轻易,箭头上的文字表示
API,箭头方向大概表现了数额的流淌方向,不必深究。

4858美高梅 19

/**
* 假如我们得以接纳JQuery
* 将着色器的代码文本从DOM中抽取出来
*/
var vShader = $(‘vertexshader’);
var fShader = $(‘fragmentshader’);
var shaderMaterial =
new THREE.ShaderMaterial({
vertexShader: vShader.text(),
fragmentShader: fShader.text()
});

/**
* 假诺我们能够运用JQuery
* 将着色器的代码文本从DOM中抽取出来
*/
var vShader = $(‘vertexshader’);
var fShader = $(‘fragmentshader’);
var shaderMaterial =
new THREE.ShaderMaterial({
vertexShader: vShader.text(),
fragmentShader: fShader.text()
});

光照

WebGL 没有为光照提供任何内置的方法,需要开发者在着色器中落实光照算法。

只但是有颜色的,模型也是有颜色的。在光照下,最后物体彰显的颜色是二者联手成效的结果。

兑现光照的格局是:将光照的数额(点光源的地点,平行光的来头,以及光的水彩和强度)作为
uniform 变量传入着色器中,将物体表面每种顶点处的法线作为 attribute
变量传入着色器,服从光照规则,修订最后片元展现的水彩。

光照又分为逐顶点的和逐片元的,两者的分别是,将法线光线交角因素位居顶点着色器中考虑依然放在片元着色器中考虑。逐片元光照更是绘影绘声,2个极端的例子是:

4858美高梅 20

那时,点光源在相距2个外表较近处,表面焦点 A
处较亮,四周较暗。可是在逐顶点光照下,表面包车型大巴水彩(的影响因子)是由顶点内插出来的,所以表面中心也会比较暗。而逐片元光照直接动用片元的职分和法线总结与点光源的交角,因而表面宗旨会相比亮。

4858美高梅 21

从此刻初始,Three.js将会编写翻译并运转你的着色器,将其总是在你创制的质量上,材料又依附于您成立的mesh上。它并没有变得比真的更易于。可能是如此吧,但大家在考虑浏览器3D编制程序,笔者想你应该预期,那个话题是有肯定复杂性的。

从此刻初阶,Three.js将会编写翻译并运转你的着色器,将其一连在你创建的质量上,材料又依附于您创建的mesh上。它并没有变得比真的更易于。恐怕是如此啊,但大家在考虑浏览器3D编制程序,作者想你应该预期,那一个话题是有必然复杂性的。

复杂模型

复杂模型或许有囊括子模型,子模型恐怕与父模型有相对运动。比如开着雨刮器的小车,雨刮器的世界坐标是受父模型小车,和本身的意况共同决定的。若要总计雨刮器某顶点的职位,须要用雨刮器相对小车的模子矩阵乘上海小车公司股份有限公司车的模子矩阵,再乘以顶点的有的坐标。

复杂模型只怕有成都百货上千外部,也许每种表面使用的着色器就分歧。平时将模型拆解为组,使用同样着色器的外表为一组,先绘制同一组中的内容,然后切换着色器。每便切换着色器都要再一次将缓冲区中的数据分配给着色器中相应变量。

如上内容引用自吴亚峰著《OpenGLES3.x游戏开发》,贝布ylon.js中的纹理对应规则能够参见

大家仍是能够像着色器质感添加此外两种属性:uniforms和attributes。他们得以是向量、整数或许浮点数,不过如本身前面所说,uniforms变量在计算机技术探究全数点的历程中维系不变,所以它们进一步可能是十足的值,而attributes变量是对每个终端而言的,所以他们应当是数组。在3个mesh中,attribute变量和终极应当是各类对应的。
7.小结
那篇教程就到这边了,实际上作者一度讲得很多了,不过在比比皆是方面笔者都只是一掠而过。在下一篇教程中笔者会提供一个繁杂的着色器,通过它本人将盛传一些attributes变量和uniforms变量来做一些模拟光照效果。
自己将那篇教程的源码打包了,你能够下载下来当作参考

我们还是可以像着色器质感添加别的三种属性:uniforms和attributes。他们能够是向量、整数或然浮点数,可是如本人前边所说,uniforms变量在盘算全体点的进度中保险不变,所以它们进一步大概是纯粹的值,而attributes变量是对各样终端而言的,所以他们应有是数组。在叁个mesh中,attribute变量和顶峰应当是各种对应的。
7.小结
那篇教程就到那边了,实际上我曾经讲得很多了,可是在很多方面本人都只是一掠而过。在下一篇教程中笔者会提供1个扑朔迷离的着色器,通过它本人将盛传一些attributes变量和uniforms变量来做一些效仿光照效果。
自家将那篇教程的源码打包了,你可以下载下来当作参考

动画

动画片的规律正是急忙地擦除和重绘。常用的章程是红得发紫的
requestAnimationFrame
。不熟悉的同班,可以参考正美的介绍。

而是那种绘制格局存在以下多少个毛病:

Three.js是1个宏伟的开源WebGL库,WebGL允许JavaScript操作GPU,在浏览器端完毕真正意义的3D。但是近日那项技术还处在发展阶段,资料极为…

您或然感兴趣的篇章:

  • THREE.JS入门教程(1)THREE.JS使用前询问
  • THREE.JS入门教程(6)创立和谐的全景图完毕步骤
  • Three.js连忙入门教程
  • Three.js源码阅读笔记(Object3D类)
  • THREE.JS入门教程(4)创造粒子系统
  • Three.js源码阅读笔记(物体是怎样协会的)
  • THREE.JS入门教程(3)着色器-下
  • three.js落成围绕某物体转动
  • three.js 入门案例详解

WebGL库

如今最流行的 WebGL 库是
ThreeJS,很强劲,官网,代码。

a、为了将二维的图形映射到三维的球面上,图片可能纹理坐标必须透过复杂的“拓扑变换”(比如图中的南极洲明明经过了拉伸变换),那致使我们在行星表面点击贰个点时,很难直观的将它对应到图片上的某部像素,同时生成适合球面包车型客车图片也必要利用专门的工具举办测算。

调剂工具

对比成熟的 WebGL 调节和测试工具是WebGL
Inspector。

b、即使把各类三角形作为3个可互相对象,极地区域的可相互对象将过于密集,想象贰个回合制战棋游戏,玩家会发觉极地区域的格子太密而赤道相邻的格子太稀疏。

网络能源和图书

英文的关于 WebGL 的财富有众多,包蕴:

  • learning webgl
  • WebGL@MDN
  • WebGL Cheat
    Sheet

境内最早的 WebGL 教程是由郝稼力翻译的,放在 hiwebgl 上,最近 hiwebgl
已经关闭,但教程还足以在这里找到。郝稼力近日运行着Lao3D。

境内曾经问世的 WebGL 书籍有:

  • WebGL入门指南:其实是一本讲
    ThreeJS 的书
  • WebGL高级编制程序:尚可的一本
  • WebGL编制程序指南:极度可靠的圆满教程

c、在画面拉近时纹理贴图会因为音讯不足出现不受控制的混淆或变形,当然,大家得以在意见拉近时用越多的细节贴图来提供更加多的新闻,但那正是3个更广大的工程了。

为了避开上述缺点,作者说了算利用另一种球体纹理绘制形式。

二 、使用自定义着色器绘制自定义纹理:

① 、在Babylon.js引擎中央银行使自定义着色器:

Babylon.js通过“着色器质感”对象提供对自定义着色器的帮助:

 1 var amigaMaterial = new BABYLON.ShaderMaterial("amiga", scene,{
 2                         vertexElement: "sh1v4.sh",
 3                         fragmentElement: "sh1f4.sh",
 4                     },
 5                     {
 6                         attributes: ["position"],
 7                         uniforms: ["worldViewProjection","worldView"]
 8                     });
 9             amigaMaterial.setVector4("uColor", new BABYLON.Vector4(0.0,1.0,0.0,1.0));
10             sphere1.material=amigaMaterial;

内部ShaderMaterial构造方法的第1个参数是材质名称、首个参数是情景对象、第三个参数标明了顶点着色器和片元着色器的文件名称,参考Babylon.js源码能够见到引擎帮忙的二种着色器代码对象命名格局:

 1 Effect.prototype._loadFragmentShader = function (fragment, callback) {
 2             // DOM element ?着色器代码是DOM标签中的内容
 3             if (fragment instanceof HTMLElement) {
 4                 var fragmentCode = BABYLON.Tools.GetDOMTextContent(fragment);
 5                 callback(fragmentCode);
 6                 return;
 7             }
 8             // Base64 encoded ?着色器代码使用了base64编码
 9             if (fragment.substr(0, 7) === "base64:") {
10                 var fragmentBinary = window.atob(fragment.substr(7));
11                 callback(fragmentBinary);
12                 return;
13             }
14             // Is in local store ?着色器代码在Babylon.js自带的着色器代码库里
15             if (Effect.ShadersStore[fragment + "PixelShader"]) {
16                 callback(Effect.ShadersStore[fragment + "PixelShader"]);
17                 return;
18             }
19             if (Effect.ShadersStore[fragment + "FragmentShader"]) {
20                 callback(Effect.ShadersStore[fragment + "FragmentShader"]);
21                 return;
22             }
23             var fragmentShaderUrl;//着色器代码是一个单独的文件,需要通过Ajax加载
24             if (fragment[0] === "." || fragment[0] === "/" || fragment.indexOf("http") > -1) {
25                 fragmentShaderUrl = fragment;
26             }
27             else {//默认情况下Engine.ShadersRepository = "src/Shaders/";
28                 fragmentShaderUrl = BABYLON.Engine.ShadersRepository + fragment;
29             }
30             // Fragment shader
31             BABYLON.Tools.LoadFile(fragmentShaderUrl + ".fragment.fx", callback);
32         };

本人选择了最终一种方法,将着色器文件放在/src/Shaders/上边,不要遗忘给着色器文件添加后缀:

4858美高梅 22

 第八个参数是亟需贝布ylon.js从内部存储器向显卡传递的“暗中同意”变量,个中attributes里是Babylon.js的各个暗中同意的极端数据,能够采纳Mesh.geometry._vertexBuffers里的以下二种顶点数据传给着色器:

4858美高梅 23

那里小编只采纳了将各样终端的岗位传递给着色器,Babylon.js引擎替我们开始展览了编译链接着色器程序、绑定缓存等一多种操作(贝布ylon.js中以“_”初步的变量一般都是在渲染过程中建立的,只有在渲染起首后才有值)。

一经一个网格有一千个极点,那么那1000个极点的岗位数据将被分别发送到显卡上的1000个终端着色器中,每一种着色器使用收取的顶点数据进行总结。

uniforms里是Babylon.js向显卡发送的默许通用变量,在那之中world对应网格的变换矩阵,View是相机的转换矩阵,Projection是相机的投影矩阵,worldViewProjection是四个矩阵变换的会晤(关于矩阵变换能够参照

对于具有的着色器uniforms型数据都以通用的,比如上边提到的一千个极点着色器都会接纳同样的”worldViewProjection”和”worldView”变量。attributes和uniforms都属于OpenGL的“存款和储蓄限定符”。

第十行代码设定了2个非默许的uniforms型变量,第8行将以此材料交给球体网格。

贰 、WebGL版本选拔:

在拓展glsl编制程序从前,3个主要的步调是采用要运用的WebGL版本:

OpenGL发展历史如下:(源文件地址:

4858美高梅 24

可知WebGL1.0对应早期的OpenGL2.x,WebGL2.0对应较新的OpenGL4.x,鲜明WebGL2.0的效能尤为强大,但考虑到自家的记录本显卡不支持WebGL2.0,只能选拔旧的WebGL1.0。本文前边的glsl编制程序均采纳OpenGL2.0的语法,OpenGL2.0存在诸多弱点,所之前边的一对内容也正是为了缓解那一个老毛病而编写制定的。

Babylon.js能够自动物检疫查和测试电脑扶助的WebGL版本,并预先利用新型版:

 1 // GL
 2             if (!options.disableWebGL2Support) {
 3                 try {
 4                     this._gl = (canvas.getContext("webgl2", options) || canvas.getContext("experimental-webgl2", options));
 5                     if (this._gl) {
 6                         this._webGLVersion = 2.0;
 7                     }
 8                 }
 9                 catch (e) {
10                     // Do nothing
11                 }
12             }
13             if (!this._gl) {
14                 if (!canvas) {
15                     throw new Error("The provided canvas is null or undefined.");
16                 }
17                 try {
18                     this._gl = (canvas.getContext("webgl", options) || canvas.getContext("experimental-webgl", options));
19                 }
20                 catch (e) {
21                     throw new Error("WebGL not supported");
22                 }
23             }
24             if (!this._gl) {
25                 throw new Error("WebGL not supported");
26             }

Babylon.js源码里包罗过多实用的3D编制程序工具,尽管不利用贝布ylon.js引擎也足以使用在那之中的工具简化原生WebGL开发。

3、简单glsl代码:

 测试用的极端着色器代码:

 1 uniform mat4 worldViewProjection;
 2 uniform mat4 worldView;
 3 attribute vec3 position;
 4 
 5 varying vec3 vPosition;
 6 
 7 void main(){
 8     gl_Position=worldViewProjection*vec4(position,1);    
 9     vPosition=vec3(worldView*vec4(position,1));
10 }

那里varying是WebGL1.0中的第二种存款和储蓄限定符,它表示这一个变量是极端着色器的总括结果,经过插值后传出片元着色器(关于WegGL1.0基础知识,推荐观看作者以前公布的3D编制程序入门教程)

gl_Position是OpenGL的放权变量,表示那几个终端经过各个矩阵变换之后在视口中渲染的职位,vPositon指顶点相对于相机的地方。

亟需专注的是:除构造函数外,glsl不帮忙浮点数和整形数里面包车型地铁机动转换,浮点数通过“int
i=int(f)”转为整形数,整形数通过”float
f=float(i)”转换为浮点数,上述代码中的vec4()和vec3()则分别是四元浮点数组和伊利浮点数组的构造函数,其它WebGL1.0不持有松开的四舍五入函数,供给选用“floor(f+0.5)”代替四舍五入,并且四舍五入之后仍旧是浮点数而非整形数。

除外数组的索引外,着色器中多方面包车型大巴总括都以浮点计算,而将整形计算的结果作为数组索引时也会遇见难题,后边会详细谈论什么处理这一题材。

片元着色器代码:

 1 precision mediump float;
 2 varying vec3 vPosition;
 3 uniform vec4 uColor;
 4 void main()
 5 {
 6     vec4 tempColor=uColor;
 7     //对2取模,参数必须是浮点型
 8     if(mod((vPosition.x+vPosition.y+vPosition.z),2.0)>1.0)
 9     {
10         tempColor+=vec4(0.0,-0.4,0.0,0.0);
11     }
12     gl_FragColor=tempColor;
13 }

gl_FragColor是一个内置变量,表示片元的终极颜色,注意glsl中的颜色值从0.0到1.0,而不是html中的0到255。

实施代码效果如下:

4858美高梅 25

足见,随着相机的移动,球体的纹理自动发生变化,那类效果是很难用贴图格局完成的。

三 、生成并保存简单的棋盘地形

假定行星的周长为50000km,将每一个地块设为长度宽度均为100km的长方形,生成并保存1个饱含陆仟0三个地块的棋盘型地面:

壹 、数据保存:

考虑到各个地块都要有所独立的相互能力,使用文件措施保存功用非常的低,尝试了html5的地点存款和储蓄成效,发现Chrome浏览器的地面存款和储蓄空间唯有5M,难以支撑布置中的对多个行星数量的保留,最后决定使用h2微型数据库保存地块数据(读者能够友善找寻关于h2数据库的学识,笔者的摄像教程里也事关了有些连锁文化)。

a、在数据库中国建工业总会集团表:

将行星想象为二个二分之一在地上二分一在私下的建造,不一致的纬度对应了不一样的层数,每一层有几四个分寸相同的房间

 1 --建立地区块表
 2 create table tab_dqk (
 3 ID varchar(40) NOT NULL,
 4 planetid varchar(40),
 5 beta double,
 6 pbeta double,
 7 alpha double,
 8 palpha double,
 9 weight varchar(1000)
10 );
11 comment on table tab_dqk is '地区块表';
12 comment on column tab_dqk.id is '主键ID';
13 comment on column tab_dqk.planetid is '地区块所属的行星id';
14 comment on column tab_dqk.beta is '地区块的仰角';
15 comment on column tab_dqk.pbeta is '地区块仰角的区分度';--即这个beta仰角上下pbeta弧度都属于这一层
16 comment on column tab_dqk.alpha is '地区块水平转角';
17 comment on column tab_dqk.palpha is '地区块水平转角的区分度';--即这个alpha水平转角左右palpha弧度都属于这个房间
18 comment on column tab_dqk.weight is '用JSON表示的地形类型id权重';
19 
20 alter table tab_dqk add column floor int;
21 alter table tab_dqk add column room int;
22 alter table tab_dqk add column altitude double;
23 
24 comment on column tab_dqk.floor is '地区块位于第几层';
25 comment on column tab_dqk.room is '地区块位于这一层的第几个房间';
26 comment on column tab_dqk.altitude is '地区块的海拔高度';
27 comment on column tab_dqk.weight is '地区块类型';

 1 --建立行星表
 2 create table tab_planet
 3 (
 4 id varchar(40) NOT NULL,
 5 name varchar(20) NOT NULL,
 6 coreid varchar(40),
 7 min_floor int NOT NULL,
 8 max_floor int NOT NULL,
 9 width_room int NOT NULL,
10 radius double,
11 mass double,
12 gravity double,
13 orbit double,
14 cycle double
15 );
16 comment on table tab_planet is '行星表';
17 comment on column tab_planet.id is '主键ID';
18 comment on column tab_planet.name is '行星名字';
19 comment on column tab_planet.coreid is '围绕旋转的主星id';
20 comment on column tab_planet.min_floor is '最低层数';
21 comment on column tab_planet.max_floor is '最高层数';
22 comment on column tab_planet.width_room is '数据宽度';
23 comment on column tab_planet.radius is '半径(km)';
24 comment on column tab_planet.mass is '质量(t)';
25 comment on column tab_planet.gravity is '重力加速度';
26 comment on column tab_planet.orbit is '同步轨道高度';
27 comment on column tab_planet.cycle is '自转周期';
28 
29 alter table tab_planet add column perimeter int;
30 
31 comment on column tab_planet.perimeter is '行星周长';

b、dao实现

历史观MVC思想认为浏览器端的平安没有有限支撑,必须在浏览器和数据库之间投入一种“后台程序”来进步安全性,这种程序平日由JAVA、C#兑现,近些年也油但是生了过多由python和JavaScript达成的后台程序。那个后台程序一般包含三层:负责接收浏览器访问的service层、负责将一定访问参数和数量涉嫌起来的application层,负责访问数据库的dao层。

只是作者认为那个试验中的全部加入者都以可相信任的,所以为了程序的简要要尝试去掉后台程序,作者发现h2数据库服务支撑http协议通讯,通过选用Fiddler对h2的网页控制台实行抓包,编写了一贯用浏览器和数据库通讯的代码:(代码在Linkh2.js中)

 1 /**
 2  * Created by lz on 2018/5/15.
 3  */
 4 var jsessionid="";
 5 var Url="";
 6 var UrlHead="http://127.0.0.1:8082/";
 7 var H2State="offline";
 8 var H2LoginCallback;//回调函数对象
 9 function H2Login(func)
10 {
11     H2LoginCallback=func;
12     Url=UrlHead+"";
13     Argv="";
14     Request(xmlHttp,"POST",Url,true,Argv,"application/x-www-form-urlencoded",H2LoginCallBack,0);
15 }
16 function H2LoginCallBack()
17 {
18     if(xmlHttp.readyState==4) {
19         if(isTimout=="1")
20         {
21             alert("登陆验证请求超时!!");
22             clearTimeout(timer);
23             xmlHttp.abort();
24         }
25         else {
26             if (xmlHttp.status == 200) {
27                 clearTimeout(timer);//停止定时器
28                 try
29                 {
30                     var str_id=xmlHttp.responseText;
31                     xmlHttp.abort();
32                     jsessionid=str_id.substr(str_id.search(/jsessionid/)+11,32) ;//从h2获取一个jsessionid
33                     console.log(jsessionid);
34                     H2Login2();
35                 }catch(e)
36                 {
37                     alert(e);
38                     console.error(e)
39                     xmlHttp.abort();
40                 }
41             }
42         }
43     }
44 }
45 function H2Login2()
46 {
47     Url=UrlHead+"login.do?jsessionid="+jsessionid;//用这个jsessionid登录
48     Argv="language=en&setting=Generic H2 (Embedded)&name=Generic H2 (Embedded)" +
49         "&driver=org.h2.Driver&url=jdbc:h2:tcp://127.0.0.1/../../datawar" +
50         "&user=datawar&password=datawar";
51     Request(xmlHttp,"POST",Url,true,Argv,"application/x-www-form-urlencoded",H2Login2CallBack,0);
52 }
53 function H2Login2CallBack()
54 {
55     if(xmlHttp.readyState==4) {
56         if(isTimout=="1")
57         {
58             alert("登陆验证请求超时!!");
59             clearTimeout(timer);
60             xmlHttp.abort();
61         }
62         else {
63             if (xmlHttp.status == 200) {
64                 clearTimeout(timer);//停止定时器
65                 try
66                 {
67                     var str_logres=xmlHttp.responseText;//这时已经在h2服务端建立登录状态
68                     xmlHttp.abort();
69                     console.log("完成h2数据库登录");
70                     H2State="online";
71                     //Query();
72                     //CreateChess();//测试时将运算启动放在这里,实际使用时,通过渲染循环检测H2State标志来启动运算
73                     H2LoginCallback();//这样可以执行函数对象吗????《-可以
74                 }catch(e)
75                 {
76                     alert(e);
77                     console.error(e)
78                     xmlHttp.abort();
79                 }
80             }
81         }
82     }
83 }

其间“Request”是多少个Ajax请求函数,内容如下:

4858美高梅 264858美高梅 27

 1 /**
 2  * Created by Administrator on 2015/1/28.
 3  * Update by Administrator on 2015/09/17.
 4  */
 5 //Ajax通信页面
 6 var xmlHttp=createXMLHttpRequest();
 7 var isTimout="0";//0表示未超时
 8 var timer;//用来存定时器
 9 function createXMLHttpRequest()
10 {
11     var xhr=false;
12     if(window.XMLHttpRequest)
13     {
14         try
15         {
16             netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
17         }
18         catch(e)
19         {
20 
21         }
22         xhr=new XMLHttpRequest();
23     }
24     else if(window.ActiveXObject)
25     {
26         try
27         {
28             xhr=new window.ActiveXObject("Msxm12.XMLHTTP");
29 
30         }
31         catch(e)
32         {
33             try
34             {
35                 xhr=new window.ActiveXObject("Microsoft.XMLHTTP");
36             }
37             catch(e)
38             {
39 
40                 alert("您的浏览器不支持ajax!");
41 
42             }
43         }
44     }
45     return xhr;
46 }
47 //目前的版本为单线程的ajax访问,不能支持多个ajax同时访问
48 Request=function(xhr,method,src,isajax,argv,content_type,recallfunc,timeout)
49 {//连接对象、连接方式、连接目的地址、是否异步、提交内容、表单的内容类型、回调函数、 超时时间
50     xmlHttp.open(method,src,isajax)//第三个参数为true为异步方式
51     if(method=="POST")
52         xmlHttp.setRequestHeader("Content-Type",content_type);
53     xmlHttp.setRequestHeader("Access-Control-Allow-Origin","*");
54     xmlHttp.onreadystatechange=recallfunc;
55     xmlHttp.send(argv);
56     isTimout="0";
57     if(timeout==1) {//0表示不检测超时,1表示检测超时,好像不好使??
58         timer = window.setTimeout("dualTimeout();", timeout);
59     }
60 }

View Code

看得出浏览器直接接纳公开传递了数据库用户名和密码

② 、数据变化

代码如下:(testchess.html)

 1 //根据极坐标和变化的半径生成颜色交错的地区块
 2     function CreateChess()
 3     {
 4         var size_dqk=100;//每个地区块的长宽都是100km
 5         var perimeter_planet=40000;//这个行星的周长是40000km
 6         var r_planet=perimeter_planet/(2*Math.PI);//行星的半径
 7      
 8 
 9         //我们将行星的表面想象成一个一半在地下一半在地上的建筑,len_beta就是根据周长算得的地上或地下的层数
10         var len_beta=sswr(((perimeter_planet/2)/size_dqk)/2);//通过长度而不是弧度来分层!!
11         var pbeta=(Math.PI/4)/len_beta;
12 
13         //对于每一层,
14         for(var i=-len_beta;i<=len_beta;i++)
15         {
16             var rad_beta=(Math.PI/2)*(i/len_beta);
17             var r_floor=Math.cos(rad_beta)*r_planet;//这一层的半径
18             var len_alpha_floor=sswr((r_floor*2*Math.PI)/size_dqk);//根据这一层的周长算出这一层有多少个房间
19             var palpha=Math.PI/len_alpha_floor;//每一个地区块的角度边界,在这个边界范围内即属于这个地区块
20             var beta=i*pbeta;
21             console.log(i+"/"+len_beta+">"+len_alpha_floor);
22             //对于圆环上的每一个片
23             for(var j=0;j<len_alpha_floor;j++)
24             {
25                 var obj={};
26                 obj.palpha=palpha;
27                 obj.alpha=j*palpha;
28                 obj.pbeta=pbeta;
29                 obj.beta=beta;
30                 obj.weight={};
31                 obj.floor=i;
32                 obj.room=j;
33                 if((Math.floor(i/3)%2)==(Math.floor(j/3)%2))//棋盘形,但试验时并没有生成想要的棋盘形,没有调试是为什么
34                 {
35                     obj.weight="{land_textblack:1}";//在数据库中直接使用字符串形式保存
36                 }
37                 else
38                 {
39                     obj.weight="{land_textyellow:1}";
40                 }
41 
42                 //先尝试单独推入每一个地区块
43                 PushChess(obj);//将这个地区块数据写入数据库
44             }
45         }
46     }
47     function PushChess(obj)
48     {
49         Url="http://127.0.0.1:8082/query.do?jsessionid="+jsessionid;
50         Argv="sql=insert into tab_dqk values(uuid(),'test',"+obj.beta+","+obj.pbeta+","+obj.alpha+","+obj.palpha+",'"
51                 +obj.weight+"',"+obj.floor+","+obj.room+")";
52         //使用同步Ajax请求保证进度同步,在连续使用同步Ajax时不需要xmlHttp.abort()!!!!
53         Request(xmlHttp,"POST",Url,false,Argv,"application/x-www-form-urlencoded",PushChessCallBack,0);
54     }
55     function PushChessCallBack()
56     {//空方法
57 
58     }

半径计算图解如下:

4858美高梅 28

代码中sswr是自身编写的二个四舍五入措施:

4858美高梅 294858美高梅 30

 1 //四舍五入,目标浮点数、取整方式,小数点后精度
 2 function sswr(float,type,accuracy)
 3 {
 4     var float2=float;
 5     if(type==null||type==0)
 6     {
 7 
 8     }
 9     else if(type==1)//向下取整
10     {
11         float2+=-0.5;
12     }
13     else if(type==2)//向上取整
14     {
15         float2+=0.5;
16     }
17     var acc=Math.pow(10,accuracy?accuracy:0);//用于保留小数点后精度,保留小数点后一位时,acc为10
18     var int_res=div(Math.round(float2*acc),acc);
19     return int_res;
20 }

View Code

为了编制程序容易,生成各地块后立刻用协同格局将数据存入数据库,等保存完结后再生成下三个地区块,这么些简单的Ajax也只支持单线程工作。奇怪的是这一次测试时选拔post情势成功保存数据,但前边编制程序时post格局则无Ajax再次回到值,改用get情势传送参数后又保留成功,因为日子少于没有详细研讨,读者假诺感兴趣能够能够调节和测试一下,告诉作者干什么会如此。

说到底在数据库中插入了50930条数据。

③ 、数据突显

数量查询和呈现代码如下:(testdatatexture.html)

a、查询行星新闻:

 1 function CreateChess()//读取地区块的一些整体信息,从planet表获取更合理?
 2     {
 3         Url="http://127.0.0.1:8082/query.do?jsessionid="+jsessionid;
 4         Argv="sql=select min_floor,max_floor,width_room from tab_planet where id='test'";//查找这个行星的地区块层数
 5         //使用同步Ajax请求保证进度同步
 6         Request(xmlHttp,"POST",Url,true,Argv,"application/x-www-form-urlencoded",CreateChessCallBack,0);
 7     }
 8     //地区块计算使用的变量
 9     var min_floor= 0,max_floor= 0,width_room= 0,height_floor=0;//最低层数,最高层数
10     var can_temp=document.createElement("canvas");//用来做像素处理的隐形canvas
11     var context_temp;
12     var imagedata_temp;//=context_temp.getImageData(0,0,400,199);
13     var strsrc_dqk="";

多少中涵盖了各个地方块的“层数”和“房间号”音信,笔者的思路是把那么些音信传播显卡,由片元着色器程序算出各种片元所属的房间,然后在扩散的数据中找到这么些屋子的颜料,这么些思路类似OpenGL中的3D纹理。

一种将数万条数据传入显卡的创造措施是将数据转载为一张图片,交由显卡的纹理采样器进行处理,小编把那种用来传输数据的纹路命名为“数据纹理”。恰好我盼望传入的数额就是房间对应的地形颜色,与图片的数据结构相符。

那里建立了三个掩蔽的canvas用来把各样屋子的颜色放入数据纹理的相应像素。

b、处理获得的行星音讯,据此获取各市块的数量:(这段代码的进行作用较低)

 1                var str_res=xmlHttp.responseText;//从h2数据库获取到的是一个dom文档,要从中找到所需要的字段
 2                         xmlHttp.abort();
 3                         var div=document.createElement("div");
 4                         //div.innerHTML=str_res;//这样做会报错,因为加载整个dom时,标签中的引用和代码都会被执行!!!!
 5                         div.innerHTML=str_res.substring(str_res.search(/<table/),str_res.search(/<\/table>/)+8);
 6                         div.style.display=false;
 7                         //document.getElementById("div_allbase").appendChild(div);不需要这一句
 8                         var tr=div.getElementsByTagName("tr")[1];//从返回的数据表里提取出数据部分
 9                         var tds=tr.getElementsByTagName("td");
10                         min_floor=parseInt(tds[0].innerHTML);
11                         max_floor=parseInt(tds[1].innerHTML);
12                         width_room=parseInt(tds[2].innerHTML);//房间最多的一层有多少房间
13                         height_floor=min_floor+max_floor+1;//总共有多少层
14                         var int_wh=width_room>height_floor?width_room:height_floor;//取宽高中较大的值
15                         var int_size=newland.FindPower(int_wh);//找到大于size的最小的2的整数次幂,注意!!!!
16                         can_temp.width=int_size;
17                         can_temp.height=int_size;
18                         context_temp=can_temp.getContext("2d");
19                         context_temp.fillStyle="rgba(0,0,255,1)";
20                         context_temp.fillRect(0,0,int_size,int_size);
21                         imagedata_temp=context_temp.getImageData(0,0,int_size,int_size);//取canvas的像素数据
22 
23                         arr_floorroom=[];
24                         pbeta=0;//认为每一层的弧度区分度是一定的
25                         arr_palpha=[];//每一层内的房间的弧度区分度不同
26                         var arr_temp=JSON.parse(localStorage.getItem("arr_floorroom"));//读取本地存储
27 
28                         if(arr_temp&&arr_temp.length>0)//如果有本地持久化存储就使用本地的存储,节省时间
29                         {//经测试Chrome浏览器的5M空间正好可以放下一个行星的数据
30                             strsrc_dqk=JSON.parse(localStorage.getItem("strsrc_dqk"));
31                             arr_floorroom=arr_temp;
32                             arr_palpha=JSON.parse(localStorage.getItem("arr_palpha"));
33                             pbeta=JSON.parse(localStorage.getItem("pbeta"));
34 
35                         }
36                         else
37                         {
38                             for(var i=min_floor;i<=max_floor;i++)//读取每一层的数据
39                             {
40                                 Url="http://127.0.0.1:8082/query.do?jsessionid="+jsessionid;
41                                 Argv="sql=select id,beta,pbeta,alpha,palpha,weight,floor,room from tab_dqk" +
42                                         " where planetid='test' and floor="+i;//查找这个行星的地区块层数
43                                 //使用同步Ajax请求保证进度同步
44                                 Request(xmlHttp,"POST",Url,false,Argv,"application/x-www-form-urlencoded",QueryCallBack,0);
45                             }
46                         }
47                         localStorage.setItem("arr_floorroom",JSON.stringify(arr_floorroom));
48                         localStorage.setItem("arr_palpha",JSON.stringify(arr_palpha));
49                         localStorage.setItem("pbeta",JSON.stringify(pbeta));
50                         //大小超过了本地存储限制
51                         xmlHttp.abort();
52                         DrawPlanet();//绘制行星

里头FindPower是自个儿编写的二个搜寻2的整多次幂的办法:

 1 //根据正方形边长找大于size的最小的2的整数次幂
 2 newland.FindPower=function(size)
 3 {
 4     var int1=Math.ceil(Math.log2(size));
 5     return Math.pow(2,int1);
 6 }
 7 //根据数据长度找最小的2的整数次幂
 8 newland.FindPower2=function(len)
 9 {
10     var int1=Math.sqrt(len);
11     return this.FindPower(int1);
12 }

借助这一艺术,大家把datatexture(数据纹理)设置为边长是2的平头次幂的长方形图片。在原生WebGL中,纹理图片的边长必须是2的平头次幂,不然会发生错误,而Babylon.js即便协理使用任意尺寸的图片,但非标准化准图片在传出显卡时会自动进行线性采样情势的拉伸,而导致图片颜色交界处的模糊,那种歪曲对于数据纹理准确性的震慑是致命的。

你能够团结查找一下“近期点采集样品”与“线性纹理采集样品”的文化,datatexture必须利用以来点采集样品。

p阿尔法是水平区分度,表示各个房间的主导到房间墙壁的弧度,明显越走近两极房间越少,水平分别的值也就越高,arr_p阿尔法保存了每一层的水平区分度,是2个长度为199的数组。

c、处理每一层的地域块数据:

 1                var str_res=xmlHttp.responseText;//这时已经在h2服务端建立登录状态
 2                         //xmlHttp.abort();
 3                         var div=document.createElement("div");
 4                         div.innerHTML=str_res.substring(str_res.search(/<table/),str_res.search(/<\/table>/)+8);
 5                         var trs=div.getElementsByTagName("tr");
 6                         var len=trs.length;
 7                         var int_floor=parseInt(trs[1].getElementsByTagName("td")[6].innerHTML);
 8                         if(!arr_floorroom[int_floor])//这里认为只有一个行星,在实际使用时,数组还要加上一维
 9                         {
10                             arr_floorroom[int_floor]=[];
11                         }
12                         if(pbeta==0)
13                         {
14                             pbeta=parseFloat(trs[1].getElementsByTagName("td")[2].innerHTML);
15                         }
16                         console.log(int_floor+"/"+max_floor+">"+(len-1));//输出一下处理进度
17                         arr_palpha.push(parseFloat(trs[1].getElementsByTagName("td")[4].innerHTML));
18                         for(var i=1;i<len;i++)//对于这一层中的每个room
19                         {
20                                 var tds=trs[i].getElementsByTagName("td");
21                                 var int_room=parseInt(tds[7].innerHTML);
22                                 var arr_landtype={land_textblack:[0,0,0,255],land_textyellow:[255,255,0,255]}
23                                 //保存在内存中的数据结构
24                                 //var obj=eval(tds[5].innerHTML);
25                                 var obj={};
26                                 eval("obj="+tds[5].innerHTML);
27                                 arr_floorroom[int_floor][int_room]={id:tds[0],beta:tds[1].innerHTML,pbeta:tds[2].innerHTML,alpha:tds[3].innerHTML
28                                     ,palpha:tds[4].innerHTML,weight:obj};
29                                 //使用何种数据结构传递到显卡?使用一个超长数组arr_set4?《-这是不行的
30                                 var num1= 0,num2= 0,num3= 0,num4= 255;
31                                 for(key in obj)//将每一种颜色的三个通道加权到一起
32                                 {
33                                     var num_key=obj[key];
34                                     num1+=(num_key*arr_landtype[key][0]);
35                                     num2+=(num_key*arr_landtype[key][1]);
36                                     num3+=(num_key*arr_landtype[key][2]);
37                                 }
38                                 var index= (int_floor+max_floor)*4*can_temp.width+(i-1)*4;//每个room由4个元素组成
39                                 imagedata_temp.data[index]=num1;//这里存的真的是颜色
40                                 imagedata_temp.data[index+1]=num2;
41                                 imagedata_temp.data[index+2]=num3;
42                                 imagedata_temp.data[index+3]=num4;
43                         }

因为100km*100km的所在块在远距离视角下也是1个非常的大的区域,下一步布署在拉近视角时,在一个地域块中再生成更加多类型的地势,所以所谓“地区块的形势”,其实是其内含有的种种地形的比例分红,在天涯看时地区块的水彩是其涵盖的各样地形的加权。

那段测试里本人使用了两种地形:纯鲜红的land_textblack和色情的land_textyellow,而三种地区块的地貌则分级是全体的land_textblack和land_textyellow。颜色的加权值被放进了canvas的像素中。

d、绘制行星:

 1 //用glsl和Babylon.js结合的方式绘制行星
 2     function DrawPlanet()
 3     {
 4         var amigaMaterial = new BABYLON.ShaderMaterial("amiga2", scene,{
 5                     vertexElement: "sh2v4.sh",
 6                     fragmentElement: "sh2f4.sh",
 7                 },
 8                 {
 9                     attributes: ["position"],
10                     uniforms: ["worldViewProjection","worldView"]
11                 });
12         amigaMaterial.doNotSerialize=true;
13         sphere1.material=amigaMaterial;
14         if(strsrc_dqk=="")
15         {
16             context_temp.putImageData(imagedata_temp,0,0);
17             strsrc_dqk=can_temp.toDataURL("image/png");//将canvas转化为dataurl
18             localStorage.setItem("strsrc_dqk",JSON.stringify(strsrc_dqk));
19         }
20         var utexturedqk = new BABYLON.Texture.CreateFromBase64String(strsrc_dqk,"utexturedqk", scene
21                 ,false,false,BABYLON.Texture.NEAREST_NEAREST);//将dataurl转化为Babylon.js纹理
22         amigaMaterial.setTexture("utexturedqk",utexturedqk);//将纹理和显卡采样器关联
23         amigaMaterial.setFloat("wid_utexturedqk",can_temp.width);//数据纹理宽度,将内存中的变量和显卡中的通用变量关联
24         amigaMaterial.setFloat("hei_utexturedqk",can_temp.width);
25         amigaMaterial.setFloat("pbeta",pbeta);//层间区分角度
26 
27         var size=newland.FindPower2(arr_palpha.length);//注意!!!!
28         var strsrc_palpha=newland.TranArrToPng1(arr_palpha,size,size);//每一层内的房间区分角度,用4个元素保存一个浮点数
29         var utexturepalpha = new BABYLON.Texture.CreateFromBase64String(strsrc_palpha,"utexturepalpha", scene
30                 ,true,false,BABYLON.Texture.NEAREST_NEAREST);
31         amigaMaterial.setTexture("utexturepalpha",utexturepalpha);
32         amigaMaterial.setFloat("wid_utexturepalpha",size);//room区分度的纹理宽度
33         amigaMaterial.setFloat("hei_utexturepalpha",size);
34 
35         amigaMaterial.setFloat("uarrpalphalen",arr_palpha.length);
36         amigaMaterial.setFloat("max_floorf",max_floor);//Babylon.js不支持传递整形量??GpenGL中int也是以float形式计算的!!!!
37         amigaMaterial.setFloat("MathPI",Math.PI);
38 
39         amigaMaterial.onCompiled=function()//Babylon.js文档中写effect是material的一个内容,而material需要一个“编译过程”,编译之后的material才具备effect属性
40         {//而且对Babylon.js来说,material能传递的变量类型比较少,比如不能传递整形量,而effect则可以传递更多的数据类型
41          //amigaMaterial.getEffect().setArray("uarrpalpha",arr);//每一层水平区分度*/effect可以向显卡传递数组

           //console.log(amigaMaterial.getEffect());        
      }

42     }

那边咱们要考虑使用何种格局把长度为199的浮点型数组arr_p阿尔法传入显卡,事实上OpenGL帮衬传入数组型通用变量,但对数组的援救分成三种:一种是我们后面看到的vec叁 、vec4那类向量数组,glsl把全部向量看做几个变量,而另一种类似“uniform
float
uarrp阿尔法[500]”的自定义数组则是把数组中的每三个因素都当做3个uniform变量处理!(?)。

基于StackOverFlow上三个异域同行的考查,OpenGL最两只可以帮忙200个左右的uniform变量(?),那表示大家难以直接用数组的章程传入arr_p阿尔法。别的在WebGL1.0中的glsl不扶助不定长度的数组,那象征大家亟须在编排着色器代码前对数组的轻重有适合的预计,恐怕依据行星的大小一时半刻调节着色器代码。

在glsl中应用长数组的另一个难点是:glsl竟然不扶助直接用近期赋值的变量作为数组索引!类似

1 int i=1;
2 float f=arr[i+1];

那种数组用法是不允许的!!!!

要将总括结果作为数组的目录只可以动用if else大概switch
case枚举出每种对应的情景,或然采纳:

 1 float getData500(float data[500],int id) {
 2     int len=int(floor(uarrpalphalen+0.5));
 3     for (int i=0; i<500; i++) {
 4         if(i>=len)//i不能和非常量比较!!只好换个方式限制它
 5         {
 6             return 0.0;
 7         }
 8         if (i == id) return data[i];
 9     }
10 }

内部id是总括出来的目录,其余glsl的for循环的中段也不帮助“i<len”那种写法,i必须低于叁个明明的常数。

故此改为利用datatexture传递水平区分度数组

然而使用datatexture时又遇见三个题材,canvas会自动把颜色分量转化为0到255的平头,而那边的水平区分度全是0到1之间的小数,会被机关转为0或1,为缓解这一题材把水平区分度数据转载为科学计数法并用像素表示:

 1 //将一个浮点数组转化为DataTexture,这是浮点数小于1的情况,要注意canvas和webgl对颜色属性的自动处理!!!!
 2 newland.TranArrToPng1=function(arr,width,height)
 3 {
 4     var can_temp=document.createElement("canvas");
 5     can_temp.width=width;
 6     can_temp.height=height;
 7     var context=can_temp.getContext("2d");
 8     context.fillStyle="rgba(0,0,255,1)";//多余的位都是1?
 9     context.fillRect(0,0,width,height);
10     var imagedata=context.getImageData(0,0,width,height);
11     var len=arr.length;//小数部分会自动四舍五入!!!!默认palpha必定小于1
12     for(var i=0;i<len;i+=1)
13     {
14         var str_num=arr[i]+"";
15         //var int_0=str_num.indexOf();
16         var len_str=str_num.length;
17         var count_0=0;
18         for(var j=0;j<len_str;j++)
19         {
20             if(str_num[j]=="0"||str_num[j]==".")
21             {
22                 continue;
23             }
24             else
25             {
26                 count_0=j;//找到第一个非零数
27                 break;
28             }
29         }
30         var num1=parseInt(str_num.substr(count_0,2));
31         var num2=parseInt(str_num.substr(count_0+2,2));
32         //var num3=parseInt(str_num.substr(count_0+4,2));
33         var num4=4+(count_0-2);
34         imagedata.data[i*4]=num1;//科学计数法:用像素颜色的第一第二个分量保存四位有效数字,用第四个分量保存10的负指数
35         imagedata.data[i*4+1]=num2;
36         imagedata.data[i*4+2]=num4;
37         //imagedata.data[i*4+3]=num4;
38     }
39     context.putImageData(imagedata,0,0);
40     var strsrc_palpha=can_temp.toDataURL("image/png");
41     //can_temp.dispose();
42     can_temp=null;
43     return strsrc_palpha;
44 }

推行顺序,看到运转效果与计划有着偏差:

4858美高梅 31

南半球的珍珠白地区块少了累累行,没有变异臆想的棋盘形,时间有限没有详细调节和测试

拉近相机:

4858美高梅 32

可以看到各个区域块的境界清晰可知,没有发生模糊。

选取console.log输出用到的三个dataurl作为参照:

4858美高梅 33

4858美高梅 34

肆 、着色器代码:

a、顶点着色器:

 1 uniform mat4 worldViewProjection;
 2 uniform mat4 worldView;
 3 attribute vec3 position;
 4 
 5 varying vec3 vPosition;
 6 varying vec3 oPosition;//自身坐标系里的位置
 7 
 8 void main(){
 9     gl_Position=worldViewProjection*vec4(position,1);
10     vPosition=vec3(worldView*vec4(position,1));
11     oPosition=position;
12 }

咱俩前边生成地形的盘算都以在行星的自家坐标系里展开的,所以在片元着色器里也急需以行星的本人坐标系为参照鲜明地区块的层数和房间号,所以把多少个不经过任何矩阵变换的顶点地方新闻传播片元着色器。

b、片元着色器:

 1 precision highp float;
 2 //varying vec4 vColor;
 3 varying vec3 vPosition;
 4 varying vec3 oPosition;
 5 //uniform vec4 uColor;
 6 uniform sampler2D utexturedqk;//地区块数据纹理的采样器
 7 uniform float wid_utexturedqk;//数据纹理的宽高
 8 uniform float hei_utexturedqk;
 9 
10 uniform sampler2D utexturepalpha;//一个单元里保存了四个元素!!!!
11 //uniform vec3 uarrdqk[60000];//es3.0之前的glsles不支持隐含数组!!!!
12 uniform float pbeta;
13 uniform float wid_utexturepalpha;
14 uniform float hei_utexturepalpha;
15 //uniform float uarrpalpha[500];//用来测试的行星只有199层,预设为500层应该够了
16 uniform float uarrpalphalen;
17 uniform float max_floorf;
18 uniform float MathPI;
19 
20 float getDataVec4(vec4 data,int id) {
21     for (int i=0; i<4; i++) {
22         if (i == id) return data[i];
23     }
24 }
25 float getData500(float data[500],int id) {
26     int len=int(floor(uarrpalphalen+0.5));
27     for (int i=0; i<500; i++) {
28         if(i>=len)//i不能和非常量比较!!只好换个方式限制它
29         {
30             return 0.0;
31         }
32         if (i == id) return data[i];
33     }
34 }
35 
36 void main()
37 {
38     //vec4 tempColor=uColor;//es3.0之前不支持round!!!!
39     //glsl事实上以float为计算基准
40     //int max_floor=int(floor(max_floorf+0.5));
41     //算层数
42     float r=sqrt(oPosition.x*oPosition.x+oPosition.y*oPosition.y+oPosition.z*oPosition.z);//这个片元到球心的距离
43     float beta=asin(oPosition.y/r);//俯仰角
44     //int int_beta=int(floor((beta/(pbeta*2.0))+0.5));
45     float count_beta=(beta/(pbeta*2.0));
46     //int index_beta=int(floor(count_beta+ max_floorf+0.5));
47     float index_beta=floor(count_beta+ max_floorf+0.5);//第几层
48     //int roomcount=0;
49     //使用数据纹理法,取这一层的区分度
50     //int int1=int(floor(mod(index_beta,4.0)+0.5));51     //float float1=(index_beta/4.0);
52     float floatu=(mod(index_beta,wid_utexturepalpha))/(wid_utexturepalpha)+(0.5/wid_utexturepalpha);//u是x轴坐标,v是y轴坐标
53     float floatv=(((index_beta)/(wid_utexturepalpha)))/(hei_utexturepalpha)+(0.5/(wid_utexturepalpha*hei_utexturepalpha));
54     vec2 UVpalpha=vec2(floatu,floatv);//上面计算的uv坐标加了一个偏移量,防止坐标正好落在两个像素的边界上
55     vec4 vec4palphas=texture2D(utexturepalpha, UVpalpha);//glsl中的颜色为0到1.0,所以要乘以255.0获得传入的科学计数法
56     float palpha=(vec4palphas[0]*255.0*100.0+vec4palphas[1]*255.0)/pow(10.0,vec4palphas[2]*255.0);
57     //float palpha=getData500(uarrpalpha,int(floor(index_beta+0.5)));//改为尝试数组法传递数据
58     //取这一层的转角
59     float alpha=atan(oPosition.z,oPosition.x);//标准反正切函数有两个参数!!
60     if(alpha<0.0)
61     {
62         alpha+=(MathPI*2.0);
63     }
64     //取地区块数据纹理的坐标
65     float floatu2=(alpha/(palpha*2.0))/wid_utexturedqk;
66     float floatv2=index_beta/hei_utexturedqk+0.5/hei_utexturedqk;
67     vec2 UVdqk=vec2(floatu2,floatv2);
68     gl_FragColor=texture2D(utexturedqk, UVdqk);
69     //gl_FragColor=vec4palphas;
70     //gl_FragColor=texelFetch(utexturedqk,ivec2(int(floor(alpha/(palpha*2.0)+0.5)),int(floor(index_beta+0.5))),0);//这个整数像素的方法是WebGL2开始加入的!!!!
71     //gl_FragColor=vec4(1.0*floatu,1.0*floatv,1.0*floatv2,1.0);//红,绿,蓝结果不是0就是1??!!
72 
73     //int index_dqk=roomcount-1+int(floor((alpha/palpha)+0.5));
74     //vec4 tempColor=vec4(uarrdqk[index_dqk],1.0);
75 
76     //float float_3=index_beta/(max_floorf*2.0);
77     //float float_4=oPosition.y/5.0;
78     //canvas的imagedata用255,255,255,255定义颜色通道,而glsl用1.0,1.0,1.0,1.0定义!!!!
79 
80 
81 }

4858美高梅 ,glsl语言不帮忙字符串类型,WebGL1.0也不帮助从显卡反写数据到内存,一种有效的调节方法是将有些总括结果转化为颜色展现在显示器上,然后用拾色器提取值。

四 、依据规则变更随机的行星表面地形(testarenas.html)

一 、生成地区块的基本数据结构:

4858美高梅 354858美高梅 36

 1 function CookDqk()//生成地区块,每一floor的每个room
 2     {
 3         var size_dqk=100;//每个地区块的长宽都是100km
 4         var r_planet=perimeter/(2*Math.PI);//行星的半径
 5         var len_beta=sswr(((perimeter/2)/size_dqk)/2);//通过弧度来分层!!100
 6         pbeta=(Math.PI/4)/len_beta;
 7         //对于每一层,
 8         for(var i=-len_beta;i<=len_beta;i++)
 9         {
10 
11             var rad_beta=(Math.PI/2)*(i/len_beta);
12             var r_floor=Math.cos(rad_beta)*r_planet;//这一层的半径
13             var len_alpha_floor=sswr((r_floor*2*Math.PI)/size_dqk);
14             var palpha=Math.PI/len_alpha_floor;//每一个地区块的角度边界,在这个边界范围内即属于这个地区块
15             arr_palpha.push(palpha);
16             var beta=i*pbeta*2;
17             //console.log(i+"/"+len_beta+">"+len_alpha_floor);
18             var arr1=[];
19             //对于圆环上的每一个片
20             for(var j=0;j<len_alpha_floor;j++)
21             {
22                 var obj={};
23                 obj.palpha=palpha;
24                 obj.alpha=j*palpha*2;
25                 obj.pbeta=pbeta;
26                 obj.beta=beta;
27                 //obj.weight={};
28                 obj.floor=i;
29                 obj.room=j;
30                 obj.countcook=0;
31                 obj.altitude=0;
32                
33                 arr1.push(obj);
34               
35             }
36             if(arr1.length>0)
37             {
38                 arr_floorroom.push(arr1);
39             }
40 
41         }
42         CookDqk2();//对生成的数据结构进行  规律随机填充
43     }

View Code

贰 、使用正态随机数与加和平均鲜明各地块的海拔

  1 //使用正态随机数和加和平均确定每个地区块的海拔
  2     function CookDqk2()
  3     {
  4         var len=arr_floorroom.length;
  5         //生成初始的随机正态随机海拔
  6         console.log("生成初始的随机正态随机海拔");
  7         for(var i=0;i<len;i++)
  8         {
  9             //console.log(i+" in "+len);
 10             var len2=arr_floorroom[i].length;
 11             for(var j=0;j<len2;j++)
 12             {
 13                 var obj=arr_floorroom[i][j];
 14                 obj.altitude=dc1.getNumberInNormalDistribution(-10,1000);//平均海拔是-10,常见的海拔在正负1000以内
 15                 if(obj.altitude<-10000)
 16                 {
 17                     obj.altitude=-10000;
 18                 }
 19                 else if(obj.altitude>10000)
 20                 {
 21                     obj.altitude=10000;
 22                 }
 23                 obj.countcook=1;
 24                 if(i%2==1)//如果是奇数层,room偏移一个识别范围,这样地形看起来更自然
 25                 {
 26                     obj.alpha+=obj.palpha;
 27                 }
 28             }
 29         }
 30         //使用加和平均方法使海拔趋于连续(高度平滑)
 31         console.log("使用加和平均方法使海拔趋于连续");
 32         for(var i=0;i<len;i++)//将地区块的海拔和周围相邻的所有地区块的海拔相加取平均值,作为这个地区块的海拔
 33         {
 34             console.log(i+" in "+len);
 35             var len2=arr_floorroom[i].length;
 36             for(var j=0;j<len2;j++)
 37             {
 38                 var obj=arr_floorroom[i][j];
 39                 obj.altitude1=obj.altitude;
 40                 if(i>0)//考虑这个room下面的floor
 41                 {
 42                     //var alpha=obj.alpha;
 43                     var len3=arr_floorroom[i-1].length;
 44                     for(var k=0;k<len3;k++)//遍历下层的room
 45                     {
 46                         var subplpha=Math.abs(arr_floorroom[i-1][k].alpha-obj.alpha);
 47                         if(subplpha>Math.PI)
 48                         {
 49                             subplpha=Math.PI*2-subplpha;
 50                         }
 51                         if(subplpha<=(obj.palpha+arr_floorroom[i-1][k].palpha))
 52                         {//对这个地区块有影响
 53                             obj.altitude1+=arr_floorroom[i-1][k].altitude;
 54                             obj.countcook++;
 55                         }
 56                     }
 57 
 58                 }
 59                 if(i<len-1)//考虑这个room上面的floor
 60                 {
 61                     var len3=arr_floorroom[i+1].length;
 62                     for(var k=0;k<len3;k++)//遍历上层的room
 63                     {
 64                         var subplpha=Math.abs(arr_floorroom[i+1][k].alpha-obj.alpha);
 65                         if(subplpha>Math.PI)
 66                         {
 67                             subplpha=Math.PI*2-subplpha;
 68                         }
 69                         if(subplpha<=(obj.palpha+arr_floorroom[i+1][k].palpha))
 70                         {//对这个地区块有影响
 71                             obj.altitude1+=arr_floorroom[i+1][k].altitude;
 72                             obj.countcook++;
 73                         }
 74                     }
 75                 }
 76                 //考虑本层的相邻元素
 77                 if(j==0)
 78                 {
 79                     obj.altitude1+=arr_floorroom[i][1].altitude;
 80                     obj.altitude1+=arr_floorroom[i][len2-1].altitude;
 81                     obj.countcook+=2;
 82                 }else if(j==(len2-1))
 83                 {
 84                     obj.altitude1+=arr_floorroom[i][0].altitude;
 85                     obj.altitude1+=arr_floorroom[i][len2-2].altitude;
 86                     obj.countcook+=2;
 87                 }
 88                 else{
 89                     obj.altitude1+=arr_floorroom[i][j-1].altitude;
 90                     obj.altitude1+=arr_floorroom[i][j+1].altitude;
 91                     obj.countcook+=2;
 92                 }
 93             }
 94         }
 95         var min_altitude= 0,max_altitude=0;
 96         console.log("去除总权值");
 97         for(var i=0;i<len;i++)
 98         {
 99             console.log(i+" in "+len);
100             var len2=arr_floorroom[i].length;
101             for(var j=0;j<len2;j++)
102             {
103                 var obj=arr_floorroom[i][j];
104                 obj.altitude=obj.altitude1/obj.countcook;
105                 if(obj.altitude<min_altitude)
106                 {
107                     min_altitude=obj.altitude;
108                 }
109                 if(obj.altitude>max_altitude)
110                 {
111                     max_altitude=obj.altitude;
112                 }
113                 //delete obj.altitude1;
114             }
115         }
116         console.log("最低、最高海拔为:"+min_altitude+"、"+max_altitude);
117         //根据海拔高度与概率规则确定海洋与陆地,根据纬度和高度确定陆地的类型(高度达到一定程度后优于纬度)
118         CookDqk3();
119     }

 关钱林森态随机数的学识能够参见那篇小说:

三 、根据海拔中度和纬度分明地形:

  1 function CookDqk3()
  2     {
  3         console.log("开始生成地区块级地形");
  4         var len=arr_floorroom.length;
  5         for(var i=0;i<len;i++) {
  6             console.log(i+" in "+len);
  7             var len2 = arr_floorroom[i].length;
  8             for (var j = 0; j < len2; j++)
  9             {
 10                 var obj=arr_floorroom[i][j];
 11                 getLandtypeDqk(obj);//根据规则确定这个地区块的地形
 12             }
 13         }
 14         //地区块平滑
 15         console.log("地区块平滑");
 16         for(var i=0;i<len;i++)
 17         {
 18             console.log(i+" in "+len);
 19             var len2=arr_floorroom[i].length;
 20             for(var j=0;j<len2;j++)
 21             {
 22                 var obj=arr_floorroom[i][j];
 23 
 24                 if(i>0)//考虑这个room下面的floor
 25                 {
 26                     //var alpha=obj.alpha;
 27                     var len3=arr_floorroom[i-1].length;
 28                     for(var k=0;k<len3;k++)//遍历下层的room
 29                     {
 30                         var obj1=arr_floorroom[i-1][k];
 31                         var subplpha=Math.abs(obj1.alpha-obj.alpha);
 32                         if(subplpha>Math.PI)
 33                         {
 34                             subplpha=Math.PI*2-subplpha;
 35                         }
 36                         if(subplpha<=(obj.palpha+obj1.palpha))
 37                         {//对这个地区块有影响
 38                             if(!obj.landtypedqk[obj1.type2])
 39                             {
 40                                 obj.landtypedqk[obj1.type2]=obj1.effect;//这一种地形的权重
 41                             }
 42                             else
 43                             {
 44                                 obj.landtypedqk[obj1.type2]+=obj1.effect;
 45                             }
 46                             obj.landtypedqkeffect+=obj1.effect;//所有地形的权重
 47                         }
 48                     }
 49 
 50                 }
 51                 if(i<len-1)//考虑这个room上面的floor
 52                 {
 53                     var len3=arr_floorroom[i+1].length;
 54                     for(var k=0;k<len3;k++)//遍历上层的room
 55                     {
 56                         var obj1=arr_floorroom[i+1][k];
 57                         var subplpha=Math.abs(obj1.alpha-obj.alpha);
 58                         if(subplpha>Math.PI)
 59                         {
 60                             subplpha=Math.PI*2-subplpha;
 61                         }
 62                         if(subplpha<=(obj.palpha+obj1.palpha))
 63                         {//对这个地区块有影响
 64                             if(!obj.landtypedqk[obj1.type2])
 65                             {
 66                                 obj.landtypedqk[obj1.type2]=obj1.effect;
 67                             }
 68                             else
 69                             {
 70                                 obj.landtypedqk[obj1.type2]+=obj1.effect;
 71                             }
 72                             obj.landtypedqkeffect+=obj1.effect;
 73                         }
 74                     }
 75                 }
 76                 //考虑本层的相邻元素
 77                 if(j==0)
 78                 {
 79                     var obj1=arr_floorroom[i][1];
 80                     if(!obj.landtypedqk[obj1.type2])
 81                     {
 82                         obj.landtypedqk[obj1.type2]=obj1.effect;
 83                     }
 84                     else
 85                     {
 86                         obj.landtypedqk[obj1.type2]+=obj1.effect;
 87                     }
 88                     obj.landtypedqkeffect+=obj1.effect;
 89                     var obj1=arr_floorroom[i][len2-1];
 90                     if(!obj.landtypedqk[obj1.type2])
 91                     {
 92                         obj.landtypedqk[obj1.type2]=obj1.effect;
 93                     }
 94                     else
 95                     {
 96                         obj.landtypedqk[obj1.type2]+=obj1.effect;
 97                     }
 98                     obj.landtypedqkeffect+=obj1.effect;
 99                 }
100                 else if(j==(len2-1))
101                 {
102                     var obj1=arr_floorroom[i][0];
103                     if(!obj.landtypedqk[obj1.type2])
104                     {
105                         obj.landtypedqk[obj1.type2]=obj1.effect;
106                     }
107                     else
108                     {
109                         obj.landtypedqk[obj1.type2]+=obj1.effect;
110                     }
111                     obj.landtypedqkeffect+=obj1.effect;
112                     var obj1=arr_floorroom[i][len2-2];
113                     if(!obj.landtypedqk[obj1.type2])
114                     {
115                         obj.landtypedqk[obj1.type2]=obj1.effect;
116                     }
117                     else
118                     {
119                         obj.landtypedqk[obj1.type2]+=obj1.effect;
120                     }
121                     obj.landtypedqkeffect+=obj1.effect;
122                 }
123                 else{
124                     var obj1=arr_floorroom[i][j-1];
125                     if(!obj.landtypedqk[obj1.type2])
126                     {
127                         obj.landtypedqk[obj1.type2]=obj1.effect;
128                     }
129                     else
130                     {
131                         obj.landtypedqk[obj1.type2]+=obj1.effect;
132                     }
133                     obj.landtypedqkeffect+=obj1.effect;
134                     var obj1=arr_floorroom[i][j+1];
135                     if(!obj.landtypedqk[obj1.type2])
136                     {
137                         obj.landtypedqk[obj1.type2]=obj1.effect;
138                     }
139                     else
140                     {
141                         obj.landtypedqk[obj1.type2]+=obj1.effect;
142                     }
143                     obj.landtypedqkeffect+=obj1.effect;
144                 }
145             }
146         }
147         console.log("对每个地区块进行加权并入库");
148         for(var i=0;i<len;i++)//对每个地区块进行加权,这段代码执行很慢
149         {
150             var len2=arr_floorroom[i].length;
151             console.log(i+" in "+len);
152             for(var j=0;j<len2;j++)
153             {
154 
155                 var obj=arr_floorroom[i][j];
156                 obj.altitude=obj.altitude/obj.countcook;
157                 var rate_type1final=Math.random()*obj.landtypedqkeffect;
158                 var rate_type1final_count=0;
159                 obj.type2final="默认dqk";
160                 for(key in obj.landtypedqk)
161                 {
162                     rate_type1final_count+=obj.landtypedqk[key];//这一种地形的权重
163                     if(rate_type1final<rate_type1final_count)//如果随机数小于这种地形的累积权重
164                     {
165                         obj.type2final=key;
166                         break;
167                     }
168                 }
169                 //在这里把这个地区块插入数据库?不知道什么原因POST方法失败了,改用GET方法
170                 /*Url="http://127.0.0.1:8082/query.do?jsessionid="+jsessionid;
171                 Argv="sql=insert into tab_dqk values(uuid(),'test1',"+obj.beta+","+obj.pbeta+","+obj.alpha+","+obj.palpha+",'"
172                         +obj.type2final+"',"+obj.floor+","+obj.room+","+obj.altitude+")";
173                 //使用同步Ajax请求保证进度同步,在连续使用同步Ajax时不需要xmlHttp.abort()!!!!
174                 Request(xmlHttp,"POST",Url,false,Argv,"application/x-www-form-urlencoded",PushChessCallBack,0);*/
175                 Url="http://127.0.0.1:8082/query.do?jsessionid="+jsessionid+"&sql=insert into tab_dqk values(uuid(),'test1',"+obj.beta+","+obj.pbeta+","+obj.alpha+","+obj.palpha+",'"
176                         +obj.type2final+"',"+obj.floor+","+obj.room+","+obj.altitude+")"
177                 Argv="";
178                 Request(xmlHttp,"GET",Url,false,Argv,"application/x-www-form-urlencoded",PushChessCallBack,0);
179             }//这里改变了一下数据结构,直接用weight字段存储确定了的地区块级地形
180         }
181         CookDqk4();//着色
182     }

getLandtypeDqk是基江子磊拔和纬度鲜明地形的法子:(那一个主意效能非常低)

 1 function getLandtypeDqk(obj)
 2     {
 3         var height=obj.altitude;//对于这个地区块
 4         var beta=obj.beta;
 5         var rate_land=Math.random();
 6         for(key in tab_landtypedqk)//这个方法并不能保证顺序!!!!//for key和eval占用了大量时间????
 7         {//按顺序查找每一个地区块级地形是否符合条件,
 8             if(eval(tab_landtypedqk[key].eval))//JavaScript语言的一大特点是可以随时把字符串转化为可执行代码,
 9             {//这使得JavaScript语言可以非常灵活,但是会降低执行效率和安全性
10                 obj.type1=key;
11                 var count_rate=0;//用来累加概率
12                 var obj1=tab_landtypedqk[key];
13                 for(key2 in obj1)
14                 {
15                     if(key2!="eval")
16                     {
17                         var rate_type2=Math.random();
18                         count_rate+=obj1[key2].rate;
19                         if(rate_type2<count_rate)
20                         {
21                             obj.type2=key2;
22                             obj.effect=obj1[key2].effect;//对周边地块的影响程度
23                             break;
24                         }
25                     }
26                 }
27                 break;
28             }
29         }
30         if (!obj.type1)//如果这个地区块没有被分配地形
31         {
32             obj.type1="未定义";
33             obj.type2="默认dqk";
34             obj.effect=0;
35         }
36         obj.landtypedqk={};//这三个变量用于对地形进行平滑处理
37         obj.landtypedqk[obj.type2]=obj.effect;
38         obj.landtypedqkeffect=obj.effect;
39     }

在平等海拔和纬度也许有多样时势存在,各种地形都有1个并发概率,取三个Infiniti制数,如果这么些自由数稍差于遍历到这几个时势时的可能率累积,则将这一个地点块设为那种时势。

每个地形还有一个effect属性,表示那种时局对广大时局的震慑能力,比如如若一片热带雨林周围全被沙漠包围,那么那片雨林有十分的大或然变成沙漠。

海拔和纬度与时局的照应关系设定如下:(tab_datalib.js)

 1 //地区块地形元素分布表
 2 var beta_2326=((23+26/60)/180)*Math.PI;//南北回归线弧度
 3 var beta_6034=((60+34/60)/180)*Math.PI;//南北极圈弧度
 4 var beta_8=((8)/180)*Math.PI;//赤道附近弧度
 5 var tab_landtypedqk={//rate按从小到大排列生成的随机数小于哪个就定为何种地形,effect在卷积平滑阶段起作用,表示这个地形对周围环境的影响程度
 6     "热带海洋":{eval:"height<0&&rate_land<0.9&&Math.abs(beta)<beta_2326","热带海洋dqk":{rate:1,effect:1}},//eval是判断地区块大类型的判断条件,以后在设计技能效果时也可能要借鉴这里
 7     "温带海洋":{eval:"height<0&&rate_land<0.9&&Math.abs(beta)>beta_2326&&Math.abs(beta)<beta_6034","温带海洋dqk":{rate:1,effect:1}},
 8     "寒带海洋":{eval:"height<0&&rate_land<0.9&&Math.abs(beta)>beta_6034","寒带海洋dqk":{rate:1,effect:1}},
 9     "温带1500米以下":{eval:"rate_land>0.1&&Math.abs(beta)>beta_2326&&Math.abs(beta)<beta_6034&&height<1500","草原dqk":{rate:0.4,effect:1},"森林dqk":{rate:0.4,effect:1},"戈壁dqk":{rate:0.2,effect:2}},
10     "温带1500米以上":{eval:"rate_land>0.1&&Math.abs(beta)>beta_2326&&Math.abs(beta)<beta_6034&&height>=1500","雪山dqk":{rate:1,effect:1}},
11     "亚热带3000米以下":{eval:"rate_land>0.1&&Math.abs(beta)>beta_8&&Math.abs(beta)<beta_2326&&height<3000","热带雨林dqk":{rate:0.5,effect:1},"稀树草原dqk":{rate:0.5,effect:1}},
12     "亚热带3000米以上":{eval:"rate_land>0.1&&Math.abs(beta)>beta_8&&Math.abs(beta)<beta_2326&&height>=3000","雪山dqk":{rate:1,effect:1}},
13     "热带3000米以下":{eval:"rate_land>0.1&&Math.abs(beta)<beta_8&&height<3000","热带雨林dqk":{rate:0.5,effect:1},"沙漠dqk":{rate:0.5,effect:2}},
14     "热带3000米以上":{eval:"rate_land>0.1&&Math.abs(beta)<beta_8&&height>=3000","雪山dqk":{rate:1,effect:1}},
15     "寒带-100米以下":{eval:"rate_land>0.1&&Math.abs(beta)>beta_6034&&height<-100","草原dqk":{rate:0.4,effect:1},"森林dqk":{rate:0.4,effect:1},"戈壁dqk":{rate:0.2,effect:2}},
16     "寒带-100到200米以内":{eval:"rate_land>0.1&&Math.abs(beta)>beta_6034&&height<200&&height>=-100","寒带森林dqk":{rate:0.6,effect:1},"冰川dqk":{rate:0.4,effect:1}},
17     "寒带200米以上":{eval:"rate_land>0.1&&Math.abs(beta)>beta_6034&&height>200","冰川dqk":{rate:1,effect:1}}
18 }
19 var tab_landtypedqk2={//每一种地区块的远观颜色和内部地貌块(单位块?)占比
20     "默认dqk":{color:[250,126,126],content:{"红白格dmk":{rate:1,effect:0}}},//完全红白格,这种表示错误和未定义的地貌块不会影响周围
21     "热带海洋dqk":{color:[15,63,105],content:{"海洋水面dmk":{rate:0.99,effect:1},"雨林dmk":{rate:0.995,effect:0},"沙滩dmk":{rate:1,effect:0}}},
22     "温带海洋dqk":{color:[15,63,105],content:{"海洋水面dmk":{rate:0.99,effect:1},"森林dmk":{rate:0.995,effect:0},"沙滩dmk":{rate:1,effect:0}}},
23     "寒带海洋dqk":{color:[15,63,105],content:{"海洋水面dmk":{rate:0.5,effect:1},"冰面dmk":{rate:1,effect:1}}},
24     "草原dqk":{color:[93, 153, 63],content:{"草地dmk":{rate:0.95,effect:1},"内陆水面dmk":{rate:1,effect:1}}},
25     "森林dqk":{color:[33,68,44],content:{"森林dmk":{rate:0.95,effect:1},"内陆水面dmk":{rate:1,effect:1}}},
26     "戈壁dqk":{color:[127, 102, 79],content:{"戈壁dmk":{rate:1,effect:1}}},
27     "雪山dqk":{color:[220, 221, 220],content:{"雪地dmk":{rate:0.8,effect:1},"岩石dmk":{rate:1,effect:0}}},
28     "热带雨林dqk":{color:[33,68,44],content:{"雨林dmk":{rate:0.95,effect:1},"内陆水面dmk":{rate:1,effect:1}}},
29     "稀树草原dqk":{color:[117, 118, 68],content:{"稀树草原dmk":{rate:0.95,effect:1},"内陆水面dmk":{rate:1,effect:1}}},
30     "沙漠dqk":{color:[175, 117, 68],content:{"沙地dmk":{rate:0.99,effect:1},"内陆水面dmk":{rate:0.995,effect:0},"绿洲dmk":{rate:1,effect:0}}},
31     "寒带森林dqk":{color:[],content:{"寒带森林dmk":{rate:0.85,effect:1},"雪地dmk":{rate:1,effect:1}}},
32     "冰川dqk":{color:[201, 216, 220],content:{"冰面dmk":{rate:0.85,effect:1},"雪地dmk":{rate:1,effect:1}}}
33 }
34 var tab_landtypedmk={//每一种地貌块的纹理url
35     "红白格dmk":{eval_effect:"",Url:"../../ASSETS/IMAGE/Texture_landtypedmk/amiga.jpg",color:[250,126,126]},//对单位的影响,纹理Url,纹理的平均颜色
36     "海洋水面dmk":{eval_effect:"",Url:"../../ASSETS/IMAGE/Texture_landtypedmk/sea.png",color:[15,63,105]},
37     "雨林dmk":{eval_effect:"",Url:"../../ASSETS/IMAGE/Texture_landtypedmk/yulin.png",color:[33,68,44]},
38     "沙滩dmk":{eval_effect:"",Url:"../../ASSETS/IMAGE/Texture_landtypedmk/shatan.png",color:[205, 160, 109]},
39     "森林dmk":{eval_effect:"",Url:"../../ASSETS/IMAGE/Texture_landtypedmk/yulin.png",color:[33,68,44]},//没找到温带森林,暂时用雨林代替
40     "冰面dmk":{eval_effect:"",Url:"../../ASSETS/IMAGE/Texture_landtypedmk/ice.png",color:[201, 216, 220]},
41     "草地dmk":{eval_effect:"",Url:"../../ASSETS/IMAGE/Texture_landtypedmk/grass.png",color:[93, 153, 63]},
42     "内陆水面dmk":{eval_effect:"",Url:"../../ASSETS/IMAGE/Texture_landtypedmk/lake.png",color:[93,143,180]},
43     "戈壁dmk":{eval_effect:"",Url:"../../ASSETS/IMAGE/Texture_landtypedmk/gebi.png",color:[127, 102, 79]},
44     "雪地dmk":{eval_effect:"",Url:"../../ASSETS/IMAGE/Texture_landtypedmk/snow.png",color:[220, 221, 220]},
45     "岩石dmk":{eval_effect:"",Url:"../../ASSETS/IMAGE/Texture_landtypedmk/stone.png",color:[82, 81, 74]},
46     "稀树草原dmk":{eval_effect:"../../ASSETS/IMAGE/Texture_landtypedmk/xishucaoyuan.png",Url:"",color:[117, 118, 68]},
47     "沙地dmk":{eval_effect:"",Url:"../../ASSETS/IMAGE/Texture_landtypedmk/sand.png",color:[175, 117, 68]},
48     "绿洲dmk":{eval_effect:"",Url:"../../ASSETS/IMAGE/Texture_landtypedmk/lvzhou.png",color:[127, 144, 111]},
49     "寒带森林dmk":{eval_effect:"",Url:"../../ASSETS/IMAGE/Texture_landtypedmk/lvzhou.png",color:[127, 144, 111]}
50 
51 }

此处设定种种100km*100km的地面块能够由更小的“地貌块”组成,在极近时地貌块的地形突显为实在的纹理图,在较远时地貌块表现为纹理图的平均色,在更远一些时用地点块代替地貌块,地区块的颜料为地貌块的加权。

现阶段以此切换功效还未编制,因为时间有限实际地形纹理也远非仔细设置,地区块的颜色加权也没做,直接选择了占相比较多的地形块颜色。

领到纹理图平均颜色的代码如下:(testpix.html)

4858美高梅 374858美高梅 38

 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>提取一副固定大小图片的平均颜色color4</title>
 6 </head>
 7 <body>
 8 <div id="div_allbase">
 9     <canvas style="width: 512px;height: 512px" width="512" height="512" id="can_pic">
10 
11     </canvas>
12 </div>
13 </body>
14 <script>
15     var canvas=document.getElementById("can_pic");
16     window.onload=loadImage;
17     function loadImage()
18     {
19         var context=canvas.getContext("2d");
20         var img=document.createElement("img");
21         img.src="../../ASSETS/IMAGE/Texture_landtypedmk/lvzhou.png";
22         img.onload=function()
23         {//在图片加载完毕后才可以在canvas里绘制
24             context.drawImage(img,0,0);
25             var imagedata_temp=context.getImageData(0,0,512,512);//规定地貌块纹理图片的宽高是512
26             var data=imagedata_temp.data;
27             var len=data.length;
28             var color4=[0,0,0,0];
29             for(var i=0;i<len;i+=4)
30             {
31                 color4[0]+=data[i];
32                 color4[1]+=data[i+1];
33                 color4[2]+=data[i+2];
34                 color4[3]+=data[i+3];
35             }
36             var int=512*512;
37             color4[0]=Math.round(color4[0]/int);
38             color4[1]=Math.round(color4[1]/int);
39             color4[2]=Math.round(color4[2]/int);
40             color4[3]=Math.round(color4[3]/int);
41             console.log(color4);
42         }
43 
44     }
45 </script>
46 </html>

View Code

④ 、生成数据纹理

 1 function CookDqk4()
 2     {
 3         var len=arr_floorroom.length;
 4         //开始生成数据纹理
 5         console.log("开始生成数据纹理");
 6         for(var i=0;i<len;i++) //每一行
 7         {
 8             //console.log(i+" in "+len);
 9             var len2 = arr_floorroom[i].length;
10             for (var j = 0; j < len2; j++) {
11                 var obj = arr_floorroom[i][j];
12                 var index= (i)*4*can_temp.width+(j-1)*4;//每个room由4个元素组成
13                 var color4=[];
14                 if(tab_landtypedqk2[obj.type2final].color)
15                 {//从地形对象中获取颜色
16                     color4=tab_landtypedqk2[obj.type2final].color;
17                 }
18                 else
19                 {
20                     color4=[250,126,126];//默认纹理远观颜色
21                 }
22                 imagedata_temp.data[index]=color4[0];//这里存的真的是颜色
23                 imagedata_temp.data[index+1]=color4[1];
24                 imagedata_temp.data[index+2]=color4[2];
25                 imagedata_temp.data[index+3]=255;
26 
27             }
28         }
29 
30     }

下一场用和前边类似的主意将数据纹理送入显卡并拓展渲染

着色器代码如下:

极端着色器:

4858美高梅 394858美高梅 40

 1 uniform mat4 worldViewProjection;
 2 uniform mat4 worldView;
 3 attribute vec3 position;
 4 
 5 varying vec3 vPosition;
 6 varying vec3 oPosition;//自身坐标系里的位置
 7 
 8 void main(){
 9     gl_Position=worldViewProjection*vec4(position,1);
10     vPosition=vec3(worldView*vec4(position,1));
11     oPosition=position;
12 }

View Code

片元着色器:

4858美高梅 414858美高梅 42

 1 precision highp float;
 2 //varying vec4 vColor;
 3 varying vec3 vPosition;
 4 varying vec3 oPosition;
 5 //uniform vec4 uColor;
 6 uniform sampler2D utexturedqk;
 7 uniform float wid_utexturedqk;
 8 uniform float hei_utexturedqk;
 9 
10 uniform sampler2D utexturepalpha;//一个单元里保存了四个元素!!!!
11 //uniform vec3 uarrdqk[60000];//es3.0之前的glsles不支持隐含数组!!!!
12 uniform float pbeta;
13 uniform float wid_utexturepalpha;
14 uniform float hei_utexturepalpha;
15 //uniform float uarrpalpha[500];//用来测试的行星只有199层,预设为500层应该够了
16 uniform float uarrpalphalen;
17 uniform float max_floorf;
18 uniform float MathPI;
19 
20 float getDataVec4(vec4 data,int id) {
21     for (int i=0; i<4; i++) {
22         if (i == id) return data[i];
23     }
24 }
25 float getData500(float data[500],int id) {
26     int len=int(floor(uarrpalphalen+0.5));
27     for (int i=0; i<500; i++) {
28         if(i>=len)//i不能和非常量比较!!只好换个方式限制它
29         {
30             return 0.0;
31         }
32         if (i == id) return data[i];
33     }
34 }
35 float getOdevity(float a)//判断浮点型整数的奇偶性,偶返回0,奇返回1
36 {
37     float b=mod(a,2.0);
38     float c=0.0;
39     if(b>=0.5&&b<1.5)
40     {
41         c=1.0;
42     }
43     return c;
44 }
45 
46 void main()
47 {
48     //vec4 tempColor=uColor;//es3.0之前不支持round!!!!
49     //glsl事实上以float为计算基准
50     //int max_floor=int(floor(max_floorf+0.5));
51     //算层数
52     float r=sqrt(oPosition.x*oPosition.x+oPosition.y*oPosition.y+oPosition.z*oPosition.z);
53     float beta=asin(oPosition.y/r);//俯仰角
54     //int int_beta=int(floor((beta/(pbeta*2.0))+0.5));//层数
55     float count_beta=(beta/(pbeta*2.0));
56     //int index_beta=int(floor(count_beta+ max_floorf+0.5));//整数层数索引
57     float index_beta=floor(count_beta+ max_floorf+0.5);
58     //int roomcount=0;
59     //使用数据纹理法,取这一层的区分度
60     //int int1=int(floor(mod(index_beta,4.0)+0.5));//使用哪个颜色分量
61     //float float1=(index_beta/4.0);//在纹理采样器中的顺序索引
62     float floatu=(mod(index_beta,wid_utexturepalpha))/(wid_utexturepalpha)+(0.5/wid_utexturepalpha);//猜测u是x轴
63     float floatv=(((index_beta)/(wid_utexturepalpha)))/(hei_utexturepalpha)+(0.5/(wid_utexturepalpha*hei_utexturepalpha));
64     vec2 UVpalpha=vec2(floatu,floatv);
65     vec4 vec4palphas=texture2D(utexturepalpha, UVpalpha);
66     float palpha=(vec4palphas[0]*255.0*100.0+vec4palphas[1]*255.0)/pow(10.0,vec4palphas[2]*255.0);
67     //float palpha=getData500(uarrpalpha,int(floor(index_beta+0.5)));//改为尝试数组法传递数据
68     //取这一层的转角
69     float alpha=atan(oPosition.z,oPosition.x);//标准反正切函数有两个参数!!
70 
71     if(getOdevity(index_beta)==1.0)//为了体现交错效果,如果是奇数层alpha要减去palpha
72     {
73         alpha-=palpha;
74     }
75     if(alpha<0.0)
76     {
77         alpha+=(MathPI*2.0);
78     }
79     //取地区块数据纹理的索引
80     float floatu2=(alpha/(palpha*2.0))/wid_utexturedqk;
81     float floatv2=index_beta/hei_utexturedqk+0.5/hei_utexturedqk;
82     vec2 UVdqk=vec2(floatu2,floatv2);
83     gl_FragColor=texture2D(utexturedqk, UVdqk);
84     //gl_FragColor=vec4palphas;
85     //gl_FragColor=texelFetch(utexturedqk,ivec2(int(floor(alpha/(palpha*2.0)+0.5)),int(floor(index_beta+0.5))),0);//这个整数像素的方法是WebGL2开始加入的!!!!
86     //gl_FragColor=vec4(1.0*floatu,1.0*floatv,1.0*floatv2,1.0);//红,绿,蓝结果不是0就是1??!!
87 
88     //int index_dqk=roomcount-1+int(floor((alpha/palpha)+0.5));
89     //vec4 tempColor=vec4(uarrdqk[index_dqk],1.0);
90 
91     //float float_3=index_beta/(max_floorf*2.0);
92     //float float_4=oPosition.y/5.0;
93     //canvas的imagedata用255,255,255,1定义颜色通道,而glsl用1.0,1.0,1.0,1.0定义!!!!
94 
95 
96 }

View Code

片元着色器和后面的界别是加了2个论断奇偶层数的点子,此外,在WebGL2.0中参与的texelFetch方法可以一向依照像素的地方对纹理图实行采集样品,比使用纹理坐标进行采集样品方便广大。

执行顺序效果如下:

4858美高梅 43

拉近相机:

4858美高梅 44

当中变化地形耗费时间1分30秒,平滑地形并入库耗费时间3分10秒,从数据库查询必要约1分钟

五、总结

其一措施耗费时间可比长,不恐怕实时应用,但能够用来三遍性创设地形数次选取,大概能够因而优化代码来增加运转速度。生成的海域和陆上混杂度比较高,恐怕能够考虑从种子地方随机生长地形的算法,那样能够使得大陆和海域区分的更是显眼。着色器代码中从不设想周期性光照效果,也从未考虑极点处的地区块如何处理。

下一步准备给所在块添加鼠标交互,和更细节的地貌块显示。

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图
Copyright @ 2010-2019 美高梅手机版4858 版权所有