본문 바로가기

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

[3D 인터랙티브 웹] 3D Scene - 2 렌더링

Three.js 기본세팅

import { mode } from "mathjs";
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.setPixelRatio(window.devicePixelRatio); //현재 디바이스의 픽셀 비율 설정 -> 더 선명한 이미지
renderer.setSize(window.innerWidth, window.innerHeight);

const camera = new THREE.PerspectiveCamera(35, window.innerWidth/window.innerHeight);

const scene = new THREE.Scene();
scene.background= new THREE.Color("#5ddaa2");

const hemiLight = new THREE.HemisphereLight(0xffffff, 0x000000, 1);
scene.add(hemiLight);

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

    camera.position.copy(gltf.cameras[0].position);
    const controls = new OrbitControls(camera, canvas);
   
    controls.enableDamping = true;
    //물체가 위에 있음 ->control의 target지점이 아래쪽에 있음 -> 카메라의 정면을 바라보게 하기 -> controls.target 치 변경
    controls.target.set(0, camera.position.y, 0);
    controls.update();

    render();
})


function render() {
    requestAnimationFrame(render);
    renderer.render(scene,camera)
}

 

렌더링 결과 -> 검정색으로 나옴 

Material의 metalness 변경하기

model.traverse(child =>{
        if(child.isMesh){
            child.material.metalness = 0;
        }
    })

 

로켓과 배경색 제외하고 전부 Lighting 영향 받지 않게하기 & tonemapping

model.traverse(child =>{
        if(child.isMesh){
            child.material.metalness = 0;
            console.log(child.name);

            if(child.name == "colorize" || child.name == "theme"){
            } else {
                child.material = new THREE.MeshBasicMaterial({
                    map: child.material.map, //조명(hemisLight)의 영향을 받지 않고 본연의 색만 나타냄.
                })
            }
        }
    })
 
 
const renderer =  new THREE.WebGLRenderer({canvas:canvas, antialias:true});
renderer.setPixelRatio(window.devicePixelRatio); //현재 디바이스의 픽셀 비율 설정 -> 더 선명한 이미지
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.outputEncoding = THREE.sRGBEncoding; //모델링 색보정 -> Contrast가 강한 렌더링 방식에서 밝게 만들어줌
renderer.toneMapping = THREE.ReinhardToneMapping;
renderer.toneMappingExposure = 3; //밝게하기
 

 

모델링 되었을 때 처음 Scene 조정하기 using GUI

  • 로켓의 색상과 팔레트의 색상 일치시키기 -> 색상 수집하기 by GUI
  • day와 night object 중 하나 비활성화
  • color 팔레트 생성하기 
const gui = new GUI(); //우측 상단에 close controls 생성 -> control에 로켓의 색상과 테마의 색상 등록하기
 
const colors = {
    white: 0xffffff,
    blue: 0x11ff,
    green: 0x13c200,
    brown: 0xff2d00,
};
 
 
scene.getObjectByName("night").visible=false; //비활성
    scene.getObjectByName("colorize").material.color.set(0xff2d00); //0x11ff -> 처음 Scene 조정
    scene.getObjectByName("theme").material.color.set(0x13c200);


    const color = {
        color: 0xffffff,
    };
    // gui.addColor(scene.getObjectByName("colorize").material, "color"); //컬러 팔레트 생성(addColor), 색상 조정 가능
    gui.addColor(color, "color").onChange((color) => {
        scene.getObjectByName("colorize").material.color.set(color) //색상을 바꿀때마다 로켓색상도 같이 바뀜
    });
 
 

 

클릭에 따라서 애니메이션 플레이시키기 using AnimationMixer

AnimationMixer : Three.js에서 애니메이션을 효과적으로 다룰 수 있도록 도와주는 클래스, 다음과 같은 기능을 제공함

  • 애니메이션 트랙(Animation Tracks) 관리 - Mixer를 통해 애니메이션 트랙을 관리하고 여러 트랙을 조합할 수 있음. 각 트랙은 모델의 특정 속성(위치, 회전, 스케일, 뼈, 등)에 대한 애니메이션을 나타냄
  • 애니메이션 블렌딩(Blending) - 애니메이션을 부드럽게 블렌딩하여 자연스러운 전환 효과를 생성함
  • 애니메이션 업데이트 - 현재 프레임에 해당하는 애니메이션 상태를 업데이트
  • 애니메이션 제어 - 애니메이션의 재생, 일시정지, 정지 등을 제어할 수 있음

 

1. 모델링의 이름에 따라서 애니메이션 나누기

//animation play
    mixer = new THREE.AnimationMixer(gltf.scene);
    // console.log(gltf.animations[0]) //tracks 내에 13개의 Track이 존재함 -> track 가져오기
    const tracks = gltf.animations[0].tracks;
    const animations = {}; //track을 가져와서 animations에 넣을거임
    //track을 clip으로 바꿔주기
    tracks.forEach((track)=>{
        const name = track.name.split(".")[0];

        if(!animations[name]){
            animations[name] = [];
        }

        animations[name].push(track); //모델의 이름에 맞춰서 해당 애니메이션 KeyframeTrack들이 들어감
    });

    const clips = [];
    Object.entries(animations).forEach(animation =>{
        //Key 이름과 값을 분리하기
        const name = animation[0];
        const track = animation[1];
        const clip = new THREE.AnimationClip(name, -1, track ) //duration은 무한인 새로운 clip 생성
        clips.push(clip); //AnimtaionClip들이 모델링의 이름에 맞춰서 들어감
    })
    // console.log(clips)
    gltf.animations = clips;

 

2. click 인터랙션에 따라 해당 Object의 애니메이션 플레이시키 using raycaster

addEventListener("click", (event) => {
        const raycaster = new THREE.Raycaster();
        const mouse = new THREE.Vector2()

        mouse.x = (event.clientX / window.innerWidth - 0.5) * 2;
        mouse.y = (event.clientY / window.innerHeight - 0.5) * -2;

        raycaster.setFromCamera(mouse, camera);
        const intersects = raycaster.intersectObjects(gltf.scene.children, true);

        if(intersects.length > 0){
            const name = intersects[0].object.name;
            // console.log(name)//클릭했을 때 해당 object의 이름을 출력
            playAnimation(name); //클릭시 해당 애니메이션 플레
        }
    })

    function playAnimation(name) {
        const clip = gltf.animations.find((animation ) => {
            return animation.name === name;
        })

        if(clip) {
            const action = mixer.clipAction(clip);
            action.stop();
            action.loop = THREE.LoopOnce;
            action.clampWhenFinished = true;
            action.play();
        }
    }

    window.playAnimation = playAnimation; //console창에서 playAnimation 실행 가능
 
const clock = new THREE.Clock();

function render() {
    requestAnimationFrame(render);
    renderer.render(scene,camera)
    mixer.update(clock.getDelta())
}

 

3. 화살표 버튼 클릭시 테마의 색상과 배경 색상 바꾸기

if(intersects.length > 0){
            const name = intersects[0].object.name;
            // console.log(name)//클릭했을 때 해당 object의 이름을 출력
            playAnimation(name);

            //팔레트 색상 선택시 로켓 색상 변경
            if (name === "color_2") {
                scene.getObjectByName("colorize").material.color.set(colors.white);
            }
            if (name === "color_3") {
                scene.getObjectByName("colorize").material.color.set(colors.brown);
            }
            if (name === "color") {
                scene.getObjectByName("colorize").material.color.set(colors.green);
            }
            if (name === "color_1") {
                scene.getObjectByName("colorize").material.color.set(colors.blue);
            }
            //화살표 클릭세 테마 변경하기
            if(name === "button_left" || name === "button_right") {
                // if(isDay){
                //  isDay = false;
                // } else {
                //  isDay = true;
                // }
                isDay = !isDay; //토글기능

                if(isDay){
                    scene.getObjectByName("day").visible = true;
                    scene.getObjectByName("night").visible = false;
                    scene.getObjectByName("theme").material.color.set(colors.green);
                } else {
                    scene.getObjectByName("day").visible = false;
                    scene.getObjectByName("night").visible = true;
                    scene.getObjectByName("theme").material.color.set(colors.blue);
                }
            }