创建3D场景
三维场景Scene
对象可以理解为虚拟的3D场景,用来表示模拟生活中的真实三维场景,或者说三维世界.
三维场景Scene
// 创建3D场景对象Scene
const scene = new THREE.Scene();
物体形状:几何体Geometry
// 创建一个长方体几何对象Geometry
const geometry = new THREE.BoxGeometry(100, 100, 100);
物体外观:材质Material
// 创建一个材质对象Material
const material = new THREE.MeshBasicMaterial({
color: 0xff0000,//0xff0000设置材质颜色为红色
});
物体:网格模型Mesh
// 两个参数分别为几何体geometry、材质material
const mesh = new THREE.Mesh(geometry, material); // 网格模型对象Mesh
模型位置.position
const mesh = new THREE.Mesh(geometry, material); // 网格模型对象Mesh
// 设置网格模型在三维空间中的位置坐标,默认是坐标原点
mesh.position.set(0,10,0);
.add()
方法
- 在threejs中你创建了一个表示物体的虚拟对象Mesh,需要通过
.add()
方法,把网格模型mesh
添加到三维场景scene
中.
scene.add(mesh);
透视投影相机
Threejs如果想把三维场景Scene
渲染到web网页上,还需要定义一个虚拟相机Camera
,就像你生活中想获得一张照片,需要一台用来拍照的相机.
透视投影相机PerspectiveCamera
// 实例化一个透视投影相机对象
const camera = new THREE.PerspectiveCamera();
相机位置.position
// 相机在Three.js三维坐标系中的位置
// 根据需要设置相机位置具体值
camera.position.set(200, 200, 200);
相机观察目标.lookAt()
- 你用相机拍照你需要控制相机的拍照目标,具体说相机镜头对准哪个物体或说哪个坐标,对于threejs相机而言,就是设置
.lookAt()
方法的参数,指定一个3D坐标.
// 相机观察目标指向Threejs 3D空间中某个位置
camera.lookAt(0, 0, 0); // 坐标原点
camera.lookAt(mesh.position);// 指向mesh对应的位置
透视投影相机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);
参数 | 含义 | 默认值 |
---|---|---|
fov | 相机视锥体竖直方向视野角度 | 50 |
aspect | 相机视锥体水平方向和竖直方向长度比,一般设置为Canvas画布宽高比width / height | 1 |
near | 相机视锥体近裁截面相对相机距离 | 0.1 |
far | 相机视锥体远裁截面相对相机距离,far-near构成了视锥体高度方向 | 2000 |
渲染器
生活中如果有了景物和相机,那么如果想获得一张照片,就需要你拿着相机,按一下,咔,完成拍照,对于threejs而言,如果完成“咔”这个拍照动作,就需要一个新的对象,也就是WebGL渲染器.
WebGL渲染器WebGLRenderer
- 通过WebGL渲染器可以实例化一个WebGL渲染器对象。
// 创建渲染器对象
const renderer = new THREE.WebGLRenderer();
设置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);
其他
(1)坐标辅助器AxesHelper
- 用于简单模拟3个坐标轴的对象(红色代表 X 轴;绿色代表 Y 轴.;蓝色代表 Z 轴).
- 构造函数–AxesHelper(size:Number)
- 代码示例
const axesHelper = new THREE.AxesHelper( 5 );
scene.add( axesHelper );
(2)轨道控制器OrbitControls
-
轨道控制器可以使得相机围绕目标进行轨道运动.
-
导入轨道控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
- 创建轨道控制器
const controls = new OrbitControls( camera, renderer.domElement );
- 属性
[1].enableDamping : Boolean
- 将其设置为true以启用阻尼(惯性),这将给控制器带来重量感.(默认值为false)
- 请注意,如果它被启用,你必须在你的动画循环里调用.update().
[2].dampingFactor: Float
- 当.enableDamping设置为true的时候,设置阻尼系数. (默认值为0.05)
- 请注意,如果它被启用,你必须在你的动画循环里调用.update().
[3].autoRotate : Boolean
- 将其设为true,以自动围绕目标旋转.
- 请注意,如果它被启用,你必须在你的动画循环里调用.update().
(3)纹理加载器TextureLoader
-
加载texture的一个类,内部使用ImageLoader来加载文件.
-
构造函数:TextureLoader( manager : LoadingManager)
-
方法
[1].load(url:String,onLoad:Function,onProgress:Function, onError:Function):Texture
- url:文件的URL或者路径,也可以为Data URL.
- 使用纹理加载器
// 创建纹理加载器
let textureLoader=new THREE.TextureLoader();
// 加载纹理
let texture=textureLOader.load("./dog.jpg");
(4)Draco加载器
-
使用Draco库压缩的几何体加载程序.
-
Draco是一个用于压缩和解压缩三维网格和点云的开源库,压缩的几何体可以明显更小,代价是客户端设备上的额外解码时间.
-
独立Draco文件的扩展名为==.drc==,包含顶点位置、法线、颜色和其他属性;Draco文件不包含材质、纹理、动画或节点层次结构,要使用这些功能,请将Draco几何体嵌入glTF文件中,可以使用glTF管道将普通的glTF文件转换为Draco压缩的glTF;当将Draco与glTF一起使用时,GLTFLoader将在内部使用DRACOLoader的实例.
-
导入Draco加载器
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
- 设置Draco加载器
//实例化加载器draco
const dracoLoader=new DRACOLoader();
//设置draco路径
dracoLoader.setDecoderPath("./draco/");
//设置gltf加载器draco解码器
gltfLoader.setDRACOLoader(dracoLoader);
(5)GLTF加载器
- glTF是一种开放格式的规范,用于更高效地传输、加载3D内容;该类文件以JSON(.gltf)格式或二进制(.glb)格式提供,外部文件存储贴图(.jpg、.png)和额外的二进制数据(.bin);一个glTF组件可传输一个或多个场景,包括网格、材质、贴图、蒙皮、骨架、变形目标、动画、灯光以及摄像头.
- 导入GLTF加载器
// GLTFLoader是一个附加组件,必须显示
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
- 加载模型
gltfLoader.load(
// 模型路径
"./model/Duck.glb",
//加载完成回调
(gltf)=>{
console.log(gltf);
scene.add(gltf.scene);
}
)
(6)数学库
欧拉角Euler
- 欧拉角描述一个旋转变换,通过指定轴顺序和其各个轴向上的指定旋转角度来旋转一个物体.
- 构造器
Euler( x : Float, y : Float, z : Float, order : String )
x - (optional)用弧度表示x轴旋转量.(默认值是0)
y - (optional)用弧度表示y轴旋转.(默认值是0)
z - (optional)用弧度表示z轴旋转量.(默认值是0)
order - (optional)表示旋转顺序的字符串,默认为'XYZ'(必须是大写).
- 方法
[1]设置物体的局部旋转,以弧度来表示.rotation:Euler
.
三维向量Vector3
-
一个三维向量表示的是一个有顺序的、三个为一组的数字组合(标记为x、y、z).
-
构造器
Vector3( x : Float, y : Float, z : Float )
x - 向量的x值(默认为0)
y - 向量的y值(默认为0)
z - 向量的z值(默认为0)
- 方法
[1]设置物体的x、y、z的分量.set:Vector3
.
[2]设置物体的局部缩放.scale:Vector3
.
(7)监听
- 监听窗口变化
window.addEventListener("resize",()=>{
// 重置渲染器宽高比
renderer.setSize(window.innerWidth,window.innerHeight);
// 重置相机宽高比
camera.aspect=window.innerHeight/window.innerHeight;
// 更新相机投影矩阵
camera.updateProjectionMatrix();
}
-
调用js接口控制画布全屏和退出全屏
// 调用js接口控制画布全屏和退出全屏 window.addEventListener('dblclick',()=>{ const fullScreenElement=document.fullscreenElement; if (!fullScreenElement){ // 双击控制屏幕进入全屏,退出全屏 // 让画布全屏 renderer.domElement.requestFullscreen(); }else{ // 退出全屏 使用document对象 document.exitFullscreen(); } })
(8)lil-Gui
- 导入lil.gui
import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js"
- 代码示例
let eventObj={
Fullscreen:function(){
//全屏
document.body.requestFullscreen();
},
ExitFullscreen:function(){
document.exitFullscreen();
}
}
// 创建GUI
const gui=new GUI();
// 添加按钮
gui.add(eventObj,"Fullscreen").name("全屏");
gui.add(eventObj,"exitFullscreen").name("退出全屏");
let folder=gui.addFolder("立方体位置");
folder.add(cube.position,"x").min(-10).max(10).step(1).name("立方体x轴位置");
folder.add(cube.position,"y").min(-10).max(10).step(1).name("立方体y轴位置");
folder.add(cube.position,"z").min(-10).max(10).step(1).name("立方体z轴位置");
gui.add(material,"wireframe").name("父元素线框模式");
let colorParams={
cubeColor:"#ff0000"
};
gui.addColor(colorParams,"cubeColor").name("立方体颜色").onChange((val)=>{
cube.material.color.set(val);
})
(9)BufferGeometry
- 概念:是面片、线或点几何体的有效表述,包括顶点位置、面片索引、法相量、颜色值、UV 坐标和自定义缓存属性值;使用BufferGeometry可以有效减少向GPU传输上述数据所需的开销.
- 构造函数
[1]BufferGeometry():创建一个新的BufferGeometry,同时将预置属性设置为默认值.
- 属性
[1].groups
-
将当前几何体分割成组进行渲染,每个部分都会在单独的WebGL的draw call中进行绘制.
-
分割后的每个部分都是一个如下的表单:
// start表明当前draw call中的没有索引的几何体的几何体的第一个顶点,或者第一个三角面片的索引
// count指明当前分割包含多少顶点
// materialIndex指出当前用到的材质队列的索引
{ start: Integer, count: Integer, materialIndex: Integer }
- 方法
[1]为当前几何体设置一个attribute属性.setAttribute(name:String,attribute:BufferAttribute)
.
[2]设置缓存的.index.setIndex(index:BufferAttribute)
- 实现正方形结构
// 创建几何体
const geometry=new THREE.BufferGeometry();
// 创建顶点数据
// 顶点是有序的,每三个为一个顶点,逆时针为正面
const vertices=new Float32Array([
-1.0,-1.0,0.0,1.0,-1.0,0.0,1.0,1.0,0.0,
-1.0,-1.0,0.0,1.0,1.0,0.0,-1.0,1.0,0.0,
]);
// 创建顶点属性
// itemSize = 3 因为每个顶点都是一个三元组
geometry.setAttribute("position",new THREE.BufferAttribute(vertices,3));
// 创建材质
const material=new THREE.MeshBasicMaterial({
color:0x00ff00,
// 两面可见
side:THREE.DoubleSide,
});
const cube = new THREE.Mesh( geometry, material );
scene.add(cube)
- 索引实现正方形结构
// 使用索引绘制
const geometry=new THREE.BufferGeometry();
const vertices=new Float32Array([
// 第一个点
-1.0,-1.0,0.0,
// 第二个点
1.0,-1.0,0.0,
// 共用顶点
1.0,1.0,0.0,
// 第三个点
-1.0,1.0,0.0,
]);
geometry.setAttribute("position",new THREE.BufferAttribute(vertices,3));
// 创建索引const indices=new Uint16Array([0,1,2,2,3,0]);
// 创建索引属性
geometry.setIndex(new THREE.BufferAttribute(indices,1));
const material=new THREE.MeshBasicMaterial({
color:0x00ff00,
side:THREE.DoubleSide,
});
const cube = new THREE.Mesh( geometry, material );
scene.add(cube)
- 划分组实现不同材质
// 索引同上
// 设置2个顶点组,形成2个材质
geometry.addGroup(0,3,0);
geometry.addGroup(3,3,1);
const cubematerial0=new THREE.MeshBasicMaterial({
color:0x00ff00,
});
const cubematerial1=new THREE.MeshBasicMaterial({
color:0xff00ff,
});
const cube = new THREE.Mesh( geometry, [cubematerial0,cubematerial1] );
(10)MeshBasicMaterial
- 概念:基础网格材质,一个以简单着色(平面或线框)方式来绘制几何体的材质.
- 构造函数
[1]MeshBasicMaterial( parameters : Object )
- parameters(可选):用于定义材质外观的对象,具有一个或多个属性,材质的任何属性都可以从此处传入(包括从Material继承的任何属性).
- 属性color例外,其可以作为十六进制字符串传递,默认情况下为0xffffff,内部调用Color.set(color).
- 属性
[1].map
- 颜色贴图,可以选择包括一个alpha通道.
[2].aoMap
- 该纹理的红色通道用作环境遮挡贴图.(默认值为null)
- aoMap需要第二组UV.
[3].aoMapIntensity:Float
- 环境遮挡效果的强度.
- 默认值为1,0是不遮挡效果.
[4].alphaMap
- alpha贴图是一张灰度纹理,用于控制整个表面的不透明度.
- 黑色:完全透明;白色:完全不透明.
[5].lightMap
- 光照贴图,默认值为null.
- lightMap需要第二组UV.
[6].envMap
- 环境贴图,默认值为null.
- 方法
[1].getHex(colorSpace:string=SRGBColorSpace)
:Integer
- 返回此颜色的十六进制值.
- 加载背景纹理
const loader=new THREE.TextureLoader();
const bgTexture=loader.load("./bg.jpg");
// 经纬线映射贴图 -- 针对球体 实现周围环境渲染
bgTexture.mapping=THREE.EquirectangularRefractionMapping;
scene.background=bgTexture;// 设置场景环境
scene.environment=bgTexture;// 设置环境贴图
(11)纹理属性(色彩空间)
- 在threejs中,纹理的colorSpace属性用于定义纹理的颜色空间,颜色空间是一个可以描述颜色的数学模型.
- 三种颜色空间类型
- THREE.NoColorSpace:这意味着没有应用任何特定的颜色空间,纹理的颜色数据会被原样使用.
- THREE.SRGBColorSpace:在此颜色空间中,颜色数据以sRGB格式存储,sRGB是一个RGB标准,它试图将色彩的表现和人眼感知到的颜色更好地匹配;相对于线性颜色空间,sRGB颜色空间在暗区提供了更多的颜色级别,使用此颜色空间时,需要注意图像的颜色可能会被转换为非线性的sRGB格式.
- THREE.LinearSRGBColorSpace:这也是一个以sRGB格式存储颜色数据的颜色空间,但颜色数据被当作线性数据处理,在进行计算和处理时,这种颜色空间可以提供更精确的结果;然而,需要注意的是,使用此颜色,空间可能会使得颜色在暗区看起来过于暗.
- 设置颜色空间
// 创建纹理加载器
let textureLoader=new THREE.TextureLoader();
// 加载纹理
let texture=textureLOader.load("./dog.jpg");
texture.colorSpace=THREE.LinearSRGBColorSpace;
const material=new THREE.MeshBasicMaterial({
map:texture
});
(12)Fog
- 在Three.js中,Fog类是用于创建线性雾的效果,雾效果常用于模拟真实世界中视觉深度递减的效果,也可以用于创建某些艺术效果,当物体距离观察者越远,雾就越密,物体的颜色就越接近雾的颜色.
- 雾效果是通过修改物体的颜色来实现的,因此,如果一个物体的材质不包含颜色的信息,那么雾效果可能不会对这个物体产生任何影响.
- 在一些复杂的场景中,雾效果可能会对性能产生影响,这是因为雾效果需要再每个像素处进行计算,如果场景中包含大量的几何体或复杂的材质,那么雾效果可能会导致渲染速度变慢.
- 雾效果并不会组织物体的渲染,也就是说,即使一个物体完全被雾覆盖,它仍然会被渲染,如果场景中包含大量的远离摄像机的物体,那么你可能需要使用其他的方法(例如,裁剪距离或物体级别的可见性控制)来优化渲染性能.
- 设置线性雾
const scene=new THREE.Scene();
// color:雾的颜色,可以是一个十六进制的整数,或者一个CSS样式的字符串
// near:雾开始的距离,在这个距离之内,物体的颜色不受雾的影响
// far:这是雾结束的距离,在这个距离之外,物体的颜色完全被雾的颜色覆盖
scene.fog=new THREE.Fog(0xcccccc,10,15);
- 在Three.js中,FogExp2类用于创建质数平方雾效果,相比于线性雾,指数平方雾提供了一个近摄像头处视野清晰,但离摄像头越远,雾效果加强速度超过指数级增长的效果,它可以模拟一些特定环境下的视觉效果,例如雾天或者水下环境.
- 设置指数雾
const scene=new THREE.Scene();
// color:雾的颜色,可以是一个十六进制的整数,或者一个CSS样式的字符串
// density:雾的密度,密度越大,雾效果越强
scene.fog=new THREE.FogExp2(0xcccccc,0.002);
(13)光线投影实现3D场景交互
// 创建射线
const raycaster=new THREE.Raycaster();
// 创建鼠标向量
const mouse=new THREE.Vector2();
window.addEventListener("click",(event)=>{
mouse.x=(event,clientX / window.innerWidth)*2-1;
mouse.y=-((event,clientY / window.innerHeight)*2-1);
// 通过摄像机和鼠标位置更新射线
raycaster.setFromCamera(mouse,camera);
// 计算物体和射线的焦点
const intersects=raycaster.intersectObjects(scene.children);
if(intersects.length>0){
if(intersects[0].Object._isSelect){
intersects[0].object.material.color.set(
intersects[0].object._originColor
);
intersects[0].object._isSelect=false;
return;
}
intersects[0].object._isSelect=true;
intersects[0].object._originColor=intersects[0].object.color.getHex();
intersects[0].object.material.color.set(0xff0000);
}
});
(14)灯光与阴影
- 要求
[1]材质要满足能够对光照有反应.
[2]设置渲染器开启阴影的计算renderer.shadoMap.enabled=true
.
[3]设置光照投射阴影directionalLight.castShadow=true
.
[4]设置物体投射阴影sphere.castShadow=true
.
[5]设置物体接受阴影plane.receiveShadow=true
.
- 其他设置
// 设置阴影贴图的分辨率
directionalLight.shadow.radius=20;
// 设置阴影贴图的分辨率
directionalLight.shadow.mapSize.set(2048,2048);
// 设置平行光投射相机的属性
directionalLight.shadow.camera.near=0.5;
directionalLight.shadow.camera.far=500;
directionalLight.shadow.camera.top=5;
directionalLight.shadow.camera.bottom=-5;
directionalLight.shadow.camera.left=-5;
directionalLight.shadow.camera.right=5;
(15)Tweem补间动画
- tween.js是一款可生成平滑动画效果的js动画库 , tween引擎可以计算从开始动画点到结束动画点之间值,来产生平滑的动画效果.
- 方法
[1].start
- 开启补间动画
new TWEEN.Tween().start(time)
,start 方法接受一个参数time,如果加入参数,那么补间不会立即开始直到特定时刻才会开始.
[2].stop
- 关闭补间动画
new TWEEN.Tween().stop()
,关闭这个正在执行的补间动画.
[3].repeat
- 控制补间重复的次数
new TWEEN.Tween().repeat()
,repeat它接受一个参数,描述第一个补间完成后需要多少次重复.
[4].yoyo
- 控制补间重复的模式,
new TWEEN.Tween().yoyo()
,这个功能只有在使用repeat时才有效果,补间的行为将像悠悠球一样来回运动,而不是重新开始.
[5].delay
- 控制补间开始前的延迟
new TWEEN.Tween().delay()
,补间开始之前的延迟时间接受一个参数用于控制具 体时间.
[6].pause
- 暂定补间动画
new TWEEN.Tween().pause()
,暂停当前补间运动.
[7].resume
- 恢复补间动画
new TWEEN.Tween().resume()
,恢复这个已经被暂停的补间运动.
[8].to
- 控制补间的运动形式及方向
new TWEEN.Tween().to()
,当tween启动时,Tween.js将读取当前属性值并应用相对值来找出新的最终值.
- 缓动效果函数
[1]格式:.easing(TWEEN.Easing.easing函数.easing类型)
[2]easing函数:
- Linear ==> 线性匀速运动效果
- Quadratic ==> 二次方的缓动
- Cubic ==> 三次方的缓动
- Sinusoidal ==> 正弦曲线的缓动
- Exponential ==> 指数曲线的缓动
- Circular ==> 圆形曲线的缓动
- Elastic ==> 指数衰减的正弦曲线缓动
- Back ==> 超过范围的三次方的缓动
- Bounce ==> 指数衰减的反弹缓动
[3]easing类型:
- In ==> easeIn,加速,先慢后快
- Out ==> easeOut,减速,先快后慢
- InOut ==> easeInOut,前半段加速,后半段减速
- 导入Tween
import * as TWEEN from "three/examples/jsm/libs/tween.module.js";
- 设置补间动画
const tween=new TWEEN.Tween(sphere1.position);
tween.to({x:4},1000);
// 设置循环无数次
tween.repeat(Infinity);
// 循环往复
tween.yoyo(true);
// 设置缓动函数
tween.easing(TWEEN.Easing.Quadratic.InOut);
/* 链接多个动画
tween.to({y:-4},1000);
tween.chain(tween2);
*/
// 启动补间动画
tween.start();