如何用TresJS或TroisJS实现3D物体看向鼠标光标?
实现3D物体看向鼠标光标(TresJS 版本)
核心思路
原示例通过顶点着色器计算物体到鼠标的方向,让顶点朝向鼠标。在TresJS中可通过自定义着色器材质实现,关键是传递鼠标坐标、相机投影矩阵等 uniforms 给着色器。
完整实现步骤
1. 基础场景搭建
先创建TresJS基础场景,包含相机、渲染器和轨道控制器(可选)。
<template> <TresCanvas shadows> <TresPerspectiveCamera :position="[0, 0, 5]" /> <TresOrbitControls /> <!-- 带交互效果的物体 --> <LookAtMouseMesh /> <TresDirectionalLight :position="[5, 5, 5]" castShadow /> <TresAmbientLight :intensity="0.2" /> </TresCanvas> </template> <script setup> import { TresCanvas, TresPerspectiveCamera, TresOrbitControls, TresDirectionalLight, TresAmbientLight } from '@tresjs/core' import LookAtMouseMesh from './LookAtMouseMesh.vue' </script>
2. 自定义着色器组件(LookAtMouseMesh.vue)
这是核心部分,编写顶点/片段着色器,同时处理鼠标坐标传递。
<template> <TresMesh> <TresSphereGeometry :radius="1" :segments="32" /> <TresShaderMaterial :vertexShader="vertexShader" :fragmentShader="fragmentShader" :uniforms="uniforms" /> </TresMesh> </template> <script setup> import { ref, onMounted, watch } from 'vue' import { TresMesh, TresSphereGeometry, TresShaderMaterial } from '@tresjs/core' import { useThree } from '@tresjs/core' const { camera } = useThree() // 存储鼠标归一化坐标(-1到1范围) const mouse = ref({ x: 0, y: 0 }) // 着色器 uniforms const uniforms = ref({ mouse: { value: [mouse.value.x, mouse.value.y] }, cameraPosition: { value: camera.value.position }, projectionMatrix: { value: camera.value.projectionMatrix }, viewMatrix: { value: camera.value.matrixWorldInverse } }) // 顶点着色器:计算顶点朝向鼠标的方向 const vertexShader = /* glsl */` uniform vec2 mouse; uniform vec3 cameraPosition; uniform mat4 projectionMatrix; uniform mat4 viewMatrix; varying vec3 vNormal; void main() { vNormal = normal; // 将鼠标坐标从屏幕空间转换到世界空间 vec3 mouseWorld = (projectionMatrix * viewMatrix * vec4(mouse, 0.0, 1.0)).xyz; mouseWorld = normalize(mouseWorld - cameraPosition); // 计算顶点朝向鼠标的旋转 vec3 objectNormal = normalize(normalMatrix * normal); vec3 newNormal = normalize(mix(objectNormal, mouseWorld, 0.5)); // 混合系数可调整 // 应用旋转到顶点位置 vec3 transformed = vec3(position); transformed += newNormal * 0.1; // 可选:添加顶点位移效果 gl_Position = projectionMatrix * modelViewMatrix * vec4(transformed, 1.0); } ` // 片段着色器:基础漫反射效果 const fragmentShader = /* glsl */` varying vec3 vNormal; void main() { vec3 lightDirection = normalize(vec3(1.0, 1.0, 1.0)); float diff = max(dot(vNormal, lightDirection), 0.0); gl_FragColor = vec4(diff * vec3(0.8, 0.2, 0.2), 1.0); } ` // 监听鼠标移动,更新归一化坐标 const handleMouseMove = (event) => { const { clientX, clientY } = event const { innerWidth, innerHeight } = window mouse.value.x = (clientX / innerWidth) * 2 - 1 mouse.value.y = -(clientY / innerHeight) * 2 + 1 uniforms.value.mouse.value = [mouse.value.x, mouse.value.y] } onMounted(() => { window.addEventListener('mousemove', handleMouseMove) }) // 监听相机参数变化,更新uniforms watch(camera, (newCam) => { uniforms.value.cameraPosition.value = newCam.position uniforms.value.projectionMatrix.value = newCam.projectionMatrix uniforms.value.viewMatrix.value = newCam.matrixWorldInverse }, { deep: true }) </script>
3. 关键细节说明
- 鼠标坐标转换:将屏幕坐标转为ThreeJS归一化设备坐标,再通过相机矩阵转成世界空间,才能计算物体到鼠标的方向。
- uniforms实时更新:需实时传递鼠标坐标、相机位置和矩阵,确保效果随鼠标移动、相机视角变化同步更新。
- 旋转强度调整:顶点着色器中
mix(objectNormal, mouseWorld, 0.5)的0.5可修改,值越大物体朝向鼠标的程度越高。
4. TroisJS 适配提示
如果用TroisJS,核心逻辑一致,仅组件导入路径改为troisjs,示例结构类似:
<template> <TroisCanvas> <Camera :position="[0, 0, 5]" /> <OrbitControls /> <Mesh> <SphereGeometry :radius="1" /> <ShaderMaterial :vertexShader="vertexShader" :fragmentShader="fragmentShader" :uniforms="uniforms" /> </Mesh> </TroisCanvas> </template>
内容的提问来源于stack exchange,提问作者Pedro BARROS PARENTE
相关产品推荐
相关产品推荐

