three.js基础之加载外部模型、纹理贴图、投射阴影、几何体颜色属性
初始化共用方法
<canvas id="model"></canvas>
<canvas id="texture"></canvas>
<canvas id="shadow"></canvas>
<canvas id="color"></canvas>
<script type="importmap">
{
"imports": {
"three": "./js/build/three.module.js",
"three/addons/": "./js/jsm/"
}
}
</script>
<script type="module">
import * as THREE from "three";
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
function init(domId, callback) {
const width = 300;
const height = 200;
const renderer = new THREE.WebGLRenderer({
canvas: document.getElementById(domId),
antialias: true,
});
renderer.setClearColor(0x000000);
renderer.setSize(width, height);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.shadowMap.enabled = true; //渲染器允许阴影渲染
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 1000);
camera.position.set(-30, 30, 30);
camera.lookAt(0, 0, 0);
scene.add(camera);
const ambienLight = new THREE.AmbientLight(0xffffff, 1);
scene.add(ambienLight);
const spotLight = new THREE.SpotLight(0xffffff, 20, 0, Math.PI / 3, 0, 0);
spotLight.position.set(-30, 30, -30);
spotLight.castShadow = true;
scene.add(spotLight);
const axesHelper = new THREE.AxesHelper(150);
scene.add(axesHelper);
callback(scene);
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
animate();
}
</script>
外部模型
init("model", (scene) => {
// 加载模型
const loader = new GLTFLoader();
loader.load(
"./models/Flower.glb",
function (gltf) {
console.log("gltf文件返回的对象结构", gltf);
console.log("gltf对象场景", gltf.scene);
gltf.scene.position.set(0, 0, 0);
gltf.scene.scale.set(50, 50, 50);
scene.add(gltf.scene);
//查看模型尺寸
const box = new THREE.Box3().setFromObject(gltf.scene);
const size = box.getSize(new THREE.Vector3());
console.log(size);
},
function (xhr) {
const percent = xhr.loaded / xhr.total;
console.log("加载进度" + percent);
}
);
});
纹理贴图
init("texture", (scene) => {
//纹理贴图加载器
const texLoader = new THREE.TextureLoader();
//纹理贴图
texLoader.load("./images/FloorsCheckerboard_S_Diffuse.jpg", (texture) => {
//设置为SRGB颜色空间
texture.colorSpace = THREE.SRGBColorSpace;
const material = new THREE.MeshBasicMaterial({
map: texture, //材质的颜色贴图属性
side: THREE.DoubleSide,
});
const geometry = new THREE.PlaneGeometry(30, 20);
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
});
});
投射阴影
init("shadow", (scene) => {
const planeGeometry = new THREE.PlaneGeometry(50, 20, 1, 1);
const planeMaterial = new THREE.MeshLambertMaterial({ color: 0xffffff });
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.receiveShadow = true; //接收阴影效果
plane.position.set(10, 0, 0);
plane.rotateX(-Math.PI / 2);
scene.add(plane);
const cubeGeometry = new THREE.BoxGeometry(4, 4, 4);
const cubeMaterial = new THREE.MeshLambertMaterial({ color: 0xff0000 });
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.position.set(-5, 5, 0);
cube.castShadow = true; //产生阴影的模型对象
scene.add(cube);
const sphereGeometry = new THREE.SphereGeometry(4, 20, 20);
const sphereMaterial = new THREE.MeshLambertMaterial({ color: 0x7777ff });
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
sphere.position.set(10, 5, 0);
sphere.castShadow = true; //产生阴影的模型对象
scene.add(sphere);
});
几何体颜色属性
init("color", (scene) => {
const geometry = new THREE.BufferGeometry();
const vertices = new Float32Array([0, 0, 0, 5, 0, 0, 0, 5, 0]);
geometry.attributes.position = new THREE.BufferAttribute(vertices, 3);
const colors = new Float32Array([1, 0, 0, 0, 0, 1, 0, 1, 0]);
// 设置几何体attributes属性的颜色color属性,3个为一组,表示一个顶点的颜色数据RGB
geometry.attributes.color = new THREE.BufferAttribute(colors, 3);
const material = new THREE.MeshBasicMaterial({
vertexColors: true, //默认false,设置为true表示使用顶点颜色渲染
side: THREE.DoubleSide,
});
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
const geometry2 = new THREE.BufferGeometry();
const curve2 = new THREE.CatmullRomCurve3([new THREE.Vector3(-5, 2, 9), new THREE.Vector3(-1, 4, 4), new THREE.Vector3(0, 0, 0), new THREE.Vector3(6, -6, 0), new THREE.Vector3(7, 0, 8)]);
const pointsArr2 = curve2.getSpacedPoints(100);
geometry2.setFromPoints(pointsArr2);
const pos = geometry2.attributes.position;
const count = pos.count;
// 计算每个顶点的颜色值
const colorsArr = [];
for (let i = 0; i < count; i++) {
const percent = i / count;
colorsArr.push(percent, 0, 1 - percent); //蓝色到红色渐变色
}
const colors2 = new Float32Array(colorsArr);
geometry2.attributes.color = new THREE.BufferAttribute(colors2, 3);
const material2 = new THREE.LineBasicMaterial({
vertexColors: true, //使用顶点颜色渲染
});
const line = new THREE.Line(geometry2, material2);
scene.add(line);
const colorsArr3 = [];
// 根据顶点距离起点远近进行颜色插值计算
const c1 = new THREE.Color(0x00ffff); //曲线起点颜色
const c2 = new THREE.Color(0xffff00); //曲线结束点颜色
for (let i = 0; i < count; i++) {
const percent = i / count;
const c = c1.clone().lerp(c2, percent); //颜色插值计算
colorsArr3.push(c.r, c.g, c.b);
}
const colors3 = new Float32Array(colorsArr3); //类型数组创建顶点颜色color数据
const geometry3 = geometry2.clone();
geometry3.attributes.color = new THREE.BufferAttribute(colors3, 3);
geometry3.translate(5, 2, 1);
const line3 = new THREE.Line(geometry3, material2);
scene.add(line3);
});
面模型属性
<canvas id="mesh-properties"></canvas>
<script type="importmap">
{
"imports": {
"three": "./js/build/three.module.js",
"three/addons/": "./js/jsm/"
}
}
</script>
<script type="module">
import * as THREE from "three";
import { GUI } from "three/addons/libs/lil-gui.module.min.js";
const gui = new GUI();
let renderer, scene, camera;
function init(domId, callback) {
const width = window.innerWidth;
const height = window.innerHeight;
renderer = new THREE.WebGLRenderer({
canvas: document.getElementById(domId),
antialias: true,
});
renderer.setClearColor(0x000000);
renderer.setSize(width, height);
renderer.setPixelRatio(window.devicePixelRatio);
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 1000);
camera.position.set(-30, 30, 30);
camera.lookAt(0, 0, 0);
scene.add(camera);
const ambienLight = new THREE.AmbientLight(0xffffff, 1);
scene.add(ambienLight);
const axesHelper = new THREE.AxesHelper(150);
scene.add(axesHelper);
callback(scene);
renderer.render(scene, camera);
}
init("mesh-properties", (scene) => {
const planeGeometry = new THREE.PlaneGeometry(60, 40, 1, 1);
const planeMaterial = new THREE.MeshLambertMaterial({
color: 0xffffff,
});
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.receiveShadow = true;
plane.rotation.x = -0.5 * Math.PI;
plane.position.set(0, 0, 0);
scene.add(plane);
const controls = new (function () {
this.scaleX = 1;
this.scaleY = 1;
this.scaleZ = 1;
this.positionX = 0;
this.positionY = 4;
this.positionZ = 0;
this.rotationX = 0;
this.rotationY = 0;
this.rotationZ = 0;
this.scale = 1;
this.translateX = 0;
this.translateY = 0;
this.translateZ = 0;
this.visible = true;
this.translate = function () {
cube.translateX(controls.translateX);
cube.translateY(controls.translateY);
cube.translateZ(controls.translateZ);
controls.positionX = cube.position.x;
controls.positionY = cube.position.y;
controls.positionZ = cube.position.z;
};
})();
const material = new THREE.MeshLambertMaterial({ color: 0x44ff44 });
const geom = new THREE.BoxGeometry(5, 4, 3);
const cube = new THREE.Mesh(geom, material);
cube.position.y = 4;
cube.castShadow = true;
scene.add(cube);
const guiScale = gui.addFolder("scale");
guiScale.add(controls, "scaleX", 0, 5);
guiScale.add(controls, "scaleY", 0, 5);
guiScale.add(controls, "scaleZ", 0, 5);
const guiPosition = gui.addFolder("position");
const contX = guiPosition.add(controls, "positionX", -10, 10);
const contY = guiPosition.add(controls, "positionY", -4, 20);
const contZ = guiPosition.add(controls, "positionZ", -10, 10);
contX.listen();
contX.onChange(function (value) {
cube.position.x = controls.positionX;
});
contY.listen();
contY.onChange(function (value) {
cube.position.y = controls.positionY;
});
contZ.listen();
contZ.onChange(function (value) {
cube.position.z = controls.positionZ;
});
const guiRotation = gui.addFolder("rotation");
guiRotation.add(controls, "rotationX", -4, 4);
guiRotation.add(controls, "rotationY", -4, 4);
guiRotation.add(controls, "rotationZ", -4, 4);
const guiTranslate = gui.addFolder("translate");
guiTranslate.add(controls, "translateX", -10, 10);
guiTranslate.add(controls, "translateY", -10, 10);
guiTranslate.add(controls, "translateZ", -10, 10);
gui.add(controls, "visible");
render();
function render() {
cube.visible = controls.visible;
cube.rotation.x = controls.rotationX;
cube.rotation.y = controls.rotationY;
cube.rotation.z = controls.rotationZ;
cube.position.x = controls.translateX;
cube.position.y = controls.translateY;
cube.position.z = controls.translateZ;
cube.scale.set(controls.scaleX, controls.scaleY, controls.scaleZ);
requestAnimationFrame(render);
renderer.render(scene, camera);
}
});
</script>