@@ -77,6 +77,48 @@ type ZoomCameraSource = 'button' | 'gesture'
|
|||||||
|
|
||||||
const CANVAS_FONT_FAMILY = '"鸿蒙黑体", "HarmonyOS Sans SC", "HarmonyOS Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif'
|
const CANVAS_FONT_FAMILY = '"鸿蒙黑体", "HarmonyOS Sans SC", "HarmonyOS Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif'
|
||||||
const FLOOR_CAMERA_DISTANCE_FACTOR = 0.78
|
const FLOOR_CAMERA_DISTANCE_FACTOR = 0.78
|
||||||
|
const SGS_VISUAL_RENDER_CONFIG = {
|
||||||
|
sceneBackground: 0xeceff1,
|
||||||
|
camera: {
|
||||||
|
fov: 42,
|
||||||
|
near: 0.1,
|
||||||
|
far: 20000,
|
||||||
|
initialPosition: new THREE.Vector3(20, 20, 20)
|
||||||
|
},
|
||||||
|
renderer: {
|
||||||
|
dprCap: 1.5,
|
||||||
|
powerPreference: 'high-performance' as WebGLPowerPreference,
|
||||||
|
antialias: true,
|
||||||
|
alpha: false,
|
||||||
|
toneMapping: THREE.NoToneMapping,
|
||||||
|
toneMappingExposure: 1.0,
|
||||||
|
shadows: false
|
||||||
|
},
|
||||||
|
lights: {
|
||||||
|
hemisphere: {
|
||||||
|
skyColor: 0xffffff,
|
||||||
|
groundColor: 0xb0bec5,
|
||||||
|
intensity: 1.7
|
||||||
|
},
|
||||||
|
key: {
|
||||||
|
color: 0xffffff,
|
||||||
|
intensity: 2.2,
|
||||||
|
position: new THREE.Vector3(80, 120, 80)
|
||||||
|
},
|
||||||
|
fill: {
|
||||||
|
color: 0xffffff,
|
||||||
|
intensity: 0.55,
|
||||||
|
position: new THREE.Vector3(-60, 70, -50)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
controls: {
|
||||||
|
minDistance: 10,
|
||||||
|
maxDistance: 600,
|
||||||
|
minPolarAngle: 0,
|
||||||
|
maxPolarAngle: Math.PI / 2 - 0.05,
|
||||||
|
dampingFactor: 0.06
|
||||||
|
}
|
||||||
|
} as const
|
||||||
|
|
||||||
interface CameraFitOptions {
|
interface CameraFitOptions {
|
||||||
distanceFactor?: number
|
distanceFactor?: number
|
||||||
@@ -1420,33 +1462,39 @@ const initThree = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
scene = new THREE.Scene()
|
scene = new THREE.Scene()
|
||||||
scene.background = new THREE.Color(0xf7f8f4)
|
scene.background = new THREE.Color(SGS_VISUAL_RENDER_CONFIG.sceneBackground)
|
||||||
|
|
||||||
const width = Math.max(1, container.clientWidth)
|
const width = Math.max(1, container.clientWidth)
|
||||||
const height = Math.max(1, container.clientHeight)
|
const height = Math.max(1, container.clientHeight)
|
||||||
camera = new THREE.PerspectiveCamera(42, width / height, 0.1, 10000)
|
camera = new THREE.PerspectiveCamera(
|
||||||
camera.position.set(80, 80, 120)
|
SGS_VISUAL_RENDER_CONFIG.camera.fov,
|
||||||
|
width / height,
|
||||||
|
SGS_VISUAL_RENDER_CONFIG.camera.near,
|
||||||
|
SGS_VISUAL_RENDER_CONFIG.camera.far
|
||||||
|
)
|
||||||
|
camera.position.copy(SGS_VISUAL_RENDER_CONFIG.camera.initialPosition)
|
||||||
|
|
||||||
renderer = new THREE.WebGLRenderer({
|
renderer = new THREE.WebGLRenderer({
|
||||||
antialias: true,
|
antialias: SGS_VISUAL_RENDER_CONFIG.renderer.antialias,
|
||||||
alpha: true,
|
alpha: SGS_VISUAL_RENDER_CONFIG.renderer.alpha,
|
||||||
powerPreference: 'high-performance'
|
powerPreference: SGS_VISUAL_RENDER_CONFIG.renderer.powerPreference
|
||||||
})
|
})
|
||||||
renderer.setSize(width, height)
|
renderer.setSize(width, height)
|
||||||
renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2))
|
renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, SGS_VISUAL_RENDER_CONFIG.renderer.dprCap))
|
||||||
renderer.outputColorSpace = THREE.SRGBColorSpace
|
renderer.outputColorSpace = THREE.SRGBColorSpace
|
||||||
renderer.toneMapping = THREE.NeutralToneMapping
|
renderer.toneMapping = SGS_VISUAL_RENDER_CONFIG.renderer.toneMapping
|
||||||
renderer.toneMappingExposure = 1.0
|
renderer.toneMappingExposure = SGS_VISUAL_RENDER_CONFIG.renderer.toneMappingExposure
|
||||||
|
renderer.shadowMap.enabled = SGS_VISUAL_RENDER_CONFIG.renderer.shadows
|
||||||
container.appendChild(renderer.domElement)
|
container.appendChild(renderer.domElement)
|
||||||
|
|
||||||
controls = new OrbitControls(camera, renderer.domElement)
|
controls = new OrbitControls(camera, renderer.domElement)
|
||||||
controls.enableDamping = true
|
controls.enableDamping = true
|
||||||
controls.dampingFactor = 0.08
|
controls.dampingFactor = SGS_VISUAL_RENDER_CONFIG.controls.dampingFactor
|
||||||
controls.enableZoom = true
|
controls.enableZoom = true
|
||||||
controls.minDistance = 6
|
controls.minDistance = SGS_VISUAL_RENDER_CONFIG.controls.minDistance
|
||||||
controls.maxDistance = 1200
|
controls.maxDistance = SGS_VISUAL_RENDER_CONFIG.controls.maxDistance
|
||||||
controls.minPolarAngle = 0.08
|
controls.minPolarAngle = SGS_VISUAL_RENDER_CONFIG.controls.minPolarAngle
|
||||||
controls.maxPolarAngle = Math.PI * 0.48
|
controls.maxPolarAngle = SGS_VISUAL_RENDER_CONFIG.controls.maxPolarAngle
|
||||||
syncControlInteractionOptions()
|
syncControlInteractionOptions()
|
||||||
|
|
||||||
loader = new GLTFLoader()
|
loader = new GLTFLoader()
|
||||||
@@ -1461,15 +1509,25 @@ const initThree = async () => {
|
|||||||
routeGroup.name = 'GuideModelRoute'
|
routeGroup.name = 'GuideModelRoute'
|
||||||
scene.add(routeGroup)
|
scene.add(routeGroup)
|
||||||
|
|
||||||
const ambientLight = new THREE.AmbientLight(0xffffff, 0.9)
|
const hemisphereLight = new THREE.HemisphereLight(
|
||||||
scene.add(ambientLight)
|
SGS_VISUAL_RENDER_CONFIG.lights.hemisphere.skyColor,
|
||||||
|
SGS_VISUAL_RENDER_CONFIG.lights.hemisphere.groundColor,
|
||||||
|
SGS_VISUAL_RENDER_CONFIG.lights.hemisphere.intensity
|
||||||
|
)
|
||||||
|
scene.add(hemisphereLight)
|
||||||
|
|
||||||
const keyLight = new THREE.DirectionalLight(0xffffff, 2.0)
|
const keyLight = new THREE.DirectionalLight(
|
||||||
keyLight.position.set(80, 120, 60)
|
SGS_VISUAL_RENDER_CONFIG.lights.key.color,
|
||||||
|
SGS_VISUAL_RENDER_CONFIG.lights.key.intensity
|
||||||
|
)
|
||||||
|
keyLight.position.copy(SGS_VISUAL_RENDER_CONFIG.lights.key.position)
|
||||||
scene.add(keyLight)
|
scene.add(keyLight)
|
||||||
|
|
||||||
const fillLight = new THREE.DirectionalLight(0xdfeaff, 0.45)
|
const fillLight = new THREE.DirectionalLight(
|
||||||
fillLight.position.set(-80, 60, -80)
|
SGS_VISUAL_RENDER_CONFIG.lights.fill.color,
|
||||||
|
SGS_VISUAL_RENDER_CONFIG.lights.fill.intensity
|
||||||
|
)
|
||||||
|
fillLight.position.copy(SGS_VISUAL_RENDER_CONFIG.lights.fill.position)
|
||||||
scene.add(fillLight)
|
scene.add(fillLight)
|
||||||
|
|
||||||
resizeObserver = new ResizeObserver(handleResize)
|
resizeObserver = new ResizeObserver(handleResize)
|
||||||
@@ -2881,8 +2939,8 @@ const setCameraView = (
|
|||||||
const targetCenter = center.clone().add(targetOffset)
|
const targetCenter = center.clone().add(targetOffset)
|
||||||
const distance = Math.abs(maxDim / Math.sin(fov / 2)) * distanceFactor
|
const distance = Math.abs(maxDim / Math.sin(fov / 2)) * distanceFactor
|
||||||
|
|
||||||
camera.near = Math.max(0.1, maxDim / 1000)
|
camera.near = Math.max(SGS_VISUAL_RENDER_CONFIG.camera.near, maxDim / 1000)
|
||||||
camera.far = Math.max(10000, maxDim * 20)
|
camera.far = Math.max(SGS_VISUAL_RENDER_CONFIG.camera.far, maxDim * 20)
|
||||||
camera.up.copy(options.up || new THREE.Vector3(0, 1, 0))
|
camera.up.copy(options.up || new THREE.Vector3(0, 1, 0))
|
||||||
camera.updateProjectionMatrix()
|
camera.updateProjectionMatrix()
|
||||||
|
|
||||||
@@ -2901,8 +2959,8 @@ const setCameraView = (
|
|||||||
nextPosition.add(screenOffset)
|
nextPosition.add(screenOffset)
|
||||||
nextTarget.add(screenOffset)
|
nextTarget.add(screenOffset)
|
||||||
}
|
}
|
||||||
controls.minDistance = Math.max(2, maxDim * 0.08)
|
controls.minDistance = Math.max(SGS_VISUAL_RENDER_CONFIG.controls.minDistance, maxDim * 0.08)
|
||||||
controls.maxDistance = Math.max(20, maxDim * 8)
|
controls.maxDistance = Math.max(SGS_VISUAL_RENDER_CONFIG.controls.maxDistance, maxDim * 8)
|
||||||
moveCameraTo(nextPosition, nextTarget)
|
moveCameraTo(nextPosition, nextTarget)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2954,7 +3012,7 @@ const zoomCamera = (direction: 'in' | 'out', options: { source?: ZoomCameraSourc
|
|||||||
const currentDistance = offset.length()
|
const currentDistance = offset.length()
|
||||||
const zoomFactor = direction === 'in' ? 0.72 : 1.28
|
const zoomFactor = direction === 'in' ? 0.72 : 1.28
|
||||||
const minDistance = controls.minDistance || 2
|
const minDistance = controls.minDistance || 2
|
||||||
const maxDistance = controls.maxDistance || 1200
|
const maxDistance = controls.maxDistance || SGS_VISUAL_RENDER_CONFIG.controls.maxDistance
|
||||||
const nextDistance = Math.min(maxDistance, Math.max(minDistance, currentDistance * zoomFactor))
|
const nextDistance = Math.min(maxDistance, Math.max(minDistance, currentDistance * zoomFactor))
|
||||||
|
|
||||||
if (!Number.isFinite(nextDistance) || nextDistance <= 0) return
|
if (!Number.isFinite(nextDistance) || nextDistance <= 0) return
|
||||||
@@ -4198,12 +4256,12 @@ const focusCameraOnPoi = (poi: RenderPoi) => {
|
|||||||
const direction = new THREE.Vector3(0.72, 0.58, 1).normalize()
|
const direction = new THREE.Vector3(0.72, 0.58, 1).normalize()
|
||||||
const nextPosition = target.clone().add(direction.multiplyScalar(distance))
|
const nextPosition = target.clone().add(direction.multiplyScalar(distance))
|
||||||
|
|
||||||
camera.near = Math.max(0.1, maxDim / 1200)
|
camera.near = Math.max(SGS_VISUAL_RENDER_CONFIG.camera.near, maxDim / 1200)
|
||||||
camera.far = Math.max(10000, maxDim * 20)
|
camera.far = Math.max(SGS_VISUAL_RENDER_CONFIG.camera.far, maxDim * 20)
|
||||||
camera.updateProjectionMatrix()
|
camera.updateProjectionMatrix()
|
||||||
|
|
||||||
controls.minDistance = Math.max(2, maxDim * 0.025)
|
controls.minDistance = Math.max(SGS_VISUAL_RENDER_CONFIG.controls.minDistance, maxDim * 0.025)
|
||||||
controls.maxDistance = Math.max(20, maxDim * 6)
|
controls.maxDistance = Math.max(SGS_VISUAL_RENDER_CONFIG.controls.maxDistance, maxDim * 6)
|
||||||
moveCameraTo(nextPosition, target)
|
moveCameraTo(nextPosition, target)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user