赞
踩
下面对threejs进行简单科普介绍,先说下threejs可以做什么。
参考资料:Three.js中文网
在人与人之间联系的互联网时代,主要是满足人与人之间的交流,Web页面的交互界面主要呈现为2D的交互效果,比如按钮、输入框等。
随着物联网的发展,工业、建筑等各个领域与物联网相关Web项目网页交互界面都会呈现出3D化的趋势。物联网相比较传统互联网更强调的是人与物、物与物的联系。
物联网数字孪生:收费站——预览地址
智慧城市——预览地址
智慧小区——预览地址
机械、BIM领域在线展示交互——预览地址
地球飞线大屏3D可视化——预览地址
中国地图3D数据可视化——预览地址
轿车展品720展示——预览地址
全景室内效果——预览地址
在正式学习Three.js之前,先做一些必要的准备工作,具体说就是下载threejs官方文件包,threejs官方文件包提供了很多有用的学习资源。
Three.js处于飞速发展之中,过去几年和现在Three.js基本上每个月都会发布一个新的版本,主要是增加一些新的功能,也可能废弃或更名某些API。
threejs官方文件包所有版本:https://github.com/mrdoob/three.js/releases
Threejs中文网(电子书课件):www.webgl3d.cn
three.js官网可以下载three.js文件包,不过默认是最新版本的,three.js官网的文档一般也是当前最新版本。
因为threejs每月更新的时候,API会有变化,我的建议是,实际开发的时候,three.js API的使用规则,一切以你项目threejs版本对应的文档为准,下节课会具体给大家说怎么在本地预览任何你想要的版本文档。
Threejs官网中文文档链接:https://threejs.org/docs/index.html#manual/zh/
github链接查看所有版本threejs:https://github.com/mrdoob/three.js/releases
选择你需要的版本three.js文件包下载,然后解压,就可以预览里面的很多学习资源。
如果你那边gihtub打不开或下载很慢,你可以去three.js中文网提供网盘资源下载。
网盘链接:https://pan.baidu.com/s/1_Ix8TiOScypNcQe3BIl5vA?pwd=rrks 提取码:rrks
下载three.js文件包解压后,你可以看到如下目录(不同版本会略有差异)。
对于开发者而言,大家经常接触的是文档docs和案例examples两个文件夹,平时查看文档,可以打开文档docs里面html文件,案例examples里面提供了海量threejs功能案例。
three.js-文件包
└───build——three.js相关库,可以引入你的.html文件中。
│
└───docs——Three.js API文档文件
│───index.html——打开该文件,本地离线方式预览threejs文档
└───examples——大量的3D案例,是你平时开发参考学习的最佳资源
│───jsm——threejs各种功能扩展库
└───src——Three.js引擎的源码,有兴趣可以阅读。
│
└───editor——Three.js的可视化编辑器,可以编辑3D场景
│───index.html——打开应用程序
本节课主要给大家说下threejs怎么引入的问题。
比如你采用的是Vue + threejs或React + threejs技术栈,这很简单,threejs就是一个js库,直接通过npm命令行安装就行。
npm安装特定版本three.js(注意使用哪个版本,查文档就查对应版本)
// 比如安装148版本
npm install three@0.148.0 --save
执行import * as THREE from 'three';
,ES6语法引入three.js核心。
// 引入three.js
import * as THREE from 'three';
如果你没有前端ES6基础,可以参考我分享的前端公开课ES6入门教程。
除了three.js核心库以外,在threejs文件包中examples/jsm目录下,你还可以看到各种不同功能的扩展库。
一般来说,你项目用到那个扩展库,就引入那个,用不到就不需要引入。
// 引入扩展库OrbitControls.js
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
// 引入扩展库GLTFLoader.js
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
// 扩展库引入——旧版本,比如122, 新版本路径addons替换了examples/jsm
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
如果不是正式开发Web3D项目,只是学习threejs功能,完全没必要用webpack或vite搭建一个开发环境。
学习使用的环境,只要创建一个.html文件,编写threejs代码,最后通过本地静态服务打开.html文件就行。
你可以像平时开发web前端项目一样,通过script标签把three.js当做一个js库引入你的项目。
three.js库可以在threejs官方文件包下面的build目录获取到。
<script src="./build/three.js"></script>
//随便输入一个API,测试下是否已经正常引入three.js
console.log(THREE.Scene);
给script标签设置type="module"
,也可以在.html文件中使用import
方式引入three.js。
<script type="module">
// 现在浏览器支持ES6语法,自然包括import方式引入js文件
import * as THREE from './build/three.module.js';
</script>
学习环境中,.html文件引入three.js,最好的方式就是参考threejs官方案例,通过配置<script type="importmap">
,实现学习环境.html文件和vue或reaact脚手架开发环境一样的写法。这样你实际项目的开发环境复制课程源码,不用改变threejs引入代码。
下面配置的type="importmap"
代码具体写法不用掌握记忆,复制粘贴后,能修改目录就行,你可以去电子书课件或者课件源码中复制。
<!-- 具体路径配置,你根据自己文件目录设置,我的是课件中源码形式 -->
<script type="importmap">
{
"imports": {
"three": "../../../three.js/build/three.module.js"
}
}
</script>
<!-- 配置type="importmap",.html文件也能和项目开发环境一样方式引入threejs -->
<script type="module">
import * as THREE from 'three';
// 浏览器控制台测试,是否引入成功
console.log(THREE.Scene);
</script>
通过配置<script type="importmap">
,让学习环境.html文件,也能和vue或react开发环境中一样方式方式引入threejs扩展库。
配置addons/
等价于examples/jsm/
。
<script type="importmap">
{
"imports": {
"three": "./three.js/build/three.module.js",
"three/addons/": "./three.js/examples/jsm/"
}
}
</script>
<script type="module">
// three/addons/路径之后对应的是three.js官方文件包`/examples/jsm/`中的js库
// 扩展库OrbitControls.js
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
// 扩展库GLTFLoader.js
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
console.log(OrbitControls);
console.log(GLTFLoader);
</script>
入门Three.js的第一步,就是认识场景Scene、相机Camera、渲染器Renderer三个基本概念,接下来,咱们通过三小节课,大家演示“第一个3D案例”完成实现过程。
学习建议:只要你能把第一个3D案例搞明白,后面学习就会非常顺利了。
本节课先完成第一个3D案例的一部分,也就是3D场景Scene的创建。
Scene
你可以把三维场景Scene对象理解为虚拟的3D场景,用来表示模拟生活中的真实三维场景,或者说三维世界。
// 创建3D场景对象Scene
const scene = new THREE.Scene();
Geometry
Three.js提供了各种各样的几何体API,用来表示三维物体的几何形状。
文档搜索关键词geometry
你可以看到threejs提供各种几何体相关API,具体使用方法,也可以参考文档。
//创建一个长方体几何对象Geometry
const geometry = new THREE.BoxGeometry(100, 100, 100);
Material
如果你想定义物体的外观效果,比如颜色,就需要通过材质Material
相关的API实现。
threejs不同材质渲染效果不同,下面就以threejs最简单的网格基础材质MeshBasicMaterial为例给大家实现一个红色材质效果。
//创建一个材质对象Material
const material = new THREE.MeshBasicMaterial({
color: 0xff0000,//0xff0000设置材质颜色为红色
});
Mesh
实际生活中有各种各样的物体,在threejs中可以通过网格模型Mesh表示一个虚拟的物体,比如一个箱子、一个鼠标。
// 两个参数分别为几何体geometry、材质material
const mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
.position
实际生活中,一个物体往往是有位置的,对于threejs而言也是一样的,你可以通过位置属性.position
定义网格模型Mesh
在三维场景Scene
中的位置。
const mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
//设置网格模型在三维空间中的位置坐标,默认是坐标原点
mesh.position.set(0,10,0);
.add()
方法在threejs中你创建了一个表示物体的虚拟对象Mesh,需要通过.add()
方法,把网格模型mesh
添加到三维场景scene
中。
scene.add(mesh);
写到这里,我知道你已经迫不及待想执行代码看看渲染效果了,那么你需要看看后面两节课关于相机Camera和渲染器Renderer的介绍
Threejs如果想把三维场景Scene
渲染到web网页上,还需要定义一个虚拟相机Camera
,就像你生活中想获得一张照片,需要一台用来拍照的相机。
PerspectiveCamera
Threejs提供了正投影相机OrthographicCamera和透视投影相机PerspectiveCamera。本节课先给大家比较常用的透视投影相机PerspectiveCamera
。
透视投影相机PerspectiveCamera
本质上就是在模拟人眼观察这个世界的规律。
// 实例化一个透视投影相机对象
const camera = new THREE.PerspectiveCamera();
.position
生活中用相机拍照,你相机位置不同,拍照结果也不同,threejs中虚拟相机同样如此。
比如有一间房子,你拿着相机站在房间里面,看到的是房间内部,站在房子外面看到的是房子外面效果。
相机对象Camera
具有位置属性.position
,通过位置属性.position
可以设置相机的位置。
//相机在Three.js三维坐标系中的位置
// 根据需要设置相机位置具体值
camera.position.set(200, 200, 200);
.lookAt()
你用相机拍照你需要控制相机的拍照目标,具体说相机镜头对准哪个物体或说哪个坐标。对于threejs相机而言,就是设置.lookAt()
方法的参数,指定一个3D坐标。
//相机观察目标指向Threejs 3D空间中某个位置
camera.lookAt(0, 0, 0); //坐标原点
camera.lookAt(0, 10, 0); //y轴上位置10
camera.lookAt(mesh.position);//指向mesh对应的位置
你可以把三维场景中长方体mesh
想象为一个房间,然后根据相机位置和长方体位置尺寸对比,判断两者相对位置。你可以发现设置相机坐标(200, 200, 200),位于长方体外面一处位置。
// 长方体尺寸100, 100, 100
const geometry = new THREE.BoxGeometry( 100, 100, 100 );
const mesh = new THREE.Mesh(geometry,material);
// 网格模型位置xyz坐标:0,10,0
mesh.position.set(0,10,0);
// 相机位置xyz坐标:200, 200, 200
camera.position.set(200, 200, 200);
你生活中相机拍照的照片是有大小的,对于threejs而言一样,需要定义相机在网页上输出的Canvas画布(照片)尺寸,大小可以根据需要定义,这里先随机定义一个尺寸。
Canvas画布:课程中会把threejs虚拟相机渲染三维场景在浏览器网页上呈现的结果称为Canvas画布。
// 定义相机输出画布的尺寸(单位:像素px)
const width = 800; //宽度
const height = 500; //高度
PerspectiveCamera
:视锥体透视投影相机的四个参数fov, aspect, near, far
构成一个四棱台3D空间,被称为视锥体,只有视锥体之内的物体,才会渲染出来,视锥体范围之外的物体不会显示在Canvas画布上。
// width和height用来设置Three.js输出的Canvas画布尺寸(像素px)
const width = 800; //宽度
const height = 500; //高度
// 30:视场角度, width / height:Canvas画布宽高比, 1:近裁截面, 3000:远裁截面
const camera = new THREE.PerspectiveCamera(30, width / height, 1, 3000);
PerspectiveCamera
参数介绍:
PerspectiveCamera( fov, aspect, near, far )
参数 | 含义 | 默认值 |
---|---|---|
fov | 相机视锥体竖直方向视野角度 | 50 |
aspect | 相机视锥体水平方向和竖直方向长度比,一般设置为Canvas画布宽高比width / height | 1 |
near | 相机视锥体近裁截面相对相机距离 | 0.1 |
far | 相机视锥体远裁截面相对相机距离,far-near构成了视锥体高度方向 | 2000 |
生活中如果有了景物和相机,那么如果想获得一张照片,就需要你拿着相机,按一下,咔,完成拍照。对于threejs而言,如果完成“咔”这个拍照动作,就需要一个新的对象,也就是WebGL渲染器WebGLRenderer。
WebGLRenderer
通过WebGL渲染器WebGLRenderer可以实例化一个WebGL渲染器对象。
// 创建渲染器对象
const renderer = new THREE.WebGLRenderer();
.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); //执行渲染操作
.domElement
渲染器WebGLRenderer
通过属性.domElement
可以获得渲染方法.render()
生成的Canvas画布,.domElement
本质上就是一个HTML元素:Canvas画布。
document.body.appendChild(renderer.domElement);
<div id="webgl" style="margin-top: 200px;margin-left: 100px;"></div>
document.getElementById('webgl').appendChild(renderer.domElement);
本节课的目的就是为了加强大家对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);
实际生活中物体表面的明暗效果是会受到光照的影响,比如晚上不开灯,你就看不到物体,灯光比较暗,物体也比较暗。在threejs中,咱们用网格模型Mesh
模拟生活中物体,所以threejs中模拟光照Light
对物体表面的影响,就是模拟光照Light
对网格模型Mesh
表面的影响。
你可以打开课件中案例源码,对比有光照和无光照两种情况,网格模型Mesh
表面的差异。
threejs提供的网格材质,有的受光照影响,有的不受光照影响。
基础网格材质MeshBasicMaterial不会受到光照影响。
//MeshBasicMaterial不受光照影响
const material = new THREE.MeshBasicMaterial();
漫反射网格材质MeshLambertMaterial会受到光照影响,该材质也可以称为Lambert网格材质,音译为兰伯特网格材质。
一个立方体长方体使用MeshLambertMaterial材质,不同面和光线夹角不同,立方体不同面就会呈现出来不同的明暗效果。
//MeshLambertMaterial受光照影响
const material = new THREE.MeshLambertMaterial();
Three.js提供了多种模拟生活中光源的API,文档搜索关键词light就可以看到。
点光源PointLight可以类比为一个发光点,就像生活中一个灯泡以灯泡为中心向四周发射光线。
//点光源:两个参数分别表示光源颜色和光照强度
// 参数1:0xffffff是纯白光,表示光源颜色
// 参数2:1.0,表示光照强度,可以根据需要调整
const pointLight = new THREE.PointLight(0xffffff, 1.0);
除了通过THREE.PointLight
的参数2设置光照强度,你可以可以直接访问光照强度属性.intensity
设置。
pointLight.intensity = 1.0;//光照强度
实际生活中点光源,比如比如一个灯泡,随机距离的改变,光线会衰减,越来越弱,光源衰减属性.decay
默认值是2.0,如果你不希望衰减可以设置为0.0
。
pointLight.decay = 0.0;//设置光源不随距离衰减
【扩展提醒】:如果使用默认衰减2.0
,不同版本可能有差异,对于部分threejs新版本,有时候你可能看不到光源效果,这时候可以把光照强度加强试试看,如果你的版本不影响,就不用加强光照强度(根据版本情况灵活对应)。
// 你可以对比不同光照强度明暗差异(传播同样距离)
pointLight.intensity = 10000.0;//光照强度
pointLight.intensity = 50000.0;//光照强度
你把点光源想象为一个电灯泡,你在3D空间中,放的位置不同,模型的渲染效果就不一样。
注意光源位置尺寸大小:如果你希望光源照在模型的外表面,那你就需要把光源放在模型的外面。
//点光源位置
pointLight.position.set(400, 0, 0);//点光源放在x轴上
光源和网格模型Mesh对应一样是三维场景的一部分,自然需要添加到三维场景中才能起作用。
scene.add(pointLight); //点光源添加到场景中
设置好上面所有代码,你现在可以执行代码,用浏览器查看渲染效果。
pointLight.position.set(400, 200, 300);
平时开发调试代码,或者展示模型的时候,可以通过相机控件OrbitControls实现旋转缩放预览效果。
你可以打开课件案例源码测试下效果。
// 引入轨道控制器扩展库OrbitControls.js
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
注意:如果你在原生.html文件中,使用上面引入方式import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
,注意通过<script type="importmap">
配置。
<script type="importmap">
{
"imports": {
"three": "../../../three.js/build/three.module.js",
"three/addons/": "../../../three.js/examples/jsm/"
}
}
</script>
// 设置相机控件轨道控制器OrbitControls
const controls = new OrbitControls(camera, renderer.domElement);
// 如果OrbitControls改变了相机参数,重新调用渲染器渲染三维场景
controls.addEventListener('change', function () {
renderer.render(scene, camera); //执行渲染操作
});//监听鼠标、键盘事件
OrbitControls本质上就是改变相机的参数,比如相机的位置属性,改变相机位置也可以改变相机拍照场景中模型的角度,实现模型的360度旋转预览效果,改变透视投影相机距离模型的距离,就可以改变相机能看到的视野范围。
controls.addEventListener('change', function () {
// 浏览器控制台查看相机位置变化
console.log('camera.position',camera.position);
});
本节课通过平行光DirectionalLight和环境光AmbientLight进一步了解光照对应模型Mesh表面的影响。
PointLightHelper
通过点光源辅助观察对象PointLightHelper可视化点光源。
预览观察:可以借助相机控件OrbitControls
旋转缩放三维场景便于预览点光源位置
// 光源辅助观察
const pointLightHelper = new THREE.PointLightHelper(pointLight, 10);
scene.add(pointLightHelper);
改变点光源位置,观察光照效果变化。
pointLight.position.set(100, 60, 50);
// 改变点光源位置,使用OrbitControls辅助观察
pointLight.position.set(-400, -200, -300);
环境光AmbientLight没有特定方向,只是整体改变场景的光照明暗。
//环境光:没有特定方向,整体改变场景的光照明暗
const ambient = new THREE.AmbientLight(0xffffff, 0.4);
scene.add(ambient);
平行光DirectionalLight就是沿着特定方向发射。
// 平行光
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
// 设置光源的方向:通过光源position属性和目标指向对象的position属性计算
directionalLight.position.set(80, 100, 50);
// 方向光指向对象网格模型mesh,可以不设置,默认的位置是0,0,0
directionalLight.target = mesh;
scene.add(directionalLight);
DirectionalLightHelper
通过点光源辅助观察对象DirectionalLightHelper可视化点光源。
// DirectionalLightHelper:可视化平行光
const dirLightHelper = new THREE.DirectionalLightHelper(directionalLight, 5,0xff0000);
scene.add(dirLightHelper);
平行光照射到网格模型Mesh表面,光线和模型表面构成一个入射角度,入射角度不同,对光照的反射能力不同。
光线照射到漫反射网格材质MeshLambertMaterial对应Mesh表面,Mesh表面对光线反射程度与入射角大小有关。
// 对比不同入射角,mesh表面对光照的反射效果
directionalLight.position.set(100, 0, 0);
directionalLight.position.set(0, 100, 0);
directionalLight.position.set(100, 100, 100);
directionalLight.position.set(100, 60, 50);
//directionalLight.target默认指向坐标原点
threejs可以借助HTML5的API请求动画帧window.requestAnimationFrame
实现动画渲染。
window.requestAnimationFrame
// requestAnimationFrame实现周期性循环执行
// requestAnimationFrame默认每秒钟执行60次,但不一定能做到,要看代码的性能
let i = 0;
function render() {
i+=1;
console.log('执行次数'+i);
requestAnimationFrame(render);//请求再次执行函数render
}
render();
备注说明:对于部分高刷新率的电脑硬件设备,.requestAnimationFrame
每秒钟默认调用函数执行次数也是有可能超过60次的,比如你的电脑显卡、显示器等硬件能够支持144hz刷新频率,.requestAnimationFrame
的每秒执行上限,也可以接近144帧率。
动画说白了就是一张张照片,连起来依次展示,这样就形成一个动画效果,只要帧率高,人的眼睛就感觉不到卡顿,是连续的视频效果。
const renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
// renderer.render(scene, camera); //执行渲染操作
document.body.appendChild(renderer.domElement);
// 渲染函数
function render() {
renderer.render(scene, camera); //执行渲染操作
mesh.rotateY(0.01);//每次绕y轴旋转0.01弧度
requestAnimationFrame(render);//请求再次执行渲染函数render,渲染下一帧
}
render();
// 渲染循环
const clock = new THREE.Clock();
function render() {
const spt = clock.getDelta()*1000;//毫秒
console.log('两帧渲染时间间隔(毫秒)',spt);
console.log('帧率FPS',1000/spt);
renderer.render(scene, camera); //执行渲染操作
mesh.rotateY(0.01);//每次绕y轴旋转0.01弧度
requestAnimationFrame(render);//请求再次执行渲染函数render,渲染下一帧
}
render();
设置了渲染循环,相机控件OrbitControls就不用再通过事件change
执行renderer.render(scene, camera);
,毕竟渲染循环一直在执行renderer.render(scene, camera);
。
threejs渲染输出的结果就是一个Cavnas画布,canvas画布也是HTML的元素之一,这意味着three.js渲染结果的布局和普通web前端习惯是一样的。
通过renderer.domElement
属性可以访问threejs的渲染结果,也就是HTML的元素canvas
画布。
你可以把threejs的渲染结果renderer.domElement
,插入到web页面上任何一个元素中,只要符合你项目的布局规则即可。
<div id="webgl" style="margin-top: 100px;margin-left: 200px;"></div> <script type="module"> // width和height用来设置Three.js输出的Canvas画布尺寸(像素px) const width = 800; //宽度 const height = 500; //高度 const camera = new THREE.PerspectiveCamera(30, width / height, 1, 3000); /** * 创建渲染器对象 */ const renderer = new THREE.WebGLRenderer(); renderer.setSize(width, height); //设置three.js渲染区域的尺寸(像素px) renderer.render(scene, camera); //执行渲染操作 //three.js执行渲染命令会输出一个canvas画布,也就是一个HTML元素,你可以插入到web页面中 // document.body.appendChild(renderer.domElement); document.getElementById('wegbl').appendChild(renderer.domElement);
// width和height用来设置Three.js输出的Canvas画布尺寸(像素px)
const width = window.innerWidth; //窗口文档显示区的宽度作为画布宽度
const height = window.innerHeight; //窗口文档显示区的高度作为画布高度
const renderer = new THREE.WebGLRenderer();
document.body.appendChild(renderer.domElement);
全屏布局注意CSS的设置。
<style>
body{
overflow: hidden;
margin: 0px;
}
</style>
canvas画布宽高度动态变化,需要更新相机和渲染的参数,否则无法正常渲染。
// onresize 事件会在窗口被调整大小时发生
window.onresize = function () {
// 重置渲染器输出画布canvas尺寸
renderer.setSize(window.innerWidth, window.innerHeight);
// 全屏情况下:设置观察范围长宽比aspect为窗口宽高比
camera.aspect = window.innerWidth / window.innerHeight;
// 渲染器执行render方法的时候会读取相机对象的投影矩阵属性projectionMatrix
// 但是不会每渲染一帧,就通过相机的属性计算投影矩阵(节约计算资源)
// 如果相机的一些属性发生了变化,需要执行updateProjectionMatrix ()方法更新相机的投影矩阵
camera.updateProjectionMatrix();
};
three.js每执行WebGL渲染器.render()
方法一次,就在canvas画布上得到一帧图像,不停地周期性执行.render()
方法就可以更新canvas画布内容,一般场景越复杂往往渲染性能越低,也就是每秒钟执行.render()
的次数越低。
通过stats.js库可以查看three.js当前的渲染性能,具体说就是计算three.js的渲染帧率(FPS),所谓渲染帧率(FPS),简单说就是three.js每秒钟完成的渲染次数,一般渲染达到每秒钟60次为最佳状态。
stats.js下载链接:https://github.com/mrdoob/stats.js
//引入性能监视器stats.js
import Stats from 'three/addons/libs/stats.module.js';
//创建stats对象
const stats = new Stats();
//stats.domElement:web页面上输出计算结果,一个div元素,
document.body.appendChild(stats.domElement);
// 渲染函数
function render() {
//requestAnimationFrame循环调用的函数中调用方法update(),来刷新时间
stats.update();
renderer.render(scene, camera); //执行渲染操作
requestAnimationFrame(render); //请求再次执行渲染函数render,渲染下一帧
}
render();
可以通过setMode()
方法的参数mode的数值设置首次打开页面,测试结果的显示模式,鼠标单击可以更换不同的显示模式。
// stats.domElement显示:渲染帧率 刷新频率,一秒渲染次数
stats.setMode(0);//默认模式
//stats.domElement显示:渲染周期 渲染一帧多长时间(单位:毫秒ms)
stats.setMode(1);
控制长方体模型数量,你可以逐渐增加或减少,看看帧率变化,电脑性能不同结果不同。
// 随机创建大量的模型,测试渲染性能
const num = 1000; //控制长方体模型数量
for (let i = 0; i < num; i++) {
const geometry = new THREE.BoxGeometry(5, 5, 5);
const material = new THREE.MeshLambertMaterial({
color: 0x00ffff
});
const mesh = new THREE.Mesh(geometry, material);
// 随机生成长方体xyz坐标
const x = (Math.random() - 0.5) * 200
const y = (Math.random() - 0.5) * 200
const z = (Math.random() - 0.5) * 200
mesh.position.set(x, y, z)
scene.add(mesh); // 模型对象插入场景中
}
本节课通过阵列一片立方体,进一步体验下透视投影相机的投影规律。
const geometry = new THREE.BoxGeometry(100, 100, 100);
//材质对象Material
const material = new THREE.MeshLambertMaterial({
color: 0x00ffff, //设置材质颜色
transparent: true,//开启透明
opacity: 0.5,//设置透明度
});
for (let i = 0; i < 10; i++) {
const mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
// 沿着x轴分布
mesh.position.set(i*200,0,0);
scene.add(mesh); //网格模型添加到场景中
}
//创建一个长方体几何对象Geometry const geometry = new THREE.BoxGeometry(100, 100, 100); //材质对象Material const material = new THREE.MeshLambertMaterial({ color: 0x00ffff, //设置材质颜色 transparent: true,//开启透明 opacity: 0.5,//设置透明度 }); for (let i = 0; i < 10; i++) { for (let j = 0; j < 10; j++) { const mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh // 在XOZ平面上分布 mesh.position.set(i * 200, 0, j * 200); scene.add(mesh); //网格模型添加到场景中 } }
const camera = new THREE.PerspectiveCamera(30, width / height, 1, 3000);
// camera.position.set(292, 223, 185);
//在原来相机位置基础上拉远,可以观察到更大的范围
camera.position.set(800, 800, 800);
camera.lookAt(0, 0, 0);
// const camera = new THREE.PerspectiveCamera(30, width / height, 1, 3000);
const camera = new THREE.PerspectiveCamera(30, width / height, 1, 8000);
// camera.position.set(292, 223, 185);
// 超出视锥体远裁界面的范围的会被剪裁掉,不渲染 可以调整far参数适配
camera.position.set(2000, 2000, 2000);
camera.lookAt(0, 0, 0);
// const camera = new THREE.PerspectiveCamera(30, width / height, 1, 3000);
const camera = new THREE.PerspectiveCamera(30, width / height, 1, 8000);
camera.position.set(2000, 2000, 2000);
// camera.lookAt(0, 0, 0);
// 改变相机观察目标点
camera.lookAt(1000, 0, 1000);
注意相机控件OrbitControls会影响lookAt设置,注意手动设置OrbitControls的目标参数
// 设置相机控件轨道控制器OrbitControls
const controls = new OrbitControls(camera, renderer.domElement);
// 相机控件.target属性在OrbitControls.js内部表示相机目标观察点,默认0,0,0
// console.log('controls.target', controls.target);
controls.target.set(1000, 0, 1000);
controls.update();//update()函数内会执行camera.lookAt(controls.targe)
透视投影相机的投影规律是远小近大,通过相机观察阵列立方体大小变化,可以看到距离相机越远,立方体的渲染视觉效果越小。
增加相机视角fov,视锥体范围更大,意味着可以看到渲染范围更大,远小近大的视觉效果更明显。
Three.js提供的几何体API很多,本节课先给大家介绍几个比较简单的案例,为后面的学习打下基础。
你可以结合threejs文档,把下面动手把下面几何体相关代码全部测试一遍,并预览3D效果。
//BoxGeometry:长方体
const geometry = new THREE.BoxGeometry(100, 100, 100);
// SphereGeometry:球体
const geometry = new THREE.SphereGeometry(50);
// CylinderGeometry:圆柱
const geometry = new THREE.CylinderGeometry(50,50,100);
// PlaneGeometry:矩形平面
const geometry = new THREE.PlaneGeometry(100,50);
// CircleGeometry:圆形平面
const geometry = new THREE.CircleGeometry(50);
Three.js的材质默认正面可见,反面不可见,对于矩形平面PlaneGeometry
、圆形平面如果你想看到两面,可以设置side: THREE.DoubleSide
。
new THREE.MeshBasicMaterial({
side: THREE.FrontSide, //默认只有正面可见
});
new THREE.MeshBasicMaterial({
side: THREE.DoubleSide, //两面可见
});
MeshPhongMaterial
高光网格材质MeshPhongMaterial
和基础网格材质MeshBasicMaterial
、漫反射网格材质MeshLambertMaterial
一样都是网格模型的Mesh
的材质。
高光网格材质MeshPhongMaterial和漫反射网格材质MeshLambertMaterial一样会受到光照的影响。
MeshPhongMaterial
对光照反射特点MeshPhongMaterial
和MeshLambertMaterial
都会收到光照的影响区别在于,对光线反射方式有差异。
MeshPhongMaterial
可以实现MeshLambertMaterial
不能实现的高光反射效果。对于高光效果,你可以想象一下,你在太阳下面观察一辆车,你会发现在特定角度和位置,你可以看到车表面某个局部区域非常高亮。
MeshPhongMaterial
可以提供一个镜面反射效果,可以类比你生活中拿一面镜子,放在太阳光下,调整角度,可以把太阳光反射到其它地方,如果反射光对着眼睛,也就是反射光线和视线平行的时候,会非常刺眼。
MeshLambertMaterial
对应的Mesh受到光线照射,没有镜面反射的效果,只是一个漫反射,也就是光线向四周反射。
.shininess
通过MeshPhongMaterial
的高光亮度.shininess
属性,可以控制高光反射效果。
// 模拟镜面反射,产生一个高光效果
const material = new THREE.MeshPhongMaterial({
color: 0xff0000,
shininess: 20, //高光部分的亮度,默认30
});
.specular
可以给颜色属性.specular
设置不同的值,比如0x444444
、0xfffffff
查看渲染效果变化。
// 模拟镜面反射,产生一个高光效果
const material = new THREE.MeshPhongMaterial({
color: 0xff0000,
shininess: 20, //高光部分的亮度,默认30
specular: 0x444444, //高光部分的颜色
});
一般实际开发,threejs的WebGL渲染器需要进行一些通用的基础配置,本节课给大家简单介绍下,比如渲染模糊或锯齿问题。
.antialias
设置渲染器锯齿属性.antialias
的值可以直接在参数中设置,也可通过渲染器对象属性设置。
const renderer = new THREE.WebGLRenderer({
antialias:true,
});
或
renderer.antialias = true,
window.devicePixelRatio
如果你有web前端基础,应该了解window
对象,设备像素比.devicePixelRatio
是window对象的一个属性,该属性的值和你的硬件设备屏幕相关,不同硬件设备的屏幕window.devicePixelRatio
的值可能不同,可能就是1、1.5、2.0等其它值。
// 不同硬件设备的屏幕的设备像素比window.devicePixelRatio值可能不同
console.log('查看当前屏幕设备像素比',window.devicePixelRatio);
.setPixelRatio()
如果你遇到你的canvas画布输出模糊问题,注意设置renderer.setPixelRatio(window.devicePixelRatio)
。
注意:注意你的硬件设备设备像素比window.devicePixelRatio
刚好是1,那么是否执行.setPixelRatio()
不会有明显差异,不过为了适应不同的硬件设备屏幕,通常需要执行该方法。
// 获取你屏幕对应的设备像素比.devicePixelRatio告诉threejs,以免渲染模糊问题
renderer.setPixelRatio(window.devicePixelRatio);
.setClearColor()
renderer.setClearColor(0x444444, 1); //设置背景颜色
dat.gui.js说白了就是一个前端js库,对HTML、CSS和JavaScript进行了封装,学习开发的时候,借助dat.gui.js可以快速创建控制三维场景的UI交互界面,你打开课件中案例源码体验一下就能感受到。
课程学习dat.gui.js也不仅仅是为了学习dat.gui.js,也是建立一种思想,就是threejs三维空间的很多参数,不是心算出来的,往往需要可视化的方式调试出来。
gihtub地址:https://github.com/dataarts/dat.gui
npm地址:https://www.npmjs.com/package/dat.gui
你可以通过npm或github方式获得dat.gui.js库,当然为了学习方便,threejs官方案例扩展库中也提供了gui.js,你可以直接使用。
// 引入dat.gui.js的一个类GUI
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
创建一个GUI对象,你可以看到浏览器右上角多了一个交互界面,GUI本质上就是一个前端js库。
// 实例化一个gui对象
const gui = new GUI();
.domElement
:改变GUI界面默认的style属性通过.domElement
属性可以获取gui界面的HTML元素,那就意味着你可以改变默认的style样式,比如位置、宽度等。
//改变交互界面style属性
gui.domElement.style.right = '0px';
gui.domElement.style.width = '300px';
.add()
方法执行gui的.add()
方法可以快速创建一个UI交互界面,比如一个拖动条,可以用来改变一个JavaScript对象属性的属性值。
格式:.add(控制对象,对象具体属性,其他参数)
其他参数,可以一个或多个,数据类型也可以不同,gui会自动根据参数形式,自动生成对应的交互界面。
参数3和参数4,分别是一个数字,交互界面是一个鼠标可以拖动的拖动条,可以在一个区间改变属性的值
执行gui.add(obj, 'x', 0, 100);
你可以发现右上角gui界面增加了新的内容,可以控制obj对象x属性的新交互界面。
//创建一个对象,对象属性的值可以被GUI库创建的交互界面改变
const obj = {
x: 30,
};
// gui增加交互界面,用来改变obj对应属性
gui.add(obj, 'x', 0, 100);
.add()
功能——改变对象属性值为了方便观察.add()
是如何改变JavaScript对象属性的,可以浏览器控制台不停地打印obj的值,这样通过gui界面拖动改变obj对象属性的的时候,便于观察obj的变化。
const obj = {x: 30};
setInterval(function () {
console.log('x', obj.x);
}, 10)
const obj = {
x: 30,
y: 60,
z: 300,
};
// gui界面上增加交互界面,改变obj对应属性
gui.add(obj, 'x', 0, 100);
gui.add(obj, 'y', 0, 50);
gui.add(obj, 'z', 0, 60);
three.js在调试场景渲染效果的时候,比如光照的强度,人大脑的CPU是没有能力通过光照参数算出来模型渲染效果的,一般来说你先大概给一个经验值,然后通过gui在这个大概值的基础上下浮动可视化调试。
光源对象具有一个光照强度属性.intensity
,可以通过gui拖动条改变该属性。
// 光照强度属性.intensity
console.log('ambient.intensity',ambient.intensity);
// 通过GUI改变mesh.position对象的xyz属性
gui.add(ambient, 'intensity', 0, 2.0);
mesh.position
是JavaScript对象,具有x
、y
、z
属性,这三个属性分别表示模型的xyz坐标,这就是说,gui改变mesh.position
的x
、y
、z
属性,就可以可视化改变mesh的位置。
gui.add(mesh.position, 'x', 0, 180);
gui.add(mesh.position, 'y', 0, 180);
gui.add(mesh.position, 'z', 0, 180);
本节课结合threejs,给大家介绍gui.js库更多的方法。
.name()
方法.add()
创建的交互界面,会默认显示所改变属性的名字,为了通过交互界面更好理解你改变的某个对象属性,你可以通过.name()
方法改变gui生成交互界面显示的内容。
const gui = new GUI();//创建GUI对象
gui.add(ambient, 'intensity', 0, 2.0).name('环境光强度');
gui.add(directionalLight, 'intensity', 0, 2.0).name('平行光强度');
.step()
方法步长.step()
方法可以设置交互界面每次改变属性值间隔是多少。
gui.add(ambient, 'intensity', 0, 2.0).name('环境光强度').step(0.1);
.onChange()
方法当gui界面某个值的时候,.onChange()
方法就会执行,这时候你可以根据需要通过.onChange()
执行某些代码。
const obj = {
x: 30,
};
// 当obj的x属性变化的时候,就把此时obj.x的值value赋值给mesh的x坐标
gui.add(obj, 'x', 0, 180).onChange(function(value){
mesh.position.x = value;
// 你可以写任何你想跟着obj.x同步变化的代码
// 比如mesh.position.y = value;
});
.addColor()
颜色值改变.addColor()
生成颜色值改变的交互界面
const obj = {
color:0x00ffff,
};
// .addColor()生成颜色值改变的交互界面
gui.addColor(obj, 'color').onChange(function(value){
mesh.material.color.set(value);
});
前面大家学过通过.add()
方法可以添加一个拖动条用来改变对象的某个属性,本节课给大家介绍.add()
方法创建新的UI交互界面,比如下拉菜单、单选框。
.add()
方法参数3和4数据类型:数字格式:add(控制对象,对象具体属性,其他参数)
其他参数,可以一个或多个,数据类型也可以不同,gui会自动根据参数形式,自动生成对应的交互界面。
参数3和参数4,分别是一个数字,交互界面是一个鼠标可以拖动的拖动条,可以在一个区间改变属性的值
// 参数3、参数4数据类型:数字(拖动条)
gui.add(obj, 'x', 0, 180).onChange(function (value) {
mesh.position.x = value;
});
.add()
方法参数3数据类型:数组参数3是一个数组,生成交互界面是下拉菜单
const obj = {
scale: 0,
};
// 参数3数据类型:数组(下拉菜单)
gui.add(obj, 'scale', [-100, 0, 100]).name('y坐标').onChange(function (value) {
mesh.position.y = value;
});
.add()
方法参数3数据类型:对象参数3是一个对象,生成交互界面是下拉菜单
const obj = {
scale: 0,
};
// 参数3数据类型:对象(下拉菜单)
gui.add(obj, 'scale', {
left: -100,
center: 0,
right: 100
// 左: -100,//可以用中文
// 中: 0,
// 右: 100
}).name('位置选择').onChange(function (value) {
mesh.position.x = value;
});
.add()
方法对应属性的数据类型:布尔值如果.add()
改变属性的对应的数据类型如果是布尔值,那么交互界面就是一个单选框。
const obj = {
bool: false,
};
// 改变的obj属性数据类型是布尔值,交互界面是单选框
gui.add(obj, 'bool').name('是否旋转');
gui.add(obj, 'bool').onChange(function (value) {
// 点击单选框,控制台打印obj.bool变化
console.log('obj.bool',value);
});
.add()
方法改变布尔值案例控制一个对象是否旋转。
gui.add(obj, 'bool').name('旋转动画');
// 渲染循环
function render() {
// 当gui界面设置obj.bool为true,mesh执行旋转动画
if (obj.bool) mesh.rotateY(0.01);
renderer.render(scene, camera);
requestAnimationFrame(render);
}
render();
.addFolder()
当GUI交互界面需要控制的属性比较多的时候,为了避免混合,可以适当分组管理,这样更清晰。
gui交互界面不分组,只有一个默认的总的菜单。
const gui = new GUI(); //创建GUI对象 //创建一个对象,对象属性的值可以被GUI库创建的交互界面改变 const obj = { color: 0x00ffff,// 材质颜色 specular: 0x111111,// 材质高光颜色 }; // 材质颜色color gui.addColor(obj, 'color').onChange(function(value){ material.color.set(value); }); // 材质高光颜色specular gui.addColor(obj, 'specular').onChange(function(value){ material.specular.set(value); }); // 环境光强度 gui.add(ambient, 'intensity',0,2); // 平行光强度 gui.add(directionalLight, 'intensity',0,2); // 平行光位置 gui.add(directionalLight.position, 'x',-400,400); gui.add(directionalLight.position, 'y',-400,400); gui.add(directionalLight.position, 'z',-400,400);
.addFolder()
分组new GUI()
实例化一个gui对象,默认创建一个总的菜单,通过gui对象的.addFolder()
方法可以创建一个子菜单,当你通过GUI控制的属性比较多的时候,可以使用.addFolder()
进行分组。
.addFolder()
返回的子文件夹对象,同样具有gui对象的.add()
、.onChange()
、.addColor()
等等属性。
const gui = new GUI(); //创建GUI对象
const obj = {
color: 0x00ffff,// 材质颜色
};
// 创建材质子菜单
const matFolder = gui.addFolder('材质');
matFolder.close();
// 材质颜色color
matFolder.addColor(obj, 'color').onChange(function(value){
material.color.set(value);
});
// 材质高光颜色specular
matFolder.addColor(obj, 'specular').onChange(function(value){
material.specular.set(value);
});
// 环境光子菜单
const ambientFolder = gui.addFolder('环境光');
// 环境光强度
ambientFolder.add(ambient, 'intensity',0,2);
// 平行光强度
dirFolder.add(directionalLight, 'intensity',0,2);
// 平行光位置
dirFolder.add(directionalLight.position, 'x',-400,400);
dirFolder.add(directionalLight.position, 'y',-400,400);
dirFolder.add(directionalLight.position, 'z',-400,400);
.close()
和展开.open()
交互界面gui对象创建的总菜单或gui.addFolder()
创建的子菜单都可以用代码控制交互界面关闭或开展状态。
const gui = new GUI(); //创建GUI对象
gui.close();//关闭菜单
// 创建材质子菜单
const matFolder = gui.addFolder('材质');
matFolder.close();//关闭菜单
.addFolder()
套娃——子菜单嵌套子菜单.addFolder()
创建的对象,同样也具有.addFolder()
属性,可以继续嵌套子菜单。
// 平行光子菜单
const dirFolder = gui.addFolder('平行光');
dirFolder.close();//关闭菜单
// 平行光强度
dirFolder.add(directionalLight, 'intensity',0,2);
const dirFolder2 = dirFolder.addFolder('位置');//子菜单的子菜单
dirFolder2.close();//关闭菜单
// 平行光位置
dirFolder2.add(directionalLight.position, 'x',-400,400);
dirFolder2.add(directionalLight.position, 'y',-400,400);
dirFolder2.add(directionalLight.position, 'z',-400,400);
有些时候你想了解threejs某个类(对象)的语法,或者某个对象属性和方法的语法,这时候你可以查询threejs官方文件包提供的案例examples、文档docs。
three.js-文件包
...
└───docs——Three.js API文档文件
│───index.html——打开该文件,本地离线方式预览threejs文档
└───examples——大量的3D案例,是你平时开发参考学习的最佳资源
│───.html——各种3D案例
...
案例examples文件里面提供了很多3D案例,平时学习你可以参考里面的小案例去学习。
通过关键词全文检索案例examples,比如vscode代码编辑器,在左侧找到threejs文件包的examples目录,右键,点击在文件中查找,输入一个关键词就可以查找,比如输入类名gui、OrbitControls、.lookAt()…,通过关键字找到对应的.html文件,你就可以参考里面的代码了。
关于文档的使用,前面也说过,这里再给大家再给大家介绍更多文档使用技巧。
通过threejs中文网(webgl3d.cn)分享一个文档链接,可以直接跳转到threejs官网提供的中文文档。
文档左上角提供了一个输入框,可以快速查看某个API,如果你不记得完整名字,也可以通过threejs API部分名字查询。
进入threejs API页面,有的类属性或方法比较多,滚动页面查询比较麻烦的话,如果你想查询某个方法或属性,可以Ctrl+F调用谷歌浏览器的当前网页关键字查询功能,找到特定的API的方法或属性,比如找到类PerspectiveCamera
,搜索该类的.fov、.aspect、.updateProjectionMatrix()等属性或方法。
如果你通过案例examples、文档docs还不能更加清晰的了解某个属性或方法的含义,可以查看threejs封装的源码,当然对于threejs新手或编程基础不好的学员,不太推荐。
本节课从JavaScript面向对象语法的角度,给大家总结下threejs API的使用习惯,这样方便大家更好的使用threejs API。
Three.js提供了各种各样的类(构造函数),通过new
关键字可以实例化类(构造函数),获得一个对象,对象具有属性和方法。
// new实例化类THREE.MeshLambertMaterial,创建一个材质对象
const material = new THREE.MeshLambertMaterial();
// 可以看到材质对象的属性color、side、opacity、transparent...
// 通过属性可以看到threejs默认的属性值
console.log('查看材质对象',material);
// 查看材质默认属性值
console.log('material.color',material.color);
console.log('material.side',material.side);
console.log('material.transparent',material.transparent);
console.log('material.opacity',material.opacity);
通过类(构造函数)的参数设置属性
const material = new THREE.MeshLambertMaterial({
color: 0x00ffff,
side:THREE.DoubleSide,
transparent:true,
opacity:0.5,
});
查看选项参数设置的材质属性值,可以和原来默认属性值对比
console.log('material.color',material.color);
console.log('material.side',material.side);
console.log('material.transparent',material.transparent);
console.log('material.opacity',material.opacity);
// 访问对象属性改变属性的值
material.transparent = false;
material.opacity = 1.0;
console.log('directionalLight',ambient.intensity);
directionalLight.intensity = 0.1;//改变光源强度
如果你学习过JavaScript面向对象,应该有父类和子类的概念,子类是通过父类派生出来的,会继承父类的属性或方法。
环境光、平行光源的父类Light
mesh、light光源的父类Object3D
如果你想通过文档查询一个类的方法或属性,除了可以查询类本身,还可以查询类的父类。
console.log('模型位置属性',mesh.position);
mesh.position.x = 50;//访问属性改变位置x坐标
mesh.translateX(50);//执行方法改变位置属性
如果你想了解更多Vue+Threejs结合开发的详细知识点,可以参考系统课程后面Vue+Threejs实战案例或课件里面Vue+Threejs案例模板。
有些同学是前端转来过来的,受到平时开发习惯影响,第一反应可能是threejs能不能与vue或react结合。
其实threejs知识点相对普通web前端是比较独立的,threejs的用法,你直接用.html文件写,还是结合vue或React框架写,API语法都是一样的。
所以你学习threejs的重点不是考虑前端框架问题,而是threejs本身,掌握了threejs,剩下的事情就很简单了。
回顾下前面1.6. 第一个3D案例知识点
three.js执行渲染方法.render();
会输出一个canvas画布renderer.domElement
,这个Canvas画布本质上就是一个HTML元素。
threejs与Vue结合的时候,你只需要把Canvas画布renderer.domElement
插入到你的Vue页面上就行,插入任何一个div或其它元素中,或者放到某个Vue组件中都行。
// WebGL渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
renderer.render(scene, camera);
//three.js执行渲染命令会输出一个canvas画布(HTML元素)
document.body.appendChild(renderer.domElement);
接下来,你写的threejs代码结构,并不一定就要和我下面视频完全一致,你可以根据你自己项目情况,自由调整。
Vite
使用脚手架Vite快速创建一个vue工程文件,具体跟着视频可操作即可
npm create vite@latest
执行命令npm create vite@latest
,然后选择你想要的开发环境即可。
第一步是选择你的前端框架,第二步是选择是否支持TS。
注意:安装使用Vite之前,确保你电脑已经安装Nodejs了,尽量用最新版本的。
命令行执行npm i
,安装所有默认依赖
命令行执行npm run dev
,查看vite里面Vue代码默认渲染效果。
现在你可以把默认的HTML和CSS代码删掉,然后在引入threejs代码。
安装threesjs时候,你可以指定你想要的版本。
// 比如安装157版本
npm install three@0.157.0 -S
新建index.js
文件,把threejs代码写在index.js里面。
index.js
文件引入three.js。
import * as THREE from 'three';
复制前面课程第一个3D案例的代码,粘贴到index.js
文件。
// 三维场景 const scene = new THREE.Scene(); // 模型对象 const geometry = new THREE.BoxGeometry(50, 50, 50); const material = new THREE.MeshBasicMaterial({ color: 0x0000ff, }); const mesh = new THREE.Mesh(geometry, material); scene.add(mesh); // AxesHelper:辅助观察的坐标系 const axesHelper = new THREE.AxesHelper(250); scene.add(axesHelper); const width = 800; //宽度 const height = 500; //高度 // 相机 const camera = new THREE.PerspectiveCamera(30, width / height, 1, 3000); camera.position.set(292, 223, 185); camera.lookAt(0, 0, 0); // WebGL渲染器 const renderer = new THREE.WebGLRenderer(); renderer.setSize(width, height); renderer.render(scene, camera); //three.js执行渲染命令会输出一个canvas画布(HTML元素),你可以插入到web页面中 document.body.appendChild(renderer.domElement);
然后把threejs对应的index.js
文件引入到vue的main.js
文件中。
// main.js文件
import './index.js'// 执行threejs代码
当然你也可以根据需要,在其它Vue组件中调用执行threejs代码。
上面画布设置了固定宽高度,下面改成文档区域宽高度,也就是所谓canvas画布全屏
const width = window.innerWidth;
const height = window.innerHeight;
const camera = new THREE.PerspectiveCamera(30, width / height, 1, 3000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
// 可以放到vite项目style.css文件中
body{
// 把canvas画布与body区域边距设置为0
margin: 0px;
}
OrbitControls
查看文件node_modules,在目录three/examples/jsm
中,你可以看到threejs的很多扩展库。
对于这些扩展库,不会一次都引入,一般你用到那个,单独引入即可,下面以OrbitControls
为例给大家展示。
OrbitControls
功能就是旋转缩放平移,在1.9小节有具体讲解:1.9. 相机控件OrbitControls,如果还没学习,可以提前看下。
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
复制1.9. 相机控件OrbitControls里面关于相机控件的代码。
// 设置相机控件轨道控制器OrbitControls
const controls = new OrbitControls(camera, renderer.domElement);
// 如果OrbitControls改变了相机参数,重新调用渲染器渲染三维场景
controls.addEventListener('change', function () {
renderer.render(scene, camera); //执行渲染操作
});//监听鼠标、键盘事件
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。