Threejs的分享
回顾上篇知识点
-
Threejs官方文档:threejs.org/
第一步
-
Scene — 场景。场景能够让你在什么地方、摆放什么东西来交给three.js来渲染。类似于去照相馆,摄影师,背景和你就成为一个场景
-
Camera — 摄像机。 PerspectiveCamera(透视摄像机)、OrthographicCamera(正交摄像机)
透视和正交的区别:透视相机模拟人眼,远小近大。正交相机远近一样,适合2D游戏或UI元素。
-
Renderer — 渲染器。用WebGL渲染出你精心制作的场景。
第二步
-
Geometry — 几何体。 例如:BoxGeometry(立方体),PlaneGeometry(平面体),SphereGeometry(球体)
-
Textures — 纹理贴图。Textures的介绍
Q:为什么做地球仪的时候长方形的地图可以包住球形?A: 类似手机的全景模式下拍的一个360度的照片是一个长方形一样。包裹模式
-
Material — 材质。以什么样的方式来绘制几何体的材质,比如是否受光照影响 MeshBasicMaterial(不受光照影响)
代码示例
<template>
<div id="container"></div>
</template>
<script>
/* eslint-disable */
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
export default {
name: "HelloThreeD",
mounted() {
var scene, camera, renderer, cube;
function initThree() {
//场景
scene = new THREE.Scene();
//镜头
camera = new THREE.PerspectiveCamera( 75, document.body.clientWidth / document.body.clientHeight, 0.1, 1000);
camera.position.set(5, 5, 5);
//渲染器
renderer = new THREE.WebGLRenderer();
renderer.setSize(document.body.clientWidth, document.body.clientHeight);
document.getElementById("container").appendChild(renderer.domElement);
// 创建坐标
const axis = new THREE.AxesHelper(5);
scene.add(axis);
//镜头控制器
var controls = new OrbitControls(camera, renderer.domElement);
controls.update();
// 添加立方体
const geometry = new THREE.BoxGeometry(2,2,2);
const material = new THREE.MeshBasicMaterial({ color: 0xff0000,transparent: true, opacity: 0.8 });
// 添加地球
// const geometry = new THREE.SphereGeometry(2);
// var earthTexture = new THREE.TextureLoader().load("./images.jpeg");
// const material = new THREE.MeshBasicMaterial({
// map: earthTexture,
// });
cube = new THREE.Mesh(geometry, material);
scene.add(cube);
animate();
}
function animate() {
requestAnimationFrame(animate);
cube.rotation.y += 0.01;
renderer.render(scene, camera);
}
window.onload = initThree;
},
};
</script>
<style >
html,
body,
#app {
margin: 0px !important;
}
#container {
height: 100vh;
}
</style>
本篇重点介绍的api
第一步
-
GLTFLoader — 加载器。文档
第二步
-
Light — 灯光。DirectionalLight(平行光)、 PointLight(点光源)例子
点光源和平行光的区别:点光源类似灯泡,产生明暗不一的光照效果。平行光类似于太阳光,光照效果在整个场景中是均匀的,不会随着距离的增加而衰减。
-
Material — 材质。 MeshBasicMaterial(不受光照影响)、MeshStandardMaterial(标准材质)、MeshPhysicalMaterial(适用于玻璃)
metalness 与金属的相似度。非金属材质,如木材或石材,使用0.0,金属使用1.0,通常没有中间值。 roughness :粗糙程度,0.0表示平滑的镜面反射,1.0表示完全漫反射
第三步
-
lil-gui库— 控制gui。例子
代码示例
<template>
<div>
<div id="container"></div>
</div>
</template>
<script>
import * as THREE from "three";
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
import Stats from "three/examples/jsm/libs/stats.module.js";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { UnrealBloomPass } from "three/examples/jsm/postprocessing/UnrealBloomPass";
import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer";
import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass";
import { OutputPass } from "three/examples/jsm/postprocessing/OutputPass";
import { ShaderPass } from "three/examples/jsm/postprocessing/ShaderPass";
import GUI from "lil-gui";
export default {
name: "HelloThreeD",
data() {
return {
camera: null,
scene: null,
renderer: null,
mesh: null,
model: null,
controls: null,
bodyMatetial: null,
wheelMatetial: null,
glassMaterial: null,
lightMaterial: null,
composer: null,
lightOpen: false,
};
},
mounted() {
this.init();
this.initBloomPass();
this.initFloor();
this.initLight();
this.animate();
this.initGui();
},
methods: {
//初始化
init() {
// 创建场景对象Scene
this.scene = new THREE.Scene();
// 背景颜色
this.scene.background = new THREE.Color("#f9f9f9");
//添加坐标轴
var axes = new THREE.AxesHelper(500); //500表示xyz轴的长度,红:x,绿:y,蓝:z
this.scene.add(axes);
//网格模型添加到场景中
let geometry = new THREE.BoxGeometry(1, 1, 1);
let material = new THREE.MeshNormalMaterial({
color: "black",
});
this.mesh = new THREE.Mesh(geometry, material);
/**
* 相机设置
*/
let container = document.getElementById("container");
//创建一个摄像机对象(摄像机决定了能够在场景里看到什么)
this.camera = new THREE.PerspectiveCamera(
40,
window.innerWidth / window.innerHeight,
0.1,
1000
);
//以下是 一个正常的属性设置,比较符合常规三维软件的视角:左右为X轴,右为正;前后为Y轴,前为正;上下为Z轴,上为正。
this.camera.position.set(12, 7, 5);
// 加载模型 模型必须整包放在public目录
const gltfLoader = new GLTFLoader();
gltfLoader.load("./car.gltf", (gltf) => {
this.model = gltf.scene;
// 计算模型的包围盒 为了是模型水平居中
var box = new THREE.Box3().setFromObject(this.model);
var size = new THREE.Vector3();
var center = new THREE.Vector3();
box.getCenter(center);
box.getSize(size);
// 定义更换的车身材质 初始值 下有方法initGui支持点击选颜色更换
this.bodyMatetial = new THREE.MeshStandardMaterial({
color: "red",
metalness: 1,
roughness: 0.5,
clearcoat: 1,
clearcoatRoughness: 0,
});
this.glassMaterial = new THREE.MeshPhysicalMaterial({
color: "#793e3e",
metalness: 0.5,
roughness: 0.1,
transmission: 1.0, //透光性
opacity:0.9,
transparent: true
});
// 定义初始的车轱辘
this.wheelMatetial = new THREE.MeshPhongMaterial({
color: "#ffffff",
metalness: 1,
roughness: 0.5,
clearcoat: 1,
clearcoatRoughness: 0,
});
this.model.traverse((item) => {
// 车身有这么多的部件构成 全部写上 更换为上面定义的材质bodyMatetial
if (
["Obj3d66-870414-4-94", "Obj3d66-870414-4-94_3"].includes(item.name)
) {
item.material = this.bodyMatetial;
}
//车窗玻璃
if (
["Obj3d66-870414-4-94_1", "Obj3d66-870414-4-94_2"].includes(
item.name
)
) {
item.material = this.glassMaterial;
}
//轱辘
if (["Obj3d66-870414-4-94_12"].includes(item.name)) {
item.material = new THREE.MeshStandardMaterial({
color: "black",
roughness: 1,
metalness: 0.1,
});
}
if (["Obj3d66-870414-4-94_13"].includes(item.name)) {
item.material = this.wheelMatetial;
}
});
this.scene.add(this.model);
});
/**
* 创建渲染器对象
*/
this.renderer = new THREE.WebGLRenderer({ antialias: true });
this.renderer.setSize(container.clientWidth, container.clientHeight);
container.appendChild(this.renderer.domElement);
//创建控件对象
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
},
//辉光效果
initBloomPass() {
// 场景渲染器
this.composer = new EffectComposer(this.renderer);
const renderPass = new RenderPass(this.scene, this.camera);
this.composer.addPass(renderPass);
//创建辉光效果
this.unrealBloomPass = new UnrealBloomPass(
new THREE.Vector2(window.innerWidth, window.innerHeight),
1.5,
0.4,
0.85
);
this.unrealBloomPass.threshold = 1; // 辉光强度
this.unrealBloomPass.strength = 1.5; // 辉光阈值
this.unrealBloomPass.radius = 1; //辉光半径
this.unrealBloomPass.renderToScreen = false; //
// 辉光合成器
this.glowComposer = new EffectComposer(this.renderer);
this.glowComposer.renderToScreen = false;
this.glowComposer.addPass(new RenderPass(this.scene, this.camera));
this.glowComposer.addPass(this.unrealBloomPass);
// 着色器
let shaderPass = new ShaderPass(
new THREE.ShaderMaterial({
uniforms: {
baseTexture: { value: null },
bloomTexture: { value: this.glowComposer.renderTarget2.texture },
tDiffuse: {
value: null,
},
},
vertexShader:
"\t\t\tvarying vec2 vUv;\n" +
"\n" +
"\t\t\tvoid main() {\n" +
"\n" +
"\t\t\t\tvUv = uv;\n" +
"\n" +
"\t\t\t\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n" +
"\n" +
"\t\t\t}",
fragmentShader:
"\t\t\tuniform sampler2D baseTexture;\n" +
"\t\t\tuniform sampler2D bloomTexture;\n" +
"\n" +
"\t\t\tvarying vec2 vUv;\n" +
"\n" +
"\t\t\tvoid main() {\n" +
"\n" +
"\t\t\t\tgl_FragColor = ( texture2D( baseTexture, vUv ) + vec4( 1.0 ) * texture2D( bloomTexture, vUv ) );\n" +
"\n" +
"\t\t\t}",
defines: {},
}),
"baseTexture"
);
shaderPass.renderToScreen = true;
shaderPass.needsSwap = true;
this.composer.addPass(shaderPass);
},
// 动画 旋转
animate() {
this.renderer.render(this.scene, this.camera);
if (this.lightOpen) {
this.composer.render();
this.glowComposer.render();
}
requestAnimationFrame(this.animate);
},
// 添加地面
initFloor() {
const floorGeometry = new THREE.PlaneGeometry(200, 200);
const material = new THREE.MeshPhysicalMaterial({
color: "#818181",
side: THREE.DoubleSide,
metalness: 0,
roughness: 0.1,
});
const floorMesh = new THREE.Mesh(floorGeometry, material);
// 默认垂直的 旋转以下变成水平的
floorMesh.rotation.x = Math.PI / 2;
floorMesh.receiveShadow = true;
this.scene.add(floorMesh);
},
// 添加聚光灯和阴影
initLight() {
const light = new THREE.DirectionalLight(0xffffff, 1); // 柔和的白光
light.position.set(0, 0, 10);
this.scene.add(light);
const light2 = new THREE.DirectionalLight(0xffffff, 1); // 柔和的白光
light2.position.set(0, 10, 0);
this.scene.add(light2);
const light3 = new THREE.DirectionalLight(0xffffff, 1); // 柔和的白光
light3.position.set(0, 0, -10);
this.scene.add(light3);
const light4 = new THREE.DirectionalLight(0xffffff, 1); // 柔和的白光
light4.position.set(10, 10, 0);
this.scene.add(light4);
const light5 = new THREE.DirectionalLight(0xffffff, 1); // 柔和的白光
light5.position.set(0, 10, -10);
this.scene.add(light5);
const light6 = new THREE.DirectionalLight(0xffffff, 1); // 柔和的白光
light5.position.set(10, 0, 0);
this.scene.add(light6);
},
// 添加改变颜色的面板 这里用到包 lil-gui 需要安装引入
// 网址 https://lil-gui.georgealways.com/
initGui() {
let obj = {
bodyColor: "#ff0000",
glassColor: "#793e3e",
wheelColor: "#ffffff",
};
// 初始化
const gui = new GUI();
// gui.addColor就可以出现颜色选择器
gui
.addColor(obj, "bodyColor")
.name("车身颜色")
.onChange((value) => {
this.bodyMatetial.color.set(value);
});
gui
.addColor(obj, "glassColor")
.name("车窗颜色")
.onChange((value) => {
this.glassMaterial.color.set(value);
});
gui
.addColor(obj, "wheelColor")
.name("车轱辘颜色")
.onChange((value) => {
this.wheelMatetial.color.set(value);
});
//设置灯光开关
gui
.add(this, "lightOpen")
.name("开关")
.onChange((value) => {
this.lightOpen = value;
});
},
},
};
</script>
<style>
html,
body,
#app {
margin: 0px !important;
}
#container {
height: 100vh;
}
.btn {
position: absolute;
top: 0px;
left: 0px;
}
</style>