• 카메라 가지고 놀기 1탄

    2022. 10. 4.

    by. 고구마달랭이

    뚝딱뚝딱 오늘의 Three.js 일기

    gsap를 사용해서 카메라를 움직여 보장 ~ ~

    결과물은 맨 마지막에 🙊

     


     

    class App {
      constructor(){};	// 생성자
      initialize()	// 초기화 함수
      models();	// 메쉬 세팅
      setCamera();	// 카메라 세팅
      render();	// 렌더 설정
      resize();	// 화면 리사이즈 세팅
      scroll();	// 스크롤에 따라 카메라 값 세팅
    };
    
    window.onload = function(){
      new App();
    };

    우선 크게 구조는 이렇게 된다!

    저 함수들이 무엇인고 ~ ~

    차근 차근 알아보자 ㅎㅇㅎ

     


     

    constructor()

      constructor() {
        this.initialize();
        this.render();
      }

    처음 페이지가 로드되면 new App() 객체가 실행이 되고,

    그 객체가 실행이 되면서 constructor는 자동으로 실행이 된당

    그렇다면 페이지가 로드될 때 마다 initialize 함수와 render 함수는 바로 실행되겠징!?

    여기서 this는 객체 자신, 즉 App()을 가리킨다!

     


     

    initialize()

      initialize() {
        const canvas = document.querySelector('.canvas')
        let scene = new THREE.Scene();  // 무대 생성
        let renderer = new THREE.WebGLRenderer({ canvas });  // { canvas: canvas } 축약형
    
        renderer.setClearColor(0x000000, 1.0);  // (배경색, 불투명도)
        renderer.setPixelRatio(window.devicePixelRatio);  // renderer 픽셀 비율을 현재 장치의 픽셀 비율과 맞춤
        renderer.setSize(window.innerWidth, window.innerHeight);  // 화면 사이즈와 같게 세팅
    
        window.onresize = this.resize.bind(this);  // bind(this)를 써주지 않으면 this가 window를 가리킬 수 있음
        window.onscroll = this.scroll.bind(this);
    
        this.scene = scene;  // 구한 scene과 renderer의 값을 this에 담아줌 (다른 함수에서 써야되니까)
        this.renderer = renderer;
    
        this.setCamera();
        this.models();
        this.scroll();
      }

    초기화 함수 답게 기본 설정값들 쏙쏙

     


     

    models()

      models() {
        let cubeGeometry = new THREE.BoxGeometry(1, 1, 1);
        let cubeMaterial = new THREE.MeshNormalMaterial(); // x축을 R, y축을 G, z축을 B로 매핑해서 현재 표면의 법선벡터가 가리키는 방향으로 색상 표현
        let cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
    
        cube.position.x = 0;
        cube.position.y = 0;
        cube.position.z = 0;
    
        this.scene.add(cube);
    
        this.cube = cube;
      }

    Mesh = Geometry+ Material

    사람 👧🏻 = 뼈 💀 + 가죽 💪🏻

    MeshNormalMateral()은 그냥 색이 예뻐서 썼당 ㅎㅎ

     

    cube의 포지션을 (0,0,0)으로 세팅 해주고

    무대(scene) 위에 cube 추가!

    this에 만든 cube 넣어주기!

     


     

    setCamera()

      setCamera() {
        let camera = new THREE.PerspectiveCamera(  // 3D 장면을 렌더링하는데 가장 널리 쓰이는 투영 모드
          45,  // 시야각(field of view)
          window.innerWidth / window.innerHeight,  // 종횡비(aspect)
          0.1,  // near
          1000  // far
        );
        
        camera.position.set(1, 1, 3); // (x, y, z) (좌우, 상하, 앞뒤)
        
        this.scene.add(camera);
        
        this.camera = camera;
      }

    PerspectiveCamera는 원근법이 나타나는 카메라!

    카메라의 near와 far 사이에 있는 부분만 렌더링하게 해서 불필요한 자원낭비를 막을 수 있당

     

    카메라의 기본 위치값은 (0, 0, 0)이기 때문에 약간 뒤로 빼줘야 한다

    (0, 0, 0)에 서 있는 사람 심장에 카메라가 들어있는 거랑 같걸랑 ~ ~

    포지션 값은 position.x = 1;  이렇게 줘두 된다! 편한대로 하기 ㅎㅎ

     

    똑같이 scene에 camera 추가 해주고 this에 담아주자

     


     

    render()

      render() {
        this.renderer.render(this.scene, this.camera);  // 무대와 카메라를 화면에 그리기
        requestAnimationFrame(this.render.bind(this));  // 부드러운 애니메이션
        
        this.camera.lookAt(this.cube.position);  // 카메라가 cube의 위치를 따라 쳐다보게 설정
      }

    브라우저 화면 상에 무언가를 render할 때 위치나 색을 계산하는 과정을 리플로우라고 하는데

    그 계산을 실제로 수행해서 브라우저상에 그리는 것을 리페인트라고 한당 🎨

     

    애니메이션의 경우 리페인트가 끝나기도 전에 계속 실행이 되면 부드럽게 움직이지 않게 되고..

    그것을 해결해 주는 것이 requestAnimationFrame() !!

    리페인트 과정이 끝난 후 this.render.bind(this)를 하도록 도와준다 👍

     


     

    resize()

      resize() {
        let camera = this.camera;
        let renderer = this.renderer;
        let scene = this.scene;
    
        camera.aspect = window.innerWidth / window.innerHeight;  // 카메라 비율을 창 비율과 맞춤
        camera.updateProjectionMatrix();  // 속성이 변경됐으면 적용시키기 위해 써줌
    
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.render(scene, camera);
      }

    창 사이즈가 변경되었을 때

    camera의 비율과 renderer의 사이즈를 현재 창 사이즈와 같게 해주기 ~ ~

     

    카메라의 비율을 변경(update)했기 때문에

    updateProjectionMatrix()를 써주어야 변경된 값이 적용 된당

     


     

    scroll()

      scroll() {
        const values = [];
        
        values.push({ x: 1, y: 1, z: 3})
        values.push({ x: 1, y: 1, z: 3})
        values.push({ x: 3, y: 1, z: 0})
        values.push({ x: 2, y: 2, z: -2})
        values.push({ x: 3, y: -1, z: 1})
        values.push({ x: 1, y: 1, z: 3})
    
        let currentSection = 0;
        currentSection = Math.round(scrollY / innerHeight);
    
        gsap.to(
          this.camera.position,
          {
            duration: 1,
            x: values[currentSection].x,
            y: values[currentSection].y,
            z: values[currentSection].z
          }
        )
      }

    values 라는 배열을 만들고

    원하는 좌표의 값을 객체에 담아 push로 배열에 넣어준당

     

    그리고 currentSection을 구해주는데

    css에서 각 섹션의 높이값을 100vh로 설정해주었기 때문에

    scrollY를 innerHeight로 나누어 주기만 하면 된답 ㅎㅎ

     

    나는 중간 쯤에서 카메라가 움직이길 원해서 Math.round를 주어따

    정확한 수치를 원한다면 다른 방법을 써야겠징?

     

    gsap로 카메라 포지션 x, y, z의 값을 1초에 걸쳐 움직이라고 넣어주면 끝 ~! ~!

     


     

    짜잔 ~ ~ 결과물 !! 두둥 🙊

     

     

    기본적인 box mesh에 텍스트만 흘려도 예뿌다!!

    간단한 스크롤 이벤트인데도 3D는 달라 ! ! 짱이뿌다!! ㅎㅎㅎ

     

    mesh만 다른 3D 모델링 된 걸루 바꾸면 더더 great 하겠지요 ~ ~ ?

    gsap를 사용한 카메라 이동은

    특정한 위치를 설명할 때 쓰기 좋은 것 같당!

     

    카메라 갖고 놀기 2탄에서는

    지금 처럼 휙휙 이동 말고

    스크롤에 따라 쩜쩜쩜 움직이는 이동으로 ~ ~ ^ㅇ^

     


     

    아효 이거 쓰는 거 진짜 힘든디

    쓰면서 머리에 꼭꼭 새겨 넣어지는 게 느껴진당 ㅎ ㅎ

    그리고 의문점이 드는 부분들도 생기고 ~~ ~

    암텅 좋네요 ~~

     

    오늘 한화 모델링된 거랑

    시영cd님이 샘플로 만들어 주신 거 받았는데

    (샘플이라 소스가 불친절할 거라고 하셔따 ㅋㅋㅋㅋ 친절해도 이해 못해요...)

    보고 나니까 더더 현실을 직시하게 되어따 ^^...

    나.. 정말... 성장한다...

     

    오늘도 내일도 내일 모레도 앞으로도 쭉 ~~~ 화팅 ~!~!

    포기하지 말고! ! 힘내쟈 단단해지장~!

    현정이 할 수 있다 아자아자 ! ! 👊

     

    아 스팸 넣은 김치찌개 먹고싶다

    댓글