[WebGL2-My Cosmos-03] Canvas WebGL 설정

이전 글 : [WebGL2-My Cosmos-02] Canvas 공통 설정

 

[WebGL2-My Cosmos-02] Canvas 공통 설정

이전 글 : [WebGL2-My Cosmos-01] 프로젝트 세팅 [WebGL2-My Cosmos-01] 프로젝트 세팅프로젝트 아이디어WebGL2로 웹에서 3D 그래픽을 구현하고자 내가 좋아하는 우주를 배경으로 시작하고 싶었다.입력한 이

jinsk-joy.tistory.com

 

WebGL 설정

WebGL을 적용하는 캔버스를 관리하기 위한 CanvasGL 클래스를 생성한다.

 

생성자

  • CanvasOption을 상속받아 기본적인 CSS 크기를 설정하고 CavasGL 클래스에서 GL의 물리적 크기를 설정한다.
  • 3D 렌더링을 위해 깊이 테스트를 활성화한다. 깊이 테스트를 활성화하지 않을 경우 그려지는 순서대로 픽셀을 덮어 씌우기 때문에 원근 효과가 제대로 적용되지 않는다.
  • WebGL로 애니메이션을 구현하기 위해 FPS, Interval 값을 설정하고 애니메이션을 관리하기 위한 멤버 변수를 생성한다.
class CanvasGL extends CanvasOption {
    /**
     * GL설정 및 애니메이션 관리
     * @param {string} canvasId
     */
    constructor(canvasId) {
    	// CanvasOption을 상속받아 사용한다.
        super(canvasId);
        
        // webgl2 context를 가져온다. 만약 없을 경우 에러를 발생시킨다.
        this.gl = this.canvas.getContext("webgl2");
        if (isNull(this.gl)) throwError(ERROR_MSG.NO_WEBGL2);

        // 깊이 테스트 활성화
        // WebGL은 기본적으로 2D 기반이므로, 3D 렌더링 시 깊이 축을 고려해야한다.
        // DEPTH_TEST를 활성화하면 WebGL이 Z버퍼를 사용하여 
        // 화면에서 가까운 객체가 먼 객체보다 앞에 그려지도록 처리한다.
        this.gl.enable(this.gl.DEPTH_TEST);
        
        // CanvasOption 사이즈 설정을 바탕으로 WebGL의 물리적 크기 설정
        this.setCanvasGLSize();

        // 애니메이션 관련 설정
        this.fps = ANIMATION.FPS;
        this.interval = 1000 / this.fps;
        this.animationId = null;
        this.animationFunc = null;
    }
    
    생략...
}

 

GL 사이즈 설정

  • 생성자에서 실행될 WebGL의 물리적 크기를 설정하기 위한 메서드를 따로 작성하였다.
  • WebGL의 기본 좌표 공간 즉 클립 공간은 (-1 ~ 1)의 범위를 가지며 픽셀 단위의 크기가 아니다. 좌상단이 (-1, 1), 정가운데가 (0, 0) 우상단이 (1, 1)이라 기존의 좌표체계와 다르다.
  • viewport를 설정하면 클립 공간을 실제 우리가 사용하는 좌표체계인 픽셀 좌표계로 변환하여 실제 화면 크기에 맞게 설정할 수 있게 된다. 이 과정에 없으면 WebGL이 캔버스의 일부만 사용하거나 왜곡된 화면을 출력할 수 있다.
setCanvasGLSize() {
    // 부모 클래스인 CanvasOption의 메서드를 실행해 CSS 크기를 설정한다.
    // 이 과정에서 canvas 태그의 width, height와 DPR의 값이 결정된다.
    this.initCanvasOptionSizeVars();

    // CSS 크기에 DPR을 곱하여 WebGL의 물리적 크기(실제 랜더링 해상도)를 설정한다.
    this.gl.canvas.width = this.canvasCssWidth * this.dpr;
    this.gl.canvas.height = this.canvasCssHeight * this.dpr;

    // WebGL의 viewport 크기를 물리적 크기로 설정하여 캔버스 전체 영역이 정상적으로 랜더링되도록 한다.
    this.gl.viewport(0, 0, this.gl.canvas.width, this.gl.canvas.height);
    
    // 카메라 설정 시 필요한 종횡비를 계산하여 설정한다. (투영행렬에 사용)
    this.aspect = this.gl.canvas.width / this.gl.canvas.height;
}

 

애니메이션 공통 설정

  • 2개의 GL 화면에서 애니메이션을 사용하고 있으므로 공통부분을 설정하는 메서드를 만들어 재사용성을 높였다.
  • this.animationFunc은 각 화면에서 실행될 개별 애니메이션 로직을 저장하고 실행하는 역할을 한다.
  • 애니메이션 속도는 초당 60 프레임(FPS)을 유지되며 1 프레임의 지속 시간은 약 16.67ms(1000ms / 60, interval)이다.
  • 애니메이션 속도를 일정하게 유지하기 위해 delta(현재 시간과 이전 시간과의 차이)가 16.67ms를 초과한 경우 초과된 시간을 보정하여 다음 프레임이 일정한 간격으로 유지되도록 한다.
// 애니메이션 실행 메서드 (공통 뼈대)
renderAnimation() {
    // 이전 프레임의 시간을 저장하는 변수
    // 초기값은 currentTime이 지원되면 해당 값을 사용하고 아닐경우 performance.now를 사용한다.
    let then = document.timeline?.currentTime || performance.now();

    // 애니메이션 프레임을 처리하는 함수
    const frame = (now) => {
        // 현재 프레임과 이전 프레임의 시간차이를 계산한다.
        const delta = now - then;

        // 일정 시간(interval - 1000ms / 60프레임)이 경과하면 애니메이션을 갱신한다.
        if (delta >= this.interval) {
            // GL에서 시간단위는 주로 sec를 사용하므로 ms를 sec로 변환한다.
            const uTime = now * 0.001;
            
            // 이전 프레임의 잔상을 제거하기 위해 색상, 깊이 버퍼를 초기화한다.
            this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);

            // 각 화면에서 개별적으로 설정한 애니메이션을 실행한다.
            this.animationFunc(uTime);

            // 오차를 보전해 일관된 속도를 유지한다.
            then = now - (delta % this.interval);
        }

        // 다음 프레임을 예약하여 재귀적으로 애니메이션이 실행
        this.animationId = requestAnimationFrame(frame);
    };

    // 애니메이션 실행 시작
    this.animationId = requestAnimationFrame(frame);
}
// 애니메이션 취소 함수
cancelAnimation() {
    // cancelAnimationFrame과 animationId을 사용하여 현재 실행되고 있는 애니메이션 중단
    cancelAnimationFrame(this.animationId);
    
    // 애니메이션을 취소한다음 id를 초기화 시킨다.
    this.animationId = null;
}

 

 

Github 레포

 

GitHub - jinsk9268/my-cosmos: 나만의 우주 탐험하기

나만의 우주 탐험하기. Contribute to jinsk9268/my-cosmos development by creating an account on GitHub.

github.com