물리(Physics) 계수

반발계수(coefficient of restitution, COR)

반발 계수는 물체의 충돌 전후 속도의 비율을 나타내는 분수값이다. 반발 계수가 1인 물체는 탄성 충돌을 하며, 반발 계수가 1보다 작은 물체는 비탄성 충돌을 한다. 반발 계수가 0이면 완전 비탄성 충돌을 하며, 충돌한 물체와 붙어서 튀지 않는다. (출처: 위키백과)

마찰력(friction)

마찰력은 두 물체의 접촉면 사이에서 물체의 운동을 방해하는 힘이다. 마찰력의 계수는 물질의 고유 성질이 아닌, 어떤 물체의 어떤 표면 상태에서 어떤 물질과 접촉하는가 등에 관계있으며 실험을 통해 결정되는 현상론적인 양이다. 교과서는 일반적으로 마찰력은 접촉면의 넓이에는 무관하다고 서술하나 이것은 접촉면이 이상적으로 매끄러운 경우에만 성립한다. 실제로는 접촉면의 매끄러운 정도 등에 따라 영향을 받는다. (출처: 위키백과)

질량(mass)

질량은 물리학에서 물질이 가지고 있는 고유한 양을 일컫는 말이다. 질량의 SI 단위는 킬로그램(kg)이다. (출처: 위키백과)

THREE.JS 퀵 레퍼런스 코드

Three.js를 이용한 개발 시 개인적으로 빠르게 참조하기 위해 작성한 글입니다.

three.js 기본 프로젝트 생성

git clone https://github.com/GISDEVCODE/threejs-with-javascript-starter.git .

그림자 적용에 대한 코드

renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.VSMShadowMap;

const shadowLight = new THREE.DirectionalLight(0xffe79d, 0.7);
shadowLight.position.set(150, 220, 100);
shadowLight.target.position.set(0,0,0);
shadowLight.castShadow = true;
shadowLight.shadow.mapSize.width = 1024*10;
shadowLight.shadow.mapSize.height = 1024*10;
shadowLight.shadow.camera.top = shadowLight.shadow.camera.right = 1000;
shadowLight.shadow.camera.bottom = shadowLight.shadow.camera.left = -1000;
shadowLight.shadow.camera.far = 800;
shadowLight.shadow.radius = 5;
shadowLight.shadow.blurSamples = 5;
shadowLight.shadow.bias = -0.0002;

const cameraHelper = new THREE.CameraHelper(shadowLight.shadow.camera);
this._scene.add(cameraHelper);

island.receiveShadow = true;
island.castShadow = true;

지오메트리의 좌표 수정

const sphereGeom = new THREE.SphereGeometry(6 + Math.floor(Math.random() * 12), 8, 8);
const sphereGeomPosition = sphereGeom.attributes.position;
for (var i = 0; i < sphereGeomPosition.count; i++) {
    sphereGeomPosition.setY(i, sphereGeomPosition.getY(i) + Math.random() * 4 - 2);
    sphereGeomPosition.setX(i, sphereGeomPosition.getX(i) + Math.random() * 3 - 1.5);
    sphereGeomPosition.setZ(i, sphereGeomPosition.getZ(i) + Math.random() * 3 - 1.5);
}

sphereGeom.computeVertexNormals();
sphereGeom.attributes.position.needsUpdate = true;

지오메트리에 사용자 정의 데이터 주입

// 주입
const waves = [];
const waterGeoPositions = waterGeo.attributes.position;
for (let i = 0; i < waterGeoPositions.count; i++) {
    waves[i] = Math.random() * 100;
}
waterGeo.setAttribute("wave", new THREE.Float32BufferAttribute(waves, 1));

// 읽기
const waves = sea.geometry.attributes.wave;

for(let i=0; i<positions.count; i++) {
    const v = waves.getX(i);
}

안개 설정 코드

scene.fog = new THREE.Fog("rgba(54,219,214,1)", 1000, 1400);

OrbitControls 관련 코드

const controls = new OrbitControls(this._camera, this._divContainer);
controls.minPolarAngle = -Math.PI / 2;
controls.maxPolarAngle = Math.PI / 2 + 0.1;
controls.enableZoom = true;
controls.enablePan = false;
controls.autoRotate = true;
controls.autoRotateSpeed = 0.2;

this._controls = controls;

this._controls.update();

Object3D의 MBR 얻기

const board = this._scene.getObjectByName("Board");
const box = new THREE.Box3().setFromObject(board);
console.log(box);

Mesh의 월드좌표에 대한 position 얻기

mesh.updateMatrixWorld();

const worldPos = new THREE.Vector3();
worldPos.setFromMatrixPosition(mesh.matrixWorld);

Faked Shadow

그림자를 위한 매시에 대한 재질 속성 지정이 핵심. 참고로 shadow에 대한 이미지는 투명 이미지가 아님. 즉, 배경색이 하얀색인 이미지임.

const shadow = new THREE.TextureLoader().load( 'models/gltf/ferrari_ao.png' );

const mesh = new THREE.Mesh(
    new THREE.PlaneGeometry( 0.655 * 4, 1.3 * 4 ),
    new THREE.MeshBasicMaterial( {
        map: shadow, 
        blending: THREE.MultiplyBlending, 
        toneMapped: false, 
        transparent: true
    } )
);
mesh.rotation.x = - Math.PI / 2;
mesh.renderOrder = 2;
carModel.add( mesh );

텍스쳐 이미지 품질 올리기

샘플링 횟수를 올리는 것으로 속도는 느려질 수 있으나 품질은 향상됨

texture.anisotropy = renderer.capabilities.getMaxAnisotropy();

async 리소스 로딩

async function init() {
    const rgbeLoader = new RGBELoader().setPath('textures/equirectangular/');
    const gltfLoader = new GLTFLoader().setPath('models/gltf/DamagedHelmet/glTF/');

    const [texture, gltf] = await Promise.all([
        rgbeLoader.loadAsync( 'venice_sunset_1k.hdr' ),
        gltfLoader.loadAsync( 'DamagedHelmet.gltf' ),
    ]);
}

init().catch(function(err) {
    console.error(err);
});

텍스쳐를 Canvas로 후다닥 만들기

const canvas = document.createElement( 'canvas' );
canvas.width = 1;
canvas.height = 32;

const context = canvas.getContext( '2d' );
const gradient = context.createLinearGradient( 0, 0, 0, 32 );
gradient.addColorStop( 0.0, '#ff0000' );
gradient.addColorStop( 0.5, '#00ff00' );
gradient.addColorStop( 1.0, '#0000ff' );
context.fillStyle = gradient;
context.fillRect( 0, 0, 1, 32 );

const sky = new THREE.Mesh(
	new THREE.SphereGeometry( 10 ),
	new THREE.MeshBasicMaterial( { map: new THREE.CanvasTexture( canvas ), side: THREE.BackSide } )
);
scene.add( sky );

GLTF 파일 로딩

import { GLTFLoader } from "../examples/jsm/loaders/GLTFLoader.js"

const loader = new GLTFLoader();
loader.load("./data/ring.glb", gltf => {
    const object = gltf.scene;
    this._scene.add(object);
});    

InstancedMesh

const mesh = new THREE.InstancedMesh(geometry, material, 10000)

const matrix = new THREE.Matrix4()
const dummy = new THREE.Object3D()
 
for(let i = 0; i < 10000; i++) {
  mesh.getMatrixAt(i, matrix)
  matrix.decompose(dummy.position, dummy.rotation, dummy.scale)
  
  dummy.rotation.x = Math.random()
  dummy.rotation.y = Math.random()
  dummy.rotation.z = Math.random()

  dummy.updateMatrix()

  mesh.setMatrixAt(i, dummy.matrix)
  mesh.setColorAt(i, new THREE.Color(Math.random() * 0xffffff)
}

mesh.instanceMatrix.needsUpdate()

Image 기반 광원(IBL)

import { RGBELoader } from 'three/examples/jsm/Addons.js'

...

new RGBELoader().setPath("./").load("pine_attic_2k.hdr", (data) => {
  data.mapping = THREE.EquirectangularReflectionMapping;
  // this.scene.background = data;
  // this.scene.backgroundBlurriness = 0.6;
  this.scene.environment = data;
})

GLTF / GLB 파일 로딩

import { GLTFLoader, OrbitControls } from "three/addons/Addons.js"

...

const loader = new GLTFLoader();
loader.load(
  "fileName.glb",
  (gltf) => {
    this._scene.add( gltf.scene );    
  },
  (xhr) => { console.log( ( xhr.loaded / xhr.total * 100 ) + "% loaded" ); },
  (error) => { console.log( "An error happened" ); }
);

Object3D를 빌보드로 만들기

this._mesh.quaternion.copy(this._camera.quaternion );
// or
this._mesh.rotation.setFromRotationMatrix( this._camera.matrix );

PostgreSQL의 Time Zone 변경

먼저 현재 DB에 대한 TimeZone을 확인하기 위한 명령어는 다음과 같습니다.

SHOW timezone;

타임존이 한국이 아닐 경우 아래의 명령을 통해 한국으로 변경합니다. !DB_NAME!에 데이터베이스 이름이 들어갑니다.

ALTER DATABASE !DB_NAME! SET timezone = 'Asia/Seoul';

타임존을 변경한 뒤에는 PostgreSQL을 반드시 재시작해야합니다.