个人笔记--基础篇
什么是threejs
Three.js是一个跨浏览器的脚本,使用JavaScript函数库或API来在网页浏览器中创建和展示动画的三维计算机图形。Three.js使用WebGL。源代码托管在GitHub。Three.js允许使用JavaScript创建网页中的GPU加速的3D动画元素,而不是使用特定的浏览器插件。webgl封装成便于用户使用的Three.js ,类似jquery封装了JavaScript。
什么是webgl
WebGL是一种JavaScript API,用于在不使用插件的情况下在任何兼容的网页浏览器中呈现交互式2D和3D图形。WebGL完全集成到浏览器的所有网页标准中,可将影像处理和效果的GPU加速使用方式当做网页Canvas的一部分。WebGL元素可以加入其他HTML元素之中并与网页或网页背景的其他部分混合,WebGL技术结合了HTML5和 Java Script,允许开发者在网页(Web页面)上创建和渲染三维图形。
threejs入门
参考资料:
threejs官网:threejs.org/
threejs英文文档:threejs.org/docs/index.…
threejs中文文档:www.yanhuangxueyuan.com/threejs/doc…
threejs零基础入门教程:www.yanhuangxueyuan.com/Three.js/
threejs博客教程:www.wenjiangs.com/doc/kl5y4qv…
threejs通用文档:threejs.org/manual/#zh/…
threejs编辑器:threejs.org/editor/
threejs github:github.com/mrdoob/thre…
安装
项目启动运行
1、vite或者vue-cli等框架启动,直接运行命令行启动
npm start
//或
npm run dev
2、从 CDN 导入,使用服务器启动。常用的两种启动方式如下:
如果是vscode编辑工具启动,安装插件:Live Server,并在html页面中右键,点击Open with Live Server。
如果用cmd命令行启动
//全局安装依赖包
npm install -g http-server
//切换到HTML文件所在的目录
cd /path/to/your/html/files
//cmd命令
http-server --bind your-ip-address
场景搭建
threejs由场景、相机、渲染器、灯光、控制器等几个要素组成。每个要素都有不同的类型,例如光照有太阳光、环境光、半球光等等,每中光照都有不同的属性可以进行配置。
场景(scene):场景是所有物体的容器,如果要显示一个物体,就需要将物体对象加入场景中。
let scene = new THREE.Scene();
相机(camera):相机决定了场景中那个角度的景色会显示出来。相机就像人的眼睛一样,人站在不同位置,抬头或者低头都能够看到不同的景色。
let camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000)
渲染器(renderer):渲染器决定了渲染的结果应该画在页面的什么元素上面
let renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
//挂载到页面
document.body.appendChild(renderer.domElement)
灯光(light):模拟现实环境中的光照
let directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
控制器(controls):对3D场景进行旋转、放大缩小等操作
let controls = new OrbitControls(camera, renderer.domElement);
简单案例
<!--
* @Descripttion:
* @Date: 2022-07-19 14:44:28
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<style>
* {
margin: 0;
padding: 0;
overflow: hidden;
box-sizing: border-box;
}
</style>
<body>
<div id="container"></div>
</body>
<script type="importmap">
{
"imports": {
"three": "./build/three.module.js"
}
}
</script>
<script type="module">
import * as THREE from "three";
import { OrbitControls } from "./jsm/controls/OrbitControls.js";
let camera, scene, renderer, controls;
const container = document.getElementById("container");
/* 场景 */
function initScene() {
scene = new THREE.Scene();
scene.background = new THREE.Color("#7DD5EE");
var axes = new THREE.AxesHelper(100);
scene.add(axes);
}
/* 相机 */
function initCamera() {
camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 1, 5000);
camera.position.set(0, 200, 400);
}
/* 渲染器 */
function initRender() {
renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild(renderer.domElement);
}
/* 灯光 */
function initLight() {
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(50, 200, 100);
}
/* 控制器 */
function initControls() {
controls = new OrbitControls(camera, renderer.domElement);
}
/* 窗口变动触发 */
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
// 设置纹理偏移
requestAnimationFrame(animate);
render();
}
/* 渲染内容 */
function initContent() {
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);
}
function render() {
renderer.render(scene, camera);
controls.update();
}
function init() {
initScene();
initCamera();
initRender();
initLight();
initControls();
initContent();
window.addEventListener("resize", onWindowResize, false);
}
/* 初始加载 */
(function () {
init();
animate();
})();
</script>
</html>
添加几何体
添加几何体流程:
- 声明几何体形状。例如:正方体,长方体。
- 声明几何体材质。例如:金属材质。
- 使用网格模型mesh组合形状与材质,形成物体实例。
- 将物体实例添加到场景中。
const geometry = new THREE.BoxGeometry(100, 100, 100);
const material = new THREE.MeshBasicMaterial({ color: 'blue' });
const mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
scene.add(mesh);
常见几何体有:
常见材质有:
模型导入
threejs常使用到的模型格式分为:obj模型、gltf/glb模型、fbx模型、json模型。这些模型可以有3DMAX、Blender、Maya等建模软件进行建模并导出,由开发人员拿到模型后利用threejs api将模型导入到3D场景中。
例如gltf模型导入
第一步:引入相关加载器包文件
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
第二步:导入模型
//声明要使用的加载器
const loader = new GLTFLoader();
//导入模型
loader.load('models/gltf/duck/duck.gltf',
//回调函数:获取模型数据,添加到场景中
function ( gltf ) {
scene.add( gltf.scene );
},
//回调函数:获取模型加载的进度相关参数
function ( xhr ) {
console.log( ( xhr.loaded / xhr.total * 100 ) + '% loaded' );
},
//回调函数:获取加载中的报错信息
function ( error ) {
console.log( 'An error happened' );
}
);
由此便将模型导入到场景中,打开项目即可看到模型。打印模型数据console.log(gltf.scene),控制台得到以下数据。
第三步:模型交互
//设置位置
plane.position.x=10;
plane.position.y=10;
plane.position.z=10;
plane.position.set(10,10,10)
plane.position=new THREE.Vector3(10,10,10)
//缩放
plane.scale.set(0.5, 0.5, 0.5);
//旋转
plane.rotation.x = -0.5 * Math.PI;
plane.rotation.set(-0.5 * Math.PI,0,0)
plane.rotation=new THREE.Vector3(-0.5 * Math.PI,0,0)
//如果想使用度数(0到360)设置旋转。
var degrees=45
var inRadius=degree * (Math.PI/180)
//平移
plane.translateX(4)
个人笔记--进阶项目篇
原生项目框架
新建入口文件index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="container"></div>
</body>
<script type="importmap">
{
"imports": {
"three": "./build/three.module.js"
}
}
</script>
<script type="module">
import * as THREE from "three";
...开发代码
</script>
</html>
其他资源采用script/link方式引入
Vite构建工具框架
1、安装
//自主选择
npm create vite@latest
//或
npm create vite@latest my-vue-app -- --template vanilla
2、项目设置 新建src文件夹,并建立自己的文件分类(img/js...),并在html文件中引入。
vite.config.js配置入口文件,index.html或main.js
import { defineConfig } from "vite";
import { resolve } from "path";
import path from "path";
export default defineConfig({
build: {
rollupOptions: {
input: {
index: resolve(__dirname, "index.html"),
},
},
},
server: {
port: "9000", //端口
host: true,
open: true, //服务启动时自动在浏览器中打开应用
},
resolve: {
alias: {
//别名配置
"~": path.resolve(__dirname, "./"), //配置的别名
"@": path.resolve(__dirname, "./src"),
},
},
});
Parce构建工具框架
1、安装
在开始之前,您需要安装 Node 和 Yarn 或 npm,并为您的项目创建一个目录。然后,使用 Yarn 将 Parcel 安装到您的应用程序中:
npm install --save-dev parcel
//或
yarn add --dev parcel
2、项目设置
现在已经安装了 Parcel,让我们为我们的应用程序创建一些源文件。Parcel 接受任何类型的文件作为入口点,但 HTML 文件是一个很好的起点。Parcel 将从那里遵循您的所有依赖项来构建您的应用程序。
创建src文件夹,并且创建index.html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link rel="stylesheet" href="./assets/css/style.css" />
</head>
<body>
<script src="./main/main.js" type="module"></script>
</body>
</html>
创建一个main.js
import * as THREE from "three";
// console.log(THREE);
// 目标:了解three.js最基本的内容
// 1、创建场景
const scene = new THREE.Scene();
// 2、创建相机
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
// 设置相机位置
camera.position.set(0, 0, 10);
scene.add(camera);
// 添加物体
// 创建几何体
const cubeGeometry = new THREE.BoxGeometry(1, 1, 1);
const cubeMaterial = new THREE.MeshBasicMaterial({ color: 0xffff00 });
// 根据几何体和材质创建物体
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
// 将几何体添加到场景中
scene.add(cube);
// 初始化渲染器
const renderer = new THREE.WebGLRenderer();
// 设置渲染的尺寸大小
renderer.setSize(window.innerWidth, window.innerHeight);
// console.log(renderer);
// 将webgl渲染的canvas内容添加到body
document.body.appendChild(renderer.domElement);
// 使用渲染器,通过相机将场景渲染进来
renderer.render(scene, camera);
3、打包脚本
到目前为止,我们一直在parcel直接运行 CLI,但在您的package.json文件中创建一些脚本以简化此操作会很有用。我们还将设置一个脚本来使用该命令构建您的应用程序以进行parcel build最后,您还可以使用该字段在一个地方声明您的source,这样您就不需要在每个parcel命令中重复它们。
package.json:
{
"name": "01-three_basic",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "parcel src/index.html",
"build": "parcel build src/index.html"
},
"author": "",
"license": "ISC",
"devDependencies": {
"parcel": "^2.4.1"
},
"dependencies": {
"dat.gui": "^0.7.9",
"gsap": "^3.10.3",
"three": "^0.139.2"
}
}
现在您可以运行yarn build以构建您的生产项目并yarn dev启动开发服务器。
常用方法篇
设置物体旋转方向和位置
//设置位置
plane.position.x=10;
plane.position.y=10;
plane.position.z=10;
plane.position.set(10,10,10)
plane.position=new THREE.Vector3(10,10,10)
//缩放
plane.scale.set(0.5, 0.5, 0.5);
//旋转
plane.rotation.x = -0.5 * Math.PI;
plane.rotation.set(-0.5 * Math.PI,0,0)
plane.rotation=new THREE.Vector3(-0.5 * Math.PI,0,0)
//如果想使用度数(0到360)设置旋转。
var degrees=45
var inRadius=degree * (Math.PI/180)
//平移
plane.translateX(4)
常用特效篇
label标签
import { CSS3DRenderer, CSS3DSprite } from "../jsm/renderers/CSS3DRenderer.js";
var planeInfo = document.createElement("div");
planeInfo.className = "label";
planeInf2.innerHTML = "CSS3DSprite";
infoModal = new CSS3DSprite(planeInfo);
infoModal.position.set(-20, 20, 20);
scene.add(infoModal);
labelRenderer2 = new CSS3DRenderer();
labelRenderer2.setSize(window.innerWidth, window.innerHeight);
labelRenderer2.domElement.style.position = "absolute";
labelRenderer2.domElement.style.top = "0px";
// labelRenderer.domElement.style.pointerEvents = 'none';
container.appendChild(labelRenderer2.domElement);
function render() {
renderer.render(scene, camera);
labelRenderer.render(scene, camera);
labelRenderer2.render(scene, camera);
controls.update();
}
其他插件
性能检测
import Stats from './jsm/libs/stats.module.js';
let stats = new Stats();
container.appendChild( stats.dom );
function animate() {
requestAnimationFrame( animate );
render();
stats.update();
}
补间动画
<script src="./js/tween.min.js"></script>
function startTween() {
// 创建一个 tween 动画对象
var tween = new TWEEN.Tween(camera.position)
.to({ x: 20, y: 20, z: 20 }, 5000) // 定义结束位置和时间
.easing(TWEEN.Easing.Quadratic.Out) // 指定缓动函数
.onUpdate(function () {
// 动画更新回调函数,每次更新时执行
console.log(camera.position);
})
.onComplete(function () {
// 动画完成后执行的回调函数
console.log("Animation completed");
});
// 开始动画
tween.start();
}
水面
import { Water } from './jsm/objects/Water.js';
var waterGeometry = new THREE.PlaneBufferGeometry(10000, 10000);
let water = new Water(
waterGeometry,
{
textureWidth: 512,
textureHeight: 512,
waterNormals: new THREE.TextureLoader().load('./assets/img/waternormals.jpg', function (texture) {
texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
}),
alpha: 1.0,
sunDirection: light.position.clone().normalize(),
sunColor: 0xffffff,
waterColor: 0x00456e,
distortionScale: 5.7,
fog: scene.fog !== undefined
}
);
water.rotation.x = - Math.PI / 2;
scene.add(water);
function render() {
renderer.render(scene, camera);
controls.update()
water.material.uniforms[ 'time' ].value += 1.0 / 60.0;
}
太阳光
import { Lensflare, LensflareElement } from "./jsm/objects/Lensflare.js";
const pointLight = new THREE.PointLight(0xffffff, 1.2, 2000);
pointLight.color.setHSL(0.995, 0.5, 0.9);
pointLight.position.set(1000, 45, -500);
const textureLoader = new THREE.TextureLoader();
const textureFlare0 = textureLoader.load("./练习模型/模型5/lensflare0.png");
const textureFlare1 = textureLoader.load("./练习模型/模型5/lensflare1.png");
// 镜头光晕
const lensflare = new Lensflare();
lensflare.addElement(new LensflareElement(textureFlare0, 600, 0, pointLight.color));
lensflare.addElement(new LensflareElement(textureFlare1, 60, 0.6));
lensflare.addElement(new LensflareElement(textureFlare1, 70, 0.7));
lensflare.addElement(new LensflareElement(textureFlare1, 120, 0.9));
lensflare.addElement(new LensflareElement(textureFlare1, 70, 1));
pointLight.add(lensflare);
scene.add(pointLight);