Three.js Object 3D

강정우·2025년 1월 3일
0

three.js

목록 보기
5/24
post-thumbnail

Object 3D

Object 3D 는 가장 기본적인 class 이다. 그리고 많은 객체들이 이 Object 3D 를 상속받아서 만들어진다.
이 3D 객체는 3차원 공간에서 객체를 조작하기위한 기본적인 메서드들과 속성값들을 제공한다.
가장 대표적인 예로 Meshes, Lights, Cameras, Object3D 그룹이 있다.

우선 Object 3D 의 가장 대표적인 몇가지 속성에 대해 알아보자.
그러기 위해 기본적인 코드를 작성하자.

import './style.css'
import * as THREE from 'three'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import Stats from 'three/addons/libs/stats.module.js'
import { GUI } from 'dat.gui'

const scene = new THREE.Scene()
scene.add(new THREE.AxesHelper(5))

const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
camera.position.set(1, 2, 3)

const renderer = new THREE.WebGLRenderer({antialias: true})
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.domElement)

window.addEventListener('resize', () => {
    camera.aspect = window.innerWidth / window.innerHeight
    camera.updateProjectionMatrix()
    renderer.setSize(window.innerWidth, window.innerHeight)
})

new OrbitControls(camera, renderer.domElement)

const geometry = new THREE.BoxGeometry()
const material = new THREE.MeshNormalMaterial({ wireframe: true })

const cube = new THREE.Mesh(geometry, material)
scene.add(cube)

const stats = new Stats()
document.body.appendChild(stats.dom)

const gui = new GUI()

const cubeFolder = gui.addFolder('Cube')
cubeFolder.add(cube, 'visible')
cubeFolder.open()

function animate() {
    requestAnimationFrame(animate)

    renderer.render(scene, camera)

    stats.update()
}

animate()

AxesHelper

우선 아래 속성들을 알아보기 전에 AxesHelper 를 보자.

위 사진과 같이 x,y,z 축에 선을 그어 객체가 정위치에 위치할 수 있도록 도와주는 선이다.

const scene = new THREE.Scene()
scene.add(new THREE.AxesHelper(5))

Object3D properties

.rotation

const rotationFolder = cubeFolder.addFolder('Rotation')
rotationFolder.add(cube.rotation, 'x', 0, Math.PI * 2)
rotationFolder.add(cube.rotation, 'y', 0, Math.PI * 2)
rotationFolder.add(cube.rotation, 'z', 0, Math.PI * 2)
rotationFolder.open()

.position

const positionFolder = cubeFolder.addFolder('Position')
positionFolder.add(cube.position, 'x', -5, 5)
positionFolder.add(cube.position, 'y', -5, 5)
positionFolder.add(cube.position, 'z', -5, 5)
positionFolder.open()

.scale

const scaleFolder = cubeFolder.addFolder('Scale')
scaleFolder.add(cube.scale, 'x', -5, 5)
scaleFolder.add(cube.scale, 'y', -5, 5)
scaleFolder.add(cube.scale, 'z', -5, 5)
scaleFolder.open()

.visibillity

cube.visible = false

// truthy, falsy
cubeFolder.add(cube, 'visible')

Object3D Hierarchy

Object3D 는 모든 것의 부모 class 라고 하였다. 그리고 Scene 역시 Object3D의 자식이다. 그리고 이 scene 에 .add() 를 통하여 어떤 객체를 추가하면 이는 Scene 의 자식이 되며 'resize' 이벤트등을 통하여 Scene 의 크기나 위치가 변경되면 Scene 의 모든 자식들에게 영향을 미친다.

즉, 이 말은 Object3D를 다른 Object3D를 상속받은 객체의 자식으로 만들 수도 있다는 뜻이다.
이는 기존 오브젝트에 새 오브젝트를 계속 추가하여 Object3D 계층 구조를 만들 수 있다는 것이다.

const object1 = new THREE.Mesh(new THREE.SphereGeometry(), new THREE.MeshPhongMaterial({ color: 0xff0000 }))
object1.position.set(4, 0, 0)
scene.add(object1)
object1.add(new THREE.AxesHelper(5))

const object2 = new THREE.Mesh(new THREE.SphereGeometry(), new THREE.MeshPhongMaterial({ color: 0x00ff00 }))
object2.position.set(4, 0, 0)
object1.add(object2)
object2.add(new THREE.AxesHelper(5))

const object3 = new THREE.Mesh(new THREE.SphereGeometry(), new THREE.MeshPhongMaterial({ color: 0x0000ff }))
object3.position.set(4, 0, 0)
object2.add(object3)
object3.add(new THREE.AxesHelper(5))

위 코드와 사진을 보면 Scene <- 빨간공 <- 초록공 <- 파란공 이렇게 붙어있는 것을 볼 수 있다.
그리고 코드의 position 을 보면 모두 4,0,0 을 주었고 사진의 왼쪽 빨간색 박스를 보면 local position 은 4,0,0 이지만 world position 은 각각의 Object3D 의 부모의 영향을 받아 4씩 더해지고 있다.

get world position

그리고 Object3D 의 world 속성에 관하여 데이터를 얻고 싶다면 .getWorldPosition(), .getWorldDirection(), .getWorldQuaternion(), .getWorldScale() 를 사용하면 된다.

const objectsWorldPosition = new THREE.Vector3()
object.getWorldPosition(objectsWorldPosition)
console.log(objectsWorldPosition)

const objectsWorldDirection = new THREE.Vector3()
object.getWorldDirection(objectsWorldDirection)
console.log(objectsWorldDirection)

const objectsWorldQuaternion = new THREE.Quaternion()
object.getWorldQuaternion(objectsWorldQuaternion)
console.log(objectsWorldQuaternion)

const objectsWorldScale = new THREE.Vector3()
object.getWorldScale(objectsWorldScale)
console.log(objectsWorldScale)

참고로 Quaternion 은 rotation 보다 더 세부적인 값을 설정할 수 있다. 이는 추후 다루도록 하겠다.

import './style.css'
import * as THREE from 'three'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import Stats from 'three/addons/libs/stats.module.js'
import { GUI } from 'dat.gui'

const scene = new THREE.Scene()
scene.add(new THREE.AxesHelper(5))

const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
camera.position.set(4, 4, 4)

const renderer = new THREE.WebGLRenderer()
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.domElement)

const controls = new OrbitControls(camera, renderer.domElement)
controls.target.set(8, 0, 0)
controls.update()

const light = new THREE.PointLight(0xffffff, 400)
light.position.set(10, 10, 10)
scene.add(light)

const object1 = new THREE.Mesh(new THREE.SphereGeometry(), new THREE.MeshPhongMaterial({ color: 0xff0000 }))
object1.position.set(4, 0, 0)
scene.add(object1)
object1.add(new THREE.AxesHelper(5))

const object2 = new THREE.Mesh(new THREE.SphereGeometry(), new THREE.MeshPhongMaterial({ color: 0x00ff00 }))
object2.position.set(4, 0, 0)
object1.add(object2)
object2.add(new THREE.AxesHelper(5))

const object3 = new THREE.Mesh(new THREE.SphereGeometry(), new THREE.MeshPhongMaterial({ color: 0x0000ff }))
object3.position.set(4, 0, 0)
object2.add(object3)
object3.add(new THREE.AxesHelper(5))

window.addEventListener('resize', () => {
    camera.aspect = window.innerWidth / window.innerHeight
    camera.updateProjectionMatrix()
    renderer.setSize(window.innerWidth, window.innerHeight)
})

const gui = new GUI()
const object1Folder = gui.addFolder('Object1 (Red Ball)')
object1Folder.add(object1.position, 'x', 0, 10, 0.01).name('X Position')
object1Folder.add(object1.rotation, 'x', 0, Math.PI * 2, 0.01).name('X Rotation')
object1Folder.add(object1.scale, 'x', 0, 2, 0.01).name('X Scale')
object1Folder.open()
const object2Folder = gui.addFolder('Object2 (Green Ball)')
object2Folder.add(object2.position, 'x', 0, 10, 0.01).name('X Position')
object2Folder.add(object2.rotation, 'x', 0, Math.PI * 2, 0.01).name('X Rotation')
object2Folder.add(object2.scale, 'x', 0, 2, 0.01).name('X Scale')
object2Folder.open()
const object3Folder = gui.addFolder('Object3 (Blue Ball)')
object3Folder.add(object3.position, 'x', 0, 10, 0.01).name('X Position')
object3Folder.add(object3.rotation, 'x', 0, Math.PI * 2, 0.01).name('X Rotation')
object3Folder.add(object3.scale, 'x', 0, 2, 0.01).name('X Scale')
object3Folder.open()

const stats = new Stats()
document.body.appendChild(stats.dom)

const debug = document.getElementById('debug') as HTMLDivElement

function animate() {
    requestAnimationFrame(animate)

    renderer.render(scene, camera)

    const object1WorldPosition = new THREE.Vector3()
    object1.getWorldPosition(object1WorldPosition)
    const object2WorldPosition = new THREE.Vector3()
    object2.getWorldPosition(object2WorldPosition)
    const object3WorldPosition = new THREE.Vector3()
    object3.getWorldPosition(object3WorldPosition)

    debug.innerText =
        'Red\n' +
        'Local Pos X : ' +
        object1.position.x.toFixed(2) +
        '\n' +
        'World Pos X : ' +
        object1WorldPosition.x.toFixed(2) +
        '\n' +
        '\nGreen\n' +
        'Local Pos X : ' +
        object2.position.x.toFixed(2) +
        '\n' +
        'World Pos X : ' +
        object2WorldPosition.x.toFixed(2) +
        '\n' +
        '\nBlue\n' +
        'Local Pos X : ' +
        object3.position.x.toFixed(2) +
        '\n' +
        'World Pos X : ' +
        object3WorldPosition.x.toFixed(2) +
        '\n'

    stats.update()
}

animate()
profile
智(지)! 德(덕)! 體(체)!

0개의 댓글