Three.js笔记分享


创建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对应的位置

img

透视投影相机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 / height1
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
  1. 用于简单模拟3个坐标轴的对象(红色代表 X 轴;绿色代表 Y 轴.;蓝色代表 Z 轴).
  2. 构造函数–AxesHelper(size:Number)
  3. 代码示例
const axesHelper = new THREE.AxesHelper( 5 );
scene.add( axesHelper );
(2)轨道控制器OrbitControls
  1. 轨道控制器可以使得相机围绕目标进行轨道运动.

  2. 导入轨道控制器

import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
  1. 创建轨道控制器
const controls = new OrbitControls( camera, renderer.domElement );
  1. 属性

​ [1].enableDamping : Boolean

  • 将其设置为true以启用阻尼(惯性),这将给控制器带来重量感.(默认值为false)
  • 请注意,如果它被启用,你必须在你的动画循环里调用.update().

​ [2].dampingFactor: Float

  • 当.enableDamping设置为true的时候,设置阻尼系数. (默认值为0.05)
  • 请注意,如果它被启用,你必须在你的动画循环里调用.update().

​ [3].autoRotate : Boolean

  • 将其设为true,以自动围绕目标旋转.
  • 请注意,如果它被启用,你必须在你的动画循环里调用.update().
(3)纹理加载器TextureLoader
  1. 加载texture的一个类,内部使用ImageLoader来加载文件.

  2. 构造函数:TextureLoader( manager : LoadingManager)

  3. 方法

​ [1].load(url:String,onLoad:Function,onProgress:Function, onError:Function):Texture

  • url:文件的URL或者路径,也可以为Data URL.
  1. 使用纹理加载器
// 创建纹理加载器
let textureLoader=new THREE.TextureLoader();
// 加载纹理
let texture=textureLOader.load("./dog.jpg");
(4)Draco加载器
  1. 使用Draco库压缩的几何体加载程序.

  2. Draco是一个用于压缩和解压缩三维网格和点云的开源库,压缩的几何体可以明显更小,代价是客户端设备上的额外解码时间.

  3. 独立Draco文件的扩展名为==.drc==,包含顶点位置、法线、颜色和其他属性;Draco文件不包含材质、纹理、动画或节点层次结构,要使用这些功能,请将Draco几何体嵌入glTF文件中,可以使用glTF管道将普通的glTF文件转换为Draco压缩的glTF;当将Draco与glTF一起使用时,GLTFLoader将在内部使用DRACOLoader的实例.

  4. 导入Draco加载器

import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
  1. 设置Draco加载器
//实例化加载器draco
const dracoLoader=new DRACOLoader();
//设置draco路径
dracoLoader.setDecoderPath("./draco/");
//设置gltf加载器draco解码器
gltfLoader.setDRACOLoader(dracoLoader);
(5)GLTF加载器
  1. glTF是一种开放格式的规范,用于更高效地传输、加载3D内容;该类文件以JSON(.gltf)格式或二进制(.glb)格式提供,外部文件存储贴图(.jpg、.png)和额外的二进制数据(.bin);一个glTF组件可传输一个或多个场景,包括网格、材质、贴图、蒙皮、骨架、变形目标、动画、灯光以及摄像头.
  2. 导入GLTF加载器
// GLTFLoader是一个附加组件,必须显示
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
  1. 加载模型
gltfLoader.load(
	// 模型路径
    "./model/Duck.glb",
    //加载完成回调
    (gltf)=>{
        console.log(gltf);
        scene.add(gltf.scene);
    }
)
(6)数学库
欧拉角Euler
  1. 欧拉角描述一个旋转变换,通过指定轴顺序和其各个轴向上的指定旋转角度来旋转一个物体.
  2. 构造器
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. 方法

​ [1]设置物体的局部旋转,以弧度来表示.rotation:Euler.

三维向量Vector3
  1. 一个三维向量表示的是一个有顺序的、三个为一组的数字组合(标记为x、y、z).

  2. 构造器

Vector3( x : Float, y : Float, z : Float )
x - 向量的x值(默认为0)
y - 向量的y值(默认为0)
z - 向量的z值(默认为0)
  1. 方法

​ [1]设置物体的x、y、z的分量.set:Vector3.

​ [2]设置物体的局部缩放.scale:Vector3.

(7)监听
  1. 监听窗口变化
window.addEventListener("resize",()=>{
  // 重置渲染器宽高比
  renderer.setSize(window.innerWidth,window.innerHeight);
  // 重置相机宽高比
  camera.aspect=window.innerHeight/window.innerHeight;
  // 更新相机投影矩阵
  camera.updateProjectionMatrix();
}
  1. 调用js接口控制画布全屏和退出全屏

    // 调用js接口控制画布全屏和退出全屏
    window.addEventListener('dblclick',()=>{
        const fullScreenElement=document.fullscreenElement;
        if (!fullScreenElement){
            // 双击控制屏幕进入全屏,退出全屏
            // 让画布全屏
            renderer.domElement.requestFullscreen();
        }else{
        // 退出全屏 使用document对象
        document.exitFullscreen();
    	}
    })
    
(8)lil-Gui
  1. 导入lil.gui
import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js"
  1. 代码示例
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
  1. 概念:是面片、线或点几何体的有效表述,包括顶点位置、面片索引、法相量、颜色值、UV 坐标和自定义缓存属性值;使用BufferGeometry可以有效减少向GPU传输上述数据所需的开销.
  2. 构造函数

​ [1]BufferGeometry():创建一个新的BufferGeometry,同时将预置属性设置为默认值.

  1. 属性

​ [1].groups

  • 将当前几何体分割成组进行渲染,每个部分都会在单独的WebGL的draw call中进行绘制.

  • 分割后的每个部分都是一个如下的表单:

// start表明当前draw call中的没有索引的几何体的几何体的第一个顶点,或者第一个三角面片的索引
// count指明当前分割包含多少顶点
// materialIndex指出当前用到的材质队列的索引
{ start: Integer, count: Integer, materialIndex: Integer }
  1. 方法

​ [1]为当前几何体设置一个attribute属性.setAttribute(name:String,attribute:BufferAttribute).

​ [2]设置缓存的.index.setIndex(index:BufferAttribute)

  1. 实现正方形结构
// 创建几何体
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)
  1. 索引实现正方形结构
// 使用索引绘制
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)
  1. 划分组实现不同材质
// 索引同上
// 设置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. 概念:基础网格材质,一个以简单着色(平面或线框)方式来绘制几何体的材质.
  2. 构造函数

​ [1]MeshBasicMaterial( parameters : Object )

  • parameters(可选):用于定义材质外观的对象,具有一个或多个属性,材质的任何属性都可以从此处传入(包括从Material继承的任何属性).
  • 属性color例外,其可以作为十六进制字符串传递,默认情况下为0xffffff,内部调用Color.set(color).
  1. 属性

​ [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. 方法

​ [1].getHex(colorSpace:string=SRGBColorSpace):Integer

  • 返回此颜色的十六进制值.
  1. 加载背景纹理
const loader=new THREE.TextureLoader();
const bgTexture=loader.load("./bg.jpg");
// 经纬线映射贴图 -- 针对球体 实现周围环境渲染
bgTexture.mapping=THREE.EquirectangularRefractionMapping;

scene.background=bgTexture;// 设置场景环境
scene.environment=bgTexture;// 设置环境贴图
(11)纹理属性(色彩空间)
  1. 在threejs中,纹理的colorSpace属性用于定义纹理的颜色空间,颜色空间是一个可以描述颜色的数学模型.
  2. 三种颜色空间类型
  • THREE.NoColorSpace:这意味着没有应用任何特定的颜色空间,纹理的颜色数据会被原样使用.
  • THREE.SRGBColorSpace:在此颜色空间中,颜色数据以sRGB格式存储,sRGB是一个RGB标准,它试图将色彩的表现和人眼感知到的颜色更好地匹配;相对于线性颜色空间,sRGB颜色空间在暗区提供了更多的颜色级别,使用此颜色空间时,需要注意图像的颜色可能会被转换为非线性的sRGB格式.
  • THREE.LinearSRGBColorSpace:这也是一个以sRGB格式存储颜色数据的颜色空间,但颜色数据被当作线性数据处理,在进行计算和处理时,这种颜色空间可以提供更精确的结果;然而,需要注意的是,使用此颜色,空间可能会使得颜色在暗区看起来过于暗.
  1. 设置颜色空间
// 创建纹理加载器
let textureLoader=new THREE.TextureLoader();
// 加载纹理
let texture=textureLOader.load("./dog.jpg");
texture.colorSpace=THREE.LinearSRGBColorSpace;
const material=new THREE.MeshBasicMaterial({
    map:texture
});
(12)Fog
  1. 在Three.js中,Fog类是用于创建线性雾的效果,雾效果常用于模拟真实世界中视觉深度递减的效果,也可以用于创建某些艺术效果,当物体距离观察者越远,雾就越密,物体的颜色就越接近雾的颜色.
  2. 雾效果是通过修改物体的颜色来实现的,因此,如果一个物体的材质不包含颜色的信息,那么雾效果可能不会对这个物体产生任何影响.
  3. 在一些复杂的场景中,雾效果可能会对性能产生影响,这是因为雾效果需要再每个像素处进行计算,如果场景中包含大量的几何体或复杂的材质,那么雾效果可能会导致渲染速度变慢.
  4. 雾效果并不会组织物体的渲染,也就是说,即使一个物体完全被雾覆盖,它仍然会被渲染,如果场景中包含大量的远离摄像机的物体,那么你可能需要使用其他的方法(例如,裁剪距离或物体级别的可见性控制)来优化渲染性能.
  5. 设置线性雾
const scene=new THREE.Scene();
// color:雾的颜色,可以是一个十六进制的整数,或者一个CSS样式的字符串
// near:雾开始的距离,在这个距离之内,物体的颜色不受雾的影响
// far:这是雾结束的距离,在这个距离之外,物体的颜色完全被雾的颜色覆盖
scene.fog=new THREE.Fog(0xcccccc,10,15);
  1. 在Three.js中,FogExp2类用于创建质数平方雾效果,相比于线性雾,指数平方雾提供了一个近摄像头处视野清晰,但离摄像头越远,雾效果加强速度超过指数级增长的效果,它可以模拟一些特定环境下的视觉效果,例如雾天或者水下环境.
  2. 设置指数雾
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. 要求

​ [1]材质要满足能够对光照有反应.

​ [2]设置渲染器开启阴影的计算renderer.shadoMap.enabled=true.

​ [3]设置光照投射阴影directionalLight.castShadow=true.

​ [4]设置物体投射阴影sphere.castShadow=true.

​ [5]设置物体接受阴影plane.receiveShadow=true.

  1. 其他设置
// 设置阴影贴图的分辨率
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补间动画
  1. tween.js是一款可生成平滑动画效果的js动画库 , tween引擎可以计算从开始动画点到结束动画点之间值,来产生平滑的动画效果.
  2. 方法

​ [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. 缓动效果函数

​ [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,前半段加速,后半段减速
  1. 导入Tween
import * as TWEEN from "three/examples/jsm/libs/tween.module.js";
  1. 设置补间动画
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();
  • 24
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值