본문 바로가기

3D Project/3D 인터랙티브 웹

[3D 인터랙티브 웹] 3D Scene - 1(클릭 인터랙션) 렌더링

기본 Three.js 세팅 

import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";

const canvas = document.createElement("canvas");
document.body.appendChild(canvas);

const renderer =  new THREE.WebGLRenderer({canvas:canvas, antialias:true});
renderer.setSize(window.innerWidth, window.innerHeight);

//THREE.PerspectiveCamera의 생성자는 두개의 인자를 받음
//-> 1. 시야각(FOV) 2. 카메라의 비율(Aspect Ratio) -> 일반적으로 창의 크기에 따라 동적으로 조절됨
const camera = new THREE.PerspectiveCamera(35, window.innerWidth/window.innerHeight);
const scene = new THREE.Scene();
scene.background = new THREE.Color("hsl(255, 50%, 40%)")

const loader = new GLTFLoader();
loader.load("/gltf/scene/scene-1.glb", (gltf) => {
    scene.add(gltf.scene);

    render();
})

//화면 렌더링 -> 매 프레임마다 화면을 갱신하고 업데이트
function render () {
    requestAnimationFrame(render); //애니메이션 프레임 요청, 브라우저에게 현재 화면의 업데이트 주기에 맞춰 다음 프레임을 그려달라고 요청
    renderer.render(scene, camera) //Three.js의 렌더러를 사용하여 3D 장면을 카메라의 시점에서 렌더링함
}

 

카메라의 정보를 Cinema 4D에서 작업한 camera와 연동하기

const loader = new GLTFLoader();
loader.load("/gltf/scene/scene-1.glb", (gltf) => {
    scene.add(gltf.scene);

    //Cinema 4D에서 작업한 첫 번째  Main camera의 위치값과 회전값 연동 by copy method
    camera.position.copy(gltf.cameras[0].position);
    camera.rotation.copy(gltf.cameras[0].rotation)
}

 

 

결과 -> 검은색으로 나타남

원인 

  1.  Material -> metalness 확인하기, 1인 경우에 주변 배경이 기본적으로 환경 맵이 검정색으로 설정되어 있어서 검은색을 전체 반사 시켜서 물체의 고유색이 나타나지 않음
  2.  주변 조명 세팅이 없어서
    //traverse method를 사용해서 scene 안에 있는 모든 Obejct들 Mesh Object들만 불러오기
    gltf.scene.traverse(child => {
        if(child.isMesh){
            console.log(child.material) //모든 Mesh Object에 붙어있는 Material의 목록을 볼 수 있음
            child.material.metalness = 0;
        }
    }) -> 원인 1 해결
 
const ambientLight = new THREE.AmbientLight(0xffffff, 1);
scene.add(ambientLight); ->원인2 해결
 
 

 

결과 -> 모델링 성공. 그러나 Contrast가 굉장히 진하고 채도가 높게 모델링 되었음 -> renderer의 설정을 통해 조정

 

모델링 색보정 by renderer + orbitControl 설정

const renderer =  new THREE.WebGLRenderer({canvas:canvas, antialias:true});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.outputEncoding = THREE.sRGBEncoding; //모델링 색보정 -> Contrast가 강한 렌더링 방식에서 밝게 만들어줌
renderer.toneMapping = THREE.ReinhardToneMapping; //화면 위에 다른 색상 교정 filter를 씌움, Contrast도 낮아지고 밝기도 낮아짐
renderer.toneMappingExposure = 4; //밝게하기
 
 
    //Orbitcontrol -> 현재 카메라의 위치를 기반으로 조정됨, 따라서 최근에 위치와 회전값이 조정된 상태에서 orbitControl 설정하기
    controls = new OrbitControls(camera, canvas);

 

Cinema 4D에서 설정해줬던 애니메이션 플레이시키기 by mixer, action

    mixer = new THREE.AnimationMixer(gltf.scene);
    action = mixer.clipAction(gltf.animations[0]) //animation 1개 만듦. 따라서 1개만 actino에 적용하기
    action.play();
 
 
 
const clock = new THREE.Clock();

//화면 렌더링 -> 매 프레임마다 화면을 갱신하고 업데이트
function render () {
    requestAnimationFrame(render); //애니메이션 프레임 요청, 브라우저에게 현재 화면의 업데이트 주기에 맞춰 다음 프레임을 그려달라고 요청
    renderer.render(scene, camera) //Three.js의 렌더러를 사용하여 3D 장면을 카메라의 시점에서 렌더링함
    mixer.update(clock.getDelta);
}
 
 

 

클릭했을 때, 애니메이션 플레이 시키기 -> 클릭 인터랙션 부여 by addEventListener

  1. 처음 로드 되었을 때 애니메이션 시간 0초로 고정시키기 
  2. 조건문을 통해 mixer.update를 조정하기
  3. 클릭시 play가 true로 되게끔 addEventListener 부여하기
mixer = new THREE.AnimationMixer(gltf.scene);
    action = mixer.clipAction(gltf.animations[0]) //animation 1개 만듦. 따라서 1개만 actino에 적용하기
    action.loop = THREE.LoopOnce;
    action.play();
    action.time=0; //animtaion time은 0으로 고정
    mixer.update(0); //mixer의 update를 0으로 만들어 줌으로써 시간이 흐르지 앟게끔 고정   -> 1번
 
    //조건문을 통해 바로 Animation이 실행되지 않도록하기  -> 2번
    if(play == true) mixer.update(clock.getDelta())
 
 
//3
addEventListener("click", (event) => {
        play = true;
    })

 

플레이 버튼을 클릭했을 때만 애니메이션 실행시키기 -> RayCaster : three.js에서 클릭을 통해서 Object와 소통할 수 있는 방법

  1. raycaster 선언
  2. mouse x,y의 좌표를 -1~1로 변환하기
  3. raycaster와 mouse 좌표 연동하기
  4. click할때마다 애니메이션 실행시키
addEventListener("click", (event) => {
        const raycaster = new THREE.Raycaster();
        const mouse = new THREE.Vector2();
        mouse.x = (event.clientX/window.innerWidth - 0.5) *2; //x는 0~1 -> -1~1, threejs에서는 좌표를 인식할 때 정가운데가 원점(0,0)임
        mouse.y = -1*((event.clientY/window.innerHeight - 0.5) *2);

        raycaster.setFromCamera(mouse, camera);
        //해당 광선과 겹치는 모든 mesh 목록 받아오기
        const intersects = raycaster.intersectObjects(gltf.scene.children, true);
        // console.log(intersects)

        //button name: play_171
        if(intersects.length > 0 && intersects[0].object.name === "play_171"){
            play = true;
            action.reset(); //클릭할 때마다 애니메이션 플레이시키기
        }
    })

 

+ 창을 늘리고 줄일때마다 canvas 크기 변하게 하기

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

 

3D Scene 모델링 결과