three.js实现3D影院的原理的代码分析


Posted in Javascript onDecember 18, 2017

本篇文章我们通过介绍3D影院的视觉原理,并介绍了three.js事件处理过程,全面分析了实现3D影院的基础知识。

1.创建一个3d的空间

可以想象一下我们在房间内,房间是一个立方体,如果你有生活品味,可能会在房间内贴上壁纸,three.js可以很方便的创建一个立方体,并且给它的周围贴上纹理,让照相机在立方体之中,照相机可以360旋转,就模拟了一个真实的场景。

转换为代码:

const path = 'assets/image/'
 const format = '.jpg'
 const urls = [
 `${path}px${format}`, `${path}nx${format}`,
 `${path}py${format}`, `${path}ny${format}`,
 `${path}pz${format}`, `${path}nz${format}`
 ]
 const materials = []
 urls.forEach(url => {
 const textureLoader = new TextureLoader()
 textureLoader.setCrossOrigin(this.crossOrigin)
 const texture = textureLoader.load(url)
 materials.push(new MeshBasicMaterial({
 map: texture,
 overdraw: true,
 side: BackSide
 }))
 })
 const cube = new Mesh(new CubeGeometry(9000, 9000, 9000), new MeshFaceMaterial(materials))
 this.scene.add(cube)

CubeGeometry创建一个超大的立方体 MeshFaceMaterial给立方体贴上文理,由于视角是在立方体内部,所以side:BackSide 2.粒子效果

一个3d模型是由点,线,面组成的,可以遍历模型的每一个点,把每一个点转换为几何模型,并且给它贴上文理,拷贝每一个点的位置,用这些几何模型重新构成一个只有点的模型,这就是粒子效果的基本原理。

this.points = new Group()
 const vertices = []
 let point
 const texture = new TextureLoader().load('assets/image/dot.png')
 geometry.vertices.forEach((o, i) => {
 // 记录每个点的位置
 vertices.push(o.clone())
 const _geometry = new Geometry()
 // 拿到当前点的位置
 const pos = vertices[i]
 _geometry.vertices.push(new Vector3())
 const color = new Color()
 color.r = Math.abs(Math.random() * 10)
 color.g = Math.abs(Math.random() * 10)
 color.b = Math.abs(Math.random() * 10)
 const material = new PointsMaterial({
 color,
 size: Math.random() * 4 + 2,
 map: texture,
 blending: AddEquation,
 depthTest: false,
 transparent: true
 })
 point = new Points(_geometry, material)
 point.position.copy(pos)
 this.points.add(point)
 })
 return this.points

new Group创建一个群,可以说是粒子的集合通过point.position.copy(pos)设置粒子和位置,坐标和模型中对应点的位置相同 3.点击事件的处理

three.js的点击事件需要借助光线投射器(Raycaster),为了方便理解,请先看一张图:

three.js实现3D影院的原理的代码分析

Raycaster发射一个射线,intersectObject监测射线命中的物体

this.raycaster = new Raycaster()
// 把你要监听点击事件的物体用数组储存起来
this.seats.push(seat)

onTouchStart(event) {
 event.preventDefault()
 event.clientX = event.touches[0].clientX;
 event.clientY = event.touches[0].clientY;
 this.onClick(event)
 }

 onClick(event) {
 const mouse = new Vector2()
 mouse.x = ( event.clientX / this.renderer.domElement.clientWidth ) * 2 - 1
 mouse.y = - ( event.clientY / this.renderer.domElement.clientHeight ) * 2 + 1;
 this.raycaster.setFromCamera(mouse, this.camera)
 // 检测命中的座位
 const intersects = this.raycaster.intersectObjects(this.seats)
 if (intersects.length > 0) {
 intersects[0].object.material = new MeshLambertMaterial({
  color: 0xff0000
 })
 }
 }

intersects.length > 0 表示射线命中了某个几何体偷懒只实现了移动端的点击实现,如果想看pc怎么实现,请看thee.js官网

4.着色器的初步使用

着色器分为顶点着色器和片元着色器,用GLSL语言编写,是一种和GPU沟通的的语言,这里只讲如何使用

const vertext = `
 void main()
 {
 gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
 }
 `

const fragment = `
 uniform vec2 resolution;
 uniform float time;

 vec2 rand(vec2 pos)
 {
 return fract( 0.00005 * (pow(pos+2.0, pos.yx + 1.0) * 22222.0));
 }
 vec2 rand2(vec2 pos)
 {
 return rand(rand(pos));
 }

 float softnoise(vec2 pos, float scale)
 {
 vec2 smplpos = pos * scale;
 float c0 = rand2((floor(smplpos) + vec2(0.0, 0.0)) / scale).x;
 float c1 = rand2((floor(smplpos) + vec2(1.0, 0.0)) / scale).x;
 float c2 = rand2((floor(smplpos) + vec2(0.0, 1.0)) / scale).x;
 float c3 = rand2((floor(smplpos) + vec2(1.0, 1.0)) / scale).x;

 vec2 a = fract(smplpos);
 return mix(
 mix(c0, c1, smoothstep(0.0, 1.0, a.x)),
 mix(c2, c3, smoothstep(0.0, 1.0, a.x)),
 smoothstep(0.0, 1.0, a.y));
 }

 void main(void)
 {
 vec2 pos = gl_FragCoord.xy / resolution.y;
 pos.x += time * 0.1;
 float color = 0.0;
 float s = 1.0;
 for(int i = 0; i < 8; i++)
 {
 color += softnoise(pos+vec2(i)*0.02, s * 4.0) / s / 2.0;
 s *= 2.0;
 }
 gl_FragColor = vec4(color);
 }
 `
// 设置物体的质材为着色器质材
 let material = new ShaderMaterial({
 uniforms: uniforms,
 vertexShader: vertext,
 fragmentShader: fragment,
 transparent: true,
 })

5.光晕效果

由于是模拟电影院,我想做一个投影仪,模拟投影仪射出的光线。

// 光晕效果必须设置alpha = true
 const renderer = this.renderer = new WebGLRenderer({alpha: true, antialias: true})

 let textureFlare = new TextureLoader().load('assets/image/lensflare0.png')
 let textureFlare3 = new TextureLoader().load('assets/image/lensflare3.png')
 let flareColor = new Color(0xffffff)
 let lensFlare = new LensFlare(textureFlare, 150, 0.0 , AdditiveBlending, flareColor)
 lensFlare.add(textureFlare3, 60, 0.6, AdditiveBlending);
 lensFlare.add(textureFlare3, 70, 0.7, AdditiveBlending);
 lensFlare.add(textureFlare3, 120, 0.9, AdditiveBlending);
 lensFlare.add(textureFlare3, 70, 1.0, AdditiveBlending);
 lensFlare.position.set(0, 150, -85)

主要的光线还是靠lensflare0.png模拟 textureFlare3设置光晕的范围

Javascript 相关文章推荐
理解JavaScript中的事件
Sep 23 Javascript
JavaScript 判断判断某个对象是Object还是一个Array
Jan 28 Javascript
js关于字符长度限制的问题示例探讨
Jan 24 Javascript
JavaScript对数组进行随机重排的方法
Jul 22 Javascript
jQuery拖拽通过八个点改变div大小
Nov 29 Javascript
jquery平滑滚动到顶部插件使用详解
May 08 jQuery
解决jquery有正确返回值但不执行success函数的问题
Aug 20 jQuery
对VUE中的对象添加属性
Sep 18 Javascript
JavaScript HTML DOM元素 节点操作汇总
Jul 29 Javascript
微信内置开发 iOS修改键盘换行为搜索的解决方案
Nov 06 Javascript
vue组件创建的三种方式小结
Feb 03 Javascript
Vant Weapp组件踩坑:picker的初始赋值解决
Nov 12 Javascript
JS函数节流和函数防抖问题分析
Dec 18 #Javascript
vue 将页面公用的头部组件化的方法
Dec 18 #Javascript
浅谈使用React.setState需要注意的三点
Dec 18 #Javascript
vue 项目如何引入微信sdk接口的方法
Dec 18 #Javascript
微信小程序实现给嵌套template模板传递数据的方式总结
Dec 18 #Javascript
微信小程序实现页面跳转传值以及获取值的方法分析
Dec 18 #Javascript
10个在JavaScript开发中常遇到的BUG
Dec 18 #Javascript
You might like
用js进行url编码后用php反解以及用php实现js的escape功能函数总结
2010/02/08 PHP
PHP 显示客户端IP与服务器IP的代码
2010/10/12 PHP
php数据序列化测试实例详解
2017/08/12 PHP
PHP实现长轮询消息实时推送功能代码实例讲解
2021/02/26 PHP
Javascript全局变量var与不var的区别深入解析
2013/12/09 Javascript
js对文章内容进行分页示例代码
2014/03/05 Javascript
jQuery中dequeue()方法用法实例
2014/12/29 Javascript
JS实现表格数据各种搜索功能的方法
2015/03/03 Javascript
动态加载js文件简单示例
2016/04/21 Javascript
AngularJs表单验证实例代码解析
2016/11/29 Javascript
Require.JS中的几种define定义方式示例
2017/06/01 Javascript
纯JS实现简单的日历
2017/06/26 Javascript
Three.js开发实现3D地图的实践过程总结
2017/11/20 Javascript
详解Angular操作cookies方法
2018/06/01 Javascript
Async/Await替代Promise的6个理由
2019/06/15 Javascript
es6 for循环中let和var区别详解
2020/01/12 Javascript
微信小程序vant弹窗组件的实现方式
2020/02/21 Javascript
python连接MySQL数据库实例分析
2015/05/12 Python
python dataframe astype 字段类型转换方法
2018/04/11 Python
详解Django之admin组件的使用和源码剖析
2018/05/04 Python
使用python socket分发大文件的实现方法
2019/07/08 Python
Python CategoricalDtype自定义排序实现原理解析
2020/09/11 Python
10个python爬虫入门基础代码实例 + 1个简单的python爬虫完整实例
2020/12/16 Python
实例讲解使用CSS实现多边框和透明边框的方法
2015/09/08 HTML / CSS
使用CSS3的box-sizing属性解决div宽高被内边距撑开的问题
2016/06/28 HTML / CSS
波兰在线香水店:Perfumy.pl
2019/08/12 全球购物
委托与事件是什么关系?为什么要使用委托
2014/04/18 面试题
百度软件工程师职位
2013/02/14 面试题
Python中如何定义一个函数
2016/09/06 面试题
企业宗旨标语
2014/06/10 职场文书
迟到检讨书2000字(精选篇)
2014/10/07 职场文书
慰问信范文
2015/02/14 职场文书
办公用品质量保证书
2015/05/11 职场文书
2015年学校信息技术工作总结
2015/05/25 职场文书
汽车车尾标语大全
2015/08/11 职场文书
详解Redis的三种常用的缓存读写策略步骤
2022/05/06 Redis