我要实现的效果是:点击三个球中的某一个,那个球发亮。
实现的原理是: 三个球在3D的位置, 相机通过射线到一个canvas 的点位是落在3D的物体位置。
创建三个球:
创建射线,如何理解以下红色框,红色框的意思是 鼠标的坐标轴相对于canvas画布从最左上角0,0的位置的对应的位置。
可以设置指定获取某些物体:
显示一个:
则会显示两个:
展示每个焦点的属性:
对物体的改变颜色:
展示:
原来:
点击后:
如何查看焦点属性,颜色的位置:
源码:
<script setup>
// 导入 threejs
import * as THREE from "three";
// 导入轨道控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
// 导入 GUI
import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";
// 导入hdr加载器
import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js";
// 导入glb加载器
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
// 导入draco解码器
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader.js";
// Postprocessing imports
import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer.js";
import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass.js";
import { OutlinePass } from "three/examples/jsm/postprocessing/OutlinePass.js";
import { ShaderPass } from "three/examples/jsm/postprocessing/ShaderPass.js";
import { FXAAShader } from "three/examples/jsm/shaders/FXAAShader.js";
// 创建场景
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x87ceeb); // 浅蓝色
// 创建相机
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.x = 2;
camera.position.y = 10;
camera.lookAt(0, 0, 0);
// 添加坐标辅助器
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
// 轨道控制器
const controls = new OrbitControls(camera, renderer.domElement);
controls.update();
// Post-processing setup
const composer = new EffectComposer(renderer);
const renderPass = new RenderPass(scene, camera);
composer.addPass(renderPass);
const outlinePass = new OutlinePass(
new THREE.Vector2(window.innerWidth, window.innerHeight),
scene,
camera
);
composer.addPass(outlinePass);
// FXAA Anti-aliasing
const effectFXAA = new ShaderPass(FXAAShader);
effectFXAA.uniforms['resolution'].value.set(
1 / window.innerWidth,
1 / window.innerHeight
);
composer.addPass(effectFXAA);
// 设置 OutlinePass 的参数
outlinePass.edgeStrength = 3.0; // 轮廓强度
outlinePass.edgeGlow = 0.0;
outlinePass.edgeThickness = 1.0; // 轮廓厚度
outlinePass.pulsePeriod = 0; // 无脉冲效果
outlinePass.visibleEdgeColor.set("#ff0000"); // 轮廓颜色
outlinePass.hiddenEdgeColor.set("#190a05");
// 渲染函数
function animate() {
requestAnimationFrame(animate);
controls.update();
composer.render();
}
animate();
// 监听窗口变化
window.addEventListener("resize", () => {
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
composer.setSize(window.innerWidth, window.innerHeight);
effectFXAA.uniforms['resolution'].value.set(
1 / window.innerWidth,
1 / window.innerHeight
);
});
// 创建 lil-gui 控制面板
const gui = new GUI();
// 创建三个球 第一个球
const sphere1 = new THREE.Mesh(
new THREE.SphereGeometry(1, 32, 32),
new THREE.MeshBasicMaterial({ color: 0x00ff00 })
);
sphere1.position.x = -2;
scene.add(sphere1);
// 第二个球
const sphere2 = new THREE.Mesh(
new THREE.SphereGeometry(1, 32, 32),
new THREE.MeshBasicMaterial({ color: 0x0000ff })
);
sphere2.position.x = -4;
sphere2.position.z = 2;
scene.add(sphere2);
// 第三个球
const sphere3 = new THREE.Mesh(
new THREE.SphereGeometry(1, 32, 32),
new THREE.MeshBasicMaterial({ color: 0xcccccc })
);
sphere3.position.x = -4;
sphere3.position.z = -2;
scene.add(sphere3);
// 创建射线
const raycaster = new THREE.Raycaster();
// 创建鼠标向量
const mouse = new THREE.Vector2();
let selectedObjects = [];
// 点击事件处理
window.addEventListener("click", (event) => {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects([sphere1, sphere2, sphere3]);
if (intersects.length > 0) {
const object = intersects[0].object;
// Toggle outline effect
if (selectedObjects.includes(object)) {
selectedObjects = selectedObjects.filter(obj => obj !== object);
} else {
selectedObjects = [object]; // 单选
}
outlinePass.selectedObjects = selectedObjects;
}
});
</script>
<style>
* {
margin: 0;
padding: 0;
}
canvas {
display: block;
position: fixed;
left: 0;
top: 0;
width: 100vw;
height: 100vh;
}
</style>
描边使用outline
展示:
代码:
<script setup>
// 导入 threejs
import * as THREE from "three";
// 导入轨道控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
// 导入 GUI
import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";
// 导入hdr加载器
import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js";
// 导入glb加载器
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
// 导入draco解码器
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader.js";
// Postprocessing imports
import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer.js";
import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass.js";
import { OutlinePass } from "three/examples/jsm/postprocessing/OutlinePass.js";
import { ShaderPass } from "three/examples/jsm/postprocessing/ShaderPass.js";
import { FXAAShader } from "three/examples/jsm/shaders/FXAAShader.js";
// 创建场景
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x87ceeb); // 浅蓝色
// 创建相机
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.x = 2;
camera.position.y = 10;
camera.lookAt(0, 0, 0);
// 添加坐标辅助器
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
// 轨道控制器
const controls = new OrbitControls(camera, renderer.domElement);
controls.update();
// Post-processing setup
const composer = new EffectComposer(renderer);
const renderPass = new RenderPass(scene, camera);
composer.addPass(renderPass);
const outlinePass = new OutlinePass(
new THREE.Vector2(window.innerWidth, window.innerHeight),
scene,
camera
);
composer.addPass(outlinePass);
// FXAA Anti-aliasing
const effectFXAA = new ShaderPass(FXAAShader);
effectFXAA.uniforms['resolution'].value.set(
1 / window.innerWidth,
1 / window.innerHeight
);
composer.addPass(effectFXAA);
// 设置 OutlinePass 的参数
outlinePass.edgeStrength = 30.0; // 轮廓强度
outlinePass.edgeGlow = 0.0;
outlinePass.edgeThickness = 1.0; // 轮廓厚度
outlinePass.pulsePeriod = 0; // 无脉冲效果
outlinePass.visibleEdgeColor.set("#ff0000"); // 轮廓颜色
outlinePass.hiddenEdgeColor.set("#190a05");
// 渲染函数
function animate() {
requestAnimationFrame(animate);
controls.update();
composer.render();
}
animate();
// 监听窗口变化
window.addEventListener("resize", () => {
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
composer.setSize(window.innerWidth, window.innerHeight);
effectFXAA.uniforms['resolution'].value.set(
1 / window.innerWidth,
1 / window.innerHeight
);
});
// 创建 lil-gui 控制面板
const gui = new GUI();
// 创建三个球 第一个球
const sphere1 = new THREE.Mesh(
new THREE.SphereGeometry(1, 32, 32),
new THREE.MeshBasicMaterial({ color: 0x00ff00 })
);
sphere1.position.x = -2;
scene.add(sphere1);
// 第二个球
const sphere2 = new THREE.Mesh(
new THREE.SphereGeometry(1, 32, 32),
new THREE.MeshBasicMaterial({ color: 0x0000ff })
);
sphere2.position.x = -4;
sphere2.position.z = 2;
scene.add(sphere2);
// 第三个球
const sphere3 = new THREE.Mesh(
new THREE.SphereGeometry(1, 32, 32),
new THREE.MeshBasicMaterial({ color: 0xcccccc })
);
sphere3.position.x = -4;
sphere3.position.z = -2;
scene.add(sphere3);
// 创建射线
const raycaster = new THREE.Raycaster();
// 创建鼠标向量
const mouse = new THREE.Vector2();
let selectedObjects = [];
// 点击事件处理
window.addEventListener("click", (event) => {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects([sphere1, sphere2, sphere3]);
if (intersects.length > 0) {
const object = intersects[0].object;
// Toggle outline effect
if (selectedObjects.includes(object)) {
selectedObjects = selectedObjects.filter(obj => obj !== object);
} else {
selectedObjects = [object]; // 单选
}
outlinePass.selectedObjects = selectedObjects;
}
});
</script>
<style>
* {
margin: 0;
padding: 0;
}
canvas {
display: block;
position: fixed;
left: 0;
top: 0;
width: 100vw;
height: 100vh;
}
</style>