关于three.js:DataGear制作基于threejs的3D数据可视化看板

<article class=“article fmt article-content”><h3>DataGear专业版 1.0.0 已公布,欢送试用! http://datagear.tech/pro/</h3><p>DataGear 反对采纳原生的HTML、JavaScript、CSS制作数据可视化看板,也反对导入由<code>npm</code>、<code>vite</code>等前端工具构建的前端程序包。得益于这一个性,能够很容易制作基于three.js的3D数据可视化看板。</p><p>首先,参考three.js的官网教程 https://threejs.org/docs/index.html#manual/en/introduction/Installation 编写3D前端源码包。</p><p>源码包中蕴含两个文件:<code>index.html</code>、<code>main.js</code>,如下所示:</p><p>index.html</p><pre><code class=“html”><!DOCTYPE html><html><head><meta charset=“utf-8”></head><body><script type=“module” src="/main.js"></script><script type=“module”>import { ThreeRenderer } from “/main.js”;window.ThreeRenderer = ThreeRenderer;</script><div style=“padding:1rem;"> <button onclick=“threeRender()">渲染</button> <button onclick=“threeUpdate()">更新</button> <p></p> <div id=“threeChart” style=“width:300px;height:300px;"></div></div><script>var renderer;function threeRender(){ renderer = new ThreeRenderer(document.getElementById(“threeChart”)); renderer.render();}function threeUpdate(){ renderer.update(0xff0000);}</script></body></html></code></pre><blockquote><code>index.html</code>是下述<code>main.js</code>中定义3D组件的调试页面,点击【渲染】、【更新】按钮可调试3D组件成果。</blockquote><p>main.js</p><pre><code class=“javascript”>import * as THREE from ’three’;export function ThreeRenderer(dom){ this.dom = dom; this.render = function() { const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera( 75, this.dom.clientWidth / this.dom.clientHeight, 0.1, 1000 ); const renderer = new THREE.WebGLRenderer(); renderer.setSize( this.dom.clientWidth, this.dom.clientHeight ); this.dom.appendChild( renderer.domElement ); const geometry = new THREE.BoxGeometry( 1, 1, 1 ); const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } ); 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(); this.cube = cube; }; this.update = function(hexColor) { const cube = this.cube; const material = cube.material; const color = material.color; color.setHex(hexColor); };}</code></pre><blockquote><code>main.js</code>定义了一个<code>ThreeRenderer</code>3D组件类,大部分代码由three.js官网教程拷贝,它的<code>render</code>函数绘制一个简略的3D立方体, <code>update</code>函数能够更新这个3D立方体的色彩。</blockquote><p>调试:</p><pre><code>npm install –save threenpm install –save-dev vitenpx vite</code></pre><p>执行<code>npx vite build</code>将它们构建为前端程序包:</p><pre><code>index.htmlassets/index-.js</code></pre><p>先将上述前端程序包压缩为<code>ZIP</code>包后导入为DataGear看板,而后将<code>index.html</code>中的</p><pre><code><script type=“module” crossorigin src="/assets/index-.js”></script></code></pre><p>批改为采纳相对路径引入形式:</p><pre><code><script type=“module” crossorigin src=“assets/index-.js”></script></code></pre><p>此时,点击【保留并展现】看板后,关上展现页面,点击【渲染】、【更新】按钮,将能够看到3D成果,如下所示:</p><p></p><p>上面,咱们将上述3D组件制作为DataGear自定义图表,能够依据数据集返回的数值,更新其色彩。</p><p>首先,新建SQL数据集:</p><p>名称:<code>最新指标值</code></p><p>SQL:</p><pre><code>SELECT D_VALUE AS VALUEFROM t_date_valueORDER BY d_date DESCLIMIT 0, 1</code></pre><blockquote>上述SQL从<code>t_date_value</code>表中查问最新日期的指标值</blockquote><p>而后,新建一个关联上述数据集的<code>自定义</code>类型的图表;</p><p>名称:<code>最新指标值</code></p><p>图表类型:<code>自定义</code></p><p>数据集:<code>最新指标值</code></p><p>更新距离:<code>2000毫秒</code></p><blockquote>上述图表每隔2秒更新一次数据</blockquote><p>最初,批改方才导入看板的<code>index.html</code>,增加自定义图表渲染器,当<code>t_date_value</code>表中最新指标值大于等于<code>80</code>时,将3D模型渲染为红色,否则,渲染为绿色。</p><p>批改后的<code>index.html</code>如下所示:</p><pre><code class=“html”><!DOCTYPE html><html><head><meta charset=“utf-8”><script src=“assets/index-.js”></script><script>//自定义图表渲染器var chartRenderer ={ render: function(chart) { var internal = new ThreeRenderer(chart.element()); internal.render(); chart.internal(internal); }, update: function(chart, results) { var chartDataSet = chart.chartDataSetMain(); var result = chart.resultOf(results, chartDataSet); var value = chart.resultCell(result, “VALUE”, 0); var color = (value >= 80 ? 0xff0000 : 0x00ff00); var internal = chart.internal(); internal.update(color); }};</script></head><body><div style=“padding:1rem;text-align:center;"> <h1>DataGear制作3D图表</h1> <h5>http://www.datagear.tech</h5> <div style=“display:inline-block;width:300px;height:300px;margin:1rem;” dg-chart-renderer=“chartRenderer” dg-chart-widget="【图表ID】"></div> <div style=“display:inline-block;width:300px;height:300px;margin:1rem;” dg-chart-renderer=“chartRenderer” dg-chart-widget="【图表ID】"></div></div></body></html></code></pre><blockquote>上述<code>assets/index-*.js</code>应替换为理论的JS文件名,<code>【图表ID】</code>应替换为理论的<code>最新指标值</code>图表的ID</blockquote><p>点击【保留并展现】,即可看到3D图表成果,如下图所示:</p><p></p><p>官网地址:http://www.datagear.tech</p><p>源码地址:</p><p>Gitee:https://gitee.com/datagear/datagear</p><p>Github:https://github.com/datageartech/datagear</p></article> ...

February 29, 2024 · 2 min · jiezi

关于three.js:Threejs教程光源对物体表面影响

举荐:将NSDT场景编辑器退出你的3D工具链其余系列工具:NSDT简石数字孪生 光源对物体外表影响理论生存中物体外表的明暗成果是会受到光照的影响,threejs中同样也要模仿光照Light对网格模型Mesh外表的影响。 你能够关上课件中案例源码,比照有光照和无光照两种状况,网格模型Mesh外表的差别。 受光照影响材质threejs提供的网格材质,有的受光照影响,有的不受光照影响。根底网格材质MeshBasicMaterial (opens new window)不会受到光照影响。 //MeshBasicMaterial不受光照影响const material = new THREE.MeshBasicMaterial(); 漫反射网格材质MeshLambertMaterial (opens new window)会受到光照影响,该材质也能够称为Lambert网格材质,音译为兰伯特网格材质。 一个立方体长方体应用MeshLambertMaterial材质,不同面和光线夹角不同,立方体不同面就会出现进去不同的明暗成果。 //MeshLambertMaterial受光照影响const material = new THREE.MeshLambertMaterial(); 光源简介Three.js提供了多种模仿生存中光源的API,文档搜寻关键词light就能够看到。点光源点光源PointLight (opens new window)能够类比为一个发光点,就像生存中一个灯泡以灯泡为核心向周围发射光线。 //点光源:两个参数别离示意光源色彩和光照强度// 参数1:0xffffff是纯白光,示意光源色彩// 参数2:1.0,示意光照强度,能够依据须要调整const pointLight = new THREE.PointLight(0xffffff, 1.0);光源地位你把点光源设想为一个电灯泡,你在3D空间中,放的地位不同,模型的渲染成果就不一样。 留神光源地位尺寸大小:如果你心愿光源照在模型的外外表,那你就须要把光源放在模型的里面。 //点光源地位pointLight.position.set(400, 0, 0);//点光源放在x轴上扭转光源地位,察看网格模型外表的明暗变动。 directionalLight.position.set(100, 60, 50); 光源增加到场景光源和网格模型Mesh对应一样是三维场景的一部分,天然须要增加到三维场景中能力起作用。 scene.add(directionalLight); //点光源增加到场景中 3D建模学习工作室

July 4, 2023 · 1 min · jiezi

关于three.js:Threejs教程三维坐标系

举荐:将NSDT场景编辑器退出你的3D工具链其余系列工具:NSDT简石数字孪生 三维坐标系本节课的目标就是为了增强大家对threejs三维空间的意识。 辅助察看坐标系THREE.AxesHelper()的参数示意坐标系坐标轴线段尺寸大小,你能够依据须要扭转尺寸。 // AxesHelper:辅助察看的坐标系const axesHelper = new THREE.AxesHelper(150);scene.add(axesHelper);材质半透明设置设置材质半透明,这样能够看到坐标系的坐标原点。 const material = new THREE.MeshBasicMaterial({ color: 0x0000ff, //设置材质色彩 transparent:true,//开启通明 opacity:0.5,//设置透明度});AxesHelper的xyz轴three.js坐标轴色彩红R、绿G、蓝B别离对应坐标系的x、y、z轴,对于three.js的3D坐标系默认y轴朝上。 设置模型在坐标系中的地位或尺寸通过模型的地位、尺寸设置,加深3D坐标系的概念。测试:设置长方体xyz不同方向尺寸 // 设置几何体长宽高,也就是x、y、z三个方向的尺寸//比照三个参数别离对应xyz轴哪个方向new THREE.BoxGeometry(100, 60, 20);测试:扭转地位 // 设置模型mesh的xyz坐标mesh.position.set(100,0,0);扭转相机参数——预览新的渲染成果你能够尝试源码中扭转相机的参数,看看场景中的物体渲染成果怎么变动。 相机放在x轴负半轴,指标观察点是坐标原点,这样相当于相机的眼帘是沿着x轴正方向,只能看到长方体的一个矩形立体。 camera.position.set(-1000, 0, 0);camera.lookAt(0, 0, 0);// 相机眼帘沿着x轴负半轴,mesh位于相机前面,天然看不到camera.position.set(-1000, 0, 0);camera.lookAt(-2000, 0, 0);相机far偏小,mesh位于far之外,物体不会显示在画布上。 // const camera = new THREE.PerspectiveCamera(30, width / height, 1, 3000);// 你能够进行上面测试,扭转相机参数,把mesh放在视锥体之外,看看是否显示// 3000改为300,使mesh位于far之外,mesh不在视锥体内,被剪裁掉const camera = new THREE.PerspectiveCamera(30, width / height, 1, 300); 3D建模学习工作室整顿翻译

July 4, 2023 · 1 min · jiezi

关于three.js:Threejs教程渲染器

举荐:将NSDT场景编辑器退出你的3D工具链。其余系列工具:NSDT简石数字孪生 渲染器生存中如果有了风物和相机,那么如果想取得一张照片,就须要你拿着相机,按一下,咔,实现拍照。对于threejs而言,如果实现“咔”这个拍照动作,就须要一个新的对象,也就是WebGL渲染器WebGLRenderer (opens new window)。 WebGL渲染器WebGLRenderer通过WebGL渲染器WebGLRenderer (opens new window)能够实例化一个WebGL渲染器对象。 设置Canvas画布尺寸.setSize()// 定义threejs输入画布的尺寸(单位:像素px)const width = 800; //宽度const height = 500; //高度renderer.setSize(width, height); //设置three.js渲染区域的尺寸(像素px)渲染器渲染办法.render()渲染器WebGLRenderer执行渲染办法.render()就能够生成一个Canvas画布(照片),并把三维场景Scene出现在canvas画布下面,你能够把.render()了解为相机的拍照动作“咔”。 renderer.render(scene, camera); //执行渲染操作 渲染器Canvas画布属性.domElement渲染器WebGLRenderer通过属性.domElement能够取得渲染办法.render()生成的Canvas画布,.domElement实质上就是一个HTML元素:Canvas画布。 document.body.appendChild(renderer.domElement);Canvas画布插入到任意HTML元素中<div id="webgl" style="margin-top: 200px;margin-left: 100px;"></div>document.getElementById('webgl').appendChild(renderer.domElement);3D建模学习工作室

June 30, 2023 · 1 min · jiezi

关于three.js:Threejs教程模型对象旋转平移缩放变换

举荐:将NSDT场景编辑器退出你的3D工具链其余系列工具:NSDT简石数字孪生 模型对象旋转平移缩放变换点模型Points、线模型Line、网格网格模型Mesh等模型对象的基类都是Object3D,如果想对这些模型进行旋转、缩放、平移等操作,如何实现,能够查问Threejs文档Object3D对相干属性和办法的介绍。 缩放网格模型Mesh的属性.scale示意模型对象的缩放比例,默认值是THREE.Vector3(1.0,1.0,1.0),.scale的属性值是一个三维向量对象Vector3,查看three.js文档你能够晓得Vector3对象具备属性.x、.y、.z,Vector3对象还具备办法.set(),.set办法有三个示意xyz方向缩放比例的参数。 网格模型xyz方向别离缩放0.5,1.5,2倍 mesh.scale.set(0.5, 1.5, 2) x轴方向放大2倍 mesh.scale.x = 2.0; 地位属性.position模型地位.position属性和.scale属性的属性值一样也是三维向量对象Vector3,通过模型地位属性.position能够设置模型在场景Scene中的地位。模型地位.position的默认值是THREE.Vector3(0.0,0.0,0.0)。 设置网格模型y坐标 mesh.position.y = 80; 设置模型xyz坐标 mesh.position.set(80,2,10); 平移网格模型沿着x轴正方向平移100,能够屡次执行该语句,每次执行都是绝对上一次的地位进行平移变换。 // 等价于mesh.position = mesh.position + 100;mesh.translateX(100);//沿着x轴正方向平移间隔100沿着Z轴负方向平移间隔50。 mesh.translateZ(-50); 沿着自定义的方向挪动。 //向量Vector3对象示意方向var axis = new THREE.Vector3(1, 1, 1);axis.normalize(); //向量归一化//沿着axis轴示意方向平移100mesh.translateOnAxis(axis, 100);执行.translateX()、.translateY()、.translateOnAxis()等办法实质上扭转的都是模型的地位属性.position。 旋转立方体网格模型绕立方体的x轴旋转/4,能够屡次执行该语句,每次执行都是绝对上一次的角度进行旋转变动 mesh.rotateX(Math.PI/4);//绕x轴旋转/4 网格模型绕(0,1,0)向量示意的轴旋转/8 var axis = new THREE.Vector3(0,1,0);//向量axismesh.rotateOnAxis(axis,Math.PI/8);//绕axis轴旋转/8执行旋转.rotateX()等办法和执行平移.translateY()等办法一样都是对模型状态属性的扭转,区别在于执行平移办法扭转的是模型的地位属性.position,执行模型的旋转办法扭转的是示意模型角度状态的角度属性.rotation或者四元数属性.quaternion。 模型的角度属性.rotation和四元数属性.quaternion都是示意模型的角度状态,只是示意办法不同,.rotation属性值是欧拉对象Euler,.quaternion属性值是是四元数对象Quaternion // 绕着Y轴旋转90度mesh.rotateY(Math.PI / 2);//控制台查看:旋转办法,扭转了rotation属性console.log(mesh.rotation);3D建模学习工作室

June 29, 2023 · 1 min · jiezi

关于three.js:Threejs教程对象克隆复制

举荐:将NSDT场景编辑器退出你的3D工具链其余系列工具:NSDT简石数字孪生 对象克隆.clone()和复制.copy()Threejs大多数对象都有克隆.clone()和复制.copy()两个办法,点模型Points、线模型Line、网格网格模型Mesh一样具备这两个办法。 复制办法.copy()A.copy(B)示意B属性的值赋值给A对应属性。var p1 = new THREE.Vector3(1.2,2.6,3.2);var p2 = new THREE.Vector3(0.0,0.0,0.0);p2.copy(p1)// p2向量的xyz变为p1的xyz值console.log(p2);克隆办法.clone()N = M.clone()示意返回一个和M雷同的对象赋值给N。 var p1 = new THREE.Vector3(1.2,2.6,3.2);var p2 = p1.clone();// p2对象和p1对象xyz属性雷同console.log(p2);网格模型复制和克隆网格模型复制克隆和三维向量根本逻辑是雷同,然而留神三维向量Vector3的.x、.y、.z属性值是数字,也就是说是根本类型的数据,对于网格模型而言,网格模型对象的几何体属性mesh.geometry和材质属性mesh.material的属性值都是对象的索引值。 var box=new THREE.BoxGeometry(10,10,10);//创立一个立方体几何对象var material=new THREE.MeshLambertMaterial({color:0x0000ff});//材质对象var mesh=new THREE.Mesh(box,material);//网格模型对象var mesh2 = mesh.clone();//克隆网格模型mesh.translateX(20);//网格模型mesh平移scene.add(mesh,mesh2);//网格模型增加到场景中缩放几何体box,你能够发现下面代码中的两个网格模型的大小都产生了变动,因为网格模型克隆的时候,mesh对象的几何体对象mesh.geometry属性值是box对象的索引值,返回的新对象mesh2几何体属性mesh.geometry的值同样是box对象的索引值。 box.scale(1.5,1.5,1.5);//几何体缩放 留神通过本节课的学习,对Threejs不同对象的克隆.clone()和复制.copy()办法有一个大抵印象即可。 理论开发的时候,留神不同对象的复制或克隆办法可能稍有区别,应用的时候最好通过代码测试,或者间接查看threejs源码某个类对.clone()和.copy()封装,这样更为直观分明。 几何体复制和克隆几何体克隆或复制和网格模型在属性值深拷贝、浅拷贝方面有些不同,比方几何体的顶点属性Geometry.vertices,Geometry.vertices的属性值是一个数组对象,然而复制或克隆的时候,不是取得对象的索引值,而是深拷贝属性的值,能够在threejs源码Geometry.js全文检索copy: function关键词,找到该类对copy办法的封装细节。 3D建模学习工作室

June 29, 2023 · 1 min · jiezi

关于three.js:整个宇宙将为你闪烁-除了三体人前端也可以-哈哈

整个宇宙将为你闪动 除了三体人前端也能够 哈哈整个宇宙将为你闪动,这句话出自《三体》,过后就被吸引住了,而后就想着把它实现进去。我的项目次要应用 three.js,设计灵感及贴图来源于 Solar System Scope 。 视频展现: Bilibili 哔哩哔哩 我的项目源码: GitHub 前置项在开始之前心愿你对 three.js 有肯定的理解,这里举荐一个教程,Discover threejs, three.js 主创之一编写的适宜入门的教程。 我的项目只用原生 js 并没有引入 Vue 或 React 等框架,因为我想让我的项目更纯正些,也心愿本人不要来到框架就不会写代码了。 基础设施创立场景// scene.jsimport { Scene } from 'three'function createScene() { const scene = new Scene() return scene}export { createScene }创立相机// camera.jsimport { PerspectiveCamera } from 'three'function createCamera() { const camera = new PerspectiveCamera(45, 1, 0.1, 2000) camera.position.set(0, 0, 20) return camera}export { createCamera }渲染场景// renderer.jsimport { WebGLRenderer } from 'three'function createRenderer() { const renderer = new WebGLRenderer() return renderer}export { createRenderer } 增加物体到场景中实现下面的基本操作,咱们就能够往场景中增加物体了。 ...

June 21, 2023 · 5 min · jiezi

关于three.js:Threejs教程常见光源类型

举荐:将NSDT场景编辑器退出你的3D工具链其余系列工具:NSDT简石数字孪生 常见光源类型Threejs虚构光源是对自然界光照的模仿,threejs搭建虚构场景的时候,为了更好的渲染场景,往往须要设置不同的光源,设置不同的光照强度,就像摄影师给你拍照要设置各种辅助灯光一样。 环境光AmbientLight环境光是没有特定方向的光源,次要是平均整体扭转Threejs物体外表的明暗成果,这一点和具备方向的光源不同,比方点光源能够让物体外表不同区域明暗水平不同。 //环境光:环境光色彩RGB成分别离和物体材质色彩RGB成分别离相乘var ambient = new THREE.AmbientLight(0x444444);scene.add(ambient);//环境光对象增加到scene场景中你能够把光源色彩从0x444444更改为0x888888,能够看到threejs场景中的网格模型外表变的更亮。 点光源PointLight点光源就像生存中的白炽灯,光线沿着发光外围向外发散,同一立体的不同地位与点光源光线入射角是不同的,点光源照耀下,同一个立体不同区域是呈现出不同的明暗成果。 和环境光不同,环境光不须要设置光源地位,而点光源须要设置地位属性.position,光源地位不同,物体外表被照亮的面不同,远近不同因为衰减明暗水平不同。 你能够把案例源码中点光源地位从(400, 200, 300)地位扭转到(-400, -200, -300),你会发现网格模型被照亮的地位从后面变到了前面,这很失常,光源只能照亮面对着光源的面,背对着光源的无奈照射到,色彩会比拟暗。 //点光源var point = new THREE.PointLight(0xffffff);//设置点光源地位,扭转光源的地位point.position.set(400, 200, 300);scene.add(point);平行光DirectionalLight平行光顾名思义光线平行,对于一个立体而言,立体不同区域接管到平行光的入射角一样。 点光源因为是向周围发散,所以设置好地位属性.position就能够确定光线和物体外表的夹角,对于平行光而言,次要是确定光线的方向,光线方向设定好了,光线的与物体外表入射角就确定了,仅仅设置光线地位是不起作用的。 在三维空间中为了确定一条直线的方向只须要确定直线上两个点的坐标即可,所以Threejs平行光提供了地位.position和指标.target两个属性来一起确定平行光方向。指标.target的属性值能够是Threejs场景中任何一个三维模型对象,比方一个网格模型Mesh,这样Threejs计算平行光照耀方向的时候,会通过本身地位属性.position和.target示意的物体的地位属性.position计算出来。 // 平行光var directionalLight = new THREE.DirectionalLight(0xffffff, 1);// 设置光源的方向:通过光源position属性和指标指向对象的position属性计算directionalLight.position.set(80, 100, 50);// 方向光指向对象网格模型mesh2,能够不设置,默认的地位是0,0,0directionalLight.target = mesh2;scene.add(directionalLight);平行光如果不设置.position和.target属性,光线默认从上往下照耀,也就是能够认为(0,1,0)和(0,0,0)两个坐标确定的光线方向。 留神一点平行光光源的地位属性.position并不示意平行光从这个地位向远处照耀,.position属性只是用来确定平行光的照耀方向,平行光你能够了解为太阳光,从有限远处照耀过去。 聚光源SpotLight聚光源能够认为是一个沿着特定方会逐步发散的光源,照耀范畴在三维空间中形成一个圆锥体。通过属性.angle能够设置聚光源发散角度,聚光源照耀方向设置和平行光光源一样是通过地位.position和指标.target两个属性来实现。 // 聚光光源var spotLight = new THREE.SpotLight(0xffffff);// 设置聚光光源地位spotLight.position.set(200, 200, 200);// 聚光灯光源指向网格模型mesh2spotLight.target = mesh2;// 设置聚光光源发散角度spotLight.angle = Math.PI / 6scene.add(spotLight);//光对象增加到scene场景中光源辅助对象Threejs提供了一些光源辅助对象,就像AxesHelper可视化显示三维坐标轴一样显示光源对象,通过这些辅助对象能够不便调试代码,查看地位、方向。 辅助对象构造函数名聚光源辅助对象SpotLightHelper点光源辅助对象PointLightHelper平行光光源辅助对象DirectionalLightHelper光照计算算法Three.js渲染的时候光照计算还是比较复杂的,这里不进行深刻介绍,只给大家说下光源色彩和网格模型Mesh色彩相乘的常识,如果你有趣味能够学习计算机图形学或者WebGL教程。 Threejs在渲染的时候网格模型材质的色彩值mesh.material.color和光源的色彩值light.color会进行相乘,简略说就是RGB三个重量别离相乘。 平行光漫反射简略数学模型:漫反射光的色彩 = 网格模型材质色彩值 x 光线色彩 x 光线入射角余弦值漫反射数学模型RGB重量示意:(R2,G2,B2) = (R1,G1,B1) x (R0,G0,B0) x ...

June 9, 2023 · 1 min · jiezi

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

download:网易Three.js可视化企业实战WEBGL课-2023全面升级版Three.js可视化:在浏览器中创立交互式3D图形Three.js是一个开源的JavaScript库,用于在Web浏览器中创立和渲染3D图形。应用Three.js,您能够轻松地在浏览器中出现简单的3D场景,并与用户进行互动。本文将探讨Three.js的基本功能以及如何应用它来创立各种类型的3D图形。 什么是Three.js?Three.js是一款十分风行的JavaScript库,由Mr.doob(Ricardo Cabello)开发,用于在Web浏览器中创立和渲染3D图形。它是基于WebGL技术构建的,这使得它能够在任何反对WebGL的浏览器上运行,包含Chrome、Firefox、Safari和Opera等。Three.js提供了丰盛的性能,包含灯光、暗影、纹理贴图、几何体、线条、粒子等,能够很容易地实现各种类型的3D图形。 基本概念在开始应用Three.js之前,咱们须要理解一些基本概念。首先,咱们须要一个场景(Scene),用于寄存所有的3D对象。其次,咱们须要一个相机(Camera),用于定义观察者的视角和地位。最初,咱们须要一个渲染器(Renderer),用于将场景和相机中的对象渲染为2D图像,以便在浏览器中显示。 创立一个根本场景让咱们看一下如何应用Three.js创立一个根本场景。首先,咱们须要创立一个场景: javascriptvar scene = new THREE.Scene();接下来,咱们须要一个相机。在这个例子中,咱们将应用透视相机(PerspectiveCamera): javascriptvar camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);这里的75示意相机的视线角度,window.innerWidth / window.innerHeight是相机的宽高比,0.1是近截面间隔,1000是远截面间隔。 当初,咱们须要一个渲染器。在这个例子中,咱们将应用WebGLRenderer: javascriptvar renderer = new THREE.WebGLRenderer();renderer.setSize(window.innerWidth, window.innerHeight);document.body.appendChild(renderer.domElement);这里的setSize()办法设置了渲染器的大小,而appendChild()办法则将渲染器增加到HTML文档中的body元素中。 最初,咱们须要将相机和场景增加到渲染器中: javascriptrenderer.render(scene, camera);当初,咱们曾经创立了一个根本的场景,并将其渲染到浏览器中。 创立一个立方体让咱们当初尝试创立一个简略的3D物体。在这个例子中,咱们将创立一个立方体: javascriptvar geometry = new THREE.BoxGeometry();var material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });var cube = new THREE.Mesh(geometry, material);scene.add(cube);这里的BoxGeometry()办法创立了一个立方体的几何体,而MeshBasicMaterial()办法则创立了一个材质,它应用了绿色(0x00ff00)来渲染立方体。最初,咱们将立方体增加到场景中。 创立动画成果当初,让咱们为立方体创立一些动画成果。在这个例子中,咱们将使立方体绕着Y轴旋转: javascriptfunction animate() { requestAnimationFrame(animate);cube.rotation.y += 0.01;renderer.render(scene, camera);}animate();这里的requestAnimationFrame()办法用于循环调用animate()函数,而cube.rotation.y属性

May 30, 2023 · 1 min · jiezi

关于three.js:Threejs可视化示例让你的数据可视化变得更加生动

Three.js可视化示例:让你的数据可视化变得更加活泼如果你是一个数据科学家或者是一位数据分析师,你可能曾经意识到了一个重要的问题:如何将咱们的数据转换成有意义的信息并展现给其他人看。在过来,咱们会应用图表、报告或幻灯片等形式来进行数据展现。然而这些形式都有一个独特的毛病,那就是不足互动性和视觉效果。 最近几年,随着WebGL技术的倒退,Three.js成为了可视化畛域的热门工具之一。Three.js是一个基于WebGL的JavaScript库,它能够创立高性能的3D图形和动画。在这篇文章中,我将向大家介绍Three.js的基础知识,并提供一个简略的示例来展现如何将你的数据可视化变得更加活泼。 Three.js简介Three.js是一个基于WebGL的JavaScript库,它能够让咱们创立高性能的3D场景和动画。WebGL是一种用于在浏览器中渲染3D图形的API(利用程序接口),它利用GPU的并行处理能力来实现高性能渲染。 Three.js能够被用于很多不同的场景,比方游戏开发、建筑设计、医学图像等等。它反对各种不同的根本图形,包含立方体、球体、圆柱体、立体等等,并且还能够加载3D模型和纹理图片。 Three.js的应用装置首先,你须要将Three.js库引入到你的我的项目中。你能够从最新版本的库文件,或者应用CDN链接引入库文件。 <script src="https://cdn.jsdelivr.net/npm/three@0.135.0/build/three.min.js"></script>创立场景在应用Three.js之前,咱们须要先创立一个场景。场景是所有3D对象的容器,咱们能够在场景中增加相机、灯光、3D模型等。 // 创立场景 var scene = new THREE.Scene();创立相机相机用于管制察看场景的角度和地位。Three.js提供了多种不同类型的相机,比方透视相机、正交相机等。 // 创立透视相机var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);camera.position.z = 5;创立渲染器渲染器将场景中的3D对象渲染到屏幕上。Three.js提供了多种不同类型的渲染器,比方WebGLRenderer、CanvasRenderer等。 // 创立WebGLRenderervar renderer = new THREE.WebGLRenderer();renderer.setSize(window.innerWidth, window.innerHeight);document.body.appendChild(renderer.domElement);增加3D对象当初咱们曾经创立了场景、相机和渲染器,接下来咱们须要增加3D对象到场景中。 // 创立立方体var geometry = new THREE.BoxGeometry(1, 1, 1);var material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });var cube = new THREE.Mesh(geometry, material);scene.add(cube);渲染场景最初一步是将场景中的3D对象渲染到屏幕上。这个过程通常是在一个循环中进行的,每次循环都会从新渲染一遍场景。 function animate() { requestAnimationFrame(animate); cube.rotation.x += 0.01; cube.rotation.y += 0.01; renderer.render(scene, camera);}animate();总结以上就是有对于Three.js可视化示例:让你的数据可视化变得更加活泼的示例文章,欢送点评。

May 26, 2023 · 1 min · jiezi

关于three.js:Threejs-进阶之旅滚动控制模型动画和相机动画-

申明:本文波及图文和模型素材仅用于集体学习、钻研和观赏,请勿二次批改、非法流传、转载、出版、商用、及进行其余获利行为。 摘要专栏上篇文章《Three.js 进阶之旅:页面平滑滚动-王国之泪》 解说并实现了如何应用 R3F 进行页面图片平滑滚动,本文内容在上节的根底上,学习如何应用滚动管制 ScrollControls 来管制模型的的动画播放和相机动画,通过滚动鼠标滚轮或者高低挪动触摸板,来管制模型的动画播放进度或者相机的方位视角,从而呈现出惊艳的视觉效果。这种乏味的成果大家在平时浏览一些网页的时候应该常常见到,如一些 3D产品 介绍页向下滑动鼠标滚轮时产品同时旋转并依据产品的不同视角加载不同文案、或者 3D数字地球 依据滚轮的挪动间隔转到某个国家或地区、还有一些 个人简历 页面或时间轴页面也常常用到这种成果。通过本文的浏览和案例页面的实现,你将学习到的常识包含:R3F 生态中的 ScrollControls、Html、useScroll、useGLTF、useAnimations 等组件和办法的根本用法、在 R3F 中加载模型并播放模型骨骼动画、通过滚动管制模型动画播放过程和相机参数、页面元素的一些 CSS 动画及页面整体丝滑滚动动画实现等。 成果本文案例的实现成果如下图所示,页面主体元素由一个三维模型 、及底部的 5 页 HTML 页面形成,页面初始加载时模型是静止的,当咱们应用鼠标或触控板或间接拖动页面滚动条时 ,相机镜头 从侧面近处平滑过渡到模型侧面远处,模型开始自动播放自带骨骼动画,模型动作依据页面滚动间隔和滚动时速率的大小而不同。当咱们点击页面顶部菜单时,页面会平滑滚动到对应地位,模型也会播放动画。 当页面逆向 滚动时,相机和模型动画也会逆向变动和播放。 文章应用 GIF 可能会造成丢帧或卡顿,能够亲自关上预览链接试试,大屏拜访成果更佳。 在线预览地址:https://dragonir.github.io/dancingDuck/本专栏系列代码托管在 Github 仓库【threejs-odessey】,后续所有目录也都将在此仓库中更新。 代码仓库地址:git@github.com:dragonir/threejs-odessey.git原理如果用原生 JavaScript 实现滚动动画成果,就须要监听滚动事件和计算滚动间隔。本文还是和上篇文章《hree.js 进阶之旅:页面平滑滚动-王国之泪》一样,间接应用封装好的组件 ScrollControls 和 Scroll 来实现,它们的具体用法和原理可返回上篇文章查看。本文中最终实现的页面须要加载模型并播放它自带的骨骼动画,因而用到了以下几个 @react-three/drei 中的组件和 hooks。 Html容许咱们将 HTML 内容绑定到场景中的任意对象,它将主动投影到对象上。 <Html as='div' // 包裹元素,默认为 'div' wrapperClass // 包裹元素的类名,默认为 undefined prepend // 画布前面的元素,默认为 false center // 增加 -50%/-50% css变换,默认为 false fullscreen // 与左上角对齐并填满屏幕,默认为 false distanceFactor={10} // 如果设置该值,子元素将按与相机的间隔进行缩放,默认为 undefined zIndexRange={[100, 0]} // Z阶范畴,默认为 [16777271, 0] portal={domnodeRef} // 对指标容器的援用,默认为 undefined transform // 若设置 true,将进行 3d 矩阵转换,默认为 false sprite // 渲染为 sprite,仅在转换模式下失效,默认为 false occlude // 遮挡模式,默认为 false,当设置为 blending 时将开启真正混合遮挡 castShadow // 产生暗影 receiveShadow // 接管暗影 // 像 Mesh 一样设置材质 material={<meshPhysicalMaterial side={DoubleSide} opacity={0.1} />} // 笼罩默认定位性能 calculatePosition={(el: Object3D, camera: Camera, size: { width: number; height: number }) => number[]} occlude={[ref]} // 能够为真或 Ref<Object3D>,当为 true 时遮挡整个场景,默认为 undefined onOcclude={(visible) => null} // 可见性批改时的回调,默认为 undefined {...groupProps} // 反对所有 THREE.Group 属性 {...divProps} // 反对所有 HTML DIV 元素属性> <h1>hello</h1> <p>world</p></Html>Html 能够通过配置 occlude 属性暗藏在几何体前面。当 Html 组件暗藏时,它将在最外部的 div 上设置 opacity 属性,如果须要增加动画成果或者管制过渡成果,能够应用 onOcclude 自定义办法。 ...

May 25, 2023 · 3 min · jiezi

关于three.js:Threejs教程材质共有属性私有属性

举荐:将NSDT场景编辑器退出你的3D工具链其余系列工具:NSDT简石数字孪生 材质共有属性、公有属性如果你的javascript语言根底还能够,应该明确类、基类、子类、父类等概念。如果你有这些类的概念,那么在学习Threejs的过程中,如何查找Threejs文档将会比较顺利。 点材质PointsMaterial、根底线材质LineBasicMaterial、根底网格材质MeshBasicMaterial、高光网格材质MeshPhongMaterial等材质都是父类Material的子类。 各种各样的材质子类都有本人的特定属性,比方点材质特有的尺寸属性.size、高光网格材质特有的高光色彩属性.specular等等这些属性能够成为子类材质的公有属性。 所有子类的材质都会从父类材质Material继承透明度opacity、面side等属性,这些来自父类的属性都是子类共有的属性。 .side属性在Three.js开发过程中你可能会遇到上面的问题,比方three.js矩形立体planegeometry的网格模型插入场景看不到,一个球体或立方体网格模型如何反面显示贴图,侧面不显示...,对于这些问题能够通过Three.js材质对象.side属性来设置。 材质.side属性的具体介绍能够查看Threejs文档中所有材质对象的基类Material。 .side属性的属性值定义面的渲染形式后面前面 或 双面. 属性的默认值是THREE.FrontSide,示意后面. 也能够设置为前面THREE.BackSide 或 双面THREE.DoubleSide. var material = new THREE.MeshBasicMaterial({ color: 0xdd00ff, // 后面FrontSide 反面:BackSide 双面:DoubleSide side:THREE.DoubleSide,});材质透明度.opacity通过材质的透明度属性.opacity能够设置材质的通明水平,.opacity属性值的范畴是0.0~1.0,0.0值示意齐全通明, 1.0示意齐全不通明,.opacity默认值1.0。 当设置.opacity属性值的时候,须要设置材质属性transparent值为true,如果材质的transparent属性没设置为true, 材质会放弃齐全不通明状态。 在结构函数参数中设置transparent和.opacity的属性值 var material = new THREE.MeshPhongMaterial({ color: 0x220000, // transparent设置为true,开启通明,否则opacity不起作用 transparent: true, // 设置材质透明度 opacity: 0.4,});通过拜访材质对象属性模式设置transparent和.opacity的属性值 // transparent设置为true,开启通明,否则opacity不起作用material.transparent = true; // 设置材质透明度material.opacity = 0.4;3D建模学习工作室

May 23, 2023 · 1 min · jiezi

关于three.js:Threejs教程常用材质介绍

举荐:将NSDT场景编辑器退出你的3D工具链其余系列工具:NSDT简石数字孪生 罕用材质介绍为了不便开发Threejs提供了一系列的材质,所有材质就是对WebGL着色器代码的封装,如果你不理解WebGL,会通过查阅Threejs文档应用相干材质类即可。 点材质PointsMaterial点材质比较简单,只有PointsMaterial,通常应用点模型的时候会应用点材质PointsMaterial。点材质PointsMaterial的.size属性能够每个顶点渲染的方形区域尺寸像素大小。 var geometry = new THREE.SphereGeometry(100, 25, 25); //创立一个球体几何对象// 创立一个点材质对象var material = new THREE.PointsMaterial({ color: 0x0000ff, //色彩 size: 3, //点渲染尺寸});//点模型对象 参数:几何体 点材质var point = new THREE.Points(geometry, material);scene.add(point); //网格模型增加到场景中线材质线材质有根底线材质LineBasicMaterial和虚线材质LineDashedMaterial两个,通常应用应用Line等线模型才会用到线材质。 根底线材质LineBasicMaterialvar geometry = new THREE.SphereGeometry(100, 25, 25);//球体// 直线根底材质对象var material = new THREE.LineBasicMaterial({ color: 0x0000ff});var line = new THREE.Line(geometry, material); //线模型对象scene.add(line); //点模型增加到场景中虚线材质LineDashedMaterial// 虚线材质对象:产生虚线成果var material = new THREE.LineDashedMaterial({ color: 0x0000ff, dashSize: 10,//显示线段的大小。默认为3。 gapSize: 5,//间隙的大小。默认为1});var line = new THREE.Line(geometry, material); //线模型对象// computeLineDistances办法 计算LineDashedMaterial所需的间隔数组line.computeLineDistances();网格模型Threejs提供的网格类材质比拟多,网格材质波及的材质品种和材质属性也比拟多,一节课也无奈解说完,本节课先有一个理性的认知。网格材质顾名思义,网格类模型才会应用的材质对象。根底网格材质对象MeshBasicMaterial,不受带有方向光源影响,没有棱角感。 ...

May 10, 2023 · 1 min · jiezi

关于three.js:Threejs教程访问几何体对象的数据

举荐:将NSDT场景编辑器退出你的3D工具链其余系列工具:NSDT简石数字孪生 拜访几何体对象的数据理论开发我的项目的时候,可能会加载内部模型,有些时候须要获取模型几何体的顶点数据,如果想获取几何体的顶点数据首先要相熟three.js几何体BoxGeometry和BufferGeometry的构造。拜访几何体顶点数据其实很简略,刚开始学习不必刻意记忆,间接查看threejs文档,就像拜访javascript对象的属性一样。 测试BoxGeometry调用BoxGeometry创立一个立方体,执行THREE.BoxGeometry构造函数会主动生成几何体对象的顶点地位坐标、顶点法向量等数据。你能够通过执行上面代码,而后查看浏览器控制台打印的数据 var geometry = new THREE.BoxGeometry(100, 100, 100); //创立一个立方体几何对象Geometryconsole.log(geometry);console.log('几何体顶点地位数据',geometry.vertices);console.log('三角行面数据',geometry.faces);BoxGeometry、PlaneGeometry、SphereGeometry等几何体类的基类是Geometry,所以拜访这些几何体的顶点数据,不晓得具体属性名称,能够查问threejs文档Geometry。 测试PlaneBufferGeometryPlaneBufferGeometry示意一个矩形平面几何体,执行上面代码,你能够查看该几何体的相干顶点数据。 //创立一个矩形平面几何体var geometry = new THREE.PlaneBufferGeometry(100, 100);console.log(geometry);console.log('几何体顶点地位数据',geometry.attributes.position);console.log('几何体索引数据',geometry.index);BoxBufferGeometry、PlaneBufferGeometry、SphereBufferGeometry等几何体类的基类是BufferGeometry,所以拜访这些几何体的顶点数据,不晓得具体属性名称,能够查问threejs文档BufferGeometry。 案例通过上面代码批改BoxGeometry的三角形顶点色彩的数据,能够渲染进去如下成果。 var geometry = new THREE.BoxGeometry(100, 100, 100); //创立一个立方体几何对象Geometry// 遍历几何体的face属性geometry.faces.forEach(face => { // 设置三角面face三个顶点的色彩 face.vertexColors = [ new THREE.Color(0xffff00), new THREE.Color(0xff00ff), new THREE.Color(0x00ffff), ]});var material = new THREE.MeshBasicMaterial({ // color: 0x0000ff, vertexColors: THREE.FaceColors, // wireframe:true,//线框模式渲染}); //材质对象Material案例你能够通过上面代码删除立方体局部三角形面,测试删除成果。 var geometry = new THREE.BoxGeometry(100, 100, 100); //创立一个立方体几何对象Geometry// pop():删除数组的最初一个元素 shift:删除数组的第一个元素geometry.faces.pop();geometry.faces.pop();geometry.faces.shift();geometry.faces.shift();var material = new THREE.MeshLambertMaterial({ color: 0x0000ff, side: THREE.DoubleSide, //两面可见}); //材质对象Material拜访内部模型几何体顶点数据Threejs加载内部模型的时候,会把几何体解析为缓冲类型几何体BufferGeometry,所以拜访内部模型几何体顶点数据,能够查看文档BufferGeometry。 ...

May 6, 2023 · 1 min · jiezi

关于three.js:Threejs-进阶之旅页面平滑滚动王国之泪-

申明:本文波及图文和模型素材仅用于集体学习、钻研和观赏,请勿二次批改、非法流传、转载、出版、商用、及进行其余获利行为。 摘要浏览网页时,常被一些基于鼠标滚轮管制的页面动画所惊艳到,比方greensock 官网这些 showcase 案例页面就十分优良,它们大多数都是应用 Tween.js、gasp 及 greensock 提供的一些动画扩大库实现的。应用 Three.js 也能很容易实现丝滑的滚动成果,本文应用 React + Three.js + React Three Fiber 技术栈,实现一个《塞尔达传说:王国之泪》主题格调基于滚动管制的平滑滚动图片展现页面。通过本文的浏览,你将学习到的知识点包含:理解 R3F 中 useFrame hook 及 useThree hook 基本原理及用法;理解 @react-three/drei 库的根本组成,学习应用它提供的 Preload、useIntersect、ScrollControls、Scroll、及 Image 等组件和办法;用 CSS 生成简略的循环悬浮动画等。 成果本文案例的实现成果如下图所示,当页面每个模块滚动进入视区时,每个模块会具备平滑向上挪动的视差成果,并且随同着由大到小的缩放动画,当鼠标悬浮到以后模块时,模块会产生高亮 ✨ 成果。除此之外,页面还有一些其余的装璜,比方塞尔达格调的页面背景和边框、具备缓动动画成果的希卡之石以及同样具备平滑滚动成果的文字装璜王国之泪四个字。 页面的整体布局是这样的,总共有 7 页,即高度为 700vh,每一页都具备不同的布局格调款式,滚动时都会具备缓动成果。 关上以下链接,在线预览成果,本文中的 gif 造成丢帧和画质损失,大屏拜访成果更佳。 在线预览地址:https://dragonir.github.io/tearsOfTheKingdom/本专栏系列代码托管在 Github 仓库【threejs-odessey】,后续所有目录也都将在此仓库中更新。 代码仓库地址:git@github.com:dragonir/threejs-odessey.git原理本文是应用 React Three Fiber 实现的,它不仅能够非常容易实现丑陋的三维图形,在二维立体页面开发中也能大放异彩。在开始实现本文案例之前,咱们先来汇总下本文中须要利用到的知识点。把握这些原理和办法,能够帮忙咱们迅速构建一个交互体验极佳的平滑滚动页面。 useFrame此 hook 容许在页面每一帧渲染的时候运行代码,比方更新渲染成果、控件等,与 Three.js 中调用 requestAnimationFrame 履行重绘动画成果是一样的。你将接管到状态值 state 和时钟增量 delta。回调函数将在渲染帧之前被调用,当组件卸载时,它会主动从渲染循环中登记。· useFrame((state, delta, xrFrame) => { // 此函数在共享渲染循环内以本机刷新率运行}); 留神,在 useFrame 中不能应用 setState 更新状态值!管制渲染循序如果你须要更多的管制,你能够传递一个数字渲染优先级值。这将导致 React Three Fiber 齐全禁用主动渲染。当初,渲染程序将由咱们本人管制,这在前期渲染通道解决以及在多个视图渲染的场景下十分有用。 ...

May 6, 2023 · 6 min · jiezi

关于three.js:Threejs教程顶点索引复用顶点数据

举荐:将NSDT场景编辑器退出你3D工具链其余工具系列:NSDT简石数字孪生 顶点索引复用顶点数据通过几何体BufferGeometry的顶点索引属性BufferGeometry.index能够设置几何体顶点索引数据,如果你有WebGL根底很容易了解顶点索引的概念,如果没有也没有关系,上面会通过一个简略的例子形象阐明。比方绘制一个矩形网格模型,至多须要两个三角形拼接而成,两个三角形,每个三角形有三个顶点,也就是说须要定义6个顶点地位数据。对于矩形网格模型而言,两个三角形有两个顶点地位是重合的。也就是说能够反复的地位能够定义一次,而后通过通过顶点数组的索引值获取这些顶点地位数据。 不应用顶点索引上面通过几何体六个顶点定义了两个三角形,几何体的顶点地位数据、顶点法向量数据都是6个。 var geometry = new THREE.BufferGeometry(); //申明一个空几何体对象//类型数组创立顶点地位position数据var vertices = new Float32Array([ 0, 0, 0, //顶点1坐标 80, 0, 0, //顶点2坐标 80, 80, 0, //顶点3坐标 0, 0, 0, //顶点4坐标 和顶点1地位雷同 80, 80, 0, //顶点5坐标 和顶点3地位雷同 0, 80, 0, //顶点6坐标]);// 创立属性缓冲区对象var attribue = new THREE.BufferAttribute(vertices, 3); //3个为一组// 设置几何体attributes属性的地位position属性geometry.attributes.position = attribuevar normals = new Float32Array([ 0, 0, 1, //顶点1法向量 0, 0, 1, //顶点2法向量 0, 0, 1, //顶点3法向量 0, 0, 1, //顶点4法向量 0, 0, 1, //顶点5法向量 0, 0, 1, //顶点6法向量]);// 设置几何体attributes属性的地位normal属性geometry.attributes.normal = new THREE.BufferAttribute(normals, 3); //3个为一组,示意一个顶点的xyz坐标顶点索引上面代码通过几何体BufferGeometry的顶点索引BufferGeometry.index定义了一个矩形。通过顶点索引组织网格模型三角形的绘制,因为矩形的两个三角形有两个顶点地位反复,所以顶点地位数据、顶点法向量数据都只须要定义4个就能够。 ...

April 26, 2023 · 1 min · jiezi

关于three.js:Threejs教程顶点位置数据解析渲染

举荐:将NSDT场景编辑器退出你3D工具链其余工具系列:NSDT简石数字孪生 顶点地位数据解析渲染如果你没有WebGL根底,能够先不必记忆每个的threejs 具体内容,有一个大抵印象即可,学习本节课的重点是建设顶点的概念。如果你建设了顶点的概念,那么对于你深刻了解学习hree.js很有帮忙。如果你曾经有WebGL根底或者说图形学根底,阐明你必定有顶点的概念,本节课重点能够放在学习threejs的API应用细节,threejs引擎是如何封装webgl的。 JavaScript类型化数组本节课会用到javascript的类型化数组,你如果不理解,可查看MDN对于javascript类型化数组的介绍,也能够查看文章类型化数组。 自定义几何体你能够间接调用BoxGeometry间接创立一个立方体几何体,调用SphereGeometry创立一个球体几何体。不过为了大家更好的建设顶点概念,通过上面的代码自定义了一个几何体,通过网格模型能够渲染进去两个三角形成果。上面代码通过Threejs引擎的BufferGeometry和BufferAttribute两个API自定义了一个具备六个顶点数据的几何体。var geometry = new THREE.BufferGeometry(); //创立一个Buffer类型几何体对象 //类型数组创立顶点数据var vertices = new Float32Array([ 0, 0, 0, //顶点1坐标 50, 0, 0, //顶点2坐标 0, 100, 0, //顶点3坐标 0, 0, 10, //顶点4坐标 0, 0, 100, //顶点5坐标 50, 0, 10, //顶点6坐标]);// 创立属性缓冲区对象var attribue = new THREE.BufferAttribute(vertices, 3); //3个为一组,示意一个顶点的xyz坐标// 设置几何体attributes属性的地位属性geometry.attributes.position = attribue;通过自定义的几何体创立一个网格模型。对于网格模型而言,几何体所有顶点每三个顶点为一组能够确定一个三角形,几何体是六个顶点,也就是说能够绘制两个三角形,当然了你能够本人再减少三个顶点地位坐标数据,测试下渲染成果。 // 三角面(网格)渲染模式var material = new THREE.MeshBasicMaterial({ color: 0x0000ff, //三角面色彩 side: THREE.DoubleSide //两面可见}); //材质对象var mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh点模型Points为了更好的了解几何体是由顶点形成的,能够把几何体geometry作为点模型Points而不是网格模型Mesh的参数,你会发现下面的六个点坐标会渲染为六个方形的点区域,能够用上面代码代替下面的网格模型局部代码测试成果。对于网格模型Mesh而言,几何体geometry三个顶点为一组渲染进去一个三角形,对于点模型Points而言,几何体的每个顶点对应地位都会渲染进去一个方形的点区域,该区域能够设置大小。 // 点渲染模式var material = new THREE.PointsMaterial({ color: 0xff0000, size: 10.0 //点对象像素尺寸}); //材质对象var points = new THREE.Points(geometry, material); //点模型对象scene.add(points); //点对象增加到场景中 ...

April 21, 2023 · 1 min · jiezi

关于three.js:Threejs-进阶之旅全景漫游高阶版在线看房-

申明:本文波及图文和模型素材仅用于集体学习、钻研和观赏,请勿二次批改、非法流传、转载、出版、商用、及进行其余获利行为。 摘要专栏上篇文章《Three.js 进阶之旅:全景漫游-初阶挪动相机版》中通过创立多个球体全景场景并挪动相机和控制器的形式实现了多个场景之间的穿梭漫游。这种形式的毛病也是不言而喻的,随着全景场景的减少来创立对应数量的球体,使得空间关系计算难度晋升,并且大幅升高浏览器渲染行性能。在上一篇文章的根底上,本文通过以下几点对全景性能加以优化,最初实现一个能够利用到理论我的项目中的在线看房案例。通过浏览本文和实际案例,你将学到的常识包含:应用 Three.js 用新的技术思路实现多个场景的加载和场景间的丝滑过渡切换、随着空间始终和角度实时变动的房源小地图、在全景场景中增加如高空指引、空间物体展现、房间标注等多种类型的交互热点等。 成果咱们先来看看本文在线看房案例的最终实现成果,页面主体由代表多个房间的全景图 、全景空间中的用于标识物体的交互热点 、显示房间名称的空间热点 ️ 、用于高空后退指引的交互热点 、固定在侧边的房间切换按钮 ◻️ 以及右上侧的房间小地图 形成。左右拖动页面能够进行以后房间的全景预览,同时小地图上的锚点旋转角度和地位也依据以后房间的地位和旋转角度的变动而变动,应用鼠标滚轮或触摸板放大放大页面能够查看房间全景图的整体和部分细节。 点击高空后退指引标记热点或空间中的房间名标签热点、以及固定在左边的房间名按钮时能够丝滑切换到对应的房间,固定在右侧的按钮和空间中的房间名标签之间互相联动,当页面视区无奈看到空间中的房间名标签时,它会主动固定到右侧按钮处。点击房间中的物体标识热点能够与之产生交互。 关上以下链接,在线预览成果,gif 造成丢帧和画质损失,在线大屏拜访成果更佳。 在线预览地址:https://dragonir.github.io/panorama-advanced/本专栏系列代码托管在 Github 仓库【threejs-odessey】,后续所有目录也都将在此仓库中更新。 代码仓库地址:git@github.com:dragonir/threejs-odessey.git原理比照上篇有哪些优化点看完本文在线看房案例,咱们先来总结下本文在上篇文章示例的根底上,做了哪些优化?下图几个标注点对应本文实现的一些新的性能和优化,通过以下几点的实现能够晋升多个全景场景漫游我的项目的加载渲染性能和用户体验。 ①:是指应用新的技术思路加载多个全景图场景,并应用着色器实现多个场景之间的优雅过渡。②:是用于标注室内物体的空间交互热点,点击能够实现交互。③:是固定于页面侧边的切换空间按钮,只有悬浮在空间中的房间标签④看不见的时候,对应的房间切换按钮才会显示。④:是悬浮在空间中的房间标签,当旋转场景看不见它的时候,它会固定到③处,造成空间的标签和固定在侧边的标签之间是互相联动的视觉效果。⑤:是高空场景切换指引热点,点击高空的热点能够切换到下个场景,视觉上造成在空间中后退的成果。⑥:是示意整个房子的小地图,在全景空间中旋转或者全景漫游时,图中的锚点的方向和地位会对应扭转。 房间场景切换原理上篇文章示例咱们通过创立多个球体挪动相机和控制器的形式实现多个全景场景之间的漫游,而这篇文章中,咱们将通过创立多个场景的形式,来实现两个全景场景之间的过渡漫游成果。 实现原理示意图如下所示,页面总共将创立 3 个场景,origin 示意以后场景,destination 示意指标场景,利用以后场景和指标场景合成用于展现过渡成果的 transition 过渡场景,当点击切换房间按钮时,三个场景的加载顺便别离为 origin -> transition -> destiontion,由此在视觉上造成从上个房间切换到下个房间并且随同突变过渡的场景漫游成果。 实现对应下面几个优化点及多个场景的切换原理,咱们当初来一步步实现本文中的案例: 〇 场景初始化<canvas class="webgl"></canvas>咱们先来看看页面应用了哪些资源,OrbitControls 是镜头轨道控制器,能够在全景图场景中应用鼠标进行旋转和放大放大;TWEEN 和 Animations 用于实现一些镜头补间动画成果;rooms, markers, roomLabels 是自定义的数据,别离示意房间信息、房间高空后退指引标记物和房间名称热点标记物;fragment 和 vertex 是用于实现原始场景和指标场景切换的动画过渡成果;TinyMap 是和以后房间地位和镜头旋转方向同步的小地图组件。 import * as THREE from 'three';import { OrbitControls } from '@/utils/OrbitControls.js';import { TWEEN } from 'three/examples/jsm/libs/tween.module.min.js';import { rooms, markers, roomLabels } from '@/views/home/data';import fragment from '@/shaders/cross/fragment.js';import vertex from '@/shaders/cross/vertex.js';import Animations from '@/utils/animations';import TinyMap from '@/components/TinyMap.vue';初始化 Three.js 构建三维场景的渲染器、场景、相机、控制器等根本元素,须要留神的是这次把场景命名为原场景 sceneOrigin,因为后续还要场景多个场景,通过多个场景间的动画过渡成果,实现各个房间全景图之间的穿梭漫游成果。 ...

April 10, 2023 · 5 min · jiezi

关于three.js:Threejs-进阶之旅全景漫游初阶移动相机版

申明:本文波及图文和模型素材仅用于集体学习、钻研和观赏,请勿二次批改、非法流传、转载、出版、商用、及进行其余获利行为。 摘要3D 全景技术能够实现日常生活中的很多性能需要,比方地图的街景全景模式、数字展厅、在线看房、社交媒体的全景图预览、短视频直播平台的全景直播等。Three.js 实现全景性能也是非常不便的,当然了目前曾经有很多相干内容的文章,我之前就写过一篇《Three.js 实现3D全景侦探小游戏》。因而本文内容及此专栏下一篇文章探讨的重点不是如何实现 3D 全景图性能,而是如何一步步优雅实现在多个3D全景中穿梭漫游,达到如在真实世界中后退后退的视觉效果。 全景漫游系列文章将分为高低两篇,本篇内容咱们先介绍如何通过挪动相机的办法来达到场景切换的目标。通过本文的学习,你将学到的知识点包含:在 Three.js 中创立全景图的几种形式、在 3D 全景图中增加交互热点、利用 Tween.js 实现相机切换动画、多个全景图之间的切换等。 成果本文最终将实现如下的成果,左右管制鼠标旋转屏幕能够预览室内三维全景图,同时全景图内有多个交互热点,它们标识着三维场景内的一些物体,比方沙发 、电视机 等,交互热点会随着场景的旋转而旋转,点击热点 能够弹出交互反馈提示框。 点击屏幕上有其余场景名称的按钮比方 客厅、卧室、书房 时,能够从以后场景切换到指标场景全景图,交互热点也会同时切换。 关上以下链接,在线预览成果,大屏拜访成果更佳。 在线预览地址:https://dragonir.github.io/panorama-basic/本专栏系列代码托管在 Github 仓库【threejs-odessey】,后续所有目录也都将在此仓库中更新。 代码仓库地址:git@github.com:dragonir/threejs-odessey.git原理咱们先来简略总结下在 Three.js 中实现三维全景性能的有哪些形式: 球体在球体内增加 HDR 全景照片能够实现三维全景性能,全景照片是一张用球形相机拍摄的图片,如下图所示: const geometry = new THREE.SphereGeometry(500, 60, 40);geometry.scale(- 1, 1, 1);const texture = new THREE.TextureLoader().load( 'textures/hdr.jpg');const material = new THREE.MeshBasicMaterial({ map: texture });const mesh = new THREE.Mesh(geometry, material);scene.add(mesh); 球体全景图 Three.js 官网示例立方体在立方体内增加全景图贴图的形式也能够实现三维全景图性能,此时须要对 HDR 全景照片进行裁切,宰割成 6 张来别离对应立方体的 6 个面。 ...

March 28, 2023 · 5 min · jiezi

关于three.js:-和我一起学Threejs初级篇5-掌握材质

本文为《和我一起学 Three.js 系列》「高级篇」的第六篇文章,文中的示例代码基于往期文章的代码进行扩大,在入手实际前,请确保您曾经在本地创立好了引入 three.js 的古代前端开发环境。工夫过的真快!明天(2023.3.27),我终于依照当初每周更新一篇文章的打算,实现了《和我一起学 Three.js 系列》「高级篇」的 6 篇「无条件公布」的文章。每篇文章我都花了大量的工夫收集材料,从 0 开始编写代码(以保障您操作的后果和我雷同)以及思考如何让文章更易于了解,截止目前,您应该曾经能够在 3D 场景中增加各种平面物品并与之交互,您还应该理解到纹理的不同品种,以及它是让简略的 3D 模型变为事实物品的一把要害钥匙 。 而本章节,咱们不仅会让上一章所学没有徒劳,还会更进一步,向您展现 3D 世界又一个激动人心的概念:「材质(Materail)」,通过学习本章节的内容,您将有能力串联起来之前所有的常识,打造一个充斥趣味的 3D 世界!话不多说,让咱们开始吧 ! 0. 系列文章合集《 和我一起学【Three.js】「高级篇」:0. 总论》《 和我一起学【Three.js】「高级篇」:1. 搭建 3D 场景》《 和我一起学【Three.js】「高级篇」:2. 把握几何体》《 和我一起学【Three.js】「高级篇」:3. 把握摄影机》《 和我一起学【Three.js】「高级篇」:4. 把握纹理》您以后在这里 《 和我一起学【Three.js】「高级篇」:5. 把握材质》《 和我一起学【Three.js】「高级篇」:6. 把握光照》( 本篇文章将于 2023.4.3 更新并反对公众号内付费观看,将在全系列文章总赞数 >= 500 时解锁公布)《 和我一起学【Three.js】「高级篇」:7. 把握暗影》( 本篇文章将于 2023.4.10 更新并反对公众号内付费观看,将在全系列文章总赞数 >= 1000 时解锁公布)《 和我一起学【Three.js】「高级篇」:8. 死记硬背,神功小成》( 本篇文章将于 2023.4.17 更新并反对公众号内付费观看,将在第 7,8 章节解锁后公布)1. 什么是材质(material)?在 Three.js 中,「材质(material)」是用来定义物体外观的属性。它蕴含如何渲染物体的信息,如色彩,光照,反射等等。材质能够被赋予不同的属性,以便实现各种不同的外观成果。 咱们上一章所提到的「纹理(Texture)」是材质的一种属性,它能够被用来在物体外表增加图像,图案,色彩等。纹理能够用来模仿各种不同的外表个性,例如木头、金属、石头等等。 为了区别「材质」和「纹理」的概念,咱们能够设想一件木质家具。该家具的「材质」是木头,但它的外观可能会因为在木外表上利用不同的纹理而产生不同的成果。例如,一个家具制造商可能会在木外表上利用一层润滑的漆,使其看起来更加润滑;另一个家具制造商则可能会在木外表上利用一个毛糙的纹理,使其看起来更加人造。同样地,在 Three.js 中,咱们能够通过利用不同的纹理来扭转物体的外观,以实现咱们想要的成果。 在 Three.js 中,材质被用于为几何体的每个可见像素着色,这一过程的算法实现被称为「着色器(shader)」(一种在 GPU 上运行的程序,它用于计算几何体的每个像素的色彩和外观,能够对材质进行定制化的着色和光照计算,以及其余各种成果)。至此,咱们能够将之前一张形容 Three.js 中创立 3D 对象的类关系图更新为上面这样: ...

March 27, 2023 · 3 min · jiezi

关于three.js:-和我一起学Threejs初级篇4-掌握纹理

本文为《和我一起学 Three.js 系列》高级篇的第五篇文章,文中的示例代码基于上一篇文章中的代码进行相应的扩大,请确保您曾经浏览上一篇文章,并和我一起应用 vite 搭建了古代前端开发环境。感谢您一路追随我来到这里!截止目前为止,咱们应该有能力搭建一个 3D 场景,在其中增加各种官网提供的几何体,并通过应用控制器,调整摄影机地位与几何体交互。这所有看起来都还不错,但未免有些枯燥。所幸本章节以及下一节的内容将让咱们的 3D 世界变得丰富多彩。 本章节咱们谈判及 Web 3D 世界一个十分常见的概念:「纹理」(Texture),它将会和下一章节的「材质」(Materials)概念一起使咱们简略的几何体变成真实世界中咱们相熟的物体!让咱们一起开展这趟旅途吧! 1. 什么是纹理(Textures)兴许应用纹理(Textures)的另一个名称能更加直观的反映其本质:「贴图」。没错,纹理自身就是一些图片,用来笼罩在咱们的几何体或模型的外表,从而使物体具备拟真的成果。 尽管听起来简略,然而别忘了,咱们不只是想让物体「看起来」像是某个事实物体,咱们还心愿它合乎物体「在事实中的样子」,我是指在面对不同的光照环境时,物体须要有不同的反馈,以及物体须要有其对应的「质感」。这就不是一张图片能够解决的问题了,为此,咱们须要有不同类型的图片表白物体的不同属性(例如:哪里通明,哪里应该有金属光泽等等)。还须要理解在 Three.js 的世界中,如何为一个对象加载多张纹理贴图。 让咱们先从意识不同的纹理开始: 2. 纹理的品种 在本章节中,我并不会向您介绍具体「贴图」的办法,因为这波及「材质」和「光照」等稍后咱们要提及的内容。本章节的用意在于让您明确纹理有哪些品种,它们长什么样子,以及在物体上会出现什么样的成果。如果您可能胜利拜访:https://3dtextures.me/ 这个网站,并下载其提供的收费纹理资源,您会发现,其中的一些图片会令人不明所以,它们是不同的纹理类别,有特定的用处: 图片来源于:https://drive.google.com/drive/folders/1tLmUWv9WocFh88XMqsexdTkMDE6R2ABg 本章节我会应用该纹理进行演示,后续将不再额定表明资源出处。 2.1 反照率纹理(Albedo Texture)反照率纹理(Albedo Texture)又称「色彩(Color)纹理」,是一种根本的纹理类型,用于模仿物体外表的色彩和反射个性。它是最罕用的纹理类型之一,也是创立真切 3D 场景必不可少的一部分。 在 3D 渲染中,反照率是指物体外表对于不同色彩光线的反射率,通常用一张 RGB 图片来示意。反照率纹理中的每个像素都对应着物体外表上的一个点,这个点的色彩和反射个性能够由纹理像素的色彩值来确定。例如,纹理中的红色像素示意该点外表对所有色彩的光线反射率都很高,彩色像素示意该点外表对所有色彩的光线反射率都很低。 上面的图片是一张反照率纹理: 将反照率纹理利用于一个球体时,会取得这样的成果: 为了演示不便,我设置了强度为 2 的红色环境光以及一束蓝色直射光,并且将粗糙度与金属度设置为 0.8,我想您走漏这些只是为了未来您可能实现和我一样的成果,但当初您并不需要晓得如何设置。2.2 高度纹理(Height Texture)高度纹理(Height Texture)又称为「深度纹理」。在该图像中,每个像素的灰度值被用于示意相应地位的高度或深度。它通常被用于创立几何体的外表细节,例如山峰、岩石、河流等。 通过应用高度纹理,能够在几何体外表增加细节,并模仿光的反射和折射等视觉效果。 上面的图片是一张高度纹理: 在增加高度纹理后,咱们的物体将会有显著的凹凸成果: 2.3 透明度纹理(Alpha Texture)透明度纹理(Alpha Texture)也称为「Alpha 通道纹理」,它是一种非凡的纹理,其中蕴含了用于管制材质透明度的 Alpha 通道数据。 Alpha 通道是图像中的第四个通道,它示意每个像素的透明度值。在 Alpha 纹理中,Alpha 通道的值被用于管制每个像素的透明度。应用 Alpha 纹理,您能够轻松地创立通明的材质成果。例如,您能够应用 Alpha 纹理来管制几何体外表的通明区域,以实现相似玻璃、水、烟雾等材质的成果。 上面的图片是一张透明度纹理: ...

March 20, 2023 · 2 min · jiezi

关于three.js:Threejs-进阶之旅物理效果3D乒乓球小游戏-

申明:本文波及图文和模型素材仅用于集体学习、钻研和观赏,请勿二次批改、非法流传、转载、出版、商用、及进行其余获利行为。 摘要本文在专栏上一篇内容《Three.js 进阶之旅:物理成果-碰撞和声音》的根底上,将应用新的技术栈 React Three Fiber 和 Cannon.js 来实现一个具备物理个性的小游戏,通过本文的浏览,你将学习到的知识点包含:理解什么是 React Three Fiber 及它的相干生态、应用 React Three Fiber 搭建根底三维场景、如何应用新技术栈给场景中对象的增加物理个性等,最初利用上述知识点,将开发一个简略的乒乓球小游戏。 成果在正式学习之前,咱们先来看看本文示例最终实现成果:页面主体内容是一个手握乒乓球拍的模型和一个乒乓球 ,对球拍像现实生活中一样进行颠球施力操作,乒乓球能够在球拍上弹起,乒乓球弹起的高度随着施加在球拍上的力的大小的变动而变动,球拍地方显示的是间断颠球次数 5️,当乒乓球从球拍掉落时一局游戏完结,球拍上的数字归零 0️ 。快来试试你一次能够颠多少个球吧 。 关上以下链接,在线预览成果,大屏拜访成果更佳。 在线预览地址:https://dragonir.github.io/physics-pingpong/本专栏系列代码托管在 Github 仓库【threejs-odessey】,后续所有目录也都将在此仓库中更新。 代码仓库地址:git@github.com:dragonir/threejs-odessey.git原理React-Three-FiberReact Three Fiber 是一个基于 Three.js 的 React 渲染器,简称 R3F。它像是一个配置器,把 Three.js 的对象映射为 R3F 中的组件。以下是一些相干链接: 仓库: https://github.com/pmndrs/react-three-fiber官网: https://docs.pmnd.rs/react-three-fiber/getting-started/introduction示例: https://docs.pmnd.rs/react-three-fiber/getting-started/examples 特点应用可重用的组件以申明形式构建动静场景图,使 Three.js 的解决变得更加轻松,并使代码库更加整洁。这些组件对状态变动做出反馈,具备开箱即用的交互性。Three.js 中所有内容都能在这里运行。它不针对特定的 Three.js 版本,也不须要更新以批改,增加或删除上游性能。渲染性能与 Three.js 和 GPU 相仿。组件参加 React 之外的 render loop 时,没有任何额定开销。写 React Three Fiber 比拟繁琐,咱们能够写成 R3F 或简称为 Fiber。让咱们从当初开始应用 R3F 吧。 ...

March 20, 2023 · 6 min · jiezi

关于three.js:-和我一起学Threejs初级篇3-掌握摄影机

本文为《和我一起学 Three.js 系列》高级篇的第四篇文章,文中的示例代码基于上一篇文章中的代码进行相应的扩大,请确保您曾经浏览上一篇文章,并和我一起应用 vite 搭建了古代前端开发环境。1. 什么是摄影机?在 Three.js 中,摄影机(Camera)用来定义场景中的「视角」以及「可见范畴」(_为了晋升渲染性能,超出可见范畴的物体将不会被渲染,这被称为「视锥体剔除(frustum culling)」技术_)。除此之外,摄影机实际上还管制着场景中物体的「地位」和「大小」,通过挪动摄影机,GPU 会渲染出合乎直觉的物体透视成果,从而让咱们有优良的 3D 场景体验。 因而,把握 Three.js 提供的各种摄影机以及管制摄影机的各种办法,可能使咱们的 3D 场景和用户进行互动并极大的丰盛场景的出现形式。 把握本章节的概念和内容十分重要,请您务必放弃急躁,入手实际。2. 摄影机的品种在 Three.js 中,所有摄影机都封装为特定的子类,它们对立继承自一个抽象类:[Camera](https://threejs.org/docs/?q=camera#api/en/cameras/Camera)。Three.js 提供的摄影机有: 数组摄影机([ArrayCamera](https://threejs.org/docs/?q=camera#api/en/cameras/ArrayCamera)):次要用于须要渲染多个视角的场景,例如 VR,AR,多屏幕游戏等。它通过将多个摄影机实例放入数组并传入 ArrayCamera 中,GPU 会渲染各个摄影机视角下的场景,并将它们合并在画布中,例如这个示例展现了不同角度的摄影机察看同一个物体的成果: 平面摄影机([StereoCamera](https://threejs.org/docs/?q=StereoCamera#api/en/cameras/StereoCamera)):能够同时生成两个相机对象,一个渲染左眼图像,一个用于渲染右眼图像。这就很适宜在 VR,AR,3D 视频等利用中产生更加实在的 3D 成果(_由 StereoCamera 生成的 VR 成果能够通过 Oculus Rift,HTC Vive 等 VR 头显设施观看_)。 立方体摄影机([CubeCamera](https://threejs.org/docs/?q=CubeCamera#api/en/cameras/CubeCamera)):用于生成「环境贴图(Environment Map)」,它会生成一个立方体场景,捕获场景内的环境信息,别离在 6 个面各渲染一次,而后将渲染后果用于物体的反射贴图,折射贴图,从而加强场景的真实感和细节成果。「环境贴图」是一种罕用于加强场景真实感的技术,它通过将场景中周围环境信息捕获下来,并将其利用于物体外表的材质中,从而使物体看起来更加实在和具备反射性。具体来说,环境贴图是一张蕴含了场景中周围环境的图像,通常为立方体贴图。它能够捕捉到场景中的反射、折射、漫反射、全局光照等信息,使得物体的外表看起来更加实在,同时也能够加强场景的光照成果。 正交摄影机([OrthographicCamera](https://threejs.org/docs/?q=Ortho#api/en/cameras/OrthographicCamera)):将会渲染一个没有透视成果的场景,无论物体和摄影机的间隔如何,物体的大小都不会放生变动。能够通过该摄影机创立 RTS 类游戏,例如帝国时代。 透视摄影机([PerspectiveCamera](https://threejs.org/docs/?q=Camera#api/en/cameras/PerspectiveCamera)):这是 Three.js 中最罕用的摄影机,它能够模仿人眼察看物体时近大远小的透视成果。 请留神透视摄影机与正交摄影机在渲染立方体时的不同,在正交摄影机中,所有立方体的尺寸是类似的。3. 创立摄影机 上面将进入代码实际环节,请务必确保您已浏览之前的文章,和我领有雷同的开发环境! 本节咱们不会涵盖「立方体摄影机」和「平面摄影机」,因为前者波及咱们下一章的内容,我会放在一起介绍。而后者因为须要额定设施,对于大多数人难以察看后果,因而略去不提。 3.1 透视摄像机让咱们首先介绍最罕用的透视摄像机,创立一个透视摄像机非常简单,只须要实例化 PerspectiveCamera 类,并传入对应的参数: const camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height, 1, 100)PerspectiveCamera 函数接管四个参数,从前到后顺次为: ...

March 13, 2023 · 2 min · jiezi

关于three.js:-和我一起学Threejs初级篇0-总论

「和我一起学 XXX」是我 2023 年的一个新企划,目标是向读者(也包含将来的本人)介绍我正在学习的某项新技术。文章会通过长期重复迭代的形式放弃其内容的新鲜度。文章有较大内容更新时,会在文章结尾进行更新工夫阐明(因为工夫精力有限,更新的内容只能保障少数几个平台的同步,请见谅)。1. 什么是 Three.jsThree.js 是一个基于 WebGL 的 3D JavaScript 开源库(遵循 MIT 协定),它使 JavaScript 开发者可能更不便地在 Web 利用中创立 3D 场景。 请留神该定义的如下局部: 基于 WebGL:WebGL 是一种 3D 绘图协定,对于开发者而言,它是一组更底层的绘图 API,它负责绘制点,线与三角形,应用 WebGL 绘制简单的 3D 场景,须要十分多的代码;3D JavaScript 开源库:Three.js 基于十分宽松的 MIT 协定,这意味着您能够自在应用,批改 Three.js 代码创立商业利用;更不便地:就像 jQuery 基于 JavaScript 提供了更敌对地 API 使开发者可能轻松地操作 DOM 一样,Three.js 也封装出更敌对地 API 供开发者绘制 3D 场景,相较于应用 WebGL,应用 Three.js 绘制 3D 场景须要的代码量要少的多得多。3D 场景:它蕴含: 3D 游戏;建筑设计和数据可视化看板;AR,VR;虚构展厅,虚构商品展现;交互式展览,培训等;您能够在 Three.js 官网发现丰盛的案例,它们从不同方面展现了 Three.js 的魅力和弱小! 2. (我)为什么要学习 Three.js?在理解 Three.js 是什么后,若抉择持续学习,想必您有本人的理由。对于我而言,学习 Three.js 的次要动机是「好玩」(Just for fun!)。 ...

February 20, 2023 · 1 min · jiezi

关于three.js:Threejs-进阶之旅物理效果碰撞和声音-

申明:本文波及图文和模型素材仅用于集体学习、钻研和观赏,请勿二次批改、非法流传、转载、出版、商用、及进行其余获利行为。 摘要本文内容次要汇总如何在 Three.js 创立的 3D 世界中增加物理成果,使其更加实在。所谓物理成果指的是对象会有重力,它们能够互相碰撞,施加力之后能够挪动,而且通过铰链和滑块还能够在挪动过程中在对象上施加束缚。 通过本文的浏览,你将学习到如何应用 Cannon.js 在 Three.js 中创立一个 3D 物理世界,并在物理世界更新对象、分割材质、施加外力、解决多个物体中增加物体之间的碰撞成果,通过检测碰撞强烈水平来增加撞击声音等。 成果本文最终将实现如下所示的成果,点击 DAT.GUI 中创立立方体 和球体 的按钮,对应的物体将在领有重力的三维世界中坠落,物体与高空及物体与物体之间产生碰撞时能够产生与碰撞强度匹配的撞击音频 ,点击重置按钮,创立的物体将被革除。 关上以下链接,在线预览成果,大屏拜访成果更佳。 在线预览地址:https://dragonir.github.io/physics-cannon本专栏系列代码托管在 Github 仓库【threejs-odessey】,后续所有目录也都将在此仓库中更新。 代码仓库地址:git@github.com:dragonir/threejs-odessey.git原理在专栏之前的原理和示例学习中,咱们曾经能够应用光照、暗影、Raycaster 等个性生成一些简略的物理成果,然而如果须要实现像物体张力、摩擦力、拉伸、反弹等物理成果时,咱们能够应用一些业余的物理个性开源库来实现。 为了实现物理成果,咱们将在 Three.js 中创立一个物理世界,它纯正是实践性质的,咱们无奈间接看到它,然而在其中,三维物体将产生掉落、碰撞、摩擦、滑动等物理个性。具体原理是当咱们在 Three.js 中创立一个网格模型时,同时会将其增加到物理世界中,在每一帧渲染任何内容之前咱们会通知物理世界如何自行更新,而后咱们将获取物理世界中更新的位移和旋转坐标数据,将其利用到 Three.js 三维网格中。 库曾经有很多性能齐备的物理个性库,咱们就没必要反复造轮子了。物理个性库能够分为 2D 库和 3D 库,尽管咱们是应用 Three.js 开发三维性能,然而有些 2D库 在三维世界中同样是实用的而且它们的性能会更好,如果咱们须要开发的物理性能是碰撞类的,则能够应用 2D 库,比方Ouigo Let's play就是一个应用 2D 库开发的优良示例。上面是一些罕用的物理个性库。 对于 3D 物理库,次要有以下三个: Ammo.js 官网:http://schteppe.github.io/ammo.js-demos/仓库:https://github.com/kripken/ammo.js/文档:https://github.com/kripken/ammo.js/#readmeBullet 一个应用 C++ 编写的物理引擎的 JavaScript 间接移植包比拟重量级,以后依然由社区更新保护Cannon.js 官网:https://schteppe.github.io/cannon.js/仓库:https://github.com/schteppe/cannon.js文档:http://schteppe.github.io/cannon.js/docs/比 Ammo.js 更加轻量级,应用起来更难受次要由一个开发者保护,曾经多年未更新,有一个保护的 fork 是 cannon-esOimo.js ...

February 15, 2023 · 6 min · jiezi

关于three.js:threejs起步学习之创建旋转立方体并随时停止和控制旋转

间接看成品 能够看到点击开始旋转的时候立方体开始旋转,点击进行旋转的时候立方体进行旋转。 先放官网文档threejs官网 点击en切换为中文,而后点击创立第一个场景,页面就全副变成中文啦。 先应用原生js编写代码,首先就是下载threejs而后引入 <script src="./three.js"></script>而后就能够应用THREE这个对象了 要创立一个立方体,首先咱们须要创立一个场景,一个摄像机,一个渲染器,这样咱们能力透过摄像机看到渲染出的场景 var width = 500var height = 500const scene = new THREE.Scene();const camera = new THREE.PerspectiveCamera(75,width/height,0.1,1000)const renderer = new THREE.WebGLRenderer();renderer.setSize(width,height);document.body.appendChild(renderer.domElement);首先我定义了整体的宽高,如果间接用window.innerWidth和innerHeight的话,就占满了整个页面,所以我抉择固定一个小一点的宽高,不便察看和后续增加元素。 threejs有几种不同的相机,这里用的是PerspectiveCamera(透视相机)。第一个参数是视线角度(FOV),就是你能在显示器上看到的场景范畴,单位是角度(与弧度不同)常常玩游戏的人必定晓得这个参数的作用,比方射击游戏里,FOV越大,能看到的场景越多这里举荐一个FOV无关的材料https://zhuanlan.zhihu.com/p/... 第二个参数是长宽比,即画面的比例,比方4:3、16:9这种。比例设置不对,那一个物体看起来就会被压扁或者拉伸。 接下来两个参数是近截面和远截面。当一个物体的某个局部离摄像机的近截面更近或者远截面更远的时候,这部分就不会被渲染到场景里。 上面就是渲染器了,这里应用的是WebGLRenderer渲染器,其余的我临时没有用过,也不懂。 创立完渲染器实例当前,咱们还须要设置渲染尺寸,如果整个页面都须要渲染,那就把setSize里的参数改为浏览器的宽高,如果性能不行,能够尝试调小这个值,比方都除以2。 最初一步就是把渲染器renderer的dom元素renderer.domElement增加到咱们的HTML中,也就是在页面里减少了一个<canvas> 当然,当初页面上还是一片白,因为咱们没有真正的渲染canvas外面的货色 加上这一步 function animate() { renderer.render( scene, camera );}animate();就能够看到页面上渲染进去了一个彩色的区域 第二步,摆个盒子下来 先上代码 const geometry = new THREE.BoxGeometry(1,1,1);const material = new THREE.MeshBasicMaterial({color:0x00ff00});const cube = new THREE.Mesh(geometry,material);scene.add(cube);camera.position.z = 5;要创立一个立方体,咱们须要一个BoxGeometry对象,这个对象蕴含了一个立方体中所有的顶点和面。 上面,咱们须要给这个立方体一个材质,让它有色彩。Threejs自带了几种材质,这里应用的是MeshBasicMaterial。所有的材质都存有利用于他们属性的对象。这里简单化,只设置一个color属性,值为0x00ff00 留神,和CSS中的十六进制不同。 第三步,咱们须要一个Mesh(网格)。网格蕴含一个几何体以及作用在该几何体上的材质。间接讲该网格对象放入到场景中。 个别状况下,当咱们调用scene.add()办法的时候,物体将被间接增加到(0,0,0)坐标,摄像机默认地位也在这里,所以为了防止物体和摄像机重叠,咱们须要将摄像机往外挪动一点,即设置camera的position中的z轴值为5就行 保留当前,页面上就变成了这样 第三步,让立方体动起来 首先弄两个按钮下来 <div> <div class="btn" onclick="btnfunc()">点我开始旋转</div> <div class="btn" onclick="cancel()">点我进行旋转</div></div> ...

February 9, 2023 · 1 min · jiezi

关于three.js:Threejs-进阶之旅新春特典Rabbit-craft-go-

申明:本文波及图文和模型素材仅用于集体学习、钻研和观赏,请勿二次批改、非法流传、转载、出版、商用、及进行其余获利行为。 摘要兔年到了,祝大家身材健,康万事顺利。本文内容作为兔年新春留念页面,将应用 Three.js 及 其余前端开发常识,创立一个以兔子为主题的 3D 简略的趣味页面 Rabbit craft go。本文内容包含应用纯代码创立三维浮岛、小河、树木、兔子、胡萝卜以及兔子的静止交互、浮岛的动画成果等。本文蕴含的知识点绝对比较简单,次要包含 应用 Three.js 网格立方体搭建三维卡通场景、键盘事件的监听与三维场景动画的联合等,如果仔细阅读并实际过本专栏《Three.js 进阶之旅》的话,非常容易把握。 兔子造型来源于 Three.js开源论坛,页面整体造型灵感来源于《我的世界》,页面名称灵感来源于游戏《Lara Craft Go》。成果咱们先来看看实现成果,页面加载实现后是一个游戏操作提醒界面,能够通过键盘 ⌨ 空格键 及 W、 A、 S、 D 或方向键操作小兔子静止。点击开始按钮后,游戏提醒界面隐没,能够看到倒三角造型的天空浮岛及浮岛上方的树木 、河流 ⛵、桥 、胡萝卜 、兔子 等元素,接着摄像机镜头 主动拉近并聚焦到兔子上。 依照操作提醒界面的按键,能够操作兔子进行后退、转向、跳跃等静止,当兔子的静止地位触碰到胡萝卜时,胡萝卜会隐没同时兔子会进行跳跃静止。当兔子静止到小河或者超出浮岛范畴时,兔子则会坠落到下方。 关上以下链接,在线预览成果,大屏拜访成果更佳。 在线预览地址:https://dragonir.github.io/rabbit-craft-go本专栏系列代码托管在 Github 仓库【threejs-odessey】,后续所有目录也都将在此仓库中更新。 代码仓库地址:git@github.com:dragonir/threejs-odessey.git实现文章篇幅无限,因而删减了三维模型的地位信息等细节调整代码,只提供构建三维模型的整体思路逻辑,想理解该局部内容的具体介绍能够浏览本专栏前几篇文章及浏览本文配套源码。当初,咱们来看看整个页面的实现具体步骤: 页面构造Rabbit Craft Go 页面的整体构造如下,其中 canvas.webgl 是用于渲染场景的容器、残余标签都是一些装璜元素或提醒语。 <canvas class="webgl"></canvas><div class="mask" id="mask"> <div class="box"> <div class="keyboard"> <div class="row"><span class="key">W/↑</span></div> <div class="row"><span class="key">A/←</span><span class="key">S/↓</span><span class="key">D/→</span></div> <div class="row"><span class="key space">space</span></div> </div> <p class="tips"><b>W</b>: 行走&emsp;<b>S</b>: 进行&emsp;<b>A</b>: 向左转&emsp;<b>D</b>: 向右转&emsp;<b>空格键</b>: 跳跃</p> <p class="start"><button class="button" id="start_button">开始</button></p> </div></div><a class='github' href='https://github.com/dragonir/threejs-odessey' target='_blank' rel='noreferrer'> <span class='author'>three.js odessey</span></a><h1 class="title">RABBIT CRAFT GO!</h1><div class="banner"><i></i></div>场景初始化场景初始化过程中,咱们引入必须的开发资源,并初始化渲染场景、相机、控制器、光照、页面缩放适配等。其中内部资源的引入,其中 OrbitControls 用于页面镜头缩放及挪动管制;TWEEN 和 Animations 用于生成镜头补间动画,也就是刚开始时浮岛由远及近的镜头切换动画中成果;Island、Carrot、Rabbit、Waterfall 等是用来构建三维世界的类。为了使场景更加卡通化,应用了 THREE.sRGBEncoding 渲染成果。场景中增加了两种光源,其中 THREE.DirectionalLight 用来生成暗影成果。 ...

January 22, 2023 · 7 min · jiezi

关于three.js:Threejs开发3D展馆-大帅老猿threejs特训

本文将介绍如果应用Threejs开发制作一个3D展馆,成果如图: 首先,咱们须要一个展馆模型。我是通过Blender做进去的,在Blender中将模型导出为glb格局。 接下来,咱们通过代码将模型加载到网页中。 在3D图形开发中,最根本的概念就是场景、相机和光源。这三个是形成3D世界的基本要素。所以,咱们须要先创立一个场景、设置相机和光源。 const scene = new THREE.Scene(); //设置相机const camera = new THREE.PerspectiveCamera(45, container.offsetWidth / container.offsetHeight, 10, 10000);camera.position.set(5, 10, -40);//设置相机地位camera.lookAt(0,0,0);//让相机看相原点//光源,平行光var light2 = new THREE.DirectionalLight(0xffffff, 1);scene.add(light2);而后,咱们应用Threejs提供的GLTFLoader将模型加载进来。 const loader = new GLTFLoader();loader.load('../resources/models/zhanguan2.glb',function (gltf) {scene.add(gltf.scene);//将模型增加到场景中})这时候咱们会发现,整个场馆有点暗,而且只有场馆外面是亮的,围墙是黑乎乎的。因为咱们用的是平行光,有些中央会照不到,所以就是黑的了。接下来,咱们用环境贴图的形式,照亮整个场馆。咱们应用这张图片: new RGBELoader().load('../resources/sky.hdr', function (texture) { texture.mapping = THREE.EquirectangularReflectionMapping scene.environment = texture renderer.outputEncoding = THREE.sRGBEncoding renderer.render(scene, camera) }) 接下来,咱们将人物角色加载进来,同样应用的是GLTFLoader. new GLTFLoader().load('../resources/models/player.glb', (gltf) => { playerMesh = gltf.scene scene.add(playerMesh)}角色加载进来后,如何让她挪动呢?应用playerMesh.translateZ()办法,让其在Z轴方向挪动,对应到场景中,就是在高空上挪动了。那如果碰到墙壁,是不是就不应该让角色继续前进了呢?所以要实现碰撞检测性能,这里应用Threejs中的Raycaster射线检测来实现。 window.addEventListener('keydown', (e) => { if (e.key === 'w') { const curPos = playerMesh.position.clone(); //获取角色以后地位 playerMesh.translateZ(1);//让角色往前走一步 const frontPos = playerMesh.position.clone();//在获取当初的地位 playerMesh.translateZ(-1 );//退回原来的地位 const frontVector3 = frontPos.sub(curPos).normalize()//让后面地位的向量减去前面地位的向量 const raycasterFront = new THREE.Raycaster(playerMesh.position.clone().add(playerHalfHeight), frontVector3); const collisionResultsFrontObjs = raycasterFront.intersectObjects(scene.children); if (collisionResultsFrontObjs && collisionResultsFrontObjs[0] && collisionResultsFrontObjs[0].distance > 1) { // 间隔大于1才能够继续前进 playerMesh.translateZ(0.1); } }if (e.key === 's') {//后退playerMesh.translateZ(-0.1);}})到这里,展馆的基本功能就实现完了。 ...

January 15, 2023 · 1 min · jiezi

关于three.js:threejs初识-大帅老猿threejs特训

threejs初识 | 大帅老猿threejs特训筹备工作要装置three 的 npm 模块,请在你的我的项目文件夹里关上终端窗口,并运行: npm install three引入头文件 import * as THREE from 'three';//相机轨道控制器import {OrbitControls} from "three/examples/jsm/controls/OrbitControls";//gltf资源加载器import {GLTFLoader} from "three/examples/jsm/loaders/GLTFLoader";//纹理贴图加载器import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader';利用创立场景const scene = new THREE.Scene();const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.01, 10);camera.position.set(0.3, 0.3, 0.5);//渲染器const renderer = new THREE.WebGLRenderer({ antialias: true });renderer.setSize(window.innerWidth, window.innerHeight);document.body.appendChild(renderer.domElement);const controls = new OrbitControls(camera, renderer.domElement);//增加灯光const directionLight = new THREE.DirectionalLight(0xffffff, 0.4);scene.add(directionLight);加载资源let donuts;new GLTFLoader().load('../resources/models/donuts.glb', (gltf) => { scene.add(gltf.scene); donuts = gltf.scene; mixer = new THREE.AnimationMixer(gltf.scene); const clips = gltf.animations; // 播放所有动画 clips.forEach(function (clip) { const action = mixer.clipAction(clip); action.loop = THREE.LoopOnce; // 停在最初一帧 action.clampWhenFinished = true; action.play(); });})加载场景贴图new RGBELoader() .load('../resources/sky.hdr', function (texture) { scene.background = texture; texture.mapping = THREE.EquirectangularReflectionMapping; scene.environment = texture; renderer.outputEncoding = THREE.sRGBEncoding; renderer.render(scene, camera);});动画办法实现function animate() { requestAnimationFrame(animate); renderer.render(scene, camera); controls.update(); if (donuts){ donuts.rotation.y += 0.01; } if (mixer) { mixer.update(0.02); }}animate();成果截图 ...

January 15, 2023 · 1 min · jiezi

关于three.js:ThreeJS一盘魔性的甜甜圈-大帅老猿threejs特训

收场魔镜魔镜谁的代码写的最好,最烂的是你,最好的不晓得。。。。。 恍惚间进入了梦幻,收场就是Blender咣当掉下一些超两米的甜甜圈,大喊连忙实现工作不然下一个掉头上 素材解决我丢这是哪个过程出了bug关上Blender一顿拖拽,分和,在磕磕盼盼中管制的了外星来的甜甜圈 看着这甜甜圈仿佛能够吃,离气氛还毛病场景,没有香槟还不能来点烛光 跳动的甜圈刚好在搭建的three场景中减少一束光,照亮不起眼的床 const directionLight = new THREE.DirectionalLight(0xffffff, 0.4);scene.add(directionLight);甜甜圈来吧,到照相机前站着别动,我给你个片段特写GLTFLoader押着怪异的甜甜圈来了 const scene = new THREE.Scene();const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.01, 10);const renderer = new THREE.WebGLRenderer({ antialias: true });renderer.setSize(window.innerWidth, window.innerHeight);document.body.appendChild(renderer.domElement);camera.position.set(0.3, 0.3, 0.5);const controls = new OrbitControls(camera, renderer.domElement);new GLTFLoader().load('../resources/models/donuts.glb', (gltf) => { console.log(gltf); scene.add(gltf.scene); donuts = gltf.scene; // gltf.scene.traverse((child)=>{ // console.log(child.name); // }) mixer = new THREE.AnimationMixer(gltf.scene); const clips = gltf.animations; // 播放所有动画 clips.forEach(function (clip) { const action = mixer.clipAction(clip); action.loop = THREE.LoopOnce; // 停在最初一帧 action.clampWhenFinished = true; action.play(); });})new RGBELoader() .load('../resources/sky.hdr', function (texture) { scene.background = texture; texture.mapping = THREE.EquirectangularReflectionMapping; scene.environment = texture; renderer.outputEncoding = THREE.sRGBEncoding; renderer.render(scene, camera);});![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d7938bd3127f467f9c3e50fe70cc0f01~tplv-k3u1fbpfcp-watermark.image?)甜甜圈还在不安分的掉落着,那就洗脑循环吧,不爽也打不着我 ...

January 14, 2023 · 1 min · jiezi

关于three.js:八叉树空间-初学

学了 three 元宇宙开发后发现人物只能在立体上行走,于是我人物上楼梯 跳跃就特地的好奇……对于常识的好奇不下于我爱喝啤酒,于是我全世界的去找材料和在技术群里问同学,终于让我晓得three 有一个货色叫 八叉树 接下来记录一下我用 八叉树 的一些小心得…… 必须的引入的库import { Octree } from 'three/examples/jsm/math/Octree.js';import { Capsule } from 'three/examples/jsm/math/Capsule.js';这两个库必须引入,在 八叉树空间 的所有撞检和重力都靠这两个库实现 申明 Capsule 和 Octreeconst worldOctree = new Octree();const playerCollider = new Capsule( new Vector3(0,0.35,0), new Vector3(0,1,0), 0.35 );场景模型引入增加入八叉树空间new GLTFLoader().setPath("./model/test/").load("collision-world.glb", (gltf) => { ... worldOctree.fromGraphNode( gltf.scene ); // 把场景模型退出到八叉树空间 ...});整体的来说就是 整个八叉树空间 就是 Capsule 和 Octree 运算 全副代码<!-- --><template> <div id="three" ref="three"> <div ref="le" @mousedown="ddmousedown"></div> </div></template><script setup lang="ts">import { ref, reactive, onMounted, onUnmounted } from "vue";import * as dat from "dat.gui";import { ACESFilmicToneMapping, AmbientLight, AnimationAction, AnimationMixer, AnimationUtils, AxesHelper, BackSide, CameraHelper, Clock, Color, DirectionalLight, DirectionalLightHelper, Fog, Group, HemisphereLight, IcosahedronGeometry, Mesh, MeshBasicMaterial, MeshLambertMaterial, PerspectiveCamera, PlaneGeometry, PointLight, Raycaster, Scene, Sphere, SpotLight, sRGBEncoding, TextureLoader, Vector3, VideoTexture, VSMShadowMap, WebGLRenderer,} from "three";import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";import Stats from 'three/examples/jsm/libs/stats.module.js';import { Octree } from 'three/examples/jsm/math/Octree.js';import { OctreeHelper } from 'three/examples/jsm/helpers/OctreeHelper.js';import { Capsule } from 'three/examples/jsm/math/Capsule.js';/** * 配置 */// MARK: 配置=======const le = ref();const three = ref();const donuts = ref();const scene = new Scene(); // 场景对象Sceneconst axes = new AxesHelper(50); // 轴长度const th = reactive({ ctrl: new dat.GUI({ width: 200 }), renderer: new WebGLRenderer({ antialias: true, }), // 渲染器对象 ambienLight: new AmbientLight(0xffffff, 0.1), // 自然光 planeGeometry: new PlaneGeometry(100, 100), // 高空 spotLight: new SpotLight(0xffffff), // 聚光灯 pointlight: new PointLight(0xffffff, 6, 60), // 点光源 directionalLight: new DirectionalLight(0xffffff, 0.2), // 平行光源 hemisphereLight: new HemisphereLight(0xffffff, 0x00ff00, 1), // 半球光光源});scene.background = new Color(0x88ccee);scene.fog = new Fog( 0x88ccee, 0, 50 );// scene.add(axes); // 增加轴const clock =ref<any>(new Clock());const stats = ref<any>(Stats());let helper:any = null;const GRAVITY = ref<number>(30);const NUM_SPHERES = ref<number>(100);const SPHERE_RADIUS = ref<number>(0.2);const STEPS_PER_FRAME = ref<number>(5);const worldOctree = new Octree();const playerCollider = new Capsule( new Vector3(0,0.35,0), new Vector3(0,1,0), 0.35 );const playerVelocity = new Vector3();const playerDirection = new Vector3();let playerOnFloor = false;let mouseTime = 0;const keyStates:any = {};/** * 影相机 */// MARK: 影相机=======let camera = new PerspectiveCamera(75,window.innerWidth / window.innerHeight,0.1,1000); //透视相机camera.rotation.order = 'YXZ';/** * 灯光 */// MARK: 灯光=======scene.add(th.ambienLight); // 自然光th.directionalLight.position.set( - 5, 25, - 1 );th.directionalLight.castShadow = true;th.directionalLight.shadow.camera.near = 0.01;th.directionalLight.shadow.camera.far = 500;th.directionalLight.shadow.camera.right = 30;th.directionalLight.shadow.camera.left = - 30;th.directionalLight.shadow.camera.top = 30;th.directionalLight.shadow.camera.bottom = - 30;th.directionalLight.shadow.mapSize.width = 1024;th.directionalLight.shadow.mapSize.height = 1024;th.directionalLight.shadow.radius = 4;th.directionalLight.shadow.bias = - 0.00006;scene.add( th.directionalLight );// const helper = new DirectionalLightHelper(th.directionalLight, 5);// scene.add(helper);const dirCameraHelper = new CameraHelper(th.directionalLight.shadow.camera);// scene.add(dirCameraHelper);const hemisphereLight = new HemisphereLight( 0x4488bb, 0x002244, 0.5 );hemisphereLight.position.set( 2, 1, 1 );scene.add( hemisphereLight );/** * 渲染 场景 */// MARK: 渲染 场景=======th.renderer.shadowMap.enabled = true;th.renderer.setPixelRatio( window.devicePixelRatio );th.renderer.setSize( window.innerWidth, window.innerHeight );th.renderer.shadowMap.enabled = true;th.renderer.shadowMap.type = VSMShadowMap;th.renderer.outputEncoding = sRGBEncoding;th.renderer.toneMapping = ACESFilmicToneMapping;/** * Stats 性能检测 */// MARK: 性能检测=======stats.value.domElement.style.position = 'absolute';stats.value.domElement.style.top = '0px';/** * 高空 */// MARK: 高空=======/** * OrbitControls 轨道控制器 */// MARK: OrbitControls 轨道控制器=======/** * 导入模型 */// MARK: 导入模型=======let playerMesh:any= null;let playerMixer= ref<AnimationMixer>();let actionWalk= ref<AnimationAction>();let actionIdle= ref<AnimationAction>();const lookTarget = new Vector3(0, 2, 0);new GLTFLoader().setPath("./model/test/").load("player2.glb", gltf => { // 角色 playerMesh = gltf.scene; gltf.scene.position.set(0,0,0); gltf.scene.rotateY(Math.PI); gltf.scene.traverse((child)=>{ child.receiveShadow = true; child.castShadow = true; }) camera.position.set(0, 2.5, -5); // camera.lookAt(playMesh.value.position); camera.lookAt(lookTarget); const pointLight: PointLight = new PointLight(0xffffff, 1); pointLight.position.set(0, 2, -1); playerMixer.value = new AnimationMixer(gltf.scene); const clipWalk = AnimationUtils.subclip(gltf.animations[0], 'walk', 0, 30); actionWalk.value = playerMixer.value.clipAction(clipWalk); const clipIdle = AnimationUtils.subclip(gltf.animations[0], 'idle', 31, 281); actionIdle.value = playerMixer.value.clipAction(clipIdle); actionIdle.value.play(); gltf.scene.add(pointLight); gltf.scene.add(camera); scene.add(gltf.scene);});new GLTFLoader().setPath("./model/test/").load("collision-world.glb", (gltf) => { // console.log(gltf); scene.add(gltf.scene); donuts.value = gltf.scene; worldOctree.fromGraphNode( gltf.scene ); gltf.scene.traverse( (child:any) => { if ( child.isMesh ) { child.castShadow = true; child.receiveShadow = true; if ( child.material.map ) { child.material.map.anisotropy = 4; } } } ); helper = new OctreeHelper( worldOctree ); helper.visible = false; scene.add( helper ); animate();});/** * 建盘管制人物 */// MARK: 建盘管制人物=======let isWalk = ref<boolean>(false);const playerHalfHeight = new Vector3(0,0.3, 0);window.addEventListener("keydown", event => { keyStates[event.code] = true; if (event.key == "w") { if (!isWalk.value) { if( actionIdle.value && actionWalk.value){ crossPlay(actionIdle.value,actionWalk.value); } isWalk.value = true; } }});window.addEventListener("keyup", event => { keyStates[ event.code ] = false; if (event.key == "w") { if( actionIdle.value && actionWalk.value){ crossPlay(actionWalk.value,actionIdle.value); } isWalk.value = false; }});let ddmousedown =()=>{ document.body.requestPointerLock(); mouseTime = performance.now();}document.addEventListener( 'mouseup', () => { // if ( document.pointerLockElement !== null ) throwBall();});let preClientX:number;window.addEventListener("mousemove", event => { if(document.pointerLockElement === document.body){ playerMesh.rotation.y -= -(event.movementX / 300); // playerMesh.rotation.x -= -(event.movementY / 2000); }});let playerCollisions = ()=> { const result = worldOctree.capsuleIntersect( playerCollider ); playerOnFloor = false; if ( result ) { playerOnFloor = result.normal.y > 0; if ( ! playerOnFloor ) { playerVelocity.addScaledVector( result.normal, - result.normal.dot( playerVelocity ) ); } playerCollider.translate( result.normal.multiplyScalar( result.depth ) ); }}let updatePlayer = ( deltaTime:number )=> { let damping = Math.exp(-4*deltaTime)-1; if ( ! playerOnFloor ) { playerVelocity.y-=GRAVITY.value*deltaTime; // small air resistance damping *= 0.1; } playerVelocity.addScaledVector( playerVelocity, damping ); const deltaPosition = playerVelocity.clone().multiplyScalar( deltaTime ); playerCollider.translate( deltaPosition ); playerCollisions(); if(playerMesh) playerMesh.position.copy( playerCollider.end );}let getForwardVector = ()=>{ playerMesh.getWorldDirection( playerDirection ); playerDirection.y = 0; playerDirection.normalize(); return playerDirection;}let getSideVector = ()=>{ playerMesh.getWorldDirection( playerDirection ); playerDirection.y = 0; playerDirection.normalize(); playerDirection.cross( playerMesh.up ); return playerDirection;}let controls=( deltaTime:number )=>{ // gives a bit of air control const speedDelta = deltaTime * ( playerOnFloor ? 25 : 8 ); if ( keyStates[ 'KeyW' ] ) { playerVelocity.add( getForwardVector().multiplyScalar(speedDelta*0.6)); } if ( keyStates[ 'KeyS' ] ) { // playerVelocity.add( getForwardVector().multiplyScalar(-speedDelta)); } if ( keyStates[ 'KeyA' ] ) { // playerVelocity.add( getSideVector().multiplyScalar(-speedDelta)); } if ( keyStates[ 'KeyD' ] ) { // playerVelocity.add( getSideVector().multiplyScalar(speedDelta)); } if ( playerOnFloor ) { if ( keyStates[ 'Space' ] ) { playerVelocity.y = 15; } }}let teleportPlayerIfOob=()=>{ if(!playerMesh) return; if ( playerMesh.position.y <= - 25 ) { playerCollider.start.set( 0, 0.35, 0 ); playerCollider.end.set( 0, 1, 0 ); playerCollider.radius = 0.35; playerMesh.position.copy( playerCollider.end ); playerMesh.rotation.set( 0, 0, 0 ); }}let crossPlay = (curAction:AnimationAction, newAction:AnimationAction)=>{ curAction.fadeOut(0.3); newAction.reset(); newAction.setEffectiveWeight(1); newAction.play(); newAction.fadeIn(0.3);}let render = () => { th.renderer.render(scene, camera);};//============================================场景搭建end==================================onMounted(() => { le.value.appendChild(th.renderer.domElement); le.value.appendChild(stats.value.domElement); window.addEventListener( "resize", () => { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); th.renderer.setSize(window.innerWidth, window.innerHeight); render(); }, false );});//============================================构建场景 start==================================// MARK: 构建场景=======//============================================构建场景 end==================================//============================================gui start==================================/** * dat.gui 插件 */// MARK: dat.gui 插件=======let ctrlObj2 = {};let basicfolder = th.ctrl.addFolder("MeshBasicMaterial");basicfolder.add( { debug: false }, 'debug' ).onChange(value=>{ helper.visible = value;});basicfolder.open();//============================================gui end==================================let animate=()=> { const deltaTime = Math.min(0.05, clock.value.getDelta())/STEPS_PER_FRAME.value; //咱们在子步骤中寻找碰撞,以升高危险 //一个物体疾速穿过另一个物体而无奈检测。 for ( let i = 0; i < STEPS_PER_FRAME.value; i ++ ) { controls( deltaTime ); updatePlayer( deltaTime ); teleportPlayerIfOob(); } th.renderer.render( scene, camera ); stats.value.update(); if (playerMixer.value) { playerMixer.value.update(0.045); } requestAnimationFrame( animate );}onUnmounted(() => { th.ctrl.destroy(); // 销毁dat.GUI});</script><style scoped lang="less">@import "~@/common/less/index.less";</style>运行成果如下 ...

January 14, 2023 · 5 min · jiezi

关于three.js:threejs碰撞检测前进后退上下楼梯一口气搞定

原文参考我的公众号文章 # threejs碰撞检测-后退后退,高低楼梯一口气搞定! 物体挪动 - 前后碰撞检测次要还是依附 Raycaster(origin, direction) 射线检测。在物体前后挪动时,实时获取物体的地位origin=target.position.clone()作为「射线发射点」,并通过player.getWorldDirection(dir)实时获取物体的方向作为射线「发射方向」,当物体挪动的 forward>0,示意后退,否则示意后退。如果是后退,则dir.negate();方向反转。效果图GIF 图只有 4fps,20 品质,所以很卡。否则图片太大了 次要代码,正文具体阐明了过程/** * JoyStick控制器-角色碰撞检测【前|后】 * @param {Object3D} target 须要碰撞检测的挪动物体 * @param {THREE.Raycaster} raycaster 挪动物体的碰撞检测射线 * @param {Number} intersectDistance 检测间隔,小于此间隔示意碰撞了 * @param {Boolean} recursive 是否递归检测场景中的物体 * @returns Boolean */function checkCollide( target, raycaster, intersectDistance = 2, recursive = true) { // 是否在【后退】 let isForward = this.js.forward >= 0; // 获取target以后地位,Y轴加一个固定量,代表纵轴射线发射(检测碰撞的)地位 let origin = target.position.clone().add(new THREE.Vector3(0, 1, 0)); // 获取target以后朝向 let direction = new THREE.Vector3(); //定义一个方向向量 this.player.getWorldDirection(direction); direction.normalize(); this.playerLight.position.x = this.player.position.x; this.playerLight.position.y = this.player.position.y + 2; this.playerLight.position.z = this.player.position.z; // 如果在【后退】,检测方向direction取反 if (!isForward) { direction.negate(); // console.log("move fallback:", direction); } else { // console.log("move forward:", direction); } // 设置射线发射地位 raycaster.ray.origin.copy(origin); // 设置射线发射方向 raycaster.ray.direction.copy(direction); // 开始【前、后】检测:对于blender制作的模型,须要递归遍历所有child,否则无奈实现射线碰撞检测{[childs], true} let ins = raycaster.intersectObjects(this.objects, recursive); if (ins.length) { let { distance, object } = ins[0]; if (object.name == "stair") { return false; } if (distance < intersectDistance) { console.warn("碰撞了:", object); // 主动回退1米,给接下来小角度后退留余地,否则会继续触发碰撞条件而无奈挪动 if (isForward) { console.log("后退过程碰撞了,主动后退1米"); this.player.translateZ(-1); } else { console.log("后退过程碰撞了,主动后退1米"); this.player.translateZ(+1); } return true; } } return false;}一些优化产生碰撞时,依据此前挪动方向,向相同的方向回退 1m(this.player.translateZ(±1);),这样能够避免转弯时继续碰撞物体TODO 还未解决在楼梯下后退后退对楼梯的碰撞检测物体挪动 - 高低楼梯次要还是依附 THREE.Raycaster(origin, dir) 射线检测。从挪动物体的顶部的中央收回射线,方向朝下,检测楼梯,进行间隔差值判断。须要留神的点在于要躲避人「走在楼梯下」的场景,不要误判为「上楼」。效果图GIF 图只有 4fps,20 品质,所以很卡。否则图片太大了 ...

January 13, 2023 · 2 min · jiezi

关于three.js:threejs入门一些基础理论|大帅老猿threejs特训

前言加入了胖达老师的threejs直播课。有须要的视频私信取。本篇文章为入门实践局部。 能够学到什么:一、软能力1. 零碎全流程了解web3D 利用/数字孪生/元宇宙,程序开发与3D美术资源制作2. 建设与3D美术团队良好沟通合作能力3. 良好把控3D画面成果和性能均衡4. 造就程序和美术联合思维,最简省的形式实现最好的成果5. 相熟罕用3D软件,获得web3D程序员根本应用能力6. 独立开发Web3D利用(程序、2D/3D美术)7. Three.js和3D美术实战经验二、硬件能力1. Three.js根底API(本身想法通过Three.js失去简略实现)2. 3D美术工具的应用3. 甜甜圈3D模型及调整4. Three.js掉落的甜甜圈案例5. Blender 软件根底6. 全流程演示从展厅图片建设展厅3D模型7. PBR材质介绍8. 贴图解决,材质设置9. 实现残缺展厅模型10. 加载展厅模型11. 角色模型12. 角色骨骼动作13. 角色动作合并14. 角色动作管制15. 环境照明16. 自在展览的展厅三、web3D的次要展现形式1.浏览器间接渲染:电脑浏览器、挪动端浏览器(包含微信)以及微信小程序2.服务端渲染(服务端运行:成果好,然而经营老本高)将3D画面像素推流到前端/小程序展现四、相干概念数字孪生:三维实景模型 + 多系统监控/告警数据,实现近程监管(监控、治理) AR:人进入虚拟世界VR:虚构进入事实世界五、浏览器运行3D的计划ActiveX插件:已过期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根底概念 根底场景三大件- scene 场景 new THREE Scene():- camera 相机 new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );- renderer 渲染器 new THREE.WebGLRenderer();灯光 材质(重点)MeshBasicMaterial 根底材质(测试调试用的多)MeshStandardMaterial PBR材质(写实我的项目标配材质) ...

January 11, 2023 · 1 min · jiezi

关于three.js:threejs-入门用例

这是一个底层基于 WebGL 开发的3D渲染引擎( 当然,后续是否会正式公布基于 WebGPU 的版本,也是可能的)。 和间接应用WebGL相比,比方 着色器 ,大部分状况下你无需本人开发,不过,状况并不总是这样,如果你的需要太过非凡,咱们仍旧能够用更靠近原生的形式来绘制,这是一个十分敌对的设计。 绘制流程个别最通用的绘制流程大抵如下: 你能够提前看看咱们最终要绘制的成果(一个旋转的立方体): 点击在线例子能够查看代码。 场景所谓的场景,也就是空间的属性,就是以后空间外面有什么货色;比方:有什么物体(物体的材质、形态、尺寸)、有没有光(点光源还是环境光、或者平行光)等。 对咱们这里而言,很显著,空间里有一个正六面体,而且,如同还有光照在下面。 那么,咱们首先创立好场景,后续再补充场景中的内容: var scene = new THREE.Scene();网格模型当初,咱们来创立一个立方体: var geometry = new THREE.BoxGeometry(100, 100, 100);立方体是红色的,所以,还须要创立一个材质对象: var material = new THREE.MeshLambertMaterial({ color: "red"});而后,把立方体和材质对象关联起来,就取得了示意这个残缺立方体信息的模型对象了: var mesh = new THREE.Mesh(geometry, material);最初,把这个立方体放到之前创立的空间中去: scene.add(mesh);光照光个别有多种,比方环境光,其实咱们就能够认为是漫反射,在原生代码中,咱们须要本人设计光叠加的算法,而在这里,你只须要调用api设置参数即可: var ambient = new THREE.AmbientLight("green");同样的,也须要被增加到以后空间中去: scene.add(ambient);别的类型的光也相似,只是设置的参数不一样,这里就不再赘述了。 相机其实就相当于你的眼睛的可视区域。空间中有什么,不代表你就应该看见什么,通过相机来确定你看的地位、方向、范畴等。 还有一点须要特地阐明,因为你看见的其实是立体,空间自身是3D的,那就存在一个投影算法,不同的投影算法最终你空间的内容也是不一样的(咱们这里抉择的是正射投影),整体来说比拟好了解,间接看代码: var width = window.innerWidth; //窗口宽度var height = window.innerHeight; //窗口高度var k = width / height; //窗口宽高比var s = 100; //三维场景显示范畴管制系数,系数越大,显示的范畴越大var camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 1000);camera.position.set(200, 300, 200); //设置相机地位camera.lookAt(scene.position); //设置相机方向(指向的场景对象)渲染器好了,空间筹备好了,相机也筹备好了,接下来就是渲染进去了。 ...

January 11, 2023 · 1 min · jiezi

关于three.js:THREEJS-相机控制旋转

自行实现相机管制中的旋转性能. 相机管制旋转对于相机旋转比拟好的是应用球坐标系,绝对于直角坐标系而言,球坐标系用来形容旋转更加适合直观且对于方位角、极角而言属于线性扭转。对于线性函数咱们是十分喜爱的,因为线性代表简略,能用越简略的函数来形容关系的就越适宜。 再想一下如果应用笛卡尔坐标系来解决角度问题,那么肯定是个三角函数,三角函数复杂度大于线性函数,这是咱们不想看到的。 下图所示是球坐标系示意图: 代码解释// 获取二维坐标 将本来在左上角的原点挪动到屏幕核心,当初取值范畴是[-1,1]const offsetX = ((e.offsetX - mouseDownX) / dom.clientWidth) * 2;const offsetY = ((e.offsetY - mouseDownY) / dom.clientHeight) * 2;const matrix3 = new THREE.Matrix3();mouseDownX = e.offsetX;mouseDownY = e.offsetY;// 记住本来相机的地位,咱们只挪动视角不挪动地位cameraPosition.copy(camera.position);let rotation = new THREE.Vector3(0, 0, -1);// 获得相机的世界坐标的归一化坐标rotation.applyNormalMatrix(matrix3.setFromMatrix4(camera.matrixWorld));let spherical = new THREE.Spherical();// 从归一化坐标设置球,能够说是将相机坐标映射到了半径为1的球坐标系上// setFromVector3用于设置theta和phi, 等于是将笛卡尔坐标系换算成球坐标系spherical.setFromVector3(rotation);// 上一步将笛卡尔坐标系换算成球坐标系后,theta和phi都是线性扭转的了,对theta和phi相加减即可spherical.theta -= offsetX;spherical.phi += offsetY;spherical.makeSafe();rotation.setFromSpherical(spherical);// 相机挪动到原点 因为球坐标系在原点camera.position.set(0, 0, 0);camera.lookAt(rotation.x, rotation.y, rotation.z);// 视角扭转后挪动到原地位,深藏功与名camera.position.copy(cameraPosition);camera.updateMatrixWorld();

January 9, 2023 · 1 min · jiezi

关于three.js:THREEJS-将构件缩放至视野中的方法

概述只将某个物体缩放至整个屏幕, 非常常见的利用场景,这和相机视线无关,须要分透视相机和正交相机来别离阐明。 透视相机首先假设物体被相机视线笼罩,有如下图 首先必须晓得物体所在的突围球半径 r, 其次相机角度是已知的,就是 camera.fov, fov 是角度制数据,简略依据三角函数能够得出球心到相机的间隔 s。 s = r/sin(fov)应用代码来表白就是 const dir = new THREE.Vector3(0, 1, 0); // 方向const dist = Math.abs(sphere.radius / Math.sin(((fov / 360) * Math.PI) / 2));const temp = new THREE.Vector3();temp.addVectors(sphere.center, dir.multiplyScalar(dist));正交相机正交相机和剖切很类似,除了 position、rotation 外,应用 left、top、right、bottom 来规定视线范畴 这个更加容易了解,对于屏幕都是矩形的电脑来说,这个缩放将会恰达益处(精确度高,透视相机因为用到了突围球其实不是很准确)。 const left = -(boundingBox.max.x - boundingBox.min.x) / 2;const right = (boundingBox.max.x - boundingBox.min.x) / 2;const top = (boundingBox.max.z - boundingBox.min.z) / 2;const bottom = -(boundingBox.max.z - boundingBox.min.z) / 2;const camera = new THREE.OrthographicCamera(left, right, top, bottom, 1, 1000);camera.lookAt(new THREE.Vector3(0, -100, 0));用处: 制作小地图 ...

January 9, 2023 · 1 min · jiezi

关于three.js:使用-threejs-加载基本三维模型-大帅老猿threejs特训

如果你是初学者,那么学习三维动画可能是一项比拟艰难的工作。然而,应用 three.js 这个弱小的 JavaScript 库,能够让你轻松地制作出精美的三维动画。 首先,咱们须要导入 three.js 库,并创立场景、相机和渲染器。场景是三维空间中所有物体的容器,而相机则决定了咱们所看到的视角。渲染器则负责将场景出现在屏幕上。 接下来,咱们能够应用 OrbitControls 插件来管制相机的地位和视角,并应用 GLTFLoader 插件来加载三维模型文件。这里咱们应用的是 donuts.glb 文件,它蕴含了一个甜甜圈的三维模型。 为了让咱们的场景更加真切,咱们还能够应用 RGBELoader 插件来加载环境贴图,并将它设置为场景的背景。这样,咱们就能够在咱们的三维模型中感触到周围环境的光照成果了。 最初,咱们还能够在动画函数中增加代码来主动旋转模型,并播放它的动画。这样,咱们就能够轻松地制作出精彩的三维动画了。 总的来说,应用 three.js 制作三维动画十分不便,它提供了丰盛的插件和 API,让咱们可能疾速上手。如果你也想学习 three.js,无妨退出猿创营(v:dashuailaoyuan),一起交流学习。 import * as THREE from 'three';import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader';// 三D三大件// 场景const scene = new THREE.Scene();// 相机const camera = new THREE.PerspectiveCamera(75, window.innerWidth/innerHeight, 0.1, 10)// 渲染器const renderer = new THREE.WebGLRenderer({antialias: true});renderer.setSize(window.innerWidth, window.innerHeight);document.body.appendChild(renderer.domElement);// 设置相机地位camera.position.set(0.3,0.3,0.5);// 增加 控制器,能够拖拽,缩放const controls = new OrbitControls(camera, renderer.domElement);// 增加灯光,DirectionLight方向光const directionLight = new THREE.DirectionalLight(0xffffff, 0.4);// scene.add(directionLight);let donuts;let mixer;// 加载 glb 模型new GLTFLoader().load('../resources/models/donuts.glb', (gltf) => { // 模型增加到场景 scene.add(gltf.scene); donuts = gltf.scene; // mixer = new THREE.AnimationMixer(gltf.scene); const clips = gltf.animations; clips.forEach((clip) => { const action = mixer.clipAction(clip); action.loop = THREE.LoopOnce; action.clampWhenFinished = true; action.play(); })})// 加载环境贴图 360全景图new RGBELoader() .load('../resources/sky.hdr', (texture) => { // 设置为场景背景 scene.background = texture; texture.mapping = THREE.EquirectangularReflectionMapping; // 设置环境光 scene.environment = texture; renderer.outputEncoding = THREE.sRGBEncoding; renderer.render(scene, camera); });function animate() { requestAnimationFrame(animate); // 渲染场景 renderer.render(scene, camera); // 更新控制器 controls.update(); // 主动旋转 if (donuts) { donuts.rotation.y += 0.001; } // 播放动画 if (mixer) { mixer.update(0.003); }}animate();

December 31, 2022 · 1 min · jiezi

关于three.js:绝配周杰伦bgm阿瑟爱心代码-酷炫播放器

前言最近电视剧《点燃我,和煦你》中的一个桥段,李峋写的爱心代码火了 ❤️❤️❤️,相似的表白神器每年都会在特定的工夫(这些日期你都晓得吗)出圈几个。 当各路大佬开始晒各种语言各种类型的爱心代码,我一个顶级CV工程师,在这塌实的环境下天然也想着夸耀一番,于是在这片汪洋大海中通过我的层层筛选,这款爱心代码被我挑中并对他进行了小小的革新。 插件地址 对于插件: 这款插件的作者从代码来看曾经无从讲究了,感激大佬栽树。 作为前端实现爱心代码的酷炫成果,尤其是3d成果应用的技术栈根本都是以three.js+canvas为主 这款插件也是应用three.js + canvas 开发,具体的开发过程不做过多的剖析,cv工程师不太会three.js,见笑了 重点认识一下Web Audio API: Web Audio API 为管制 Web 上的音频提供了一个弱小且通用的零碎,容许开发人员抉择音频源、为音频增加成果、创立音频可视化、利用空间成果(如平移)等等。 声音远近,大小,触动等等这些成果的可视化都须要Web Audio API的帮忙 接口:这里简略列举了几个插件中用到的接口,更多接口可返回MDN查看 AudioListenerAudioListener 用一个虚构的listener示意在场景中所有的地位和非地位相干的音效. 一个three.js程序通常创立一个AudioListener. 它是音频实体构造函数的必须参数,比方 Audio and PositionalAudio. 大多数状况下, listener对象是camera的子对象. Camera的3D变换示意了listener的3D变换. AudioLoader用来加载 AudioBuffer的一个类。 外部默认应用FileLoader> > 来加载文件。(我的项目中用来加载音频文件) AudioAnalyser创立AudioAnalyser对象, 应用AnalyserNode 去剖析音频数据. 开始 插件动画成果曾经完满了,合乎装要求。 去掉边框更显大气; bgm像铃声换成我周董的更能俘获人心; 周董咬字不清加个歌词显示更加人性化(开玩笑的啦,喜爱的就是这个味)。 动画追随旋律的成果插件曾经有了,配上歌词那不就是一个现成的播放器吗 思路一: 实现辨认音频显示歌词,相似视频播放显示字幕的成果。 一通百度失去的后果次要分两种 上传语音到服务端,服务端利用解析语音的API,返回文字;利用原生api或各种库调取浏览器麦克风,辨认语音转文字办法一在我这个捣鼓着玩的我的项目里显著不实用,办法二延时太久,辨认精确度太低 思路二:√ 获取歌词,实现歌词与音频的同步,这种计划是可行的 实现获取歌词网上找到LRC格局的歌词,肯定要是LRC格局,放到一个文本域中,设置为暗藏 <textarea id="lrc_content" cols="30" style="display: none"> [ti:花海] [ar:周杰伦] [00:03.63]花海 [00:06.35]周杰伦 [00:14.49]... [00:18.56]... [00:26.90]静止了 所有的花开 [00:33.31]边远了 清晰了爱 [00:39.53]情侣们 爱却更喜爱 [00:45.99]那时候 我不懂 这场爱 [00:51.55] [00:52.50]你喜爱 站在那窗台 [00:58.47]你良久 都没再来 [01:05.12]黑白的 世界染上空白 [01:12.18]是你流的泪晕开 [01:16.14] [01:17.26]不要你来到 间隔隔不开 [01:23.65]怀念变成海 在窗外进不来 [01:29.82]原谅说太快 爱成了妨碍 [01:36.24]手中的风筝放太快回不来 [01:42.22] [01:42.74]不要你来到 泪已化不开 [01:48.96]经验的妨碍 我在期待重来 [01:55.51]天空仍璀璨 她爱着大海 [02:01.79]情歌被战胜 爱已不存在 [02:09.83] [02:34.80]你喜爱 站在那窗台 [02:41.14]你良久 都没再来 [02:47.40]黑白的 世界染上空白 [02:54.66]是你流的泪晕开 [02:59.87] [03:00.24]不要你来到 间隔隔不开 [03:05.75]怀念变成海 在窗外进不来 [03:12.16]原谅说太快 爱成了妨碍 [03:18.56]手中的风筝放太快回不来 [03:24.53] [03:25.07]不要你来到 泪已化不开 [03:31.37]经验的妨碍 我在期待重来 [03:37.81]天空仍璀璨 她爱着大海 [03:44.12]情歌被战胜 爱已不存在 [03:54.85]</textarea>js读取歌词 ...

December 15, 2022 · 2 min · jiezi

关于three.js:使用Threejs实现炫酷的赛博朋克风格3D数字地球大屏

背景近期工作有波及到数字大屏的需要,于是利用业余时间,联合 Three.js 和 CSS实现赛博朋克2077格调视觉效果 实现炫酷 3D 数字地球大屏页面。页面应用 React + Three.js + Echarts + stylus 技术栈,本文波及到的次要知识点包含:THREE.Spherical 球体坐标系的利用、Shader 联合 TWEEN 实现飞线和冲击波动画成果、dat.GUI 调试工具库的应用、clip-path 创立不规则图形、Echarts 的根本应用办法、radial-gradient 创立雷达图形及动画、GlitchPass 增加故障格调前期、Raycaster 网格点击事件等。 成果 如下图 所示,页面次要头部、两侧卡片、底部仪表盘以及主体 3D 地球 形成,地球外围有 飞线 动画和 冲击波 动画成果 ,通过 鼠标能够旋转和放大地球。点击第一张卡片的 START ⬜ 按钮会给页面增加故障格调前期 ⚡,双击地球会弹出随机提醒语弹窗。 实现 资源引入引入开发必备的资源,其中除了根底的 React 和样式表之外,dat.gui 用于动态控制页面参数,其余残余的次要分为两局部:Three.js相干, OrbitControls 用于镜头轨道控制、TWEEN 用于补间动画管制、mergeBufferGeometries 用户合并模型、EffectComposer RenderPass GlitchPass 用于生成前期故障成果动画、 lineFragmentShader 是飞线的 Shader、Echarts相干按需引入须要的组件,最初应用 echarts.use 使其失效。 import './index.styl';import React from 'react';import * as dat from 'dat.gui';// three.js 相干import * as THREE from 'three';import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';import { TWEEN } from 'three/examples/jsm/libs/tween.module.min.js';import { mergeBufferGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils';import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';import { GlitchPass } from 'three/examples/jsm/postprocessing/GlitchPass.js';import lineFragmentShader from '@/containers/EarthDigital/shaders/line/fragment.glsl';// echarts 相干import * as echarts from 'echarts/core';import { BarChart /*...*/ } from 'echarts/charts';import { GridComponent /*...*/ } from 'echarts/components';import { LabelLayout /*...*/ } from 'echarts/features';import { CanvasRenderer } from 'echarts/renderers';echarts.use([BarChart, GridComponent, /* ...*/ ]); 页面构造页面次要构造如以下代码所示,.webgl 用于渲染 3D 数字地球;.header 是页面顶部,外面包含工夫、日期、星际坐标、Cyberpunk 2077 Logo、自己 Github 仓库地址等;.aside 是左右两侧的图表展现区域;.footer 是底部的仪表盘,展现一些雷达动画和文本信息;如果仔细观察,能够看出背景有噪点成果,.bg 就是用于生成噪点背景成果。 ...

July 31, 2022 · 6 min · jiezi

关于three.js:关于Threejs的基本使用和概念场景相机渲染器等

这是一个底层基于WebGL开发的3D渲染引擎( 当然,后续是否会正式公布基于WebGPU的版本,也是可能的)。 和间接应用WebGL相比,比方着色器,大部分状况下你无需本人开发,不过,状况并不总是这样,如果你的需要太过非凡,咱们仍旧能够用更靠近原生的形式来绘制,这是一个十分敌对的设计。 绘制流程个别最通用的绘制流程大抵如下: 筹备好场景→筹备好相机→应用渲染器渲染进去 你能够提前看看咱们最终要绘制的成果: 这是一个旋转的立方体。 你能够点击此处进行查看运行成果。 场景所谓的场景,也就是空间的属性,就是以后空间外面有什么货色;比方:有什么物体(物体的材质、形态、尺寸)、有没有光(点光源还是环境光、或者平行光)等。 对咱们这里而言,很显著,空间里有一个正六面体,而且,如同还有光照在下面。 那么,咱们首先创立好场景,后续再补充场景中的内容: var scene = new THREE.Scene();网格模型当初,咱们来创立一个立方体: var geometry = new THREE.BoxGeometry(100, 100, 100);立方体是红色的,所以,还须要创立一个材质对象: var material = new THREE.MeshLambertMaterial({ color: "red"});而后,把立方体和材质对象关联起来,就取得了示意这个残缺立方体信息的模型对象了: var mesh = new THREE.Mesh(geometry, material);最初,把这个立方体放到之前创立的空间中去: scene.add(mesh);光照光个别有多种,比方环境光,其实咱们就能够认为是漫反射,在原生代码中,咱们须要本人设计光叠加的算法,而在这里,你只须要调用api设置参数即可: var ambient = new THREE.AmbientLight("green");同样的,也须要被增加到以后空间中去: scene.add(ambient);别的类型的光也相似,只是设置的参数不一样,这里就不再赘述了。 相机其实就相当于你的眼睛的可视区域。空间中有什么,不代表你就应该看见什么,通过相机来确定你看的地位、方向、范畴等。 还有一点须要特地阐明,因为你看见的其实是立体,空间自身是3D的,那就存在一个投影算法,不同的投影算法最终你空间的内容也是不一样的(咱们这里抉择的是正射投影),整体来说比拟好了解,间接看代码: var width = window.innerWidth; //窗口宽度var height = window.innerHeight; //窗口高度var k = width / height; //窗口宽高比var s = 100; //三维场景显示范畴管制系数,系数越大,显示的范畴越大var camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 1000);camera.position.set(200, 300, 200); //设置相机地位camera.lookAt(scene.position); //设置相机方向(指向的场景对象)渲染器好了,空间筹备好了,相机也筹备好了,接下来就是渲染进去了。 ...

July 25, 2022 · 2 min · jiezi

关于three.js:用threejs写一个下雨动画

最近看了《Three.js开发指南》,粗浅地意识到光看不练跟没看差不多,所以就练习写了这个小动画。 我的项目地址: https://github.com/alasolala/... 前置常识WebGL让咱们能在浏览器开发3D利用,然而间接应用WebGL编程还是挺简单的,开发者须要晓得WebGL的底层细节,并且学习简单的着色语言来取得WebGL的大部分性能。Three.js提供了一系列很简略的对于WebGL个性的JavaScript API,使开发者能够很不便地创作出难看的3D图形。在Three.js官网,就有很多酷炫3D成果。 应用Three.js开发3D利用,通常要包含渲染器(Renderer)、场景(Scene)、照相机(Camera),以及你在场景中创立的物体,光照。 构想一下照相的状况,咱们须要一个场景(Scene),在这个场景中摆好要拍摄的物体,设置光照环境,摆放好照相机(Camera)的地位和朝向,而后就能够拍照了。渲染器(Renderer)可能和摄影师比拟像吧,负责下命令拍摄,并且生成图像(照片)。 将上面的代码的复制并运行,就能够失去一个很简略的3D场景。 <!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>room</title></head><body> <div id="webgl-output"></div> <script src="https://unpkg.com/three@0.119.0/build/three.js"></script> <script> function init () { const scene = new THREE.Scene() const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000 ) camera.position.set(-30, 40, 30) camera.lookAt(0,0,0) scene.add(camera) const planeGeometry = new THREE.PlaneGeometry(60,20) const planeMaterial = new THREE.MeshLambertMaterial({ color: 0xAAAAAA }) const plane = new THREE.Mesh(planeGeometry, planeMaterial) plane.rotation.x = -Math.PI / 2 plane.position.set(15, 0, 0) scene.add(plane) const sphereGeometry = new THREE.SphereGeometry(4, 20, 20) const sphereMaterial = new THREE.MeshLambertMaterial({ color: 0xffff00 }) const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial) sphere.position.set(20, 4, 2) scene.add(sphere) const spotLight = new THREE.SpotLight(0xffffff) spotLight.position.set(-20, 30, -15) scene.add(spotLight) const renderer = new THREE.WebGLRenderer() renderer.setClearColor(new THREE.Color(0x000000)) renderer.setSize(window.innerWidth, window.innerHeight) document.getElementById('webgl-output').appendChild(renderer.domElement) renderer.render(scene, camera) } init()</script></body></html>场景(Scene)THREE.Scene对象是所有不同对象的容器,但这个对象自身没有很简单的操作,咱们通常在程序最开始的时候实例化一个场景,而后将照相机、物体、光源增加到场景中。 ...

July 22, 2022 · 4 min · jiezi

关于three.js:threeJs相机

此篇将threeJs相机的应用做一下记录,简略的封装为vue组件,以备参考。之后缓缓欠缺此文。 一、立方相机cubeCameracubeCamera,结构一个蕴含6个PerspectiveCameras(透视摄像机)的立方摄像机,并将其拍摄的场景渲染到一个WebGLCubeRenderTarget上,生成指标纹理(WebGLCubeRenderTarget.texture)。render target是一个缓存,缓存显卡为正在后盾渲染的场景绘制的像素。被用于不同的成果,例如用在一个图像渲染到屏幕上之前先做一些前期解决。此处,将WebGLCubeRenderTarget.texture作为某个具备envMap属性材质的模型的环境贴图,即此模型反射的环境图案。 CubeCamera.vue<template> <div> <div id="canvasContainer" style="width: 900px; height: 800px"></div> </div></template><script setup>import { onMounted } from 'vue'import * as THREE from 'three'import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'let scene, camera, renderer, width, height, controlsonMounted(() => { init()})function init() { const canvasContainer = document.querySelector('#canvasContainer') width = canvasContainer.clientWidth height = canvasContainer.clientHeight // 1、创立场景 scene = new THREE.Scene() // 2、创立渲染器 renderer = new THREE.WebGLRenderer() // renderer.setClearColor('#FFFFFF') // 设置渲染器的背景色 renderer.setClearColor('#88b4e1') renderer.setSize(width, height) // 设置渲染区域尺寸 canvasContainer.appendChild(renderer.domElement) // 将渲染器创立的canvas元素增加到容器元素 // 3、创立透视相机,用于拍摄主场景 camera = new THREE.PerspectiveCamera(fov, width / height, 0.1, 1000) camera.position.set(100, 50, 200) // 设置相机地位 camera.lookAt(scene.position) // 设置相机方向(指向的场景对象) // 4、创立立方相机 cubeCamera() // 为具备镜面反射成果的模型A拍摄反射的图形,而后作为A的环境贴图 // 要先创立renderer, 因为cubeCamera.update(renderer, scene) // 5、其余 pointLight() // 点灯光 initControls() // 用鼠标管制透视相机的拍摄角度 axisHelper() // 坐标辅助器 animate() }// 循环动画渲染function animate() { requestAnimationFrame(animate) renderer.render(scene, camera)}// 轨道控制器,使得相机围绕指标进行轨道静止function initControls() { controls = new OrbitControls(camera, renderer.domElement)}// 辅助三维坐标系function axisHelper() { const axis = new THREE.AxesHelper(250) scene.add(axis)}// 创立一个虚构的球形网格Mesh的辅助对象来模仿点光源PointLightfunction pointLight() { const pointLight = new THREE.PointLight(0xffffff, 1, 1000) pointLight.position.set(100, 150, 100) //点光源地位 scene.add(pointLight) // 创立点光源辅助对象模仿点光源 const pointLightHelper = new THREE.PointLightHelper(pointLight, 1) scene.add(pointLightHelper)}let sphere, box// 创立立方相机function cubeCamera() { // 创立cube相机的渲染指标 const cubeRenderTarget = new THREE.WebGLCubeRenderTarget(128, { format: THREE.RGBAFormat, generateMipmaps: true, minFilter: THREE.LinearMipmapLinearFilter }); // 创立cube相机 const cubeCamera = new THREE.CubeCamera(1, 100000, cubeRenderTarget) scene.add(cubeCamera) // 创立具能反射环境的模型 const sphereGeometry = new THREE.SphereGeometry(50) const sphereMaterial = new THREE.MeshPhongMaterial({ color: 0xff0000, envMap: cubeRenderTarget.texture }); sphere = new THREE.Mesh(sphereGeometry, sphereMaterial) sphere.position.set(80, -50, 0) scene.add(sphere) // 创立被反射的模型 const boxGeometry = new THREE.BoxGeometry(20, 20, 20) const boxMaterial = new THREE.MeshPhongMaterial({ color: 0x00ff00 }) box = new THREE.Mesh(boxGeometry, boxMaterial) box.position.set(80, 100, 0) scene.add(box) // 更新渲染指标对象 sphere.visible = false cubeCamera.position.copy(sphere.position) cubeCamera.update(renderer, scene) // 能够渲染场景了 sphere.visible = true}</script>效果图: ...

July 19, 2022 · 2 min · jiezi

关于three.js:利用vue3dloader同时加载多个3D模型

家喻户晓ThreeJS是web端用于加载3D模型的JS库,广泛应用于物联网及局部游戏行业中,其弱小就弱小在能够加载大多数类型的3D模型,如:.dae/.fbx/.gltf(.glb)/.obj/.ply/.stl/.json的模型。 然而在工作中,常常会遇到独立的模型会比拟大,建模师会将一个大型的模型拆分成多个小模型,因而就须要threeJS加载多个模型;另一种状况是须要加载多种不同品种的模型。 我将以上两种状况辨别为以下两个需要: 加载多个3D模型加载多个不同品种的3D模型因为加载多个3D模型,大多数人会想到间接应用for循环对模型进行加载,然而理论利用中,当同时加载.fbx模型大小超过60M左右,浏览器就会变得十分卡顿。由此我将其再次宰割为两种加载形式: 并行加载(即for循环)按程序一一加载面对以上的性能需要,因而我开发了一款可能满足以上需要的vue组件,vue-3d-loader(github.com) 性能反对列表加载单个3D模型同时加载多个3D模型同时加载多个不同类型3D模型设置场景宽高设置材质及纹理交互管制鼠标事件灯光相机地位及旋转增加标注点演示自动播放模型内置动画 加载多个模型 图片材质 灯光 在你的我的项目中装置vue-3d-loader留神:vue2版本应用1.x.x版本,vue3版本应用2.0.0+版本 npm i vue-3d-loader@2.0.0 -S应用办法全局应用,在入口文件中全局装置,代码如下: /* vue2 */import vue3dLoader from "vue-3d-loader";Vue.use(vue3dLoader)/* vue3 */import vue3dLoader from "vue-3d-loader";createApp(App).use(vue3dLoader).mount('#app')非全局应用,在Vue文件中应用如下代码: import { vue3dLoader } from "vue-3d-loader"; // 留神 vue3dLoader 写在 {...} 内在组件中应用标签<vue3dLoader></vue3dLoader> <vue3dLoader :height="200" :showFps="true" :filePath="['/fbx/1.fbx', '/obj/2.obj', '/gltf/3.gltf']" :mtlPath="[null, '/obj/2.mtl', null]" :backgroundColor="0xff00ff"></vue3dLoader>filePath为必填,并反对字符串与数组两种形式传入 反对的事件eventdescriptionmousedown(event, intersects)鼠标按下, intersect:以后相交最近的物体mousemove(event, intersects)鼠标挪动, intersect:以后相交最近的物体mouseup(event, intersects)鼠标放开, intersect:以后相交最近的物体click(event, intersects)点击, intersect:以后相交最近的物体load加载模型事件process(event, fileIndex)加载进度, fileIndex:以后加载模型的索引error(event)谬误事件同一个场景中加载多个模型<!-- 可同时加载多个不同品种的模型 --><template> <vue3dLoader :filePath="filePath" :scale="{ x: 0.4, y: 0.4, z: 0.4 }" :cameraPosition="{ x: 100, y: 200, z: 30 }" ></vue3dLoader></template><script>export default { data() { return { filePath: [ "/models/fbx/Samba Dancing.fbx", "models/collada/pump/pump.dae", ], }; },};</script>材质及纹理加载<!-- obj加载mtl材质 --><vue3dLoader filePath="/obj/1.obj" mtlPath="/obj/1.mtl" ></vue3dLoader><!-- fbx图片纹理加载 --><vue3dLoader filePath="/fbx/1.fbx" textureImage="/fbx/1.png" ></vue3dLoader>更多演示3D演示(因为网络问题可能速度会比较慢) ...

July 15, 2022 · 1 min · jiezi

关于three.js:ThreeJS学习记录三监听鼠标点击和移动

监听鼠标挪动,几何体变大变小监听鼠标点击,弹出信息 应用办法:Raycaster import * as THREE from 'three'import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'import Stats from 'three/examples/jsm/libs/stats.module'let container, statslet camera, scene, raycaster, rendererlet INTERSECTEDconst pointer = new THREE.Vector2()let clickPosX = 0, clickPosY = 0function onPointerMove(event) { pointer.x = (event.clientX / window.innerWidth) * 2 - 1 pointer.y = -(event.clientY / window.innerHeight) * 2 + 1 if (INTERSECTED) { INTERSECTED.scale.set(3, 3, 3) } render()}function onPointerClick(event) { pointer.x = (event.clientX / window.innerWidth) * 2 - 1 pointer.y = -(event.clientY / window.innerHeight) * 2 + 1 if (INTERSECTED) { alert('点击了' + INTERSECTED.name) } render()}function calcMousePosition(event, x, y) { // 决定信息弹窗的定位 var event = event || window.event if (event.pageX || event.pageY) { x = event.pageX y = event.pageY } else if (event.clientX || event.clientY) { x = event.clientX + document.documentElement.scrollLeft + document.body.scrollLeft y = event.clientY + document.documentElement.scrollTop + document.body.scrollTop } return { x: x, y: y }}function createDom() { const geometry = new THREE.BoxBufferGeometry(10, 10, 10) const material = new THREE.MeshBasicMaterial({ color: 0x00fa9a }) const mesh = new THREE.Mesh(geometry, material) mesh.name = '几何体1' scene.add(mesh) mesh.position.set(0, -5, 0) const geometry1 = new THREE.BoxBufferGeometry(10, 10, 10) const mesh1 = new THREE.Mesh(geometry1, material) mesh1.name = '几何体2' scene.add(mesh1) mesh1.position.set(0, 20, 0) const ambient = new THREE.AmbientLight(0xffffff, 1) scene.add(ambient) // var axesHelper = new THREE.AxesHelper(250) // scene.add(axesHelper)}function init() { scene = new THREE.Scene() scene.background = new THREE.Color(0xffffff) camera = new THREE.PerspectiveCamera( 35, window.innerWidth / window.innerHeight, 0.1, 1000 ) camera.position.set(18, 20, 100) renderer = new THREE.WebGLRenderer() renderer.setPixelRatio(window.devicePixelRatio) renderer.setSize(window.innerWidth, window.innerHeight) document.body.appendChild(renderer.domElement) controls = new OrbitControls(camera, renderer.domElement) controls.addEventListener('change', render) window.addEventListener('resize', onWindowResize) document.addEventListener('mousemove', onPointerMove) document.addEventListener('click', onPointerClick) stats = new Stats() document.body.appendChild(stats.dom) raycaster = new THREE.Raycaster() createDom()}function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight camera.updateProjectionMatrix() renderer.setSize(window.innerWidth, window.innerHeight)}function render() { stats.update() camera.updateMatrixWorld() raycaster.setFromCamera(pointer, camera) const intersects = raycaster.intersectObjects(scene.children, false) if (intersects.length > 0) { if (INTERSECTED != intersects[0].object) { if (INTERSECTED) { INTERSECTED.scale.set(1, 1, 1) INTERSECTED = null } INTERSECTED = intersects[0].object } } else { if (INTERSECTED) { INTERSECTED.scale.set(1, 1, 1) INTERSECTED = null } } renderer.render(scene, camera)}init()render()

July 12, 2022 · 2 min · jiezi

关于three.js:ThreeJS学习记录二立方体不同面不同图片待补充

学习点一:material数组在设置Mesh的时候,第一个参数是几何体,第二个参数就是材质。依据官网文档看,第二个参数能够是一个数组。也就是说能够设置几何体不同面的材质图片。 学习点二:程序大家能够看下哪一个面会对应哪个图片,nx\ny\nz\px\py\pz。 学习点三:红色是透明度为0,彩色是透明度为1设置material时,图片红色的中央会透出材质的色彩,彩色中央不会。比照下,上面是图片,material的color设置青色,附在几何体上的成果: 次要应用module: OrbitControls 几何体:BoxBufferGeometry、SphereGeometry、CircleGeometry、CylinderGeometry、IcosahedronGeometry 材质加载:TextureLoader 材质:MeshPhongMaterial 环境光:AmbientLight 具体代码import * as THREE from 'three'// 轨道控制器import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'let scene, camera, controls, rendererlet axesHelper// 设置加载管理器let event = {}event.onLoad = () => { render() //当材质图片加载实现,从新渲染一下}function init() { renderer = new THREE.WebGLRenderer({ antialias: true }) // 创立渲染器对象 renderer.setPixelRatio(window.devicePixelRatio) renderer.setSize(window.innerWidth, window.innerHeight) //设置渲染区域尺寸 document.body.appendChild(renderer.domElement) //body元素中插入canvas对象 scene = new THREE.Scene() camera = new THREE.PerspectiveCamera( 35, window.innerWidth / window.innerHeight, 1, 1000 ) camera.position.set(0, 5, 100) controls = new OrbitControls(camera, renderer.domElement) controls.addEventListener('change', render) //监听鼠标、键盘事件 // 环境光 const ambient = new THREE.AmbientLight(0xffffff, 1) scene.add(ambient) // 几何体 const box = new THREE.BoxBufferGeometry(10, 10, 10) //正方体六面 const ball = new THREE.SphereGeometry(5, 16, 8) //球一面 const circle = new THREE.CircleGeometry(5, 32) //圆形一面 const cylinder = new THREE.CylinderGeometry(5, 5, 20, 32) //圆柱 const twelve = new THREE.IcosahedronGeometry(10, 0) //十二面 // 材质 let texture = new THREE.TextureLoader() let t1 = texture.load('./textures/六面/nx.jpg', event.onLoad) let t2 = texture.load('./textures/六面/px.jpg', event.onLoad) let t3 = texture.load('./textures/六面/ny.jpg', event.onLoad) let t4 = texture.load('./textures/六面/py.jpg', event.onLoad) let t5 = texture.load('./textures/六面/nz.jpg', event.onLoad) let t6 = texture.load('./textures/六面/pz.jpg', event.onLoad) let t1Material = new THREE.MeshPhongMaterial({ color: 0x00fa9a, map: t1, side: THREE.DoubleSide, //双面渲染 }) let t2Material = new THREE.MeshPhongMaterial({ color: 0x00fa9a, map: t2 }) let t3Material = new THREE.MeshPhongMaterial({ color: 0x00fa9a, map: t3 }) let t4Material = new THREE.MeshPhongMaterial({ color: 0x00fa9a, map: t4 }) let t5Material = new THREE.MeshPhongMaterial({ color: 0x00fa9a, map: t5 }) let t6Material = new THREE.MeshPhongMaterial({ color: 0x00fa9a, map: t6 }) // 六面材质数组 let materialArr = [ t1Material, t2Material, t3Material, t4Material, t5Material, t6Material, ] let material = new THREE.MeshPhongMaterial({ color: 0x00fa9a }) let mesh_box = new THREE.Mesh(box, materialArr) let mesh_ball = new THREE.Mesh(ball, material) //球只有一个面 let mesh_circle = new THREE.Mesh(circle, material) let mesh_cylinder = new THREE.Mesh(cylinder, materialArr) let mesh_twelve = new THREE.Mesh(twelve, material) mesh_box.position.set(-32, 0, 0) mesh_ball.position.set(-16, 0, 0) mesh_circle.position.set(0, 0, 0) mesh_cylinder.position.set(16, 0, 0) mesh_twelve.position.set(32, 0, 0) scene.add(mesh_box) scene.add(mesh_ball) scene.add(mesh_circle) scene.add(mesh_cylinder) scene.add(mesh_twelve) render() window.addEventListener('resize', onWindowResize)}function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight camera.updateProjectionMatrix() render.setSize(window.innerWidth, window.innerHeight)}function render() { renderer.render(scene, camera) //执行渲染操作 // requestAnimationFrame(render)}function buildHelper() { // 辅助坐标系 参数250示意坐标系大小,能够依据场景大小去设置 axesHelper = new THREE.AxesHelper(250) scene.add(axesHelper)}init()buildHelper()render()

July 11, 2022 · 2 min · jiezi

关于three.js:Threejs-实现3D开放世界小游戏阿狸的多元宇宙-

申明:本文波及图文和模型素材仅用于集体学习、钻研和观赏,请勿二次批改、非法流传、转载、出版、商用、及进行其余获利行为。背景2545光年之外的开普勒1028星系,有一颗色彩斑斓的宜居星球 ,星际移民 必须穿戴基地发放的防辐射服能力生存。阿狸 驾驶星际飞行器 来临此地,快帮它在限定工夫内应用轮盘挪动找到基地获取防辐射服吧! 本文应用 Three.js + React + CANNON 技术栈,实现通过滑动屏幕管制模型在 3D 世界里静止的 Low Poly 低多边形格调小游戏。本文次要波及到的知识点包含:Three.js 暗影类型、创立粒子系统、cannon.js 根本用法、应用 cannon.js 高度场 Heightfield 创立地形、通过轮盘挪动管制模型动画等。 成果游戏玩法:点击开始游戏按钮,通过操作屏幕底部轮盘来挪动阿狸,在倒计时限定工夫内找到基地。主线工作:限定工夫内找到庇护所。干线工作:自在摸索凋谢世界。 在线预览: 地址1:https://3d-eosin.vercel.app/#... 地址2:https://dragonir.github.io/3d...已适配: PC 端 挪动端 小提示:站得越高看得越远,模摸糊糊据说基地位于初始地位的西面,开始时应该向左前方后退哦。设计游戏流程如下图所示:页面加载实现后玩家 点击开始按钮,而后在限定工夫内通过管制页面底部轮盘 挪动模型,找到指标基地所在的地位。寻找胜利或失败都会显示后果页 ,后果下面有两个按钮再试一次和自在摸索,点击再试一次工夫会重置,而后从新回到终点开始倒计时。点击自在摸索则不在计时,玩家能够在 3D 凋谢世界里操作模型自在摸索。同时,游戏内页面也提供一个时光倒流按钮,它的作用是玩家 能够在失败前本人手动重置倒计时 ⏳,从新回到终点开始游戏。 实现加载资源加载开发所需的必备资源:GLTFLoader 用于加载狐狸 和基地 模型、CANNON 是用于创立 3D 世界的物理引擎;CannonHelper 是对 CANNON 一些应用办法的封装;JoyStick 用于创立通过监听鼠标挪动地位或触碰屏幕产生的位移来管制模型挪动的轮盘 。 import * as THREE from 'three';import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';import CANNON from 'cannon';import CannonHelper from './scripts/CannonHelper';import JoyStick from './scripts/JoyStick';页面构造页面构造比较简单,.webgl 用于渲染 WEBGL;.tool 是游戏内的工具栏,用于重置游戏和显示一些提醒语;.loading 是游戏加载页面,用来显示游戏加载进度、介绍游戏规则、显示游戏开始按钮;.result 是游戏后果页面,用于显示游戏胜利或失败后果,并提供再试一次和自在摸索两个按钮 。 ...

April 1, 2022 · 6 min · jiezi

关于three.js:Threejs系列-写一个第一三人称视角小游戏

大家好,我是秋风,在上一篇 中说到了 Three.js 系列的指标以及宝可梦游戏,那么明天就来通过 Three.js 来谈谈对于游戏中的视角追随问题。置信我的读者都或多或少玩一些游戏,例如王者光荣、绝地求生、宝可梦、塞尔达、原神之类的游戏。那么你晓得他们别离是什么视角的游戏么?你晓得第一人称视角和第三人称视角的差别么?通过代码咱们怎么能实现这样的成果呢?如果你对以上问题好奇,并且不能齐全答复。那么请跟随着我一起往下看吧。 视角解说首先咱们先来看看第一人称视角、第三人称视角的概念。其实对于咱们而言 第一人称 和 第三人称,是十分相熟的,第一人称就是以本人的口气讲述一件事,例如自传都是以这种模式抒写,第三人称则是以旁观者,例如很多小说,都是以他(xxx)来展开式将的,观众则是以上帝视角看着这整个故事。对应的第一人称视角、第三人称视角也是雷同的概念,只不过是视觉下面。那么他们各自有下面区别呢?第一人称视角的有点是能够给玩家带来最大限度的沉迷感,从第一人称视角“我”去察看场景和画面,能够让玩家更加粗疏地感触到其中的细节,最常见的就是相似绝地求生、极品飞车之类的。 而第一人称视角也有他的局限性。玩家的视线受限,无奈看到更广大的的视线。另一个就是第一人称视角会给玩家带来“3D眩晕感”。当反应速度更不上镜头速度的时候会造成眩晕感。那么第三人称视角呢?他的劣势就是自在,视线宽阔,人物挪动和视角是离开的,一个用来操作人物前进方向,另一个则是用来操控视线方向。 它的劣势就是无奈很好的聚焦部分,容易错过细节。然而总的来说,目前大多数游戏都提供了两种视角的切换来满足不同的情景。例如相对求生中平时走路用第三人称视角追随挪动,开枪的时候个别用第一人称视角。好了,到目前为主咱们曾经晓得了第一人称视角、第三人称视角各自概念、区别。那么咱们接下来以第三人称视角为例,开展剖析咱们该如何实现这样的一个成果呢?(第三人称的编写好后,稍加批改就能够变成第一人称,因而以更加简单的第三人称为例)把大象放入冰箱须要几步?三步!关上冰箱,把大象放进冰箱,关上冰箱。显然如果真的要把大象放进冰箱是很难的事件,然而从宏观角度来看,就是三个步骤。因而咱们也将实现第三人称视角这个性能分成三步: 步骤拆分以下的步骤拆分不会蕴含任何代码,请放心使用:1.人物如何静止咱们都晓得在物理实在的世界中,咱们静止起来是靠咱们双腿,迈开就动起来了。那这个过程从更宏观的角度来看是怎么样的呢?其实如果从地球外,从一个更远的角度来看,咱们做静止更像是一个个平移变动。雷同地,咱们在计算机中来示意静止也就是使用了平移变动。平移变动具体大家以前都比拟相熟,如果当初不相熟了呢,也没有什么关系,先看上面的坐标轴。(小方块的边长是1) 小方块从A1地位挪动到地位A2就是平移变动,如果用数学表达式来示意的话就是 下面是什么意思呢?就是说咱们让小方块中所有的小点的 x 值都加2,而 y 的值不变。咱们随便取一些值来验证一下。例如A1地位小方块,左下角是 (0,0), 通过以上变动,就变成了 (2, 0),咱们来A2中看小方块新的地位就是 (2, 0);再用右上角的 (1,1) 代入,发现就变成了(3,1),和咱们实在挪动到的地位也是一样的。所以下面的式子没有什么问题。然而起初呢,大家感觉像下面那样的式子用来示意略微有点不够通用。至于这里为什么说不够通用,在前面的系列文章中会具体解说,因为还波及到了其余变动,例如旋转、缩放,他们都能够用一个矩阵来进行形容,因而如果平移也可能用矩阵的形式来表白,那么整个问题就变得简略了,也就是说:静止变动 = 矩阵变动咱们来看看把最开始的式子变成矩阵是什么样子的: 能够简略解说一下左边这个矩阵是怎么来的 左上角的这个局部称为单位矩阵,前面的 2 0 则就是咱们须要的平移变动,至于为什么从2维变成了3维,则是因为引入了一个齐次矩阵的概念。同样的原理,类比到 3维,咱们就须要用到4维矩阵。所以说,咱们通过一系列的例子,最终想要失去的一个论断就是,所有的静止都是矩阵变动。 2.镜头朝向人物咱们都晓得,在事实世界中咱们眼睛看进来的视线是无限的,在电脑中也是一样的。假如在电脑中咱们的视线是  3 * 3 的方格,咱们还是以之前坐标轴举例子,黄色区域是咱们的视线可见区域: 当初咱们让小块往右挪动3个单位,再网上挪动1个单位。 这个时候咱们会发现,咱们的视线内曾经看不到这个小块了。试想一下,咱们正在玩一个射击游戏,敌人在眼前挪动,咱们为了找到它会在怎么办?没错,咱们会旋转咱们的脑袋,从而使得敌人裸露在咱们的视线内。就像这样: 这下就把敌人锁定住了,可能始终让人物呈现在咱们的视线内并且放弃绝对静止。3.镜头与人物同距光有镜头朝向人物还不够,咱们还得让咱们的镜头和人物同距。为什么这么说呢,首先还是咱们坐标轴的例子,然而这次咱们将裁减一个z轴:而后咱们看看失常下的立体截图 截图: 当初咱们将咱们的小块往-Z 挪动1个单位: 截图: 这个时候咱们发现这个小方块变小了,并且随着小方块往 -z方向挪动的越多,咱们看到的小块会越来越小。这个时候咱们明明没有扭转咱们的视角,然而还是无奈很好的跟踪小块。因而咱们须要挪动为咱们视角的地位,当咱们看不清一个远处的路标的时候,咱们会怎么办?没错,凑近点! 截图: 完满!当初咱们通过三个方向的解说,将如果实现一个第三人称视角的性能从实践下面实现了! 搞代码接下来咱们只须要依照咱们的以上的实践,来实现代码就好了,代码无奈就是咱们用另一种语言的实现形式,晓得了原理都是非常简单的。1.初始化画布场景 ...

March 2, 2022 · 1 min · jiezi

关于three.js:threejs中出现条纹阴影和网格阴影解决办法

ThreeJS 开启暗影正确做法: 渲染器启用暗影 renderer.shadowMap.enabled = true;灯光产生暗影 light.castShadow = true;物体遮挡暗影 obj.castShadow = true;高空显示暗影 obj.receiveShadow = true;无暗影成果: 有暗影成果,产生了许多条纹: 产生起因 将四方块设置为 不接管暗影 obj.receiveShadow = false; 可防止条纹,但四方块上就没有投影成果了。将材质的 material.side = 0; side属性设置为0也能够失常。或者将材质的投影面设置为反面也可解决 material.shadowSide = THREE.BackSide;正确暗影成果: 以上为产生条纹暗影解决办法。 而模型上呈现了网格暗影或投射出的暗影比拟含糊: 则是暗影不够精密导致投射在本身或其余五提倡的暗影呈现锯齿,解决办法:light.shadow.mapSize.width=2048; //暗影贴图宽度设置为2048像素light.shadow.mapSize.height=2048; //暗影贴图高度设置为2048像素(light为你本人定义的可投射暗影灯光) 值的大小能够本人跟据理论状况设置,设置得小一点性能会更加好,设置得大一点成果会更好,不过会占用更多的图像运算资源。 以上。

February 21, 2022 · 1 min · jiezi

关于three.js:THREEjs渲染顺序

本文将会讲述THREE.js渲染程序,这里的渲染程序指的是处于前后地位的物体,是如何渲染进去物体之间的遮挡关系的。 次要的讲述内容包含: 不通明物体的默认渲染程序是怎么的;通明物体的默认渲染程序是怎么的;不通明物体和通明物体一起渲染的时候,默认渲染程序是怎么的;如何扭转物体的默认渲染程序。不通明物体的默认渲染程序是怎么的首先,咱们通过一个简略的例子介绍下咱们要实现的成果: 一个根本场景,场景中搁置两个4X4正方形,正方形与XOY立体平行,红色正方形放在(0, 0, 0)的地位,绿色正方形放在(2, 0, -2)的地位。 相机应用THREE.PerspectiveCamera,这种相机会有近大远小的成果,相机搁置在(0, 0, 6)的地位,此时,相机看向Z轴的负方向。 相机和两个立体的空间地位如下图1、图2所示:图1 图2 通过相机和两个立体的空间地位关系,咱们晓得 红色立体在绿色立体的后面显示;红色立体会遮挡绿色立体的一部分;最初出现成果如下图3所示:图3 此时,你能够先本人思考一下,如果让你本人实现,你会如何实现? 我过后本人想了一下,首先咱们是晓得这两个物体和相机之间的间隔的,咱们能够依照物体间隔相机的远近给物体排个序,而后依照由远及近的程序渲染物体。也就是先渲染绿色的立体,而后渲染红色的立体。下面这个简略的例子没有问题,那对其余简单的场景是否实用呢? 比方,这两个立体间隔相机的地位是一样的?如下图4所示:图4 依照咱们方才的构想,对于上述情景,咱们渲染进去的是要么绿色齐全在下面,要么红色齐全在下面,如下图5或图6所示:图5图6 然而理论的渲染后果应该如下图7所示:图7 所以上述依照由远及近的程序渲染的计划只能满足局部应用场景。那么,THREE.js是如何实现的呢? THREE.js次要是应用WebGL构建3D场景的开源库,是对WebGL提供的能力的一个易用性封装。所以在探讨THREE.js中物体的渲染程序之前就得先看下WebGL是如何渲染不同地位的物体的。而WebGL是基于OpenGL的,所以这个问题就变成了OpenGL是如何渲染不同地位的物体的。 OpenGL是应用深度测试(depth testing)来保障物体渲染进去正确的遮挡关系的。深度测试应用了深度缓冲区(depth buffer)。和色彩缓冲区(color buffer)相似,只不过色彩缓冲区中存储的是每个像素点的色值,而深度缓冲区存储的是色彩缓冲区以后像素点的色值所在的深度。深度缓冲区和色彩缓冲区具备雷同的宽度和高度。 那么,在渲染过程中,对于每一个像素点,咱们存储的数据包含: 以后像素的色值(通过色彩缓冲区获取);以后像素对应的物体片元在空间中的深度(通过深度缓冲区获取)。上面,咱们通过上述图4的例子阐明一下,OpenGL是如何通过深度测试(depth testing)实现物体正确的遮挡关系的。 假如物体先绘制绿色的立体,物体在绘制的时候是逐像素绘制的,此时咱们已知的信息包含像素的色值和深度信息。对于绿色立体投影到的每一个像素,咱们在色彩缓冲区中该像素的地位写入色值,在深度缓冲区中该像素的地位写入深度信息。 而后,咱们开始绘制红色的物体。当绘制红色物体的每一个像素时,咱们晓得了该像素的色值和深度信息D2。而后,咱们依据该像素的坐标,获取深度缓冲区中已绘制像素对应的深度值D1,而后比拟D1和D2的值。有以下三种状况: 如果D2小于D1,那就是该物体的以后像素在后面,也就是该像素应该取该物体的以后像素的色值。此时,更新色彩缓冲区以后像素的色值为红色,更新深度缓冲区以后像素的深度为D2;如果D2大于D1,那就是该物体的以后像素在前面,也就是该像素不会显示进去,所以以后像素的色彩缓冲区和深度缓冲区的值都不必扭转;如果D2等于D1,行为和D2小于D1统一。总结来说,就是咱们有一个判断该像素点是否渲染的函数。该函数的输出是 以后期待渲染的像素点的深度值;深度缓冲区中以后像素点的深度值。输入是一个布尔值表明以后像素是否应用新的色值渲染。 THREE.js中该函数的默认值是LessEqualDepth,也就是上述D2和D1比拟的三种状况。该函数的所有取值能够参考THREE.js官网Depth Mode。 所以,还是下面那个例子: 对于红色物体右边的每一个像素,该深度值D2小于深度缓冲区中的深度值D1,所以色彩缓冲区更新为红色。对于红色物体左边的每一个像素,该深度值D2大于深度缓冲区中的深度值D1,所以放弃原来的色彩。最终的后果就是左半边是红色,右半边是绿色。 咱们下面剖析了先绘制绿色立体,再绘制红色立体的状况。你能够本人尝试剖析先绘制红色立体,再渲染绿色立体的状况。 最初的后果就是渲染后果的遮挡关系根本和绘制的先后顺序无关。 通明物体的默认渲染程序是怎么的后面讲述了不通明物体的渲染程序,那么,如果场景中的物体都是通明物体的时候,又是如何渲染的呢? 还是拿后面的例子举例,此时咱们把两个立体都设置成半透明的。如下图8所示:图8 如果还是用后面那个逻辑,每个像素点的色彩要么不变,要么应用新物体的色彩,加上深度测试的逻辑之后,渲染进去的成果如下图9所示:图9 在现实生活中,透过通明的物体咱们应该是能够看到该通明物体前面的物体的。显然,图9并没有实现这样的成果。那么,问题出在什么中央呢? 咱们后面在深度测试的时候,在往色彩缓冲区中写入色值的时候,要么写入以后物体的色值,要么抛弃以后物体的色值。而对于通明物体来说,最终显示的色值并不是单个物体的色彩,而是多个可见物体色彩的一个混合(blend)。 那么,在后面步骤中,当咱们判断以后物体是在后面时,能够从简略粗犷的间接应用该色值变为依据以后物体的色值和在色彩缓冲区中的色值依照透明度进行一个混合,而后应用混合后的色值更新色彩缓冲区。 THREE.js提供了多种blend办法,默认是NormalBlending。NormalBlending的计算公式如下:color(RGB) = (sourceColor * sourceAlpha) + (destinationColor * (1 - sourceAlpha))color(A) = (sourceAlpha * 1) + (destinationAlpha * (1 - sourceAlpha)) 增加上混合逻辑,最初实现进去的成果如下图10所示,这个成果也是合乎咱们心理预期的一个成果:图10 在渲染不通明物体的时候,咱们发现最终的实现成果和物体绘制的前后程序没有关系。那么,对于通明物体呢?咱们试验一下: 先渲染红色立体,再渲染绿色立体渲染成果如下图11所示:图11 ...

January 4, 2022 · 1 min · jiezi

关于three.js:小程序中开发Threejs教程及踩坑记录

一、后期调研因小程序环境下,没有浏览器相干的运行环境如window, document等,所以在小程序中应用three.js须要应用相应的移植版本。调研了以下三种:①微信官网给出的版本:https://github.com/wechat-min...加载不带贴图的glb文件没问题。但加载带贴图的glb文件报错,对loader文件没有做移植,并且看官网不怎么保护了。②three平台化产品,然而感觉上手不是特地敌对,并且比拟大。https://github.com/deepkolos/...③yannliao微信官网社区里比拟热门的版本https://github.com/yannliao/t... 最终应用了第三种,对glb带材质的文件也能失常加载。 二、应用教程1、在示例仓库(https://github.com/yannliao/t...)中,下载 libs及jsm文件夹,放入本人我的项目根目录下2、在index.js页面的js文件下引入three import * as THREE from '../../libs/three.weapp.min.js'import { OrbitControls } from '../../jsm/loaders/OrbitControls'Page({ data: {}, onLoad: function () { wx.createSelectorQuery() .select('#c') .node() .exec((res) => { const canvas = THREE.global.registerCanvas(res[0].node) this.setData({ canvasId: canvas._canvasId }) const camera = new THREE.PerspectiveCamera(70, canvas.width / canvas.height, 1, 1000); camera.position.z = 500; const scene = new THREE.Scene(); scene.background = new THREE.Color(0xAAAAAA); const renderer = new THREE.WebGLRenderer({ antialias: true }); const controls = new OrbitControls(camera, renderer.domElement); // controls.enableDamping = true; // controls.dampingFactor = 0.25; // controls.enableZoom = false; camera.position.set(200, 200, 500); controls.update(); const geometry = new THREE.BoxBufferGeometry(200, 200, 200); const texture = new THREE.TextureLoader().load('./pikachu.png'); const material = new THREE.MeshBasicMaterial({ map: texture }); // const material = new THREE.MeshBasicMaterial({ color: 0x44aa88 }); const mesh = new THREE.Mesh(geometry, material); scene.add(mesh); // renderer.setPixelRatio(wx.getSystemInfoSync().pixelRatio); // renderer.setSize(canvas.width, canvas.height); function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(canvas.width, canvas.height); } function render() { canvas.requestAnimationFrame(render); // mesh.rotation.x += 0.005; // mesh.rotation.y += 0.01; controls.update(); renderer.render(scene, camera); } render() }) }, onUnload: function () { THREE.global.unregisterCanvas(this.data.canvasId) }, touchStart(e) { console.log('canvas', e) THREE.global.touchEventHandlerFactory('canvas', 'touchstart')(e) }, touchMove(e) { console.log('canvas', e) THREE.global.touchEventHandlerFactory('canvas', 'touchmove')(e) }, touchEnd(e) { console.log('canvas', e) THREE.global.touchEventHandlerFactory('canvas', 'touchend')(e) }, touchCancel(e) { // console.log('canvas', e) }, longTap(e) { // console.log('canvas', e) }, tap(e) { // console.log('canvas', e) }, documentTouchStart(e) { // console.log('document',e) }, documentTouchMove(e) { // console.log('document',e) }, documentTouchEnd(e) { // console.log('document',e) },})3、 在index.wxml中退出canvas组件, 其中须要手动绑定相应的事件,用于手势管制。 ...

December 20, 2021 · 2 min · jiezi

关于three.js:Threejs-新旧版本创建几何对象差异

昨天遇到个小问题,也搞了挺长时间:用Three.js在webgl中绘制三维对象,从网上抄的一段代码,运行报错: THREE.Geometry is not a constructor 纳尼,找不到Geometry对象? function createLine(){ let start = new THREE.Vector3(7, 1.8, -2); let end = new THREE.Vector3(1.6,1.8, -2); // 就是这里创立的对象找不到 ***** let geometry = new THREE.Geometry(); // **************************** let lineMaterial = new THREE.LineBasicMaterial({color: 0x00ff00}); geometry.vertices.push(start); geometry.vertices.push(end); let line = new THREE.Line(lineGeometry, lineMaterial); scene.add(line); return line;}一开始还认为是模块导入问题,折腾了一阵才发现,THREE模块中居然真的就没有Geometry!难道网上的代码是愚人节贴的?遇事不决问google,终于确认人家是降级了。新版的Three.js不再提供Geometry对象,代之以BufferGeometry,代码批改如下: function createLine(){ let start = new THREE.Vector3(7, 1.8, -2); let end = new THREE.Vector3(1.6,1.8, -2); let lineMaterial = new THREE.LineBasicMaterial({color: 0x00ff00}); let pointsArray = [start, end]; let lineGeometry = new THREE.BufferGeometry().setFromPoints(pointsArray); let line = new THREE.Line(lineGeometry, lineMaterial); scene.add(line); return line;}

December 16, 2021 · 1 min · jiezi

关于three.js:Threejs学习-创建立方体

Three.js 零根底入门教程(郭隆邦)Three.js 中文文档First Scene几何体 GeometryTHREE.BoxGeometry(宽, 高, 长) 立方体THREE.SphereGeometry(半径, number, number) 球体var geometry = new THREE.BoxGeometry(100, 100, 100);var geometry = new THREE.SphereGeometry(60, 40, 40); 资料 Material对立方体的色彩、透明度等等属性进行设置 var material = new THREE.MeshLambertMaterial({ color: 0x0000ff });光源 Light创立一个点光源,参数定义光照强度,光源强度变低时,物体也随之变黯淡 var point = new THREE.PointLight(0xffffff);相机 CameraTHREE.OrthographicCamera()创立一个正射投影相机对象,参数扭转拍照窗口的大小。取景范畴变小,物体变大,反之变小。 var camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 1000);整体构造 过程THREE.Scene() 创立场景对象发明物体对象 new THREE.BoxGeometry 创立物体new THREE.MeshLambertMaterial 应用资料new THREE.Mesh(box,material) 创立网格,增加物体new THREE.PointLight(0xffffff) 创立光源new THREE.AmbientLight(0x444444) 创立环境光scene.add(mesg/point/ambient) 向场景中增加对象THREE.OrthographicCamera() 创立相机对象THREE.WebGLRenderer() 创立渲染对象renderer.render(scene,camera) 投影插入多个几何体几何体//长方体 参数:长,宽,高var geometry = new THREE.BoxGeometry(100, 100, 100);// 球体 参数:半径60 经纬度细分数40,40var geometry = new THREE.SphereGeometry(60, 40, 40);// 圆柱 参数:圆柱面顶部、底部直径50,50 高度100 圆周分段数var geometry = new THREE.CylinderGeometry(50, 50, 100, 25);// 正八面体var geometry = new THREE.OctahedronGeometry(50);// 正十二面体var geometry = new THREE.DodecahedronGeometry(50);// 正二十面体var geometry = new THREE.IcosahedronGeometry(50);辅助三维坐标系 AxisHelper// 辅助坐标系 参数250示意坐标系大小,能够依据场景大小去设置var axisHelper = new THREE.AxisHelper(250);scene.add(axisHelper);同时绘制多个几何体代码var geometry1 = new THREE.BoxGeometry(100, 100, 100);var material1 = new THREE.MeshLambertMaterial({ color: 0x0000ff,});var mesh1 = new THREE.Mesh(geometry1, material1);scene.add(mesh1);var geometry2 = new THREE.SphereGeometry(60, 40, 40);var material2 = new THREE.MeshLambertMaterial({ color: 0xff00ff,});var mesh2 = new THREE.Mesh(geometry2, material2);mesh2.translateX(70); //球体网格模型沿Y轴正方向平移120scene.add(mesh2);var geometry3 = new THREE.CylinderGeometry(50, 50, 100, 25);var material3 = new THREE.MeshLambertMaterial({ color: 0xffff00,});var mesh3 = new THREE.Mesh(geometry3, material3);// mesh3.translateX(120); //球体网格模型沿Y轴正方向平移120mesh3.position.set(120, 0, 0); //设置mesh3模型对象的xyz坐标为120,0,0scene.add(mesh3);成果 ...

December 14, 2021 · 2 min · jiezi

关于three.js:ThreeJS-模型点击事件添加

created () { //申明raycaster和mouse变量 var raycaster = new THREE.Raycaster(); var mouse = new THREE.Vector2();},mounted () { window.addEventListener( 'click', onMouseClick, false );},methods: { onMouseClick( event ) { //通过鼠标点击的地位计算出raycaster所须要的点的地位,以屏幕核心为原点,值的范畴为-1到1. mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1; mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1; // 通过鼠标点的地位和以后相机的矩阵计算出raycaster raycaster.setFromCamera( mouse, camera ); // 获取raycaster直线和所有模型相交的数组汇合 var intersects = raycaster.intersectObjects( scene.children ); console.log(intersects); //将所有的相交的模型的色彩设置为红色,如果只须要将第一个触发事件,那就数组的第一个模型扭转色彩即可 for ( var i = 0; i < intersects.length; i++ ) { intersects[ i ].object.material.color.set( 0xff0000 ); } }}

September 11, 2021 · 1 min · jiezi

关于three.js:Threejs-学习路程

记录一下集体学习 Three.js 的历程 以下链接的次要内容蕴含:根本文档 + 前期解决通道 + 模型换肤 + 组案例 + 多通道渲染等,内容比拟系统,就不一一阐明了。 文档学习:Three.js 零根底入门Three.js 文档后处理通道 案例演示:官网案例演示(含预览图)官网案例(含预览图)智慧城市案例模型旁增加文字正文1模型旁增加文字正文2星云图陨石火球模型换肤(可搜 start.jpg 查看实现)太阳系行星运行图水波球创立文字流光路线 纯特效展现(无代码):特效展现一特效展现二 UnrealBloomPass 通道相干案例文章:高亮空幻成果实现分区 Bloom 辉光物体高亮 模型相干文章:模型边界 扩大:泛光WebGL学习之HDR与BloomHDR和Bloom 如有侵权请告知

August 24, 2021 · 1 min · jiezi

关于three.js:Threejs实现穿越云层动效

上文说到,我对《你的性情主导色》流动中最感兴趣的局部就是通过 Three.js 实现穿梭云层动效了,据作者说每朵云呈现的地位都是随机的,成果很好,下图是我实现的版本。 在线 Demo 首先说下实现穿梭云层动效的基本思路: 沿着 Z 轴平均的放一堆 64*64 的立体图形,这些立体的 X 坐标和 Y 坐标是随机的(很像下图的桶装薯片)把下面的所有图形合并成一个大的图形把大的图形和贴片材质(云)生成网格,网格放进场景中动效就是将相机从远处沿着 Z 轴迟缓挪动,就会有了穿梭云层的成果 首先官网文档提供了一个创立一个场景的疾速开始,浏览后能够对上面的内容更好的了解。 上面介绍下Three.js中的基本概念。仅限我这老手的了解。有讲的好的文档或者分享,欢送帮忙指个路。 场景场景就是一块空间,用来装下咱们想要渲染的内容。最简略的用途就是,场景能够增加一个网格,而后渲染进去。 // 初始化场景var scene = new THREE.Scene();// 其余代码...// 把物体增加进场景scene.add(mesh);// 渲染场景renderer.render(scene, camera);这里说下场景中的坐标规定:原点是 canvas 的立体核心,Z 轴垂直于 X、Y 轴,正向是冲着咱们的,我这里把 Z 轴的线做了些旋转,不然咱们看不到,如下图: 代码: const scene = new THREE.Scene();var camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000);camera.position.set(0, 0, 100);const renderer = new THREE.WebGLRenderer();renderer.setSize(window.innerWidth, window.innerHeight);document.body.appendChild(renderer.domElement);// 线段1,红色的,从原点到X轴40const points = [];points.push(new THREE.Vector3(0, 0, 0));points.push(new THREE.Vector3(40, 0, 0));const geometry1 = new THREE.BufferGeometry().setFromPoints(points);var material1 = new THREE.LineBasicMaterial({ color: 'red' });var line1 = new THREE.Line(geometry1, material1);// 线段2,蓝色的,从原点到Y轴40points.length = 0;points.push(new THREE.Vector3(0, 0, 0));points.push(new THREE.Vector3(0, 40, 0));const geometry2 = new THREE.BufferGeometry().setFromPoints(points);var material2 = new THREE.LineBasicMaterial({ color: 'blue' });var line2 = new THREE.Line(geometry2, material2);// 线段3,绿色的,从原点到Z轴40points.length = 0;points.push(new THREE.Vector3(0, 0, 0));points.push(new THREE.Vector3(0, 0, 40));const geometry3 = new THREE.BufferGeometry().setFromPoints(points);var material3 = new THREE.LineBasicMaterial({ color: 'green' });var line3 = new THREE.Line(geometry3, material3);// 做了个旋转,不然看不到Z轴上的线line3.rotateX(Math.PI / 8);line3.rotateY(-Math.PI / 8);scene.add(line1, line2, line3);renderer.render(scene, camera);相机场景内的物体要想被咱们看见,也就是渲染进去,须要相机去“看”,通过下面的坐标系图,咱们晓得同一个物体,相机察看的角度不同,必定也会呈现出不一样的画面。最罕用的就是这里用的透视相机,能够穿透物体,用在这里正好穿透云层,成果拔群。 ...

August 17, 2021 · 3 min · jiezi

关于three.js:带你入门threejs从0到1实现一个3d可视化地图

前言终于到周末了,前几篇的文章始终给大家介绍2d,canvas 和svg的一些货色。7月份我打算输入3篇万字长文带大家系统地学习可视化表白的3种形式,svg、canvas、webgl。所以这是第一篇文章3d的。 读完本篇文章,你能够学到什么 对于three.js 这个框架有一个简略的了解,能够入门下。学习three中的Raycaster,次要是用鼠标来判断以后抉择的是哪一个物体。我用一个简略的实例 带大家用three实现简略的可视化地球案例 。3d框架的抉择——three.js1.为什么抉择three.js 官网对 Threejs 的介绍非常简单:“Javascript 3D library”。openGL 是一个跨平台3D/2D的绘图规范,WebGL 则是openGL 在浏览器上的一个实现。web前端开发人员能够间接用WebGL 接口进行编程,但 WebGL 只是十分根底的绘图API,须要编程人员有很多的数学知识、绘图常识能力实现3D编程工作,而且代码量微小。Threejs 对 WebGL 进行了封装,让前端开发人员在不须要把握很多数学知识和绘图常识的状况下,也可能轻松进行web 3D开发,升高了门槛,同时大大晋升了效率。总结来一句话: 就是你不懂计算机图形学,只有了解了three.js的一些基本概念你能够。 Threejs 的基本要素——场景定义如下: 场景:是一个三维空间,所有物品的容器,能够把场景设想成一个空房间,接下来咱们会往房间里放要出现的物体、相机、光源等。用代码示意就是如下: const scene = new THREE.Scene();你就把他设想成一个房间,而后你能够往里面去增加一些物体,加一个正方体哈,加矩形,什么都能够。其实three.js 整个之间的关系是一个 树形构造。 Threejs 的基本要素——相机相机:Threejs必须要往场景中增加一个相机,相机用来确定地位、方向、角度,相机看到的内容就是咱们最总在屏幕上看到的内容。在程序运行过程中,能够调整相机的地位、方向和角度。three.js 中的相机分为两种一种是正交相机 和透视相机,接下来我给大家一一介绍,然而了解照相机的状况下,你要先了解一个概念——视椎体 透视相机视锥体是摄像机可见的空间,看上去像截掉顶部的金字塔。视锥体由6个裁剪面围成,形成视锥体的4个侧面称为上左下右面,别离对应屏幕的四个边界。为了避免物体离摄像机过近,设置近切面,同时为了避免物体离摄像机太远而不可见,设置远切面。 oc 就是照相机的地位, 近立体、和远平面图中曾经标注。从图中能够看出,棱台组成的6个面之内的货色,是能够被看到的。 影响透视照相机的大小因素: 摄像机视锥体垂直视线角度 也就是图中的a摄像机视锥体近端面 也就是图中的 near plane摄像机视锥体远端面 也就是图中的far plane摄像机视锥体长宽比 示意输入图像的宽和高之比对应的three 中的照相机: const camera = new THREE.PerspectiveCamera( 45, width / height, 1, 1000 ); 透视相机最大的特点:就是合乎咱们人眼察看事物的特点, 近大远小。 近大远小的背地的实现原理就是相机会有一个投影矩阵: 投影矩阵的做的事件很简略,就是把视椎体转换成一个正方体。 所以远截面的点就要放大, 近距离的反而放大。 ...

July 4, 2021 · 5 min · jiezi

关于three.js:借助threejs实现全景图片360°预览

简介上篇文章前端如何实现一个图片笼罩的球体,咱们借助three.js实现了一个3D的旋转球体。这篇文章咱们略微革新下代码,实现一个全景图片预览示例。本文会应用three.js以及插件来实现3种demo demo&&代码1.three.js实现 2.借助JeremyHeleine/Photo-Sphere-Viewer插件实现 3.借助mistic100/Photo-Sphere-Viewer插件实现 · 示例代码地址(gitee) 启动我的项目须要动态服务器以下示例只展现要害代码,残缺代理去仓库查看即可1.three.js实现预览地址残缺代码 创立场景//globevar width = window.innerWidth;var height = window.innerHeight;const textureLoader = new THREE.TextureLoader();//scenevar scene = new THREE.Scene();//cameravar camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 10000);camera.position.y = 0;camera.position.z = 500;scene.add(camera);创立图片以及球体这里咱们为了实现全景,球的半径能够适当做大一些,就有那个全景的感觉了 //imgTexturevar imgTexture = textureLoader.load("./park.jpg");imgTexture.minFilter = THREE.LinearFilter;imgTexture.magFilter = THREE.LinearFilter;imgTexture.format = THREE.RGBFormat;var cubeGeometry = new THREE.SphereGeometry(500, 60, 40);var sphereMat = new THREE.MeshBasicMaterial({ map: imgTexture });sphereMat.side = THREE.BackSide;var cube = new THREE.Mesh(cubeGeometry, sphereMat);scene.add(cube);要害代码var cubeGeometry = new THREE.SphereGeometry(500, 60, 40);咱们把半径改为50看下改为500再看下, ...

July 2, 2021 · 1 min · jiezi

关于three.js:前端如何实现一个图片覆盖的球体

简介做展现类我的项目的时候,通常要实现一些圆形抉择的成果。这个借助css的rotate 就能够实现.然而!要实现一个3D的带图片的旋转球体该怎么做呢?让咱们开始吧。 残缺代码codesandbox残缺代码gitee预览地址备用预览地址工具three.jsOrbitControls 绘制创立一个场景将场景借助three.js来进行显示,咱们须要以下几个对象:场景、相机和渲染器,这样咱们就能透过摄像机渲染出场景。 let camera, scene, renderer;// scenescene = new THREE.Scene();// cameracamera = new THREE.PerspectiveCamera( 10, window.innerWidth / window.innerHeight, 0.1, 200);camera.position.set(30, 5, 20);//rendererrenderer = new THREE.WebGLRenderer();renderer.setPixelRatio(window.devicePixelRatio);renderer.setSize(window.innerWidth, window.innerHeight);document.body.appendChild(renderer.domElement);咱们花一点点工夫来解释一下这里产生了什么。咱们当初建设了场景、相机和渲染器。 three.js里有几种不同的相机,在这里,咱们应用的是PerspectiveCamera(透视摄像机)。 第一个参数是视线角度(FOV)。视线角度就是无论在什么时候,你所能在显示器上看到的场景的范畴,数值越大越远。 第二个参数是长宽比(aspect ratio)。 也就是你用一个物体的宽除以它的高的值。比如说,当你在一个宽屏电视上播放老电影时,能够看到图像好像是被压扁的。 接下来的两个参数是近截面(near)和远截面(far)。 当物体某些局部比摄像机的远截面远或者比近截面近的时候,该这些局部将不会被渲染到场景中。或者当初你不必放心这个值的影响,但将来为了取得更好的渲染性能,你将能够在你的应用程序里去设置它。 接下来是渲染器。这里是施展魔法的中央。除了咱们在这里用到的WebGLRenderer渲染器之外,Three.js同时提供了其余几种渲染器,当用户所应用的浏览器过于老旧,或者因为其余起因不反对WebGL时,能够应用这几种渲染器进行降级。 除了创立一个渲染器的实例之外,咱们还须要在咱们的应用程序里设置一个渲染器的尺寸。比如说,咱们能够应用所须要的渲染区域的宽高,来让渲染器渲染出的场景填充斥咱们的应用程序。因而,咱们能够将渲染器宽高设置为浏览器窗口宽高。对于性能比拟敏感的应用程序来说,你能够应用setSize传入一个较小的值,例如window.innerWidth/2和window.innerHeight/2,这将使得应用程序在渲染时,以一半的长宽尺寸渲染场景。 如果你心愿放弃你的应用程序的尺寸,然而以较低的分辨率来渲染,你能够在调用setSize时,将updateStyle(第三个参数)设为false。例如,假如你的<canvas> 标签当初曾经具备了100%的宽和高,调用setSize(window.innerWidth/2, window.innerHeight/2, false)将使得你的应用程序以一半的分辨率来进行渲染。 最初一步很重要,咱们将renderer(渲染器)的dom元素(renderer.domElement)增加到咱们的HTML文档中。这就是渲染器用来显示场景给咱们看的<canvas>元素。 “嗯,看起来很不错,那你说的那个球体在哪儿?”接下来,咱们就来增加球体。 创立球体let earth;const earthGeometry = new THREE.SphereGeometry(2, 16, 16);const earthMaterial = new THREE.MeshBasicMaterial({ specular: 0x333333, shininess: 5, map: textureLoader.load("./img/earth_atmos_2048.jpg"), specularMap: textureLoader.load("./img/earth_specular_2048.jpg"), normalMap: textureLoader.load("./img/earth_normal_2048.jpg"), normalScale: new THREE.Vector2(0.85, 0.85),});earth = new THREE.Mesh(earthGeometry, earthMaterial);scene.add(earth);// controlsconst controls = new THREE.OrbitControls(camera, renderer.domElement);controls.minDistance = 5;controls.maxDistance = 100;controls.enablePan = false;要创立一个球体,咱们须要一个SphereGeometry(球缓冲几何体)对象. 这个对象该几何体是通过扫描并计算围绕着Y轴(程度扫描)和X轴(垂直扫描)的顶点来创立的 ...

July 1, 2021 · 1 min · jiezi

关于three.js:threejs-glsl-用-shader-写出夜空中的萤火虫

参考课程是这位老哥的 three.js 教程 十分厉害 感兴趣的能够买来看看如果自身对 shader 还不够理解的话,the book of shaders 是十分好的入门读物说到做“萤火虫”,必定会想到用“粒子”(particles)。 用 buffer geometry 创立粒子首先定义一个 buffer geometry, 而后创立一个 BufferAttribute 用于存储每一个萤火虫的地位信息; const firefliesGeometry = new THREE.BufferGeometry()const firefliesCount = 30 //创立萤火虫的个数const positionArray = new Float32Array(firefliesCount * 3)for(let i = 0; i < firefliesCount; i++){ positionArray[i * 3 + 0] = Math.random() * 4 positionArray[i * 3 + 1] = Math.random() * 4 positionArray[i * 3 + 2] = Math.random() * 4}firefliesGeometry.setAttribute('position', new THREE.BufferAttribute(positionArray, 3))咱们先给它一个 pointsMaterial 看看这些点都在哪里; ...

June 27, 2021 · 2 min · jiezi

关于three.js:threejs添加警报点

1.初始化scene,创立警报点增加到scene serverGroup = new THREE.Group();scene.add(serverGroup);pointGroup = this.addAletPoint([1.5,112,0])pointGroup.mesh.name = 'zhongxin'serverGroup.add(pointGroup.mesh);serverGroup.add(pointGroup.mesh2);// 增加警报点addAletPoint(position) { let vertexShader = [ 'varying vec3 vVertexWorldPosition;', 'varying vec3 vVertexNormal;', 'varying vec4 vFragColor;', 'void main(){', ' vVertexNormal = normalize(normalMatrix * normal);', //将法线转换到视图坐标系中 ' vVertexWorldPosition = (modelMatrix * vec4(position, 1.0)).xyz;', //将顶点转换到世界坐标系中 ' // set gl_Position', ' gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);', '}' ].join('\n') let fragmentShader1 = [ 'uniform vec3 glowColor;', 'uniform float coeficient;', 'uniform float power;', 'varying vec3 vVertexNormal;', 'varying vec3 vVertexWorldPosition;', 'varying vec4 vFragColor;', 'void main(){', ' vec3 worldCameraToVertex= vVertexWorldPosition - cameraPosition;', //世界坐标系中从相机地位到顶点地位的间隔 ' vec3 viewCameraToVertex = (viewMatrix * vec4(worldCameraToVertex, 0.0)).xyz;', //视图坐标系中从相机地位到顶点地位的间隔 ' viewCameraToVertex = normalize(viewCameraToVertex);', //规一化 ' float intensity = pow(coeficient + dot(vVertexNormal, viewCameraToVertex), power);', ' gl_FragColor = vec4(glowColor, intensity);', '}' ].join('\n') //实质透明度递加 let sphere = new THREE.SphereBufferGeometry(2, 16, 16) let material = new THREE.ShaderMaterial({ uniforms: { coeficient: { type: 'f', value: 1.0 }, power: { type: 'f', value: 1 }, glowColor: { type: 'c', value: new THREE.Color('#ff0000') } }, vertexShader: vertexShader, fragmentShader: fragmentShader1, blending: THREE.NormalBlending, // depthWrite:false, // depthTest: true, transparent: true }) let mesh = new THREE.Mesh(sphere, material) let sphere2 = new THREE.SphereBufferGeometry(0.1, 16, 16) let material2 = new THREE.MeshPhongMaterial({ color: new THREE.Color('#ff0000') // depthWrite:false, // depthTest: true }) let mesh2 = new THREE.Mesh(sphere2, material2) mesh.position.set(...position) // ,高度, mesh2.position.set(...position) // ,高度, return { mesh: mesh, mesh2: mesh2 }},2.原作者地址:https://segmentfault.com/u/yo...3.管制警报点显示以及闪动(lightI在data()曾经初始化了,作为光源的直径大小,通过scale来管制) ...

June 24, 2021 · 2 min · jiezi

关于three.js:Threejs-raycaster当canvas不是满屏幕时点击捕捉模型

let a = document.getElementsByTagName("canvas") var canvas for(let i of a){ if(i.parentElement&&i.parentElement.id&&i.parentElement.id =="modelBox"){ canvas = i } } let x = ((event.clientX - canvas.getBoundingClientRect().left) / canvas.offsetWidth) *2-1;// 规范设施横坐标 // 这里的mainCanvas是个dom元素,getBoundingClientRectangle会返回以后元素的视口大小. let y = -((event.clientY - canvas.getBoundingClientRect().top) / canvas.offsetHeight) *2+1;// 规范设施纵坐标 let standardVector =new THREE.Vector3(x, y,1);// 规范设施坐标 // 规范设施坐标转世界坐标 let worldVector = standardVector.unproject(camera); // 射线投射方向单位向量(worldVector坐标减相机地位坐标) let ray = worldVector.sub(camera.position).normalize(); // 创立射线投射器对象 let raycaster =new THREE.Raycaster(camera.position, ray); // 获取raycaster直线和所有模型相交的数组汇合 var intersects = raycaster.intersectObjects(scene.children, true);这里定位的modelBox是threejs的canvas的父辈div的id ...

December 11, 2020 · 1 min · jiezi

关于three.js:精读用threejs实现全景图

之前做组内分享,选了threejs来学习一下,接下来就把用threejs实现的全景图来解读一下。 1. three.js根底回顾1.1 three.js概览1. 是什么? Three.js 是一款 webGL 框架,它封装了底层的图形接口,使得可能在无需把握繁冗的图形学常识的状况下,也能用简略的代码实现三维场景的渲染。 2. 外围 渲染器: 将场景中的物体进行渲染场景: 场景是所有物体的容器,也对应着咱们创立的三维世界相机: 一种投影形式,将三维的场景显示到二维的屏幕上1.2 相机1. 是什么 咱们应用Three.js创立的场景是三维的,而通常状况下显示屏是二维的,那么三维的场景如何显示到二维的显示屏上呢?照相机就是这样一个形象,它定义了三维空间到二维屏幕的投影形式,用“照相机”这样一个类比,能够使咱们直观地了解这一投影形式。 2. 分类 正交投影照相机: 对于在三维空间内平行的线,投影到二维空间中也肯定是平行的透视投影照相机: 有近大远小的成果1.3 模型1. 次要有以下几种模型 2. 模型包含什么 几何形态材质1.4 光与影1. 分类 环境光指场景整体的光照成果。是因为场景内若干光源的屡次反射造成的亮度统一的成果,通常用来为整个场景指定一个根底亮度 点光源点光源是不计光源大小,能够看作一个点收回的光源。点光源照到不同物体外表的亮度是线性递加的 平行光对于任意平行的立体,平行光照耀的亮度都是雷同的 聚光灯聚光灯是一种非凡的点光源,它可能朝着一个方向投射光线 暗影明暗是绝对的,暗影的造成也就是因为比四周取得的光照更少。因而,要造成暗影,光源必不可少。 2. 全景图的实现建设球体模型并令照相机位于球体中一直挪动照相机的地位使其察看到球体内的各个角度2.1 建设根本场景初始化渲染器const setupRenderer = () => { const renderer = new THREE.WebGLRenderer({antialias: true}) renderer.setSize(window.innerWidth, window.innerHeight) document.body.appendChild(renderer.domElement) return renderer}初始化相机// 建设透视投影相机制作出“近大远小”的成果const setupCamera = () => { const aspectRatio = window.innerWidth / window.innerHeight const camera = new THREE.PerspectiveCamera(90, aspectRatio, 0.0001, 10000) camera.position.set(window.obj.camerax, window.obj.cameray, window.obj.cameraz) return camera}初始化场景scene和辅助线 const scene = new THREE.Scene() const axesHelper = new THREE.AxesHelper(1000) const cameraHelper = new THREE.CameraHelper(camera) const gridHelper = new THREE.GridHelper(1000, 10) // 安排场景 scene.add(axesHelper) scene.add(cameraHelper) scene.add(gridHelper)建设球体模型// 采纳加载纹理贴图的形式将一张一般的全景图贴在球体的外表const sphereGeo = new THREE.SphereGeometry(radius)const sphereMaterial = new THREE.MeshBasicMaterial({ map: texture, side: THREE.DoubleSide,})const sphere = new THREE.Mesh(sphereGeo, sphereMaterial)这样,根本的场景就展示进去了。此时咱们只能看到球中的一个方向。 ...

November 7, 2020 · 3 min · jiezi

关于three.js:炫酷用Perlin-Noise生成随机地形

良久不发THREE.js的教程了,明天给大家带了一个小案例,介绍如何用柏林噪声在THREE.js中生成随机地形,先来看下成果。 随机数和噪声 噪声实际上就是随机数生成器,一般噪声生成的随机数毫无法则可言。 在上世纪80年代,柏林噪声的发明者Ken Perlin尝试用一般噪声模仿随机成果,用于正在制作的经典迪士尼电影电子世界争霸战(TRON),但始终无奈失去称心的成果,柏林噪声算法就这样应运而生。 2010年TRON重拍版本 柏林噪声 自然界中层叠的山峦,大理石的纹理,海面高下起伏的波浪,这些景象看似横七竖八,却都有外在法则可循,一般的噪声无奈模拟出这些天然成果,柏林噪声算法使这些成为可能。 用柏林噪声算法能够失去一组平滑插值的随机数,它们之间互相关联,能够用来生成靠近天然的随机值。下图中,右边是一般噪声生成的随机纹理,左边是柏林噪声生成的纹理,能够看到后者生成的纹理更加天然平滑,随机值之间有显著过渡成果。 我用canvas绘制了两组图像,别离是用两种算法生成一些随机点,而后将这些点连接起来,能够看到右侧柏林噪声失去的是一条绝对平滑的曲线,而右边一般噪声的图形毫无法则可言。 一般噪声和柏林噪声随机点连线 生成随机地形 在THREE.js咱们用一个立体来生成随机地形,立体是由一系列顶点形成的,咱们只须要扭转这些顶点的z轴坐标值,就能产生高下起伏的成果,咱们先用随机噪声来试试。 let pos = this.groundGeo.attributes.position.arrayfor(let i = 0; i < pos.length; i+=3) { pos[i+2] = Math.random() * 2}this.groundGeo.attributes.position.needsUpdate = true 随机噪声产生的地形 能够看到随机噪声产生的地形太过于僵硬,必定不是咱们想要的后果,再来试试柏林噪声。 let pos = this.groundGeo.attributes.position.arrayfor(let i = 0; i < pos.length; i+=3) { pos[i+2] = this.simplex.noise2D(tx, ty) * 2}this.groundGeo.attributes.position.needsUpdate = true咱们失去了更加天然平滑的地形成果,这里咱们应用的Simplex Noise是柏林噪声的更高效,简介的实现,也是柏林噪声的作者提出的,这里咱们不关怀这些算法的实现细节。 柏林噪声产生的地形成果 挪动地形 要实现地形向观察者挪动,咱们只须要扭转柏林噪声的x坐标,让它随着工夫递增。 let pos = this.groundGeo.attributes.position.arraythis.offsetX += 0.01for(let i = 0; i < pos.length; i+=3) { let tx = this.offsetX let ty = this.offsetY tx += row * 0.05 ty += col * 0.05 pos[i+2] = this.simplex.noise2D(tx, ty) * 5}this.groundGeo.attributes.position.needsUpdate = true这里应用一个offsetX变量,它的值在每帧循环中递增。留神,我只贴了要害代码,具体细节能够点击浏览原文来取得本案例的源文件。 ...

September 9, 2020 · 1 min · jiezi

新手向用Threejs写一个旋转的动态三角形注释超详细

Three.JS是基于WebGL的Javascript开源框架,一模一样的效果我也有用webGL做过,链接是这个https://segmentfault.com/a/11... html部分还是很简单 <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>demoThree</title></head><body> <canvas id="demo-three"></canvas></body><script type="text/javascript" src="./three.js"></script><script type="text/javascript" src="./demo_three.js"></script></html>下面是js部分,每行代码都有比较详细的注释解释 // 定义宽高const width = 400;const height = 400;const canvas = document.getElementById('demo-three');// WebGLRenderer如果不传参数不会把dom元素和最终渲染出来的屏幕上的html上的dom元素相关联,那种情况就需要把domElement,append到body上,才能有相关画布出现let renderer = new THREE.WebGLRenderer({ canvas});// 绘制场景,在scene中维护在场景中渲染的物体let scene = new THREE.Scene();// 使用正交相机(没有透视效果,适合我们这次的平面三角形),正交相机需要定义上下左右前后的坐标情况,从而规定正交相机所能截取到的相关的平面空间中,物体的情况(视角空间的截取)let camera = new THREE.OrthographicCamera(-width / 2, width / 2, height / 2, -height / 2, -1000, 1000)// 黑色背景,透明度1.0renderer.setClearColor(new THREE.Color(0x000000), 1.0);// renderer大小renderer.setSize(400, 400);// 在three.js官网可查看,有很多预定义的geometry,但是没有三角形,我们可以通过shapeGeometry来绘制let triangleShape = new THREE.Shape();// 将绘制起始点移动到(const0, 100)这个位置triangleShape.moveTo(0, 100);// 开始划线triangleShape.lineTo(-100, -100);triangleShape.lineTo(100, -100);triangleShape.lineTo(0, 100);// 定义几何体const geometry = new THREE.ShapeGeometry(triangleShape);// 定义材质,三角形只是基本图形,不需要光照const material = new THREE.MeshBasicMaterial({ color: 0xff0000, // 默认是一面的(只渲染法向量所对应那一面),定义为DoubleSide,将正反面都渲染出来 side: THREE.DoubleSide});// 第一个参数geometry,是我们之前定义的几何体的坐标情况,就很像之前的webGl中顶点着色器传入的顶点序列,不涉及颜色信息,只是定义几何体形状// 第二个参数material, 很像webGl中片段着色器中的,给顶点序列上色let mesh = new THREE.Mesh(geometry, material);// 设置mesh在scene中的位置mesh.position.x = 0;mesh.position.y = 0;mesh.position.z = 1;scene.add(mesh);// 相机沿着z轴负方向望进去,所以把相机设置在了原点// 设置位置直接通过position,要比原生webGl去修改往顶点着色器传的数据要方便很多camera.position.x = 0;camera.position.y = 0;camera.position.z = 0;// camera从(0, 0, 0)的位置望向(0,0,1)camera.lookAt(new THREE.Vector3(0, 0, 1));let currentAngle = 0;let lastTimestamp = Date.now();function animate() { // 记下时间,拿到现在的时间戳,因为每次requestAnimationFrame间隔时间是不知道的 const now = Date.now(); // 减去老的时间戳 const duration = now - lastTimestamp; lastTimestamp = now; // 想要requestAnimationFrame间隔时间内转180度 currentAngle = currentAngle + duration / 1000 * Math.PI;}function render() { animate(); // mesh绕y轴旋转currentAngle角度 mesh.rotation.set(0, currentAngle, 0); // 渲染 renderer.render(scene, camera); requestAnimationFrame(render);}render();效果: ...

July 8, 2020 · 1 min · jiezi

threejs-load-obj-文件-合并-group中所有mesh

需求load obj文件得到的是个group 有多个mesh ,现在需要合并,方便做移动操作。 解决方法创建geometry let geometry = new THREE.Geometry();遍历 merge for(let i = 0;i<arr.length;i++){ let item = new THREE.Geometry().fromBufferGeometry( arr[i].geometry ) geometry.merge( item,arr[i].matrix ); }注意尝试过 BufferGeometry 并不好用,所以这里吧 Geometry 转成了 BufferGeometry

June 8, 2020 · 1 min · jiezi