基于视角角度为React Three Fiber中GLB模型添加玻璃效果
React Three Fiber 实现视角驱动的玻璃边缘光晕效果
需要为GLB模型实现基于视角角度的玻璃效果,核心要求是:
- 模型边缘呈现白色光晕,且光晕随视角旋转动态变化
- 效果需匹配目标截图的视觉表现(边缘锐利的白色发光,主体玻璃半透明)
现有实现的着色器逻辑在边缘过渡、光晕强度控制上与目标效果存在差异,同时缺少渲染层级优化,容易出现透明物体遮挡问题,以下是优化后的完整实现:
1. 调整着色器逻辑(贴合锐利边缘光晕)
替换原片段着色器,优化边缘检测与光晕计算逻辑,确保光晕仅在视角垂直的边缘区域显示,同时精准控制主体玻璃的透明度:
const vertexShader = ` varying vec3 vNormal; varying vec3 vWorldPosition; void main() { vNormal = normalize(normalMatrix * normal); vWorldPosition = (modelMatrix * vec4(position, 1.0)).xyz; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } `; const fragmentShader = ` varying vec3 vNormal; varying vec3 vWorldPosition; uniform vec3 uColor; uniform float uOpacity; uniform vec3 uCameraPosition; uniform float uGlowIntensity; uniform float uEdgeThreshold; void main() { vec3 viewDir = normalize(uCameraPosition - vWorldPosition); // 计算法线与视角方向的夹角,取绝对值适配双面渲染 float facing = abs(dot(vNormal, viewDir)); // 边缘检测:仅在视角垂直的窄区域触发光晕 float edgeFactor = smoothstep(uEdgeThreshold, uEdgeThreshold + 0.05, 1.0 - facing); // 主体玻璃透明度:正面半透明,边缘随光晕增强透明度 float alpha = uOpacity * (0.2 + (1.0 - edgeFactor) * 0.3); // 光晕叠加:边缘区域用加法混合白色发光 vec3 glow = vec3(1.0) * edgeFactor * uGlowIntensity; vec3 finalColor = uColor + glow; gl_FragColor = vec4(finalColor, alpha); } `;
2. 优化材质配置与渲染逻辑
修改SceneContent组件,调整材质渲染参数解决透明物体层级问题,同时优化相机位置更新逻辑:
import React, { useEffect, useRef } from "react"; import { useGLTF, OrbitControls } from "@react-three/drei"; import { useFrame, useThree } from "@react-three/fiber"; import * as THREE from "three"; const SceneContent = ({ modelPath }) => { const { scene } = useGLTF(modelPath); const { camera } = useThree(); const glassMaterialRef = useRef(null); const shaderValues = { baseColor: "#aee0ff", // 淡蓝色玻璃基底色 opacity: 0.8, // 基础透明度 glowIntensity: 2.0, // 光晕强度 edgeThreshold: 0.1, // 边缘检测阈值,值越小边缘越窄 }; useEffect(() => { const glassMaterial = new THREE.ShaderMaterial({ vertexShader, fragmentShader, uniforms: { uColor: { value: new THREE.Color(shaderValues.baseColor) }, uOpacity: { value: shaderValues.opacity }, uGlowIntensity: { value: shaderValues.glowIntensity }, uEdgeThreshold: { value: shaderValues.edgeThreshold }, uCameraPosition: { value: camera.position }, }, transparent: true, side: THREE.DoubleSide, depthWrite: false, // 关闭深度写入,避免透明物体遮挡 depthTest: true, blending: THREE.AdditiveBlending, // 加法混合增强光晕发光效果 }); glassMaterialRef.current = glassMaterial; scene.traverse((child) => { if (child.isMesh) { child.material = glassMaterial; child.renderOrder = 1; // 设置渲染顺序,确保玻璃在不透明物体之后渲染 } }); return () => { glassMaterial.dispose(); }; }, [scene, camera.position, shaderValues]); // 直接更新材质uniforms,避免遍历场景的性能开销 useFrame(() => { if (glassMaterialRef.current) { glassMaterialRef.current.uniforms.uCameraPosition.value.copy(camera.position); } }); return ( <> <primitive object={scene} /> <OrbitControls target={[0, 0, 0]} enableDamping /> </> ); };
3. 调整场景灯光与相机
优化ModelViewer的灯光配置,增强玻璃的反光与光晕表现:
const ModelViewer = ({ modelPath }) => { return ( <Canvas camera={{ position: [0, 350, 800], fov: 45 }} style={{ background: "#1a1a1a", height: "750px" }} > <ambientLight intensity={0.4} /> <directionalLight position={[500, 500, 500]} intensity={1.2} /> <directionalLight position={[-500, -300, -500]} intensity={0.8} /> <pointLight position={[0, 200, 400]} intensity={0.6} color="#ffffff" /> <SceneContent modelPath={modelPath} /> </Canvas> ); }; export default ModelViewer;
效果说明
- 边缘光晕随相机视角动态变化:模型旋转时,视角垂直的边缘会自动触发白色发光效果
- 主体玻璃保持半透明淡蓝色调,与目标截图视觉匹配
- 关闭深度写入与设置渲染顺序解决了透明物体的层级遮挡问题
内容的提问来源于stack exchange,提问作者Masum Billah
相关产品推荐
相关产品推荐

