GLSL, 원(Circle)

완전한 3차원 장면은 Shader로 완성된다고 할 수 있는데요. Shader를 통해 매우 사실적인 물, 안개, 번개 등과 같은 표현이 가능합니다. 아래는 Shader 언어 중 GLSL로 작성된 원을 렌더링하는 코드입니다.

#ifdef GL_ES
precision mediump float;
#endif

uniform vec2 u_resolution;

float circle(vec2 position, float radius) {
    return step(length(position), radius);
}

void main() {
    vec2 position = gl_FragCoord.xy / u_resolution;
    position -= 0.5;
    vec3 color = vec3(circle(position, 0.3));
    gl_FragColor = vec4(color, 1.0);

}

결과는 아래와 같구요.

코드를 보면 12번은 색상을 결정해야 하는 위치 좌표를 (0,0) ~(1,1)의 범위로 정규화해 줍니다. (0,0)은 화면의 좌측하단입니다. 13번은 다시 이 좌표를 (-0.5,-0.5) ~ (0.5,0.5)로 변경해 줍니다. 원의 가운데 위치하도록 하기 위함입니다. 8번은 원을 표현하기 위한 픽셀의 색상값을 결정하는 함수의 코드인데요. step(A, B)는 B가 A보다 크거나 같으면 1을 반환하고, 아니라면 0을 반환합니다.

3D 모델을 화면에 가득 채우고 보는 시점 지정하기

three.js를 이용하여 3D 모델을 불러와서 화면에 렌더링 할 때, 해당 모델의 크기와 원점이 모두 상이하여 이를 공통적으로 처리하기 위한 함수입니다.

_zoomFit(object3D, camera, viewMode, bFront) {
    const box = new THREE.Box3().setFromObject(object3D);
    const sizeBox = box.getSize(new THREE.Vector3()).length();
    const centerBox = box.getCenter(new THREE.Vector3());

    let offsetX = 0, offsetY = 0, offsetZ = 0;
    viewMode === "X" ? offsetX = 1 : (viewMode === "Y") ? offsetY = 1 : offsetZ = 1;
    if(!bFront) {
        offsetX *= -1;
        offsetY *= -1;
        offsetZ *= -1;
    }
    camera.position.set(centerBox.x + offsetX, centerBox.y + offsetY, centerBox.z + offsetZ);

    const halfSizeModel = sizeBox * 0.5;
    const halfFov = THREE.Math.degToRad(camera.fov * .5);
    const distance = halfSizeModel / Math.tan(halfFov);

    const direction = (new THREE.Vector3()).subVectors(camera.position, centerBox).normalize();

    const position = direction.multiplyScalar(distance).add(centerBox);
    camera.position.copy(position);

    camera.near = sizeBox / 100;
    camera.far = sizeBox * 100;

    camera.updateProjectionMatrix();

    camera.lookAt(centerBox.x, centerBox.y, centerBox.z);
}

실제 활용은 다음처럼 fbx 형식의 모델 파일 불러와 화면상에 렌더링하는 예를 보면 쉽게 확인할 수 있습니다.

loader.load('data/Rumba Dancing.fbx', object => {
    this._scene.add(object);
    this._zoomFit(object, this._camera, "X", true);
} );

위의 _zoomFit의 세번째와 네번째 인자인 “X”, true는 양(plus)의 X축 방향에서 바라본다는 의미입니다. 위 코드의 결과는 다음과 같습니다.

Three.JS 동영상 강좌

웹에서 3차원 그래픽 기능을 개발할 수 있는 자바스크립트 라이브러리인 Three.js에 대한 강좌입니다. Three.js 학습을 계획하고 있다면 이 강좌가 도움이 되리라 확신합니다.














JavaScript의 class에서 private Field, Method 정의

최근 JS의 class에서 private 접근자를 지원한다는 것을 알게 되었습니다. 그전에는 밑줄(_)로 시작하는 필드와 매서드가 private라는 강제성이 없는 방식으로 해왔는데요. 아래는 #width와 #height 필드 그리고 #getArea 매서드가 private 접근자의 성격을 갖습니다.

class Rectangle {
    #width = 10;
    #height = 10;

    constructor(height, width) {
        this.#width = width;
        this.#height = height;
    }

    #getArea() {
        return this.#width * this.#height;
    }

    get area() {
        return this.#getArea();
    }
}

이제 아래의 코드는 에러입니다.

const rec = new Rectangle(10, 10);
console.log(rec.#width);

에러의 내용도 명확히 “Private field ‘#width’ …”라고 되어 있습니다.

그러나 Rectangle에 width라는 필드를 추가할 수 있는데, 이 width는 public입니다. #width와는 별개이구요. 혼란스럽습니다. 게다가 아직 protected 접근자는 제공하지도 않습니다.

웹에서의 객체지향적 프로그래밍을 위해서는 아직까지 TypeScript가 강력한 대안으로 생각됩니다.

경위도로 지정한 위치 사이의 흐름선을 3D로 표현하기

구에 지구에 대한 텍스쳐를 맵핑하고 경위도로 지정된 2개의 위치 사이에 흐름선을 표현하는 시각화에 대한 구현체입니다. 최종 실행 결과는 아래와 같습니다.

WebGL을 기반으로 하는 3차원 라이브러리 three.js를 사용했으며, class를 통한 모듈방식으로 구현하였는데, 전체 소스 코드는 다음 URL로 다운로드 받으시기 바랍니다.

GIS 개발자로써 가장 중요한 코드 중 하나를 언급하면 바로 경위도 좌표를 xyz 좌표계로 변환해 주는 함수인데요. 바로 아래의 코드입니다.

_getPosFromLatLonRad(lat, lon, radius) {
    var phi = (90 - lat) * (Math.PI / 180)
    var theta = (lon + 180) * (Math.PI / 180)

    let x = -((radius) * Math.sin(phi)*Math.cos(theta))
    let z = ((radius) * Math.sin(phi)*Math.sin(theta))
    let y = ((radius) * Math.cos(phi))

    return {x,y,z}
}

위도와 경도 그리고 구의 반지름을 받아 해당하는 xyz 축의 좌표를 반환합니다.