Three.js - Camera

0

WebGL로 가는 길

목록 보기
10/10

Camera

PerspectiveCamera

원근감을 표현해 랜더링

  • 인자1: fovy - 절두체의 높이 방향에 대한 각도 (던위 deg)
  • 인자2: aspect - 절두체의 가로길이를 세로길이로 나눈 비율
  • 인자3: zNear - 카메라로부터의 거리(가장 가까운)
  • 인자4: zFar - 카메라로부터의 거리(가장 먼)

zNear와 zFar 사이에 장면의 모든 물체가 들어와야 화면에 보임.

_setupCamera() {
  const width = this._app.clientWidth;
  const height = this._app.clientHeight;
  const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 100);
  // ddd
  camera.position.set(7, 7, 0); 		// 카메라의 위치
  camera.lookAt(0, 0, 0); 				// 카메라가 바라보는 위치

  this._camera = camera;
  this._scene.add(camera);
}

OrthographicCamera

원근감 없이 물체의 크기대로 랜더링

물체를 따라 카메라 시각 이동

import "../scss/common.scss";
import * as THREE from "three";
import webGL from "three/examples/jsm/capabilities/WebGL";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { RectAreaLightUniformsLib } from "three/examples/jsm/lights/RectAreaLightUniformsLib";
import { RectAreaLightHelper } from "three/examples/jsm/helpers/RectAreaLightHelper";

class App {
  constructor() {
    const scene = new THREE.Scene();
    const renderer = new THREE.WebGLRenderer({ antialias: true });
    const app = document.getElementById("app");

    this._scene = scene;
    this._renderer = renderer;
    this._app = app;

    this._renderer.setPixelRatio(window.devicePixelRatio);
    this._renderer.setSize(window.innerWidth, window.innerHeight);
    this._app.appendChild(this._renderer.domElement);

    this._setupCamera();
    this._setupLight();
    this._setupModel();
    this._setupControls();

    window.onresize = this.resize.bind(this);
    this.resize();

    requestAnimationFrame(this.render.bind(this));

    // if (webGL.isWebGLAvailable()) {
    //   animate();
    // } else {
    //   const warning = webGL.getWebGLErrorMessage();
    //   document.getElementById("app").appendChild(warning);
    // }
  }

  _setupCamera() {
    // const width = this._app.clientWidth;
    // const height = this._app.clientHeight;
    const camera = new THREE.PerspectiveCamera(
      75,
      window.innerHeight / window.innerHeight,
      0.1,
      100
    );
    // ddd
    camera.position.set(7, 7, 0); // 카메라의 위치
    camera.lookAt(0, 0, 0); // 카메라가 바라보는 위치

    this._camera = camera;
    // this._scene.add(camera);
  }

  _setupLight() {
    RectAreaLightUniformsLib.init(); // 광원을 사용하기 위해 초기화 코드 선행

    const light = new THREE.RectAreaLight(0xffffff, 10, 6, 1);
    // 색상, 세기, 광원의 가로길이, 광원의 세로길이
    light.position.set(0, 5, 0);
    light.rotation.x = THREE.Math.degToRad(-90); // -90도 방향으로 빛을 비춤

    const helper = new RectAreaLightHelper(light);
    light.add(helper);

    this._scene.add(light);
    this._light = light;
  }

  _setupModel() {
    const groundGeometry = new THREE.PlaneGeometry(10, 10);
    const groundMaterial = new THREE.MeshStandardMaterial({
      color: "#2c3e5a",
      roughness: 0.5,
      metalness: 0.5,
      side: THREE.DoubleSide,
    });

    const ground = new THREE.Mesh(groundGeometry, groundMaterial);
    ground.rotation.x = THREE.Math.degToRad(-90);
    this._scene.add(ground);

    const bigSphereGeometry = new THREE.SphereGeometry(1.5, 64, 64, 0, Math.PI);
    const bigSphereMaterial = new THREE.MeshStandardMaterial({
      color: "#fff",
      roughness: 0.1,
      metalness: 0.2,
    });

    const bigSphere = new THREE.Mesh(bigSphereGeometry, bigSphereMaterial);
    bigSphere.rotation.x = THREE.Math.degToRad(-90);
    this._scene.add(bigSphere);

    const torusGeometry = new THREE.TorusGeometry(0.4, 0.1, 32, 32);
    const torusMaterial = new THREE.MeshStandardMaterial({
      color: "#9b59b6",
      roughness: 0.5,
      metalness: 0.9,
    });

    for (let i = 0; i < 8; i++) {
      const torusPivot = new THREE.Object3D();
      const torus = new THREE.Mesh(torusGeometry, torusMaterial);
      torusPivot.rotation.y = THREE.Math.degToRad(45 * i);
      torus.position.set(3, 0.5, 0);
      torusPivot.add(torus);
      this._scene.add(torusPivot);
    }

    const smallSphereGeometry = new THREE.SphereGeometry(0.3, 32, 32);
    const smallSphereMaterial = new THREE.MeshStandardMaterial({
      color: "#e74c3c",
      roughness: 0.2,
      metalness: 0.5,
    });

    const smallSpherePivot = new THREE.Object3D();
    const smallSphere = new THREE.Mesh(
      smallSphereGeometry,
      smallSphereMaterial
    );
    smallSpherePivot.add(smallSphere);
    smallSpherePivot.name = "smallSpherePivot"; // 이름을 부여해주면 scene 객체를 통해 언제든 조회할 수 있다.
    smallSphere.position.set(3, 0.5, 0);
    this._scene.add(smallSpherePivot);

    const targetPivot = new THREE.Object3D();
    const target = new THREE.Object3D();
    targetPivot.add(target);
    targetPivot.name = "targetPivot";
    target.position.set(3, 0.5, 0);
    this._scene.add(targetPivot);
  }

  _setupControls() {
    new OrbitControls(this._camera, this._app);
  }

  resize() {
    const width = this._app.clientWidth;
    const height = this._app.clientHeight;
    const aspect = width / height;

    if (this._camera instanceof THREE.PerspectiveCamera) {
      this._camera.aspect = aspect;
    } else {
      this._camera.left = -1 * aspect; // xLeft
      this._camera.right = 1 * aspect; // xRight
    }
    this._camera.updateProjectionMatrix();

    this._renderer.setSize(width, height);
  }

  render(time) {
    // time : 렌더링이 처음 시작된 이후 경과된 값으로 밀리세컨드
    this._renderer.render(this._scene, this._camera);
    this.update(time);
    requestAnimationFrame(this.render.bind(this));
  }

  update(time) {
    time *= 0.001;

    const smallSpherePivot = this._scene.getObjectByName("smallSpherePivot");
    if (smallSpherePivot) {
      smallSpherePivot.rotation.y = THREE.Math.degToRad(time * 50);

      const smallSphere = smallSpherePivot.children[0];
      smallSphere.getWorldPosition(this._camera.position);

      const targetPivot = this._scene.getObjectByName("targetPivot");
      if (targetPivot) {
        targetPivot.rotation.y = THREE.Math.degToRad(time * 50 + 10);

        const target = targetPivot.children[0];
        const pt = new THREE.Vector3();

        target.getWorldPosition(pt);
        this._camera.lookAt(pt);
      }

      if (this._light.target) {
        const smallSphere = smallSpherePivot.children[0];
        smallSphere.getWorldPosition(this._light.target.position);

        if (this._lightHelper) this._lightHelper.update();
      }
    }
  }
}

window.onload = () => {
  new App();
};
profile
를 질투하는 그냥 개발자입니다.

0개의 댓글