近年来web得到了快速的发展。随着HTML5的普及,网页的表现能力越来越强大。网页上已经可以做出很多复杂的动画,精美的效果。还能通过WebGL在网页中绘制高性能的3D图形。 学习资料来源:https://www.three3d.cn/threejs/01-%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA/01-%E5%89%8D%E7%AB%AF3D%E5%8F%AF%E8%A7%86%E5%8C%96Three.js%E5%AD%A6%E4%B9%A0%E8%B7%AF%E7%BA%BF.html
随着浏览器性能和网络带宽的大幅度提升,以及WebGL的实现,使得3D技术不再是桌面程序的专利,越来越多的Web应用使用3D技术。
对网站而言,Web3D技术运用,实现企业网站三维呈现,让企业形象更直观、更立体地展现给客户,打破传统平面展示模式,打造智慧、个性、创新的企业形象。目前政府有大量的新基建项目,如数字孪生、智慧城市、智慧园区、智慧工厂等等都涉及到了3D可视化技术。
今天就来给大家讲讲如何系统的学好WEB 3D可视化技术。
我们可以先从threejs库入手。threejs是一个让用户通过javascript入手进入搭建webgl项目的类库。众所周知学习webgl需要图形学知识,而webgl需要通过js和glsl两种语言。
如果我们不通过threejs使用webgl势必逃不过底层知识:你必须全面了解着色器语法和自己编写顶点着色片元着色;但你使用了threejs显然可以便捷的逃过这些难懂的底层,对于传统js从业人员直接挑战的shader确实是有难度的。
学习three.js首先掌握基础概念什么是点、线、面、几何体、材质、物体、场景、相机、渲染器、动画、控制器等基础概念,搞定一个最基础的场景和3d物体的显示。接着学会调试3D开发代码。接着即可深入上诉概念的每一个概念,详细了解官网文档该类的各种属性与概念。
接着3d渲染要真实性,肯定离不开PBR
,详细了解什么是PBR,PBR基于物理的光照原理的渲染,。掌握什么是环境贴图、凹凸贴图、置换贴图、放射光、,环境贴图、金属贴图、粗糙度贴图等等,去打造真实的物体显示效果。接着掌握如何绘制粒子群,来绘制雨雪、落叶、星河等各种粒子效果,甚至产品的粒子效果。
掌握了这些,基本就算入了个小门了,接着就是要实现能和物体进行交互,如何选中与场景中的物体进行交互。而且还要能够掌握物理引擎让物体有真实的物理效果,例如重力,反弹、摩擦力等这样物体相互作用会更加真实。
接着就要开始真正进入WEBGL魔力的世界,掌握着色器语言,控制GPU渲染,掌握实现three.js底层封装的原理,能够图形渲染原理,掌握编写顶点着色器和片元着色器,来绘制动态飘扬的旗帜。以及编写动态的烟雾和乌云,水纹。
掌握了这些就可以写节日酷炫的烟花了,接着可以继续掌握各种后期合成效果,对整个渲染画面进行调整,例如打造闪烁的画面,雪花感的陈旧电视画面,又或者通过编写着色器,打造出水底世界的效果。
接着掌握曲线和物体运动的结合,在加上着色器编写,即可实现各种飞线、雷达、光墙特效。通过地理信息数据,获取建筑信息,可以生成建筑的框架和高度渲染出数字城市。当然日常网页也或有一些文字信息标识,想要文字标识也加上3d效果,就需要掌握css3d的渲染器来渲染。
当然如果需要掌握渲染精美真实的智慧园区的,就需要掌握建模技术,例如学习blender软件搭建模型和优化模型,才能最终输出到网页中,包括动画也可以先用blender做好在输出到网页中,不用辛苦的进行复杂动画的编写,可以可视化的制作。
因为Three.js官网是国外的服务器,所以为了方便学习和快速的查阅文档,我们可以自己搭建Three.js官网和文档,方便随时查看案例和文档内容进行学习。
1、首先进入threejs库GitHub地址:https://github.com/mrdoob/three.js
2、下载完整代码
一般安装可以用npm、yarn等包管理工具,课程以yarn举例,如果没有安装可以用npm install yarn -g进行安装。
yarn install
5、启动项目
yarn start
版本
{
"name": "01-startapp",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"devDependencies": {
"vite": "^4.3.9"
},
"dependencies": {
"three": "^0.153.0"
}
}
// 导入threejs
import * as THREE from "three";
// 创建场景
const scene = new THREE.Scene();
// 创建相机
const camera = new THREE.PerspectiveCamera(
45, // 视角
window.innerWidth / window.innerHeight, // 宽高比
0.1, // 近平面
1000 // 远平面
);
// 创建渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 创建几何体
const geometry = new THREE.BoxGeometry(1, 1, 1);
// 创建材质
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
// 创建网格
const cube = new THREE.Mesh(geometry, material);
// 将网格添加到场景中
scene.add(cube);
// 设置相机位置
camera.position.z = 5;
camera.lookAt(0, 0, 0);
// 渲染函数
function animate() {
requestAnimationFrame(animate);
// 旋转
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
// 渲染
renderer.render(scene, camera);
}
animate();
// 导入threejs
import * as THREE from "three";
// 导入轨道控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
// 创建场景
const scene = new THREE.Scene();
// 创建相机
const camera = new THREE.PerspectiveCamera(
45, // 视角
window.innerWidth / window.innerHeight, // 宽高比
0.1, // 近平面
1000 // 远平面
);
// 创建渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 创建几何体
const geometry = new THREE.BoxGeometry(1, 1, 1);
// 创建材质
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
// 创建网格
const cube = new THREE.Mesh(geometry, material);
// 将网格添加到场景中
scene.add(cube);
// 设置相机位置
camera.position.z = 5;
camera.position.y = 2;
camera.position.x = 2;
camera.lookAt(0, 0, 0);
// 添加世界坐标辅助器
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
// 添加轨道控制器
const controls = new OrbitControls(camera, document.body);
// 设置带阻尼的惯性
controls.enableDamping = true;
// 设置阻尼系数
controls.dampingFactor = 0.05;
// 设置旋转速度
controls.autoRotate = true;
// 渲染函数
function animate() {
controls.update();
requestAnimationFrame(animate);
// 旋转
// cube.rotation.x += 0.01;
// cube.rotation.y += 0.01;
// 渲染
renderer.render(scene, camera);
}
animate();
前面我们创建了物体,为了让物体移动起来。我们可以设置它的position属性进行位置的设置。
相机和立方体都是物体。每个物体都是1个对象。
在官方文档里,我们可以看到相机camera和物体mesh都继承Object3D类。所以camera、mesh都属于3d对象。从3d对象的官方文档里,我们可以找到position属性,并且该属性一个vector3对象。因此通过官方vector3类的文档,我们可以简单使用下面2种方式来修改position位置,当然后面还会讲解更多的方式。
//设置该向量的x、y 和 z 分量。
mesh.position.set(x,y,z);
//直接设置position的x,y,z属性
mesh.position.x = x;
mesh.position.y = y;
mesh.position.z = z;
官方文档:https://threejs.org/docs/index.html?q=vect#api/zh/math/Vector3
例如,每一帧让立方体向右移动0.01,并且当位置大于5时,从0开始。那么可以这么设置。
function render() {
cube.position.x += 0.01;
if (cube.position.x > 5) {
cube.position.x = 0;
}
renderer.render(scene, camera);
// 渲染下一帧的时候就会调用render函数
requestAnimationFrame(render);
}
render();
// 导入threejs
import * as THREE from "three";
// 导入轨道控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
// 创建场景
const scene = new THREE.Scene();
// 创建相机
const camera = new THREE.PerspectiveCamera(
45, // 视角
window.innerWidth / window.innerHeight, // 宽高比
0.1, // 近平面
1000 // 远平面
);
// 创建渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 创建几何体
const geometry = new THREE.BoxGeometry(1, 1, 1);
// 创建材质
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const parentMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 });
// 创建网格
let parentCube = new THREE.Mesh(geometry, parentMaterial);
const cube = new THREE.Mesh(geometry, material);
parentCube.add(cube);
parentCube.position.set(-3, 0, 0);
// cube.position.x = 2;
cube.position.set(3, 0, 0);
// 将网格添加到场景中
scene.add(parentCube);
// 设置相机位置
camera.position.z = 5;
camera.position.y = 2;
camera.position.x = 2;
camera.lookAt(0, 0, 0);
// 添加世界坐标辅助器
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
// 添加轨道控制器
const controls = new OrbitControls(camera, document.body);
// 设置带阻尼的惯性
controls.enableDamping = true;
// 设置阻尼系数
controls.dampingFactor = 0.05;
// 设置旋转速度
// controls.autoRotate = true;
// 渲染函数
function animate() {
controls.update();
requestAnimationFrame(animate);
// 旋转
// cube.rotation.x += 0.01;
// cube.rotation.y += 0.01;
// 渲染
renderer.render(scene, camera);
}
animate();
scale设置缩放
//例如设置x轴放大3倍、y轴方向放大2倍、z轴方向不变
cube.scale.set(3, 2, 1);
//单独设置某个轴的缩放
cube.scale.x = 3
rotation设置旋转
//直接设置旋转属性,例如围绕x轴旋转90度
cube.rotation.x = -Math.PI/2
//围绕x轴旋转45度
cube.rotation.set(-Math.PI / 4, 0, 0, "XZY");
旋转动画
每一帧旋转弧度制的0.01角度,实现动画代码
function render() {
cube.position.x += 0.01;
cube.rotation.x += 0.01;
if (cube.position.x > 5) {
cube.position.x = 0;
}
renderer.render(scene, camera);
// 渲染下一帧的时候就会调用render函数
requestAnimationFrame(render);
}
// 导入threejs
import * as THREE from "three";
// 导入轨道控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
// 创建场景
const scene = new THREE.Scene();
// 创建相机
const camera = new THREE.PerspectiveCamera(
45, // 视角
window.innerWidth / window.innerHeight, // 宽高比
0.1, // 近平面
1000 // 远平面
);
// 创建渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 创建几何体
const geometry = new THREE.BoxGeometry(1, 1, 1);
// 创建材质
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const parentMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 });
// 创建网格
let parentCube = new THREE.Mesh(geometry, parentMaterial);
const cube = new THREE.Mesh(geometry, material);
parentCube.add(cube);
parentCube.position.set(-3, 0, 0);
parentCube.rotation.x = Math.PI / 4;
// parentCube.scale.set(2, 2, 2);
// cube.position.x = 2;
cube.position.set(3, 0, 0);
// 设置立方体的放大
// cube.scale.set(2, 2, 2);
// 绕着x轴旋转
cube.rotation.x = Math.PI / 4;
// 将网格添加到场景中
scene.add(parentCube);
// 设置相机位置
camera.position.z = 5;
camera.position.y = 2;
camera.position.x = 2;
camera.lookAt(0, 0, 0);
// 添加世界坐标辅助器
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
// 添加轨道控制器
const controls = new OrbitControls(camera, document.body);
// 设置带阻尼的惯性
controls.enableDamping = true;
// 设置阻尼系数
controls.dampingFactor = 0.05;
// 设置旋转速度
// controls.autoRotate = true;
// 渲染函数
function animate() {
controls.update();
requestAnimationFrame(animate);
// 旋转
// cube.rotation.x += 0.01;
// cube.rotation.y += 0.01;
// 渲染
renderer.render(scene, camera);
}
animate();
你会发现,我们前面写好的代码,在页面尺寸发生改变的时候,并不能自适应的改变尺寸,而出现空白或者滚动条突出的情况。所以监听屏幕大小的改变,来重新设置相机的宽高比例和渲染器的尺寸大小,代码如下:
// 监听画面变化,更新渲染画面
window.addEventListener("resize", () => {
// console.log("画面变化了");
// 更新摄像头
camera.aspect = window.innerWidth / window.innerHeight;
// 更新摄像机的投影矩阵
camera.updateProjectionMatrix();
// 更新渲染器
renderer.setSize(window.innerWidth, window.innerHeight);
// 设置渲染器的像素比
renderer.setPixelRatio(window.devicePixelRatio);
});
aspect属性是设置摄像机视锥体的长宽比,通常是使用画布的宽/画布的高。camera.updateProjectionMatrix()用于更新摄像机投影矩阵,相机任何参数被改变以后必须被调用
控制场景全屏
经常我们需要全屏的展示三维场景。例如,我们想要双击,实现全屏效果,代码如下:
window.addEventListener("dblclick", () => {
const fullScreenElement = document.fullscreenElement;
if (!fullScreenElement) {
// 双击控制屏幕进入全屏,退出全屏
// 让画布对象全屏
renderer.domElement.requestFullscreen();
} else {
// 退出全屏,使用document对象
document.exitFullscreen();
}
});
fullscreenElement只读属性返回当前在此文档中以全屏模式显示的元素。
如果文档当前未使用全屏模式,则返回值为null。
使用element.requestFullscreen()方法以全屏模式查看元素,exitFullscreen方法退出全屏。
let eventObj = {
Fullscreen: function () {
// 全屏
document.body.requestFullscreen();
console.log("全屏");
},
ExitFullscreen: function () {
document.exitFullscreen();
console.log("退出全屏");
},
};
// 创建GUI
const gui = new GUI();
// 添加按钮
gui.add(eventObj, "Fullscreen").name("全屏");
gui.add(eventObj, "ExitFullscreen").name("退出全屏");
// 控制立方体的位置
// gui.add(cube.position, "x", -5, 5).name("立方体x轴位置");
let folder = gui.addFolder("立方体位置");
folder
.add(cube.position, "x")
.min(-10)
.max(10)
.step(1)
.name("立方体x轴位置")
.onChange((val) => {
console.log("立方体x轴位置", val);
});
folder
.add(cube.position, "y")
.min(-10)
.max(10)
.step(1)
.name("立方体y轴位置")
.onFinishChange((val) => {
console.log("立方体y轴位置", val);
});
folder.add(cube.position, "z").min(-10).max(10).step(1).name("立方体z轴位置");
gui.add(parentMaterial, "wireframe").name("父元素线框模式");
let colorParams = {
cubeColor: "#00ff00",
};
gui
.addColor(colorParams, "cubeColor")
.name("立方体颜色")
.onChange((val) => {
cube.material.color.set(val);
});
所有的3D图形都是由三角形构成
// 导入threejs
import * as THREE from "three";
// 导入轨道控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
// 导入lil.gui
import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";
// 创建场景
const scene = new THREE.Scene();
// 创建相机
const camera = new THREE.PerspectiveCamera(
45, // 视角
window.innerWidth / window.innerHeight, // 宽高比
0.1, // 近平面
1000 // 远平面
);
// 创建渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// // 创建几何体
const cubegeometry = new THREE.BoxGeometry(1, 1, 1);
console.log(cubegeometry);
// // 创建材质
// const material = new THREE.MeshBasicMaterial({
// color: 0x00ff00,
// wireframe: true,
// });
// const cube = new THREE.Mesh(geometry, material);
// console.log(geometry);
// // 将网格添加到场景中
// 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, -1.0, 1.0, 0, -1.0, -1.0, 0,
// ]);
// // 创建顶点属性
// geometry.setAttribute("position", new THREE.BufferAttribute(vertices, 3));
// 使用索引绘制
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,
]);
// 创建顶点属性
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));
console.log(geometry);
// 创建材质
const material = new THREE.MeshBasicMaterial({
color: 0x00ff00,
// side: THREE.DoubleSide,
wireframe: true,
});
const plane = new THREE.Mesh(geometry, material);
scene.add(plane);
// 设置相机位置
camera.position.z = 5;
camera.position.y = 2;
camera.position.x = 2;
camera.lookAt(0, 0, 0);
// 添加世界坐标辅助器
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
// 添加轨道控制器
const controls = new OrbitControls(camera, renderer.domElement);
// 设置带阻尼的惯性
controls.enableDamping = true;
// 设置阻尼系数
controls.dampingFactor = 0.05;
// 设置旋转速度
// controls.autoRotate = true;
// 渲染函数
function animate() {
controls.update();
requestAnimationFrame(animate);
// 渲染
renderer.render(scene, camera);
}
animate();
// 监听窗口变化
window.addEventListener("resize", () => {
// 重置渲染器宽高比
renderer.setSize(window.innerWidth, window.innerHeight);
// 重置相机宽高比
camera.aspect = window.innerWidth / window.innerHeight;
// 更新相机投影矩阵
camera.updateProjectionMatrix();
});
let eventObj = {
Fullscreen: function () {
// 全屏
document.body.requestFullscreen();
console.log("全屏");
},
ExitFullscreen: function () {
document.exitFullscreen();
console.log("退出全屏");
},
};
// 创建GUI
const gui = new GUI();
// 添加按钮
gui.add(eventObj, "Fullscreen").name("全屏");
gui.add(eventObj, "ExitFullscreen").name("退出全屏");
// 控制立方体的位置
// gui.add(cube.position, "x", -5, 5).name("立方体x轴位置");
// 创建渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// // 创建几何体
const cubegeometry = new THREE.BoxGeometry(1, 1, 1);
console.log(cubegeometry);
// // 创建材质
const cubematerial0 = new THREE.MeshBasicMaterial({
color: 0x00ff00,
// wireframe: true,
});
const cubematerial1 = new THREE.MeshBasicMaterial({
color: 0xff0000,
});
const cubematerial2 = new THREE.MeshBasicMaterial({
color: 0x0000ff,
});
const cubematerial3 = new THREE.MeshBasicMaterial({
color: 0xffff00,
});
const cubematerial4 = new THREE.MeshBasicMaterial({
color: 0x00ffff,
});
const cubematerial5 = new THREE.MeshBasicMaterial({
color: 0xff00ff,
});
const cube = new THREE.Mesh(cubegeometry, [
cubematerial0,
cubematerial1,
cubematerial2,
cubematerial3,
cubematerial4,
cubematerial5,
]);
// console.log(geometry);
cube.position.x = 2;
// 将网格添加到场景中
scene.add(cube);
// 导入threejs
import * as THREE from "three";
// 导入轨道控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
// 导入lil.gui
import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";
// 导入hdr加载器
import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js";
// 创建场景
const scene = new THREE.Scene();
// 创建相机
const camera = new THREE.PerspectiveCamera(
45, // 视角
window.innerWidth / window.innerHeight, // 宽高比
0.1, // 近平面
1000 // 远平面
);
// 创建渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 设置相机位置
camera.position.z = 5;
camera.position.y = 2;
camera.position.x = 2;
camera.lookAt(0, 0, 0);
// 添加世界坐标辅助器
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
// 添加轨道控制器
const controls = new OrbitControls(camera, renderer.domElement);
// 设置带阻尼的惯性
controls.enableDamping = true;
// 设置阻尼系数
controls.dampingFactor = 0.05;
// 设置旋转速度
// controls.autoRotate = true;
// 渲染函数
function animate() {
controls.update();
requestAnimationFrame(animate);
// 渲染
renderer.render(scene, camera);
}
animate();
// 监听窗口变化
window.addEventListener("resize", () => {
// 重置渲染器宽高比
renderer.setSize(window.innerWidth, window.innerHeight);
// 重置相机宽高比
camera.aspect = window.innerWidth / window.innerHeight;
// 更新相机投影矩阵
camera.updateProjectionMatrix();
});
let params = {};
// 创建GUI
const gui = new GUI();
// 创建纹理加载器
let textureLoader = new THREE.TextureLoader();
// 加载纹理
let texture = textureLoader.load(
"./texture/watercover/CityNewYork002_COL_VAR1_1K.png"
);
// 加载ao贴图
let aoMap = textureLoader.load("./texture/watercover/CityNewYork002_AO_1K.jpg");
// 透明度贴图
let alphaMap = textureLoader.load("./texture/door/height.jpg");
// 光照贴图
let lightMap = textureLoader.load("./texture/colors.png");
// 高光贴图
let specularMap = textureLoader.load(
"./texture/watercover/CityNewYork002_GLOSS_1K.jpg"
);
// rgbeLoader 加载hdr贴图
let rgbeLoader = new RGBELoader();
rgbeLoader.load("./texture/Alex_Hart-Nature_Lab_Bones_2k.hdr", (envMap) => {
// 设置球形贴图
envMap.mapping = THREE.EquirectangularReflectionMapping;
// 设置环境贴图
scene.background = envMap;
// 设置环境贴图
scene.environment = envMap;
// 设置plane的环境贴图
planeMaterial.envMap = envMap;
});
let planeGeometry = new THREE.PlaneGeometry(1, 1);
let planeMaterial = new THREE.MeshBasicMaterial({
color: 0xffffff,
map: texture,
// 允许透明
transparent: true,
// 设置ao贴图
aoMap: aoMap,
aoMapIntensity: 1,
// 透明度贴图
// alphaMap: alphaMap,
// 设置光照贴图
// lightMap: lightMap,
// 设置高光贴图
specularMap: specularMap,
reflectivity: 0.5,
});
// planeMaterial.map = texture;
let plane = new THREE.Mesh(planeGeometry, planeMaterial);
scene.add(plane);
gui.add(planeMaterial, "aoMapIntensity").min(0).max(1).name("ao强度");
// 导入threejs
import * as THREE from "three";
// 导入轨道控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
// 导入lil.gui
import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";
// 导入hdr加载器
import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js";
// 创建场景
const scene = new THREE.Scene();
// 创建相机
const camera = new THREE.PerspectiveCamera(
45, // 视角
window.innerWidth / window.innerHeight, // 宽高比
0.1, // 近平面
1000 // 远平面
);
// 创建渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 设置相机位置
camera.position.z = 5;
camera.position.y = 2;
camera.position.x = 2;
camera.lookAt(0, 0, 0);
// 添加世界坐标辅助器
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
// 添加轨道控制器
const controls = new OrbitControls(camera, renderer.domElement);
// 设置带阻尼的惯性
controls.enableDamping = true;
// 设置阻尼系数
controls.dampingFactor = 0.05;
// 设置旋转速度
// controls.autoRotate = true;
// 渲染函数
function animate() {
controls.update();
requestAnimationFrame(animate);
// 渲染
renderer.render(scene, camera);
}
animate();
// 监听窗口变化
window.addEventListener("resize", () => {
// 重置渲染器宽高比
renderer.setSize(window.innerWidth, window.innerHeight);
// 重置相机宽高比
camera.aspect = window.innerWidth / window.innerHeight;
// 更新相机投影矩阵
camera.updateProjectionMatrix();
});
let params = {};
// 创建GUI
const gui = new GUI();
// 创建纹理加载器
let textureLoader = new THREE.TextureLoader();
// 加载纹理
let texture = textureLoader.load(
"./texture/watercover/CityNewYork002_COL_VAR1_1K.png"
);
console.log(texture);
texture.colorSpace = THREE.SRGBColorSpace;
// texture.colorSpace = THREE.LinearSRGBColorSpace;
// texture.colorSpace = THREE.NoColorSpace;
// 加载ao贴图
let aoMap = textureLoader.load("./texture/watercover/CityNewYork002_AO_1K.jpg");
// 透明度贴图
let alphaMap = textureLoader.load("./texture/door/height.jpg");
// 光照贴图
let lightMap = textureLoader.load("./texture/colors.png");
// 高光贴图
let specularMap = textureLoader.load(
"./texture/watercover/CityNewYork002_GLOSS_1K.jpg"
);
// rgbeLoader 加载hdr贴图
let rgbeLoader = new RGBELoader();
rgbeLoader.load("./texture/Alex_Hart-Nature_Lab_Bones_2k.hdr", (envMap) => {
// 设置球形贴图
envMap.mapping = THREE.EquirectangularReflectionMapping;
// 设置环境贴图
scene.background = envMap;
// 设置环境贴图
scene.environment = envMap;
// 设置plane的环境贴图
planeMaterial.envMap = envMap;
});
let planeGeometry = new THREE.PlaneGeometry(1, 1);
let planeMaterial = new THREE.MeshBasicMaterial({
color: 0xffffff,
map: texture,
// 允许透明
transparent: true,
// 设置ao贴图
aoMap: aoMap,
aoMapIntensity: 1,
// 透明度贴图
// alphaMap: alphaMap,
// 设置光照贴图
// lightMap: lightMap,
// 设置高光贴图
specularMap: specularMap,
reflectivity: 0.5,
});
// planeMaterial.map = texture;
let plane = new THREE.Mesh(planeGeometry, planeMaterial);
scene.add(plane);
gui.add(planeMaterial, "aoMapIntensity").min(0).max(1).name("ao强度");
gui
.add(texture, "colorSpace", {
sRGB: THREE.SRGBColorSpace,
Linear: THREE.LinearSRGBColorSpace,
})
.onChange(() => {
texture.needsUpdate = true;
});
// 导入threejs
import * as THREE from "three";
// 导入轨道控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
// 导入lil.gui
import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";
// 导入hdr加载器
import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js";
// 创建场景
const scene = new THREE.Scene();
// 创建相机
const camera = new THREE.PerspectiveCamera(
45, // 视角
window.innerWidth / window.innerHeight, // 宽高比
0.1, // 近平面
1000 // 远平面
);
// 创建渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 设置相机位置
camera.position.z = 5;
camera.position.y = 2;
camera.position.x = 2;
camera.lookAt(0, 0, 0);
// 添加世界坐标辅助器
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
// 添加轨道控制器
const controls = new OrbitControls(camera, renderer.domElement);
// 设置带阻尼的惯性
controls.enableDamping = true;
// 设置阻尼系数
controls.dampingFactor = 0.05;
// 设置旋转速度
// controls.autoRotate = true;
// 渲染函数
function animate() {
controls.update();
requestAnimationFrame(animate);
// 渲染
renderer.render(scene, camera);
}
animate();
// 监听窗口变化
window.addEventListener("resize", () => {
// 重置渲染器宽高比
renderer.setSize(window.innerWidth, window.innerHeight);
// 重置相机宽高比
camera.aspect = window.innerWidth / window.innerHeight;
// 更新相机投影矩阵
camera.updateProjectionMatrix();
});
let params = {};
// 创建GUI
const gui = new GUI();
// 创建长方体
const boxGeometry = new THREE.BoxGeometry(1, 1, 100);
const boxMaterial = new THREE.MeshBasicMaterial({
color: 0x00ff00,
});
const box = new THREE.Mesh(boxGeometry, boxMaterial);
scene.add(box);
// 创建场景fog
scene.fog = new THREE.Fog(0x999999, 0.1, 50);
// 创建场景指数fog
// scene.fog = new THREE.FogExp2(0x999999, 0.1);
scene.background = new THREE.Color(0x999999);
// 导入threejs
import * as THREE from "three";
// 导入轨道控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
// 导入lil.gui
import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";
// 导入hdr加载器
import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js";
// 导入gltf加载器
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
// 导入draco解码器
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader.js";
// 创建场景
const scene = new THREE.Scene();
// 创建相机
const camera = new THREE.PerspectiveCamera(
45, // 视角
window.innerWidth / window.innerHeight, // 宽高比
0.1, // 近平面
1000 // 远平面
);
// 创建渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 设置相机位置
camera.position.z = 5;
camera.position.y = 2;
camera.position.x = 2;
camera.lookAt(0, 0, 0);
// 添加世界坐标辅助器
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
// 添加轨道控制器
const controls = new OrbitControls(camera, renderer.domElement);
// 设置带阻尼的惯性
controls.enableDamping = true;
// 设置阻尼系数
controls.dampingFactor = 0.05;
// 设置旋转速度
// controls.autoRotate = true;
// 渲染函数
function animate() {
controls.update();
requestAnimationFrame(animate);
// 渲染
renderer.render(scene, camera);
}
animate();
// 监听窗口变化
window.addEventListener("resize", () => {
// 重置渲染器宽高比
renderer.setSize(window.innerWidth, window.innerHeight);
// 重置相机宽高比
camera.aspect = window.innerWidth / window.innerHeight;
// 更新相机投影矩阵
camera.updateProjectionMatrix();
});
let params = {};
// 创建GUI
const gui = new GUI();
// 创建场景fog
scene.fog = new THREE.Fog(0x999999, 0.1, 50);
// 创建场景指数fog
// scene.fog = new THREE.FogExp2(0x999999, 0.1);
scene.background = new THREE.Color(0x999999);
// 实例化加载器gltf
const gltfLoader = new GLTFLoader();
// 加载模型
gltfLoader.load(
// 模型路径
"./model/Duck.glb",
// 加载完成回调
(gltf) => {
console.log(gltf);
scene.add(gltf.scene);
}
);
// 实例化加载器draco
const dracoLoader = new DRACOLoader(); // 解压器
// 设置draco路径
dracoLoader.setDecoderPath("./draco/");
// 设置gltf加载器draco解码器
gltfLoader.setDRACOLoader(dracoLoader);
gltfLoader.load(
// 模型路径
"./model/city.glb", // 做了压缩
// 加载完成回调
(gltf) => {
// console.log(gltf);
scene.add(gltf.scene);
}
);
// 加载环境贴图
let rgbeLoader = new RGBELoader();
rgbeLoader.load("./texture/Alex_Hart-Nature_Lab_Bones_2k.hdr", (envMap) => {
envMap.mapping = THREE.EquirectangularReflectionMapping;
// 设置环境贴图
scene.environment = envMap;
});
RACOLoader是Three.js中用于加载由Draco库压缩的几何体的加载器。Draco是一个开源的3D网格和点云压缩与解压库。压缩后的几何体可以在客户端设备上显著减小大小,但需要额外的解码时间。
独立的Draco文件具有.drc
扩展名,并包含顶点位置、法线、颜色和其他属性。Draco文件不包含材质、纹理、动画或节点层次结构——要使用这些功能,需要将Draco几何体嵌入glTF文件中。可以使用glTF-Pipeline将普通的glTF文件转换为Draco压缩的glTF文件。在使用Draco和glTF时,GLTFLoader内部将使用一个DRACOLoader实例。
建议创建一个DRACOLoader实例并重复使用它,以避免加载和创建多个解码器实例。
下面是一个简单的案例,演示如何使用DRACOLoader加载模型并在Three.js中进行渲染:
首先导入依赖
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
使用演示代码
// Instantiate a loader
const loader = new DRACOLoader();
// Specify path to a folder containing WASM/JS decoding libraries.
loader.setDecoderPath( '/examples/jsm/libs/draco/' );
// Optional: Pre-fetch Draco WASM/JS module.
loader.preload();
// Load a Draco geometry
loader.load(
// resource URL
'model.drc',
// called when the resource is loaded
function ( geometry ) {
const material = new THREE.MeshStandardMaterial( { color: 0x606060 } );
const mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
},
// called as loading progresses
function ( xhr ) {
console.log( ( xhr.loaded / xhr.total * 100 ) + '% loaded' );
},
// called when loading has errors
function ( error ) {
console.log( 'An error happened' );
}
);
// 导入threejs
import * as THREE from "three";
// 导入轨道控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
// 导入lil.gui
import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";
// 创建场景
const scene = new THREE.Scene();
// 创建相机
const camera = new THREE.PerspectiveCamera(
45, // 视角
window.innerWidth / window.innerHeight, // 宽高比
0.1, // 近平面
1000 // 远平面
);
// 创建渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 设置相机位置
camera.position.z = 15;
camera.position.y = 2;
camera.position.x = 2;
camera.lookAt(0, 0, 0);
// 添加世界坐标辅助器
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
// 添加轨道控制器
const controls = new OrbitControls(camera, renderer.domElement);
// 设置带阻尼的惯性
controls.enableDamping = true;
// 设置阻尼系数
controls.dampingFactor = 0.05;
// 设置旋转速度
// controls.autoRotate = true;
// 渲染函数
function animate() {
controls.update();
requestAnimationFrame(animate);
// 渲染
renderer.render(scene, camera);
}
animate();
// 监听窗口变化
window.addEventListener("resize", () => {
// 重置渲染器宽高比
renderer.setSize(window.innerWidth, window.innerHeight);
// 重置相机宽高比
camera.aspect = window.innerWidth / window.innerHeight;
// 更新相机投影矩阵
camera.updateProjectionMatrix();
});
let params = {};
// 创建GUI
const gui = new GUI();
// 创建三个球
const sphere1 = new THREE.Mesh(
new THREE.SphereGeometry(1, 32, 32),
new THREE.MeshBasicMaterial({
color: 0x00ff00,
})
);
sphere1.position.x = -4;
scene.add(sphere1);
const sphere2 = new THREE.Mesh(
new THREE.SphereGeometry(1, 32, 32),
new THREE.MeshBasicMaterial({
color: 0x0000ff,
})
);
scene.add(sphere2);
const sphere3 = new THREE.Mesh(
new THREE.SphereGeometry(1, 32, 32),
new THREE.MeshBasicMaterial({
color: 0xff00ff,
})
);
sphere3.position.x = 4;
scene.add(sphere3);
console.log(scene.children);
// 创建射线
const raycaster = new THREE.Raycaster();
// 创建鼠标向量
const mouse = new THREE.Vector2();
window.addEventListener("click", (event) => {
console.log(event.clientX, event.clientY);
// 设置鼠标向量的x,y值
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -((event.clientY / window.innerHeight) * 2 - 1);
// console.log(mouse.x, mouse.y);
// 通过摄像机和鼠标位置更新射线
raycaster.setFromCamera(mouse, camera);
// 计算物体和射线的焦点
const intersects = raycaster.intersectObjects([sphere1, sphere2, sphere3]);
if (intersects.length > 0) {
// console.log(intersects[0].object);
if (intersects[0].object._isSelect) {
console.log(intersects[0].object._originColor);
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.material.color.getHex();
intersects[0].object.material.color.set(0xff0000);
}
console.log(intersects);
});
// 导入threejs
import * as THREE from "three";
// 导入轨道控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
// 导入lil.gui
import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";
// 导入tween
import * as TWEEN from "three/examples/jsm/libs/tween.module.js";
// 创建场景
const scene = new THREE.Scene();
// 创建相机
const camera = new THREE.PerspectiveCamera(
45, // 视角
window.innerWidth / window.innerHeight, // 宽高比
0.1, // 近平面
1000 // 远平面
);
// 创建渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 设置相机位置
camera.position.z = 15;
camera.position.y = 2;
camera.position.x = 2;
camera.lookAt(0, 0, 0);
// 添加世界坐标辅助器
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
// 添加轨道控制器
const controls = new OrbitControls(camera, renderer.domElement);
// 设置带阻尼的惯性
controls.enableDamping = true;
// 设置阻尼系数
controls.dampingFactor = 0.05;
// 设置旋转速度
// controls.autoRotate = true;
// 渲染函数
function animate() {
controls.update();
requestAnimationFrame(animate);
// 渲染
renderer.render(scene, camera);
// 更新tween
TWEEN.update();
}
animate();
// 监听窗口变化
window.addEventListener("resize", () => {
// 重置渲染器宽高比
renderer.setSize(window.innerWidth, window.innerHeight);
// 重置相机宽高比
camera.aspect = window.innerWidth / window.innerHeight;
// 更新相机投影矩阵
camera.updateProjectionMatrix();
});
// 创建GUI
const gui = new GUI();
// 创建1个球
const sphere1 = new THREE.Mesh(
new THREE.SphereGeometry(1, 32, 32),
new THREE.MeshBasicMaterial({
color: 0x00ff00,
})
);
sphere1.position.x = -4;
scene.add(sphere1);
const tween = new TWEEN.Tween(sphere1.position);
tween.to({ x: 4 }, 1000);
tween.onUpdate(() => {
console.log(sphere1.position.x);
});
// 设置循环无数次
// tween.repeat(Infinity);
// 循环往复
// tween.yoyo(true);
// tween.repeat(2);
// tween.delay(3000);
// 设置缓动函数
tween.easing(TWEEN.Easing.Quadratic.InOut);
let tween2 = new TWEEN.Tween(sphere1.position);
tween2.to({ x: -4 }, 1000);
tween.chain(tween2);
tween2.chain(tween);
// 启动补间动画
tween.start();
tween.onStart(() => {
console.log("开始");
});
tween.onComplete(() => {
console.log("结束");
});
tween.onStop(() => {
console.log("停止");
});
tween.onUpdate(() => {
console.log("更新");
});
let params = {
stop: function () {
tween.stop();
},
};
gui.add(params, "stop");