样例
点击之后的效果
直接上代码
<script setup>
import * as THREE from "three";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
// 导入轨道模型控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
// 创建场景
const scene = new THREE.Scene();
// 创建相机
const camera = new THREE.PerspectiveCamera(
100,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.set(15, 10, -5);
// 创建渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 添加控制器
let control = new OrbitControls(camera, renderer.domElement);
//添加直线光
let light1 = new THREE.DirectionalLight(0xffffff, 1);
light1.position.set(0, 50, 50);
let light2 = new THREE.DirectionalLight(0xffffff, 1);
light2.position.set(0, 50, -50);
let light3 = new THREE.DirectionalLight(0xffffff, 1);
light3.position.set(50, 50, 50);
let light4 = new THREE.DirectionalLight(0xffffff, 1);
light4.position.set(-50, -10, 0);
let light5 = new THREE.DirectionalLight(0xffffff, 1);
light5.position.set(0, 0, 50);
let light6 = new THREE.DirectionalLight(0xffffff, 1);
light6.position.set(0, 0, -50);
let light7 = new THREE.DirectionalLight(0xffffff, 1);
light7.position.set(50, 0, 0);
let light8 = new THREE.DirectionalLight(0xffffff, 1);
light8.position.set(-50, 0, 0);
scene.add(light1, light2, light3, light4, light5, light6, light7, light8);
// 创建天空盒
// 相应面对应相应图片
const imgUrl = [
"bak5/right.jpg",
"bak5/left.jpg",
"bak5/top.jpg",
"bak5/down.jpg",
"bak5/back.jpg",
"bak5/front.jpg",
];
// 调用getTexturesFromAtlasFile() 给每个材质加上相应的图片
const textures = getTexturesFromAtlasFile(imgUrl, 6);
const materials = [];
for (let i = 0; i < 6; i++) {
// 创造六个面的材质
materials.push(new THREE.MeshBasicMaterial({ map: textures[i] }));
}
//创造包围盒
const skyBox = new THREE.Mesh(
new THREE.BoxGeometry(1024, 1024, 1024),
materials
);
// skyBox.position.set(0, 0, 0);
skyBox.geometry.scale(1, 1, -1);
scene.add(skyBox);
// 六个面添加图片
function getTexturesFromAtlasFile(atlasImgUrl, tilesNum) {
const textures = [];
for (let i = 0; i < tilesNum; i++) {
textures[i] = new THREE.Texture();
}
for (let i = 0; i < textures.length; i++) {
const imageObj = new Image();
imageObj.src = atlasImgUrl[i];
imageObj.onload = () => {
let context = "";
// let tileWidth = imageObj.height;
// let tileWidth = 5000;
const canvas = document.createElement("canvas");
// const canvas: HTMLCanvasElement = this.canvasRef.nativeElement; // 得到canvas 元素
context = canvas.getContext("2d");
const canvasHeight = 720;
canvas.height = canvasHeight;
canvas.width = canvasHeight;
// context.drawImage( imageObj, canvasHeight * i, 0, canvasHeight, canvasHeight, 0, 0, canvasHeight, canvasHeight );
context.drawImage(imageObj, 0, 0, canvasHeight, canvasHeight);
textures[i].image = canvas;
textures[i].needsUpdate = true;
};
}
return textures;
}
// 加载模型
const loader = new GLTFLoader();
loader.load(
"/model/scene.gltf",
function (gltf) {
scene.add(gltf.scene);
},
undefined,
function (error) {
console.error(error);
}
);
// 添加点击事件(官方)
let intersects = []; //几何体合集
const pointer = new THREE.Vector2();
document.addEventListener("click", meshOnClick);
let raycaster = new THREE.Raycaster();
function meshOnClick(event) {
pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
pointer.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(pointer, camera);
//geometrys为需要监听的Mesh合集,可以通过这个集合来过滤掉不需要监听的元素例如地面天空
//true为不拾取子对象
intersects = raycaster.intersectObjects(scene.children, true);
//被射线穿过的几何体为一个集合,越排在前面说明其位置离端点越近,所以直接取[0]
if (intersects.length > 0) {
//alert(intersects[0].object.name);
console.log(intersects[0].object);
if (intersects[0].object.name == "nature_Nature_0") {
intersects[0].object.material.color.set(0xff0000);
}
if (intersects[0].object.name == "water_Water_0") {
intersects[0].object.material.color.set(0x0000ff);
}
} else {
//若没有几何体被监听到,可以做一些取消操作
}
}
// 渲染函数
function animate() {
requestAnimationFrame(animate);
// 渲染
renderer.render(scene, camera);
}
animate();
</script>
<template></template>
<style>
* {
margin: 0;
padding: 0;
}
canvas {
display: block;
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
}
</style>
代码是在创建了vue项目之后直接在App里写的,但是不建议这么写,一般App里都是加载主代码,可以另外创建一个文件写。
模型是在 sketchfab.com/3d-models?f… 里免费下载的,大家有需要可以自己去下载