Refactor Scene component to improve camera positioning and rendering. Adjust default near clipping plane value, update floor position, and streamline camera view creation with extrinsic matrix handling.

This commit is contained in:
2025-03-24 16:36:02 +08:00
parent 76a530b293
commit c67e1ca728

View File

@ -12,7 +12,7 @@ const THREE_ADDONS = {
} as const } as const
const DEFAULT_NEAR = 0.1 const DEFAULT_NEAR = 0.05
const DEFAULT_FAR = 1 const DEFAULT_FAR = 1
const CAMERA_EXTRINSIC_MATRIX_MAP: Record<string, number[]> = { const CAMERA_EXTRINSIC_MATRIX_MAP: Record<string, number[]> = {
"AE_01": [ "AE_01": [
@ -70,7 +70,7 @@ const intrinsicToFov = (intrinsic: number[], image_size: { width: number, height
const Scene = () => { const Scene = () => {
function Floor() { function Floor() {
return ( return (
<mesh rotation-x={-Math.PI / 2} position-y={-0.05} receiveShadow> <mesh rotation-x={-Math.PI / 2} position-y={-0.5} receiveShadow>
<planeGeometry args={[15, 15]} /> <planeGeometry args={[15, 15]} />
<meshStandardMaterial color="#ccc" /> <meshStandardMaterial color="#ccc" />
</mesh> </mesh>
@ -92,11 +92,17 @@ const Scene = () => {
// https://www.ilyameerovich.com/simple-3d-text-meshes-in-three-js/ // https://www.ilyameerovich.com/simple-3d-text-meshes-in-three-js/
const CameraViewFromExtrinsic = ({ extrinsic, name, near, far, fov, textSize, aspect }: CameraViewFromExtrinsicProps) => { const CameraViewFromExtrinsic = ({ extrinsic, name, near, far, fov, textSize, aspect }: CameraViewFromExtrinsicProps) => {
console.assert(extrinsic.length === 16, "extrinsic must be a 4x4 matrix")
const font = new FontLoader().parse(HelvetikerRegular) const font = new FontLoader().parse(HelvetikerRegular)
const camera = new THREE.PerspectiveCamera(fov ?? 60, aspect ?? 4 / 3, near ?? DEFAULT_NEAR, far ?? DEFAULT_FAR) const camera = new THREE.PerspectiveCamera(fov ?? 60, aspect ?? 4 / 3, near ?? DEFAULT_NEAR, far ?? DEFAULT_FAR)
// the camera by default is looking up to the y-axis
// we want it to look at the origin
camera.rotation.x = -Math.PI / 2
const helper = <cameraHelper args={[camera]} /> const helper = <cameraHelper args={[camera]} />
const Rt = new THREE.Matrix4()
// @ts-expect-error 16 elements // @ts-expect-error 16 elements
camera.applyMatrix4(new THREE.Matrix4(...extrinsic)) Rt.set(...extrinsic)
camera.applyMatrix4(Rt)
const textRef = useRef<THREE.Mesh>(null) const textRef = useRef<THREE.Mesh>(null)
const { camera: viewCamera } = useThree() const { camera: viewCamera } = useThree()
@ -110,11 +116,8 @@ const Scene = () => {
let text: JSX.Element | null = null let text: JSX.Element | null = null
if (name) { if (name) {
const geo = new THREE_ADDONS.TextGeometry(name ?? "", { font, size: textSize ?? 0.1, depth: 0.001 }) const geo = new THREE_ADDONS.TextGeometry(name ?? "", { font, size: textSize ?? 0.1, depth: 0.001 })
const matrix = new THREE.Matrix4()
// @ts-expect-error 16 elements
matrix.set(...extrinsic)
const position = new THREE.Vector3() const position = new THREE.Vector3()
position.setFromMatrixPosition(matrix) position.setFromMatrixPosition(Rt)
text = ( text = (
<mesh ref={textRef} position={position}> <mesh ref={textRef} position={position}>
@ -132,24 +135,30 @@ const Scene = () => {
) )
} }
const scene = (<group>
{/* <OrbitControls /> */}
<ambientLight intensity={0.05} />
<directionalLight castShadow position={[3.3, 6, 4.4]} intensity={5} />
<Floor />
{Object.entries(CAMERA_EXTRINSIC_MATRIX_MAP).map(([key, value]) => {
const intrinsic = CAMERA_INTRINSIC_MATRIX_MAP[key]
const { fov_x, fov_y } = intrinsicToFov(intrinsic, { width: IMAGE_WIDTH, height: IMAGE_HEIGHT })
// make the far reverse proportional to the fov
const far = (1 / fov_x) * 20
return <CameraViewFromExtrinsic key={key} name={`${key}(${fov_x.toFixed(1)})`} extrinsic={value} fov={fov_x} aspect={IMAGE_WIDTH / IMAGE_HEIGHT} far={far} />
})}
<Axes />
</group>)
return ( return (
// Note that we don't need to import anything, All three.js objects will be treated // Note that we don't need to import anything, All three.js objects will be treated
// as native JSX elements, just like you can just write <div /> or <span /> in // as native JSX elements, just like you can just write <div /> or <span /> in
// regular ReactDOM. The general rule is that Fiber components are available under // regular ReactDOM. The general rule is that Fiber components are available under
// the camel-case version of their name in three.js. // the camel-case version of their name in three.js.
<> <>
<CameraControls /> <CameraControls />
{/* <OrbitControls /> */}
<ambientLight intensity={0.05} />
<directionalLight castShadow position={[3.3, 6, 4.4]} intensity={5} />
<Floor />
{Object.entries(CAMERA_EXTRINSIC_MATRIX_MAP).map(([key, value]) => {
const intrinsic = CAMERA_INTRINSIC_MATRIX_MAP[key]
const fov = intrinsicToFov(intrinsic, { width: IMAGE_WIDTH, height: IMAGE_HEIGHT })
return <CameraViewFromExtrinsic key={key} name={key} extrinsic={value} fov={fov.fov_x} aspect={IMAGE_WIDTH / IMAGE_HEIGHT} />
})}
<Axes />
<Stats /> <Stats />
{scene}
</> </>
) )
} }