three.js基础
记录下three.js的学习历程。
Three.js是基于 WebGL 技术,用于浏览器中开发 3D 交互场景的 JS 引擎。
Three.js擅长 WebGL 场景渲染,Three.js不擅长物理碰撞,因此不适合开发 3D 游戏
three.js基本概念包括画布Canvas、渲染器Renderer、场景Scene、相机Camera、光源Light、几何体Geometry、材质Material、颜色纹理、点模型、线模型、网格模型、阴影、外部三维模型、动画等
基础
<canvas id="basic"></canvas>
<button type="button" name="button" onclick="saveCanvasImage()">下载</button>
<script type="importmap">
{
"imports": {
"three": "./js/build/three.module.js",
"three/addons/": "./js/jsm/"
}
}
</script>
<script type="module">
import * as THREE from "three";
// Three.js是基于 WebGL 技术,用于浏览器中开发 3D 交互场景的 JS 引擎。
// Three.js擅长 WebGL 场景渲染,Three.js不擅长物理碰撞,因此不适合开发 3D 游戏
// three.js基本概念包括画布Canvas、渲染器Renderer、场景Scene、相机Camera、光源Light、几何体Geometry、材质Material、
// 颜色纹理、点模型物体、线模型物体、网格模型物体、阴影、外部三维模型、动画等
console.log(THREE.REVISION); //157
//渲染器
const domId = document.getElementById("basic");
const width = 500;
const height = 400;
const renderer = new THREE.WebGLRenderer({
canvas: domId,
antialias: true, //抗锯齿,平滑
preserveDrawingBuffer: true, //渲染器渲染的内容保存到本地
});
renderer.setSize(width, height); //设置渲染区域的尺寸(像素px)
renderer.setClearColor(0x000000, 1); //设置背景颜色,参数2为透明度值
renderer.setClearAlpha(0.8); //改变背景透明度值
renderer.setPixelRatio(window.devicePixelRatio); //设置设备像素比,以免渲染模糊问题
//场景
const scene = new THREE.Scene();
// 背景颜色
// scene.background = new THREE.Color("#ffffff");
// 指数雾,参数:雾的颜色、雾的密度
// scene.fog = new THREE.FogExp2(0xffffff, 0.015);
// 线性雾,参数:雾的颜色、开始应用雾的最小距离、应用雾的最大距离
// scene.fog = new THREE.Fog(0xffffff, 10, 100);
// 强制覆写材质
// scene.overrideMaterial = new THREE.MeshLambertMaterial({
// color: 0xffffff,
// });
// 透视相机PerspectiveCamera 使用perspective projection(透视投影)来进行投影。
// 这一投影模式被用来模拟人眼所看到的景象,它是3D场景的渲染中使用得最普遍的投影模式。
// PerspectiveCamera( fov : Number, aspect : Number, near : Number, far : Number )
// fov — 摄像机视锥体垂直视野角度
// aspect — 摄像机视锥体长宽比
// near — 摄像机视锥体近端面
// far — 摄像机视锥体远端面
const camera = new THREE.PerspectiveCamera(30, width / height, 0.1, 1000); //透视投影相机参数设置
camera.position.set(50, 50, 50); //设置相机位置
camera.lookAt(0, 0, 0);
scene.add(camera);
// 坐标轴辅助 用于简单模拟3个坐标轴的对象.红色代表 X 轴. 绿色代表 Y 轴. 蓝色代表 Z 轴.
// AxesHelper( size : Number ) size -- (可选的) 表示代表轴的线段长度。默认为 1。
const axesHelper = new THREE.AxesHelper(150);
scene.add(axesHelper);
// 立方缓冲几何体BoxGeometry
// BoxGeometry(width : Float, height : Float, depth : Float, widthSegments : Integer, heightSegments : Integer, depthSegments : Integer)
// width — X 轴上面的宽度,默认值为 1。
// height — Y 轴上面的高度,默认值为 1。
// depth — Z 轴上面的深度,默认值为 1。
// widthSegments — (可选)宽度的分段数,默认值是 1。
// heightSegments — (可选)高度的分段数,默认值是 1。
// depthSegments — (可选)深度的分段数,默认值是 1。
const geometry = new THREE.BoxGeometry(3, 2, 2);
console.log(geometry.name); //'' 几何体名字
console.log(geometry.type); //BoxGeometry 几何体类型
console.log(geometry.parameters); //几何体定义参数
console.log(geometry.userData); //{} 自定义数据
console.log(geometry.getAttribute("position")); // BufferAttribute
console.log(geometry.getAttribute("normal")); // BufferAttribute
console.log(geometry.getAttribute("uv")); // BufferAttribute 顶点uv坐标,二维顶点坐标,作用:纹理贴图上提取像素映射到网格模型Mesh的几何体表面上。
console.log(geometry.computeBoundingBox()); // undefined
console.log(geometry.computeBoundingSphere()); // undefined
// 点材质
const pointMaterial = new THREE.PointsMaterial({
color: 0xff0000,
size: 1, //点对象像素尺寸
});
//点模型物体
const points = new THREE.Points(geometry, pointMaterial);
points.position.set(-5, 0, -5);
scene.add(points);
console.log(points.name, points.type, points.userData); //'' Points {}
console.log(points.isObject3D, points.isPoints, points.visible); //true true true
console.log(points.castShadow, points.receiveShadow); //false false 是否投射阴影 是否接收阴影
console.log(points.matrix, points.matrixWorld); //Matrix4 Matrix4 物体的局部变换矩阵 物体的世界变换矩阵
console.log(points.position); //Vector3 {x: -5, y: 1, z: 0} 表示对象局部位置的Vector3
console.log(points.rotation); //Euler 物体的局部旋转,以弧度来表示
console.log(points.scale); //Vector3 {x: 1, y: 1, z: 1} 物体的局部缩放
console.log(points.up); //Vector3 {x: 0, y: 1, z: 0} 这个属性由lookAt方法所使用,例如来决定结果的朝向。
console.log(points.animations); //[] 三维物体所属的动画剪辑数组
console.log(points.children); //[] 含有对象的子级的数组
// 线材质
const lineMaterial = new THREE.LineBasicMaterial({
color: 0x00ff00, //线条颜色
});
// 创建线模型物体
const line = new THREE.LineLoop(geometry, lineMaterial);
line.position.set(-5, 0, 0);
scene.add(line);
console.log(line.isObject3D, line.isLine, line.isLineLoop, line.type); //true true true 'LineLoop'
//网格材质:默认正面可见,反面不可见。
const meshMaterial = new THREE.MeshBasicMaterial({
color: 0x0000ff,
// side: THREE.FrontSide, //默认只有正面可见
side: THREE.DoubleSide, //两面可见
// side: THREE.BackSide, //设置只有反面可见
});
//网格模型:由多个三角形的面拼接构成。正反面区分:依相机观察方向的三个点顺序逆时针为正面,顺时针为反面
const mesh = new THREE.Mesh(geometry, meshMaterial);
mesh.position.set(-5, 0, 5);
scene.add(mesh);
console.log(mesh);
console.log(mesh.isObject3D, mesh.isMesh, mesh.type); //true true 'Mesh'
mesh.rotateX(Math.PI); // 物体绕着x轴旋转
mesh.translateY(-1); //物体沿着y轴负方向平移
mesh.scale.set(1.5, 1, 0.8); //物体缩放
const meshMaterial2 = new THREE.MeshBasicMaterial({
color: 0xffffff,
side: THREE.DoubleSide,
wireframe: true, //线条模式渲染
});
const geometry2 = new THREE.BoxGeometry(3, 3, 3);
const mesh2 = new THREE.Mesh(geometry, meshMaterial2);
mesh2.position.set(-5, 0, 10);
scene.add(mesh2);
geometry2.scale(2, 2, 2); // 几何体缩放
geometry2.translate(3, 3, 0); // 几何体平移
geometry2.rotateX(Math.PI); // 几何体绕着x轴旋转
//物体克隆
const mesh3 = mesh2.clone();
//材质克隆
mesh3.material = mesh2.material.clone();
mesh3.material.color.set(0xffff00);
mesh3.position.set(5, 0, -5);
const mesh4 = mesh2.clone();
mesh4.material = mesh2.material.clone();
mesh4.material.color.set(new THREE.Color(0xff00ff));
mesh4.position.set(5, 0, 0);
const axis = new THREE.Vector3(1, 0, 0); //向量axis
mesh4.translateOnAxis(axis, 2); //沿着axis轴表示方向平移
mesh4.rotateOnAxis(axis, Math.PI / 2); //绕axis轴旋转
mesh4.scale.set(1.5, 1, 0.8);
//组
const group = new THREE.Group();
group.add(mesh3, mesh4);
group.position.set(10, 0, 0);
group.rotateY(Math.PI);
// 递归遍历group包含所有的对象
group.traverse(function (obj) {
if (obj.isMesh) {
// obj.material.color.set(0xffff00);
obj.material.visible = true;
obj.visible = true;
}
});
console.log("group的子对象", group.children);
scene.add(group);
// 对象移除
// group.remove(mesh3);
// scene.remove(mesh);
console.log("scene的子对象", scene.children);
mesh4.name = "test";
// 根据name查找对象节点
const nameNode = scene.getObjectByName("test");
nameNode.material.color.set(0xff0000);
const worldPosition = new THREE.Vector3();
nameNode.getWorldPosition(worldPosition);
//世界坐标-子对象的坐标叠加所有父对象的坐标
console.log("世界坐标", worldPosition);
//本地坐标-子对象相对于父对象的坐标
console.log("本地坐标", nameNode.position);
//可视化mesh的局部坐标系
const meshAxesHelper = new THREE.AxesHelper(50);
nameNode.add(meshAxesHelper);
const cbox = new THREE.Box3().setFromObject(nameNode);
const size = cbox.getSize(new THREE.Vector3());
console.log(size); //Vector3 {x: 77.25, y: 40.8, z: 51.000000000000014}
const box3 = new THREE.Box3();
box3.expandByObject(nameNode);
console.log("查看包围盒", box3);
const scale = new THREE.Vector3();
box3.getSize(scale);
console.log("模型包围盒尺寸", scale);
const center = new THREE.Vector3();
box3.getCenter(center);
console.log("模型中心坐标", center);
renderer.render(scene, camera);
function onResize() {
const width = 500; //若需全屏使用window.innerWidth
const height = 500; //若需全屏使用window.innerHeight
//画布宽高比变化后更新透视投影相机PerspectiveCamera的aspect属性
camera.aspect = width / height;
camera.updateProjectionMatrix();
// 重置渲染器输出画布canvas尺寸
renderer.setSize(width, height);
// 更新内部的渲染缓冲区
renderer.setPixelRatio(window.devicePixelRatio);
renderer.render(scene, camera);
}
window.addEventListener("resize", onResize, false);
window.saveCanvasImage = () => {
const link = document.createElement("a");
link.href = renderer.domElement.toDataURL("image/png");
link.download = "pic.png";
link.click();
};
</script>