关于webgl:GLB文件格式简介与GLB文件格式转换

什么是GLB文件?GLB文件(.GLB)代表“GL传输格局二进制文件”,是用于共享3D数据的标准化文件格式。确切地说,它能够蕴含无关三维模型、场景、模型、光源、材质、节点档次和动画的信息。当您关上GLB文件格式时,您能够实现残缺的三维场景可视化,并与之交互。这就是为什么它也被称为3D资产世界的JPEG。 GLB文件的用处是什么?GLB文件格式是一种绝对较新的格局,于2015年引入,作为示意GLTF文件的二进制格局(.GLTF),而不是JSON格局。因为其轻量级个性,这种格局通常用于挪动和网络应用程序,以及图形游戏、视频游戏、VR和AR应用程序,GLB文件也很容易通过电子邮件或其余文件共享平台共享。总的来说,GLB文件格式为3D内容交付和显示提供了一个通用而高效的解决方案。 GLB和GLTF文件之间有什么区别?GLB和GLTF之间存在显著差别,GLB格局是GLTF文件的一个版本,不同之处在于,GLB格局是二进制文件格式,而GLTF格局是基于JSON(JavaScript对象表示法)的。GLB将三维场景的所有元素(包含材质、节点档次和摄影机)定位在一个压缩文件中。相比之下,GLTF文件须要内部解决文件格式,例如纹理、着色器和动画数据等其余格局。这些内部元素存储在GLTF文件中,但每个元素都应用惟一的格局编码语言(JPEG用于纹理,GLSL用于着色器,BIN用于动画数据)。因而,GLB被辨认为一个独立的文件,蕴含单个网络中3D场景的所有组件,而GLTF被视为一个非独立文件,须要纹理、着色器和动画数据等元素的内部解决文件。此外,GLB格局的大小比GLTF格局小33%,这使其成为一种更高效的抉择,因为它须要更少的解决能力。 如何关上GLB文件?能够在所有次要的三维建模程序中关上和转换GLB文件。关上GLB文件最简略的办法是首先在计算机上找到文档。而后只需右键单击残缺的文件名并抉择关上。如果您有Windows设施,但没有3D建模软件,依然能够在 Microsoft 3D Viewer 或者Microsoft Paint 3D 中收费关上GLB并进行即时交互。如果你想在Mac上关上GLB文件,能够间接在线关上。什么收费程序关上GLB文件?有几个收费程序能够关上GLB文件,并能够轻松查看3D模型并与之交互。其中一个程序是微软的3D查看器,它预装在Windows 10上,三维查看器容许用户对模型进行旋转、缩放和平移,并提供各种照明和着色选项。另一个收费选项是Blender,这是一款弱小的开源3D建模软件,反对GLB文件,并提供了编辑和操作3D模型的宽泛性能。此外,许多基于web的3D查看器,如3D模型在线转换,也反对GLB文件,容许用户间接在web应用程序中查看和转换3D模型,而无需装置任何软件。 如何创立GLB文件?创立GLB文件是一个简略的过程,能够间接从风行的三维建模程序(如Blender、Autodesk 3DS Max或SketchUp)中实现。在设计软件中,将模型、光源、材质、节点层次结构、动画等的所有内部文件合并为一个我的项目文件,而后将其导出为GLB文件。一些旧版本的3D建模程序可能须要额定的软件,来导出GLB文件。 GLB文件格式的构造是什么?GLB文件格式被结构为对数据进行编码的单个二进制文件。这个独自的GLB文件大小和格局由两局部组成:一个JSON数据局部,包含现有GLTF文件的原始信息数据及其设置,另一个二进制缓冲区,反对其余文件,如动画。JSON数据局部蕴含无关3D模型的元数据,例如节点层次结构、纹理和动画。二进制缓冲区蕴含理论的三维几何体数据,例如模型顶点的地位、法线和UV。这两个局部联合在一起,能够在互联网上高效、高效地传输3D模型。 如何转换GLB文件?能够将GLB文件转换为特定格局。您能够应用网络上的在线资源,将GLB转换为FBX、将GLB转换为OBJ、将GLB转换为STL、将GLB转换为GLTF,如3D模型在线转换,该网站能够在实现GLB文件与其余格局之间的转换。 应用GLB文件的长处和毛病是什么?应用GLB文件格式的次要益处是其紧凑的尺寸,使其更适宜挪动和网络应用程序、视频游戏和AR/VR应用程序。与其余文件格式(如OBJ文件)不同,GLB扩大蕴含单个独立文件中的齐全基于物理的渲染(PBR)着色器、纹理和动画信息。这容许在关上此类文件时立刻查看和交互。须要留神的是,尽管GLB文件的压缩个性提供了劣势,但它可能蕴含的细节和复杂性与其余文件类型和格局(如GLTF文件)不同。 GLB文件如何更改PBR着色器的游戏基于物理的渲染(PBR)是一种用于在三维建模中取得更真切和精确后果的渲染技术。通过模仿光如何与事实世界中的材质交互,PBR着色器能够产生加强的渲染成果,使对象看起来更真切。为了实现这一点,PBR着色器在很大水平上依赖于纹理,例如漫反射、粗糙度和金属贴图。GLB文件非常适合此操作,因为它们在一个独立文件中蕴含残缺的PBR着色器以及所有必要的纹理。在GLB文件中拜访这些原始纹理很容易,能够在3D建模和渲染中实现更精简、更高效的工作流程。 你能打印GLB文件吗?尽管能够以GLTF/GLB文件格式打印3D模型,但通常倡议将文件转换为更规范的3D打印格局,例如STL文件格式。STL 3D文件格式还将模型组织为3D三角形和网格的列表,并且能够通过二进制版本的3D打印软件轻松读取,从而简化打印过程。相同,应用GLB文件扩展名进行3D打印可能会导致额定的解决工夫和复杂性,从而升高其效率。

August 31, 2023 · 1 min · jiezi

关于webgl:WebGLThreejs-入门与实战系统学习-Web3D-技术

download:WebGL+Three.js 入门与实战,零碎学习 Web3D 技术React 18 和 Next.js 13:前端开发的新时代 引言 前端开发是一个一直变动和进化的畛域,每年都有新的工具和技术呈现,为开发人员提供了更多的抉择和可能性。在 2023 年,两个最受关注和期待的前端开发工具是 React 18 和 Next.js 13,它们都是在原有的根底上进行了重大的更新和改良,为开发人员带来了许多新性能和劣势。本文将介绍 React 18 和 Next.js 13 的次要个性和长处,以及它们如何协同工作,为前端开发带来新的体验和成果。 React 18 React 是一个用于构建用户界面的 JavaScript 库,它是目前最风行和最宽泛应用的前端框架之一。React 18 是 React 的最新版本,它在放弃 React 的核心理念和劣势的同时,引入了一些新的概念和性能,如: Streaming SSR:这是一种新的服务端渲染(SSR)形式,它能够让 React 在服务端生成 HTML 的同时,将 HTML 流式地发送给客户端,而不是期待整个页面渲染实现后再发送。这样能够进步页面的加载速度和性能,以及晋升用户体验。React Server Components:这是一种新的组件类型,它能够让 React 在服务端运行一些组件逻辑,并且将后果发送给客户端。这样能够缩小客户端的代码量和累赘,以及进步数据获取和安全性。Automatic Batching:这是一种新的更新机制,它能够让 React 主动将多个状态更新合并为一个批次,并且在适合的机会执行。这样能够防止不必要的重渲染和性能损耗,以及晋升应用程序的稳定性。Concurrent Mode:这是一种新的渲染模式,它能够让 React 在后盾预渲染一些组件,并且在用户交互时疾速切换到最新的状态。这样能够进步页面的响应速度和平滑度,以及晋升用户体验。Suspense:这是一种新的数据获取形式,它能够让 React 在渲染组件时暂停,并且期待数据筹备好后再持续。这样能够防止显示空白或加载中的状态,并且提供更好的过渡成果。Next.js 13 Next.js 是一个基于 React 的框架,它能够让开发人员轻松地构建动态网站、服务端渲染网站、混合网站等各种类型的 Web 应用程序。Next.js 13 是 Next.js 的最新版本,它在继承 Next.js 的优良个性和生态系统的同时,引入了一些新的性能和改良,如: Edge and Node.js Runtimes:这是一种新的运行时环境,它能够让 Next.js 在边缘服务器或者 Node.js 服务器上运行,并且提供对立的 API 和体验。这样能够进步应用程序的部署灵活性和可扩展性,以及进步性能和安全性。Middleware:这是一种新的申请解决形式,它能够让开发人员在边缘服务器或者 Node.js 服务器上编写一些中间件函数,并且在每个申请达到应用程序之前执行。这样能够实现一些自定义的逻辑和性能,如身份验证、重定向、缓存等。Image Optimization:这是一种新的图片优化形式,它能够让 Next.js 在边缘服务器或者 Node.js 服务器上对图片进行压缩、裁剪、格局转换等操作,并且依据不同的设施和网络条件提供最合适的图片。这样能够缩小图片的大小和加载工夫,以及进步用户体验。Script Optimization:这是一种新的脚本优化形式,它能够让 Next.js 在构建时对 JavaScript 代码进行剖析和优化,并且依据不同的页面和组件提供最合适的脚本。这样能够缩小脚本的数量和大小,以及进步性能和可维护性。Built-in CSS and Sass Support:这是一种新的款式反对形式,它能够让开发人员间接在 Next.js 中应用 CSS 或者 Sass 文件,并且主动解决导入、作用域、压缩等问题。这样能够简化款式的编写和治理,以及进步兼容性和效率。React 18 和 Next.js 13 的协同 ...

August 19, 2023 · 1 min · jiezi

关于webgl:网易Threejs可视化企业实战WEBGL课2023全面升级版

download:网易Three.js可视化企业实战WEBGL课-2023全面升级版Vue 3整体意识和路由配置Vue.js是一个风行的JavaScript框架,用于创立动静Web应用程序。在Vue 3中,组合式API和新的个性使得开发更加灵便和高效。 本文将介绍Vue 3的整体意识以及如何应用Vue Router进行路由配置。 Vue 3整体意识组合式APIVue 3引入了一种新的API,称为组合式API,它容许咱们以性能为核心地组织代码。通过组合式API,能够更容易地复用逻辑和状态,并从基于选项的API过渡到更具表现力和可维护性的代码。 以下是一个通过应用组合式API创立Vue组件的示例: javascriptimport { reactive, computed } from 'vue' export default { setup() { const state = reactive({ count: 0,})const increment = () => { state.count++}const doubleCount = computed(() => state.count * 2)return { state, increment, doubleCount,}},}在这个示例中,咱们应用了reactive函数来创立响应式的state对象,并应用computed函数来计算doubleCount属性。而后,咱们将state、increment和doubleCount作为返回值裸露给Vue组件。 Teleport组件Vue 3引入了Teleport组件,它容许咱们将内容传输到DOM构造中的其余地位,而无需创立嵌套组件。 以下是一个应用Teleport组件的示例: html<teleport to="#modal"> <div>Modal content here</div></teleport> <div id="modal" />在这个示例中,咱们将modal内容传输到id为“modal”的DOM元素中。这使得咱们能够将模态框内容搁置在任何地位,而不用间接蕴含在模态框组件中。 Vue Router路由配置Vue Router是Vue.js官网提供的路由管理器,用于在应用程序中实现客户端路由。Vue Router提供了多种路由配置选项,包含动静路由和嵌套路由等。 以下是一个根本的Vue Router配置示例: javascriptimport { createRouter, createWebHistory } from 'vue-router' const routes = [ { ...

May 31, 2023 · 1 min · jiezi

关于webgl:WebGLThreejs-入门与实战系统学习-Web3D-技术

download:WebGL+Three.js 入门与实战,零碎学习 Web3D 技术产品思维:让你的产品更加用户导向 在当今竞争强烈的市场中,要想打造一款胜利的产品并不容易。而要想真正做好产品,就须要有一种被称为“产品思维”的理念。那么什么是产品思维呢?它又有哪些特点和劣势呢? 简略来说,产品思维就是一种以用户需要为导向来开发和设计产品的思维形式。与传统的技术或工程思维不同,产品思维将用户体验放在首位,并且提倡通过数据分析和用户反馈来继续改良产品。这样能够让企业更加聚焦用户需要,进步产品的满意度和市场份额。 那么产品思维又有哪些特点呢?首先,它强调用户体验。在传统的产品开发过程中,往往由技术人员主导,而对用户体验的关注则绝对较少。而产品思维则将用户体验作为外围,一直优化产品的界面、性能和操作流程等方面,从而实现更好的用户体验。 其次,产品思维具备敏锐的市场洞察力。基于对用户需要的深刻理解,产品思维可能更好地把握市场变化趋势,及时调整产品的策略和方向。这样能够让企业始终保持在市场的前沿,抢占竞争劣势。 最初,产品思维强调数据驱动。通过对用户行为、应用习惯等数据进行剖析,产品思维可能及时理解用户需要和反馈,并以此作为产品改良的根据。这样能够让企业更加聚焦于用户需要,一直进步产品的满意度和市场份额。 总之,产品思维是一种十分重要的理念,它可能让企业更加聚焦于用户需要,强化市场洞察力,并通过数据分析继续改良产品。因而,如果您心愿打造一款胜利的产品,那么就请务必遵循产品思维,将用户体验放在首位,一直迭代优化,为用户提供更好的产品体验。

May 3, 2023 · 1 min · jiezi

关于webgl:尝试-WebGPU-过程中掉的一些坑

TODO 两个试验我的项目 https://github.com/Triadica/lagopus.tshttps://github.com/Triadica/solubleuniform buffer 的编码规定. 数据会依照大小对齐, 然而编码的时候 https://www.w3.org/TR/WGSL/#address-space-layout-constraints 能够试试本人加上 padding 来 buffer https://stackoverflow.com/questions/74186801/is-there-any-way-to-enforce-a-16-byte-alignment-for-a-uniform-buffer-in-glsl 依照文档说的, 不止对 uniform buffer 是这样, 我还没解决过. 没有 FBO 怎么办. https://www.cnblogs.com/onsummer/p/the-missing-fbo-and-rbo-in... 整体思路能够参考 blur 的做法, 至多能跑通的, https://webgpu.github.io/webgpu-samples/samples/imageBlur#../... TODO

April 10, 2023 · 1 min · jiezi

关于webgl:OpenGL和WebGL的关系与区别

什么是WebGLWebGL™是一个跨平台的,免版税的凋谢Web规范,用于基于OpenGL ES的低级3D图形API,通过HTML5 Canvas元素向ECMAScript公开。相熟OpenGL ES 2.0的开发人员将应用GLSL将WebGL辨认为基于Shader的API,其结构在语义上与底层OpenGL es API的结构类似。它十分靠近OpenGL ES标准,对开发人员对内存治理语言(如JavaScript)的冀望做出了一些退让。WebGL 1.0 公开了 OpenGL ES 2.0 功能集;WebGL 2.0 公开了 OpenGL ES 3.0 API。 WebGL将无插件3D带到了Web上,并在浏览器中实现。 WebGL和OpenGL的区别两者都是市场上的热门抉择,接下来看看两者之间有什么区别: WebGL缩写为Web Graphics Library。它次要用于渲染二维图形和交互式三维图形。它是能够与HTML5一起应用的Javascript API。它反对跨平台,并且仅提供英语版本。WebGL程序由一个用JavaScript编写的控制代码组成。 OpenGL被称为Open Graphics Library。它被称为跨语言和平台应用程序编程接口,用于渲染二维和三维矢量图形。OpenGL提供了许多性能,如扩大。 WebGL专为渲染 2D 和 3D 图形而设计。OpenGL是一个跨语言和平台的API,用于渲染2D和3D矢量图形。WebGL次要用于在浏览器中运行Web应用程序。OpenGL次要用于桌面应用程序。WebGL是用JavaScript语言编写的。OpenGL是用C语言编写的。WebGL相对而言,它具备较少的性能。OpenGL具备许多性能,能够使应用程序或图形更具交互性。WebGL基于OpenGL ES,不足惯例OpenGL所具备的许多性能,例如WebGL仅反对顶点和片段着色器。OpenGL 具备 WebGL 中没有的性能,如几何着色器、细分着色器和计算着色器。在WebGL中,能够借助2D纹理来伪造3D纹理。在OpenGL中,能够利用几何体和着色器。WebGL基于OpenGL ES 2.这不是一般的OpenGL。OpenGL ES是OpenGL的子集。OpenGL ES的性能较少,对用户来说非常简单。OpenGL有很多性能,很难应用。总结OpenGL与WebGL都是渲染二维和三维图形的图形库。WebGL被用于HTML画布元素,这意味着它能够与HTML语言合并。WebGL很容易学习,因为它在javascript和Html中简略易用。 OpenGL的确须要良好的常识能力应用和开发应用程序。 OpenGL与WebGL都有长处和毛病。图形库的抉择能够依据应用程序的要求、可扩展性进行。 扩大Sovit3D 是一个物联网可视化PaaS开发平台,基于JavaScript语言的3D图形引擎,为Web可视化提供了丰盛的展示模式和视觉效果,帮忙软件开发公司、解决方案提供商轻松搭建3D可视化界面。平台聚焦工业数字孪生的生产管控、智慧城市的监控运维等可视化应用领域,产品的模块组态化模式能够满足全因素智慧场景的构建。广泛应用于电力能源、水利、物联网、工业互联网、智慧城市、智慧医疗、智慧农业、IT运维等各行业多畛域。 Sovit3D 平台采纳B/S架构,基于WebGL绘图技术标准,提供基于Web浏览器的3D可视化行业组件,反对HTML5/SVG等最新技术,可不便的在浏览器上进行浏览和调试。为开发人员制作合乎用户应用习惯的大屏可视化利用,包含2D图表剖析、3D修建实景、3D工业设施模型等相干内容,轻松拖拽即可实现,管制实时数据及动画展现、历史回放、报警、命令下发等性能。

February 22, 2023 · 1 min · jiezi

关于webgl:基于Web的6个完美3D图形WebGL库

古代前端、游戏和Web开发正是WebGL能够转化为数字杰作的货色。应用GPU绘制在浏览器屏幕上生成的矢量元素,WebGL创立交互式Web图形,从而取得用户体验。视觉元素的品质和复杂性使该工具在HTML或CSS等其余办法中怀才不遇。 WebGL根底WebGL不是一个图形套件。相同,它利用代码绘制几何对象,利用客户端的 GPU 引擎在 HTML 画布上栅格化图形对象。 在 3D 场景中,每个点都是由其 x,y,z 坐标标识的顶点。而后将顶点连接起来造成一组三角形形态,称为基元。利用光源来创立暗影和深度的外观。而后将基元栅格化以将 3D 矢量图形创立为 2D 像素的投影,从而诱使大脑在 2D 计算机屏幕上看到 3D 对象。 WebGL代码中有两种类型的函数: 顶点着色器片段着色器它们用于通知计算机如何在屏幕上绘制像素。尽管主程序代码是用JavaScript编写的,但着色器应用GL Shader语言,它与C / C++十分类似。 顶点着色器计算顶点的坐标,片段着色器负责计算像素色彩。着色过程须要计算机执行大量计算能力流畅地渲染图像。CPU 解决的工作负载通常太大。出于这个起因,WebGL利用GPU来更无效地调配计算。 从实质上讲,WebGL API就是自定义着色器状态,以管制在客户端屏幕上绘制的内容。侥幸的是,无需手动创立程序即可增加 3D 图形。能够应用 Three.j、Unity WebGL构建选项等资源来疾速设计 3D 体验,也能够用用像Sovit3D可视化编辑器来疾速设计,无需太多的 WebGL 基础知识。 WebGL其余库Web开发人员能够应用各种各样的WebGL框架和库来构建Web产品。利用事后编写的元素能够大大提高 Web 开发的速度。 当初让咱们疾速概述一下一些用于应用 WebGL 开发应用程序的风行附加库。 Unity WebGL 如果你正在寻找WebGL开发的资源,这可能是你会遇到的第一个库。在网页中创立内容时,Unity WebGL容许Web开发人员间接与浏览器的JavaScript引擎交互。这样,网页上的所有元素都能够互相通信。 Unity WebGL提供了不同的办法来做到这一点:从Unity脚本调用Javascript或C函数,甚至从浏览器的JavaScript向Unity脚本发送一些数据。目前,大多数支流桌面浏览器都反对Unity WebGL内容。然而,它尚不提供对挪动设施的反对。 Three.js Three.js 是一个专门为 WebGL 筹备的 JavaScript 元素库。该库具备大量成果、对象、摄像机、场景、材质、着色器和其余实用程序。这些手册仍在编写中,但网络上的宏大开发人员社区运行论坛和探讨。Babylon.jsGithub上的另一个开源库,Babylon.js通常被形容为在浏览器中显示3D图形的引擎。它的原始语言是Typescript,但它的代码由所有反对WebGL和HTML5的浏览器本地解释。 Babylon.js 具备宽泛的利用,包含游戏、立功数据可视化、时尚、医疗保健教育和军事训练。PlayCanvasPlayCanvas 专门用于游戏,是一个由专有的基于云的开发平台反对的 3D 引擎,容许 Web 开发团队从多台计算机实时编辑 Web 我的项目。这些性能包含 3D 动画、刚体物理模仿和声音设计。该引擎于 2014 年开源,在 Github 上也有一个收费的存储库。 ...

February 16, 2023 · 1 min · jiezi

关于webgl:WebGL简明教程之2使用缓冲区绘制三角形

纯色三角形咱们首先借助缓冲区绘制一个三角形: 残缺的代码如下: <canvas width=200 height=200 style="outline:1px solid gray;"> 十分道歉,您的浏览器不反对canvas!</canvas><!-- 顶点着色器 --><script type='x-shader/x-vertex' id='vs'> attribute vec4 a_position; void main(){ gl_Position=a_position; }</script><!-- 片段着色器 --><script type='x-shader/x-fragment' id='fs'> precision mediump float; uniform vec4 u_color; void main(){ gl_FragColor=u_color; }</script><script> var gl = document.getElementsByTagName('canvas')[0].getContext('webgl'); var loadShader = function (type, source) { var shader = gl.createShader(type); gl.shaderSource(shader, source); gl.compileShader(shader); return shader; }; var vertexShader = loadShader(gl.VERTEX_SHADER, document.getElementById('vs').innerHTML), fragmentShader = loadShader(gl.FRAGMENT_SHADER, document.getElementById('fs').innerHTML); var glProgram = gl.createProgram(); gl.attachShader(glProgram, vertexShader); gl.attachShader(glProgram, fragmentShader); gl.linkProgram(glProgram); gl.useProgram(glProgram); // 设置三角形的色彩(红色) var u_color = gl.getUniformLocation(glProgram, 'u_color'); gl.uniform4f(u_color, 1, 0, 0, 1); // 创立缓冲区 var buffer = gl.createBuffer(); // 把缓冲区对象绑定到指标 gl.bindBuffer(gl.ARRAY_BUFFER, buffer); // 筹备好点的数据 var data = new Float32Array([ -0.7, -0.7, 0, 0.7, -0.7, 1, 0, 0.7, 0 ]); // 写入数据到缓冲区 gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW); // 获取变量地位 var a_position = gl.getAttribLocation(glProgram, "a_position"); // 把缓冲区对象调配给指标变量 gl.vertexAttribPointer(a_position, 3, gl.FLOAT, false, data.BYTES_PER_ELEMENT * 3, 0); // 连贯指标对象和缓冲区对象 gl.enableVertexAttribArray(a_position); // 绘制一个三角形 gl.drawArrays(gl.TRIANGLES, 0, 3);</script>接着,咱们未来解读下面这段代码。 ...

January 14, 2023 · 2 min · jiezi

关于webgl:WebGL简明教程之1绘制一个点

咱们在画布的核心绘制一个正方形,你能够先看看最终的成果: 代码上面是残缺的代码,咱们后续将对外面的内容进行解说阐明: <canvas width=200 height=200 style="outline:1px solid gray;"> 十分道歉,您的浏览器不反对canvas!</canvas><!-- 顶点着色器 --><script type='x-shader/x-vertex' id='vs'> void main(){ gl_Position=vec4(0.0,0.0,0.0,1.0); gl_PointSize=100.0; }</script><!-- 片段着色器 --><script type='x-shader/x-fragment' id='fs'> void main(){ gl_FragColor=vec4(1.0,0.0,0.0,1.0); }</script><script> // 先获取gl var gl = document.getElementsByTagName('canvas')[0].getContext('webgl'); // 定义一个把着色器字符串加载成着色器对象的函数 var loadShader = function (type, source) { // 创立着色器对象 var shader = gl.createShader(type); // 绑定资源 gl.shaderSource(shader, source); // 编译着色器 gl.compileShader(shader); return shader; }; // 别离加载顶点着色器对象和片段着色器对象 var vertexShader = loadShader(gl.VERTEX_SHADER, document.getElementById('vs').innerHTML), fragmentShader = loadShader(gl.FRAGMENT_SHADER, document.getElementById('fs').innerHTML); // 创立一个着色器程序 var glProgram = gl.createProgram(); // 把后面创立的两个着色器对象增加到着色器程序中 gl.attachShader(glProgram, vertexShader); gl.attachShader(glProgram, fragmentShader); // 把着色器程序链接成一个残缺的程序 gl.linkProgram(glProgram); // 应用这个残缺的程序 gl.useProgram(glProgram); // 绘制一个点 gl.drawArrays(gl.POINTS, 0, 1);</script>绘制流程 ...

January 14, 2023 · 1 min · jiezi

关于webgl:Web3D从0开始学习ThreejsBlender实现甜甜圈掉落效果-大帅老猿threejs特训

前言最近大帅邀请胖达老师带来了元宇宙实战特训,具体解说了如何应用Blender进行3D建模、增加动画以及如何在Threejs中展现、管制3D模型,让我特地感叹,原来一些看似简单的3D我的项目能够如此简略的实现。 Web3D 简介Web3D的次要展现形式:浏览器间接渲染:电脑浏览器、挪动端浏览器(包含微信浏览器) 微信小程序;服务端渲染(服务端运行,成果好,经营老本高);将3D画面像素推流到前端浏览器/小程序展现;3D近几年风行的相干概念:数字孪生:三维实景模型 + 多系统监控/告警数据,实现近程监管(监控、治理);VR:把人装进虚拟环境AR:把虚构装进事实浏览器运行3D的计划ActiveX插件: IE、Chrome老版本、Firefox老版本,已过期; Flash: 时代王者,官网已进行保护; WebGL: 浏览器原生反对(IE11根本反对,其它浏览器根本都反对) WebGPU: 性能高,目前还未失去操作系统和浏览器的广泛支持; 可实现公布WebGL到浏览器运行的计划(从重到轻):Unity引擎: wasm webgl 空包:20m+ (不反对ie11低版本chrome及Firefox及国产化) CocosCreator: webgl 空包:6m+ (不反对ie11及低版本chrome及firefox) threejs: webgl 库大小:1m- (开源,IE11均反对) babylonjs: webgl 库大小:4m+ (微软开源,中文材料绝对较少) 明天咱们就来理解Three.js提到 Three.js,不得不先提 OpenGL 和 WebGL,OpenGL 是一个跨平台的3D/2D的绘图规范(标准),WebGL(Web Graphics Library)是一种3D绘图协定。 WebGL容许把JavaScript和OpenGL 联合在一起使用,但应用WebGL原生的API来写3D程序十分的简单,同时须要绝对较多的数学知识,对于开发者来说学习老本十分高。 Three.js是基于webGL的封装的一个易于应用且轻量级的3D库,Three.js对WebGL提供的接口进行了十分好的封装,简化了很多细节,大大降低了学习老本,极大地提高了性能,性能也十分弱小。 (1)Three.js官网 (2)Three.js 的 github 地址 起步阶段先从简略开始,间接援用 Three.js,学会最根本的 Three.js 的应用办法<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title></head><body> <script src="js/three.min.js"></script> <script> // 创立场景 const scene = new THREE.Scene(); // 创立相机,PerspectiveCamera(透视相机),参数(视线角度,长宽比,进截面,远截面) const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); // 渲染器 const renderer = new THREE.WebGLRenderer(); // 设置渲染器的宽高 renderer.setSize( window.innerWidth, window.innerHeight ); // 将renderer(渲染器)的dom元素(renderer.domElement)增加到咱们的HTML文档中 document.body.appendChild( renderer.domElement ); // demo--立方体 const geometry = new THREE.BoxGeometry( 1, 1, 1 ); // BoxGeometry 立方体对象 const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } ); // MeshBasicMaterial 其中的一种材质,设置色彩绿色 const cube = new THREE.Mesh( geometry, material ); // 创建一个网格对象 scene.add( cube ); // 将网格对象增加到场景中 camera.position.z = 5; // function animate() { requestAnimationFrame( animate ); // 创立了一个使渲染器可能在每次屏幕刷新时对场景进行绘制的循 cube.rotation.x += 0.01; // 使正方体能动起来 cube.rotation.y += 0.01; // 使正方体能动起来 renderer.render( scene, camera ); } animate(); </script></body></html>应用 npm 来开发并测试(1)获取我的项目代码(2)装置依赖 ...

January 13, 2023 · 2 min · jiezi

关于webgl:加载OBJ和MTL文件

HTML形式<!-- * @Author: yangyuguang * @Date: 2022-12-04 17:22:11 * @LastEditors: yangyuguang * @LastEditTime: 2023-01-11 11:19:31 * @FilePath: /排行榜初始代码/test2.html--><!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>加载OBJ和MTL文件</title> <style> html, body{ width: 100%; height: 100%; overflow: hidden; } *{ margin:0; padding:0; } </style> <!-- 引入three.js三维引擎 --> <script src="http://www.yanhuangxueyuan.com/versions/threejsR92/build/three.js"></script> <script src="http://www.yanhuangxueyuan.com/threejs/examples/js/controls/OrbitControls.js"></script> <script src="http://www.yanhuangxueyuan.com/threejs/examples/js/loaders/OBJLoader.js"></script> <script src="http://www.yanhuangxueyuan.com/threejs/examples/js/loaders/MTLLoader.js"></script></head><body> <script> var scene = new THREE.Scene(), camera, renderer = new THREE.WebGLRenderer(); /* OBJ和材质文件mtl加载 */ var OBJLoader = new THREE.OBJLoader(); //obj加载器 var MTLLoader = new THREE.MTLLoader();//材质文件加载器 MTLLoader.load('./static/material.mtl',function(meterials){ console.log(meterials); OBJLoader.setMaterials(meterials); OBJLoader.load('./static/model.obj',function(obj){ /* 返回的组对象插入场景中 */ scene.add(obj) obj.children[0].scale.set(5,5,5)//网络模型播放 }) }) createLight() createCamera() createRenderer() /* 通过requestAnimationFrame()操作三维场景 */ var controls = new THREE.OrbitControls(camera,renderer.domElement); //创立控件对象 // 创立一个时钟对象Clock var clock = new THREE.Clock() function render(){ renderer.render(scene,camera);//执行渲染操作 requestAnimationFrame(render) //申请再次执行渲染函数render } render() // 正投影相机设置 function createCamera(){ var width = window.innerWidth; //窗口设置 var height = window.innerHeight;//窗口高度 var k = width/height;//窗口宽高比 var s = 150;//三维场景显示范畴管制系数,系数越大,显示的范畴越大 // 创立相机对象 camera = new THREE.OrthographicCamera(-s*k,s*k,s,-s,1,1000) camera.position.set(200,300,200) //设置相机地位 camera.lookAt(scene.position);//设置相机方向(指向的场景对象) } // 透视相机 function createPerspectiveCamera(){ var width = window.innerWidth;//窗口宽度 var height = window.innerHeight//窗口高度 /* 透视投影相机对象 */ camera= new THREE.PerspectiveCamera(60,width/height,1,1000) camera.position.set(200,300,200)//设置相机地位 camera.lookAt(scene.position)//设置相机方向(指向的场景对象) } /* 创立渲染器对象 */ function createRenderer(){ renderer.setSize(window.innerWidth,window.innerHeight)//设置渲染区域尺寸 renderer.setClearColor(0xb9d3ff,1)//设置背景色彩 document.body.appendChild(renderer.domElement)//body元素中插入canvas对象 } /* 创立光源 */ function createLight(){ // 点光源 var point = new THREE.PointLight(0xffffff) point.position.set(400,200,300)//点光源地位 scene.add(point);//点光源增加到场景中 // 环境光 var ambient = new THREE.AmbientLight(0x444444) scene.add(ambient) } </script></body></html>在vue中应用第一种办法应用three-obj-mtl-loader插件 ...

January 13, 2023 · 2 min · jiezi

关于webgl:汽车行业场景化营销新方向基于-WebGL-的网上虚拟车展

车展作为车市的风向标,代表着汽车倒退的趋势,也是厂商展现本人、推广本人的舞台。WebGL 作为一种新兴的技术,为 Web 端提供了交互式三维动画新体验,汽车之家的网上车展就是两者联合之后的一种新的产品状态。 到目前为止,汽车之家车展总共经验了五年的风雨倒退,历经三大阶段,它们别离是全景实拍车展、步进式全景实拍车展和纯 3D 虚构车展。 全景实拍车展:线下车展现场实拍全景图,以此为次要素材构建而成的线上车展。 步进式全景实拍车展:应用业余设施实地拍摄线下车展全景图,同时采集空间信息,实现全景场景的平滑切换,就像镜头在行走,这也是步进式这个名称在之家的由来。 纯 3D 虚构车展:在 Web 端实现了展馆和汽车模型的加载展现,真正实现了第一人称逛展看车,源于事实而又超过事实,保障性能的同时尽可能的出现高质量视觉效。 每一次的车展迭代更新都对业务有的极大的推动,而这背地都是 WebGL 技术一直成熟和生态的一直残缺所造就的。 一、WebGL 介绍1.1 WebGL 引擎现状WebGL 是近些年呈现的一种 3D 绘图协定,这种绘图技术通过减少 OpenGL ES 2.0 的一个 JavaScript 绑定,为 HTML5 Canvas 提供硬件 3D 减速渲染,这样 Web 开发人员就能够借助零碎显卡来在浏览器里运行 3D 利用。随着挪动互联网的崛起和倒退,Web 业务也随之拓展,有时须要在网页/ H5 /小程序中渲染 3D 模型,WebGL 就有了用武之地。 WebGL 引擎应用 JavaScript 语言编程,学习成本低,容易入门,公布部署不便灵便且无需装置下载,但国内没有比拟成熟的 WebGL 开发者生态,可应用的插件很少;因为是基于 Web 浏览器生态,能够十分不便的以外链形式对外进行社区裂变流传,同时引擎能力满足简略的 3D 需要,常见的 WebGL 引擎有 three.js、BabylonJS 和 PlayCanvas 等,它们都是最近几年才呈现,积攒绝对于传统的空幻(UE)或者 Unity 引擎要少很多。 1.2 WebGL 的长处跨平台:WebGL 是 H5 规范之一,具备对立的、规范的、跨平台的 OpenGL 接口实现,通过 JavaScript 脚本实现了 Web 端的 3D 利用制作,无需任何浏览器插件反对,是浏览器级别的一次开发多处利用; ...

November 24, 2022 · 2 min · jiezi

关于webgl:如何实现水面波动一种代码实现两种效果shader-奇技淫巧

在最初咱们将一起实现如下图水面稳定的成果,不过浏览完本文,也将理解到这一思路的其余用法本篇默认读者本曾经能读懂根本的glsl语言,语法不再做过多解释,如果有不了解能够翻看之前的文章有具体解说用shader制作马赛克这是上帝的杰作:10行代码搞定“热成像”几款2077格调的shader 如何扭曲图片如何扭曲图片?办法多种多样,但偏移是其中比拟罕用也极其便宜的形式。比方能够让小骑士左侧身材偏移20%: void main() { if(st.x>0.5 ){ st.y+=0.2; } vec4 color = texture2D (u_image0, st); gl_FragColor = vec4(color.rgb, 1.0);}st是一张(0.0)->(1,1)的二维立体,让其中右侧区域的像素点都读取其上方0.2地位处的像素信息,就实现了平移掉过,这种线性的平移咱们在几款2077格调的shader一文中曾经领教,但用它模仿扭曲是有点尴尬胖虎了。所以咱们给他来点非线形的偏移: #define PI 3.14//圆周率#define E 2.71828//自然对数的底.....float Distribution(float x){ float a = 1.0/(4.0*PI*PI); float b = pow(E,-(x-0.5)*(x-0.5)/0.04); return a*b;}void main() { st.y = mod(st.y+ 10.0*Distribution(st.x),1.0); // mod保障画面间断 vec4 color = texture2D (u_image0, st); gl_FragColor = vec4(color.rgb, 1.0);}这里咱们在x=0.5处增加了一个正太散布的偏移:由正太散布的性质能够晓得,越靠近x=0.5偏移量越大,越靠近1和0偏移越小。在一些须要实现非平面镜的场景中,这个shader能够联合鱼眼及其他扭曲一起实现一些鬼魅的成果。 意外的涂鸦成果如果叠加一个正太方程就能实现扭曲,那么叠加一个稳定方程能够实现怎么的成果?这里简略选取一个正弦波: void main() { vec2 offset = vec2(0.0); offset.x = sin(st.x * 14.0) * .2; offset.y = sin(st.y * 14.0) * .2; vec4 color = texture2D(u_image0, uv+offset); gl_FragColor = vec4(color.rgb, 1.0);}这里设置一个偏移量offset,因为须要在画面中呈现间断的波峰和波谷,须要将函数的频率进步,同时咱们不想让offset取值偏移到屏幕之外,所以管制了振幅,该选什么呢?就暂定0.2吧,而后:克苏鲁来临了。这是因为此时振幅还是过大,频率快能够在画面中呈现多层次扭曲,振幅小意味着每次的稳定是轻微的。能够做以下调整: ...

November 4, 2022 · 1 min · jiezi

关于webgl:开课吧高薪webGL工程师2022最新完结无密

download:开课吧-高薪webGL工程师2022最新完结无密WebGL是什么?WebGL百度百科给出的解释是WebGL是一个3D绘图协定,而维基百科给出的解释是一个JavaScript API。因为WebGL技术旨在帮忙咱们在不应用插件的状况下,在任何兼容的web浏览器中开发交互式2D和3D web成果,因而咱们能够将其了解为帮忙咱们开发3D网页的绘图技术。当然,最底层是JavaScript API。WebGL倒退历史WebGL的倒退能够追溯到2006年。WebGL源于Mozilla员工Vladimir Forge Sivic的一个Canvas 3D试验我的项目,Canvas 3D的原型于2006年首次展现。这项技术于2007年底在FireFox和Opera浏览器中实现。2009年初,Khronos团体联盟成立了WebGL。最后的工作成员包含苹果、谷歌、Mozilla、Opera等等。2011年3月,WebGL 1.0公布。WebGL 2的开发始于2013年,最终于2017年1月实现。火狐51、Chrome 56、Opera 43中首次反对WebGL 2。总帐中的基本概念WebGL运行在电脑的GPU上,所以须要应用能在GPU上运行的代码。这样的代码须要提供成对的办法,其中一个叫做顶点着色器,另一个叫做切片着色器,应用GLSL语言。连贯顶点着色器和切片着色器的办法称为着色程序。 顶点着色器着色器的作用是计算顶点的地位,即提供顶点在裁剪空间中的坐标值。 请参考webglfundamentals一文理解该模块的内容。 片段着色器切片着色器的性能是计算图形元素的色彩值。咱们能够粗略地将切片着色器了解为网页中的像素。数据采集模式后面咱们提到了顶点着色器和片段着色器的概念,顶点着色器和片段着色器都须要相应的数据能力运行。接下来,让咱们看看着色器获取数据的四种形式: 和属性缓冲。缓冲区是发送到GPU的二进制数据序列。通常,缓冲数据包含地位、方向、纹理坐标、顶点色彩值等。当然,你能够依据本人的须要存储任何你想要的数据。属性用于解释如何从缓冲区获取所需的数据,并将其提供给顶点着色器。全局变量全局变量在着色器运行之前调配,在运行过程中全局无效。全局变量在一个绘制过程中将雷同的值传递给着色器。静脉纹理是一个数据序列,在着色程序运行时能够随便读取其中的数据。一般来说,咱们次要以纹理模式存储图像数据,然而除了色彩数据之外,您还能够依据本人的爱好存储其余数据。可变变量变量是顶点着色器将值传递给片段着色器的一种形式。 总结WebGL只关怀两件事:裁剪空间的坐标值和色彩值。要应用WebGL,只须要给它提供这两样货色。所以咱们通过提供两个着色器来做这两件事,一个顶点着色器提供裁剪空间坐标值,另一个切片着色器提供色彩值。WebGL的工作原理理解了WebGL的一些基本概念后,咱们能够看看WebGL在GPU上做了什么。之前咱们曾经理解到,WebGL在GPU上的工作次要分为两局部,即顶点着色器所做的工作(将顶点转换为裁剪空间坐标)和片段着色器所做的工作(依据顶点着色器的计算结果绘制像素)。如果咱们须要画一个三角形,此时在GPU上做的工作就是先调用顶点着色器三次,计算三角形的三个顶点在裁剪空间坐标系中的对应地位,并通过变量gl_Position保留在GPU中,而后调用切片着色器计算每个顶点的色彩值,并通过变量gl_FragColor将对应的色彩值保留在GPU中。实现所有这些工作后,咱们失去了绘制三角形所需的像素,最终失去了栅格化的三角形。原生WebGL API绘制三角形咱们之前曾经学习了WebGL的历史、基本概念和工作原理,接下来是咱们实际真谛的时候了,那么咱们就来看看如何通过WebGL在一个网页中绘制一个简略的三角形。咱们晓得WebGL作为一种3D绘图技术,自身就是依附HTML5中的canvas元素而存在的,所以在正式开始绘图之前,咱们须要做一系列的筹备工作: 首先咱们须要创立一个canvas元素作为画三角形所需的画布,并实现浏览器对canvas元素兼容性的测试。函数webglInit () {const canvasEl = document . createelement(' canvas ');//画布元素创立canvasel . width = document . body . client width;//设置画布画布的宽度canvasel . height = document . body . client height;//设置画布canvas高度document . body . append(canvasEl);//将创立的canvas画布增加到页面的body元素下。//接下来须要判断浏览器对WebGL的兼容性。如果浏览器不反对WebGL,那么咱们就不须要持续了。如果(!canvasEl.getContext("webgl") &&!canvasel . get context(" experimental-web GL "){alert("您的浏览器不反对web GL ");返回;}//如果浏览器反对Webgl,那么咱们将获取Webgl的context对象,并将其复制到变量GL中。const context =(canvasel . get context(" web GL "))?canvasEl.getContext("webgl "):get context(" experimental-web GL ");/*设置viewport context.viewport (x,y,width,height);x:用于设置视口左下角的程度坐标。默认值:0y:用于设置视口左下角的垂直坐标。默认值:0宽度:用于设置视口的宽度。默认值:画布宽度高度:用于设置视口的高度。默认值:画布的高度当您第一次创立WebGL上下文时,视口大小与画布大小相匹配。然而,如果再次更改canvas的大小,须要通知WebGL context设置一个新的viewport,所以此处能够省略这行代码,作为第一次创立它。*/context.viewport(0,0,context.canvas.width,context . canvas . height);返回上下文;}复制代码 ...

September 13, 2022 · 1 min · jiezi

关于webgl:开课吧-高薪webGL工程师2022最新含源码ppt

download:开课吧 高薪webGL工程师2022最新含源码ppt编程猿自学it java python go c基于springboot的国际化解决方案1底漆1.1国际化概述作为一名服务器端开发人员,这里我想从本人的角度简略概述一下国际化(国际化也叫i18n,因为I和N之间有18个字母)是做什么的: 在理论的国际化我的项目中,责任是让服务器依据客户指定的语言,返回相应语言的内容。 1.2 spring/spring boot我的项目中国际化游戏性概述在spring/springboot的世界中,国内游戏基于以下界面:org . spring framework . context . message source .该接口次要定义了以下三种办法:公共接口音讯源{//如果国际化配置文件中找不到var1对应的音讯,能够给它一个默认值var3。@NullableString getMessage(String var1,@Nullable Object[] var2,@Nullable String var3,Locale var 4);//如果在国际化配置文件中找不到var1对应的音讯,抛出异样String getMessage(String var1,@Nullable Object[] var2,Locale var3)抛出NoSuchMessageException//这个办法临时没有做太多的钻研,所以本文也不会波及。string getMessage(MessageSourceResolvable var 1,Locale var2)抛出NoSuchMessageException}复制代码该接口的三个重要实现类如下: ResourceBundleMessageSourceReloadableResourceBundleMessageSource动态音讯源 2如何播放ResourceBundleMessage源(默认)首先咱们来看看springboot我的项目中国际化最根本的玩法: (1)构建springboot我的项目(至多增加web依赖)。Messages.properties(默认配置) 用户名=溜溜球复制代码 messages_en_US .属性 user.name=yoyo-ENuser.name1=nrscuser.name2=nrsc{0}-{1}复制代码 音讯_zh_CN.properties 用户名1=张耳User.name2=张耳{0}-{1}复制代码(3)创立一个控制器测试类@RestController公共类i18d专制控制器{@主动连线公有MessageSource messageSource @GetMapping("/hello ")公共字符串hello() {String defaultM = messageSource。getMessage("user.name ",null,locale context holder . getlocale());字符串message1 =音讯源。getMessage("user.name1 ",null,locale context holder . getlocale());字符串message2 = messageSource。getMessage("user.name2 ",new String[]{"WW "," MM"},locale context holder . getlocale());字符串message3 = messageSource。getMessage("user.nameXX ",null," defaultName ",locale context holder . getlocale()); ...

September 12, 2022 · 3 min · jiezi

关于webgl:开课吧高薪webGL工程师2022最新完结无密

download:开课吧高薪webGL工程师2022最新完结无密文章参考 自学it666 https://www.zxit666.com/形容你在玩一个蕴含多个角色的游戏,每个角色都有两个次要属性:攻打和进攻。给你一个2D整数数组属性,其中properties[i] = [attacki,defensei]代表游戏中第I个角色的属性。如果一个角色的攻打和进攻等级都比这个角色的攻打和进攻等级高很多,那么这个角色就被认为是弱的。更正式地说,如果存在另一个字符j,其中attackj > attacki和defensej > defensei,则称字符I是弱的。返回弱字符的数量。示例1:输出:属性= [[5,5],[6,3],[3,6]]输入:0阐明:没有一个角色比另一个角色有更强的攻击力和防御力。 复制代码示例2:输出:属性= [[2,2],[3,3]]输入:1阐明:第一个角色很弱,因为第二个角色有更强的攻击力和防御力。复制代码留神:2属性[i]。长度== 2一个复制代码剖析依据题目,咱们正在玩一个有多个角色的游戏,每个角色有两个次要属性:攻打和进攻。给定一个二维整数数组属性,其中properties [i] = [attack,defense]代表第I个字符的属性。如果另一个角色的攻打和防具等级严格高于该角色的攻打和防具等级,则该角色被称为弱角色。更艰深地说,如果有另一个字符索引J,其中attackj > attacki,defence j > defence i,则索引为I的字符称为弱字符。返回弱角色的数量。其实这个问题是对于排序的,AC一次遍历就能够实现。咱们晓得问题须要弱字符的个数,所以咱们只须要数一数。我要判断的维度有两个维度:攻击力和防御力,所以能够依照物理学中“控制变量法”的思路,先判断一个维度,再判断另一个维度。要达到这个“严格大于”的要求,首先要排序。咱们依照攻击力降序和防御力升序排序,这样在确定弱角色攻击力满足“严格小于”时,就能够通过判断弱角色防御力“严格小于”来找到弱角色。而后咱们遍历属性。如果以后角色的攻击力小于之前穿梭过的角色,就不能判断是弱角色。因为防御力是不确定的,所以为了不便,咱们要用一个变量max_defense来记录被遍历角色的最大防御力,所以如果以后角色的防御力严格小于max_defense,就阐明肯定是真角色,后果能够加一。理论须要判断攻击力雷同但防御力严格小于前一个角色的状况。然而咱们的写法能够省去这个判断步骤,因为咱们是按防御力升序排序的,这样如果以后防御力严格小于之前的最大防御力,那么攻击力肯定是不相等的,因为攻击力相等时防御力是按升序排序的。依照下面的办法,一次遍历后失去的后果就是最终后果,能够返回。工夫复杂度是O(logN+N),N是属性的长度,因为属性要先排序再遍历,空间复杂度是O(1),因为没有开拓新的空间。解释类别解决方案(对象):def numberOfWeakCharacters(本身,属性):""":类型属性:List[List[int]]:rtype: int"""properties . sort(key = lambda x:(-x[0],x[1]))后果=最大进攻= 0对于_,属性中的进攻:如果进攻<最大进攻:后果+= 1否则:max_defense =进攻回送后果复制代码运行后果运行工夫:2301 ms,比游戏中弱角色数量的Python在线提交快84.75%。内存应用:69.6 MB,不到Python在线提交的游戏中弱角色数量的42.37%。复制代码

September 10, 2022 · 1 min · jiezi

关于webgl:干货为什么说开源基金会的选择很关键上

数字经济时代,人口和流量红利逐步隐没,须要咱们开拓新的生态模式,这所有都源于技术的推动。开源是一种凋谢、共享、协同的翻新合作模式。它不仅是凋谢源代码的软件技术开发,还包含更宽泛的凋谢技术畛域及协同翻新的理念与机制。开源正在以“自在”的流传模式,成为寰球信息技术倒退的弱小推动力。开源根底软件当初曾经成为咱们国家的倒退策略。 而开源的倒退离不开开源基金会,如Apache基金会、Linux基金会、以及中国的凋谢原子开源基金会,都曾经成为开源生态中十分要害的局部。目前寰球100多个开源我的项目基金会绝大部分注册在美国,这些开源基金会中,曾经孵化了800多个顶级我的项目。中国在2020年成立的凋谢原子开源基金会,随着百度、华为、腾讯、阿⾥等企业的项⽬捐献,基⾦会以后已有OpenHarmony 、OpenEuler等开源项⽬。目前Gitee的用户数量600万,但只有Github的十分之一,中国开源基础设施和产业⽣态也在逐渐倒退。97%的开发者和99%的企业都在应用开源软件,从整个软件产业的供应链上看,开源曾经成为将来信息技术的主战场了。 那么,意识理解基金会的运行规定以及如何抉择退出适合的基金会,是企业开源生态倒退十分要害一环,对此咱们整体做了梳理,会分两期内容分享给大家~ 开源基金会简介开源基金会(Open Source Foundation) 是专门为反对开源软件我的项目而创立的非营利性组织,开源基金会遵循公开、通明、凋谢等理念,为开源软件的孵化提供技术、经营、法律等全方位反对和服务,为开源社区建设和经营提供领导,是开源软件成长倒退的孵化器和加速器。 开源基金会目前已成为开源生态最重要的组织者,作为非营利组织,基金会的运行次要依赖志愿者,孵化的软件我的项目次要依附来自不同地区、不同组织的开发者协同单干。基金会遵循凋谢共享机制,激励企业、开发者、志愿者等独特参加开源,在齐全中立的合作平台做出奉献,从而实现开源生态发展壮大。材料来源于网络 开源基金会的运行模式目前国内上已有几十家权威开源基金会在寰球的开源生态中施展着重要作用,如1985年建设的自由软件基金会、1999年创立的Apache基金会、2007年成立的 Linux基金会等。他们致力于技术创新倒退、为行业利用提供解决方案,大力推广开源文化,既有综合性基金会,也有专一于某个畛域的,同时还有基于单个开源我的项目成立的基金会。从运作模式上次要分为三大类: 01 独特决策模式基金会由整个社区独特决策,回绝“独裁”,如果出现分歧就以投票形式做出决策。Apache基金会是最典型的案例。他们提倡“扁平化”运作,激励社区成员发表意见,我的项目决策由社区所有成员探讨决定。当须要协调时,我的项目会以一种懈怠的共识形式做出决策:几票赞成票,没有反对票就能够开始了,这确实是一个衰弱社区的十分重要的标记。但在波及我的项目策略倒退或法律立场时,必须以投票形式决策,此时仅我的项目提交者和项目管理委员会成员具备投票权。基金会不会强制执行流程,灵活性是他们认为可继续开源成功之路不可或缺的一部分。材料来源于网络 02 “善良的独裁者”(BDL)模式该模式主张我的项目决策者对我的项目整个生命周期放弃相对管制,负责确定我的项目方向,如果出现分歧时来做出最终决策。Linux基金会采纳该模式,我的项目负责人称为“善良的独裁者”(BDL),具备最终决策权,负责制订战略方针、率领我的项目倒退。当社区呈现质疑我的项目提交者的决定时,我的项目负责人可通过查看电子邮件存档来复审其决定,来反对或颠覆决定。这种模式不须要正式抵触解决程序,由我的项目负责人来最终决策。这里假如社区可能是无序的,当存在相互竞争的议程或观点时,可能导致重要问题得不到解决。BDL 模式防止了这些问题,因为善良的独裁者领有最终决定权,能够通过单边决策来解决抵触。材料来源于网络 03 公司主导模式公司主导的我的项目由软件公司管制和赞助,通常是为了减速开发并确保与客户需要保持一致。在这样的设置中,公司对开发的控制权比基金会在社区主导的致力中的控制权更大,但治理依然植根于社区。 只管每个基金会都有不同的价值观和模式,也有着不尽相同的倒退路线。然而他们独特的主旨是统一的,就是为开源提供法律、经营、市场、技术等全方位反对,为社区建设和经营提供领导。 为什么要退出开源基金会在国内开源基金会中,中国成员较2021年同期数量增长26%,达到125家;在云原生计算基金会中,超过20%的我的项目来自中国;在Apache软件基金会中,源自中国的沉闷开源我的项目共24个,其中有14个我的项目成为了顶级我的项目,2021年仅有5个进入Apache软件基金会孵化器的新我的项目全副来自中国;在Linux基金会中,现有中国会员139家……同时 ,2020年中国首个开源基金会“凋谢原子开源基金会”成立,基金会的成立是一次翻新实际,也是中国开源社区倒退的重要里程碑。因而,抉择退出适合且优良的开源基金会显得尤为重要,对于开源我的项目的倒退起着重要的推动作用。材料来源于网络 01 法律和知识产权反对开源基金会能够提供法律和知识产权反对。基金会能够提供一个软件知识产权治理的法律框架,在这个框架中,商业公司能够和自在/开源软件我的项目的贡献者谐和地在一起工作。以Apache基金会为例,其通过制订软件版本公布等规定,明确软件版本由基金会下设的项目管理委员会公布,以此躲避我的项目贡献者的法律危险,为开发人员提供法律爱护。同时,Apache基金会的法律顾问无偿帮忙开源软件社区解决无关许可证兼容和知识产权方面的政策问题。 02 我的项目孵化和营销推广基金会专门设立孵化器负责新我的项目顺利创立。以Apache基金会为例,其孵化器职责包含:筛选无关创立新我的项目或子项目的意向书;帮忙创立我的项目及其所需的基础设施;监督和领导孵化我的项目社区;评估孵化我的项目的成熟度,将其晋升为正式我的项目/子项目,或者在失败时进行孵化。 基金会能够应用通过验证的营销形式吸引开发人员社区和企业,从概念到执行,推广开源我的项目品牌。如Linux基金会借助大型会议、研讨会、沙龙、新媒体等路径推广、宣传旗下开源我的项目,吸引开发者和用户退出。 03 提供业余技术领导基金会设立技术领导与协调组织,帮忙开源我的项目把关技术方向和品质。如,Apache基金会的项目管理委员会(PMC)、CNCF的技术监督委员会(TOC)等。Linux 基金会会指定一位技术顾问委员会负责导师,通过电子邮件领导和帮忙开发人员。 04 基础设施反对基金会提供了一套根底服务,来满足开源我的项目在不同阶段的需要。基金会对于软件治理有一套本人的实际,这对于开发者、用户以及软件的发行,都是十分重要的。Apache基金会为旗下开源我的项目提供包含邮件列表、网站、Gitbox代码托管服务、问题跟踪器以及一系列构建和部署工具等基础设施服务。Linux基金会提供的基础设施服务包含:源代码治理、代码审查、问题追踪、通信基础设施等。 开源我的项目想要取得更快更久远的倒退,就须要较为正式的治理和法律框架。开源基金会提供了一种简略、协同的机制,通过这种机制,企业、集体等组织能够为开源软件做出奉献,基金会通过提供一个齐全中立的合作空间,让所有人参加开发我的项目,同时升高法律危险,为开发人员和开源我的项目提供了一个平安的避风港。**下期咱们对一些优良的开源基金会进行介绍,欢送大家继续关注~** Orillusion致力于打造全世界第一款齐全开源基于WebGPU规范的一种轻量级渲染引擎,指标是在浏览器中实现桌面级的渲染成果,反对超大简单场景的3D出现。易上手,易分享,易迭代,易合作、成本低,跨平台是咱们的外围劣势,咱们将为3D场景暴发时代提供引擎根底工具。 将来咱们将会继续把最干货最前沿的WebGPU技术分享给每一位社区成员,也欢送大家为Orillusion开源社区做出本人的奉献。咱们始终深信,开源社区的技术留痕是每一位技术人员最高尚的谋求!因而,咱们尊重,咱们认可,咱们更期待,退出Orillusion,让咱们共同进步! ——Link uncharted, 链接将来世界

August 25, 2022 · 1 min · jiezi

关于webgl:webglthreejs生成房间楼层

楔子在很多数字孪生我的项目中,都会波及到楼层的建模。楼层的建模因为构造繁多,如果都是建模师进行手动建模,工作量会比拟大。而楼层自身的构造,能够形象成能够通过门路结构的对象(这和之前的文章提及的的管路以及路线相似),这不便咱们通过代码的形式来生成房间楼层。 墙体几何对象PathCubeGeometry楼层个别分成墙体和地板两个局部,首先来看下墙体对象。 以threejs为根底,扩大一个几何对象PathCubeGeometry。 该对象通过一个Path3D门路来结构一个墙的几何体,该几何体能够分成start,end,top,bottom,outside,inside等几个外表分组,这样就不便给内外表和外外表,以及顶面等设置不同的材质贴图的成果。 次要代码如下: this.vert = vert; this.rawUV = rawUV; this.pathU = pathU; this.indices = [] this.vertices = []; this.uvs = [], this.normals = []; this.generateSideWall(vert,inner,outer,innerTop, outerTop, true,closed); this.generateSideWall(vert,inner,outer,innerTop, outerTop, false,closed); this.generateTopBottom(vert,inner,outer,innerTop,outerTop,true,closed); this.generateTopBottom(vert,inner,outer,innerTop,outerTop,false,closed); if(!closed) { this.generateAZSide(vert,inner,outer,innerTop,outerTop,true); this.generateAZSide(vert,inner,outer,innerTop,outerTop,false); }通过PathCubeGeometry,咱们能够不便的构建墙体,比方如下示例代码: const materials = [m1, m2, m3, m3, m3, m3];const points = json.outerWall.path;const p0 = points[0];const path = new Path3D();const scale = 20;path.moveTo(p0.x * scale, p0.y * scale, p0.z * scale);for (let i = 1; i < points.length; i++) {let p = points[i]; path.lineTo(p.x * scale, p.y * scale, p.z * scale);}path.closePath();cosnt patCube = new PathCubeGeometry(path, 10, 50, 32, 500);const mesh = new dt.Mesh(patCube, materials);首先通过Path3D结构一个门路(门路来源于传入的一系列点points),而后通过门路结构一个结合体对象PathCubeGeometry,最初生成实体,成果如下图所示: ...

August 20, 2022 · 2 min · jiezi

关于webgl:干货语义网Web30Web3元宇宙这些概念还傻傻分不清楚下

上一期内容,咱们理解到海内外Web3.0或者说元宇宙的倒退方向不尽相同,其次要起因是两者所处的经济周期和经济构造不同造成的。 海内因为海内的Web3.0倒退都是和金融挂钩,根本都是要发行Token,而后再建设本人的生态。Token在海内是能够间接跟法币进行兑换的,这样就会在短时间内吸引大量的创业者和资本的投入。这种状况的劣势就是从分布式的底层协定到下层利用都会疾速呈现大量的新我的项目,其中肯定不乏一些好我的项目,比方力争取代HTTP的IPFS协定等;但同时带来的问题,就是这种通过Token疾速积攒大量资本的守业模式,对开创团队的“定力”要求更高,毕竟通过发行Token,曾经帮忙创业者换来了十分雄厚的资金,那真正再去投入全情搞研发的初衷和能源就须要更动摇的信念来反对。有点儿像SPAC上市,制度的发明者和初衷肯定是好的,能够帮忙科技企业借助资本疾速推出产品,实现市场验证。然而,稍有不慎,很可能又调演变成已经的“ICO庞氏”。 https://blog.liquid.com/coin-... 咱们置信将来肯定有Web3.0的Facebook和微信,但大概率不是有了Web3.0,就能做成Web3.0的Facebook和微信。其实Facebook和微信能胜利不是有了区块链,而是因为它们自身就是好的利用。利用自身比底层技术是不是区块链更重要。最早提出元宇宙概念的Roblox,没有区块链的加持,然而切中了青少年用户群体,同样获得了胜利。当然,将来底层技术可能都调演变成分布式的网络架构,开发的利用自然而然就是分布式的,也就不会有人再强调本人是基于区块链的某某利用了,拼的必然还是利用自身。 https://moralis.io/decentrali... 国内中国的Web3.0显著比海内要“激进”很多。当下中国没有齐全分布式的底层公链,而是次要在倒退联盟链,包含阿里的蚂蚁链,腾讯的至信链,还有政府主导的长安链等。 联盟链绝对于公链最大的区别就是,它并不是齐全的去中心化,而是半中心化,由一些绝对大的节点(不同的组织)独特参加治理。因为联盟链不须要全副参加节点进行信息确认,所以它的劣势非常明显,比方交易速度快,交易成本低,数据隐衷不会默认公开,可控性强等。因为联盟链不是真正的分布式架构,还是有绝对的中心化概念的,在寰球的Web3.0大背景下天然也存在很多的争议。 https://www.foley.com/en/insi... 咱们认为“不可能三角”带来的问题,目前还是没有方法彻底解决的,更多的是衡量和折中。比方最出名的比特币网络,如果用它来进行商品交易,所有的节点来确认可能须要几个小时的工夫,那这种利用场景显然是没有方法被满足的。而且自然界中如同也的确没有齐全的分布式构造,因为齐全的分布式带来的很可能就是效率的低下。 以后的互联网存在三个比较严重的问题:虚伪信息、隐衷泄露、利用孤岛。人们都寄希望于Web3.0能够真正解决上述问题,而且从技术角度剖析,确实是可行的。通过区块链和智能合约能够很好的实现数据确实权和流程自动化,无效解决虚伪信息问题;通过Token能够让用户本身真正领有本人的数字资产,无效爱护个人隐私;而最初的利用孤岛,指的是各种国内外的超级APP利用。本来凋谢的互联网逐步被一些科技巨头的几款利用所垄断,而Web3.0的分布式特点也是寻求冲破的外围所在。在这种状况下,中国依据本身的倒退状况,确定了属于咱们本人的Web3.0倒退路线。主推联盟链,通过Token和智能合约,解决很多咱们当下互联网构造所面临的问题,比方用户的隐衷,商业合同推动,供应链溯源,图片和视频数字资产确实权,大宗交易的合规等等。 总结,这两种模式并没有优劣好坏之分,都是基于不同的倒退阶段天然演进的。 引擎不管从技术角度,还是商业角度,甚至是人类常识状态演进的角度,元宇宙必然到来。然而,至多须要5-10年的倒退,才有可能实现。在这个漫长的过程中,引擎处于一个十分重要的环节。它是虚拟世界和真实世界的桥梁,是数字内容的外围生产力工具,通过引擎能够更加简略高效的发明虚构的数字世界。在之前的内容里,咱们介绍过很多对于渲染的概念,比方离线渲染,实时渲染,云渲染,混合渲染。这是因为渲染是引擎中很重要的一部分。因为将来的元宇宙肯定是能够实时交互的,所以渲染能力次要还是依附实时渲染和混合渲染相干的技术来撑持。除了渲染,引擎中还蕴含很多其余的技术能力,比方资源、物理、动画、声音、粒子、天气、地形,还有更底层的平台适配和更下层的工具链等等。其实这些能力最早能齐全体现的就是游戏引擎,比方Unity和Unreal。随着元宇宙概念的呈现,传统的游戏引擎也在缓缓的去游戏化,把很多游戏技术利用到其余的行业当中,缓缓的从游戏引擎变成了互动引擎或者说是交互式内容创作引擎。 https://www.gamearter.com/blo... 咱们认为元宇宙肯定不是一个或者几个独立的超级APP来构建的,而应该像World Wide Web (3W)凋谢网络一样,通过对立的规范,保障咱们随时随地接入元宇宙,并且在任何设施上都能够取得雷同的体验,这也是咱们特地关注Web生态的起因。人们通过浏览器,接入一个凋谢的世界,能够实现不同利用的自在切换,并且无效突破商店“抽成”的围墙。 https://thenextweb.com/news/w... 咱们在之前的文章中提过,从技术路线的角度来看,以后的引擎分为Native客户端引擎和Web浏览器引擎。他们的比照剖析,咱们已经也有过具体的介绍:Web or Native 谁才是元宇宙的将来(上)?Web or Native 哪个才是元宇宙的将来(下)? 简略说,Native引擎能够更容易施展出硬件的极致性能,实现高质量的3A级数字内容;而Web引擎,能够更低成本的疾速实现内容创作,同时借助浏览器的跨平台人造劣势,实现内容的疾速散发裂变。家喻户晓,Web生态中的3D内容以后依附的是WebGL规范,而且曾经造成了丰盛的Web3D内容生态。然而随着最新规范WebGPU的推出,才是真正能使得浏览器迫近Native原生图形API成果的最终解决办法。将来构建元宇宙简单空间,内容开发和创作是十分外围的一环,也是引擎的次要工作。 像Metamask这样的钱包利用曾经在浏览器中实现了用户的身份确认问题,而元宇宙场景中的3D内容创作怎么在浏览器中疾速高质量的实现,便是要解决的下一个问题。在WebGPU规范行将公布之时,Orillusion正在致力抓住机遇,力争做一款国人本人的跨时代的Web3D引擎。 Orillusion致力于打造全世界第一款齐全开源基于WebGPU规范的一种轻量级渲染引擎,指标是在浏览器中实现桌面级的渲染成果,反对超大简单场景的3D出现。易上手,易分享,易迭代,易合作、成本低,跨平台是咱们的外围劣势,咱们将为3D场景暴发时代提供引擎根底工具。 将来咱们将会继续把最干货最前沿的WebGPU技术分享给每一位社区成员,也欢送大家为Orillusion开源社区做出本人的奉献。咱们始终深信,开源社区的技术留痕是每一位技术人员最高尚的谋求!因而,咱们尊重,咱们认可,咱们更期待,退出Orillusion,让咱们共同进步! ——Link uncharted, 链接将来世界

August 17, 2022 · 1 min · jiezi

关于webgl:WebGPU-导入1-入门常见问题与个人分享

1. 常见问题① WebGL、ThreeJS 会淘汰吗?WebGL 是不是过期了?WebGPU 性能是不是比 WebGL 强?ThreeJS 会淘汰吗?ThreeJS、BabylonJS 两个利用级 3D 库始终在设计 WebGPU 为底层的新渲染器,后者尤为踊跃。 什么是利用级?这两个库都屏蔽了 WebGL 的底层调用细节,大多数时候,你编写的代码更专一于“场景”中的“三维物体”的地位,加载“模型文件”,高级点的会写丑陋的“材质”。这些带引号的概念,在 WebGL 底层接口都是没有的,是实时渲染技术的一些概念在 JavaScript 中的实现。 如果你是利用级别的开发者,不关怀这些库如何封装底层图形 API,那当初大可不必入场,等正式公布再理解也不迟,毕竟这两个受众这么广的库,为了放弃稳定性,在下层封装上不会短时间内进行激进改变。 WebGL 会淘汰吗?WebGL 是不是过期了?短时间内不会,至多 10 年内。不信你看看 Java8?(开个玩笑) WebGL 1.0 公布至今已有 10 年,齐全落实 WebGL 2.0 的实现也才一年(截至发文),有很多作品、程序仍旧在用基于 WebGL 技术族运行着,发光发热。借用马克思主义哲学的一个原理,是说“旧事物在它的作用没有齐全施展进去之前是绝不会自行沦亡的,旧事物当中除了消极因素,仍然存在积极因素”。 WebGL 的“全局状态”机制继承自 OpenGL,有大量的材料可供查问,大量的案例仍存于互联网中。 WebGPU 的性能是不是比 WebGL 强?有一部分人被 WebGPU 吸引过去的起因是“跨平台 + 性能”这对组合。 诚然,WebGL 比照 Canvas2D 绘图技术的性能,是间接碾压的,在这十年中给 Web 前端带来了高性能的可能,然而长时间的实际下来,有的人发现了“WebGL 程序有时候,并不是那么快,甚至卡的要死”,他们 兴许不会深究底层起因,间接寄希望于一个他们认为的新的王:WebGPU,这其实是一种很不唯心主义的、十分唯心主观的认识。 WebGPU 从设计上来看,的确有不少特色是优于 WebGL 的,列举如下: PromiseAPI / async + await 的异步语法退出:对网络数据加载、解码等同步耗时操作有了更好的反对,肯定水平上防止了回调天堂问题面向对象的 API 设计:绝对于 WebGL API 的过程式调用,选项式图形 API 的应用更迷信不再应用全局状态机制:应用指令缓冲节约了 CPU 到 GPU 之间的信息传递老本,要晓得 WebGL 切换某个状态(例如纹理、着色器程序),就是在切换全局状态对象,这个在宏观上是比拟耗时的,而 60 帧速率的每一帧只有 16.67 毫秒左右,能在宏观节约每一点每一滴的工夫,累计起来就不少了原生反对计算管线、计算着色器:WebGL 磨磨蹭蹭实现的 GPU 并行计算性能间接提供进去,并与渲染管线位置对等然而这些仅仅是在底层的对话根底上比 WebGL 强,这些底层解决的问题,不肯定是有的敌人的“性能问题”。譬如,在应用层、代码逻辑层可能会有这些起因导致了“性能问题”: ...

August 1, 2022 · 2 min · jiezi

关于webgl:WebGL之快速入门

这里,咱们将向大家演示WebGL的一些奢侈阐明和根本应用,即便你后续应用第三方3D渲染引擎进行绘制,这里的基本概念仍旧是十分无益的,或者说是必要的。 在演示和阐明的时候,咱们抉择基于image3D.js来作为依赖库,但因为其奢侈的语法简直和原生WebGL是统一的,因而咱们认为这不是一个蹩脚的抉择(如果间接应用原生,代码会变得过于冗余,不好阐明)。 在后续的阐明中,你都无需查阅别的文档,当然,如果你想晓得的更具体,也能够间接拜访image3D.js 文档进行查阅。 绘制流程个别最通用的绘制流程大抵如下: 筹备好着色器→数据写入缓冲区并实现调配→调用绘制办法进行绘制 不晓得你是否能够了解下面每个步骤是在干什么,咱们上面将通过一个逐步丰盛的例子来进行解释。 你能够提前看看咱们最终要绘制的成果: 这是二元函数 : y=x2+ z2的图像。 你能够点击此处进行查看运行成果。 着色器绘制的第一步,就是筹备好两个着色器:顶点着色器和片段着色器。前者用于形容绘制的图形的点的地位,后者用于形容每个点的色彩。 可能这样说你会无奈了解,其实简略的说就是:咱们在绘图的时候,会一次性的把数据都传递给GPU,传递给GPU的数据须要一些"整顿"后再应用,而着色器就是驻留在GPU上的这段"整顿程序"。 咱们传递的数据是什么?不就是点的地位和点的色彩吗。所以,着色器就分为了顶点着色器和片段着色器(有时候也叫片元着色器),前者解决点,后者解决色彩。 所以,让咱们先看看这里的顶点着色器的具体代码: attribute vec4 a_position;attribute vec4 a_color;varying vec4 v_color;void main(){ gl_Position = a_position; v_color = a_color;}内置变量gl_Position就是绘图最终接管的点的数据,而咱们定义的变量a_position好比一个管道,咱们后续能够给这个变量赋值,也就间接的给gl_Position赋值了(也就是点的地位)。 那v_color是什么?你能够了解,绘图的时候,是以点为主的,每个点的色彩,须要借助点的地位来设置,而v_color就是地位到色彩的桥梁。还是间接看看片段着色器: precision mediump float;varying vec4 v_color;void main(){ gl_FragColor=v_color;}同样的存在一个内置变量,这里叫gl_FragColor, 其接管了来自顶点着色器的v_color。 我不晓得你是否了解了下面的行为,不过你可能也感觉到了,点的地位和色彩如何解决曾经筹备好了,后续咱们只须要借助a_position和a_color就能够设置数据了。 3D对象在传递数据前,咱们先就基于这两个着色器创立3D对象,这一步非常简单,间接看代码: var image3d = new image3D(document.getElementsByTagName('canvas')[0], { "vertex-shader": vsCode, "fragment-shader": fsCode, depth: true});后续的所有操作,包含传递绘制和绘制等,间接调用这个对象上的接口就能够了。 特地阐明:vsCode和fsCode就是下面两个着色器的代码,是字符串。 传递数据终于,能够给GPU传递数据了,所以,咱们先来筹备好数据: var points = [];/** 具体的写法你能够间接看最终的代码,获取的思路大略就是: 三个点拼接成一个三角形, 每个点由6个数据组成,前3个示意点的地位,后3个示意点的色彩, 而一个个三角形拼接成最终的图形。*/// 因而,点的个数就是var num = points.length / 6;这些三角形如何确定的?对xoz面,范畴是-1 ~ 1,切割成一个个正方形,而后斜切一下就能够了: ...

July 24, 2022 · 3 min · jiezi

关于webgl:干货语义网Web30Web3元宇宙这些概念还傻傻分不清楚上

随着元宇宙概念的爆火,也同样呈现了很多新的科技词汇。有些词汇的确容易造成人们的误会,甚至曾经造成了习惯性的混用,然而咱们有必要绝对谨严地对一些词汇的前因后果做一些梳理。在某些状况下,会帮忙大家做一些更精确的判断和了解。 元宇宙这个概念很大,目前也没有定论,先把这个概念放到前面探讨。首先,咱们聊聊语义网、Web3.0和Web3。 语义网语义网,是W3C组织对于WWW万维网的扩大。这个概念,在1999年就被万维网的创始人Berners-Lee提出: I have a dream for the Web [in which computers] become capable of analyzing all the data on the Web – the content, links, and transactions between people and computers. A "Semantic Web", which makes this possible, has yet to emerge, but when it does, the day-to-day mechanisms of trade, bureaucracy and our daily lives will be handled by machines talking to machines. The "intelligent agents" people have touted for ages will finally materialize. ...

July 11, 2022 · 1 min · jiezi

关于webgl:开源生态超实用开源License基础知识扫盲帖下

上一期咱们介绍了对于开源License的一些基本知识。尽管开源License的总体数量很多,然而罕用的License还是很无限的。明天咱们就更间接地理解下罕用License具体的含意和区别。通过这篇文章,首先大家能够对罕用License有一个根本的意识,同时能够让咱们更加“平安”的援用其余的开源我的项目,最初如果咱们本人须要主导开发开源我的项目,也能够更有针对性的抉择适宜本人的开源License。 ·MIT·MIT,源自麻省理工学院(Massachusetts Institute of Technology, MIT),又称X11协定。这是目前最为宽松,限度起码的开源协定。应用MIT License作为开源我的项目的作者,惟一诉求就是保留版权,而不在有其余任何限度。 在应用MIT License的开源我的项目时,只须要记住一点:批改后的代码或者发行包,无论是以源代码模式还是二进制模式,必须要蕴含原作者的许可信息。 *代表开源我的项目:React, Vue, Angular, JQuery, Node.js, Three.js, Lua, .Net Core, Ruby on Rails等 实用商业软件* 材料来源于网络 ·BSD·BSD,伯克利软件发行版(Berkeley Software Distribution)。也是相当宽松的开源协定,能够自在地应用,批改源代码,也能够将批改后的代码抉择持续开源或者闭源。然而咱们要晓得的是BSD自身并不是一个协定,它是由五个协定组成:0-Clause、1-Clause、2-Clause、 3-Clause, and 4-Clause。后面的数字代表限度条款的数目,比方4-Clause就是BSD四条款版。第一个版本的BSD指的就是4-Clause,目前4-Clause和1-Clause都曾经不怎么再应用了。而0-Clause也倒退成为了公共畛域协定(Public Domain License),连作者信息都不要求保留。目前最风行的BSD指的是BSD 3-Clause License(BSD-3-Clause),也叫做BSD 3-Clause New/Revised License。所以咱们这里次要介绍下BSD-3-Clause在产生派生我的项目时,须要留神的状况: 如果是开源我的项目派生,源代码中必须蕴含原我的项目中的BSD协定。 如果是闭源我的项目派生,比方二进制类库或者商业软件,在软件的文档和版权申明中要蕴含原我的项目中的BSD协定。 不管开源或闭源我的项目派生,不能够用BSD我的项目作者、机构或我的项目产品的名称做市场推广。 与此同时,BSD 2-Clause License(BSD-2-Clause)也比拟罕用,它也被称为BSD 2-Clause Simplified/FreeBSD License。咱们须要晓得的是BSD-2-Clause和BSD-3-Clause的最大区别就是,它疏忽了下面的第三条“不许炒作打广告”的条款限度。 *代表开源我的项目:Dart, Django, FreeBSD, Flask, Go, Nginx等 实用商业软件* 材料来源于网络 ·Apache-2.0·Apache-2.0,是一个由Apache软件基金会公布的自由软件许可证(Apache License Vesion 2.0),最新版本为 “Version 2”。它和BSD相似,也是比拟宽松的开源协定,容许用户批改和再公布。然而,公布派生我的项目时须要留神: 如果批改了源代码,须要在被批改的文件中加以阐明。 派生我的项目中,须要带有原我的项目代码中的Apache-2.0协定,同时还包含商标,专利申明以及其余原作者规定须要蕴含的阐明。 派生我的项目中,如果蕴含Notice文件,则在Notice文件中也须要带有Apache-2.0协定。 *代表开源我的项目:Android, Apache HTTP Server, Hadoop, Spark, Babylon.js, LLVM, Log4j等 ...

June 20, 2022 · 1 min · jiezi

关于webgl:开源生态打造活力开源社区共建开源新生态

“在开源还只是一个小众群体的业余爱好时,简直做任何事件,都是自在的。然而,在软件吞噬世界、开源吞噬软件的明天,开源技术,曾经成为整个世界的基础设施之一。 ——《2021年中国开源年度报告》” 先通知大家一个好消息,咱们马上就要开源啦,期待一下哦~ 在开源前,小鸥会用一点点工夫跟大家聊聊对于开源、对于社区的事儿。咱们刚刚开始长大,期待与大家共建一个有价值、有生机、有温度的开源社区! 开源和闭源开源(Open Source)即凋谢源代码,是源代码凋谢共享的开发模式,具备自在凋谢、共建共享的特点。它是一种依靠互联网平台,通过大量群体智慧独特参加合作,一直积攒,实现继续翻新的形式。 “Open Source”的概念出自Debian的社长Bruce Perens起草的“自由软件指导方针”。对确立“Open Source”概念有决定意义的是在1998 年4月7日由18位自由软件静止首领召开的“自由软件高层会议”,通过了流传开源的必要性。 那开源和闭源次要的区别都有哪些呢?看下图 开源的意义GitHub 2021 年度报告数据显示:寰球已有超过 7300 万开发者用户,其中 56.8% 来自北美之外的地区。我国开发者占 10%,有 755 万,位居寰球第二。我国开发者数量及开源贡献度增长已成为寰球最快。GitHub 预测到 2030 年我国开发者将成为寰球最大的开源群体。Raymond在《大教堂与集市》中用大教堂和集市这两个具体形象,来代表两种不同的软件开发模式: 大教堂模式:自上而下,有一群精英进行顶层设计,依照打算去实现工作。 集市模式:自下而上,没有一个相对主导外围,靠的是一般个体们的自组织,一起去实现简单的工作。Raymond观点是:随着互联网的倒退,越来越多的大教堂会隐没,那么剩下的都是大集市。 所以,咱们必须认同一个趋势:“开源软件作为一种「非垄断性」和「非排他性」的常识存在,曾经是软件世界的重要基础设施,也是数字世界不可或缺的将来。” 数字经济时代,人口和流量红利逐步隐没,这都须要咱们开拓新的生态模式,而这所有都源于技术的推动。开源是一种凋谢、共享、协同的翻新合作模式,它不仅是凋谢源代码的软件技术开发,还包含更宽泛的凋谢技术畛域及协同翻新的理念与机制。无论是在寰球还是国内,开源正在推动着信息技术的翻新倒退。这种冲破物理边界、高效麻利的沟通和协同形式必将会为数字经济的倒退带来新生机。同时,开源的高度凋谢和自在,也会让人类智慧失去更好的共享和倒退,升高学习、复用和改良老本,突破技术封闭。开源,最终将造福整个软件业和整个社会。 为什么要开源01 WebGPU,下一代Web3D技术Orillusion致力于打造一款齐全开源基于WebGPU规范的轻量级渲染引擎,指标是在浏览器中实现桌面级的渲染成果,反对超大简单场景的3D出现。易上手、易分享、易迭代、易合作、成本低,跨平台是咱们的外围劣势,咱们将为3D场景暴发时代提供引擎根底工具。 Web环境中始终没有呈现能够实现和桌面级渲染能力相媲美的革命性技术,然而这一现状随着WebGPU规范的提出,行将失去扭转,这是十分让人振奋的改革,咱们将迎来网页图形的全新时代! 通过开源能够帮忙社区更好地理解这项技术,同时也能够通过社区的影响力对这项技术进行推广,邀请更多的开发者参加进来。社区的踊跃疾速反馈也能帮咱们吸取更多的需要场景输出,帮忙技术迭代更新,技术才更有生命力。咱们冀望在将来的数字经济时代可能施展更大更踊跃的作用。 02 开发者生态,构建高价值社区咱们始终深信,软件再优良,如果没有构建起良好的生态,没有开发者和合作伙伴的共建,是很难走得更好更远的。一个开源产品,社区的文化氛围和协同创造力,才是区别于别人最大的不同。 开源生态的建设根植于社区,有了衰弱社区生态的哺养,开源技术能力取得继续成长。志趣相投的开发者们源于被动发明价值的外在能源,在社区共享、共创、共赢,将会激发出有限的创造力,这就是开源最大的魅力! Orillusion在创建之初就动摇地拥抱开源,凋谢容纳、共创共赢是咱们的根本理念。咱们秉承全球化与本土化相结合,既要放弃全球化视角,吸引来自寰球各地优良的开发者退出进来,又要在咱们原有的文化背景下打造合乎外乡特色的开发者生态,推动我国开发者力量长期倒退。 咱们深知,开源生态的建设并非欲速不达,这将是一个长期过程,须要咱们有急躁、有信念、好高鹜远、凋谢心态。 抉择开源,源于咱们深信,“独行快,众行远”! 共建社区是这样的“社区的实质是归属感,没有归属感就没有社区。——Jono Bacon” 01 每一位社区参与者才是宝藏对于开源社区而言,最重要的宝藏不是代码,而是代码背地的每一位参与者和创造者,及大家在共创协同过程中建设起来的链接。社区的建设与成长,社区的文化与气氛,源于社区体系中的每一个人,源于集体会集成的凝聚力。因而,从咱们开源第一天起,每一位参与者是咱们要珍视和爱护的宝藏。 咱们也会始终放弃这样的敬畏之心,邀请更多行业内优良的开发者进来,建设起独特合作的开源社区。 02 放眼寰球的国际化社区咱们心愿通过开源的形式将Orillusion 做成一个平凡的产品,不仅局限在国内,更要着眼寰球! Github 2021 Octoverse报告数据,寰球的开发者有7300万+,2021年新增1600万+。我国2021年在Github的新增开发者103万,总数达到755万+。我国开发者在开源世界的影响力正在一直晋升,曾经从晚期开源的受益者,逐步转变成开源的贡献者。 https://octoverse.github.com/...寰球 97% 的软件开发者和 99% 的企业在应用开源软件,《财产》100 强里就有 84% 的企业应用 GitHub。从整个软件产业的供应链上看,开源曾经成为将来信息技术的主战场。因而,随着工信部信息技术倒退司公布的《“十四五”软件和信息技术服务业倒退布局》,中国开源根底软件当初曾经成为咱们国家的倒退策略。“欠缺开源知识产权和法律体系”相继被写入《“十四五”布局和二〇三五年近景指标大纲》、《“十四五”国家知识产权爱护和使用布局》等国家政策文件。国内的代码托管平台Gitee,2021年托管的代码仓库超过了 2000 万,⽤户总量超过 800 万。在这样一个国内和国内的背景下,中国的技术开发者须要致力,也须要工夫。在这个过程中,Orillusion会邀请全世界优良开发者宽泛参加,以真正凋谢的心态单干和推动开源生态,为我国的开源倒退添砖加瓦。 03 高价值且共赢的社区文化为开发者提供清晰欠缺的文档,升高大家的学习老本和门槛,尽力做到让每个人都是可参加的,能够从提交代码、社区文档、网站建设等方面循序渐进地参加。 WebGPU小白课程曾经上线七期,失去了大家的统一好评,后续咱们将继续输入各种类型优质的体系课程,帮忙大家高效学习。同时,咱们将定期邀请行业内的大咖举办线上、线下交流活动,为大家发明分享学习的机会。同时,咱们将会和各大高校进行单干,疏导更多大学生参加开源社区的建设中。 丰盛和稀缺有着实质不同,稀缺是意味着要击败其余所有对手你才能够胜利的一个游戏规则,但丰盛是将每个人的想法汇聚在一起来减少机会,达到共赢的游戏规则。所以,咱们所建设的社区是共创、共享、共赢的合作模式。 咱们的使命在开源世界,社区的概念不是物理定义的结构,也不是某个社交软件独自的状态,它是通过大家独特参加发明、分享观点和趣味来定义的。来自不同背景、不同观点的人都能够参加,思维、教训和意见的多样性,都使得衰弱的社区衰弱倒退。 基于WebGPU新技术,基于开源新生态,Web3D场景新需要,咱们深信在这个全新的我的项目和社区中,每一位参与者能够感触到共创和翻新的力量,并在这个过程中与别人建设长久高价值的协同关系。激发激情、挖掘劣势、找到气味相投的伙伴、突破天花板,取得成长,实现自我价值~ ...

May 26, 2022 · 1 min · jiezi

关于webgl:在-WebGPU-的片元着色器中访问帧缓冲坐标

在片元着色器中拜访帧缓冲坐标 1. 技术阐明应用最新 Edge/Chrome Canary 浏览器应用 VSCode 插件 LiveServer 的 HTTP 服务器对本机提供 5500 端口的页面服务,即 http://localhost:5500/index.html应用 es-module 格调的 JavaScript 实现2. 三角形例子先上成果,前面再解析片元着色器: HTMLhtml 局部就简略一些 <canvas id="c" width="600" height="600" style="border: 1px solid darkseagreen;"></canvas><script type="module" src="./main.js"></script>不出意外的话,你能够看到一个带暗绿色边框的 canvas,长宽均为 600 像素。 JavaScriptJavaScript 代码也比较简单,省略大部分动静代码和有无判断代码: const canvas = document.getElementById('c')const shaderText = `/* 着色器代码,前面会给 */`const init = async () => { const adapter = await navigation.gpu.requestAdapter() const device = await adapter.requestDevice() const context = canvas.getContext('webgpu') const presentationFormat = context.getPreferredFormat(adapter) context.configure({ device, format: presentationFormat, size: [ 600, 600 ], // canvas 的画图尺寸 }) const pipeline = device.createRenderPipeline({ vertex: { module: device.createShaderModule({ code: shaderText }), entryPoint: 'vertexMain' }, fragment: { module: device.createShaderModule({ code: shaderText }), entryPoint: 'fragmentMain', targets: [{ format: presentationFormat }], }, primitive: { topology: 'triangle-list' }, }) const render = () => { /* 每帧创立编码器并“录制”编码过程,最终提交给设施 */ const commandEncoder = device.createCommandEncoder() const textureView = context.getCurrentTexture().createView() const renderPassDescriptor = { colorAttachments: [ { view: textureView, clearValue: { r: 0.0, g: 0.0, b: 0.0, a: 1.0 }, loadOp: 'clear', storeOp: 'store', }, ], } const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor) passEncoder.setPipeline(pipeline) passEncoder.draw(3, 1, 0, 0) passEncoder.end() device.queue.submit([commandEncoder.finish()]) requestAnimationFrame(render) } requestAnimationFrame(render)} // async function initinit()我保留了残缺的 rAF 帧动画构造。 ...

April 27, 2022 · 2 min · jiezi

关于webgl:WebGL-及其在-WebRTC-中的应用

一、前言1、什么是 WebGL ?WebGL 的全称是 Web Graphics Library,是一种 3D 绘图协定。 WebGL 容许把 JavaScript 和 OpenGL ES 2.0 联合在一起,通过减少 OpenGL ES 2.0 的一个JavaScript 绑定,WebGL 能够为 HTML5 Canvas 提供硬件 3D 减速渲染。Web 开发人员就能够借助零碎显卡来在浏览器里更流畅地展现 3D 场景和模型,还能创立简单的导航和数据视觉化。 2、OpenGL 与 GLSL在理解 WebGL 之前,咱们需理解下什么是 OpenGL 与 GLSL。 OpenGL(英语:Open Graphics Library,译名为凋谢图形库或者“开放式图形库”)是用于渲染 2D、3D 矢量图形的跨语言、跨平台的应用程序编程接口(API), GLSL 则是用来在 OpenGL 中着色编程的语言。 从上文中能够看到 WebGL 实际上是 JavaScript 操作一些 OpenGL 接口,也就意味着,可能会编写一部分 GLSL ES 2.0 的代码,没错你猜对了,WebGL 只是绑定了一层,外部的一些核心内容,例如着色器、材质、灯光等都是须要借助 GLSL ES 语法来操作的。 基于 WebGL 周边也衍生了泛滥的第三方库,比方开发利用类的 Three.js,开发游戏类的 Egert.js 等,都大大的升高了学习 WebGL 的老本,然而本着有问题解决问题,没问题制作问题再解决问题的程序猿态度,还是感觉应该略微理解一下 WebGL 的一些基本概念,以便能更好的去了解不同框架带来的便捷以及劣势! ...

March 25, 2022 · 3 min · jiezi

关于webgl:WebGPU-计算管线计算着色器通用计算入门案例2D-物理模拟

原文译名:WebGPU - 专一于解决外围(GPU Cores),而不是绘图画布(Canvas)原文公布于 2022年3月8日,传送门 https://surma.dev/things/webgpu 这篇货色十分长,不计代码字符也有1w字,能比拟好了解 WebGPU 的计算管线中的各个概念,并应用一个简略的 2D 物理模拟程序来了解它,本篇重点是在计算管线和计算着色器,绘图局部应用 Canvas2D 来实现。 WebGPU 是行将推出的 WebAPI,你能够用它拜访图形处理器(GPU),它是一种底层接口。 原作者对图形编程没有多少教训,他是通过钻研 OpenGL 构建游戏引擎的教程来学习 WebGL 的,还在 ShaderToy 上学习 Inigo Quilez 的例子来钻研着色器。因而,他能在 PROXX 中创立背景动画之类的成果,然而他示意对 WebGL 并不太称心。别急,下文马上会解释。 当作者开始留神 WebGPU 后,大多数人通知他 WebGPU 这货色比 WebGL 多很多条条框框。他没思考这些,曾经预感了最坏的状况,他尽可能找了一些教程和标准文档来看,尽管彼时并不是很多,因为他找的时候 WebGPU 还在晚期制订阶段。不过,他深刻之后发现 WebGPU 并没有比 WebGL 多所谓的“条条框框”,反而是像见到了一位老朋友一样相熟。 所以,这篇文章就是来分享学到的货色的。 作者明确指出,他 不会 在这里介绍如何应用 WebGPU 绘制图形,而是要介绍 WebGPU 如何调用 GPU 进行它自身最原始的计算(译者注:也就是通用计算)。 他感觉曾经有很多材料介绍如何用 WebGPU 进行绘图了,例如 austin 的例子,或者他思考之后也写一些绘图方面的文章。 他在这里会探讨得比拟深刻,心愿读者能正确、无效地应用 WebGPU,然而他不保障你读完就能成为 GPU 性能专家。 絮絮叨叨完结后,筹备发车。 1. WebGLWebGL 是 2011 年公布的,迄今为止,它是惟一能在 Web 拜访 GPU 的底层 API,实际上它是 OpenGL ES 2.0 的繁难封装版以便能在 Web 中应用。WebGL 和 OpenGL 都是科纳斯组标准化的,这个工作组是图形界的 W3C,能够这么了解。 ...

March 21, 2022 · 8 min · jiezi

关于webgl:webGL-vertexAttribPointer-函数理解

MDN官网定义通知显卡从以后绑定的缓冲区(bindBuffer()指定的缓冲区)中读取顶点数据。 语法void gl.vertexAttribPointer(index, size, type, normalized, stride, offset);参数index 指定要批改的顶点属性的索引size 指定每个顶点属性的组成数量,必须是1,2,3或4。type 指定数组中每个元素的数据类型normalized 当转换为浮点数时是否应该将整数数值归一化到特定的范畴stride 一个GLsizei,以字节为单位指定间断顶点属性开始之间的偏移量(即数组中一行长度)。不能大于255。如果stride为0,则假设该属性是严密打包的,即不交织属性,每个属性在一个独自的块中,下一个顶点的属性紧跟以后顶点之后。offset GLintptr (en-US)指定顶点属性数组中第一局部的字节偏移量。必须是类型的字节长度的倍数了解为什么应用vertexAttribPointer如果须要批改顶点地位和色彩, 那么须要创立两个缓冲区。而应用vertexAttribPointer 能够存多种数据,不同数据间通过偏移来辨别,这样就能够只须要一个缓冲区数据了。 参数了解index 通过gl.getAttribLocation(gl.program, "a_Position")办法能够返回a_Position属性的索引,这就是index须要的数据size 你须要一次取几个数据。 例如: const verties = new Float32Array([ 0.0, 0.5, -0.4, 0.19607843137254902, 0.8431372549019608, 0.8745098039215686, -0.5, -0.5, -0.4, 0.4, 1.0, 0.4, 0.5, -0.5, -0.4, 1.0, 0.4, 0.4, 0.5, 0.4, -0.2, 1.0, 0.4, 0.4, -0.5, 0.4, -0.2, 1.0, 1.0, 0.4, 0.0, -0.6, -0.2, 1.0, 1.0, 0.4, 0.0, 0.5, 0.0, 0.4, 0.4, 1.0, -0.5, -0.5, 0.0, 0.4, 0.4, 1.0, 0.5, -0.5, 0.0, 1.0, 0.4, 0.4, ]);如果参数设置为3, 那就是说一次拿3个数据。 ...

March 7, 2022 · 1 min · jiezi

关于webgl:WebGL-与-WebGPU比对5-渲染计算的过程

前两篇文章介绍了 WebGL 和 WebGPU 是如何筹备顶点和数字型 Uniform 数据的(纹理留到下一篇),当渲染所需的原材料筹备实现后,就要进入逻辑组装的过程。 WebGL 在这方面通过指定“WebGLProgram”,最终触发“drawArrays”或“drawElements”来启动渲染/计算。全局状态为特色的 WebGL 显然做多步骤渲染来说会麻烦一些,WebGPU 改善了渲染计算过程的接口设计,容许开发者组装更简单的渲染、计算流程。 以所有的“draw”函数调用为分界线,调用后,就认为 CPU 端的工作曾经实现,开始移交筹备好的渲染、计算原材料(数据与着色器程序)至 GPU,进而运行起渲染管线,直至输入到帧缓冲/Canvas,我称 draw 这个行为是“一个通道”。 WebGPU 的呈现,除了渲染的性能,还呈现了通用计算性能,draw 也有了兄弟概念:dispatch(调度),下文会比照介绍。1. WebGL1.1. 应用 WebGLProgram 示意一个计算过程WebGL 的整个渲染管线(尽管没有管线 API)中,能染指编程的就两处:顶点着色阶段 和片元着色阶段,别离应用顶点着色器和片元着色器实现渲染过程的定制。 很多书或入门教程都会说,顶点着色器和片元着色器是成对呈现的,而能治理这两个着色器的下层容器对象,就叫做程序对象(接口 WebGLProgram)。 const vertexShader = gl.createShader(gl.VERTEX_SHADER) // WebGLShadergl.shaderSource(vertexShader, vertexShaderSource)gl.compileShader(vertexShader)const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER) // WebGLShadergl.shaderSource(fragmentShader, fragmentShaderSource)gl.compileShader(fragmentShader)const program = gl.createProgram() // WebGLProgramgl.attachShader(program, vertexShader)gl.attachShader(program, fragmentShader)gl.linkProgram(program)其实,真正的渲染管线是有很多步骤的,顶点着色和片元着色只是比拟有代表性: 顶点着色器大多数时候负责取色、图形变换片元着色大多数时候负责计算并输入屏幕空间的片元色彩既然 WebGL 只能定制这两个阶段,又因为这俩 WebGLShader 是被程序对象(WebGLProgram)治理的,所以,一个程序对象所代表的那个“管线”,通常用于执行一个通道的计算。 在简单的 Web 三维开发中,一个通道还不足以将想要的一帧画面渲染实现,这个时候要切换着色器程序,再进行 drawArrays/drawElements,绘制下一个通道,这样组合多个通道的绘制后果,就能在一个 requestAnimationFrame 中实现想要的渲染。 1.2. WebGL 没有通道 API上文提及,在一帧的渲染过程中,有可能须要多个通道共同完成渲染。最初一次 gl.drawXXX 的调用会应用一个绘制到指标帧缓冲的 WebGLProgram,这么说可能很形象,无妨思考这样一帧的渲染过程: 渲染法线、漫反射信息到 FBO1 中;渲染光照信息到 FBO2 中;应用 FBO1 和 FBO2,把最初后果渲染到 Canvas 上。每一步都须要本人的 WebGLProgram,而且每一步都要全局切换各种 Buffer、Texture、Uniform 的绑定,这样就须要一个封装对象来实现这些状态的切换,惋惜的是 WebGL 并没有这种对象,大多数时候是第三方库应用相似的类实现的。 ...

February 28, 2022 · 2 min · jiezi

关于webgl:WebGL-与-WebGPU比对4-Uniform

家喻户晓,在 GPU 跑可编程管线的时候,着色器是并行运行的,每个着色器入口函数都会在 GPU 中并行执行。每个着色器对一大片对立格局的数据进行冲锋,体现 GPU 多外围的劣势,能够小核同时解决数据;不过,有的数据对每个着色器都是一样的,这种数据的类型是“uniform”,也叫做对立值。 这篇文章列举了原生 WebGL 1/2 中的 uniform 材料,以及 WebGPU 中的 uniform 材料,有一些例子供参考,以用来比对它们之间的差别。 1. WebGL 1.0 Uniform1.1. 用 WebGLUniformLocation 寻址在 WebGL 1.0 中,通常是在 JavaScript 端保留 WebGLUniformLocation 以向着色器程序传递 uniform 值的。 应用 gl.getUniformLocation() 办法获取这个 location,有如下几种形式 全名:gl.getUniformLocation(program, 'u_someUniformVar')重量:通常是向量的一部分,譬如 gl.getUniformLocation(program, 'u_someVec3[0]') 是获取第 0 个元素(元素类型是 vec3)的 location构造体成员:gl.getUniformLocation(program, 'u_someStruct.someMember')下面三种状况与之对应的着色器代码: // 全名uniform float u_someUniformVar;// 重量uniform vec3 u_someVec3[3]; // 留神,这里是 3 个 vec3// 构造体成员struct SomeStruct { bool someMember;};uniform SomeStruct u_someStruct; 传值分三类,标量/向量、矩阵、采样纹理,见下文。 1.2. 矩阵赋值用 uniformMatrix[234]fv对于矩阵,应用 gl.uniformMatrix[234]fv() 办法即可传递,其中,f 代表 float,v 代表 vector,即传入参数要是一个向量(即数组); ...

February 19, 2022 · 5 min · jiezi

关于webgl:WebGL-与-WebGPU比对3-顶点缓冲

1. 获取高频操作对象1.1 WebGL 获取上下文对象WebGL 获取的是 WebGLRenderingContext/WebGLRenderingContext2 对象,必须依赖于有适合宽度和高度的 HTMLCanvasElement,通常命名为 gl,gl 变量有十分多办法,容许批改 WebGL 的全局状态 const gl = document.getElementById("id")?.getContext("webgl")// ...1.2 WebGPU 获取设施对象而 WebGPU 则不依赖具体的 Canvas,它操作的是物理图形卡设施,并应用 ES6/7 的异步语法获取,获取的是 GPUAdapter 和 GPUDevice,然而与 WebGLRenderingContext 起着相似“收回大多数命令”的大管家式角色的,更多是 GPUDevice 对象 const entryFn = async () => { if (!navigator.gpu) { return } // 测试版 Chrome 有可能返回 null const adapter = await navigator.gpu.requestAdapter() if (!adapter) { return } const device = await adapter.requestDevice() // ...}entryFn()WebGPU 的入口是 navigator.gpu 对象,这个对象在 WebWorker 中也有,所以对 CPU 端的多线程有良好的反对。应用此对象异步申请适配器后,再应用适配器申请具象化的设施对象即可。 ...

February 14, 2022 · 2 min · jiezi

关于webgl:WebGL-与-WebGPU比对2-初始化篇

1. 获取高频操作对象1.1 WebGL 获取上下文对象WebGL 获取的是 WebGLRenderingContext/WebGLRenderingContext2 对象,必须依赖于有适合宽度和高度的 HTMLCanvasElement,通常命名为 gl,gl 变量有十分多办法,容许批改 WebGL 的全局状态 const gl = document.getElementById("id")?.getContext("webgl")// ...1.2 WebGPU 获取设施对象而 WebGPU 则不依赖具体的 Canvas,它操作的是物理图形卡设施,并应用 ES6/7 的异步语法获取,获取的是 GPUAdapter 和 GPUDevice,然而与 WebGLRenderingContext 起着相似“收回大多数命令”的大管家式角色的,更多是 GPUDevice 对象 const entryFn = async () => { if (!navigator.gpu) { return } // 测试版 Chrome 有可能返回 null const adapter = await navigator.gpu.requestAdapter() if (!adapter) { return } const device = await adapter.requestDevice() // ...}entryFn()WebGPU 的入口是 navigator.gpu 对象,这个对象在 WebWorker 中也有,所以对 CPU 端的多线程有良好的反对。应用此对象异步申请适配器后,再应用适配器申请具象化的设施对象即可。 ...

February 14, 2022 · 2 min · jiezi

关于webgl:WebGPU-中消失的-FBO-和-RBO

OpenGL 体系给图形开发留下了不少的技术积攒,其中就有不少的“Buffer”,耳熟能详的就有顶点缓冲对象(VertexbufferObject,VBO),帧缓冲对象(FramebufferObject,FBO)等。 切换到以三大古代图形开发技术体系为根底的 WebGPU 之后,这些经典的缓冲对象就在 API 中“隐没了”。其实,它们的职能被更迷信地扩散到新的 API 去了。 本篇讲一讲 FBO 与 RBO,这两个通常用于离屏渲染逻辑中,以及到了 WebGPU 后为什么没有这两个 API 了(用什么作为了代替)。 1 WebGL 中的 FBO 与 RBOWebGL 其实更多的角色是一个绘图 API,所以在 gl.drawArrays 函数收回时,必须确定将数据资源画到哪里去。 WebGL 容许 drawArrays 到两个中央中的任意一个:canvas 或 FramebufferObject. 很多材料都有介绍,canvas 有一个默认的帧缓冲,若不显式指定本人创立的帧缓冲对象(或者指定为 null)那就默认绘制到 canvas 的帧缓冲上。 换句话说,只有应用 gl.bindFramebuffer() 函数指定一个本人创立的帧缓冲对象,那么就不会绘制到 canvas 上。 本篇探讨的是 HTMLCanvasElement,不波及 OffscreenCanvas1.1 帧缓冲对象(FramebufferObject)FBO 创立起来简略,它大多数时候就是一个负责点名的头儿,出汗水的都是小弟,也即它下辖的两类附件: 色彩附件(在 WebGL1 中有 1 个,在 WebGL 2 能够有16个)深度模板附件(能够只用深度,也能够只用模板,也能够两个一起应用)对于 MRT 技术(MultiRenderTarget),也就是容许输入到多个色彩附件的技术,WebGL 1.0 应用 gl.getExtension('WEBGL_draw_buffers') 获取扩大来应用;而 WebGL 2.0 原生就反对,所以色彩附件的数量上有所区别。而这两大类附件则通过如下 API 进行设置: // 设置 texture 为 0 号色彩附件gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, color0Texture, 0)// 设置 rbo 为 0 号色彩附件gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, color0Rbo)// 设置 texture 为 仅深度附件gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, depthTexture, 0)// 设置 rbo 为 深度模板附件(须要 WebGL2 或 WEBGL_depth_texture)gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, depthStencilRbo)实际上,在须要进行 MRT 时,gl.COLOR_ATTACHMENT0、gl.COLOR_ATTACHMENT1 ... 这些属性只是一个数字,能够通过计算属性进行色彩附件的地位索引,也能够间接应用明确的数字代替: ...

February 14, 2022 · 4 min · jiezi

关于webgl:Web-or-Native-哪个才是元宇宙的未来下

上一篇文章咱们介绍了 Web 和 Native 的倒退历程,对Web端的跨平台个性以及生态的倒退和遍及水平广的劣势进行了剖析,还没有看到的敌人们能够点击上面链接回顾一下哦 Web or Native 谁才是元宇宙的将来(上)? 置信看完这篇文章,你曾经有了一些想法,明天咱们持续来聊一聊对于Web和Native的技术栈抉择,看看你是否也批准小鸥的认识呢~ Web劣势3: 超级APP生态的嵌入任何公司具备了超级APP之后,必然都会思考打造利用外部的生态,将尽量多的场景和能力在本人的利用生态中对立解决。比方咱们日常都会应用的微信、支付宝、淘宝等等。 思考到应答场景和需要的多变性,支付宝、淘宝、微信都是混合框架,在保留弱小的Native能力和性能的同时,又充分利用JS/Web的灵便的前端生态能力,保障开发效率的同时,又能满足疾速迭代热更新。春节邻近期间,支付宝或者淘宝中都会有很多3D的特效呈现,像红包、五福等等。 对于Unity这种Native引擎来说,尽管实现这些3D特效都非常简单,然而因为它齐全是在Native框架下的产品,在交融上存在泛滥技术难点,很难和前端的技术进行无缝的对接。所以,面对这种超级APP的人造流量汇聚地,Unity也无奈接入。而最终咱们看到的各种3D特效,都是由挪动优先、Web优先的Oasis引擎团队打造实现的! Oasis 引擎 Github 链接https://github.com/oasis-engineWeb劣势4: 易流传、易分享、易合作,人造的SaaS服务模式因为在Web环境下,通过一个链接就能够把内容分享到不同的平台,不同的人群,这种模式能够达到极致的内容流传成果。最有代表性的就是咱们的微信,通过朋友圈,微信群,公众号等等,都可能引起内容分享的病毒式裂变成果。 已经,咱们分享的内容更多的是以图片,文字,视频,音频的模式。随着元宇宙时代的到来,更多样的3D内容也可能会随着一个链接引发内容流传的爆点,这给了内容制作畛域更广大的设想空间。WebGPU规范是实现此场景的关键技术点。 更值得一提的是,基于浏览器通用反对的WebSocket/WebRTC协定,配合如OT等分布式算法能够轻松实现分布式协同,达到多人同步实现一项工作的高效单干模式,这些都是Web得天独厚的劣势体现。比方Google Doc,石墨文档,飞书文档等,也都是借助于浏览器环境实现了合作性能。依附Web协同个性最胜利的企业应该是估值超百亿美金的Figma,它的胜利同时也引爆了「设计合作」这样一个超过千亿市场规模的SaaS赛道。 说到SaaS服务,是简直所有的互联网公司都谋求的变现模式。这也正是Web另外一个微小的劣势体现,因为简直所有的SaaS都是以Web浏览器作为媒介来提供服务的。 最近十分火爆的「Gather Town」元宇宙办公服务提供商,全套的技术框架都是基于Web环境实现的,这大大降低了用户应用的门槛,也为他们的流传带来了的微小的促进作用(他们的客户端也是基于Electron打包,一套代码间接散发成为各个平台的APP进行装置)。 Web劣势5: 热加载不管喜不喜欢“元宇宙”这个词汇,它都无奈阻挡的向咱们缓缓走来。咱们了解的“元宇宙”,是一个有限可能的场景。为了实现这种有限的可能性,就要求元宇宙产品必须具备两个特点: 性能的疾速更新及有限扩大:将来,咱们的产品有了新的feature,一旦通过测试,就应该立即服务于咱们的用户;资源的疾速更新及有限扩大:不论是UGC还是PGC,用户在应用产品进行内容创作时,一旦有了新的变动,其余所有的用户应该在第一工夫无提早的状况下进行体验。Native原生的应用程序是很难实现这些特点的,因为每一次应用程序的公布更新等,都须要从新的编译,甚至还须要利用商店的审核。如果一个技术团队,面对需要变动微小的场景进行产品研发,任何前端需要的变动都要造成一次APP的从新编译公布,那效率会十分低下。这也是为什么像Flutter,React Native,Weex这种框架会越来越火,因为它们都有着和Web一样的热加载个性。 热加载的原理是利用脚本语言的可解释个性,咱们通过间接刷新的模式,或者网络通信,把新的脚本内容直接插入或者笼罩原来须要被解释的代码局部,这样再由实时的解释形语言编译器进行实时运行,就能够达到即时更新的热加载成果。 而Native的利用,都是要进行预编译,简略了解就是最终APP都会变成0101这种货色,那新扭转的内容就没有方法在未编译的状况下,被间接执行了。 咱们利用Web的人造热加载个性,再配合产品设计的模块化架构以及代码治理的Monorepo机制,就能够轻松的实现产品性能的疾速更新及有限扩大和资源的疾速更新及有限扩大。总而言之,Web可能更加适宜“元宇宙”这种高速变动且具备有限可能性的场景。 小鸥的思考Web技术栈和Native技术栈并不是一种纯竞争的关系。Native能带来更加极致的性能体验,这是毋庸置疑的事实。不论是Unity,还是Unreal,甚至是依附靠近原生图形API性能的Bgfx架构来开发各种游戏和利用,都体现了Native弱小的劣势。 Bgfx 引擎Github链接https://github.com/bkaradzic/bgfx咱们常常打一个比如,即便手机的拍照能力再强,真正的“老法师”也仍然会保持用600去打鸟。即便美图秀秀能够一键生成简直完满的自拍照,PS的专业级忠诚用户仍然会死守不放。 因而,抉择Web或Native,也不再是一个哲学问题,而是一个场景问题。 如果咱们的场景须要谋求极致的性能,那就用Native的技术栈去开发,比方Unity和Unreal。他们在游戏场景里简直垄断的位置,足以证实这一点。R Star引擎下的《荒野大镖客》更是成为了行业内的天花板级存在。 如果场景更多的是趋向于便捷,简略,易分享等等特色,抉择Web很可能是一个更佳的计划。最重要的是,当初有了WebGPU这样一个跨时代的全新规范,将最大限度的磨平Web和Native对于3D场景出现上的差距。 已经的WebGL时代,在3D场景中,Web开发者是很难真正发力的。实现一个优良的3D场景利用是一个极其苦楚的过程。因为WebGL的性能切实太差了,很多Native能够轻松实现的渲染成果,WebGL都实现不了。但在这种极其艰巨的状况下,Web上也呈现了泛滥优良的利用案例! 事实世界中应用WebGL的25+个利用https://zhuanlan.zhihu.com/p/369632693因而,抉择Web和Native,可能也不再是一个场景问题,而是一个面对元宇宙暴发时代的将来判断问题。 将来的世界,会有大量的3D内容制作需要,因为3D内容是元宇宙搭建的根底。这也就意味着,咱们面对的不是一个存量市场,而是一个增量市场,甚至是一个未知的充斥有限可能的市场。泛滥的Web开发者,都会成为将来3D场景搭建的潜在生产力。Web的技术栈会因为WebGPU规范的到来,呈现前所未有的晋升和扭转。这对于Web前端开发者,是一件太幸福的事件! 面对将来,当更多的年轻人,更多的新人进入到相应行业中,他们要思考:什么是最适宜的, 什么是最简略的,或者哪种抉择将来的需要更多,本人更可能成长。Web生态就很有可能暴发出前所未有的劣势:易上手,易分享,易迭代,成本低,跨平台,这些特点未然满足了绝大部分的场景。 因而,咱们对于WebGPU加持下的Web生态充斥了期待! 如何退出Orillusion WebGPU社区?第一步:长按下图,扫码增加管理员微信 第二步:填写论坛注册申请表 第三步:查看邮箱,点击注册申请链接 第四步:注册胜利,欢送留言发帖 欢送更多的小伙伴能够退出咱们的Orillusion社区,陪咱们一起见证WebGPU的倒退。咱们会尽本人最大的致力把最干货最前沿的WebGPU技术分享给每一位社区成员,也诚心的心愿大家为Orillusion开源社区做出本人的奉献。咱们始终深信,开源社区的技术留痕是每一位技术人员最高尚的谋求。因而,咱们尊重,咱们认可,咱们更期待,退出Orillusion,让咱们共同进步! ——Link uncharted, 链接将来世界

January 26, 2022 · 1 min · jiezi

关于webgl:WebGPU-的几个最佳实践

来自 2022 WebGL & WebGPU Meetup 的 幻灯片 1 在能用的中央都用 label 属性WebGPU 中的每个对象都有 label 属性,不论你是创立它的时候通过传递 descriptor 的 label 属性也好,亦或者是创立实现后间接拜访其 label 属性也好。这个属性相似于一个 id,它能让对象更便于调试和察看,写它简直不须要什么老本考量,然而调试的时候会十分、十分爽。 const projectionMatrixBuffer = gpuDevice.createBuffer({ label: 'Projection Matrix Buffer', size: 12 * Float32Array.BYTES_PER_ELEMENT, // 成心设的 12,实际上矩阵应该要 16 usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,})const projectionMatrixArray = new Float32Array(16)gpuDevice.queue.writeBuffer(projectionMatrixBuffer, 0, projectionMatrixArray)下面代码成心写错的矩阵所用 GPUBuffer 的大小,在谬误校验的时候就会带上 label 信息了: // 控制台输入Write range (bufferOffset: 0, size: 64) does not fit in [Buffer "Projection Matrix Buffer"] size (48).2 应用调试组指令缓冲(CommandBuffer)容许你增删调试组,调试组其实就是一组字符串,它批示的是哪局部代码在执行。谬误校验的时候,报错音讯会显示调用堆栈: ...

January 26, 2022 · 3 min · jiezi

关于webgl:WebGL-与-WebGPU-比对1-前奏

这篇讲讲历史,不太适宜直奔主题的敌人们。 1 为什么是 WebGPU 而不是 WebGL 3.0你若往 Web 图形技术的底层去深究,肯定能追溯到上个世纪 90 年代提出的 OpenGL 技术,也肯定能看到,WebGL 就是基于 OpenGL ES 做进去的这些信息。OpenGL 在那个显卡羸弱的年代施展了它应有的价值。 显卡驱动咱们都晓得当初的显卡都要装置显卡驱动程序,通过显卡驱动程序裸露的 API,咱们就能够操作 GPU 实现图形处理器的操作。 问题就是,显卡驱动和一般编程界的汇编一样,底层,不好写,于是各大厂就做了封装 —— 码界的基操。 图形 API 的简略年表OpenGL 就是干这个的,负责下层接口封装并与上层显卡驱动打交道,然而,家喻户晓,它的设计格调曾经跟不上古代 GPU 的个性了。 Microsoft 为此做进去最新的图形API 是 Direct3D 12,Apple 为此做进去最新的图形API 是 Metal,有一个驰名的组织则做进去 Vulkan,这个组织名叫 Khronos。D3D12 当初在发光发热的中央是 Windows 和 PlayStation,Metal 则是 Mac 和 iPhone,Vulkan 你可能在安卓手机评测上见得多。这三个图形 API 被称作三大古代图形API,与古代显卡(无论是PC还是挪动设施)的分割很亲密。 WebGL 能运行在各个浏览器的起因噢,忘了一提,OpenGL 在 2006 年把丢给了 Khronos 管,当初各个操作系统根本都没怎么装这个很老的图形驱动了。 那问题来了,基于 OpenGL ES 的 WebGL 为什么能跑在各个操作系统的浏览器? 因为 WebGL 再往下曾经能够不是 OpenGL ES 了,在 Windows 上当初是通过 D3D 转译到显卡驱动的,在 macOS 则是 Metal,只不过工夫越靠近当初,这种非亲儿子式的实现就越发艰难。 ...

January 15, 2022 · 3 min · jiezi

关于webgl:北京-数据分析公司-招聘渲染方向研发工程师-薪资开放

工作内容/要求:参加研发2D根底渲染引擎(为满足图可视化方向) 参考 netv,sigma图相干算法。(这个作为抉择)前端/后端日常开发也会参加。不过少... 最多算是对接。。数学要好一点点, 渲染协定Webgl / OpenGL理论开发教训。 (加分)。工作劣势:属于研发团队。领导nice(直属leader当然是我啦) 我关注就是后果。(也就是输入内容我认可。那么下班天天摸鱼都可轻易你。)我可能带给你的( 工作上 技术晋升,工作其余软能力晋升。 当然有一天你想去大厂,也能够推你一把。生存上嘛 我可能帮忙的肯定乐意效劳)公司可能带给你的,钱(对,还能够申请奖金) 环境 将来(前景的很大的。IPO的日子也不会远) 一个不一样的经验。公司介绍:规模400+,大数据分析类型公司。 薪资问题:看面试后果定。 有动向的请分割我的微信。wywin2021over - -

January 14, 2022 · 1 min · jiezi

关于webgl:如何基于模型数据绘制一个3D机器人

最终的成果如左边所示(你能够应用鼠标、手指或者键盘来管制这个机器人的旋转): 本我的项目基于image3D实现。 在正式开始之前,咱们须要筹备好模型数据,你能够去这里下载:model.json 如果想疾速体验最终成果,能够点击此处查看。 着色器首先,你须要筹备好两个着色器。 顶点着色器<script type='x-shader/x-vertex' id='vs'> attribute vec3 a_position; // 顶点坐标 uniform mat4 u_matrix; // 变换矩阵 uniform vec3 u_LPosition; // 光的地位 attribute vec3 a_normal; varying vec3 v_LDirection; varying vec3 v_normal; void main(){ // 坐标新增齐次坐标,为了和矩阵对齐 gl_Position=u_matrix * vec4(a_position,1); // 点光源方向计算:点光源方向 = 点光源坐标 - 顶点坐标 // 顶点的地位应该应用计算过的 v_LDirection=u_LPosition-gl_Position.xyz; v_normal=a_normal; }</script>片段着色器<script type='x-shader/x-fragment' id='fs'> precision mediump float; uniform vec4 u_LColor; // 光色彩 uniform vec4 u_color; // 顶点色彩 varying vec3 v_LDirection; // 光线方向 varying vec3 v_normal; // 法线方向 void main(){ // 先对方向进行序列化,使得向量长度为1 vec3 LDirection=normalize(v_LDirection); vec3 normal=normalize(v_normal); // 计算序列化后的光方向和法线方向的点乘 float dotValue=max(dot(LDirection,normal),0.2); gl_FragColor=u_color*u_LColor*dotValue;}</script>画布也就是一个canvas: ...

January 5, 2022 · 1 min · jiezi

关于webgl:Cesium-实现建筑夜景贴图

在线预览 Demo 源码 在新版本的 Cesium(1.87.0),反对了 CustomShader,能够为 Cesium3DTileset 编写自定义的 GLSL 代码。在此之前,要批改 3D Tileset 的款式,只能通过 style 属性,能做的最多也就是依据高度使不同修建展现不同色彩这种水平。 CustomShader 的根底用法能够查看官网文档,这里不再赘述,次要是介绍如何利用 CustomShader 实现修建贴图。 楼顶着色首先,楼顶不会和周围贴一样的图,先对立解决成深色。 并没有间接的属性表明以后点位于哪块墙上,但浏览文档能够发现,Cesium 提供了 normalMC 属性,也就是这个点所在立体的单位法向量。而楼顶个别是于高空平行的,将其法向量与高空的法向量比拟,如果夹角较小,就能够把它看作楼顶。 FragmentShader 代码如下(normalMC 在片段着色器不可用,须要在顶点着色器中通过 varying 传递过去,这里省略了过程): void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) { if (dot(vec3(0.0, 0.0, 1.0), v_normalMC) > 0.95) { material.diffuse = vec3(0.079, 0.107, 0.111); }}对两个向量做 dot(点积),能够失去这两个向量夹角的余弦值,再做反余弦,即可失去夹角,但反余弦操作比较慢,并且这里不须要准确的夹角值,所以对余弦值的大小做判断就行了 楼顶着色残缺代码 周围贴图解决完楼顶,再解决四个侧面。贴图的要害是确定一个二维坐标,而后间接调用 texture2D 办法获取图片上对应色彩。从视觉上看,Shader 中的 Z 坐标必定是对应图片的纵坐标,问题在于 Shader 中的 X,Y 坐标如何对应到图片的横坐标 其实这里不能应用繁多的 X,Y 坐标,这样做会有很多修建的某个侧面是繁多的色彩,达不到贴图的成果。我最初还是利用了法向量属性,计算他们与 vec3(1.0, 0.0, 0.0) & vec3(0.0, 1.0, 0.0) 的夹角,如果与 vec3(1.0, 0.0, 0.0) 的夹角较小,就应用它的 Y 坐标,反之亦然。 ...

November 22, 2021 · 2 min · jiezi

关于webgl:如何给普通图片加上水波纹shader-奇技淫巧

3D场景实现水波纹,咱们往往会应用网格去模仿实在的水流动,无论是简略的三角函数或是gerstner wave。而后通过实在物理渲染(base physcal render)来实现其中的折射与反射。这些实现能够参考《GPU GEMS》第一版。 原谅我,古早年代的书就这成果但对于2D场景这样的模仿就显得开销过大,2D场景往往会应用一些“投机取巧”的形式,例如应用沃罗诺伊纹理(voronoi)来模仿焦散成果。而本文就来聊聊如何投机出一个2D的水波纹成果,最终成果如下: 最终代码:precision mediump float;/* 变量申明*/varying vec2 uv;uniform sampler2D u_image0;uniform float u_time;uniform float u_offset;uniform float u_radio;#define MAX_RADIUS 1#define DOUBLE_HASH 0#define HASHSCALE1 .1031#define HASHSCALE3 vec3 (.1031, .1030, .0973)/* 工具函数*/float hash12 (vec2 p) { vec3 p3 = fract (vec3 (p.xyx) * HASHSCALE1); p3 += dot (p3, p3.yzx + 19.19); return fract ((p3.x + p3.y) * p3.z);}vec2 hash22 (vec2 p) { vec3 p3 = fract (vec3 (p.xyx) * HASHSCALE3); p3 += dot (p3, p3.yzx + 19.19); return fract ((p3.xx + p3.yz) * p3.zy);}void main () { vec2 frag = uv; frag.x *= u_radio; frag = frag * u_offset * 1.5; vec2 p0 = floor (frag); vec2 circles = vec2 (0.); for (int j = -MAX_RADIUS; j <= MAX_RADIUS; ++j) { for (int i = -MAX_RADIUS; i <= MAX_RADIUS; ++i) { vec2 pi = p0 + vec2 (i, j); vec2 hsh = pi; vec2 p = pi + hash22(hsh) ; // hash12 增加随机 float t = fract (0.3 * u_time + hash12(hsh)); vec2 v = p - frag; // 半径: float d = length (v) - (float (MAX_RADIUS) + 1. )*t ; float h = 1e-3; float d1 = d - h; float d2 = d + h; float p1 = sin (31. * d1) * smoothstep (-0.6, -0.3, d1) * smoothstep (0., -0.3, d1); float p2 = sin (31. * d2) * smoothstep (-0.6, -0.3, d2) * smoothstep (0., -0.3, d2); circles += 0.5 * normalize (v) * ((p2 - p1) / (2. * h) * (1. - t) * (1. - t)); } } // 两轮循环增加了weight个波(取均匀) float weight = float ((MAX_RADIUS * 2 + 1) * (MAX_RADIUS * 2 + 1)); circles /= weight; float intensity = mix (0.01, 0.05, smoothstep (0.1, 0.6, abs (fract (0.05 * u_time + .5) * 2. - 1.))); vec3 n = vec3 (circles, sin ( dot (circles, circles))); vec3 colorRipple = texture2D (u_image0, uv + intensity * n.xy).rgb; float colorGloss = 5. * pow (clamp (dot (n, normalize (vec3 (1., 0.7, 0.5))), 0., 1.), 6.); vec3 color = colorRipple + vec3(colorGloss); gl_FragColor = vec4 (color, 1.0);}折射和反射2D模仿水波纹,次要就是要实现水波的折射与反射。它们别离由反射项vec3(colorGloss)和折射项colorRipple管制其中反射项由colorGloss管制 ...

October 28, 2021 · 4 min · jiezi

关于webgl:WebGL浅入浅出

豆皮粉儿们,又见面了,明天这一期,由“Kakashi”,给咱们带来webgl的学习,webgl一种很强的数据可视化的图形库,同时数据可视化也是前端倒退的一个重要方向之一,值得入坑和学习。 本文作者:Kakashi前言:本篇文章预计浏览耗时【15min】将较为全面根底的带你学习 如何应用WebGL(并重) && 相干原理(简略)。通过本次学习 你将能够从0到1搭建本人的webgl繁难渲染库一 、背景:1.1 为什么要用WebGL?先热身一下吧,看个问题: 如果你有一堆作业没有做完,能够抉择帮手替你实现,你会抉择? A. 1个老传授 B. 9个小学生 选A的同学,你真的忍心让老传授帮忙写语文填空?选B的同学,你真的释怀让小学生帮你解线性代数?所以咱们要看看“作业”内容到底是什么,有多少作业量。如果题目是 1+2 ,9个小学生 = 人海战术收费劳动力,没有技术含量,纯正体力活,效率会很高。如果题目是线性代数 ,老传授 >> 99 个小学生。回到咱们的主题,相似的,因为设计指标的不同,CPU和GPU也是大不相同的。它们别离针对了两种不同的利用场景:CPU须要很强的通用性来解决各种不同的数据类型,同时又要逻辑判断又会引入大量的分支跳转和中断的解决。这些都使得CPU的内部结构异样简单。GPU面对的则是类型高度对立的、互相无依赖的大规模数据和不须要被打断的污浊的计算环境。 (图片来自nVidia CUDA文档。其中绿色的是计算单元,橙红色的是存储单元,橙黄色的是管制单元。)GPU在概念上实用于高度并行计算,因为GPU能够通过计算暗藏内存拜访提早,而不是通过大数据缓存和流控制来防止内存拜访提早。简而言之,CPU 基于低延时的设计,GPU是基于大的吞吐量设计。 1.2 什么是WebGL?WebGL是一种3D绘图规范,这种绘图技术标准容许把JavaScript和OpenGL ES 2.0联合在一起,通过减少OpenGL ES 2.0的一个JavaScript绑定,WebGL能够为HTML5 Canvas提供硬件3D减速渲染(局部计算GPU),这样Web开发人员就能够借助零碎显卡来在浏览器里更流畅地展现3D场景和模型了,还能创立简单的导航和数据视觉化。显然,WebGL技术标准免去了开发网页专用渲染插件的麻烦,可被用于创立具备简单3D构造的网站页面,甚至能够用来设计3D网页游戏等等。 总结一下,WebGL的实质 —— JavaScript操作OpenGL接口。 当然,WebGL只是绑定了一层,外部的一些核心内容,如着色器,材质,灯光等都是须要借助GLSL ES语法来操作的。1.2.1 WebGL / OpenGL / ES X.0之间的关系? OpenGL(Open Graphics Library)一种图形应用程序编程接口标准(Application Programming Interface,API)。它是一种能够对图形硬件设施个性进行拜访的软件库,OpenGL被设计为一个现代化的、硬件无关的接口,因而咱们能够在不思考计算机操作系统或窗口零碎的前提下,在多种不同的图形硬件零碎上,齐全通过软件的形式实现OpenGL的接口。OpenGL ES(embedded system) OpenGL ES是OpenGL的一个非凡版本,次要针对嵌入式设施应用,专用于嵌入式计算机、智能手机、家用游戏机等设施。OpenGL ES 2003-2004年被首次提出来,其中两次重要降级别离在2007年(OpenGL ES 2.0)和2012年(OpenGL ES 3.0),WebGL就是基于OpenGL ES 2.0的。二、如何应用WebGL2.1 入门示例,不再是hello world! let gl = (document.getElementById( 'canvas' ) as HTMLCanvasElement).getContext('webgl') as WebGLRenderingContextBase; // 指定一个笼罩(清空)canvas的rgba色彩,实质是setColor,它把背景色存到了webgl system中的glCOLOR_BUFFER_BIT,得手动render一下 gl.clearColor(0.0, 0.0, 0.5, 1.0); // 革除canvas,会革除全副,再应用背景色 填充 gl.clear(gl.COLOR_BUFFER_BIT); }运行后果&流程示意: ...

September 13, 2021 · 2 min · jiezi

关于webgl:如何玩转-WebGL-并行计算

简介: 现在在 Web 端应用 WebGL 进行高性能计算已有不少实际,例如在端智能畛域中的 tensorflow.js,再比方可视化畛域中的 Stardust.js。 作者 | 沧东起源 | 阿里技术公众号 现在在 Web 端应用 WebGL 进行高性能计算已有不少实际,例如在端智能畛域中的 tensorflow.js,再比方可视化畛域中的 Stardust.js。在本文中,咱们将介绍以下内容: 应用 GPU 进行通用计算(GPGPU)的历史以后在 Web 端应用图形 API 实现 GPGPU 的技术原理,以及前端开发者可能遇到的难点相干业界实际,包含布局计算、动画插值等局限性与将来瞻望一 什么是 GPGPU因为硬件构造不同,GPU 与 CPU 善于执行不同类型的计算工作。CPU 通过简单的 Cache 设计实现低提早,蕴含简单的管制逻辑(分支预测),ALU 只占一小部分。而 GPU 为高吞吐量而生,蕴含大量 ALU。因而在单指令流多数据流(SIMD)场景下,GPU 的运算速度远超 CPU,并且这种差距还在一直拉大。 而一些古代 GPU 上甚至有专门负责张量计算、光线追踪的硬件(Tensor/RT Core),例如 Nvidia 的图灵架构。这使得在解决这些计算复杂度极高的工作时能取得更大的性能晋升。 这里就须要引出一个概念,用 GPU 进行除渲染外的通用计算:General-Purpose computation on Graphics Processing Units,即 GPGPU。 自 2002 年提出以来,在实时加解密、图片压缩、随机数生成等计算畛域都能看到它的身影,GPU Gems/Pro 上也有专门的章节介绍。经由 Nvidia 提出的 CUDA(Compute Unified Device Architecture) 这一对立计算架构,开发者能够应用 C、Java、Python 等语言编写本人的并行计算工作代码。 ...

August 19, 2021 · 3 min · jiezi

关于webgl:WebGL10-常用API及参数

WebGL Specifications (khronos.org) 类型以及对象定义这部分内容次要定义一部分类型和数据结构。 // 由 WebGLContextAttributes 援用enum WebGLPowerPreference { "default", "low-power", "high-performance" };// 获取上下文时反对的参数// getContext('webgl', <WebGLContextAttributes>)dictionary WebGLContextAttributes { boolean alpha = true; boolean depth = true; boolean stencil = false; boolean antialias = true; boolean premultipliedAlpha = true; boolean preserveDrawingBuffer = false; WebGLPowerPreference powerPreference = "default"; boolean failIfMajorPerformanceCaveat = false;};interface WebGLObject {};interface WebGLBuffer : WebGLObject {};interface WebGLFramebuffer : WebGLObject {};interface WebGLProgram : WebGLObject {};interface WebGLRenderbuffer : WebGLObject {};interface WebGLShader : WebGLObject {};interface WebGLTexture : WebGLObject {};interface WebGLUniformLocation {};interface WebGLActiveInfo { readonly attribute GLint size; readonly attribute GLenum type; readonly attribute DOMString name;};interface WebGLShaderPrecisionFormat { readonly attribute GLint rangeMin; readonly attribute GLint rangeMax; readonly attribute GLint precision;};WebGLRenderingContext 对象属性数据readonly attribute (HTMLCanvasElement or OffscreenCanvas) canvas;// 能够在 Web Work 上应用 canvas api// 须要调用 canvas.transferControlToOffscreen() 将渲染权转移给后盾线程readonly attribute OffscreenCanvas canvas;readonly attribute GLsizei drawingBufferWidth;readonly attribute GLsizei drawingBufferHeight;Example:gl.uniform1f(u_Width, gl.drawingBufferWidth);gl.uniform1f(u_Height, gl.drawingBufferHeight);缓冲区相干办法// 缓冲区类型const GLenum DEPTH_BUFFER_BIT = 0x00000100;const GLenum STENCIL_BUFFER_BIT = 0x00000400;const GLenum COLOR_BUFFER_BIT = 0x00004000;// 清理指定缓存内容, 能够通过或运算符一次清理多个缓冲区void clear(GLbitfield mask);//色彩缓冲区(COLOR_BUFFER_BIT) | 深度缓冲区(DEPTH_BUFFER_BIT) | 模板缓冲区(STENCIL_BUFFER_BIT)// 将指定缓冲区设置为指定的值(参数范畴都是 0.0 - 1.0)void clearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); //默认 0.0, 0.0, 0.0, 0.0void clearDepth(GLclampf depth); //默认 1.0void clearStencil(GLint s); //默认 0Example:gl.clearColor(0.0, 0.0, 0.0, 1.0);ctx.clearDepth(1); //resetctx.clearStencil(-1);gl.enable(gl.DEPTH_TEST);gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); //执行的意思绘制相干办法// 绘制的类型const GLenum POINTS = 0x0000;const GLenum LINES = 0x0001;const GLenum LINE_LOOP = 0x0002;const GLenum LINE_STRIP = 0x0003;const GLenum TRIANGLES = 0x0004;const GLenum TRIANGLE_STRIP = 0x0005;const GLenum TRIANGLE_FAN = 0x0006;void drawArrays(GLenum mode, //依照mode参数指定的形式绘制图形,下面 GLint first, //指定从哪个定点开始绘制 GLsizei count); //指定绘制须要用到多少个顶点 void drawElements(GLenum mode, //依照mode参数指定的形式绘制图形,下面 GLsizei count, //指定绘制须要用到多少个顶点 GLenum type, //指定索引值数据类型。包含:UNSIGNED_BYTE、UNSIGNED_SHORT、UNSIGNED_INT 留神这三个 GLintptr offset); //指定索引数组中绘制的偏移地位,以字节为单位drawArraysInstancedANGLE(gl.TRIANGLES, 0, 6, instanceCount)Example:gl.drawArrays(gl.TRIANGLES, 0, n); //n 个别是 数组/3gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0); //n 个别是indices.lengthext = gl.getExtension("ANGLE_instanced_arrays");ext.vertexAttribDivisorANGLE(aOffsetLocation, 1);ext.vertexAttribDivisorANGLE(aColorLocation, 1);ext.drawArraysInstancedANGLE(gl.TRIANGLES, 0, 6, instanceCount);缓存对象// 创立缓冲区对象WebGLBuffer? createBuffer();// 容许应用buffer示意的缓冲区对象并将其绑定到target示意的指标上// 下文中的target参数值const GLenum ARRAY_BUFFER = 0x8892;const GLenum ELEMENT_ARRAY_BUFFER = 0x8893;// const GLenum ARRAY_BUFFER_BINDING = 0x8894; // const GLenum ELEMENT_ARRAY_BUFFER_BINDING = 0x8895;void bindBuffer(GLenum target, //下面 WebGLBuffer? buffer); //createBuffer // 开拓存储空间,向绑定在target上的缓冲区对象写入数据data// 下文中的usage参数值const GLenum STREAM_DRAW = 0x88E0;//STATIC_DRAW 只会向缓冲区写入一次数据 须要绘制很屡次const GLenum STATIC_DRAW = 0x88E4;//TREAM_DRAW 只会向缓冲区写入一次数据 须要绘制若干次const GLenum DYNAMIC_DRAW = 0x88E8;//DYNAMIC_DRAW 会向缓冲区对象中屡次写入数据 并绘制很屡次void bufferData(GLenum target, //下面 target BufferSource? data, //data 类型化数组 比方:Float32Array GLenum usage); //下面 usage// 获取由 name 参数指定的 attribute 变量存储地址GLint getAttribLocation(WebGLProgram program, //program 指定蕴含顶点或者片元着色器的程序对象 DOMString name); //name 获取其存储的 attribute 变量名称,最大长度256字节// 将数据传给由index参数指定的attribute变量void vertexAttrib1f(GLuint index, GLfloat x);void vertexAttrib2f(GLuint index, GLfloat x, GLfloat y);void vertexAttrib3f(GLuint index, GLfloat x, GLfloat y, GLfloat z);void vertexAttrib4f(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w);// 接管参数为 Float32Array 数组void vertexAttrib1fv(GLuint index, Float32List values);void vertexAttrib2fv(GLuint index, Float32List values);void vertexAttrib3fv(GLuint index, Float32List values);void vertexAttrib4fv(GLuint index, Float32List values);// 数据类型// vertexAttribPointer 中参数type的取值const GLenum BYTE = 0x1400;const GLenum UNSIGNED_BYTE = 0x1401;const GLenum SHORT = 0x1402;const GLenum UNSIGNED_SHORT = 0x1403;const GLenum INT = 0x1404;const GLenum UNSIGNED_INT = 0x1405;const GLenum FLOAT = 0x1406; //罕用// 将绑定到ARRAY_BUFFER的缓冲区对象调配给index指定的attribute变量void vertexAttribPointer(GLuint index, //指向attribute变量 GLint size, //指定缓冲区中每个顶点重量的个数 xyz的话就3 rgba的话就4 GLenum type, //数据格式 见下面的枚举 GLboolean normalized, //是否将浮点型数据归一化到[0, 1]或者[-1, 1]区间 false GLsizei stride, //指定相邻两个顶点之间的字节数 FSIZE*5 GLintptr offset);//指定缓冲区对象中的偏移量 单位字节 能够利用这个偏移量赋值多个attribute // 开启index对应的attribute对象// 开启后不能通过 vertexAttrib[1234]f 传值void enableVertexAttribArray(GLuint index);//指向attribute变量// 敞开index对应的attribute对象void disableVertexAttribArray(GLuint index);//指向attribute变量// 删除参数buffer示意的缓冲区对象// @param buffer 缓冲区对象 由createBuffer创立void deleteBuffer(WebGLBuffer? buffer);Example: var verticesColors = new Float32Array([ 0.0, 0.5, 1.0, 0.0, 0.0, -0.5, -0.5, 0.0, 1.0, 0.0, 0.5, -0.5, 0.0, 0.0, 1.0, ]); var n = 3; var vertexColorBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, vertexColorBuffer); //bind的是createbuffer gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW); //data是数组 data是float32array var FSIZE = verticesColors.BYTES_PER_ELEMENT; var a_Position = gl.getAttribLocation(gl.program, 'a_Position');//用字符来获取缓冲区 gl.enableVertexAttribArray(a_Position); //启动吗? gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 5, 0);//数据指向缓冲区 2是读取数 FSIZE * 5是总数 FSIZE * 2是偏移数 var a_Color = gl.getAttribLocation(gl.program, 'a_Color'); gl.enableVertexAttribArray(a_Color); // Enable the assignment of the buffer object gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 5, FSIZE * 2); //3 是读取数 FSIZE * 5是总数 FSIZE * 2是偏移数 drawcall();var verticesColors = new Float32Array([ // Vertex coordinates and color 1.0,1.0,1.0,1.0,1.0,1.0, // v0 White -1.0,1.0,1.0,1.0,0.0,1.0, // v1 Magenta -1.0,-1.0,1.0,1.0,0.0,0.0, // v2 Red 1.0,-1.0,1.0,1.0,1.0,0.0, // v3 Yellow 1.0,-1.0,-1.0,0.0,1.0,0.0, // v4 Green 1.0,1.0,-1.0,0.0,1.0,1.0, // v5 Cyan -1.0,1.0,-1.0,0.0,0.0,1.0, // v6 Blue -1.0,-1.0,-1.0,0.0,0.0,0.0 // v7 Black]);// Indices of the verticesvar indices = new Uint8Array([0,1,2,0,2,3, // front 0,3,4,0,4,5, // right 0,5,6,0,6,1, // up 1,6,7,1,7,2, // left 7,4,3,7,3,2, // down 4,7,6,4,6,5 // back]);var vertexColorBuffer = gl.createBuffer();gl.bindBuffer(gl.ARRAY_BUFFER, vertexColorBuffer);gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW);var FSIZE = verticesColors.BYTES_PER_ELEMENT;var a_Position = gl.getAttribLocation(gl.program, "a_Position");gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, FSIZE * 6, 0);gl.enableVertexAttribArray(a_Position);var a_Color = gl.getAttribLocation(gl.program, "a_Color");gl.vertexAttribPointer(a_Color,3,gl.FLOAT,false,FSIZE * 6,FSIZE * 3);gl.enableVertexAttribArray(a_Color);//index的话 必须通过这个apivar indexBuffer = gl.createBuffer(); //index gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); //bind buffergl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW); // bufferdatagl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0); //这里的n是index.count var a_Position = gl.getAttribLocation(gl.program, 'a_Position'); gl.vertexAttrib3f(a_Position, 0.0, 0.0, 0.0); //感觉很少用 这个是不必指向的 gl.clearColor(0.0, 0.0, 0.0, 1.0); gl.clear(gl.COLOR_BUFFER_BIT); gl.drawArrays(gl.POINTS, 0, 1);var vertex = [ 5, -30, 0, 50, -30, 0, 50, 30, 0, 5, 30, 0];vertexBuffer2 = gl.createBuffer();vao2 = ext.createVertexArrayOES();ext.bindVertexArrayOES(vao2);gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer2);gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertex), gl.STATIC_DRAW);var index = [ 0, 1, 2, 0, 2, 3];indexBuffer2 = gl.createBuffer();gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer2);gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(index), gl.STATIC_DRAW);gl.vertexAttribPointer(aVertexPosition, 3, gl.FLOAT, false, 0, 0);gl.enableVertexAttribArray(aVertexPosition);ext.bindVertexArrayOES(null);gl.clear(gl.COLOR_BUFFER_BIT);ext.bindVertexArrayOES(vao1);draw();ext.bindVertexArrayOES(vao2);draw();ext.deleteVertexArrayOES(vao);着色器 uniform 相干// 获取指定名称的 uniform 变量存储地位WebGLUniformLocation? getUniformLocation(WebGLProgram program, DOMString name);//指定想要获取其存储地位的uniform变量名称 最大长度256字节// 将数据传给location指定的uniform变量void uniform1f(WebGLUniformLocation? location, GLfloat x);void uniform2f(WebGLUniformLocation? location, GLfloat x, GLfloat y);void uniform3f(WebGLUniformLocation? location, GLfloat x, GLfloat y, GLfloat z);void uniform4f(WebGLUniformLocation? location, GLfloat x, GLfloat y, GLfloat z, GLfloat w);void uniform1i(WebGLUniformLocation? location, GLint x);void uniform2i(WebGLUniformLocation? location, GLint x, GLint y);void uniform3i(WebGLUniformLocation? location, GLint x, GLint y, GLint z);void uniform4i(WebGLUniformLocation? location, GLint x, GLint y, GLint z, GLint w);void uniform1fv(WebGLUniformLocation? location, Float32List v);void uniform2fv(WebGLUniformLocation? location, Float32List v);void uniform3fv(WebGLUniformLocation? location, Float32List v);void uniform4fv(WebGLUniformLocation? location, Float32List v);void uniform1iv(WebGLUniformLocation? location, Int32List v);void uniform2iv(WebGLUniformLocation? location, Int32List v);void uniform3iv(WebGLUniformLocation? location, Int32List v);void uniform4iv(WebGLUniformLocation? location, Int32List v);// @param 是否对矩阵进行转置 默认 false 在webgl中必须是falsevoid uniformMatrix2fv(WebGLUniformLocation? location, GLboolean transpose, Float32List value);void uniformMatrix3fv(WebGLUniformLocation? location, GLboolean transpose, Float32List value);void uniformMatrix4fv(WebGLUniformLocation? location, GLboolean transpose, Float32List value);Example: var xformMatrix = new Float32Array([ cosB, sinB, 0.0, 0.0, -sinB, cosB, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ]); var u_xformMatrix = gl.getUniformLocation(gl.program, 'u_xformMatrix'); gl.uniformMatrix4fv(u_xformMatrix, false, xformMatrix); drawcall();着色器 texture 相干// 将图像RGB色彩值每一个重量乘以A 默认falseconst GLenum UNPACK_PREMULTIPLY_ALPHA_WEBGL = 0x9241; // 创立纹理对象以存储纹理图像WebGLTexture? createTexture();// pixelStorei 中参数pname取值const GLenum UNPACK_FLIP_Y_WEBGL = 0x9240; // 对图像进行Y轴反转,默认false// 应用 pname 和 param 指定的形式加载失去的图像void pixelStorei(GLenum pname, //下面 GLint param); //非0为true、0为false 必须是整数 个别是1 // activeTexture 办法应用的枚举常量const GLenum TEXTURE0 = 0x84C0;const GLenum TEXTURE1 = 0x84C1; //gl.TEXTURE1 =gl.TEXTURE0+1const GLenum TEXTURE31 = 0x84DF;void activeTexture(GLenum texture); //下面//target 参数const GLenum TEXTURE_2D = 0x0DE1; //立体纹理const GLenum TEXTURE_CUBE_MAP = 0x8513; //立方体纹理// 开启 texture 指定的纹理对象,并将其绑定到 target 上。 // 如果曾经通过 gl.activeTexture 激活了某个纹理单元,则纹理对象也会绑定到这个纹理单元上void bindTexture(GLenum target, //下面 WebGLTexture? texture); //createTexture // pname 参数const GLenum TEXTURE_MAG_FILTER = 0x2800;const GLenum TEXTURE_MIN_FILTER = 0x2801;const GLenum TEXTURE_WRAP_S = 0x2802;const GLenum TEXTURE_WRAP_T = 0x2803;//param 参数const GLenum NEAREST = 0x2600;const GLenum LINEAR = 0x2601;const GLenum NEAREST_MIPMAP_NEAREST = 0x2700;const GLenum LINEAR_MIPMAP_NEAREST = 0x2701;const GLenum NEAREST_MIPMAP_LINEAR = 0x2702;const GLenum LINEAR_MIPMAP_LINEAR = 0x2703;const GLenum REPEAT = 0x2901; // 平铺式的反复纹理const GLenum CLAMP_TO_EDGE = 0x812F; // 镜像对称式的反复纹理const GLenum MIRRORED_REPEAT = 0x8370; // 应用纹理图像的边缘值// 配置纹理,将param值赋给绑定到指标的纹理对象的pname参数上void texParameterf(GLenum target,//下面 GLenum pname, //下面 GLfloat param); //下面void texParameteri(GLenum target, GLenum pname, GLint param);// texImage2D 的 internalformat 参数const GLenum ALPHA = 0x1906; //只读取Alpha通道。const GLenum RGB = 0x1907; //读取RGB三个通道const GLenum RGBA = 0x1908; //读取RGBA四个通道。const GLenum LUMINANCE = 0x1909; //把每个通道视为亮度,alpha取1.0。const GLenum LUMINANCE_ALPHA = 0x190A; //每个通道视为 亮度/alpha。// texImage2D 的 type 参数const GLenum UNSIGNED_BYTE; //RGBA每个通道应用8位。const GLenum UNSIGNED_SHORT_4_4_4_4 = 0x8033; // RGBA RGBA每个通道应用4位。const GLenum UNSIGNED_SHORT_5_5_5_1 = 0x8034; // RGBA 红色5位、绿色5位、 蓝色5位、alpha用1位。const GLenum UNSIGNED_SHORT_5_6_5 = 0x8363; // RGB 红色5位、绿色6位,蓝色5位。// 将 source 指定的图像调配给绑定到指标上的纹理对象void texImage2D(GLenum target, //下面 GLint level, //传入0 (该参数是为金字塔纹理筹备的) GLint internalformat, //图像的外部格局 见上枚举 GLenum format, //format 纹理数据的格局 必须应用与 internalformat 雷同的值 GLenum type, //纹理数据的类型 TexImageSource source); //source 蕴含纹理图像的Image对象 // 为 WebGLTexture 对象生成一组mip纹理映射。void generateMipmap(GLenum target);//下面// 应用texture删除纹理对象void deleteTexture(WebGLTexture? texture); //createTextureExample:var u_Sampler = gl.getUniformLocation(gl.program, 'u_Sampler');//var a_Position = gl.getAttribLocation(gl.program, 'a_Position');//用字符来getvar image = new Image();image.onload = function(){ loadTexture(gl, n, texture, u_Sampler, image); };image.src = '../resources/sky.jpg';function loadTexture(gl, n, texture, u_Sampler, image) { var texture = gl.createTexture(); //创立 var vertexColorBuffer = gl.createBuffer(); gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1); // 倒置 gl.activeTexture(gl.TEXTURE0); //gl.enableVertexAttribArray(a_Position); //容许 gl.bindTexture(gl.TEXTURE_2D, texture); // gl.bindBuffer(gl.ARRAY_BUFFER, vertexColorBuffer); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); //设置参数 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.MIRRORED_REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image);//数据指向缓冲区 gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW); //data是数组 data是float32array gl.generateMipmap(gl.TEXTURE_2D); gl.uniform1i(u_Sampler, 0);//讲数据指向usampler gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 5, 0);//数据指向缓冲区 gl.clear(gl.COLOR_BUFFER_BIT); // Clear <canvas> gl.drawArrays(gl.TRIANGLE_STRIP, 0, n); // Draw the rectangle}帧缓冲区// 创立渲染缓冲区对象WebGLRenderbuffer? createRenderbuffer();//target 必须是 gl.RENDERBUFFERconst GLenum RENDERBUFFER = 0x8D41;//renderbuffer// 将 renderbuffer 指定的渲染缓冲区对象绑定在target指标上 createRenderbuffer// 如果 renderbuffer 为 null 则将曾经绑定在target指标上的渲染缓冲区对象解除绑定void bindRenderbuffer(GLenum target, //下面 WebGLRenderbuffer? renderbuffer); //下面 //internalformat 示意渲染缓冲区将代替色彩缓冲区 const GLenum RGBA4 = 0x8056;const GLenum RGB5_A1 = 0x8057;const GLenum RGB565 = 0x8D62;const GLenum DEPTH_COMPONENT16 = 0x81A5; // 示意渲染缓冲区将代替深度缓冲区const GLenum STENCIL_INDEX8 = 0x8D48; // 示意渲染缓冲区将代替模板缓冲区// 创立并初始化渲染缓冲区的数据区void renderbufferStorage(GLenum target, //下面 GLenum internalformat, //下面 GLsizei width, //指定渲染缓冲区的宽度和高度 单位像素 GLsizei height); // 删除渲染缓冲区对象void deleteRenderbuffer(WebGLRenderbuffer? renderbuffer);//createRenderbuffer// 创立帧缓冲区对象WebGLFramebuffer? createFramebuffer();// 绑定帧缓冲区// targetconst GLenum FRAMEBUFFER = 0x8D40;void bindFramebuffer(GLenum target, //下面 WebGLFramebuffer? framebuffer); //createFramebuffer// attachment 设置纹理为 attachment 附件const GLenum COLOR_ATTACHMENT0 = 0x8CE0; //色彩附件const GLenum DEPTH_ATTACHMENT = 0x8D00; //深度附件const GLenum STENCIL_ATTACHMENT = 0x8D20; //模板附件const GLenum DEPTH_STENCIL_ATTACHMENT = 0x821A;// textargetconst GLenum TEXTURE_2D = 0x0DE1; //立体纹理const GLenum TEXTURE_CUBE_MAP = 0x8513; //立方体纹理// level 0void framebufferTexture2D(GLenum target, //下面 GLenum attachment, //下面 GLenum textarget, //下面 WebGLTexture? texture, //下面 GLint level); //下面 // 设置渲染缓冲区对象为 attachment 附件// renderbuffertarget gl.RENDERBUFFERvoid framebufferRenderbuffer(GLenum target, //下面 GLenum attachment, //下面 GLenum renderbuffertarget, //下面 WebGLRenderbuffer? renderbuffer); //createFramebuffer// 删除帧缓冲区对象void deleteFramebuffer(WebGLFramebuffer? framebuffer);// 查看帧缓冲区GLenum checkFramebufferStatus(GLenum target); //下面Example: var texture = gl.createTexture(); // 纹理创立 gl.bindTexture(gl.TEXTURE_2D, texture); // Bind the object to target gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, OFFSCREEN_WIDTH, OFFSCREEN_HEIGHT, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);//最初是null gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); var depthBuffer = gl.createRenderbuffer(); // 创立渲染缓冲区 gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer); // Bind the object to target gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, OFFSCREEN_WIDTH, OFFSCREEN_HEIGHT); var framebuffer = gl.createFramebuffer(); //帧缓冲区 framebuffer.texture = texture; // Store the texture object gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); //帧缓冲区 贴图 必须要这个 gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer); //帧缓冲区 渲染缓冲区 var e = gl.checkFramebufferStatus(gl.FRAMEBUFFER); //判断是否正确配置COLOR_ATTACHMENT0 = RGBA/UNSIGNED_BYTE textureCOLOR_ATTACHMENT0 = RGBA/UNSIGNED_BYTE texture + DEPTH_ATTACHMENT = DEPTH_COMPONENT16 renderbufferCOLOR_ATTACHMENT0 = RGBA/UNSIGNED_BYTE texture + DEPTH_STENCIL_ATTACHMENT = DEPTH_STENCIL renderbuffer贴图相干// 指定一个为压缩格局的2D纹理图片。void compressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, [AllowShared] ArrayBufferView data);// 指定一个为压缩格局的2D纹理子图片。如果你的3D场景同时应用的纹理较多或者其余因素导致内存耗费较大,同时须要长时间运行,那么你能够思考应用压缩纹理void compressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, [AllowShared] ArrayBufferView data);// 复制2D纹理图片。void copyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border);//gl.copyTexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 0, 0, 512, 512, 0);// 复制2D纹理子图片。void copyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height);// gl.copyTexSubImage2D(gl.TEXTURE_2D, 0, 0,0, 0, 0, 16, 16);// 更新以后 WebGLTexture 的子矩形。void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, [AllowShared] ArrayBufferView? pixels);void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLenum format, GLenum type, TexImageSource source); // May throw DOMExceptionExample:gl.bindTexture(gl.TEXTURE_2D, this.screen);gl.copyTexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 0, 0, this.width, this.height, 0);启用性能// enable disable 的 cap 参数const GLenum CULL_FACE = 0x0B44;// 混合const GLenum BLEND = 0x0BE2;const GLenum DITHER = 0x0BD0;const GLenum STENCIL_TEST = 0x0B90;// 暗藏面打消const GLenum DEPTH_TEST = 0x0B71;const GLenum SCISSOR_TEST = 0x0C11;// 多边形位移 (解决深度抵触问题)const GLenum POLYGON_OFFSET_FILL = 0x8037;const GLenum SAMPLE_ALPHA_TO_COVERAGE = 0x809E;const GLenum SAMPLE_COVERAGE = 0x80A0;// 启用性能void enable(GLenum cap);// 敞开性能void disable(GLenum cap);// 解决深度抵触// 指定加到每个顶点绘制后Z值上的偏移量,偏移量依照公式 m * factor + r * units 计算,其中m代表顶点所在外表// 绝对于观察者的实现角度,而r示意硬件可能辨别两个Z值之差的最小值void polygonOffset(GLfloat factor, GLfloat units);// 尽管下面的办法能够应用,然而在渲染器中用起来还是很麻烦的。// 解决深度抵触有更好的形式,就是放大远近裁剪面的间隔Example:gl.enable(gl.POLYGON_OFFSET_FILL);gl.drawArrays(gl.TRIANGLES, 0, n/2); // The green trianglegl.polygonOffset(1.0, 1.0); // Set the polygon offsetgl.drawArrays(gl.TRIANGLES, n/2, n/2); // The yellow triangle着色器相干// type 参数const GLenum FRAGMENT_SHADER = 0x8B30;const GLenum VERTEX_SHADER = 0x8B31;WebGLShader? createShader(GLenum type); //下面// 将 source 指定的字符串模式的代码传入shader指定的着色器 ..如果之前曾经向shader传入了代码 旧的代码就会被替换掉void shaderSource(WebGLShader shader, //createShader DOMString source); //字符串 // 编译 shader 指定的着色器中的源代码void compileShader(WebGLShader shader); //createShader// pname 参数const GLenum SHADER_TYPE = 0x8B4F;const GLenum DELETE_STATUS = 0x8B80;const GLenum COMPILE_STATUS = 0x8B81;any getShaderParameter(WebGLShader shader, //createShader GLenum pname); //下面 // 如果 getShaderParameter(shader, COMPILE_STATUS) 返回false // 则能够通过 此函数获取 指定shader 的信息日志DOMString? getShaderInfoLog(WebGLShader shader); //createShader// 删除 shader 指定的着色器对象void deleteShader(WebGLShader? shader); //createShaderExample: var shader = gl.createShader(type); gl.shaderSource(shader, source); gl.compileShader(shader); var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);着色器程序相干// pname 参数// 着色器相干 章节已定义const GLenum DELETE_STATUS; const GLenum LINK_STATUS = 0x8B82;const GLenum VALIDATE_STATUS = 0x8B83;const GLenum ATTACHED_SHADERS = 0x8B85;const GLenum ACTIVE_UNIFORMS = 0x8B86;const GLenum ACTIVE_ATTRIBUTES = 0x8B89;// 创立着色器程序对象WebGLProgram? createProgram();// 将 shader 指定的着色器对象调配给 program 指定的程序对象void attachShader(WebGLProgram program, //createProgram WebGLShader shader); //createShader// 勾销 shader 指定的着色器对 program 对象的调配void detachShader(WebGLProgram program, //createProgram WebGLShader shader); //createShader// 连贯 program 指定的程序对象中的着色器void linkProgram(WebGLProgram program); //createProgram// 获取 program 指定的程序对象中 pname 指定的参数信息any getProgramParameter(WebGLProgram program, //createProgram GLenum pname); //下面 // 如果通过 getProgramParameter(LINK_STATUS) 取得返回值 为 false// 能够通过 此函数获取 program 指定的程序对象的信息日志DOMString? getProgramInfoLog(WebGLProgram program); //createProgram// 验证 WebGLProgram void validateProgram(WebGLProgram program); //createProgram// 告知 WEBGL 零碎绘制时应用的 program 对象void useProgram(WebGLProgram? program); //createProgram// 删除着色器程序对象void deleteProgram(WebGLProgram? program); //createProgramExample:gl.useProgram(program)const program = gl.createProgram();gl.attacheShader(program, vertexShader);gl.attacheShader(program, fragmentShader);gl.linkProgram(program);if(!gl.getProgramParameter(program, gl.LINK_STATUS)){ var info = gl.getProgramInfoLog(program); throw new Error('Could not compile WebGL program. \n\n ' + info);}gl.useProgram(SHADER_PROGRAM);扩大通过扩大基本上能使 WebGL1 领有 WebGL2 的能力。获取扩大以及扩大反对信息 ...

May 19, 2021 · 16 min · jiezi

关于webgl:ecahrtgl实现中国版块仿三维地图

首先申明 mapName:'china',maptype:1而后调用办法,传参1和2有不同的成果 getPeo (status) { function NumDescSort(a, b) { return b.value - a.value; } function NumAsceSort(a, b) { return a.value - b.value; } this.$nextTick(() => { this.myChart = echarts.init(document.getElementById("mapPeople")); echarts.registerMap("china", china); var geoCoordMap = {}; var alirl = []; var conData = []; var matchCount = 0 var maxVal = 0 var convertData // var distance = 0 if (status == 1) { geoCoordMap = { 昭通:[103.715, 27.3436], 毕节市:[105.2812, 27.3036], 红河哈尼族彝族自治州: [103.715, 27.3436], 嘉兴市:[120.7524, 30.7518], 金华市: [119.6424, 29.0982], 昆明市: [102.8246, 24.8898], 凉山彝族自治州: [102.265, 27.887], 六盘水市: [104.819, 26.5972], 泸州市: [105.448, 28.8876], 攀枝花市: [101.7168, 26.5862], 普洱市: [103.715, 27.3436], 曲靖市: [103.7922, 25.4888], 西双版纳傣族自治州: [103.715, 27.3436], 宜宾市: [104.6212, 28.7698], 玉溪市: [102.5394, 24.3522], 自贡市: [104.7652, 29.3634], 北京: [116.4004, 39.9028], 成都市: [104.0618, 30.661], 东莞市: [113.7468, 23.0218], 广州市: [113.2592, 23.131], 贵阳市: [106.6268, 26.6514], 杭州市: [120.2039, 30.2503], 乐山市: [103.7626, 29.5646], 宁波市: [121.6242, 29.8648], 厦门市: [118.0826, 24.4816], 上海: [121.4686, 31.232], 深圳市: [114.054, 22.5462], 苏州市: [120.5778, 31.2996], 台州市: [121.4126, 28.6618], 温州市: [120.6718, 27.9994], 重庆: [106.545, 29.5658], }; alirl = [ [ [105.2812, 27.3036], [103.715, 27.3436], ], [ [103.715, 27.3436], [103.715, 27.3436], ], [ [120.7524, 30.7518], [103.715, 27.3436], ], [ [119.6424, 29.0982], [103.715, 27.3436], ], [ [102.8246, 24.8898], [103.715, 27.3436], ], [ [102.265, 27.887], [103.715, 27.3436], ], [ [104.819, 26.5972], [103.715, 27.3436], ], [ [105.448, 28.8876], [103.715, 27.3436], ], [ [101.7168, 26.5862], [103.715, 27.3436], ], [ [103.715, 27.3436], [103.715, 27.3436], ], [ [103.7922, 25.4888], [103.715, 27.3436], ], [ [103.715, 27.3436], [103.715, 27.3436], ], [ [104.6212, 28.7698], [103.715, 27.3436], ], [ [102.5394, 24.3522], [103.715, 27.3436], ], [ [104.7652, 29.3634], [103.715, 27.3436], ], [ [103.715, 27.3436], [116.4004, 39.9028], ], [ [103.715, 27.3436], [105.2812, 27.3036], ], [ [103.715, 27.3436], [104.0618, 30.661], ], [ [103.715, 27.3436], [113.7468, 23.0218], ], [ [103.715, 27.3436], [113.2592, 23.131], ], [ [103.715, 27.3436], [106.6268, 26.6514], ], [ [103.715, 27.3436], [120.2039, 30.2503], ], [ [103.715, 27.3436], [120.7524, 30.7518], ], [ [103.715, 27.3436], [119.6424, 29.0982], ], [ [103.715, 27.3436], [102.8246, 24.8898], ], [ [103.715, 27.3436], [103.7626, 29.5646], ], [ [103.715, 27.3436], [102.265, 27.887], ], [ [103.715, 27.3436], [105.448, 28.8876], ], [ [103.715, 27.3436], [121.6242, 29.8648], ], [ [103.715, 27.3436], [103.7922, 25.4888], ], [ [103.715, 27.3436], [118.0826, 24.4816], ], [ [103.715, 27.3436], [121.4686, 31.232], ], [ [103.715, 27.3436], [114.054, 22.5462], ], [ [103.715, 27.3436], [120.5778, 31.2996], ], [ [103.715, 27.3436], [121.4126, 28.6618], ], [ [103.715, 27.3436], [120.6718, 27.9994], ], [ [103.715, 27.3436], [104.6212, 28.7698], ], [ [103.715, 27.3436], [102.5394, 24.3522], ], [ [103.715, 27.3436], [106.545, 29.5658], ], ]; conData = [ { name: "毕节市", value: 535395 }, { name: "红河哈尼族彝族自治州", value: 3458 }, { name: "嘉兴市", value: 118863 }, { name: "金华市", value: 11673 }, { name: "昆明市", value: 680763 }, { name: "凉山彝族自治州", value: 800617 }, { name: "六盘水市", value: 311 }, { name: "泸州市", value: 97157 }, { name: "攀枝花市", value: 997 }, { name: "普洱市", value: 40 }, { name: "曲靖市", value: 241885 }, { name: "西双版纳傣族自治州", value: 754 }, { name: "宜宾市", value: 832551 }, { name: "玉溪市", value: 13917 }, { name: "自贡市", value: 1959 }, { name: "北京", value: 58749 }, { name: "毕节市", value: 608996 }, { name: "成都市", value: 87722 }, { name: "东莞市", value: 770 }, { name: "广州市", value: 67 }, { name: "贵阳市", value: 22192 }, { name: "杭州市", value: 6387 }, { name: "嘉兴市", value: 135817 }, { name: "金华市", value: 185891 }, { name: "昆明市", value: 808905 }, { name: "乐山市", value: 1151 }, { name: "凉山彝族自治州", value: 814000 }, { name: "泸州市", value: 95801 }, { name: "宁波市", value: 70101 }, { name: "曲靖市", value: 217802 }, { name: "厦门市", value: 25 }, { name: "上海", value: 13156 }, { name: "深圳市", value: 1811 }, { name: "苏州市", value: 2218 }, { name: "台州市", value: 30632 }, { name: "温州市", value: 42829 }, { name: "宜宾市", value: 810975 }, { name: "玉溪市", value: 5185 }, { name: "重庆", value: 97341 }, ]; matchCount = 20; maxVal = 832551; convertData = function (data) { var res = []; for (var i = 0; i < data.length; i++) { var geoCoord = geoCoordMap[data[i].name]; if (geoCoord) { res.push({ name: data[i].name, value: geoCoord.concat(data[i].value), }); } } return res; }; var option = { backgroundColor: "transparent", tooltip: { show: true, trigger: "item" }, visualMap: [ { show:false, type: "continuous", seriesIndex: 0, calculable: true, max: maxVal, inRange: { color: ["#87aa66", "#eba438", "#d94d4c"], }, }, ], geo3D: { map: "china", roam: true, regionHeight: 1, itemStyle: { color: "#0066aa", opacity: 1, borderWidth: 0.4, borderColor: "#000", }, aspectScale: 0.8, label: { show: true, textStyle: { color: "#fff", //地图初始化区域字体色彩 fontSize: 14, opacity: 1, backgroundColor: "rgba(0,23,11,0)", }, }, emphasis: { //当鼠标放上去 地区区域是否显示名称 label: { show: true, textStyle: { color: "#fff", fontSize: 20, backgroundColor: "rgba(0,23,11,0)", }, }, }, light: { //光照暗影 main: { color: "#fff", //光照色彩 intensity: 1.2, //光照强度 //shadowQuality: 'high', //暗影亮度 shadow: false, //是否显示暗影 alpha: 55, beta: 10, }, ambient: { intensity: 0.3, }, }, viewControl: { projection: "perspective", autoRotate: false, // 主动旋转 distance: 99, // 视距 }, }, series: [ //柱状图 { name: "", type: "bar3D", coordinateSystem: "geo3D", barSize: 1, //柱子粗细 shading: "lambert", opacity: 1, bevelSize: 0.3, minHeight: 0.5, label: { show: false, formatter: "{b}", }, data: convertData(conData), }, //画线 { type: "lines3D", coordinateSystem: "geo3D", effect: { show: true, trailWidth: 2, trailOpacity: 0.6, trailLength: 0.5, constantSpeed: 8, }, blendMode: "lighter", lineStyle: { width: 0.2, opacity: 0.05, }, data: alirl, }, ], }; this.myChart.setOption(option); } else { debugger if (this.mapName == "china") { echarts.registerMap("china", china); } else { pass } matchCount = 20; debugger if (this.maptype == 1) { geoCoordMap = { 昭通: [103.715, 27.3436], 北京: [116.4004, 39.9028], 上海: [121.4686, 31.232], 重庆: [106.545, 29.5658], 成都: [104.0618, 30.661], 深圳: [114.054, 22.5462], 东莞: [113.7468, 23.0218], 广州: [113.2592, 23.131], 宜宾: [104.6212, 28.7698], 杭州: [120.2039, 30.2503], 枣庄: [117.3252, 34.814], 中山: [113.3866, 22.5198], 苏州: [120.5778, 31.2996], 天津: [117.1966, 39.085], 贵阳: [106.6268, 26.6514], 邯郸: [114.4844, 36.6086], 佛山: [113.116, 23.0234], 无锡: [120.306878, 31.492395], 武汉: [114.2984, 30.5954], 毕节: [105.2812, 27.3036], 南京: [118.7912, 32.0606], 西安: [108.934, 34.3354], 济南: [117.1134, 36.6526], 六盘水: [104.819, 26.5972], 郑州: [113.6188, 34.7468], 泸州: [105.448, 28.8876], 青岛: [120.3766, 36.0656], 淄博: [118.0522, 36.8052], 邢台: [114.4972, 37.0658], 聊城: [115.9784, 36.4564], 长沙: [112.9324, 28.2296], 宁波: [121.6242, 29.8648], 温州: [120.6718, 27.9994], 南宁: [108.362, 22.8198], 内江: [105.0546, 29.5828], 厦门: [118.0826, 24.4816], 常州: [119.9688, 31.8126], 泰安: [117.1164, 36.1936], 合肥: [117.2232, 31.8242], 石家庄: [114.509, 38.0408], 金华: [119.6424, 29.0982], 惠州: [114.411, 23.1136], 嘉兴: [120.7524, 30.7518], 吉林: [126.5422, 43.8366], 沧州: [116.8324, 38.304], 广安: [106.6366, 30.472], 乐山: [103.7626, 29.5646], 兰州: [103.8314, 36.0606], 自贡: [104.7652, 29.3634], 南通: [120.8878, 31.9824], 福州: [119.2896, 26.0762], 大连: [121.599, 38.9146], 泉州: [118.6658, 24.8776], 遵义: [106.9224, 27.7296], 沈阳: [123.459625, 41.672542], 向阳: [120.4466, 41.572], 珠海: [113.5712, 22.275], 潍坊: [119.1562, 36.7052], 济宁: [116.583, 35.4142], 攀枝花: [101.7168, 26.5862], 雅安: [102.995, 29.9888], 台州: [121.4126, 28.6618], 廊坊: [116.6988, 39.5192], 南昌: [115.8586, 28.6842], 临沂: [118.3418, 35.0518], 哈尔滨: [126.5268, 45.8022], 徐州: [117.2792, 34.2052], 盐城: [120.1378, 33.3674], 玉林: [110.1598, 22.6394], 扬州: [119.4062, 32.3954], 安顺: [105.9348, 26.249], 保定: [115.4586, 38.8846], 江门: [113.0754, 22.5808], 资阳: [104.6262, 30.1316], 延安: [109.4848, 36.5856], 健康: [109.0214, 32.6874], 太原: [112.5434, 37.8696], 衡水: [115.6916, 37.7342], 烟台: [121.4256, 37.4648], 乌鲁木齐: [87.6142, 43.8238], 绍兴: [120.5756, 30.0068], 德州: [116.304, 37.4444], 昌都: [97.1774, 31.136], 肇庆: [112.4598, 23.051], 绵阳: [104.7404, 31.4682], 长春: [125.3156, 43.8164], 西宁: [101.775, 36.6174], 河源: [114.6964, 23.7482], 洛阳: [112.4438, 34.6234], 渭南: [109.506, 34.503], 商洛: [109.9336, 33.8708], 丽水: [119.9084, 28.4504], 宜昌: [111.2776, 30.704], 宝鸡: [107.2332, 34.3634], 桂林: [110.1748, 25.2418], 遂宁: [105.5748, 30.5092], 汕头: [116.6768, 23.3566], 湛江: [110.3518, 21.273], 眉山: [103.8328, 30.0464], 景德镇: [117.1802, 29.2794], 吉安: [114.9806, 27.1246], 唐山: [118.1724, 39.6292], 湖州: [120.0996, 30.8672], 咸阳: [108.7062, 34.33], 汉中: [107.017, 33.068], 达州: [107.468, 31.212], 铜川: [108.9288, 34.9], 银川: [106.2268, 38.4856], 淮安: [119.0084, 33.6118], 莆田: [118.9954, 25.449], 铜仁: [109.1844, 27.7348], 镇江: [119.4504, 32.2032], 柳州: [109.4114, 24.3324], 德阳: [104.3878, 31.1354], 漳州: [117.6456, 24.5178], 海口: [110.194947, 20.046268], 三亚: [109.508, 18.2566], 日喀则: [88.881, 29.2778], 日照: [119.545, 35.394], 新乡: [113.9206, 35.3032], 广元: [105.8176, 32.4396], 山南: [91.7736, 29.2406], 岳阳: [113.1182, 29.3686], 榆林: [109.7354, 38.2864], 大庆: [125.0974, 46.5866], 泰州: [119.9168, 32.4572], 上饶: [117.962, 28.4478], 天水: [105.7196, 34.5806], 威海: [122.1146, 37.5124], 南阳: [112.5306, 32.995], 酒泉: [98.511, 39.7434], 安阳: [114.3804, 36.0974], 怀化: [109.9658, 27.5558], 韶关: [113.5914, 24.8138], 清远: [113.0442, 23.6876], 九江: [115.995, 29.7072], 菏泽: [115.4738, 35.2398], 河池: [108.0558, 24.6982], 东营: [118.654, 37.4224], 巴中: [106.744, 31.872], 芜湖: [118.4276, 31.3548], 亳州: [115.777, 33.8664], 邵阳: [111.4624, 27.241], 连云港: [119.2136, 34.5976], 秦皇岛: [119.594, 39.934], 淮南: [117.0172, 32.5822], 茂名: [110.9194, 21.6654], 宿迁: [118.266, 33.9646], 株洲: [113.1236, 27.8336], 大同: [113.2874, 40.0924], 白银: [104.1348, 36.5482], 承德: [117.9362, 40.976], 赣州: [114.9228, 25.838], 北海: [109.1096, 21.4628], 龙岩: [117.0274, 25.1028], 黄山: [118.3272, 29.7154], 潮州: [116.6166, 23.6596], 蚌埠: [117.3828, 32.9192], 马鞍山: [118.4724, 31.6166], 运城: [110.9898, 35.0264], 六安: [116.5084, 31.7398], 张家口: [114.8788, 40.8148], 新余: [114.9038, 27.8268], 商丘: [115.652, 34.4158], 衡阳: [112.5636, 26.9006], 安庆: [117.045, 30.518], 湘潭: [112.93, 27.8358], 郴州: [113.0326, 25.7954], 信阳: [114.0778, 32.1378], 鹰潭: [117.0482, 28.25], 阳江: [111.9776, 21.8602], 林芝: [94.3614, 29.652], 来宾: [109.2326, 23.735], 呼和浩特: [111.7352, 40.841], 包头: [109.8244, 40.6564], 荆州: [112.2336, 30.3364], 张家界: [110.475, 29.1196], 拉萨: [91.1718, 29.6546], 南平: [118.0968, 27.3688], 贵港: [109.5914, 23.116], 十堰: [110.7834, 32.6528], 揭阳: [116.367, 23.5522], 营口: [122.2266, 40.668], 宜春: [114.3834, 27.7946], 呼伦贝尔: [119.7578, 49.2122], 崇左: [107.366, 22.3766], 阜阳: [115.8094, 32.9012], 晋中: [112.7344, 37.6804], 衢州: [118.8678, 28.9428], 益阳: [112.3472, 28.583], 吕梁: [111.1356, 37.5172], 防城港: [108.3464, 21.6182], 丹东: [124.381, 40.1254], 通化: [125.9418, 41.7394], 萍乡: [113.8436, 27.635], 许昌: [113.842, 34.0352], 随州: [113.3762, 31.6918], 钦州: [108.6194, 21.9702], 晋城: [112.8348, 35.5026], 抚顺: [123.9578, 41.8794], 平顶山: [113.1854, 33.7702], 黄冈: [114.8738, 30.435], 百色: [106.6106, 23.8994], 铜陵: [117.806, 30.9508], 开封: [114.3038, 34.7984], 汕尾: [115.3668, 22.7906], 周口: [114.6902, 33.6282], 忻州: [112.7326, 38.4084], 四平: [124.3516, 43.1598], 焦作: [113.24, 35.222], 常德: [111.6948, 29.031], 永州: [111.606, 26.4252], 云浮: [112.039, 22.9176], 阳泉: [113.5772, 37.863], 襄阳: [112.129, 32.01], 娄底: [112.0126, 27.728], 三明: [117.6298, 26.2656], 锦州: [121.1256, 41.091], 滨州: [118.0164, 37.379], 宁德: [119.543, 26.6682], 临汾: [111.5148, 36.0818], 宣城: [118.7512, 30.9472], 通辽: [122.265, 43.6188], 阜新: [121.6524, 42.019], 驻马店: [114.0308, 32.9774], 贺州: [111.559559, 24.409395], 佳木斯: [130.3608, 46.8088], 齐齐哈尔: [123.9206, 47.3626], 荆门: [112.198, 31.036], 朔州: [112.4204, 39.3152], 乌海: [106.7898, 39.6542], 舟山: [122.103, 30.0176], 滁州: [118.3116, 32.3068], 梅州: [116.1178, 24.296], 克拉玛依: [84.866, 45.5916], 赤峰: [118.8922, 42.2518], 长治: [113.1088, 36.1938], 鞍山: [122.989, 41.1082], 濮阳: [115.0132, 35.7662], 辽源: [125.1374, 42.885], 定西: [104.6232, 35.5904], 牡丹江: [129.6298, 44.5572], 陇南: [104.9206, 33.394], 辽阳: [123.164, 41.2668], 黄石: [115.0312, 30.2018], 鄂尔多斯: [109.770944, 39.620142], 葫芦岛: [120.8384, 40.7186], 梧州: [111.2934, 23.481], 盘锦: [122.0662, 41.1248], 咸宁: [114.3162, 29.8438], 孝感: [113.9468, 30.9218], 鸡西: [130.9572, 45.2898], 伊春: [128.8348, 47.7238], 宿州: [116.9838, 33.6374], 松原: [124.8124, 45.1436], 绥化: [126.9748, 46.6348], 中卫: [105.1848, 37.5152], 淮北: [116.8046, 33.9578], 池州: [117.4788, 30.6612], 鹤壁: [114.286, 35.742], 七台河: [130.9292, 45.7842], 阿拉尔: [81.278327, 40.547623], 抚州: [116.3508, 27.9506], 平凉: [106.6972, 35.5298], 漯河: [114.012, 33.5826], 白山: [126.4204, 41.9408], 鄂州: [114.8896, 30.3934], 仙桃: [113.448444, 30.364899], 吐鲁番: [89.1844, 42.947], 黑河: [127.4938, 50.2446], 双鸭山: [131.1514, 46.6392], 凉山彝族自治州: [102.265, 27.887], 张掖: [100.46, 38.9334], 石嘴山: [106.376, 39.0168], 铁岭: [123.836, 42.2928], 白城: [122.8348, 45.6178], 本溪: [123.7606, 41.293], 庆阳: [107.6336, 35.739], 哈密: [93.5154, 42.8284], 嘉峪关: [98.2892, 39.774], 三门峡: [111.1962, 34.7746], 吴忠: [106.192, 37.9942], 乌兰察布: [113.121, 40.993], 武威: [102.6332, 37.9248], 金昌: [102.1874, 38.501], 天门: [113.160107, 30.665725], 潜江: [112.893716, 30.404192], 固原: [106.2746, 36.0072], 济源: [112.599893, 35.067444], 鹤岗: [130.3138, 47.3726], 海东: [102.4054, 36.4776], 巴彦淖尔: [107.419, 40.749], 石河子: [86.077816, 44.30313], 阿拉善盟: [105.6924, 38.8462], 儋州: [109.5754, 19.5238], 三沙: [112.334, 16.8322], 锡林郭勒盟: [116.0522, 43.9466], 兴安盟: [122.056, 46.079], 阿克苏地区: [80.262, 41.1688], 五家渠: [87.540332, 44.168152], 南充: [106.1072, 30.845], 图木舒克: [79.06902, 39.859708], 延边朝鲜族自治州: [129.498, 42.892], 黔南布依族苗族自治州: [107.5174, 26.255], 黔西南布依族苗族自治州: [104.8948, 25.088], 阿坝藏族羌族自治州: [102.2204, 31.9026], 甘孜藏族自治州: [101.9566, 30.0504], 黔东南苗族侗族自治州: [107.9772, 26.5856], 喀什地区: [75.9926, 39.467], 阿勒泰地区: [88.1364, 47.8536], 湘西土家族苗族自治州: [109.7336, 28.316], 大兴安岭地区: [124.219245, 52.451053], 和田地区: [79.9216, 37.11], 恩施土家族苗族自治州: [109.475, 30.2702], 阿里地区: [80.096, 32.503], 塔城地区: [82.9752, 46.7506], 那曲地区: [92.057, 31.4776], 甘南藏族自治州: [102.9076, 34.9804], 果洛藏族自治州: [100.2404, 34.4758], 昆玉: [79.288916, 37.209579], 昌吉回族自治州: [87.3054, 44.0184], 临夏回族自治州: [103.2092, 35.5928], 海北藏族自治州: [100.8986, 36.959], 海南藏族自治州: [100.6188, 36.2754], 玉树藏族自治州: [97.0074, 33.0064], 铁门关: [85.673337, 41.867431], 海西蒙古族藏族自治州: [97.3628, 37.3658], 博尔塔拉蒙古自治州: [82.067, 44.8926], 巴音郭楞蒙古自治州: [86.144, 41.7558], 克孜勒苏柯尔克孜自治州: [76.171, 39.7088], 伊犁哈萨克自治州: [81.334, 43.914], 神农架林区: [110.67091, 31.748498], 黄南藏族自治州: [102.0128, 35.5152], }; alirl = [ [ [116.4004, 39.9028], [103.715, 27.3436], ], [ [121.4686, 31.232], [103.715, 27.3436], ], [ [106.545, 29.5658], [103.715, 27.3436], ], [ [104.0618, 30.661], [103.715, 27.3436], ], [ [114.054, 22.5462], [103.715, 27.3436], ], [ [113.7468, 23.0218], [103.715, 27.3436], ], [ [113.2592, 23.131], [103.715, 27.3436], ], [ [104.6212, 28.7698], [103.715, 27.3436], ], [ [120.2039, 30.2503], [103.715, 27.3436], ], [ [117.3252, 34.814], [103.715, 27.3436], ], [ [113.3866, 22.5198], [103.715, 27.3436], ], [ [120.5778, 31.2996], [103.715, 27.3436], ], [ [117.1966, 39.085], [103.715, 27.3436], ], [ [106.6268, 26.6514], [103.715, 27.3436], ], [ [114.4844, 36.6086], [103.715, 27.3436], ], [ [113.116, 23.0234], [103.715, 27.3436], ], [ [120.306878, 31.492395], [103.715, 27.3436], ], [ [114.2984, 30.5954], [103.715, 27.3436], ], [ [105.2812, 27.3036], [103.715, 27.3436], ], [ [118.7912, 32.0606], [103.715, 27.3436], ], [ [103.715, 27.3436], [108.934, 34.3354], ], [ [117.1134, 36.6526], [103.715, 27.3436], ], [ [104.819, 26.5972], [103.715, 27.3436], ], [ [113.6188, 34.7468], [103.715, 27.3436], ], [ [105.448, 28.8876], [103.715, 27.3436], ], [ [120.3766, 36.0656], [103.715, 27.3436], ], [ [118.0522, 36.8052], [103.715, 27.3436], ], [ [114.4972, 37.0658], [103.715, 27.3436], ], [ [115.9784, 36.4564], [103.715, 27.3436], ], [ [112.9324, 28.2296], [103.715, 27.3436], ], [ [121.6242, 29.8648], [103.715, 27.3436], ], [ [120.6718, 27.9994], [103.715, 27.3436], ], [ [108.362, 22.8198], [103.715, 27.3436], ], [ [105.0546, 29.5828], [103.715, 27.3436], ], [ [118.0826, 24.4816], [103.715, 27.3436], ], [ [119.9688, 31.8126], [103.715, 27.3436], ], [ [117.1164, 36.1936], [103.715, 27.3436], ], [ [117.2232, 31.8242], [103.715, 27.3436], ], [ [114.509, 38.0408], [103.715, 27.3436], ], [ [119.6424, 29.0982], [103.715, 27.3436], ], [ [114.411, 23.1136], [103.715, 27.3436], ], [ [120.7524, 30.7518], [103.715, 27.3436], ], [ [126.5422, 43.8366], [103.715, 27.3436], ], [ [116.8324, 38.304], [103.715, 27.3436], ], [ [106.6366, 30.472], [103.715, 27.3436], ], [ [103.7626, 29.5646], [103.715, 27.3436], ], [ [103.715, 27.3436], [103.8314, 36.0606], ], [ [104.7652, 29.3634], [103.715, 27.3436], ], [ [120.8878, 31.9824], [103.715, 27.3436], ], [ [119.2896, 26.0762], [103.715, 27.3436], ], [ [121.599, 38.9146], [103.715, 27.3436], ], [ [118.6658, 24.8776], [103.715, 27.3436], ], [ [106.9224, 27.7296], [103.715, 27.3436], ], [ [123.459625, 41.672542], [103.715, 27.3436], ], [ [120.4466, 41.572], [103.715, 27.3436], ], [ [113.5712, 22.275], [103.715, 27.3436], ], [ [119.1562, 36.7052], [103.715, 27.3436], ], [ [116.583, 35.4142], [103.715, 27.3436], ], [ [101.7168, 26.5862], [103.715, 27.3436], ], [ [102.995, 29.9888], [103.715, 27.3436], ], [ [121.4126, 28.6618], [103.715, 27.3436], ], [ [116.6988, 39.5192], [103.715, 27.3436], ], [ [115.8586, 28.6842], [103.715, 27.3436], ], [ [118.3418, 35.0518], [103.715, 27.3436], ], [ [126.5268, 45.8022], [103.715, 27.3436], ], [ [117.2792, 34.2052], [103.715, 27.3436], ], [ [120.1378, 33.3674], [103.715, 27.3436], ], [ [110.1598, 22.6394], [103.715, 27.3436], ], [ [119.4062, 32.3954], [103.715, 27.3436], ], [ [105.9348, 26.249], [103.715, 27.3436], ], [ [115.4586, 38.8846], [103.715, 27.3436], ], [ [113.0754, 22.5808], [103.715, 27.3436], ], [ [104.6262, 30.1316], [103.715, 27.3436], ], [ [103.715, 27.3436], [109.4848, 36.5856], ], [ [103.715, 27.3436], [109.0214, 32.6874], ], [ [112.5434, 37.8696], [103.715, 27.3436], ], [ [115.6916, 37.7342], [103.715, 27.3436], ], [ [121.4256, 37.4648], [103.715, 27.3436], ], [ [103.715, 27.3436], [87.6142, 43.8238], ], [ [120.5756, 30.0068], [103.715, 27.3436], ], [ [116.304, 37.4444], [103.715, 27.3436], ], [ [103.715, 27.3436], [97.1774, 31.136], ], [ [112.4598, 23.051], [103.715, 27.3436], ], [ [104.7404, 31.4682], [103.715, 27.3436], ], [ [125.3156, 43.8164], [103.715, 27.3436], ], [ [103.715, 27.3436], [101.775, 36.6174], ], [ [114.6964, 23.7482], [103.715, 27.3436], ], [ [112.4438, 34.6234], [103.715, 27.3436], ], [ [103.715, 27.3436], [109.506, 34.503], ], [ [103.715, 27.3436], [109.9336, 33.8708], ], [ [119.9084, 28.4504], [103.715, 27.3436], ], [ [111.2776, 30.704], [103.715, 27.3436], ], [ [103.715, 27.3436], [107.2332, 34.3634], ], [ [110.1748, 25.2418], [103.715, 27.3436], ], [ [105.5748, 30.5092], [103.715, 27.3436], ], [ [116.6768, 23.3566], [103.715, 27.3436], ], [ [110.3518, 21.273], [103.715, 27.3436], ], [ [103.8328, 30.0464], [103.715, 27.3436], ], [ [117.1802, 29.2794], [103.715, 27.3436], ], [ [114.9806, 27.1246], [103.715, 27.3436], ], [ [118.1724, 39.6292], [103.715, 27.3436], ], [ [120.0996, 30.8672], [103.715, 27.3436], ], [ [103.715, 27.3436], [108.7062, 34.33], ], [ [103.715, 27.3436], [107.017, 33.068], ], [ [107.468, 31.212], [103.715, 27.3436], ], [ [103.715, 27.3436], [108.9288, 34.9], ], [ [103.715, 27.3436], [106.2268, 38.4856], ], [ [119.0084, 33.6118], [103.715, 27.3436], ], [ [118.9954, 25.449], [103.715, 27.3436], ], [ [109.1844, 27.7348], [103.715, 27.3436], ], [ [119.4504, 32.2032], [103.715, 27.3436], ], [ [109.4114, 24.3324], [103.715, 27.3436], ], [ [104.3878, 31.1354], [103.715, 27.3436], ], [ [117.6456, 24.5178], [103.715, 27.3436], ], [ [110.194947, 20.046268], [103.715, 27.3436], ], [ [109.508, 18.2566], [103.715, 27.3436], ], [ [103.715, 27.3436], [88.881, 29.2778], ], [ [119.545, 35.394], [103.715, 27.3436], ], [ [113.9206, 35.3032], [103.715, 27.3436], ], [ [105.8176, 32.4396], [103.715, 27.3436], ], [ [103.715, 27.3436], [91.7736, 29.2406], ], [ [113.1182, 29.3686], [103.715, 27.3436], ], [ [103.715, 27.3436], [109.7354, 38.2864], ], [ [125.0974, 46.5866], [103.715, 27.3436], ], [ [119.9168, 32.4572], [103.715, 27.3436], ], [ [117.962, 28.4478], [103.715, 27.3436], ], [ [103.715, 27.3436], [105.7196, 34.5806], ], [ [122.1146, 37.5124], [103.715, 27.3436], ], [ [112.5306, 32.995], [103.715, 27.3436], ], [ [103.715, 27.3436], [98.511, 39.7434], ], [ [114.3804, 36.0974], [103.715, 27.3436], ], [ [109.9658, 27.5558], [103.715, 27.3436], ], [ [113.5914, 24.8138], [103.715, 27.3436], ], [ [113.0442, 23.6876], [103.715, 27.3436], ], [ [115.995, 29.7072], [103.715, 27.3436], ], [ [115.4738, 35.2398], [103.715, 27.3436], ], [ [108.0558, 24.6982], [103.715, 27.3436], ], [ [118.654, 37.4224], [103.715, 27.3436], ], [ [106.744, 31.872], [103.715, 27.3436], ], [ [118.4276, 31.3548], [103.715, 27.3436], ], [ [115.777, 33.8664], [103.715, 27.3436], ], [ [111.4624, 27.241], [103.715, 27.3436], ], [ [119.2136, 34.5976], [103.715, 27.3436], ], [ [119.594, 39.934], [103.715, 27.3436], ], [ [117.0172, 32.5822], [103.715, 27.3436], ], [ [110.9194, 21.6654], [103.715, 27.3436], ], [ [118.266, 33.9646], [103.715, 27.3436], ], [ [113.1236, 27.8336], [103.715, 27.3436], ], [ [113.2874, 40.0924], [103.715, 27.3436], ], [ [103.715, 27.3436], [104.1348, 36.5482], ], [ [117.9362, 40.976], [103.715, 27.3436], ], [ [114.9228, 25.838], [103.715, 27.3436], ], [ [109.1096, 21.4628], [103.715, 27.3436], ], [ [117.0274, 25.1028], [103.715, 27.3436], ], [ [118.3272, 29.7154], [103.715, 27.3436], ], [ [116.6166, 23.6596], [103.715, 27.3436], ], [ [117.3828, 32.9192], [103.715, 27.3436], ], [ [118.4724, 31.6166], [103.715, 27.3436], ], [ [110.9898, 35.0264], [103.715, 27.3436], ], [ [116.5084, 31.7398], [103.715, 27.3436], ], [ [114.8788, 40.8148], [103.715, 27.3436], ], [ [114.9038, 27.8268], [103.715, 27.3436], ], [ [115.652, 34.4158], [103.715, 27.3436], ], [ [112.5636, 26.9006], [103.715, 27.3436], ], [ [117.045, 30.518], [103.715, 27.3436], ], [ [112.93, 27.8358], [103.715, 27.3436], ], [ [113.0326, 25.7954], [103.715, 27.3436], ], [ [114.0778, 32.1378], [103.715, 27.3436], ], [ [117.0482, 28.25], [103.715, 27.3436], ], [ [111.9776, 21.8602], [103.715, 27.3436], ], [ [103.715, 27.3436], [94.3614, 29.652], ], [ [109.2326, 23.735], [103.715, 27.3436], ], [ [111.7352, 40.841], [103.715, 27.3436], ], [ [109.8244, 40.6564], [103.715, 27.3436], ], [ [117.66, 36.2036], [103.715, 27.3436], ], [ [112.2336, 30.3364], [103.715, 27.3436], ], [ [110.475, 29.1196], [103.715, 27.3436], ], [ [103.715, 27.3436], [91.1718, 29.6546], ], [ [118.0968, 27.3688], [103.715, 27.3436], ], [ [109.5914, 23.116], [103.715, 27.3436], ], [ [110.7834, 32.6528], [103.715, 27.3436], ], [ [116.367, 23.5522], [103.715, 27.3436], ], [ [122.2266, 40.668], [103.715, 27.3436], ], [ [114.3834, 27.7946], [103.715, 27.3436], ], [ [119.7578, 49.2122], [103.715, 27.3436], ], [ [107.366, 22.3766], [103.715, 27.3436], ], [ [115.8094, 32.9012], [103.715, 27.3436], ], [ [112.7344, 37.6804], [103.715, 27.3436], ], [ [118.8678, 28.9428], [103.715, 27.3436], ], [ [112.3472, 28.583], [103.715, 27.3436], ], [ [111.1356, 37.5172], [103.715, 27.3436], ], [ [108.3464, 21.6182], [103.715, 27.3436], ], [ [124.381, 40.1254], [103.715, 27.3436], ], [ [125.9418, 41.7394], [103.715, 27.3436], ], [ [113.8436, 27.635], [103.715, 27.3436], ], [ [113.842, 34.0352], [103.715, 27.3436], ], [ [113.3762, 31.6918], [103.715, 27.3436], ], [ [108.6194, 21.9702], [103.715, 27.3436], ], [ [112.8348, 35.5026], [103.715, 27.3436], ], [ [123.9578, 41.8794], [103.715, 27.3436], ], [ [113.1854, 33.7702], [103.715, 27.3436], ], [ [114.8738, 30.435], [103.715, 27.3436], ], [ [106.6106, 23.8994], [103.715, 27.3436], ], [ [117.806, 30.9508], [103.715, 27.3436], ], [ [114.3038, 34.7984], [103.715, 27.3436], ], [ [115.3668, 22.7906], [103.715, 27.3436], ], [ [114.6902, 33.6282], [103.715, 27.3436], ], [ [112.7326, 38.4084], [103.715, 27.3436], ], [ [124.3516, 43.1598], [103.715, 27.3436], ], [ [113.24, 35.222], [103.715, 27.3436], ], [ [111.6948, 29.031], [103.715, 27.3436], ], [ [111.606, 26.4252], [103.715, 27.3436], ], [ [112.039, 22.9176], [103.715, 27.3436], ], [ [113.5772, 37.863], [103.715, 27.3436], ], [ [112.129, 32.01], [103.715, 27.3436], ], [ [112.0126, 27.728], [103.715, 27.3436], ], [ [117.6298, 26.2656], [103.715, 27.3436], ], [ [121.1256, 41.091], [103.715, 27.3436], ], [ [118.0164, 37.379], [103.715, 27.3436], ], [ [119.543, 26.6682], [103.715, 27.3436], ], [ [111.5148, 36.0818], [103.715, 27.3436], ], [ [118.7512, 30.9472], [103.715, 27.3436], ], [ [122.265, 43.6188], [103.715, 27.3436], ], [ [121.6524, 42.019], [103.715, 27.3436], ], [ [114.0308, 32.9774], [103.715, 27.3436], ], [ [111.559559, 24.409395], [103.715, 27.3436], ], [ [130.3608, 46.8088], [103.715, 27.3436], ], [ [123.9206, 47.3626], [103.715, 27.3436], ], [ [112.198, 31.036], [103.715, 27.3436], ], [ [112.4204, 39.3152], [103.715, 27.3436], ], [ [106.7898, 39.6542], [103.715, 27.3436], ], [ [122.103, 30.0176], [103.715, 27.3436], ], [ [118.3116, 32.3068], [103.715, 27.3436], ], [ [116.1178, 24.296], [103.715, 27.3436], ], [ [103.715, 27.3436], [84.866, 45.5916], ], [ [118.8922, 42.2518], [103.715, 27.3436], ], [ [113.1088, 36.1938], [103.715, 27.3436], ], [ [122.989, 41.1082], [103.715, 27.3436], ], [ [115.0132, 35.7662], [103.715, 27.3436], ], [ [125.1374, 42.885], [103.715, 27.3436], ], [ [103.715, 27.3436], [104.6232, 35.5904], ], [ [129.6298, 44.5572], [103.715, 27.3436], ], [ [103.715, 27.3436], [104.9206, 33.394], ], [ [123.164, 41.2668], [103.715, 27.3436], ], [ [115.0312, 30.2018], [103.715, 27.3436], ], [ [109.770944, 39.620142], [103.715, 27.3436], ], [ [120.8384, 40.7186], [103.715, 27.3436], ], [ [111.2934, 23.481], [103.715, 27.3436], ], [ [122.0662, 41.1248], [103.715, 27.3436], ], [ [114.3162, 29.8438], [103.715, 27.3436], ], [ [113.9468, 30.9218], [103.715, 27.3436], ], [ [130.9572, 45.2898], [103.715, 27.3436], ], [ [128.8348, 47.7238], [103.715, 27.3436], ], [ [116.9838, 33.6374], [103.715, 27.3436], ], [ [124.8124, 45.1436], [103.715, 27.3436], ], [ [126.9748, 46.6348], [103.715, 27.3436], ], [ [103.715, 27.3436], [105.1848, 37.5152], ], [ [116.8046, 33.9578], [103.715, 27.3436], ], [ [117.4788, 30.6612], [103.715, 27.3436], ], [ [114.286, 35.742], [103.715, 27.3436], ], [ [130.9292, 45.7842], [103.715, 27.3436], ], [ [103.715, 27.3436], [81.278327, 40.547623], ], [ [116.3508, 27.9506], [103.715, 27.3436], ], [ [103.715, 27.3436], [106.6972, 35.5298], ], [ [114.012, 33.5826], [103.715, 27.3436], ], [ [126.4204, 41.9408], [103.715, 27.3436], ], [ [114.8896, 30.3934], [103.715, 27.3436], ], [ [113.448444, 30.364899], [103.715, 27.3436], ], [ [103.715, 27.3436], [89.1844, 42.947], ], [ [127.4938, 50.2446], [103.715, 27.3436], ], [ [131.1514, 46.6392], [103.715, 27.3436], ], [ [102.265, 27.887], [103.715, 27.3436], ], [ [103.715, 27.3436], [100.46, 38.9334], ], [ [103.715, 27.3436], [106.376, 39.0168], ], [ [123.836, 42.2928], [103.715, 27.3436], ], [ [122.8348, 45.6178], [103.715, 27.3436], ], [ [123.7606, 41.293], [103.715, 27.3436], ], [ [103.715, 27.3436], [107.6336, 35.739], ], [ [103.715, 27.3436], [93.5154, 42.8284], ], [ [103.715, 27.3436], [98.2892, 39.774], ], [ [111.1962, 34.7746], [103.715, 27.3436], ], [ [103.715, 27.3436], [106.192, 37.9942], ], [ [113.121, 40.993], [103.715, 27.3436], ], [ [103.715, 27.3436], [102.6332, 37.9248], ], [ [103.715, 27.3436], [102.1874, 38.501], ], [ [113.160107, 30.665725], [103.715, 27.3436], ], [ [112.893716, 30.404192], [103.715, 27.3436], ], [ [103.715, 27.3436], [106.2746, 36.0072], ], [ [112.599893, 35.067444], [103.715, 27.3436], ], [ [130.3138, 47.3726], [103.715, 27.3436], ], [ [103.715, 27.3436], [102.4054, 36.4776], ], [ [107.419, 40.749], [103.715, 27.3436], ], [ [103.715, 27.3436], [86.077816, 44.30313], ], [ [105.6924, 38.8462], [103.715, 27.3436], ], [ [109.5754, 19.5238], [103.715, 27.3436], ], [ [112.334, 16.8322], [103.715, 27.3436], ], [ [116.0522, 43.9466], [103.715, 27.3436], ], [ [122.056, 46.079], [103.715, 27.3436], ], [ [103.715, 27.3436], [80.262, 41.1688], ], [ [103.715, 27.3436], [87.540332, 44.168152], ], [ [106.1072, 30.845], [103.715, 27.3436], ], [ [103.715, 27.3436], [79.06902, 39.859708], ], [ [129.498, 42.892], [103.715, 27.3436], ], [ [107.5174, 26.255], [103.715, 27.3436], ], [ [104.8948, 25.088], [103.715, 27.3436], ], [ [102.2204, 31.9026], [103.715, 27.3436], ], [ [101.9566, 30.0504], [103.715, 27.3436], ], [ [107.9772, 26.5856], [103.715, 27.3436], ], [ [103.715, 27.3436], [75.9926, 39.467], ], [ [103.715, 27.3436], [88.1364, 47.8536], ], [ [109.7336, 28.316], [103.715, 27.3436], ], [ [124.219245, 52.451053], [103.715, 27.3436], ], [ [103.715, 27.3436], [79.9216, 37.11], ], [ [109.475, 30.2702], [103.715, 27.3436], ], [ [103.715, 27.3436], [80.096, 32.503], ], [ [103.715, 27.3436], [82.9752, 46.7506], ], [ [103.715, 27.3436], [92.057, 31.4776], ], [ [103.715, 27.3436], [102.9076, 34.9804], ], [ [103.715, 27.3436], [100.2404, 34.4758], ], [ [103.715, 27.3436], [79.288916, 37.209579], ], [ [103.715, 27.3436], [87.3054, 44.0184], ], [ [103.715, 27.3436], [103.2092, 35.5928], ], [ [103.715, 27.3436], [100.8986, 36.959], ], [ [103.715, 27.3436], [100.6188, 36.2754], ], [ [103.715, 27.3436], [97.0074, 33.0064], ], [ [103.715, 27.3436], [85.673337, 41.867431], ], [ [103.715, 27.3436], [97.3628, 37.3658], ], [ [103.715, 27.3436], [82.067, 44.8926], ], [ [103.715, 27.3436], [86.144, 41.7558], ], [ [103.715, 27.3436], [76.171, 39.7088], ], [ [103.715, 27.3436], [81.334, 43.914], ], [ [110.67091, 31.748498], [103.715, 27.3436], ], [ [103.715, 27.3436], [102.0128, 35.5152], ], ]; conData = [ { name: "北京", value: 6710 }, { name: "上海", value: 6410 }, { name: "重庆", value: 5170 }, { name: "成都", value: 4690 }, { name: "深圳", value: 3780 }, { name: "东莞", value: 3490 }, { name: "广州", value: 3260 }, { name: "宜宾", value: 3000 }, { name: "杭州", value: 2340 }, { name: "枣庄", value: 2200 }, { name: "中山", value: 2100 }, { name: "苏州", value: 2060 }, { name: "天津", value: 2000 }, { name: "贵阳", value: 1910 }, { name: "邯郸", value: 1680 }, { name: "佛山", value: 1510 }, { name: "无锡", value: 1460 }, { name: "武汉", value: 1420 }, { name: "毕节", value: 1360 }, { name: "南京", value: 1350 }, { name: "西安", value: 1350 }, { name: "济南", value: 1270 }, { name: "六盘水", value: 1220 }, { name: "郑州", value: 1170 }, { name: "泸州", value: 1140 }, { name: "青岛", value: 1020 }, { name: "淄博", value: 999 }, { name: "邢台", value: 957 }, { name: "聊城", value: 956 }, { name: "长沙", value: 956 }, { name: "宁波", value: 927 }, { name: "温州", value: 861 }, { name: "南宁", value: 794 }, { name: "内江", value: 785 }, { name: "厦门", value: 782 }, { name: "常州", value: 760 }, { name: "泰安", value: 724 }, { name: "合肥", value: 704 }, { name: "石家庄", value: 699 }, { name: "金华", value: 679 }, { name: "惠州", value: 601 }, { name: "嘉兴", value: 594 }, { name: "吉林", value: 583 }, { name: "沧州", value: 580 }, { name: "广安", value: 578 }, { name: "乐山", value: 575 }, { name: "兰州", value: 561 }, { name: "自贡", value: 559 }, { name: "南通", value: 546 }, { name: "福州", value: 542 }, { name: "大连", value: 534 }, { name: "泉州", value: 531 }, { name: "遵义", value: 524 }, { name: "沈阳", value: 517 }, { name: "向阳", value: 517 }, { name: "珠海", value: 503 }, { name: "潍坊", value: 487 }, { name: "济宁", value: 484 }, { name: "攀枝花", value: 484 }, { name: "雅安", value: 474 }, { name: "台州", value: 468 }, { name: "廊坊", value: 466 }, { name: "南昌", value: 463 }, { name: "临沂", value: 460 }, { name: "哈尔滨", value: 432 }, { name: "徐州", value: 425 }, { name: "盐城", value: 413 }, { name: "玉林", value: 413 }, { name: "扬州", value: 410 }, { name: "安顺", value: 407 }, { name: "保定", value: 389 }, { name: "江门", value: 386 }, { name: "资阳", value: 385 }, { name: "延安", value: 379 }, { name: "健康", value: 379 }, { name: "太原", value: 371 }, { name: "衡水", value: 369 }, { name: "烟台", value: 357 }, { name: "乌鲁木齐", value: 356 }, { name: "绍兴", value: 351 }, { name: "德州", value: 347 }, { name: "昌都", value: 334 }, { name: "肇庆", value: 333 }, { name: "绵阳", value: 329 }, { name: "长春", value: 328 }, { name: "西宁", value: 318 }, { name: "河源", value: 316 }, { name: "洛阳", value: 307 }, { name: "渭南", value: 307 }, { name: "商洛", value: 304 }, { name: "丽水", value: 303 }, { name: "宜昌", value: 293 }, { name: "宝鸡", value: 287 }, { name: "桂林", value: 286 }, { name: "遂宁", value: 284 }, { name: "汕头", value: 280 }, { name: "湛江", value: 279 }, { name: "眉山", value: 278 }, { name: "景德镇", value: 277 }, { name: "吉安", value: 276 }, { name: "唐山", value: 273 }, { name: "湖州", value: 273 }, { name: "咸阳", value: 269 }, { name: "汉中", value: 269 }, { name: "达州", value: 268 }, { name: "铜川", value: 265 }, { name: "银川", value: 260 }, { name: "淮安", value: 256 }, { name: "莆田", value: 256 }, { name: "铜仁", value: 255 }, { name: "镇江", value: 253 }, { name: "柳州", value: 251 }, { name: "德阳", value: 251 }, { name: "漳州", value: 250 }, { name: "海口", value: 245 }, { name: "三亚", value: 244 }, { name: "日喀则", value: 241 }, { name: "日照", value: 238 }, { name: "新乡", value: 236 }, { name: "广元", value: 235 }, { name: "山南", value: 234 }, { name: "岳阳", value: 233 }, { name: "榆林", value: 232 }, { name: "大庆", value: 221 }, { name: "泰州", value: 220 }, { name: "上饶", value: 218 }, { name: "天水", value: 216 }, { name: "威海", value: 215 }, { name: "南阳", value: 215 }, { name: "酒泉", value: 214 }, { name: "安阳", value: 213 }, { name: "怀化", value: 213 }, { name: "韶关", value: 213 }, { name: "清远", value: 209 }, { name: "九江", value: 208 }, { name: "菏泽", value: 206 }, { name: "河池", value: 206 }, { name: "东营", value: 198 }, { name: "巴中", value: 197 }, { name: "芜湖", value: 194 }, { name: "亳州", value: 191 }, { name: "邵阳", value: 189 }, { name: "连云港", value: 188 }, { name: "秦皇岛", value: 187 }, { name: "淮南", value: 187 }, { name: "茂名", value: 187 }, { name: "宿迁", value: 185 }, { name: "株洲", value: 185 }, { name: "大同", value: 180 }, { name: "白银", value: 180 }, { name: "承德", value: 178 }, { name: "赣州", value: 177 }, { name: "北海", value: 177 }, { name: "龙岩", value: 176 }, { name: "黄山", value: 173 }, { name: "潮州", value: 173 }, { name: "蚌埠", value: 169 }, { name: "马鞍山", value: 166 }, { name: "运城", value: 164 }, { name: "六安", value: 164 }, { name: "张家口", value: 163 }, { name: "新余", value: 163 }, { name: "商丘", value: 162 }, { name: "衡阳", value: 162 }, { name: "安庆", value: 161 }, { name: "湘潭", value: 161 }, { name: "郴州", value: 161 }, { name: "信阳", value: 160 }, { name: "鹰潭", value: 159 }, { name: "阳江", value: 159 }, { name: "林芝", value: 159 }, { name: "来宾", value: 157 }, { name: "呼和浩特", value: 156 }, { name: "包头", value: 154 }, { name: "济南", value: 154 }, { name: "荆州", value: 152 }, { name: "张家界", value: 152 }, { name: "拉萨", value: 152 }, { name: "南平", value: 151 }, { name: "贵港", value: 149 }, { name: "十堰", value: 147 }, { name: "揭阳", value: 147 }, { name: "营口", value: 146 }, { name: "宜春", value: 145 }, { name: "呼伦贝尔", value: 144 }, { name: "崇左", value: 144 }, { name: "阜阳", value: 143 }, { name: "晋中", value: 142 }, { name: "衢州", value: 140 }, { name: "益阳", value: 140 }, { name: "吕梁", value: 139 }, { name: "防城港", value: 138 }, { name: "丹东", value: 136 }, { name: "通化", value: 136 }, { name: "萍乡", value: 136 }, { name: "许昌", value: 136 }, { name: "随州", value: 136 }, { name: "钦州", value: 135 }, { name: "晋城", value: 133 }, { name: "抚顺", value: 133 }, { name: "平顶山", value: 133 }, { name: "黄冈", value: 133 }, { name: "百色", value: 133 }, { name: "铜陵", value: 132 }, { name: "开封", value: 132 }, { name: "汕尾", value: 132 }, { name: "周口", value: 131 }, { name: "忻州", value: 130 }, { name: "四平", value: 129 }, { name: "焦作", value: 129 }, { name: "常德", value: 129 }, { name: "永州", value: 129 }, { name: "云浮", value: 127 }, { name: "阳泉", value: 125 }, { name: "襄阳", value: 125 }, { name: "娄底", value: 125 }, { name: "三明", value: 124 }, { name: "锦州", value: 123 }, { name: "滨州", value: 122 }, { name: "宁德", value: 121 }, { name: "临汾", value: 119 }, { name: "宣城", value: 118 }, { name: "通辽", value: 117 }, { name: "阜新", value: 114 }, { name: "驻马店", value: 112 }, { name: "贺州", value: 112 }, { name: "佳木斯", value: 110 }, { name: "齐齐哈尔", value: 109 }, { name: "荆门", value: 105 }, { name: "朔州", value: 100 }, { name: "乌海", value: 100 }, { name: "舟山", value: 99 }, { name: "滁州", value: 99 }, { name: "梅州", value: 98 }, { name: "克拉玛依", value: 97 }, { name: "赤峰", value: 95 }, { name: "长治", value: 94 }, { name: "鞍山", value: 94 }, { name: "濮阳", value: 94 }, { name: "辽源", value: 92 }, { name: "定西", value: 92 }, { name: "牡丹江", value: 91 }, { name: "陇南", value: 91 }, { name: "辽阳", value: 90 }, { name: "黄石", value: 89 }, { name: "鄂尔多斯", value: 88 }, { name: "葫芦岛", value: 88 }, { name: "梧州", value: 88 }, { name: "盘锦", value: 87 }, { name: "咸宁", value: 87 }, { name: "孝感", value: 86 }, { name: "鸡西", value: 85 }, { name: "伊春", value: 85 }, { name: "宿州", value: 85 }, { name: "松原", value: 84 }, { name: "绥化", value: 84 }, { name: "中卫", value: 83 }, { name: "淮北", value: 81 }, { name: "池州", value: 79 }, { name: "鹤壁", value: 79 }, { name: "七台河", value: 78 }, { name: "阿拉尔", value: 77 }, { name: "抚州", value: 76 }, { name: "平凉", value: 76 }, { name: "漯河", value: 73 }, { name: "白山", value: 72 }, { name: "鄂州", value: 71 }, { name: "仙桃", value: 71 }, { name: "吐鲁番", value: 71 }, { name: "黑河", value: 70 }, { name: "双鸭山", value: 69 }, { name: "凉山彝族自治州", value: 69 }, { name: "张掖", value: 69 }, { name: "石嘴山", value: 68 }, { name: "铁岭", value: 66 }, { name: "白城", value: 66 }, { name: "本溪", value: 65 }, { name: "庆阳", value: 65 }, { name: "哈密", value: 65 }, { name: "嘉峪关", value: 63 }, { name: "三门峡", value: 62 }, { name: "吴忠", value: 61 }, { name: "乌兰察布", value: 59 }, { name: "武威", value: 59 }, { name: "金昌", value: 58 }, { name: "天门", value: 57 }, { name: "潜江", value: 55 }, { name: "固原", value: 54 }, { name: "济源", value: 52 }, { name: "鹤岗", value: 50 }, { name: "海东", value: 49 }, { name: "巴彦淖尔", value: 42 }, { name: "石河子", value: 33 }, { name: "阿拉善盟", value: 30 }, { name: "儋州", value: 30 }, { name: "三沙", value: 29 }, { name: "锡林郭勒盟", value: 27 }, { name: "兴安盟", value: 24 }, { name: "阿克苏地区", value: 22 }, { name: "五家渠", value: 21 }, { name: "南充", value: 20 }, { name: "图木舒克", value: 19 }, { name: "延边朝鲜族自治州", value: 13 }, { name: "黔南布依族苗族自治州", value: 13 }, { name: "黔西南布依族苗族自治州", value: 11 }, { name: "阿坝藏族羌族自治州", value: 10 }, { name: "甘孜藏族自治州", value: 10 }, { name: "黔东南苗族侗族自治州", value: 10 }, { name: "喀什地区", value: 10 }, { name: "阿勒泰地区", value: 9 }, { name: "湘西土家族苗族自治州", value: 8 }, { name: "大兴安岭地区", value: 7 }, { name: "和田地区", value: 7 }, { name: "恩施土家族苗族自治州", value: 6 }, { name: "阿里地区", value: 6 }, { name: "塔城地区", value: 6 }, { name: "那曲地区", value: 5 }, { name: "甘南藏族自治州", value: 5 }, { name: "果洛藏族自治州", value: 5 }, { name: "昆玉", value: 5 }, { name: "昌吉回族自治州", value: 4 }, { name: "临夏回族自治州", value: 3 }, { name: "海北藏族自治州", value: 3 }, { name: "海南藏族自治州", value: 3 }, { name: "玉树藏族自治州", value: 3 }, { name: "铁门关", value: 3 }, { name: "海西蒙古族藏族自治州", value: 2 }, { name: "博尔塔拉蒙古自治州", value: 2 }, { name: "巴音郭楞蒙古自治州", value: 2 }, { name: "克孜勒苏柯尔克孜自治州", value: 2 }, { name: "伊犁哈萨克自治州", value: 2 }, { name: "神农架林区", value: 1 }, { name: "海南省省直辖", value: 1 }, { name: "黄南藏族自治州", value: 1 }, ]; } else { geoCoordMap = { 昆明: [102.8246, 24.8898], 曲靖: [103.7922, 25.4888], 玉溪: [102.5394, 24.3522], 保山: [99.1584, 25.1158], 丽江: [100.2326, 26.8734], 普洱: [100.969, 22.784], 临沧: [100.0902, 23.8816], 楚雄彝族自治州: [101.527, 25.0472], 红河哈尼族彝族自治州: [103.401, 23.368], 文山壮族苗族自治州: [104.25, 23.3726], 西双版纳傣族自治州: [100.7984, 22.0154], 大理白族自治州: [100.2218, 25.5858], 德宏傣族景颇族自治州: [98.593, 24.4408], 怒江傈僳族自治州: [98.8458, 25.854], 迪庆藏族自治州: [99.7034, 27.8138], }; alirl = [ [ [102.8246, 24.8898], [103.715, 27.3436], ], [ [103.7922, 25.4888], [103.715, 27.3436], ], [ [102.5394, 24.3522], [103.715, 27.3436], ], [ [99.1584, 25.1158], [103.715, 27.3436], ], [ [103.715, 27.3436], [100.2326, 26.8734], ], [ [103.715, 27.3436], [100.969, 22.784], ], [ [103.715, 27.3436], [100.0902, 23.8816], ], [ [103.715, 27.3436], [101.527, 25.0472], ], [ [103.715, 27.3436], [103.401, 23.368], ], [ [103.715, 27.3436], [104.25, 23.3726], ], [ [103.715, 27.3436], [100.7984, 22.0154], ], [ [103.715, 27.3436], [100.2218, 25.5858], ], [ [103.715, 27.3436], [98.593, 24.4408], ], [ [103.715, 27.3436], [98.8458, 25.854], ], [ [103.715, 27.3436], [99.7034, 27.8138], ], ]; conData = [ { name: "昆明", value: 25400 }, { name: "曲靖", value: 6980 }, { name: "玉溪", value: 4710 }, { name: "保山", value: 4260 }, { name: "丽江", value: 4820 }, { name: "普洱", value: 3370 }, { name: "临沧", value: 3220 }, { name: "楚雄彝族自治州", value: 1510 }, { name: "红河哈尼族彝族自治州", value: 1510 }, { name: "文山壮族苗族自治州", value: 1650 }, { name: "西双版纳傣族自治州", value: 1250 }, { name: "大理白族自治州", value: 1440 }, { name: "德宏傣族景颇族自治州", value: 1400 }, { name: "怒江傈僳族自治州", value: 1480 }, { name: "迪庆藏族自治州", value: 1310 }, ]; // distance = 120; matchCount = 10; } conData.forEach((item) => { if (maxVal <= item.value) { maxVal = item.value; } }); convertData = function (data) { var res = []; for (var i = 0; i < data.length; i++) { var geoCoord = geoCoordMap[data[i].name]; if (geoCoord) { res.push({ name: data[i].name, value: geoCoord.concat(data[i].value), }); } } return res; }; var yMax = 1000; var dataShadow = []; // var resultdata0 = []; var titledata = []; var bartop10 = []; conData.sort(NumDescSort); for (var i = 0; i < matchCount; i++) { var top10 = { name: conData[i].name, value: conData[i].value, }; bartop10.push(top10); dataShadow.push(yMax); } bartop10.sort(NumAsceSort); for (var n = 0; n < bartop10.length; n++) { titledata.push(bartop10[n].name); } var option2 = { backgroundColor: "transparent", tooltip: { show: true, trigger: "item", }, visualMap: [ { show:false, type: "continuous", seriesIndex: 0, calculable: true, max: 7000, inRange: { color: ["#87aa66", "#eba438", "#d94d4c"], }, }, ], geo3D: { map: this.mapName, roam: true, regionHeight: 1, itemStyle: { color: "#0066aa", opacity: 1, borderWidth: 0.4, borderColor: "#000", }, label: { show: true, textStyle: { color: "#fff", //地图初始化区域字体色彩 fontSize: 14, opacity: 1, backgroundColor: "rgba(0,23,11,0)", }, }, emphasis: { //当鼠标放上去 地区区域是否显示名称 label: { show: true, textStyle: { color: "#fff", fontSize: 20, backgroundColor: "rgba(0,23,11,0)", }, }, }, //shading: 'lambert', light: { //光照暗影 main: { color: "#fff", //光照色彩 intensity: 1.2, //光照强度 //shadowQuality: 'high', //暗影亮度 shadow: false, //是否显示暗影 alpha: 55, beta: 10, }, ambient: { intensity: 0.3, }, }, viewControl: { projection: "perspective", // alpha: 0,//视角的方向。 // beta: 10,//左右旋转的角度。 // center: [0, 1, 0],// 视角 // targetCoord: [116.826801, 0], autoRotate: false, // 主动旋转 autoRotateAfterStill: 10, //鼠标静止操作后复原主动旋转的工夫距离 distance: 99, // 视距 rotateSensitivity: 1, //1能够旋转,0不能旋转 }, }, series: [ //柱状图 { name: "", type: "bar3D", coordinateSystem: "geo3D", barSize: 1, //柱子粗细 shading: "lambert", opacity: 1, minHeight: 0.5, bevelSize: 0.3, label: { show: false, formatter: "{b}", }, data: convertData(conData), }, //画线 { type: "lines3D", coordinateSystem: "geo3D", effect: { show: true, trailWidth: 2, trailOpacity: 0.6, trailLength: 0.5, constantSpeed: 8, }, blendMode: "lighter", lineStyle: { width: 0.2, opacity: 0.05, }, data: alirl, }, ], }; this.myChart.setOption(option2); } }) },

April 2, 2021 · 31 min · jiezi

关于webgl:图程之路

开了个新坑,打算学习做图程,以下是本人学习门路,心愿能有所帮忙。 线性代数:先补充基础知识,重温一下以前学的内容,会有新的了解。 https://www.bilibili.com/vide... (英文中字)https://www.bilibili.com/vide... (汉语配音)webgl:学图程必定绕不开底层API工具,当然openGL、D3D都能够,只不过本人是前端,看js的代码难受一点,底层设计都是一样的。 https://webglfundamentals.org... 这个居然有中文翻译了,前几年看英文版的是真的没保持下来,谢谢大佬们的翻译https://codepen.io/greggman/p... 能够在这里试试手,跟着下面的教程一起做https://webglfundamentals.org... 官网的这篇文章倡议提前看下,是说‘编程的矩阵’和‘数学的矩阵’的区别,不然间接从线性代数那边过去会有困扰对底层的一些补充:下面的教学挺优良的,都是联合代码例子来的,不过有几个原理没说明确。 https://blog.csdn.net/linuxhe... 比方齐次坐标、透视投影的变换。这篇文章就说的很好,在学到相机这部分内容的时候能够看下。

February 10, 2021 · 1 min · jiezi

关于webgl:几款2077风格的shader其一sh风ader格化

随同着赛博朋克2077的火爆(各种意义上的),这种电子故障类的shader仿佛成为了一种时尚,因为你真不知道这是bug还是无意为之。明天咱们就来了了几款故障类shader的原理及实现吧!本期将介绍2种shader(色彩偏移与扫描偏移)下期咱们再说两种(抖动和摇摆),正当应用这四种shader你可能实现上面短片中的成果: 色彩偏移 最终成果 咱们都晓得,图片是由RGB三个通道的色彩叠加起来而成的。R+G失去黄,R+B失去洋红,G+B是青。 色彩偏移的原理就是把其中一个通道偏移一个间隔在叠加起来。比方挪动绿色通道: void main () { // Color drift float drift = sin ( u_time) * u_colorDrift * .04; // Offset vec2 offset = vec2(drift,.0); vec4 color1 = texture2D (u_image0, uv); vec4 color2 = texture2D (u_image0, uv + offset); gl_FragColor = vec4 (color1.r, color2.g, color1.b, 1.0);}其中u_time是以后工夫,保障继续抖动,u_colorDrift是横向偏移的间隔: 是不是you点抖音的感觉?那么为什么同种呈现的色彩是洋红和绿??那是因为咱们挪动了绿通道,而洋红就是剩下的蓝通道和红通道叠加的后果。这里咱们能够试验一下,如果挪动红通道。失去的抖动色彩就会是红色和青色: gl_FragColor =vec4(color2.r, color1.g, color1.b,1.0); 扫描射线这种成果会在产生逐条的偏移,相似小时候电视机调台时那种感觉(裸露年龄了) 这里咱们须要一个随机函数对不同y进行不同的偏移(图中能够看出,对y方向雷同的点偏移是统一的)。这里咱们须要一个hash函数: float hash21 (float x, float y) { return fract (sin (dot (vec2 (x, y), vec2 (12.9898, 78.233))) * 43758.5453);}核心思想是通过fract(sin(x)*a)当a是一个很大的数时结构出出一种随机: ...

January 28, 2021 · 2 min · jiezi

关于webgl:WebGL实现简单滤镜

1. WebGL介绍WebGL(全写Web Graphics Library)是一种3D绘图协定,这种绘图技术标准容许把JavaScript和OpenGL ES 2.0联合在一起,通过减少OpenGL ES 2.0的一个JavaScript绑定。 2. WebGL、OpenGL、OpenGL ES 三者的关系 3. WebGL 根底介绍const webgl = document.getElementById("webGl-layer").getContext("webgl");4. 基本原理首先应用webgl 纹理绘制图片这里如果参照 https://webglfundamentals.org... 绘制过程中应用片段做着色器对其 rgb 值进行批改5. 具体实现 <!DOCTYPE html><html lang="ch"><head> <meta charset="UTF-8"> <title>VertexBuffer</title></head><body><canvas id="webGl-layer" width="532" height="300"></canvas><div> <label for="r1">饱和度:</label><input type="range" id="r1" value="0"/></div><div> <label for="r2">R:</label><input type="range" id="r2" value="0"/></div><div> <label for="r3">G:</label><input type="range" id="r3" value="0"/></div><div> <label for="r4">B:</label><input type="range" id="r4" value="0"/></div><script> const webgl = document.getElementById("webGl-layer").getContext("webgl"); const range1 = document.getElementById("r1"), range2 = document.getElementById("r2"), range3 = document.getElementById("r3"), range4 = document.getElementById("r4"); webgl.viewport(0, 0, 532, 300); const vertexShader2D = ` precision mediump float; attribute vec4 position; attribute vec4 inputTextureCoordinate; varying vec2 textureCoordinate; void main() { gl_Position = position; textureCoordinate = vec2((position.x+1.0)/2.0, 1.0-(position.y+1.0)/2.0); } `; const fragmentShader2D = ` precision mediump float; varying vec2 textureCoordinate; uniform sampler2D inputImageTexture; uniform float size; uniform float saturation; uniform float r; uniform float g; uniform float b; uniform float a; void main() { vec4 texture = texture2D(inputImageTexture, textureCoordinate); texture.r += r; // 图片整体 r 值 texture.g += g; // 图片整体 g 值 texture.b += b; // 图片整体 b 值 // texture.a = 0.5; // 图片整体 a 值 //内暗影 // float dist = distance(textureCoordinate, vec2(0.5, 0.5)); // texture.rgb *= smoothstep(0.8, size * 0.799, dist * (1.0 + size)); //饱和度 float average = (texture.r + texture.g + texture.b) / 3.0; if (saturation > 0.0) { texture.rgb += (average - texture.rgb) * (1.0 - 1.0 / (1.001 - saturation)); } else { texture.rgb += (average - texture.rgb) * (-saturation); } gl_FragColor = texture; } `; function createShader(gl, type, source) { const shader = gl.createShader(type); gl.shaderSource(shader, source); gl.compileShader(shader); if (gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { return shader; } console.log(gl.getShaderInfoLog(shader)); gl.deleteShader(shader); } function createProgram(gl, vertexShader, fragmentShader) { const program = gl.createProgram(); gl.attachShader(program, vertexShader); gl.attachShader(program, fragmentShader); gl.linkProgram(program); if (gl.getProgramParameter(program, gl.LINK_STATUS)) { webgl.useProgram(program); return program; } console.error(gl.getProgramInfoLog(program)); gl.deleteProgram(program); } function createTextureByImageObject(gl, imgObject) { gl.activeTexture(gl.TEXTURE0); const textureObject = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, textureObject); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, imgObject); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) return textureObject; } const vertices = [ 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0 ]; const vertexShader = createShader(webgl, webgl.VERTEX_SHADER, vertexShader2D), fragmentShader = createShader(webgl, webgl.FRAGMENT_SHADER, fragmentShader2D), program = createProgram(webgl, vertexShader, fragmentShader), buffer = webgl.createBuffer(); webgl.bindBuffer(webgl.ARRAY_BUFFER, buffer); webgl.bufferData(webgl.ARRAY_BUFFER, new Float32Array(vertices), webgl.STATIC_DRAW); let v4PositionIndex = webgl.getAttribLocation(program, "position"); webgl.enableVertexAttribArray(v4PositionIndex); webgl.vertexAttribPointer(v4PositionIndex, 2, webgl.FLOAT, false, 0, 0); let img = new Image(); img.crossOrigin = "anonymous"; img.src = "http://static.atvideo.cc/2021/01/04/09/47/1609724837(1).jpg"; img.onload = function () { document.body.append(img); createTextureByImageObject(webgl, img); let saturationUniform = webgl.getUniformLocation(program, "saturation"); let rUniform = webgl.getUniformLocation(program, "r"); let gUniform = webgl.getUniformLocation(program, "g"); let bUniform = webgl.getUniformLocation(program, "b"); // let sizeUniform = webgl.getUniformLocation(program, "size"); // webgl.uniform1f(sizeUniform, 2.0); const uniform = webgl.getUniformLocation(program, "inputImageTexture"); webgl.uniform1i(uniform, 0); webgl.drawArrays(webgl.TRIANGLE_STRIP, 0, 4); range1.addEventListener("change", function () { const val = Number(range1.value) / 100; webgl.uniform1f(saturationUniform, val); webgl.drawArrays(webgl.TRIANGLE_STRIP, 0, 4); }); range2.addEventListener("change", function () { const val = Number(range2.value) / 100; webgl.uniform1f(rUniform, val); webgl.drawArrays(webgl.TRIANGLE_STRIP, 0, 4); }); range3.addEventListener("change", function () { const val = Number(range3.value) / 100; webgl.uniform1f(gUniform, val); webgl.drawArrays(webgl.TRIANGLE_STRIP, 0, 4); }); range4.addEventListener("change", function () { const val = Number(range4.value) / 100; webgl.uniform1f(bUniform, val); webgl.drawArrays(webgl.TRIANGLE_STRIP, 0, 4); }); }</script></body></html>

January 4, 2021 · 3 min · jiezi

关于webgl:webgl智慧楼宇发光效果算法系列之高斯模糊

webgl智慧楼宇发光成果算法系列之高斯含糊如果应用过PS之类的图像处理软件,置信对于含糊滤镜不会生疏,图像处理软件提供了泛滥的含糊算法。高斯含糊是其中的一种。 在咱们的智慧楼宇的我的项目中,要求对楼宇实现楼宇发光的成果。 比方如下图所示的简略楼宇成果: 楼宇发光成果须要用的算法之一就是高斯含糊。 高斯含糊简介高斯含糊算法是计算机图形学畛域中一种应用宽泛的技术, 是一种图像空间成果,用于对图像进行含糊解决,创立原始图像的柔和含糊版本。应用高斯含糊的成果,联合一些其余的算法,还能够产生发光,光晕,景深,热雾和含糊玻璃成果。 高斯含糊的原理阐明图像含糊的原理,简略而言,就是针对图像的每一个像素,其色彩取其周边像素的平均值。不同的含糊算法,对周边的定义不一样,均匀的算法也不一样。 比方之前写#过的一篇文章,webgl实现径向含糊,就是含糊算法中的一种。 均值含糊在了解高斯含糊之前,咱们先了解比拟容易的均值含糊。所谓均值含糊其原理就是取像素点四周(上下左右)像素的平均值(其中也会包含本身)。如下图所示: 能够看出,对于某个像素点,当搜寻半径为1的时候,影响其色彩值的像素是9个像素(包含本人和周边的8个像素)。假如每个像素对于核心像素的影响都是一样的,那么每个像素的影响度就是1/9。如下图所示: 下面这个3*3的影响度的数字矩阵,通常称之为卷积核。 那么最终中心点的值的求和如下图所示:最终的值是: (8 * 1 + 1 * 2 / (8 + 1) ) = 10/9当计算像素的色彩时候,对于像素的RGB每一个通道都进行的上述均匀计算即可。 下面的计算过程就是一种卷积滤镜。所谓卷积滤镜,艰深来说,就是一种组合一组数值的算法。 如果搜寻半径变成2,则会变成25个像素的均匀,搜寻半径越大,就会越含糊。像素个数与搜寻半径的关系如下: (1 + r * 2)的平方 // r = 1,后果为9,r=2,后果为25,r=3 后果为49.通常 NxN会被称之卷积核的大小。比方3x3,5x5。 在均值含糊的计算中,参加的每个像素,对核心像素的奉献值都是一样的,这是均值含糊的特点。也就是,每个像素的权重都是一样的。 正态分布如果应用简略均匀,显然不是很正当,因为图像都是间断的,越凑近的点关系越亲密,越远离的点关系越疏远。因而,加权均匀更正当,间隔越近的点权重越大,间隔越远的点权重越小。 正态分布整好满足上述的的散布需要,如下图所示: 能够看出,正态分布是一种钟形曲线,越靠近核心,取值越大,越远离核心,取值越小。 在计算平均值的时候,咱们只须要将"中心点"作为原点,其余点依照其在正态曲线上的地位,调配权重,就能够失去一个加权平均值。 高斯函数高斯函数是形容正态分布的数学公式。公式如下: 其中,是x的均值,能够了解为正态分布的核心地位,是x的方差。因为计算平均值的时候,中心点就是原点,所以等于0。 如果是二维,则有: 能够看出二维高斯函数中,x和y绝对是独立的。也就是说: G(x,y) = G(x) + G(y)这个个性的益处是,能够把二维的高斯函数,拆解成两个独立的一维高斯函数。能够提高效率。实际上,高斯含糊使用的一维高斯函数,而不是应用二维。 高斯含糊高斯含糊的原理和后面介绍的均值含糊的原理基本上一样,只是均值含糊在计算平均值的时候,周边像素的权重都是一样的。而高斯含糊下,周边像素的权重值却应用高斯函数进行计算,这也是高斯含糊的之所以被称为高斯含糊的起因。 比方当取值为则含糊半径为1的权重矩阵如下: 这9个点的权重总和等于0.4787147,如果只计算这9个点的加权均匀,还必须让它们的权重之和等于1,因而下面9个值还要别离除以0.4787147,失去最终的权重矩阵。 渲染流程理解了高斯含糊的基本原理之后,来看看高斯含糊在webgl中根本渲染流程: 首先,依照失常流程把场景或者图像渲染到一个纹理对象下面,须要应用FrameBuffer性能。对纹理对象进行施加高斯含糊算法,失去最终的高斯含糊的纹理对象。下面第二部,施加高斯含糊算法,个别又会分成两步: 先施加垂直方向的高斯含糊算法;在垂直含糊的根底上进行程度方向的高斯含糊算法。当然,也能够先程度后垂直,后果是一样的。   分两步高斯含糊算法和一步进行两个方向的高斯含糊算法的后果根本是统一的,然而却能够进步算法的效率。 有人可能说,多含糊了一步,为啥还进步了效率。 这么来说吧,如果是3x3大小的高斯含糊:分两步要获取的像素数量是 3 + 3 = 6; 而一步却是3 x 3 = 9。 如果是5x5大小的高斯含糊:分两步要获取的像素数量是 5+5=10; 而一步却是5 x 5=25 。显然能够算法执行效率。 ...

December 30, 2020 · 3 min · jiezi

关于webgl:webgl智慧楼宇发光系列之线性采样下高斯模糊

[toc] webgl智慧楼宇发光系列之线性采样下高斯含糊后面一篇文章 <webgl智慧楼宇发光成果算法系列之高斯含糊>,   咱们晓得了 高斯含糊的实质原理,就是对每个像素,依照正态分布的权重去获取周边像素的值进行均匀,是一种卷积操作。 同时咱们能够指定周边像素的数量,比方能够是3X3,或者5X5,通用的表白就是N X N, 数字N通常称之为含糊半径,这在之前的文章的代码中有体现(uRadius): uniform float uRadius;float gaussianPdf(in float x, in float sigma) { return 0.39894 * exp( -0.5 * x * x/( sigma * sigma))/sigma;}void main() { for( int i = 1; i < MAX_KERNEL_RADIUS; i ++ ) { float x = float(i); if(x > radius){ break; } ... } vec4 result = vec4(1.0) - exp(-diffuseSum/weightSum * uExposure); gl_FragColor = result;}`效率问题通常,咱们心愿含糊的成果越强烈,含糊半径就会要求越大。所谓的半径就是下面的数字N。咱们晓得,要实现一个NxN大小的高斯含糊,在纹理的每个像素点,都须要去获取周边N个像素点。因为1024_1024大小的纹理,要实现33 33 大小的高斯含糊,须要拜访大略1024 1024 _ 33 * 33≈11.4亿个纹理像素,能力利用整个图像的含糊成果。 ...

December 29, 2020 · 1 min · jiezi

关于webgl:从C4D建模到Threejs实现闹钟产品360度展示效果stlobjmtl

演示地址:https://capricorncd.github.io/blog/dist/three/index.html#/ClockObj 源码:https://github.com/capricorncd/blog/tree/master/demos/three C4D文件:clock(R21.207).c4d https://github.com/capricorncd/blog/tree/master/c4d 流程C4D建模导出.obj文件js实现(Three.js),此例开发环境应用的Webpack+React一、C4D建模这里B站有视频教程,这里就不赘述。 在线视频教程:https://www.bilibili.com/video/BV177411P7d1?p=3 建模时留神须要留神的中央: 建模:不能应用面(Disc)建模,在浏览器中不会显示,需改用圆柱体(Cylinder)。贴图:需给每个几何体贴图,不能应用分组贴图。在Three.js中分组贴图不能与单个几何体绑定,顾浏览器中不会显示贴图。二、导出.obj文件C4D中实现建模和贴图后,就能够导出.obj文件。 # 工具栏file -> Export -> Wavefront OBJ(*.obj)其余导出选项默认即可。导出obj文件的同时,会导出一个同名贴图.mtl文件。 三、js实现# "three": "^0.120.1"npm i -S three# oryarn add threesrc/components/ClockObj/core.js import { AmbientLight, DirectionalLight, PerspectiveCamera, Scene, WebGLRenderer} from 'three'import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader'import { MTLLoader } from 'three/examples/jsm/loaders/MTLLoader'import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'let scene, renderer/** * 加载贴图和模型对象文件 * load resource * @returns {Promise<unknown>} */ function loadResource() { return new Promise((resolve, reject) => { const objLoader = new OBJLoader() const mtlLoader = new MTLLoader() // 加载贴图文件 mtlLoader.load('static/clock.mtl', mtl => { // 加载对象前,先设置贴图数据 objLoader.setMaterials(mtl) // 加载对象文件 objLoader.load('static/clock.obj', res => { resolve(res) }, undefined, reject) }, undefined, reject) })}/** * 初始化 */function _init(el, obj) { // 获取容器尺寸, // 如果时window对象,请应用window.innerWidth, window.innerHeight const width = el.offsetWidth const height = el.offsetHeight // 创立场景 scene = new Scene() // 将加载实现的模型对象,增加到场景中 scene.add(obj) // 创立环境光 const ambientLight = new AmbientLight(0x666666) ambientLight.position.set(100, -100, -200) scene.add(ambientLight) // 创立平行光 const light = new DirectionalLight(0xcccccc, 1) light.position.set(2000, 1000, 1000) scene.add(light) // 创立摄像机 const camera = new PerspectiveCamera(45, width / height, 1, 80000) camera.position.set(-150, -50, 300) // 创立renderer renderer = new WebGLRenderer({ // 打消锯齿 antialias: true }) // 设置渲染区域尺寸 renderer.setSize(width, height) // 设置背景色彩 renderer.setClearColor(COLORS.black, 1) // 将场景Canvas DOM元素,增加至父元素中 el.appendChild(renderer.domElement) // 创立场景鼠标管制实例, // 能够对页面上的模型对象进行旋转/缩放等操作 const orbitControls = new OrbitControls(camera, el) orbitControls.addEventListener('change', render) // 执行渲染,指定场景和相机作为参数 function render() { renderer.render(scene, camera) } render()}/** * 导出初始化办法 */export function init(el) { loadResource().then(res => { _init(el, res) }).catch(console.error)}/** * destroy */export function destroy() { if (!scene || !renderer) return scene.remove() renderer.dispose() scene = null renderer = null}src/components/ClockObj/index.jsx ...

October 13, 2020 · 2 min · jiezi

关于webgl:webgl实现径向模糊

径向含糊简介径向含糊,是一种从核心向外呈幅射状,逐步含糊的成果。 因而径向含糊常常会产生一些核心的发散成果,在PS中同样也有径向含糊的滤镜成果。 径向含糊通常也称为变焦含糊。径向含糊(Radial Blur)能够给画面带来很好的速度感,是各类游戏中后处理的常客,也罕用于Sun Shaft等后处理特效中作为光线投射(体积光)的模仿。 在游戏中,经常应用径向含糊来模仿一些静止的动感成果。如鬼泣4中的场景切换特效,和一些技能打击特效;赛车游戏也尝用来模仿动感含糊,如狂野飙车,极品飞车等。 径向含糊还是实现体积光照的一种技术手段之一,如下图: 径向含糊的原理图形学中含糊的大抵原理都是一样的:就是从原像素四周去寻找左近像素的色彩,来独特影响以后的像素色彩。如高斯含糊就是将原像素周围像素的色彩加权求和作为原像素的色彩以达到含糊的目标。 不同的含糊就是取周边像素和加权求和的算法不太一样。径向含糊的特点是从某个中心点向外散射扩散,因而其须要采样的像素来自于以后的像素点和中心点的连线上,通过参数能够管制采样的数量和采样步进的间隔。像素的色彩是由该像素的点与中心点之间连线上进行采样,而后求将这些采样点色彩的加权均匀。依据径向含糊的个性,离指标点越近采样点越密集,反之亦然。 因而,实现径向含糊的大抵流程如下: 确定径向含糊的中心点,个别为画布的中心点,或这个某个对象的中心点在屏幕投影所在的地位。(留神中心点是2维坐标)计算以后像素和中心点的间隔和向量线段。在线段下面进行采样,加权。将含糊的后果和原图进行一个叠加合成(可能须要)webgl实现径向含糊径向含糊是一个后处理过程,径向含糊能够对动态的图片施加成果,也能够对动静渲染的图像施加成果。本示例中将对动静的图像施加成果。先上一张图看看成果: 首先绘制的几个圆环对象,而后对绘制的图像施加径向含糊。 渲染到纹理要施加径向含糊,首先要把圆环绘制到texture对象下面,咱们晓得,通过framebuffer的技术,能够实现把绘制成果输入到贴图对象上。无关framebuffer的技术,不属于本文重点介绍的内容,如果读者不相熟,能够自行查找相干材料。也能够参考:渲染到纹理。 输出贴图对象要把贴图对象输入到屏幕下面,咱们须要结构一个矩形对象,该对象正好是webgl坐标系中的四个顶点,代码如下: function quad() { var pos = [-1,1,0, -1,-1,0, 1,-1,0, 1,1,0], st = [0,1,0,0,1,0, 1,1], idx = [0,1,2,0,2,3]; return { p:pos, t:st, i:idx, }}上述对象能够正好把一个贴图对象残缺的输入到屏幕上(webgl坐标系) 实现径向含糊径向含糊的次要在着色器语言中进行实现,而且次要是在片元着色器中,上面是片元着色器的代码: var ofs = `precision mediump float;uniform sampler2D texture;uniform float strength;varying vec2 vTexCoord;const float tFrag = 1.0 / 512.0;const float nFrag = 1.0 / 30.0;const vec2 centerOffset = vec2(256.0, 256.0);float rnd(vec3 scale, float seed) { return fract(sin(dot(gl_FragCoord.stp + seed, scale)) * 43758.5453 + seed);}vec4 fragRadialBlur(){ vec2 fc = vec2(gl_FragCoord.s, 512.0 - gl_FragCoord.t); vec2 fcc = fc - centerOffset; vec3 destColor = vec3(0.0); float random = rnd(vec3(12.9898, 78.233, 151.7182), 0.0); float totalWeight = 0.0; for (float i = 0.0; i <= 30.0; i++) { float percent = (i + random) * nFrag; float weight = percent - percent * percent; vec2 t = fc - fcc * percent * strength * nFrag; destColor += texture2D(texture, t * tFrag).rgb * weight; totalWeight += weight; } return vec4(destColor / totalWeight, 1.0);}void main(void) { gl_FragColor = fragRadialBlur(); }`;随机函数首先申明了一个rnd函数,此函数是一个简略的随机数生成器,当给定向量和种子值时,将返回0到1范畴内的随机数。应用随机数,是为了让含糊的成果出现肯定的随机状态,而不是依照某种一致性的准则进行含糊。rnd 在每次片元着色器中都会调用,因而要尽量应用轻量化的实现,不然可能会造成性能负载。 ...

August 25, 2020 · 2 min · jiezi

关于webgl:前端图形学从入门到放弃002-教练我想学矩阵

本文纲要矩阵和线性变换是什么?webgl如何实现缩放和旋转?平移不是线性变换,那该怎么办?webgl如何实现平移?明天的主菜是“矩阵”在上一篇中咱们曾经实现了应用webgl绘制图形这个小指标《前端图形学从入门到放弃》001 画一个三角形明天咱们来探讨一个新的话题矩阵咱们都晓得空间中的点咱们能够用向量示意,例如二维立体中的点(1,1)就示意第一象限的点:而多个点就能组成图形,这也是上一篇文章中咱们说过的。理论生产中这些图形往往并不会固定在画面中不懂,例如咱们能够对图形进行旋转,缩放,挪动。实际上这个过程就是将图形的顶点组进行了旋转,缩放,挪动,成为了新的顶点组,再由新的顶点组绘制成新的图形。 例如咱们要将由点A(0,0),B(1,0),C(0,1)组成的三角形放大一倍,那么咱们很容易晓得放大后的点ÂḂĆ的坐标 Âx = Ax2 = 02 = 0Ây = Ay2 = 02 = 0Ḃx = Bx2 = 12 = 2Ḃy = By2 = 02 = 0Ćx = Cx2 = 02 = 0Ćy = Cy2 = 12 = 2数学家嫌这一番操作太过麻烦,而点又是能够写成向量模式的,要是能把操作简化成Â = M*A的模式就再好不过了,于是 ⎡ 2 0 ⎤ Â = ⎪ ⎪ * A ⎣ 0 2 ⎦ 真是一顿操作猛如虎,一句不懂二百五 解剖矩阵举证代表了一种计算,如上咱们应用了一个二维矩阵 ⎡ A B ⎤ ⎣ C D ⎦ 与一个二维向量相乘,会失去一个新的二维向量,计算公式如下 ...

August 9, 2020 · 2 min · jiezi

关于webgl:前端图形学从入门到放弃001-画一个三角形

这是面向前端的图形学课程。心愿用通俗的语言为大家解释清图形学的一些概念。咱们应用的前端最容易webgl,尽管未免还是要接触GLSL,别打我,这曾经是最简略的!咱们开始第一节,矩阵....不还是画个三角形吧! webgl是什么?说人话webgl就是个工具,能够拿来画图的,它依赖于canvas,在canvas上你能够获取2d的上下文,也能够获取webgl的上下文。相似宝可梦新手村能够选蒜头王八也能够选黄皮耗子 所以第一步咱们就创立一个canvas,并获取webgl上下文 <canvas id="canvas" width="1000" height="1000"></canvas><script>var canvas = document.querySelector("#canvas");var gl = canvas.getContext("webgl");</script> 开始擦滑板咯!小画家这样咱们就获取了canvas的掌控权,canvas相当于一块画布,在画图之前咱们得像保障画面是洁净的。gl提供了两个api来做这件事:gl.clearColor与gl.clear gl.clearColor承受(r,g,b,a)的色彩数据,相当于给画布选一个底色;gl.clear用来革除buffer,至于什么是buffer咱们前面会讲。这里只须要晓得gl在没次绘图时都能够记录色彩和深度两个buffer,再次绘制时须要革除! 顶点着色器和片段着色器着色器是足以燃烧你学习webgl的激情的魔鬼。但了解之后你也会感觉恍然大悟!首先三维空间的物体不论多简单都是一些集合体,所谓点动成线,线动成面,面动成体。所以点是形容空间物体的最小单元。 顶点着色器就是用来解决咱们传入的顶点的,在下节课咱们将应用顶点着色器对三角形进行旋转,缩放,平移等操作。而与之绝对的是片段着色器,它次要解决围成图形的上色工作。这并不是明确的定义,但有助于你的了解:顶点着色器提供裁剪空间坐标值而片断着色器提供色彩值 着色器的语言与js不同是一品种c语言,就是大学计算机学的那种须要main函数的语言。所以作色器的语法总结起来就是 // 各种变量 balabalavoid main() { //各种操作 balabala}上文咱们说过能够把变量传入着色器,因为作色器当初咱们来说下常见的变量 Attributes(属性):属性用来指明怎么从缓冲中获取所需数据并将它提供给顶点着色器。 例如你可能在缓冲中用三个32位的浮点型数据存储一个地位值。buffer(缓冲):缓冲是发送到GPU的一些二进制数据序列,通常状况下缓冲数据包含地位,法向量,纹理坐标,顶点色彩值等。 你能够存储任何数据。Uniforms(全局变量):全局变量在着色程序运行前赋值,在运行过程中全局无效。Textures(纹理):纹理是一个数据序列,能够在着色程序运行中随便读取其中的数据。大多数状况咱们不会本人写材质,就间接用纹理了。Varyings(可变量):可变量是一种顶点着色器给片断着色器传值的形式,按照渲染的图元是点, 线还是三角形,顶点着色器中设置的可变量会在片断着色器运行中获取不同的插值。上面咱们来写一个最简略的顶点作色器 gl承受一个字符串的作色器代码,你能够应用数组或任意模式提供这个字符串,我集体通常写在一个script标签中: <script id="vertex-shader-2d" type="notjs"> // 一个属性变量,将会从缓冲中获取数据 attribute vec2 vertPosition; attribute vec3 vertColor; varying vec3 fragColor; // 所有着色器都有一个main办法 void main() { fragColor = vertColor; // gl_Position 是一个顶点着色器次要设置的变量 gl_Position = vec4(vertPosition,0.0,1.0); }</script>这里咱们定义了两个属性vertPosition与vertColor用来存储传入的地位与色彩信息,可变量 fragColor会持续传递给片段着色器应用。内置变量gl_Position的值是四维向量vec4(x,y,z,1.0),前三个参数示意顶点的xyz坐标值,第四个参数是浮点数1.0因为案例中咱们只须要绘制一个立体内的三角形,所以z值被咱们设为0。与之绝对咱们也能写出片段着色器: <script id="fragment-shader-2d" type="notjs"> // 片断着色器没有默认精度,所以咱们须要设置一个精度 // mediump是一个不错的默认值,代表“medium precision”(中等精度) precision mediump float; varying vec3 fragColor; void main() { // gl_FragColor是一个片断着色器次要设置的变量 gl_FragColor = vec4(fragColor, 1.0); // }</script>应用作色器webgl让初学者窝火的另一个起因是,他的编程逻辑很像底层语言,而非javascript,没有很多封装。做一件小事往往要执行多个办法。例如咱们上面要应用作色器,写的这段语法: ...

August 9, 2020 · 2 min · jiezi

关于webgl:前端图形学从入门到放弃001-画一个三角形

这是面向前端的图形学课程。心愿用通俗的语言为大家解释清图形学的一些概念。咱们应用的前端最容易webgl,尽管未免还是要接触GLSL,别打我,这曾经是最简略的!咱们开始第一节,矩阵....不还是画个正方形吧! webgl是什么?说人话webgl就是个工具,能够拿来画图的,它依赖于canvas,在canvas上你能够获取2d的上下文,也能够获取webgl的上下文。相似宝可梦新手村能够选蒜头王八也能够选黄皮耗子 所以第一步咱们就创立一个canvas,并获取webgl上下文 <canvas id="canvas" width="1000" height="1000"></canvas><script>var canvas = document.querySelector("#canvas");var gl = canvas.getContext("webgl");</script> 开始擦滑板咯!小画家这样咱们就获取了canvas的掌控权,canvas相当于一块画布,在画图之前咱们得像保障画面是洁净的。gl提供了两个api来做这件事:gl.clearColor与gl.clear gl.clearColor承受(r,g,b,a)的色彩数据,相当于给画布选一个底色;gl.clear用来革除buffer,至于什么是buffer咱们前面会讲。这里只须要晓得gl在没次绘图时都能够记录色彩和深度两个buffer,再次绘制时须要革除! 定点着色器和片段着色器着色器是足以燃烧你学习webgl的激情的魔鬼。但了解之后你也会感觉恍然大悟!首先三维空间的物体不论多简单都是一些集合体,所谓点动成线,线动成面,面动成体。所以点是形容空间物体的最小单元。 定点着色器就是用来解决咱们传入的顶点的,在下节课咱们将应用顶点着色器对三角形进行旋转,缩放,平移等操作。而与之绝对的是片段着色器,它次要解决围成图形的上色工作。这并不是明确的定义,但有助于你的了解:顶点着色器提供裁剪空间坐标值而片断着色器提供色彩值 着色器的语言与js不同是一品种c语言,就是大学计算机学的那种须要main函数的语言。所以作色器的语法总结起来就是 // 各种变量 balabalavoid main() { //各种操作 balabala}上文咱们说过能够把变量传入着色器,因为作色器当初咱们来说下常见的变量 Attributes(属性):属性用来指明怎么从缓冲中获取所需数据并将它提供给顶点着色器。 例如你可能在缓冲中用三个32位的浮点型数据存储一个地位值。buffer(缓冲):缓冲是发送到GPU的一些二进制数据序列,通常状况下缓冲数据包含地位,法向量,纹理坐标,顶点色彩值等。 你能够存储任何数据。Uniforms(全局变量):全局变量在着色程序运行前赋值,在运行过程中全局无效。Textures(纹理):纹理是一个数据序列,能够在着色程序运行中随便读取其中的数据。大多数状况咱们不会本人写材质,就间接用纹理了。Varyings(可变量):可变量是一种顶点着色器给片断着色器传值的形式,按照渲染的图元是点, 线还是三角形,顶点着色器中设置的可变量会在片断着色器运行中获取不同的插值。上面咱们来写一个最简略的顶点作色器 gl承受一个字符串的作色器代码,你能够应用数组或任意模式提供这个字符串,我集体通常写在一个script标签中: <script id="vertex-shader-2d" type="notjs"> // 一个属性变量,将会从缓冲中获取数据 attribute vec2 vertPosition; attribute vec3 vertColor; varying vec3 fragColor; // 所有着色器都有一个main办法 void main() { fragColor = vertColor; // gl_Position 是一个顶点着色器次要设置的变量 gl_Position = vec4(vertPosition,0.0,1.0); }</script>这里咱们定义了两个属性vertPosition与vertColor用来存储传入的地位与色彩信息,可变量 fragColor会持续传递给片段着色器应用。内置变量gl_Position的值是四维向量vec4(x,y,z,1.0),前三个参数示意顶点的xyz坐标值,第四个参数是浮点数1.0因为案例中咱们只须要绘制一个立体内的三角形,所以z值被咱们设为0。与之绝对咱们也能写出片段着色器: <script id="fragment-shader-2d" type="notjs"> // 片断着色器没有默认精度,所以咱们须要设置一个精度 // mediump是一个不错的默认值,代表“medium precision”(中等精度) precision mediump float; varying vec3 fragColor; void main() { // gl_FragColor是一个片断着色器次要设置的变量 gl_FragColor = vec4(fragColor, 1.0); // }</script>应用作色器webgl让初学者窝火的另一个起因是,他的编程逻辑很像底层语言,而非javascript,没有很多封装。做一件小事往往要执行多个办法。例如咱们上面要应用作色器,写的这段语法: ...

August 9, 2020 · 2 min · jiezi

基于HTML5的WebGL实现的2D3D迷宫小游戏

为了实现一个基于HTML5的场景小游戏,我采用了HT for Web来实现,短短200行代码,我就能实现用“第一人称”来操作前进后退上下左右,并且实现了碰撞检测。 先来看下实现的效果: http://hightopo.com/guide/guide/core/3d/ht-3d-guide.html#ref_collision 或者http://v.youku.com/v_show/id_XMzA5MzUzODc4NA==.html?spm=a2h3j.8428770.3416059.1视频中出现的帧的问题是我屏幕录制器的问题,真正操作的时候不会有,建议用上面的链接自己操作 玩玩,鼠标或者触屏都可以,不过我觉得最方便的还是操作键盘wsad控制上下左右。 我的想法是先把场景布局好,代码如下: createHT([100, -20, 100], '#E74C3C'); createHT([-100, -20, 100], '#1ABC9C'); createHT([100, -20, -100], '#3498DB'); createHT([-100, -20, -100], '#9B59B6'); createCurve([0, -20, 0]);createCircle();这几个都是自定义的函数,createHT为描绘HT形状的图,场景中有四个,所以调用了四次;createCurve是描绘场景中间的黄色的曲线;createCircle是描绘最外层的圆,因为不是全包的圆,所以也是描点画的。 HT中封装了一个组件,ht.Shape(以下简称Shape),能够根据描点来自由描绘图形,可以通过shape.setPoints(pointsArray)将所有的点添加进数组中,并且设置到shape中,然后通过setSegments()设置线段数组信息,也就是用什么样的方式来连接两点,在Shape手册中有着重描写,感兴趣的可以参考HT for Web Shape 手册。抽其中的一个描绘点的函数来看看: function createHT(p3, color){ shape = new ht.Shape(); shape.s({ 'shape.background': null, 'shape.border.width': 10, 'shape.border.color': color, 'all.color': color }); shape.setTall(40); shape.setThickness(5); shape.setPoints([ // draw H {x: 20, y: 0}, {x: 20, y: 100}, {x: 20, y: 50}, {x: 80, y: 50}, {x: 80, y: 0}, {x: 80, y: 100}, // draw T {x: 120, y: 0}, {x: 180, y: 0}, {x: 150, y: 0}, {x: 150, y: 100} ]); shape.setSegments([ // draw H 1, // moveTo 2, // lineTo 1, // moveTo 2, // lineTo 1, // moveTo 2, // lineTo // draw T 1, // moveTo 2, // lineTo 1, // moveTo 2 // lineTo ]); shape.p3(p3); dataModel.add(shape); return shape;} 因为“HT”这个字眼要描绘的点比较多,所以代码看起来有点大,如果你看到如何描绘一个不完全的圆用20行代码来完成,而且包括样式,还是会惊讶的: ...

October 22, 2019 · 2 min · jiezi

Threejs入门之上海外滩

THREE.js入门之上海外滩最近入了three.js的坑,想用three.js做一些demo以便巩固自己最近所掌握的知识点,而且正好赶上国庆放假,随,有了这篇~预览地址: Three.js之上海外滩 欢迎start❤️~本篇虽是关于Three.js入门的文章, 但是太过入门的就不讲了。没意义,网上很多知识,本篇主要是把自己在写demo时候遇到的坑点给记录下来, 有什么不懂的直接去查阅文档或者网上搜,这里要提一下:Three.js的官方文档和例子对于开发者也是挺好的(有中文版!) 废话不多, 先看下效果吧: 代码比较多,就不一一讲解了,本篇主要分为以下几个部分: 初始化代码,创建场景,相机,渲染器,灯光,搭建不规则的地面几何体等搭建东方明珠搭建上海中心大厦搭建环球金融中心搭建金茂大厦随机算法搭建其他建筑物给所有建筑物进行贴图优化搭建黄浦江搭建360全景空间

October 4, 2019 · 1 min · jiezi

WebGL2系列之多采样渲染缓冲对象

在很久很久以前,盘古开辟了天地,他的头顶着天,脚踩着地,最后他挂了。他的毛发变成了森林,他的血液变成了河流,他的肌肉变成了大地。。。。。。卡! 哦,不对,在很久很久以前,你属于我,我拥有你。你还有没有程序员的自我修养啦。不好意思,串戏了,下面进入。。。主题本文适合对webgl、计算机图形学、前端可视化感兴趣的读者。在很久很久以前,使用WebGL1的时候,只能在默认的绘制的缓冲区上面使用MSAA,而不能在帧缓冲区上面实现,更加形象的说就是:MSAA不能用于离屏渲染。如果需要在帧缓冲区(离屏渲染)上面实现去锯齿效果,需要在贴图内容上使用自己实现的post -process的AA,比如: FXAA: https://github.com/mattdesl/g...SMAA http://threejs.org/examples/#...而且在WebGL1中,不能通过上下文来改变MSAA的采样数量,这对于WebGL1下的去锯齿效果有很大影响。 多采样渲染缓冲对象在WebGL2中,有了一个新的特性,叫做Multisampled Renderbuffer,恩,中文呢就叫做: 多采样渲染缓冲对象吧;通过多采样渲染缓冲对象,可以在帧缓冲区的渲染缓冲对象上实现MSAA(multisampled antialiasing), 然后通过下面的流程实现最终实现渲染的去锯齿: `pre-z pass –> rendering pass to FBO –> postprocessing pass –> render to window ##renderbufferStorageMultisample和多采样渲染缓冲对象相关的一个重要的函数就是gl.renderbufferStorageMultisample,下面是函数的签名:gl.renderbufferStorageMultisample(target, samples, internalFormat, width, height); 该函数的第一个target是渲染缓冲对象的“目标”,samples表示采样数,internalFormat表示数据格式,width、height表示渲染缓冲对象的宽高。下面是使用该函数的简单代码片段:var frameBuffer = gl.createFrameBuffer();var colorRenderbuffer = gl.createRenderbuffer();gl.bindRenderbuffer(gl.RENDERBUFFER, colorRenderbuffer);gl.renderbufferStorageMultisample(gl.RENDERBUFFER, 4, gl.RGBA8, FRAMEBUFFER_SIZE.x, FRAMEBUFFER_SIZE.y); gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffer);gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorRenderbuffer);gl.bindFramebuffer(gl.FRAMEBUFFER, null); 这和webgl1 中创建帧缓冲区的代码类似,并没有太大差别,不同的是如下一行:gl.renderbufferStorageMultisample(gl.RENDERBUFFER, 4, gl.RGBA8, FRAMEBUFFER_SIZE.x, FRAMEBUFFER_SIZE.y); 通过gl.renderbufferStorageMultisample方法指定了渲染缓冲对象的多重采样,采样数是4。#多采样纹理附件多采样纹理附件又是什么东西呢,好吧,其实在WebGL2中,没有这个多采样纹理附件,在OPENGL才有,为什么提到这个多采样纹理附件,大部分时间,我们的离屏渲染都需要渲染到一个纹理对象上面,才能进一步使用。在没有多采样纹理附件,只有多采样渲染缓冲对象的情况下,要实现MSAA,只能渲染到渲染缓冲对象上,但是渲染缓冲对象的内容不能直接传递给纹理对象。那么应该怎么做呢? 需要使用另外一个重要的函数:##gl.blitFramebuffer函数通过gl.blitFramebuffer函数,可以把多采样渲染缓冲对象的内容传递给纹理对象。下面是该函数的签名:gl.blitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);该函数的作用就是,把一个帧缓冲区(read framebuffer)上的指定区域像素转移给另外一个帧缓冲区(draw framebuffer)上的指定区域。其中参数srcX0, srcY0, srcX1, srcY1指定read framebuffer上的区域;dstX0, dstY0, dstX1, dstY1 指定draw framebuffer上的区域; mask指定那个buffer的内容会被copy,可选值:* gl.COLOR_BUFFER_BIT* gl.DEPTH_BUFFER_BIT* gl.STENCIL_BUFFER_BITfilter 表示当两个区域大小不同的时候,插值的方式,可以是以下值:* gl.NEAREST* gl.LINEAR下面是代码片段:var renderableFramebuffer = gl.createFramebuffer();......var colorFramebuffer = gl.createFramebuffer();var texture = gl.createTexture();gl.bindTexture(gl.TEXTURE_2D, texture);gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, FRAMEBUFFER_SIZE.x, FRAMEBUFFER_SIZE.y, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);gl.bindTexture(gl.TEXTURE_2D, null); ...

July 15, 2019 · 1 min · jiezi

WebGL-着色器偏导数dFdx和dFdy介绍

本文适合对webgl、计算机图形学、前端可视化感兴趣的读者。偏导数函数(HLSL中的ddx和ddy,GLSL中的dFdx和dFdy)是片元着色器中的一个用于计算任何变量基于屏幕空间坐标的变化率的指令(函数)。在WebGL中,使用的是dFdx和dFdy,还有另外一个函数fwidth = dFdx + dFdy。 偏导数计算在三角形栅格化期间,GPU会同时跑片元着色器的多个实例,但并不是一个pixel一个pixel去执行的,而是将其组织在2x2的一组pixels块中并行执行。偏导数就是通过像素块中的变量的差值(变化率)而计算出来的。dFdx表示的是像素块中右边像素的值减去素块中左边像素的值,而dFdy表示的是下面像素的值减去上面像素的值。如下图所示,图中显示的是渲染的屏幕像素,图中红色区域是一个像素块,p(x,y)表示在屏幕空间坐标系中坐标(x,y)的片元(像素)上的某一个变量,图中显示了dFdx和dFdy的计算过程。 偏导数函数可以用于片元着色器中的任何变量。对于向量和矩阵类型的变量,该函数会计算变量的每一个元素的偏导数。 偏导数函数是纹理mipmaps实现的基础,也能实现一系列算法和效果,特别是哪些依赖于屏幕空间坐标的(比如渲染统一线宽的线框参考我的另外一篇文章:https://www.jianshu.com/p/1a0...。 偏导数和mipmapsMipmaps用于计算纹理的一些列的子图,每个子图都比前一个的尺寸缩小了2倍。 他们用于在纹理缩小(纹理映射到比自身尺寸小的表面)的时候的去锯齿。Mipmaps 对于纹理缓存的一致性也很重要,在遍历一个三角形(的片元)的时候,它会强制获取一个最近的像素比例:这个比例保证三角形上的一个像素尽量对应纹理上的一个像素。 Mipmaps是可以同时可视化效果和性能的少数技术之一。在纹理取样过程中使用偏导数来选择最佳的 mipmap 级数。纹理坐标在屏幕空间中的变化率作为选择mimmap级数的依据,变化率越大,mimap级数越大,反之越小。 面的法线向量计算(flat shader)偏导数函数可以用来在片元着色器中计算当前面(三角形)的法线向量。当前片元的世界坐标系的水平偏导数和垂直偏导数是两个三角形表面上的两个向量,它们的叉乘结果是一个垂直于表面的向量,该向量的归一化结果就是面的法线向量。需要特别注意的是两个向量的叉乘的顺序。下面是GLSL中通过镜头坐标系中坐标计算面法线向量的代码: normalize( cross(dFdx(pos), dFdy(pos)) );关于偏导数函数的应用之一可以参考 “WebGL 单通道wireframe渲染”,更多应用将在后续介绍。 参考文档http://www.aclockworkberry.co... 欢迎关注公众号“ITman彪叔”。彪叔,拥有10多年开发经验,现任公司系统架构师、技术总监、技术培训师、职业规划师。熟悉Java、JavaScript。在计算机图形学、WebGL、前端可视化方面有深入研究。对程序员思维能力训练和培训、程序员职业规划和程序员理财投资有浓厚兴趣。

July 6, 2019 · 1 min · jiezi

WebGL进阶走进图形噪声

导语:大自然蕴含着各式各样的纹理,小到细胞菌落分布,大到宇宙星球表面。运用图形噪声,我们可以在3d场景中模拟它们,本文就带大家一起走进万能的图形噪声。 概述图形噪声,是计算机图形学中一类随机算法,经常用来模拟自然界中的各种纹理材质,如下图的云、山脉等,都是通过噪声算法模拟出来的。 通过不同的噪声算法,作用在物体纹理和材质细节,我们可以模拟不同类型的材质。 基础噪声算法一个基础的噪声函数的入参通常是一个点坐标(这个点坐标可以是二维的、三维的,甚至N维),返回值是一个浮点数值:noise(vec2(x,y))。我们将这个浮点值转成灰度颜色,形成噪声图,具体可以通过编写片元着色器程序来绘制。 上图是各类噪声函数在片元着色器中的运行效果,代码如下: // noise fragment shadervarying vec2 uv;float noise(vec2 p) { // TODO}void main() { float n = noise(uv); // 通过噪声函数计算片元坐标对应噪声值 gl_FragColor = vec4(n, n, n, 1.0);}其中noise(st)的入参st是片元坐标,返回的噪声值映射在片元的颜色上。目前基础噪声算法比较主流的有两类:1. 梯度噪声;2. 细胞噪声; 梯度噪声 (Gradient Noise)梯度噪声产生的纹理具有连续性,所以经常用来模拟山脉、云朵等具有连续性的物质,该类噪声的典型代表是Perlin Noise。 其它梯度噪声还有Simplex Noise和Wavelet Noise,它们也是由Perlin Noise演变而来。 算法步骤梯度噪声是通过多个随机梯度相互影响计算得到,通过梯度向量的方向与片元的位置计算噪声值。这里以2d举例,主要分为四步:1. 网格生成;2. 网格随机梯度生成;3. 梯度贡献值计算;4. 平滑插值 第一步,我们将2d平面分成m×n个大小相同的网格,具体数值取决于我们需要生成的纹理密度(下面以4×4作为例子); #define SCALE 4. // 将平面分为 4 × 4 个正方形网格float noise(vec2 p) { p *= SCALE; // TODO}第二步,梯度向量生成,这一步是根据第一步生成的网格的顶点来产生随机向量,四个顶点就有四个梯度向量; ...

June 12, 2019 · 5 min · jiezi

Canvas2D基础

Canvas2D基础什么是Canvas<canvas>是H5中最受欢迎的元素,在页面上划出一片区域,利用JS在上面画出图形,起初由Apple公司提出,各大浏览器厂商也对其做了不同程度上的实现。canvas中规定了了2D context和3D context(WebGL),目前绝大部分浏览器支持2D context。WebGL的发展还比较缓慢。 基本使用1、toDataURL() 将画好的图像输出为图片 //get data URI of the imagevar imgURI = drawing.toDataURL("image/png");//display the imagevar image = document.createElement("img");image.src = imgURI;document.body.appendChild(image);2、原点是基于canvas元素左上角3、2D Context的两个基本绘画操作 fill and stroke Rectangles(矩形)1、fillRect() context.fillRect(10, 10, 50, 50);//draw a blue rectangle that’s semi-transparentcontext.fillStyle = "rgba(0,0,255,0.5)";context.fillRect(30, 30, 50, 50);2、strokeRect() //draw a red outlined rectanglecontext.strokeStyle = "#ff0000";context.strokeRect(10, 10, 50, 50);//draw a blue outlined rectangle that’s semi-transparentcontext.strokeStyle = "rgba(0,0,255,0.5)";context.strokeRect(30, 30, 50, 50);3、clearRect() //draw a red rectanglecontext.fillStyle = "#ff0000";context.fillRect(10, 10, 50, 50);//draw a blue rectangle that’s semi-transparentcontext.fillStyle = "rgba(0,0,255,0.5)";context.fillRect(30, 30, 50, 50);//clear a rectangle that overlaps both of the previous rectanglescontext.clearRect(40, 40, 10, 10);Drawing Paths1、如何画一个表盘 ...

May 31, 2019 · 2 min · jiezi

基于-HTML5-WebGL-的挖掘机-3D-可视化应用

前言在工业互联网以及物联网的影响下,人们对于机械的管理,机械的可视化,机械的操作可视化提出了更高的要求。如何在一个系统中完整的显示机械的运行情况,机械的运行轨迹,或者机械的机械动作显得尤为的重要,因为这会帮助一个不了解这个机械的小白可以直观的了解机械的运行情况,以及机械的所有可能发生的动作,对于三一或者其它国内国外重工机械的公司能够有一个更好的展示或者推广。挖掘机,又称挖掘机械(excavating machinery),从近几年工程机械的发展来看,挖掘机的发展相对较快,挖掘机已经成为工程建设中最主要的工程机械之一。所以该系统实现了对挖掘机的 3D 可视化,在传统行业一般都是基于 Web SCADA 的前端技术来实现 2D 可视化监控,而且都是 2D 面板部分数据的监控,从后台获取数据前台显示数据,但是对于挖掘机本身来说,挖掘机的模型,挖掘机的动作,挖掘机的运行可视化却是更让人眼前一亮的,所以该系统对于挖机的 3D 模型做出了动作的可视化,大体包括以下几个方面: 前进后退 -- 用户可以通过键盘 wasd 实现前后左右,或者点击 2D 界面 WASD 来实现挖机的前进后退。机身旋转 -- 用户可以通过键盘左右键实现机身的旋转,或者点击 2D 界面 < > 来实现挖机机身的旋转。大臂旋转 -- 用户可点击 2D 界面第一个滑块部分实现大臂的旋转。小臂旋转 -- 用户可点击 2D 界面第二个滑块部分实现小臂的旋转。挖斗挖掘 -- 用户可点击 2D 界面第三个滑块部分实现挖斗部分的旋转挖掘。挖机动画 -- 用户可点击 2D 界面铲子图标,点击之后系统会把挖机本身几个动画做一个串联展示。本篇文章通过对挖掘机可视化场景的搭建,挖机机械动作代码的实现进行阐述,帮助我们了解如何使用 HT 实现一个挖掘机的可视化。 预览地址:基于 HTML5 WebGL 的挖掘机 3D 可视化应用 http://www.hightopo.com/demo/... 界面效果预览挖机机械运动效果 通过上面 gif 图片可以看出挖掘机的几个主要动作。 挖机挖斗运动效果 滑动页面的第三个滑杆控制挖斗的旋转挖掘。 挖机机身运动 通过上面 gif 图片可以看出挖掘机的前进后退以及机身旋转几个运动。 场景搭建该 3D 场景中所有形状都是用 HT 内部的墙面工具进行构建,通过设置墙面透明属性 shape3d.transparent 为 true 以及对构建出的墙面进行贴图来构造出场景中的类似建筑的显示效果,具体的样式可以参考 HT 的 风格手册,场景效果: ...

May 13, 2019 · 3 min · jiezi

WebGL-threejs学习笔记-法向量网格材质MeshNormalMaterial的介绍和创建360度全景天空盒的方法

WebGL学习----Three.js学习笔记(5)点击查看demo演示Demo地址:https://nsytsqdtn.github.io/d...简单网格材质 MeshNormalMaterialMeshNormalMaterial是一种不受渲染时使用的颜色影响的材质,它只与自己每一个面从内到外的法向量有关。法向量在webgl中用处十分广泛,光的反射,以及三维图形的纹理映射都与这个有关。从图中可以看到,网格的每一面渲染的颜色都是不一样的,如果我们想要在物体表面添加法向量,我们可以使用的THREE.ArrowHelper去表示每一个法向量,它的参数为 THREE.ArrowHelper(dir, origin, length, color, headLength, headWidth) **其中参数的意义为:dir:方向,默认是法向量origin:开始的坐标位置 length:辅助线的长度color:辅助线的颜色headLength:头部的长度headWidth:头部的宽度** 对于一个球体,要描述它每一个面的法向量,首先需要对它的每一个面进行遍历,取出这个面上的三个顶点(因为webgl的面都是三角形,所以是三个顶点),通过divideScalar(3)这个函数计算它的中心位置,我们就可以在这个中心位置点上,从内向外引出一个ArrowHelper,来模拟法向量。 for(let i=0;i<sphereGeometry.faces.length;i++){//在每一个面上面循环 let face = sphereGeometry.faces[i];//得到每个面的对象 let centroid = new THREE.Vector3(); //先创建一个vector3对象,要使用这个对象找到每个面的中心 centroid.add(sphereGeometry.vertices[face.a]); // 将这该面的三个顶点的索引传给sphereGeometry.vertices找到其顶点的坐标 //再添加进centroid centroid.add(sphereGeometry.vertices[face.b]); centroid.add(sphereGeometry.vertices[face.c]); centroid.divideScalar(3);//三角形的中心点坐标 let arrow = new THREE.ArrowHelper( face.normal,//face这个面的法向量 centroid, 2, 0xffcc55, 0.5, 0.5);//箭头辅助线,相当于把法向量用箭头表示出来 sphere.add(arrow); }其中,centroid.add(sphereGeometry.vertices[face.a])这段代码中的sphereGeometry.vertices存有几何体的所有顶点信息,通过[ ]索引可以取得其中的某一个顶点。face.a还有下面的face.b和c都是该面的顶点索引号,表示这个面是由顶点编号为face.a,face.b,face.c的三个顶点所构成的一个三角形(webgl的面都是三角形),然后我们再计算这三个顶点的中心点。 菜单面板的设置在菜单面板中设置一些MeshNormalmaterial的一些属性,便于去测试这种材质的一些特质其中:**this.visible = meshMaterial.visible;//是否可见 this.wireframe = meshMaterial.wireframe;//是否以线框的方式渲染物体 this.wireframeWidth = meshMaterial.wireframeLinewidth;//线框的宽度 this.transparent = meshMaterial.transparent;//是否透明 this.opacity = meshMaterial.opacity;//透明度,需要transparent为true才有效果 this.side = "front";//边的渲染方式,有三种,前面,后面,还有双面 this.selectMesh = "sphere";//当前选择的几何体 this.shading = "smooth";//着色方式,有平面着色和平滑着色,对一个面很平的几何体几乎看不出区别,如正方体** function initDatGUI() { //设置菜单中需要的参数 controls = new function () { this.rotationSpeed = 0.02; this.visible = meshMaterial.visible;//是否可见 this.wireframe = meshMaterial.wireframe;//是否以线框的方式渲染物体 this.wireframeWidth = meshMaterial.wireframeLinewidth;//线框的宽度 this.transparent = meshMaterial.transparent;//是否透明 this.opacity = meshMaterial.opacity;//透明度,需要transparent为true才有效果 this.side = "front";//边的渲染方式,有三种,前面,后面,还有双面 this.selectMesh = "sphere";//当前选择的几何体 this.shading = "smooth";//着色方式,有平面着色和平滑着色,对一个面很平的几何体几乎看不出区别,如正方体 }; let gui = new dat.GUI(); //将刚刚设置的参数添加到菜单中 let F1 = gui.addFolder("Mesh"); F1.add(controls, "rotationSpeed", 0, 0.1); F1.add(controls, "visible").onChange(function (e) { meshMaterial.visible = e; }); F1.add(controls, "wireframe").onChange(function (e) { meshMaterial.wireframe = e; }); F1.add(controls, "wireframeWidth",0,10).onChange(function (e) { meshMaterial.wireframeWidth = e; }); F1.add(controls, "transparent").onChange(function (e) { meshMaterial.transparent = e; }); F1.add(controls, "opacity",0,1).onChange(function (e) { meshMaterial.opacity = e; }); F1.add(controls, "side",["front","back","double"]).onChange(function (e) { switch (e) { case "front": meshMaterial.side = THREE.FrontSide; break; case "back": meshMaterial.side = THREE.BackSide; break; case "double": meshMaterial.side = THREE.DoubleSide; break; } meshMaterial.needsUpdate = true;//要在程序中让材质更新需要添加这一句话 }); F1.add(controls, "selectMesh",["sphere","cube","plane"]).onChange(function (e) { //先把场景的物体清除,再来添加 scene.remove(cube); scene.remove(sphere); scene.remove(plane); switch (e) { case "sphere": scene.add(sphere); break; case "cube": scene.add(cube); break; case "plane": scene.add(plane); break; } }); F1.add(controls, "shading",["flat","smooth"]).onChange(function (e) { switch (e) { case "flat": meshMaterial.shading = THREE.FlatShading; break; case "smooth": meshMaterial.shading = THREE.SmoothShading; break; } meshMaterial.needsUpdate = true;//要在程序中让材质更新需要添加这一句话 }); }**注意在程序运行过程中想要改变材质的属性,需要在改完以后,添加一句meshMaterial.needsUpdate = true,这样才能更新成功。** ...

April 26, 2019 · 4 min · jiezi

WebGL-threejs学习笔记-创建threejs代码的基本框架

WebGL学习----Three.js学习笔记(1)webgl介绍WebGL是一种3D绘图协议,它把JavaScript和OpenGL ES 2.0结合在一起,通过增加OpenGL ES 2.0的一个JavaScript绑定,WebGL可以为HTML5 Canvas提供硬件3D加速渲染。 WebGL技术标准免去了开发网页专用渲染插件的麻烦,可被用于创建具有复杂3D结构的网站页面,甚至可以用来设计3D网页游戏。 原生的WebGl比较复杂,主要通过对顶点着色器和片元着色器的操作,来实现渲染,但实现起来比较复杂,需要一定的数学基础,但更多的是需要学习基础的耐心。 Three.js介绍Three.js是一个js的开源框架,它把webgl的所有东西都封装好了,我们不再需要去了解webgl那些比较麻烦的细节,直接在此框架上进行开发,既方便,又快捷,而且很容易就能学习,相对于原生的webgl花100多行代码画几个三角形,Three.js只需要几行代码就能实现更复杂的3D效果。 下载地址: https://github.com/mrdoob/thr...。 环境搭建为了以后的学习方便,首先是搭建一个万能框架,所有的three.js开发都可以在此框架上进行。 <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Three.js</title> <script src="../../../Import/three.js"></script> <script src="../../../Import/stats.js"></script> <script src="../../../Import/Setting.js"></script> <style type="text/css"> div#canvas-frame { border: none; cursor: pointer; width: 100%; height: 850px; background-color: #333333; } </style> <script> let renderer; function initThree() { //TODO } let camera; function initCamera() { //TODO } let scene; function initScene() { //TODO } let light; function initLight() { //TODO } let cube; function initObject() { //TODO } //提前定义好的一个功能文件,方便以后的每一个程序调用 function initSetting() { loadAutoScreen(camera,renderer);//自适应屏幕 loadFullScreen();//网页全屏播放 loadStats();//性能检测插件 } function threeStart() { initSetting(); initThree(); initCamera(); initScene(); initLight(); initObject(); animation(); } function animation(){ renderer.clear(); renderer.render(scene,camera); stats.update(); requestAnimationFrame(animation); } </script></head><body onload="threeStart()"><div id="canvas-frame"></div></body></html>其中Setting.js是我写在另一个文件里面的功能文件,把一些常用的功能放在里面,方便以后写的程序可以直接去调用Setting.js的代码如下: ...

April 24, 2019 · 2 min · jiezi

WebGL-threejs学习笔记-纹理贴图模拟太阳系运转

纹理贴图的应用以及实现一个太阳系的自转公转点击查看demo演示demo地址:https://nsytsqdtn.github.io/d...three.js中的纹理纹理贴图是通过将图像应用到对象的一个或多个面,来为3D对象添加细节的一种方法。可以使用TextureLoader类的load方法来加载纹理 function loadImgTexture(){ var loader = new THREE.TextureLoader(); loader.load("metal-rust.jpg",function(texture){ var geometry = new THREE.BoxGeometry(10,10,10); var material = new THREE.MeshBasicMaterial({color:0x739783,map:texture}); mesh = new THREE.Mesh(geometry,material); scene.add(mesh); })}实现效果如下: 八大行星的贴图资源网上到处都可以找到,这里给一个还不错的地址:https://tieba.baidu.com/p/487... 完整太阳系代码: <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Three.js</title> <script src="../../Import/three.js"></script> <script src="../../Import/stats.js"></script> <script src="../../Import/Setting.js"></script> <script src="../../Import/OrbitControls.js"></script> <style type="text/css"> div#canvas-frame { border: none; cursor: pointer; width: 100%; height: 850px; background-color: #333333; } </style></head><body onload="threeStart()"><script> let renderer; function initThree() { width = document.getElementById('canvas-frame').clientWidth; height = document.getElementById('canvas-frame').clientHeight; renderer = new THREE.WebGLRenderer({ antialias : true }); renderer.setSize(width,height); document.getElementById("canvas-frame").appendChild(renderer.domElement); renderer.setClearColor(0x333333, 1.0); } let camera; function initCamera() { camera = new THREE.PerspectiveCamera(45, width / height, 1, 10000); camera.position.x = 0; camera.position.y = 500; camera.position.z = 2000; camera.up.x = 0; camera.up.y = 1; camera.up.z = 0; camera.lookAt(0,0,0); } let scene; function initScene() { scene = new THREE.Scene(); let bgTexture = new THREE.TextureLoader().load("../../Image/universe.jpg");//背景贴图 scene.background = bgTexture;//把场景的背景设置为一张图片 } let light; function initLight() { light = new THREE.PointLight(0xffffff,1);//点光源,模拟太阳 light.position.set(0,0,0); scene.add(light); light = new THREE.AmbientLight(0xffffff,0.3);//环境光 scene.add(light); } let sun; let earth; let tuxing; let shuixing; let jinxing; let huoxing; let muxing; let tianwangxing; let haiwangxing; function initObject() { let geometry = new THREE.SphereGeometry(15, 50, 50); let texture = THREE.ImageUtils.loadTexture("../../Image/shuixing.jpg");//定义一个贴图,并从本地文件中加载 let material = new THREE.MeshBasicMaterial({map:texture});//把上面定义的texture设置为星球的纹理材质 shuixing = new THREE.Mesh(geometry,material); shuixing.position.set(0,0,150);//3 scene.add(shuixing); geometry = new THREE.SphereGeometry(25, 50, 50); texture = THREE.ImageUtils.loadTexture("../../Image/jinxing.jpg"); material = new THREE.MeshBasicMaterial({map:texture}); jinxing = new THREE.Mesh(geometry,material); jinxing.position.set(0,0,200);//4 scene.add(jinxing); geometry = new THREE.SphereGeometry(30, 50, 50); texture = THREE.ImageUtils.loadTexture("../../Image/earth.jpg"); material = new THREE.MeshBasicMaterial({map:texture}); earth = new THREE.Mesh(geometry,material); earth.position.set(0,0,300);//6 scene.add(earth); geometry = new THREE.SphereGeometry(20, 50, 50); texture = THREE.ImageUtils.loadTexture("../../Image/huoxing.jpg"); material = new THREE.MeshBasicMaterial({map:texture}); huoxing = new THREE.Mesh(geometry,material); huoxing.position.set(0,0,400);//8 scene.add(huoxing); geometry = new THREE.SphereGeometry(65, 50, 50); texture = THREE.ImageUtils.loadTexture("../../Image/muxing.jpg"); material = new THREE.MeshBasicMaterial({map:texture}); muxing = new THREE.Mesh(geometry,material); muxing.position.set(0,0,600);//500 12 scene.add(muxing); geometry = new THREE.TorusGeometry(80,6,20,20); texture = THREE.ImageUtils.loadTexture("../../Image/muxinghuan.jpg"); material = new THREE.MeshBasicMaterial({map:texture}); let muxinghuan = new THREE.Mesh(geometry,material); muxinghuan.rotation.x = 1.4;//木星环 muxing.add(muxinghuan); geometry = new THREE.SphereGeometry(55, 50, 50); texture = THREE.ImageUtils.loadTexture("../../Image/tuxing.jpg"); material = new THREE.MeshBasicMaterial({map:texture}); tuxing = new THREE.Mesh(geometry,material); tuxing.position.set(0,0,800);//16 scene.add(tuxing); geometry = new THREE.TorusGeometry(65,8,20,20); texture = THREE.ImageUtils.loadTexture("../../Image/tuxinghuan.jpg"); material = new THREE.MeshBasicMaterial({map:texture}); let tuxinghuan = new THREE.Mesh(geometry,material); tuxinghuan.rotation.x = 1.4; tuxing.add(tuxinghuan); geometry = new THREE.SphereGeometry(45, 50, 50); texture = THREE.ImageUtils.loadTexture("../../Image/tianwangxing.jpg"); material = new THREE.MeshBasicMaterial({map:texture}); tianwangxing = new THREE.Mesh(geometry,material); tianwangxing.position.set(0,0,950);//19 scene.add(tianwangxing); geometry = new THREE.SphereGeometry(40, 50, 50); texture = THREE.ImageUtils.loadTexture("../../Image/haiwangxing.jpg"); material = new THREE.MeshBasicMaterial({map:texture}); haiwangxing = new THREE.Mesh(geometry,material); haiwangxing.position.set(0,0,1100);//22 scene.add(haiwangxing); geometry = new THREE.SphereGeometry(120, 50, 50); texture = THREE.ImageUtils.loadTexture("../../Image/sun.jpg"); material = new THREE.MeshBasicMaterial({map:texture, emissive: 0xffffff,side: THREE.DoubleSide,}); sun = new THREE.Mesh(geometry,material); sun.position.set(0,0,0); scene.add(sun); //在太阳外面设置一层透明的罩,看上去更像太阳 let canvas = document.createElement( 'canvas' ); canvas.width = 256; canvas.height = 256; let context = canvas.getContext( '2d' ); let gradient = context.createRadialGradient( canvas.width / 2, canvas.height / 2, 0, canvas.width / 2, canvas.height / 2, canvas.width / 2 );//创建一个圆形渐变对象 gradient.addColorStop( 0.1, 'rgba(255,60,0,0.01)' );//内圈的颜色 gradient.addColorStop( 1, 'rgba(255,125,0,0.5)' );//最外面的颜色 context.fillStyle = gradient; context.fillRect( 0, 0, canvas.width, canvas.height );//将一个画布使用圆形渐变对象进行填充 let shadowTexture = new THREE.CanvasTexture( canvas );//把刚刚画好的画布拿来作为画布贴图 let shadowMaterial = new THREE.MeshBasicMaterial( { map: shadowTexture,transparent:true } );//用此贴图来当材质 let shadowGeo = new THREE.SphereGeometry( 130,50,50); let shadowMesh; shadowMesh = new THREE.Mesh( shadowGeo, shadowMaterial ); shadowMesh.position.y = 0; shadowMesh.position.x = 0; sun.add( shadowMesh ); //画线,把太阳系每个星球运行轨迹画出来 for(let j=3;j<=22;j++) { if (j==3||j==4||j==6||j==8||j==12||j==16||j==19||j==22){ let radius = 50 * j; let lineGeometry2 = new THREE.Geometry(); for (let i = 0; i <= 2 * Math.PI; i += Math.PI / 30) { lineGeometry2.vertices.push(new THREE.Vector3(radius * Math.cos(i), 0, radius * Math.sin(i), 0)) } let material2 = new THREE.LineBasicMaterial({color: 0x8f6cab}) let cycleMesh = new THREE.Line(lineGeometry2, material2); cycleMesh.position.set(0, 0, 0); scene.add(cycleMesh); } } } //每个星球的自转函数 function zizhuan() { sun.rotation.y = sun.rotation.y+0.008>=2*Math.PI?0:sun.rotation.y+0.008; shuixing.rotation.y = shuixing.rotation.y+0.005>=2*Math.PI?0:shuixing.rotation.y+0.005; jinxing.rotation.y = jinxing.rotation.y+0.003>=0?2*Math.PI:jinxing.rotation.y+0.003; earth.rotation.y = earth.rotation.y+0.012>=2*Math.PI?0:earth.rotation.y+0.012; huoxing.rotation.y = huoxing.rotation.y+0.01>=2*Math.PI?0:huoxing.rotation.y+0.01; tuxing.rotation.y = tuxing.rotation.y+0.04>=2*Math.PI?0:tuxing.rotation.y+0.06; muxing.rotation.y = muxing.rotation.y+0.03>=2*Math.PI?0:muxing.rotation.y+0.08; tianwangxing.rotation.z = tianwangxing.rotation.z+0.015>=2*Math.PI?0:tianwangxing.rotation.z+0.015; haiwangxing.rotation.y = haiwangxing.rotation.y+0.02>=2*Math.PI?0:haiwangxing.rotation.y+0.02; } let shuixingAngle = 0; let jinxingAngle = 0; let earthAngle = 0; let huoxingAngle = 0; let tuxingAngle = 0; let muxingAngle = 0; let tianwangxingAngle = 0; let haiwangxingAngle = 0; //每个星球的公转函数 function gongzhuan() { shuixingAngle = shuixingAngle+0.03>=2*Math.PI?0:shuixingAngle +0.03; shuixing.position.set(150*Math.sin(shuixingAngle) ,0,150*Math.cos(shuixingAngle)); jinxingAngle = jinxingAngle-0.02>=0?2*Math.PI:jinxingAngle -0.02; jinxing.position.set(200*Math.sin(jinxingAngle) ,0,200*Math.cos(jinxingAngle)); earthAngle = earthAngle+0.015>=2*Math.PI?0:earthAngle +0.015; earth.position.set(300*Math.sin(earthAngle) ,0,300*Math.cos(earthAngle)); huoxingAngle = huoxingAngle+0.01>=2*Math.PI?0:huoxingAngle +0.01; huoxing.position.set(400*Math.sin(huoxingAngle) ,0,400*Math.cos(huoxingAngle)); muxingAngle = muxingAngle+0.006>=2*Math.PI?0:muxingAngle +0.006; muxing.position.set(600*Math.sin(muxingAngle) ,0,600*Math.cos(muxingAngle)); tuxingAngle = tuxingAngle+0.004>=2*Math.PI?0:tuxingAngle +0.004; tuxing.position.set(800*Math.sin(tuxingAngle) ,0,800*Math.cos(tuxingAngle)); tianwangxingAngle = tianwangxingAngle+0.003>=2*Math.PI?0:tianwangxingAngle +0.003; tianwangxing.position.set(950*Math.sin(tianwangxingAngle) ,0,950*Math.cos(tianwangxingAngle)); haiwangxingAngle = haiwangxingAngle+0.002>=2*Math.PI?0:haiwangxingAngle +0.002; haiwangxing.position.set(1100*Math.sin(haiwangxingAngle) ,0,1100*Math.cos(haiwangxingAngle)); } function initSetting() { loadAutoScreen(camera,renderer); loadFullScreen(); loadStats(); } let controller; function threeStart() { initThree(); initCamera(); initScene(); initLight(); initObject(); initSetting(); controller = new THREE.OrbitControls(camera,renderer.domElement); controller.target = new THREE.Vector3(0,0,0); controller.autoRotate = true; animation(); } function animation() { zizhuan(); gongzhuan(); renderer.clear(); renderer.render(scene,camera); requestAnimationFrame(animation); stats.update(); }</script><div id="canvas-frame"></div></body></html>注意从本地读取图片或者其他文件时,浏览器一般是不允许的,所以会出现加载不了纹理或者背景图片的情况,只能在Chrome浏览器的属性--目标的最后,加上 --allow-file-access-from-files(前面有一个空格),就可以从这个打开的快捷方式中去读取到本地文件,如果是Edge浏览器似乎就直接可以。也可以使用npm或者tomcat搭建一个本地服务器,然后把图片放进去就可以直接读取了。 ...

April 24, 2019 · 4 min · jiezi

WebGL-threejs学习笔记-阴影与实现物体的动画

实现物体的旋转、跳动以及场景阴影的开启与优化本程序将创建一个场景,并实现物体的动画效果运行的结果如图: 完整代码如下: <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Three.js</title> <script src="../../../Import/three.js"></script> <script src="../../../Import/stats.js"></script> <script src="../../../Import/Setting.js"></script> <script src="../../../Import/OrbitControls.js"></script> <script src="../../../Import/dat.gui.min.js"></script> <style type="text/css"> div#canvas-frame { border: none; cursor: pointer; width: 100%; height: 850px; background-color: #333333; } </style></head><body onload="threeStart()"><div id="canvas-frame"></div><script> //控制面板中需要的两个数据 let control = new function () { this.rotationSpeed = 0.01; this.jumpSpeed = 0.03; }; let renderer, camera, scene; let controller; let width,height; //初始化渲染器 function initThree() { width = document.getElementById('canvas-frame').clientWidth; height = document.getElementById('canvas-frame').clientHeight; renderer = new THREE.WebGLRenderer({ antialias: true });//定义渲染器 renderer.setSize(width, height);//设置渲染的宽度和高度 document.getElementById("canvas-frame").appendChild(renderer.domElement);//将渲染器加在html中的div里面 renderer.setClearColor(0x333333, 1.0);//渲染的颜色设置 renderer.shadowMapEnabled = true;//开启阴影,默认是关闭的,太影响性能 renderer.shadowMapType = THREE.PCFSoftShadowMap;//阴影的一个类型,可以不设置对比看效果 } //初始化摄像机 function initCamera() { camera = new THREE.PerspectiveCamera(45, width / height, 1, 10000);//perspective是透视摄像机,这种摄像机看上去画面有3D效果 //摄像机的位置 camera.position.x = 50; camera.position.y = 50; camera.position.z = 50; camera.up.x = 0; camera.up.y = 1;//摄像机的上方向是Y轴 camera.up.z = 0; camera.lookAt(0, 0, 0);//摄像机对焦的位置 //这三个参数共同作用才能决定画面 } //初始化场景 function initScene() { scene = new THREE.Scene(); } //摄像机的控制,可以采用鼠标拖动来控制视野 function cameraControl() { controller = new THREE.OrbitControls(camera, renderer.domElement); controller.target = new THREE.Vector3(0, 0, 0); } //一个很方便的控制面板,方便更改程序的参数 function datGUI() { let gui = new dat.GUI(); //可以设置可以调动的范围 gui.add(control, "rotationSpeed", 0, 0.05); gui.add(control, "jumpSpeed", 0, 0.08); } //初始化灯光 function initLight() { let light = new THREE.SpotLight(0xffffff, 1.0, 0);//点光源 light.position.set(-40, 60, -10); light.castShadow = true;//开启阴影 light.shadowMapWidth = 8192;//阴影的分辨率 light.shadowMapHeight = 8192; scene.add(light); light = new THREE.AmbientLight(0xffffff, 0.2);//环境光,如果不加,点光源照不到的地方就完全是黑色的 scene.add(light); } let cube; let sphere; //初始化物体 function initObject() { //定义了一个地面 let planeGeometry = new THREE.PlaneGeometry(100, 100, 1, 1); let planeMaterial = new THREE.MeshLambertMaterial({ color: 0xcccccc, }); let plane = new THREE.Mesh(planeGeometry, planeMaterial); plane.rotation.x = -0.5 * Math.PI; plane.position.x = 15; plane.receiveShadow = true;//开启地面的接收阴影 scene.add(plane);//添加到场景中 //定义了一个方块 let cubeGeometry = new THREE.BoxGeometry(4, 4, 4); let cubeMaterial = new THREE.MeshLambertMaterial({ color: 0xff1111, }); cube = new THREE.Mesh(cubeGeometry, cubeMaterial); cube.position.x = -4; cube.position.y = 3; cube.position.z = 0; cube.castShadow = true;//开启阴影 scene.add(cube); //定义了一个球体 let sphereGeometry = new THREE.SphereGeometry(4, 100, 100); let sphereMaterial = new THREE.MeshLambertMaterial({ color: 0xba7890, }); sphere = new THREE.Mesh(sphereGeometry, sphereMaterial); sphere.position.x = 20; sphere.position.y = 4; sphere.position.z = 2; sphere.castShadow = true;//开启阴影 scene.add(sphere); } //方块的自动旋转 function cubeRotation() { cube.rotation.x += control.rotationSpeed; cube.rotation.y += control.rotationSpeed; cube.rotation.z += control.rotationSpeed; } let step = 0; //球体的抛物线运动轨迹 function boxJump() { step += control.jumpSpeed; sphere.position.x = 20 + 10 * (Math.cos(step)); sphere.position.y = 4 + 10 * (Math.abs(Math.sin(step))); } //定义的一个功能文件 function initSetting() { loadAutoScreen(camera,renderer); loadFullScreen(); loadStats(); } //主函数 function threeStart() { initThree(); initCamera(); initScene(); initLight(); initObject(); cameraControl(); datGUI(); initSetting(); animation(); } //动画 function animation() { cubeRotation();//方块旋转函数 boxJump();//球体运动函数 stats.update();//更新性能检测器 renderer.clear(); renderer.render(scene, camera);//开始渲染 requestAnimationFrame(animation);//重复执行此函数,不停的渲染,达到动画的效果 }</script></body></html>其中OrbitControls.js和dat.gui.min.js这两个文件都是Three.js自带的两个很好用的工具,第一个是可以让摄像机有轨道地进行移动,而不用再自己写函数去实现,第二个是一个轻量级的图形用户界面库(GUI 组件),使用这个库可以很容易地创建出能够改变代码变量的界面组件,方便我们测试程序。 ...

April 24, 2019 · 2 min · jiezi

分享数百个 HT 工业互联网 2D 3D 可视化应用案例

过去的 2018 年,我们认为是国内工业互联网可视化的元年,图扑软件作为在工业可视化领域的重度参与者,一线见证了众多 HTML5/Web 化、2D/3D 化的项目在工业界应用落地,我们觉得有必要在此分享下过去一年,基于 HT 实施的数百个工业互联网 2D 3D 可视化应用案例,希望能激发行业和学术工作者对可视化的深度思考,为推进国内工业互联网发展出份薄力。 数百个工业互联网2D/3D可视化案例集: http://www.hightopo.com/demos... 提到工业互联网往往会涉及:物联网、IoT、5G、数字孪生、边缘计算、PaaS平台、SaaS应用、产业互联网、互联网+、工业4.0、智慧城市、智慧园区、智慧楼宇、智能制造等概念,但本文将围绕可视化的话题,围绕更基础的 HTML5/WebGL/WebVR 等底层技术,我们觉得业界还没达到智能化、平台化的成熟阶段,走得太快即使是 GE Predix 也会从明星变流星,了解垂直行业需求,采集足够多有效数据,做好实时的、稳定的、美观的、Web 化的 2D 和 3D 数据可视化呈现,是工业互联网需要走好的第一步。 3D城市建筑群: http://www.hightopo.com/demo/...3D动车站: https://hightopo.com/demo/Bul...3D海上钻井平台: http://www.hightopo.com/demo/...3D水泥工厂工艺流程: http://www.hightopo.com/demo/...3D转油站管网设备监控系统: http://www.hightopo.cn/demo/t...3D高炉炼铁工业流程: http://www.hightopo.com/demo/...3D机房可视化:http://www.hightopo.com/demo/... 正如图扑软件合作伙伴研华科技董事长刘克振所言: 先要实现数据的可视化,生产和经营管理改善达到15%后再一点一滴的往前进。 研华科技 WISE-PaaS 工业物联网云平台,基于 HT 图形组件技术,集成边缘计算和 IoT 云平台,提供从边缘感知及设备到云的数据采集、分析、可视化软件服务,协助系统集成商和制造商快速开发各垂直产业的应用,形成新形态的 IIoT 云端商务模式。2018 年研华物联网共创峰会上,研华发布了基于 Hightopo/HT for Web 的最新物联网平台架构 WISE-PaaS 3.0,WISE-PaaS/SaaS Composer 实现了流程可视化云端组态工具;支援客制化绘图元件,可将应用场景导入 3D 建模绘制与互动,并以毫秒等级的画面刷新速度,搭配 WISE-PaaS/Dashboard 将关键管理数据以视觉直观呈现,协助萃取数据价值与提升运营效率。 3D园区能耗管理系统:2D污水处理工业流程:WISE-PaaS软件平台架构: 近年来,中国制造2025战略的提出为制造业转型升级指明了方向,先进制造业作为经济增长的新引擎正在逐步发力。最近图扑软件合作伙伴三一集团,登陆浙江卫视《智造将来》节目,三一无人挖掘机被赞“新制造”的开始,节目中三一重机董事长俞宏福向观众介绍,所有三一设备已实现互联,通过大屏幕可以看到,地图上每一个点,都代表一台挖掘机在工作,此 Powered by HT 的挖掘机运维大屏,是又一 HTML5 技术在工业制造领域的经典应用案例。 HT Inside《智造将来》第一季:三一重工实时运维大屏:3D挖掘机: http://www.hightopo.com/demo/... ...

April 22, 2019 · 2 min · jiezi

4个令人震撼的CSS3动画

分享国外几个很棒的CSS3动画,我们希望你能从中获得灵感。移动鼠标或滑动以导航星星选中一个复选框以生成一系列复选框。3D缩略图预览的UI概念无限且催眠的动画。

April 9, 2019 · 1 min · jiezi

学习threejs走过的坑

1.在Chrome下帧数很低在学习的时候绘制了一个三角形,发现在chrome中帧数只有24帧,卡的像个PPT,但是在FF和QQ浏览器下帧数保持在60左右,真的是日了*了,于是上网搜索了解决方案。在浏览器中打开 chrome://flags/#ignore-gpu-blacklist 将黄字的 “Override software rendering list” 启用就可以了

February 26, 2019 · 1 min · jiezi

基于 HTML5 WebGL 的地铁站 3D 可视化系统

前言工业互联网,物联网,可视化等名词在我们现在信息化的大背景下已经是耳熟能详,日常生活的交通,出行,吃穿等可能都可以用信息化的方式来为我们表达,在传统的可视化监控领域,一般都是基于 Web SCADA 的前端技术来实现 2D 可视化监控,本系统采用 Hightopo 的 HT for Web 产品来构造轻量化的 3D 可视化场景,该 3D 场景从正面展示了一个地铁站的现实场景,包括地铁的实时运行情况,地铁上下行情况,视频监控,烟雾报警,电梯运行情况等等,帮助我们直观的了解当前的地铁站。系统中为了帮助用户更直观友好的浏览当前地铁站,提供了三种交互模式:第一人称模式 – 操作就类似行人或车在行进的效果,可以通过键盘鼠标控制前进后退。自动巡检模式 – 该模式下用户不需要任何操作,场景自动前进后退来巡查当前地铁站的场景。鼠标操作模式 – 左键旋转场景,右键平移场景。本篇文章通过对地铁站可视化场景的搭建,动画代码的实现,交互模式的原理解析,以及主要功能点的实现进行阐述,帮助我们了解如何使用 HT 实现一个简单的地铁站可视化。预览地址:基于 HTML5 WebGL 的地铁站 3D 可视化系统 http://www.hightopo.com/demo/…界面简介及效果预览地铁运行效果地铁从站外开到站内的效果为透明度逐渐增加,速度逐渐降低。漫游效果上述为自动巡检的漫游效果,场景自动进行前进旋转。监控设备交互效果当我们点击场景中的监控设备时可以查看当前设备的运行情况,运行数据等信息。场景搭建该系统中的大部分模型都是通过 3dMax 建模生成的,该建模工具可以导出 obj 与 mtl 文件,在 HT 中可以通过解析 obj 与 mtl 文件来生成 3d 场景中的所有复杂模型,当然如果是某些简单的模型可以直接使用 HT 来绘制,这样会比 obj 模型更轻量化,所以大部分简单的模型都是采用 HT for Web 产品轻量化 HTML5/WebGL 建模的方案,具体的解析代码如下: // 分别为 obj 文件地址,mtl 文件地址 ht.Default.loadObj(‘obj/metro.obj’, ‘obj/metro.mtl’, { center: true, // 模型是否居中,默认为 false,设置为 true 则会移动模型位置使其内容居中 r3: [0, -Math.PI/2, 0], // 旋转变化参数,格式为 [rx, ry, rz] s3: [0.15, 0.15, 0.15], // 大小变化参数,格式为 [sx, sy, sz] finishFunc: function(modelMap, array, rawS3) { if(modelMap) { ht.Default.setShape3dModel(‘metro’, array); // 注册一个名字为 metro 的模型 } }});上面通过加载 obj 模型之后注册了一个名字为 metro 的模型,之后如果要使用该模型可以通过以下代码来实现:var node = new ht.Node();node.s({ ‘shape3d’: ‘metro’});上面代码新建了一个 node 对象,通过设置 style 对象的 shape3d 属性可以把模型名称为 metro 用到该 node 对象上去,之后便是我们场景中看到的地铁列车模型。动画代码分析地铁动画代码的实现分析场景中地铁的运行是通过 HT 提供的调度插件来实现,调度的具体用法可以参考 HT for Web 的调度手册,该调度主要用于在指定的时间间隔进行函数回调处理,回调函数的第一个参数为 data 图元,也就是 3D 场景中的模型节点,我们可以判断当前 data 是否为我们刚才创建的 metro 那个节点来进行后续的操作,场景中模拟了一个左开的地铁和一个右开的地铁,两辆地铁会交替出现。在 3D 场景中肯定会有坐标系,HT 中是用 x, y, z 来分别表示三个轴,所以地铁的运动肯定是改变地铁在坐标系中的位置来实现地铁的运行,地铁坐标如下图所示:通过上图可以知道地铁在 3D 场景中的坐标系,如果要实现地铁的移动则只需要将地铁往图中所示红色箭头的方向进行移动,即 x 轴的方向,通过 setX 这个方法不断的修改地铁的位置达到地铁行进的目的,代码中通过 getSpeedByX 以及 getOpacityByX 两个方法来不断获取此时的列车速度以及列车透明度,以下为关键代码实现:let metroTask = { interval: 50, // 每五十秒执行一次 action: (data) => { // 即上文所提回调函数 // 判断当时传进来的节点是否为地铁列车节点 if(data === currentMetro) { // 获取地铁此时的 X 轴位置以及行进的方向 let currentX = data.getX(), direction = data.a(‘direction’); // 根据当前的 X 轴位置获取当前的列车速度 let speed = this.getSpeedByX(currentX); // 根据当前的 X 轴位置获取当前的列车透明度 let opacity = this.getOpacityByX(currentX); // 判断此时 X 轴位置是否超过某个值 即地铁是在某个范围内移动 if(Math.abs(currentX) <= 5000) { // 设置当前的透明度 opacity !== 1 ? currentMetro.s({ ‘shape3d.transparent’: true, ‘shape3d.opacity’: opacity }) : currentMetro.s({ ‘shape3d.transparent’: false}); // 设置当前的 X 轴位置 data.setX(currentX + direction * speed); // 判断此时地铁的速度为 0,所以此时应该执行开门的动画 if(speed === 0) this.doorAnimation(currentMetro, direction); } // 右方向地铁开到头,进行复位 if(currentX > 5000 && direction === 1) { currentMetro = leftMetro; currentMetro.setX(5000); } // 左方向地铁开到头,进行复位 if(currentX < -5000 && direction === -1) { currentMetro = rightMetro; currentMetro.setX(-5000); } } } };dm3d.addScheduleTask(metroTask);通过以上代码可以知道地铁在运行的过程中,主要通过修改地铁的 x 轴位置来产生前进的动画,并且需要让地铁在某个区间内进行运动,需要判断边界,而且为了模拟出真实的效果需要根据地铁当前的位置不断获取当前的列车速度以及列车透明度,以下为流程图:上图所示的为地铁进站时候的流程,当地铁停靠完毕关门后需要进行出站,此时我们只需要把地铁位置重新设置一下不为 0 即可,以下为部分代码实现:currentMetro.setX(direction * 10); // 设置出站列车的位置当执行上面那句代码之后上方的 metroTask 调度任务执行到 getSpeedByX 这个方法之后获取到的 speed 速度不为 0,因此此时会继续执行地铁行进的动画,此时的速度就是由慢至快,透明度由深至浅。以下为开门动画执行流程:自动巡检代码的实现分析系统中自动巡检的实现主要是通过修改 3D 场景中的 eye 以及 center 的值,HT 中提供了 rotate,walk 两个方法来控制视角的旋转以及视角的行进,rotate 方法在非第一人称模式时,旋转是以 center 为中心进行旋转,也就是围绕中心物体旋转,当为第一人称时旋转以 eye 为中心进行旋转,也就是旋转眼睛朝向方向。walk 函数同时改变 eye 和 center 的位置,也就是 eye 和 center 在两点建立的矢量方向上同时移动相同的偏移量。该系统中我没有采用 rotate 函数而是自己实现了视角的旋转,因为原本的 rotate 函数旋转某个角度会马上旋转过去而不会有一个旋转的过程,所以我重新实现了旋转的方法,该系统中视角旋转是通过不断修改 center 的数值来实现,具体实现过程原理如下图所示:部分实现代码如下:rotateStep() { // 即上图辅助点 C let fromCenter = this.fromCenter; // 即上图 B 点 let toCenter = this.toCenter; // 每帧转一度 let rotateValue = this.rotateFrame || Math.PI / 180; // 辅助点 C 与 B 点之间建立一个方向向量 let centerVector = new ht.Math.Vector2(toCenter.x - fromCenter.x, toCenter.y - fromCenter.y); let centerVectorLength = centerVector.length(); // 此时旋转百分比 let rotatePercent = rotateValue * this.stepNum / this.curRotateVal; if(rotatePercent >= 1) { rotatePercent = 1; this.stepNum = -2; } let newLength = rotatePercent * centerVectorLength; centerVector.setLength(newLength); let newCenterVector = centerVector.add(fromCenter); // 获取旋转过程中 center 的点信息 let newCenterPosition = [newCenterVector.x, this.personHeight, newCenterVector.y]; // 设置当前 center 的大小 this.g3d.setCenter(newCenterPosition); }通过上述代码就实现了场景中的视角旋转,并且可以通过修改 rotateValue 的值控制旋转的速度。电梯动画代码的实现分析场景中电梯是一个 obj 模型,3D 模型是由最基础的三角形面拼接合成,例如 1 个矩形可以由 2 个三角形构成,1 个立方体由 6 个面即 12 个三角形构成,以此类推更复杂的模型可以由许多的小三角形组合合成。因此 3D 模型定义即为对构造模型的所有三角形的描述,而每个三角形由三个顶点 vertex 构成,每个顶点 vertex 由 x, y, z 三维空间坐标决定,HT 中使用 vs 数组记录构成三角面的所有顶点坐标,所以如果想要让电梯运行起来,只需要把所有的顶点坐标往电梯运行的方向进行平移,以下为部分关键伪代码:// vs 指的是构成电梯模型所有的三角面顶点坐标数组// 由于场景中电梯的运行方向为往对角线右上方运动,所以只需要修改 x 轴以及 y 轴坐标值// xStep yStep 为每次电梯运动的距离setInterval(() => { // i+3 是因为 vs 数组的顺序为 x, y, z 轴 所以每次 i 偏移三个单位大小 for(let i = 0, l = vs.length; i < l; i = i + 3) { // 该顶点坐标下一个 x 轴坐标的值 let nextX = vs[i] - xStep; // 该顶点坐标下一个 y 轴坐标的值 let nextY = vs[i + 1] + yStep; vs[i] = nextX < -0.5 ? 0.5 - (Math.abs(nextX) - 0.5) : nextX; vs[i + 1] = nextY > 0.5 ? -0.5 + (Math.abs(nextY) - 0.5) : nextY; }}, 200);电梯运动动画如下图所示:监控功能展示及介绍视频监控当点击场景中的摄像头之后右侧顶部会显示出当前摄像头的监控画面,以下为实现效果图:烟雾报警监控烟雾报警会根据后台实时传递过来的状态值来变换当前烟雾报警模型的颜色,红色为报警状态,以下为实现效果图:电视列车到站时间监控日常地铁站中会有专门的电视来展示下一班地铁到站的时间表,该系统中也模拟该效果,不过该系统暂时做了电视的模型,时间暂无对接,以下为效果图:场景监控交互3D 场景中交互是比较简单的,主要是点击摄像头展示 2D 监控面板,在 2D 界面中主要是切换三种交互模式,三种交互模式为互斥的关系,以下是 3D 交互注册事件代码:g3d.mi((e) => { let {g2d, dm2d} = this; // 为点击类型 if(e.kind === ‘clickData’) { // data 为当前点击的图元 let data = e.data; // 当前图元的 shape3d 类型 let shape3d = data.s(‘shape3d’); // 判断当前 shape3d 类型是否为摄像头 if(shape3d && shape3d.indexOf(‘摄像头’) > 0) { let cameraPanel = dm2d.getDataByTag(‘cameraPanel’); // toggle 切换摄像头 2d 面板 g2d.isVisible(cameraPanel) ? cameraPanel.s(‘2d.visible’, false) : cameraPanel.s(‘2d.visible’, true); } } // 为点击 3d 场景背景类型 if(e.kind === ‘clickBackground’) { let cameraPanel = dm2d.getDataByTag(‘cameraPanel’); // 隐藏摄像头 2d 面板 g2d.isVisible(cameraPanel) && cameraPanel.s(‘2d.visible’, false); }});总结工业互联网将人,数据和机器连接起来,地铁站 3D 可视化系统则是一个很好的展现,HT 的轻量化,数据的可视化,机器的可视化,资产的管理化帮助我们更好的监控。而物联网将通过各种信息传感设备,实时采集任何需要监控、连接、互动的物体或过程等各种需要的信息,通过与 HT 的结合更好的展现出可视化的优势,当然地铁站还可以与 VR 进行结合,在各地科技展会中我们可以见到各种 VR 场景操作,HT 中也可以结合 VR 设备进行操作,可以戴上设备在地铁站中漫游,让人有身临其境的感觉,由于场景本身的轻量化,所以 VR 场景下的流畅性也是十分的高,让用户不会有头晕的感觉。当然系统本身也可以在移动端运行,以下为移动端运行截图:程序运行截图: ...

February 18, 2019 · 3 min · jiezi

学习 PixiJS — 视觉效果

平铺精灵平铺精灵是一种特殊的精灵,可以在一定的范围内重复一个纹理。你可以使用它们创建无限滚动的背景效果。要创建平铺精灵,需要使用带有三个参数的 TilingSprite 类(PIXI.extras.TilingSprite)用法:let tilingSprite = new PIXI.extras.TilingSprite(texture, width, height);参数:名称默认值描述texture 平铺精灵的纹理width100平铺精灵的宽度height100平铺精灵的高度除此之外,平铺精灵具有与普通精灵所有相同的属性,并且与普通精灵的工作方式相同。他们还有 fromImage 和 fromFrame 方法,就像普通精灵一样。以下是如何使用名称是 brick.jpg 的100 x 100像素的图像创建200 x 200像素的平铺精灵。并且从画布左上角偏移30像素。以下是关键代码:let tilingSprite = new PIXI.extras.TilingSprite( PIXI.loader.resources[imgURL].texture, 200, 200);tilingSprite.x = 30;tilingSprite.y = 30;下图显示了 brick.jpg 图像以及上面代码的效果。你可以使用 tilePosition.x 和 tilePosition.y 属性来移动平铺精灵使用的纹理。以下是如何将平铺精灵使用的纹理移动30像素。tilingSprite.tilePosition.x = 30;tilingSprite.tilePosition.y = 30;这里不是在移动平铺精灵,而是移动平铺精灵使用的纹理。下图是两种情况的对比。你还可以使用 tileScale.x 和 tileScale.y 属性更改平铺精灵使用的纹理的比例。以下是如何将平铺精灵使用的纹理的大小增加到1.5倍的关键代码:tilingSprite.tileScale.x = 1.5;tilingSprite.tileScale.y = 1.5;原图 与 上面代码实现的效果的对比:tileScale 和 tilePosition 都有一个 set 方法,可以一行代码设置 x 属性和 y 属性。参数:名称默认值描述x0新的 x 属性值y0新的 y 属性值用法:tilingSprite.tilePosition.set(30, 30);tilingSprite.tileScale.set(1.5, 1.5);平铺精灵是创建重复图像模式的便捷方式。因为你可以移动纹理的位置,所以你可以使用平铺精灵创建无缝的滚动背景。这对于许多类型的游戏都非常有用。让我们来看看如何做到这一点。首先,从无缝平铺图像开始。无缝图像是图案在各方面匹配的图像。如果并排放置图像的副本,它们看起来就像是一个连续的大图像,上面示例中用到的 brick.jpg 就是这种图像。接下来,使用此图像创建一个平铺精灵。然后在游戏循环中更新精灵的 tilePosition.x 属性。关键代码:function play() { tilingSprite.tilePosition.x -= 1;}效果图:查看示例你还可以使用此功能创建一个称为视差滚动的伪3D效果。就是在同一位置层叠多个这样的平铺精灵,并使看上去更远的图像移动得比更近的图像慢。就像下面这个示例一样!两张用于做平铺精灵的图像:实现的效果图:查看示例着色精灵有一个 tint 属性,给这个属性赋值一个十六进制颜色值可以改变精灵的色调。我们来试试吧!关键代码:sprite.tint = 0xFFFF660;原图 与 上面代码实现的效果的对比:查看示例每个精灵的 tint 属性默认值是白色(0xFFFFFF),也就是没有色调。如果你想改变一个精灵的色调而不完全改变它的纹理,就使用着色。蒙版Pixi 允许你使用 Graphics (图形)对象来屏蔽任何精灵或具有嵌套子精灵的容器。蒙版是隐藏在形状区域之外的精灵的任何部分的形状。要使用蒙版,先创建精灵和 Graphics 对象。然后将精灵的 mask 属性设置为创建的 Graphics 对象。示例:首先,用皮卡丘的图像创建一个精灵。然后创建一个蓝色正方形并定位在精灵的上方(形状的颜色并不重要)。最后,精灵的 mask 属性设置为创建的正方形对象。这样会只显示正方形区域内精灵的图像。精灵在正方形之外的任何部分都是不可见的。原图 与 使用蒙版后的对比:关键代码://创建精灵let Pikachu = new PIXI.Sprite(PIXI.loader.resources[imgURL].texture);//创建一个正方形对象let rectangle = new PIXI.Graphics();rectangle.beginFill(0x66CCFF);rectangle.drawRect(0, 0, 200, 200);rectangle.endFill();rectangle.x = 100;rectangle.y = 100;//给精灵设置蒙版Pikachu.mask = rectangle;查看示例你还可以为蒙版设置动画,去做出一些有趣的效果。而且如果是用 WebGL 渲染的话,还可以用精灵作为蒙版。下面这个示例是用三张图片做成精灵,然后把一个精灵作为蒙版,并且给蒙版设置动画的示例。效果图:查看示例混合模式blendMode 属性确定精灵如何与其下层的图像混合。如下所示,可以将它们应用于精灵:sprite.blendMode = PIXI.BLEND_MODES.MULTIPLY;以下是可以使用的17种混合模式的完整列表:没有混合NORMAL(正常)对比比较(饱和度模式)SOFT_LIGHT(柔光)HARD_LIGHT(强光)OVERLAY(叠加)对比比较(差集模式)DIFFERENCE(差值)EXCLUSION(排除)减淡效果(变亮模式)LIGHTEN(变亮)COLOR_DODGE(颜色减淡)SCREEN(滤色)ADD(线性减淡,添加)加深效果(变暗模式)DARKEN(变暗)COLOR_BURN(颜色加深)MULTIPLY(正片叠底)色彩效果(颜色模式)HUE(色相)SATURATION(饱和度)COLOR(颜色)LUMINOSITY(明度)这些混合模式和图像编辑器,比如 Photoshop 中使用的混合模式是一样的,如果你想尝试每种混合模式,你可以在 Photoshop 中打开一些图像,将这些混合模式应用于这些图像上,观察效果。注意: WebGL 渲染器仅支持 NORMAL,ADD,MULTIPLY 和 SCREEN 混合模式。任何其他模式都会像 NORMAL 一样。查看示例滤镜Pixi 拥有多种滤镜,可以将一些特殊效果应用于精灵。所有滤镜都在 PIXI.filters 对象中。滤镜是 Pixi 最好的功能之一,因为它们可以让你轻松创建一些特殊效果,否则只有通过复杂的低级 WebGL 编程才能实现这些效果。这是一个如何创建 BlurFilter (模糊滤镜)的示例(其他滤镜遵循相同的格式)://创建一个模糊滤镜let blurFilter = new PIXI.filters.BlurFilter();//设置模糊滤镜的属性blurFilter.blur = 20;//将模糊滤镜添加到精灵的滤镜数组中sprite.filters = [blurFilter];Pixi 的所有显示对象(Sprite 和 Container 对象)都有一个滤镜数组。要向精灵添加滤镜,先创建滤镜,然后将其添加到精灵的滤镜数组中。你可以根据需要添加任意数量的滤镜。sprite.filters = [blurFilter, sepiaFilter, displacementFilter];使用它就像使用其他普通数组一样。要清除所有精灵的滤镜,只需清除数组即可。sprite.filters = [];除了这些属性,所有滤镜还包括额外的 padding 和 uniforms 属性。padding 增加了滤镜区域周围的空间。uniforms 是一个可用于向 WebGL 渲染器发送额外值的对象。在日常使用中,你永远不必担心设置 uniforms 属性。PixiJS 在4.0.0版本的时候,将非核心滤镜转移到新的包 — pixi-filters,现在 PixiJS 内置的滤镜有下面这几种。AlphaFilter用来修改对象透明度的滤镜。 在其他一些文档中,你可能看到的是 VoidFilter 这个滤镜,这是因为在 PixiJS 的4.6.0版本的时候,才添加 AlphaFilter,而弃用 VoidFilter。BlurFilterBlurFilter 将高斯模糊应用于对象。可以分别为x轴和y轴设置模糊强度。BlurXFilterBlurXFilter 将水平高斯模糊应用于对象。BlurYFilterBlurYFilter 将垂直高斯模糊应用于对象。ColorMatrixFilterColorMatrixFilter 类允许你对 显示对象(displayObject) 上每个像素的 RGBA 颜色和 alpha 值应用5x4矩阵变换,以生成一组具有新的 RGBA 颜色和 alpha 值的结果。它非常强大!使用它可是实现黑白、调整亮度、调整对比度、去色、灰度、调整色调,等许多效果。DisplacementFilterDisplacementFilter 类使用指定纹理(称为置换贴图)中的像素值来执行对象的位移。你可以使用这个滤镜来实现扭曲的效果。在这篇文章中已经讲过什么是 DisplacementFilter(置换滤镜)了,并且文章中也有一个不错的示例。FXAAFilter快速近似抗锯齿滤镜。NoiseFilter杂色效果滤镜。注意:Pixi 的滤镜仅适用于 WebGL 渲染,因为 Canvas 绘图 API 太慢而无法实时更新它们。这里有一个示例,包含了 Pixi 中绝大部分的滤镜。查看示例视频纹理你可以将视频用作精灵的纹理,就像使用图像一样容易。使用 Texture 类的 fromVideo 方法就可以创建视频纹理。videoTexture = PIXI.Texture.fromVideo(videoUrl);videoSprite = new PIXI.Sprite(videoTexture);stage.addChild(videoSprite);或者,也可以使用 fromVideoUrl 方法从 URL 地址创建视频纹理。视频纹理只是一个普通的 HTML5 <video> 元素,你可以通过纹理的 baseTexture.source 属性访问它,如下所示:let videoSource = videoTexture.baseTexture.source;然后,你可以使用任何 HTML5 <video> 元素的属性和方法控制视频,例如 play 和 pause 。videoSource.play();videoSource.pause();查看 HTML <video> 元素的完整规范,可以知道所有可以使用的属性和方法。查看示例适配多种分辨率如果你对物理像素、设备独立像素、设备像素比,等一些名词还不熟悉,可以先看看这篇文章 。Pixi 会自动调整像素密度,以匹配运行内容的设备的分辨率。你所要做的就是为高分辨率和低分辨率提供不同的图像,Pixi 将帮助你根据当前的设备像素比选择正确的图像。注意:当你创建高分辨率图像时,可以将“@2x”添加到图像文件名称后面,以说明图像是支持高分辨率的屏幕,例如,Retina 屏幕。同时这也会设置精灵的 baseTexture.resolution 属性(sprite.texture.baseTexture.resolution)。第一步是找出当前的设备像素比。你可以使用 window.devicePixelRatio 方法执行此操作。将此值分配给变量。let displayResolution = window.devicePixelRatio;displayResolution 是一个描述设备像素比的数字。它由运行应用程序的设备自动提供。1是标准分辨率; 2是高密度分辨率; 你将越来越多地发现一些报告3的超高密度显示器。下一步是将此值分配给渲染选项的 resolution 属性。在创建 Pixi 应用时执行此操作,如下所示://创建一个 Pixi应用 需要的一些参数let option = { width: 640, height: 360, transparent: true, resolution: displayResolution}//创建一个 Pixi应用let app = new PIXI.Application(option);然后根据设备像素比选择正确的图像加载到纹理中。如下所示:let texture;if (displayResolution === 2) { //加载高分辨率图像 texture = PIXI.Texture.fromImage(“highResImage@2x.png”);} else { //加载普通分辨率图像 texture = PIXI.Texture.fromImage(“normalResImage.png”);}let anySprite = new PIXI.Sprite(texture);如果你需要知道加载纹理的设备像素比是多少,可以使用 texture 的 baseTexture.resolution 属性(texture.baseTexture.resolution)找出。查看示例绳(Rope)另一个有趣的效果是 Rope。它允许精灵像波浪一样振荡或像蛇一样滑行,如下图所示。首先,从想要变形的事物的图像开始。滑行蛇实际上是一个简单的直线图像,如下图所示。然后决定你想要独立移动蛇的段数。蛇图像的宽度为600像素,因此大约20个片段会产生很好的效果。将图像宽度除以段数,就是每个绳段的长度。let numberOfSegments = 20;let imageWidth = 600;let ropeSegment = imageWidth / numberOfSegments;接下来,创建一个包含20个 Point 对象的数组。每个 Point 的 x 位置(第一个参数)将与下一个 Point 分开一个 ropeSegment 的距离。let points = [];for (let i = 0; i < numberOfSegments; i++) { points.push(new PIXI.Point(i * ropeLength, 0));}现在使用 PIXI.mesh.Rope 方法 new 一个 Rope 对象。这个方法需要两个参数:一个是 Rope 对象使用的纹理一个是包含 Point 对象的数组let snake = new PIXI.mesh.Rope(PIXI.Texture.fromImage(‘snake.png’), points);将蛇添加到一个容器中,这样可以更容易定位。然后将容器添加到舞台并定位它。let snakeContainer = new PIXI.Container();snakeContainer.addChild(snake);stage.addChild(snakeContainer);snakeContainer.position.set(10, 150);现在为游戏循环中的 Point 对象设置动画。通过 for 循环将数组中的每个 Point 按照椭圆形的轨迹移动,形成波浪效果。count += 0.1;for (let i = 0; i < points.length; i++) { points[i].y = Math.sin((i * 0.5) + count) * 30; points[i].x = i * ropeLength + Math.cos((i * 0.3) + count) * numberOfSegments;}查看示例 这里还有一篇文章,讲的是用 Rope 来实现游动的锦鲤的效果,看上去也很好玩。总结本文主要聊了聊平铺精灵、着色、蒙版、混合模式、滤镜、视频纹理、适配多种分辨率、绳(Rope),相关的东西。如果你觉得文字解释的不清楚,在每小节中,都有一个或者多个相应的示例,你可以点开看看,而且示例中的注释也比较清楚。 还有就是因为 PixiJS 的 API 时常有变化,所以要注意 PixiJS 的版本,文中大部分示例用的版本是4.8.2,如果你在尝试使用的时候,发现和示例的效果不一样,可以先检查一下版本。如果文中有错误的地方,还请小伙伴们指出,万分感谢。 ...

February 3, 2019 · 2 min · jiezi

基于 HTML5 的 WebGL 楼宇自控 3D 可视化监控

前言智慧楼宇和人们的生活息息相关,楼宇智能化程度的提高,会极大程度的改善人们的生活品质,在当前工业互联网大背景下受到很大关注。目前智慧楼宇可视化监控的主要优点包括:智慧化 – 智慧楼宇是一个生态系统,像人一样拥有感知能力、自我判断能力以及控制能力。绿色化 – 绿色建筑在耗能、产能以及能源管理方面实现绿色化,楼宇安防实现绿色化监控。运行成本可控制 – 基于可持续发展的要求,现代建筑、商业建筑需运行50年以上,建筑在运行过程中能源消耗巨大,如何降低运营成本降低,使建筑在低碳、环保的状态下健康运行。传统的 智慧楼宇/楼宇自动化/楼宇安防/智慧园区 常会采用 BIM(建筑信息模型 Building information modeling)软件,如 Autodesk 的 Revit 或 Bentley 这类建筑和工程软件,但这些 BIM 建模模型的数据往往过于庞大臃肿,绝大部分细节信息对楼宇自控意义不大,反而影响拖累了行业 Web SCADA 或 Web 组态监控的趋势,所以我们采用以 Hightopo 的 HT for Web 产品轻量化 HTML5/WebGL 建模的方案,实现快速建模、运行时轻量化到甚至手机终端浏览器即可 3D 可视化运维的良好效果。本篇文章通过对智能建筑的建模,页面动画效果的实现,以及页面主要功能点进行阐述,帮助我们了解如何使用 HT 实现一个简单的智慧楼宇可视化监控,以及帮助我们了解智慧楼宇,楼宇自动化的优势。预览地址:基于 HTML5 的 WebGL 楼宇自控 3D 可视化监控 http://www.hightopo.com/demo/…界面简介及效果预览界面通过 2d 图纸叠加在 3d 场景上来实现 2d 界面 与 3d 场景的融合,2d 界面通过自动布局的机制实现了手机端与电脑端的响应式呈现。界面初始化效果界面初始化过程中的动画包括地面路径的实时渲染,楼层的展开,楼层的辉光扫描,楼层报警点动态水波,楼层监测数据面板的实时变化等等。监控界面效果 监控界面包括 人员进入大厦的实时监控,面板中动态刷新人员进入大厦的头像以及当前大厦人数等实时信息。大厦电梯运行情况实时监控,系统中展示电梯当前的运行位置以及是否在运行等信息。大厦某个具体楼层监控数据的实时监控,通过柱状图的形式展示了当前楼层具体信息的大小。大厦管道的实时监控,展示了当前智能建筑所有管道的运行情况。智能建筑建模该 3d 场景中所有的模型均为线段和六面体搭建,相比较通过 3d Max 建模然后通过 obj 导入来说场景中的三角面会少很多,更加的轻量化,例如场景中建筑的楼层,通过 ht.Shape 类绘制,该类中记录着楼层 points 点的信息以及 segments 为 ht.List 类型的线段数组信息,segments 代表着点的连接方式,用于告诉 ht.Shape 利用点的信息来绘制二次贝塞尔曲线或者三次贝塞尔曲线或者直线等信息,相关具体说明请参考 HT for Web 的形状手册,以下为绘制单层的截图:通过上图可以知道构建完一层模型之后其它几层模型均为相同的,只是 y 轴的数值不同,通过叠加几层之后便可形成一幢大楼的轮廓。如果用户需要搭建智慧园区,智慧楼宇等场景,完全可以使用这种基于 HTML5/WebGL 建模的方案,减少考虑使用 BIM 建模模型。场景中的管道以及背景地图路线都为点连线之后构成,只是通过修改线的颜色粗细或者进行贴图来修改线或者面的样式,场景中的电梯为一个颜色为黄色的简单六面体,电梯线也为一条线段而已,所以场景中的模型都是轻量化的建模,使 3d 场景运行渲染的更加流畅,提升用户体验。场景关键动画代码分析1. 地图路线动画代码分析通过上述智能建筑建模的分析我们可以知道线路都是为点与点之间进行连线而生成,所以当我们绘制完地图的路径之后可以得到所有点的信息,假如直线 AB 为地图中的某一条线段,那么我们可以知道点 A 以及点 B 的点的坐标,之后我们可以计算 AB 线段上任意一点 C 的点的坐标,然后通过连接 A 点与 C 点来形成一条与 AB 线段位置方向相同但是大小比 AB 线段短的线,直到 AC 线段的长度等于 AB 线段长度之后再进行下一条路径动画的绘制,以下为关键伪代码展示: // currentIndex 为当前路径绘制到的点的索引 // points 为当前路径所有点的信息 currentPoints 为绘制过程中点的信息 // segments 为当前路径所有点的连接方式信息 currentSegments 为绘制过程中点的连接方式信息 // 即上述此时 A 点信息let fromPoint = points[currentIndex]; // 即上述此时 B 点信息let toPoint = points[currentIndex + 1]; // 通过 AB 两点信息组成一条 AB 方向的向量let pointVector = new ht.Math.Vector2(toPoint.x - fromPoint.x, toPoint.y - fromPoint.y); // 记录该向量的长度,用于判断 AC 是否大于等于 ABlet pointVectorLength = pointVector.length(); let currentPoints = [], currentSegments = [];for(let i = 0; i < currentIndex + 1; i++) { currentPoints.push({ x: points[i].x, y: points[i].y }); currentSegments.push(segments[i]);}通过上述代码可以知道我们获取到了 currentPoints 以及 currentSegments 的信息了,之后便要计算在 fromPoint(A点) 与 toPoint(B点) 连线上点的坐标,即 C 点,以下为计算 C 点的关键伪代码: // addLength 为每次增加的线段长度值,该程序中使用 500 即每次长度增加 500let nextVectorLength = currentVectorLength + addLength, tempPoint; roadData.currentVectorLength = nextVectorLength; // 判断 AC 线段的长度是否大于 AB if(nextVectorLength > pointVectorLength) { nextVectorLength = pointVectorLength; roadData.currentVectorLength = 0; roadData.currentIndex = currentIndex + 1;}pointVector.setLength(nextVectorLength); // 即为 C 点坐标tempPoint = {x: pointVector.x + fromPoint.x, y: pointVector.y + fromPoint.y}; // 往 currentPoints 添加 C 点坐标currentPoints.push(tempPoint); // 往 currentSegments 添加 C 点连接方式,此程序中都为直线连接,所以值都为 2currentSegments.push(2); // roadNode 即为 ht.Shape 类 重新设置 ht.Shape 类点的信息roadNode.setPoints(currentPoints); // 重新设置 ht.Shape 类点的连接信息roadNode.setSegments(currentSegments); 以下为动画代码执行流程图:以下为绘制一条路线动画的截图:程序中通过向量的计算方式来不断获取 C 点的坐标,当然也可以用其它方式来计算 C 点的坐标。2. 水波以及扫描动画代码分析水波以及扫描动画都是通过 HT 提供的修改图标矩形框信息 api 来进行控制,通过调度的方式不断修改图标矩形框大小来产生水波以及扫描的动画效果,调度的具体用法可以参考 HT for Web 的调度手册,以下为水波动画的关键伪代码: // waterWaveNodes 所有水波节点let waterWaveTask = { interval: 100, // 指每隔 100 ms 调用 action 函数一次 action: function(data){ // 判断 waterWaveNodes 是包含 data if(waterWaveNodes.indexOf(data) > -1) { // 获取此时图标矩形框信息 circleRect 是个长度为 4 的数组 分别表示 x, y, width, height let circleRect = data.a(‘circleRect’); if(circleRect) { // 通过修改高度来变大水波大小 let nextHeight = circleRect[3] + 10; // 高度最大值为 250 if(nextHeight < 250) { // 对应修改 y 的大小,y 的增加大小为高度的一半 circleRect[1] = circleRect[1] - 5; circleRect[3] = nextHeight; data.a(‘circleRect’, circleRect); data.a(‘borderColor’, ‘rgba(255, 133, 133, ’ + (1 - circleRect[3] / 250) + ‘)’); } else { data.a(‘circleRect’, [-0.5,128,257,0]); data.a(‘borderColor’, ‘rgba(255, 133, 133)’); } } else { data.a(‘circleRect’, [-0.5,128,257,0]); } } } };dm3d.addScheduleTask(waterWaveTask); // 新增该调度任务下图为水波在 2d 中的截图:3. 数字变化动画代码分析从程序的截图中可以看到在 2d 面板以及 3d 场景中都有数字在动态的变化,这部分主要通过数据绑定动态来修改数值的大小,关于数据绑定可以参考 HT for Web 的数据绑定手册,也是通过调度来不断修改数值的大小,程序中我封装了产生随机数的代码,用于每次产生随机数之后绑定到对应的节点上,以下为修改 2d 面板上数字的变化伪代码: // numNode(1-7) 为 2d 面板中对应数字的节点 // data.a(‘ht.value’, number) 即为动态修改 attr 上的 ht.value 信息,之后图纸会自动更新新赋予的数值 // getRandomValue 为自己封装的产生随机数的方法this.change2dNumTask = { interval: 1000, action: (data) => { if(data === numNode1 || data === numNode2) { data.a(‘ht.value’, util.getRandomValue([500, 999], 5)); } if(data === numNode3 || data === numNode4) { data.s(’text’, util.getRandomValue([0, 30], 2) + ‘%’); } if(data === numNode5) { data.a(‘ht.value’, util.getRandomValue([0, 99999], 5, 3)); } if(data === numNode6) { data.a(‘ht.value’, util.getRandomValue([0, 100], 2)); } if(data === numNode7) { data.a(‘ht.value’, util.getRandomValue([0, 100], 2)); } } };dm2d.addScheduleTask(this.change2dNumTask); // 新增该调度任务通过以上代码可以知道修改数值是通过修改节点的 attr 以及 style 对象的某个属性值来动态变化数值,当然在程序中 2d 面板可能还会隐藏,此时该调度任务就不需要执行,可以调用 removeScheduleTask 方法来移除此调度任务。4. 柱状图高度动画代码分析在 3d 场景中柱状体也是一个六面体,只是四周用了渐变的贴图,以及顶面用了一张纯色的贴图构造出来,每个六面体都有高度的信息,HT 中通过 node.getTall() 来获取当前六面体的高度值,根据上一节讲的数据绑定,我们可以在展示柱状图的时候循环获取所有柱状体节点的高度值大小假如命名为 tall,之后通过 node.a(’tall’, tall) 将该值存储到当前柱状图节点的 attr 对象上面,之后在柱状体初始化的时候可以不断修改高度值来动态改变高度,当高度值大于 node.a(’tall’) 则说明当前柱状体初始化的高度已经完成。以下为相关的伪代码:charts.forEach((chart) => { !chart.a(’tall’) && chart.a(’tall’, chart.getTall()); // 将高度存储到 attr 上 chart.setTall(0); // 设置初始高度为 0});this.chartAnimteTask = { interval: 25, action: function(data){ if(charts.indexOf(data) > -1) { if(finishNum !== chartLength) { if(data.getTall() !== data.a(’tall’)) { let nextTall = data.getTall() + deep; // deep 为每次增加的高度 let tall = data.a(’tall’); // 获取初始化高度 // 判断下一个高度是否大于初始化高度 if(nextTall < tall) { data.setTall(nextTall); } else { data.setTall(tall); finishNum++; } } } } } };dm3d.addScheduleTask(this.chartAnimteTask); // 新增该调度任务通过上面代码可以知道动画每一步的程序执行也是通过调度来完成的,与前文几个动画的实现方式类似。5. 3d 镜头推进代码分析3d 场景中视野的推进后退都是通过 HT api 提供的修改 eye 以及 center 的数值方法来实现,通过不断调用 setEye 以及 setCenter 方法来达到修改视角的目的,eye 类比人眼睛所处的位置,center 类比人眼睛聚焦的位置,以下为实现镜头推进关键的伪代码:let e = ht.Default.clone(g3d.getEye()), // 获取当前眼睛的位置 c = ht.Default.clone(g3d.getCenter()); // 获取当前眼睛聚焦的位置 // eye 为需要修改的对应 eye 值 // center 为需要修改的对应 center 值 // 以下为分别获取 eye 与 center 在 xyz 三个坐标轴之间的差值 let edx = eye[0] - e[0], edz = eye[1] - e[1], edy = eye[2] - e[2], cdx = center[0] - c[0], cdz = center[1] - c[1], cdy = center[2] - c[2]; // 开启不断修改 eye 与 center 的动画 ht.Default.startAnim({ duration: time ? time : 3000, easing: function(t){ return t; }, finishFunc: function() { if(typeof cb === ‘function’) { cb(); } }, action: function (v) { // v 为从 0-1 变换的值 g3d.setEye([ e[0] + edx * v, e[1] + edz * v, e[2] + edy * v ]); g3d.setCenter([ c[0] + cdx * v, c[1] + cdz * v, c[2] + cdy * v ]); }});通过以上代码可以知道通过修改 eye 与 center 分别对应的 xyz 轴的值与当前 e 与 c 分别对应的 xyz 轴的值之间的距离来达到视角的变化。以下为该代码的一个应用截图:总结物联网通过各种信息传感设备,实时采集任何需要监控、连接、互动的物体或过程等各种需要的信息,与互联网结合形成的一个巨大网络。实现了物与物、物与人,所有的物品与网络的连接,方便识别、管理和控制。所以物联网带给我们的智慧楼宇的可视化监控需要监控的方面可能还有很多,该系统中针对人员出入,设备信息,管道信息等的监控实现了一个简单的智慧楼宇监控系统,物联网也将用户端延伸和扩展到了任何物品与物品之间,让我们更加了解搭建智慧园区,智慧校园等场景监控之后设备可视化,资产可视化带给我们的直观性。场景中的反光与景深等效果都是 HT 核心包提供的效果,所有的模型搭建与动画也都是通过 HT 核心包提供的 api 进行建模与动画驱动,所以在网页中展示会十分流畅,大大提高了用户的体验,并且在移动端表现也十分友好。以下为移动端的程序运行截图:程序运行截图: ...

January 28, 2019 · 4 min · jiezi

基于 HTML5 的 WebGL 3D 档案馆可视化管理系统

前言档案管理系统是通过建立统一的标准以规范整个文件管理,包括规范各业务系统的文件管理的完整的档案资源信息共享服务平台,主要实现档案流水化采集功能。为企事业单位的档案现代化管理,提供完整的解决方案,档案管理系统既可以自成系统,为用户提供完整的档案管理和网络查询功能,也可以与本单位的OA办公自动化和DPM设计过程管理,或者与MIS信息管理系统相结合,形成更加完善的现代化信息管理网络。传统档案馆随着社会的快速发展与变化,其内在形式上也发生了巨大变化,逐渐演变为现代智慧档案馆。智慧档案馆以现代科技为依托,充分结合现代物联网技术与云计算技术构建完善的城市智慧档案,实现了现代社会全面管理的目标。本文以当前流行的 H5 技术为主,为现代智慧档案馆提供一套 WEB 解决方案。代码实现场景搭建在本例中,将使用 HT UI 组件对页面实现布局;使用 RelativeLayout 相对布局器将页面分为三个部分:left, top, center,使用 VBoxLayout 纵向布局器将 LEFT 部分分为 logo,editor,chart 三个部分Graph3dView 加载 3D 场景Graph3dView 是 HT 组件中加载 3D 模型的拓扑组件,RelativeLayout 则是 HT 提供的 UI 解决方案,UI 组件中提供 HTView 组件来实现拓扑与 UI 的融合。// 初始化相对布局器var relativeLayout = new ht.ui.RelativeLayout();// 初始化 3D 拓扑var g3dView = new ht.graph3d.Graph3dView();// 初始化 HTVIEW 组件, 并将 3D 拓扑放入其中var htView = new ht.ui.HTView(g3dView);// 布局器加载 HTVIEW 组件relativeLayout.addView(htView, { width: ‘match_parent’, // 填满 height: ‘match_parent’, // 填满 marginTop: 64, // 为 TOP 留下空间 marginLeft: 250 // 为 LEFT 留下空间});创建 LEFT 中的档案袋模型左侧的 EDITOR 部分使用 HT 的调色板组件(ht.widget.Palette), 将档案袋添加到调色板上,并设置为可以拖拽:var palette = new ht.widget.Palette();// palette 面板是将图元都分在“组”里面,然后向“组”中添加图元即可var group = new ht.Group();// 设置分组为打开的状态group.setExpanded(true);// 设置组的说明group.setName(‘基础图元’);palette.dm().add(group);// 添加子图元var childNode = new ht.Node();childNode.setParent(group);childNode.setName(name);childNode.setImage(image);childNode.s({ ‘draggable’: true, // true 为可拖拽 ‘image.stretch’: ‘centerUniform’ // 图片申展方式});palette.dm().add(childNode);实现从调色板中将图元拖拽至 3D 场景在上一步中我们对调色板中的图元属性设置了可拖拽,此时可以实现拖拽图元的动画。但是并不能直接将图元拖拽到 3D 场景中,实现思路是:在拖拽时获取拖拽的图元信息拖拽到对应位置时显示可摆放位置结束拖拽后在对应位置创建对应的 3D 模型对应代码实现如下:拖拽时获取图元信息g3dView.getView().addEventListener(‘dragover’, function(e) { e.preventDefault(); var paletteNode = palette.dm().sm().ld();// 获取 palette 上最后选中的节点 if (!paletteNode || !g3d.getDataAt(e)) return; // 获取鼠标下的节点 var data = g3d.getDataAt(e); if (data.s(‘shape3d’) === ‘档案柜.json’) { // 记录文件袋拖拽到的是哪个档案柜 g3dView._focusData = data; }});拖拽到对应位置时创建 3D 模型,在实际实现过程中由于很难准确地获取到档案柜中每一个可以摆放档案袋的坐标位置,所以在本例中采用了预置的方法。具体原理就是在先创建一个正常不可见的档案柜模型,并在其中将档案袋都摆放完整,在拖拽时,将此不可见的模型与将要摆放的模型重合,此时只需判断鼠标所在的点下是否存在预置的模型存在就可以知道该处是否可以创建 3D 模型,实现效果如下:g3dView.getView().addEventListener(‘dragover’, function(e) { … // 旧逻辑省略 // 拖拽下来的时候设置 所有的 displayName 为 box 的节点 为可见 (这样拖拽才能获取到预置模型) array.forEach(function(data) { data.s(‘3d.visible’, true); }); var data = g3d.getDataAt(e); if (data.s(‘shape3d’) === ‘档案柜.json’) { // 记录文件袋拖拽到的是哪个档案柜 g3dView._focusData = data; // 将预置模型移动到拖拽的柜子坐标 shelf.p3(data.p3()); } if(data.getDisplayName() === ‘box’) { // 将对应坐标下预置的档案袋模型进行显示 // 该属性可修改模型的透明度,更多属性可参考 HT 风格手册 data.s(‘shape3d.opacity’, 0.8); } …})g3dView.getView().addEventListener(‘drop’, function(e) { // 获取鼠标位置模型 var data = g3dView.getDataAt(e); if(!data) return; // 鼠标位置不是预置模型,直接跳过 if(data.getDisplayName() !== ‘box’) return; data.s(‘shape3d.opacity’, 0); // 放手时候设置 所有的 displayName 为 box 的节点 不可见 array.forEach(function(data) { data.s(‘3d.visible’, false); }); var node = new ht.Node(); node.s(‘shape3d’, url); // 档案袋 3D 模型地址 node.r3([Math.PI/2, -Math.PI/2, 0]); // 旋转档案袋模型, 使其平放 node.p3(data.p3()); node.setParent(g3dView._focusData); node.setHost(g3dView._focusData);});档案柜虚化效果实现上面我们已经实现了档案袋拖拽至 3D 场景的效果,但是我们可以发现档案袋模型远小于柜子,要将档案袋摆放到正确的位置并不是那么容易。所以此时我们可以将需要操作的档案柜放大到正中间,其它模型进行虚化处理。// 3D 拓扑交互监听g3dView.mi(function(e){ if(e.kind === ‘doubleClickData’) { // 双击事件 var shape3d = e.data.s(‘shape3d’), parentShape3d = e.data.getParent() && e.data.getParent().s(‘shape3d’); if (shape3d && shape3d.indexOf(‘档案柜’) > -1) { // 重点突出档案柜 showDetail(e.data); } else if (parentShape3d && parentShape3d.indexOf(‘档案柜’) > -1) { showDetail(e.data.getParent()); } }});showDetail = function(data) { // 保存进入虚化状态前 视角 与 中心点 eyeBack = ht.Default.clone(graph3dView.getEye()); centerBack = ht.Default.clone(graph3dView.getCenter()); // 设置相机指向配置 var opt = {}; opt.animation = true; opt.ratio = 1; opt.direction = [1, 0.5, 0]; opt.center = [data.getX(), 100, data.getY()]; graph3dView.flyTo(data, opt); focusData = data; data.s(‘select.brightness’, 1); dataModel.each(function (d) { if (d === focusData || (!d.s(‘3d.selectable’) && d.getTag() !== ‘wall’) || d.getParent() === focusData || d.getDisplayName() === ‘box’) return; // 将拓扑中除了要操作的柜子 与柜子中档案袋 以及墙外 透明度都设置为 opacity (0~1) // 保存设置前配置, 还原用 if (!opacityMap[d.getId()]) { opacityMap[d.getId()] = { ‘shape3d.opacity’: d.s(‘shape3d.opacity’), ‘shape3d.transparent’: d.s(‘shape3d.transparent’), ‘all.opacity’: d.s(‘all.opacity’), ‘all.transparent’: d.s(‘all.transparent’), ’left.opacity’: d.s(’left.opacity’), ’left.transparent’: d.s(’left.transparent’), ‘right.opacity’: d.s(‘right.opacity’), ‘right.transparent’: d.s(‘right.transparent’), ‘front.opacity’: d.s(‘front.opacity’), ‘front.transparent’: d.s(‘front.transparent’), ‘back.opacity’: d.s(‘back.opacity’), ‘back.transparent’: d.s(‘back.transparent’), ’top.opacity’: d.s(’top.opacity’), ’top.transparent’: d.s(’top.transparent’), ‘bottom.opacity’: d.s(‘bottom.opacity’), ‘bottom.transparent’: d.s(‘bottom.transparent’), ‘3d.selectable’: d.s(‘3d.selectable’) } } // 透明度设置为 opacity d.s({ ‘shape3d.opacity’: opacity, ‘shape3d.transparent’: true, ‘all.opacity’: opacity, ‘all.transparent’: true, ’left.opacity’: opacity, ’left.transparent’: true, ‘right.opacity’: opacity, ‘right.transparent’: true, ‘front.opacity’: opacity, ‘front.transparent’: true, ‘back.opacity’: opacity, ‘back.transparent’: true, ’top.opacity’: opacity, ’top.transparent’: true, ‘bottom.opacity’: opacity, ‘bottom.transparent’: true, ‘3d.selectable’: false }); });}退出虚化模式,以监控 3D 拓扑的选中变化来实现g3dView.dm().ms(function(e) { var lastData = g3dView.sm().ld(); // 判断是否进行虚化 if(focusData) { if(lastData === focusData || (lastData && lastData.getParetn() === focusData)) return; g3dView.setEye(eyeBack); g3dView.setCenter(centerBack); // 还原模型的原透明度 g3dView.dm().each(function (d) { if (d === focusData) return; d.s(opacityMap[d.getId()]); }); focusData.s(‘select.brightness’, 0.7); focusData = null; eyeBack = null; centerBack = null; }});快速查询功能实现在 HT 的组件中有提供快速查询插件 QuickFinder ,此次我们就运用该插件来实现简单的档案编号查询// 初始化 输入框var textField = new ht.ui.TextField;textField.setIcon(“imgs/search.json”);textField.setIconPosition(“left”);// 初始化查询器,条件:idvar finder = new ht.QuickFinder(library.view.dm, “id”);// 输入框点击查询按钮时触发textField.on(‘p:value’, function(e) { var dm = library.view.dm; var value = e.newValue; var datas = finder.find(value); // 查询到对应的图元时,我们将第一个结果进行选中 if (datas && datas.size() > 0) { library.view.dm.sm().ss(datas.get(0)); }});总结经过以上功能的实现,一个基础的智慧档案管理系统就成形了。当然做为一个智慧管理系统,这些还是远远不够的,例如档案动态监控、档案室内人员走动监控、视频监控、温度监控、灾害报警等等模块都是后期可以完善的地方。这里只是简单地为大家提供了一个基于 HTML5 WEBGL 的 3D 解决方案。同样原理,智能楼宇、智能机房、智能城市也可以基于此来实现。最终实现链接可查看:https://hightopo.com/demo/int… ...

September 3, 2018 · 3 min · jiezi