Three.js自定义FlattenedSphere仍致模型悬浮,求解决方案
解决Three.js(React Three Fiber)中模型悬浮与环境球地面贴合问题
核心问题排查
你的代码存在几个关键问题导致模型悬浮效果未解决:
- 未在场景中渲染模型组件(
Model/Model2),无法观察贴合效果 - 自定义
FlattenedSphere的位置未对齐到地面基准平面 - 使用
meshBasicMaterial不支持光照和阴影交互,地面视觉上不真实 - 接触阴影(
ContactShadows)位置未匹配地面高度
分步解决方案
1. 修正场景组件,添加模型并对齐地面
在场景中加入模型组件,同时调整模型位置使其底部贴合地面:
import { Environment, OrbitControls, useGLTF, ContactShadows, useTexture, } from '@react-three/drei'; import { Perf } from 'r3f-perf'; import { useEffect } from 'react'; import React, { useRef } from 'react'; import { useControls } from 'leva'; import * as THREE from 'three'; import { Model } from './Car'; import { Model2 } from './StockCar'; export const Scene = () => { const { camera } = useThree(); useEffect(() => { camera.position.set(-18, 6, 6); camera.lookAt(0, 0, -10); }, [camera]); const { envMapIntensity, envMapHeight, envMapRadius, envMapScale } = useControls('environment map', { envMapIntensity: { value: 7, min: 0, max: 12 }, envMapHeight: { value: 9, min: 0, max: 100 }, envMapRadius: { value: 28, min: 0, max: 1000 }, envMapScale: { value: 140, min: 0, max: 1000 }, }); const { z, y, x } = useControls('car poss', { z: { value: 0, min: -10, max: 10 }, y: { value: 0, min: -10, max: 10 }, x: { value: 0, min: -10, max: 10 }, }); const envTexture = useTexture('/32.jpg'); const groundHeight = 10; // 统一地面高度变量 if (envTexture) { envTexture.mapping = THREE.EquirectangularReflectionMapping; } return ( <> <Perf position="top-left" /> <directionalLight castShadow position={[0, 8, 0]} shadow-mapSize={[1024, 1024]} intensity={1.2} > <orthographicCamera attach="shadow-camera" args={[-10, 10, 10, -10]} /> </directionalLight> <FlattenedSphere height={groundHeight} envTexture={envTexture} /> {/* 添加模型并调整y轴位置,使其贴合地面 */} <Model position={[x, y + groundHeight, z]} castShadow receiveShadow /> <OrbitControls enableDamping dampingFactor={0.05} rotateSpeed={0.6} target={[0, groundHeight, 0]} // 控制器目标对齐地面高度 minDistance={1} maxDistance={3000} maxPolarAngle={Math.PI} global={false} cursor={false} snap={false} /> {/* 调整接触阴影位置到地面 */} <ContactShadows position={[0, groundHeight - 0.1, 0]} opacity={0.6} scale={16} blur={1} far={10} /> <axesHelper args={[5]} /> </> ); };
2. 优化FlattenedSphere组件
调整位置并替换为支持光照的材质,开启阴影接收:
const FlattenedSphere = ({ height, envTexture }) => { const sphereRef = useRef(null); useEffect(() => { if (sphereRef.current) { sphereRef.current.geometry = getSize(height); } }, [height]); return ( <mesh ref={sphereRef} scale={[1, 1, 1]} position={[0, height, 0]} // 将球面底部对齐到y=0基准面 receiveShadow > {/* 使用MeshStandardMaterial实现真实光照和环境映射 */} <meshStandardMaterial map={envTexture} side={THREE.BackSide} envMap={envTexture} envMapIntensity={0.5} roughness={0.3} metalness={0.1} /> </mesh> ); };
3. 优化地面几何计算
确保地面顶点完全压平,避免视觉缝隙:
const getSize = (height) => { const geoSphere = new THREE.SphereGeometry(50, 32, 32); const pos = geoSphere.getAttribute('position'); const tmp = new THREE.Vector3(); for (let i = 0; i < pos.count; ++i) { tmp.fromBufferAttribute(pos, i); if (tmp.y < 0) { const y1 = (-height * 3) / 2; let f = tmp.y < y1 ? -height / tmp.y : 1 - (tmp.y * tmp.y) / (3 * y1 * y1); tmp.multiplyScalar(f); // 强制底部顶点贴合目标高度,消除缝隙 if (tmp.y < -height + 0.1) tmp.y = -height; tmp.toArray(pos.array, 3 * i); } } pos.needsUpdate = true; geoSphere.computeVertexNormals(); // 重新计算法向量,保证光照正确 return geoSphere; };
关键说明
- 模型对齐逻辑:
FlattenedSphere通过position.y=height将底部对齐到y=0平面,因此模型需要将y轴位置加上groundHeight,确保底部贴合地面。若模型自身原点不在底部,需额外调整偏移量。 - 材质选择:
meshBasicMaterial不响应光照,改用meshStandardMaterial可实现环境球与模型的真实光影交互。 - 阴影配置:给模型开启
castShadow和receiveShadow,方向光开启castShadow,环境球开启receiveShadow,保证阴影正确显示。
内容的提问来源于stack exchange,提问作者JGarnie
相关产品推荐
相关产品推荐

