three.js 光线投射 指定物体焦点 物体变色和描边

我要实现的效果是:点击三个球中的某一个,那个球发亮。

实现的原理是: 三个球在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>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值