본문으로 건너뛰기

WebGL 로 사각형 그리기_WebGL 노트 1

무료2015-12-19#WebGL#JS#webgl#WebGL入门#WebGL教程#WebGL指南#webgl实例

본 글에서는 간단한 WebGL 프로그램을 소개합니다. canvas2D 와 비교하여 코드량이 급증할 뿐만 아니라 습득해야 할 관련 지식도 훨씬 많아집니다

일.canvas 2D 로 사각형 그리기

canvas 2D 로 사각형을 그리는 것은 쉽습니다. 몇 줄의 코드로 쉽게 해결됩니다:

<!-- html -->
<h5>canvas2d</h5>
<canvas id="canvas2d" width="400" height="400">
    Please use a browser that supports "canvas"
</canvas>

// js
// 요소 참조 획득
var canvas2d = document.getElementById('canvas2d');
// 2D context 획득
var ctx = canvas2d.getContext('2d');

// 채우기 색 설정
ctx.fillStyle = 'rgba(255, 0, 255, 0.75)';
// 사각형 블록 그리기
ctx.fillRect(100, 100, 200, 200);

실제 효과는 200x200px 연보라색 사각형 블록으로, 주위에 너비 100px 의 투명 링이 있습니다. 코드의 관점에서 보면, 400x400px 의 투명한 canvas 중심에 불투명도 0.75 의 200x200px 보라색 사각형 블록이 칠해진 것입니다

ctx.fillRect(100, 100, 200, 200); 에서 canvas 2D 의 좌표계는 화면 좌표계와 같으며, 좌상단이 (0, 0), x 축은 오른쪽이 양수, y 축은 아래가 양수임을 알 수 있습니다

ctx.fillStyle = 'rgba(255, 0, 255, 0.75)'; 에서 rgba() 는 CSS 와 같으며, rgb 값은 0255, a 는 01 로 불투명도를 나타냄을 알 수 있습니다

이.WebGL 로 사각형 그리기

###1.context 획득

canvas2d.getContext('2d') 와 유사하게, WebGL 도 전용 context 를 획득해야 합니다:

var webgl = document.getElementById('webgl');
var gl = webgl.getContext('webgl');

여기서 WebGL 의 context 를 관례적으로 gl 로 명명하는 것은 향후 API 호출 시 OpenGL 호출 방식과 일치하게 하기 위함입니다. 네, 그냥 보기 좋게 하려는 것입니다

###2.canvas 지우기

canvas 2D 가 제공하는 clearRect 메서드와 유사하게, WebGL 에도 지우개 같은 것이 있으며, 더욱 강력하여 지운 후의 색상을 지정할 수 있습니다:

// 지울 canvas 색상 지정
// 파라미터는 rgba, 범위 0.0~1.0
gl.clearColor(0.0, 0.0, 0.0, 1.0);
// canvas 지우기
// gl.COLOR_BUFFER_BIT 색상 버퍼, 기본 지우기 색 rgba(0.0, 0.0, 0.0, 0.0) 투명한 검은색, gl.clearColor 로 지정
// gl.DEPTH_BUFFER_BIT 깊이 버퍼, 기본 깊이 1.0, gl.clearDepth 로 지정
// gl.STENCIL_BUFFER_BIT 스텐실 버퍼, 기본값 0, gl.clearStencil() 로 지정
gl.clear(gl.COLOR_BUFFER_BIT);

이 예에서는 canvas 를 지우는 것이 필수는 아니지만, 이 방법을 반드시 사용할 수 있어야 합니다. 그렇지 않으면 곤란해집니다. canvas 2D 에서 검은색 배경을 설정하려면 이렇게 하기 때문입니다:

// 검은색 배경
ctx.fillStyle = 'rgba(0, 0, 0, 1)';
ctx.fillRect(0, 0, canvas2d.width, canvas2d.height);

같은思路로, canvas 전체를 덮는 검은색 블록을 그리는 것은 WebGL 에서도 물론 가능하지만, 매우 번거롭습니다

###3.사각형 그리기

사각형을 그리기 위해 먼저 8 가지 일을 해야 합니다:

  1. 셰이더 소스 프로그램 작성

셰이더 언어 (GLSL ES) 를 사용하여 셰이더 소스 프로그램을 작성한 후 문자열 형태로 WebGL 시스템 내부에 전달하여 컴파일 실행합니다:

    // 0.셰이더 소스 프로그램
    // 정점 셰이더 소스 프로그램
    var vsSrc = 'void main() {' +
        'gl_Position = vec4(0.0, 0.0, 0.0, 1.0);' + // 좌표 설정
        'gl_PointSize = 200.0;' +                   // 크기 설정
    '}';
    // 프래그먼트 셰이더 소스 프로그램
    var fsSrc = 'void main() {' +
        'gl_FragColor = vec4(1.0, 0.0, 1.0, 0.75);' + // 색상 설정
    '}';

GLSL ES 의 문법은 C 언어와 비슷합니다. 구체적인 문법 규칙은 이후 노트에서 설명하겠습니다

  1. 셰이더 객체 생성

말할 것 없습니다. 게임 규칙입니다

    // 1.셰이더 객체 생성
    var vs = gl.createShader(gl.VERTEX_SHADER);
    var fs = gl.createShader(gl.FRAGMENT_SHADER);
    // 생성 결과 확인
    if (vs === null) {
        log('gl.createShader(gl.VERTEX_SHADER) failed');
    }
    if (fs === null) {
        log('gl.createShader(gl.FRAGMENT_SHADER) failed');
    }

3. 소스 프로그램 채우기

셰이더 객체가 있으면 셰이더 소스 프로그램을 넣을 수 있습니다

    // 2.소스 프로그램 채우기
    gl.shaderSource(vs, vsSrc);
    gl.shaderSource(fs, fsSrc);

4. 컴파일

소스 코드가 있으면 빨리 컴파일하여 에러가 나는지 확인합니다

    // 3.컴파일
    gl.compileShader(vs);
    gl.compileShader(fs);
    // 컴파일 에러 확인
    if (!gl.getShaderParameter(vs, gl.COMPILE_STATUS)) {
        log('gl.compileShader(vs) failed');
        log(gl.getShaderInfoLog(vs));   // 에러 정보 출력
    }
    if (!gl.getShaderParameter(fs, gl.COMPILE_STATUS)) {
        log('gl.compileShader(fs) failed');
        log(gl.getShaderInfoLog(fs));   // 에러 정보 출력
    }

5. 프로그램 객체 생성

셰이더 객체 외에도 프로그램 객체가 필요합니다. js 와 WebGL 내부 간 통신은 주로 이 프로그램 객체를 통해 이루어지므로, 먼저 하나 만듭니다

    // 4.프로그램 객체 생성
    var prog = gl.createProgram();
    // 생성 결과 확인
    if (prog === null) {
        log('gl.createProgram() failed');
    }

주의: 위 4 단계는 모두 정점 XX 와 프래그먼트 XX 가 하나씩이지만, 이 단계에서는 prog 객체는 1 개만 필요합니다

  1. 프로그램 객체에 셰이더 할당

각 prog 에는 2 개의 셰이더 (1 개의 정점 셰이더, 1 개의 프래그먼트 셰이더) 가 필요하므로, 할당 에러를 확인할 때는 2 와 비교하여 prog 에 2 개의 shader 가 바인딩되었는지 확인합니다

    // 5.프로그램 객체에 셰이더 할당
    gl.attachShader(prog, vs);
    gl.attachShader(prog, fs);
    // 할당 에러 확인
    if (gl.getProgramParameter(prog, gl.ATTACHED_SHADERS) !== 2) {
        log('gl.getProgramParameter(prog, gl.ATTACHED_SHADERS) failed');
    }

7. 프로그램 객체 링크

C 언어 프로그램의 compile -> link -> run 과 유사합니다

    // 6.프로그램 객체 링크
    gl.linkProgram(prog);
    // 링크 에러 확인
    if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) {
        log('gl.linkProgram(prog) failed');
        log(gl.getProgramInfoLog(prog));
    }

8. 프로그램 객체 사용

에러 확인은 하지 않습니다. 반환 값이 없기 때문입니다

    // 7.프로그램 객체 사용
    gl.useProgram(prog);

이로써 사각형 하나를 그리기 위한 준비 작업이 완료되었습니다

마지막으로 무언가를 그립니다

    // 사각형 그리기 (하나의 점이지만 점의 크기가 약간 큼)
    gl.drawArrays(gl.POINTS, 0, 1);

삼.DEMO

위 코드를 포함한 완전한 예제는 여기를 확인하십시오: http://www.ayqy.net/temp/webgl/hoho/index.html

사.WebGL 과 canvas 2D 의 차이

###1.좌표계가 다름

WebGL 에서 canvas 의 중심은 (0, 0), x 축은 오른쪽이 양수, y 축은 위가 양수, z 축은 화면에서射出하여 얼굴을 가리키는 방향이 양수 (오른손 좌표계) 입니다

###2.좌표값이 다름

WebGL 에서 전체 캔버스는 Rect(-1.0, 1.0, 2, 2) 입니다. 즉 canvas 좌상단 좌표는 (-1.0, 1.0), 우상단 좌표는 (1.0, 1.0), 캔버스 크기는 2x2 입니다. canvas 자체가 400x400px 라면, WebGL 에서 좌표값의 단위는 200px 입니다

주의: 좌표값이 아닌 속성, 예를 들어 위 셰이더 소스 프로그램 중의 gl_PointSize 의 경우, 그 단위는 여전히 px 입니다

###3.rgb 값이 다름

프래그먼트 셰이더에는 색상이 사용되었습니다. vec4(r, g, b, a) 로, CSS 의 rgba() 와 달리, WebGL 에서 rgb 값의 범위는 0.0~1.0 이며, 반드시 소수 형태여야 합니다. vec4 생성자는 float 형 파라미터만 받아들이며, int암묵적으로 float 로 변환되지 않습니다

###4.색상 중첩 규칙이 다름

DEMO 를 실행하면 canvas 2D 와 Web GL 로 그린 보라색 사각형의 색상이 다름을 알 수 있습니다. canvas 2D 에서는 먼저 검은색 배경을 그린 후 보라색 사각형을 그렸습니다. 보라색 사각형에는 0.25 의 투명도가 있으며, 최종적으로 표시되는 색상은 보라색과 검은색의 중첩입니다.

반면 WebGL 에서는 보라색 사각형의 색상이 검은색과 중첩되지 않았습니다 (body 배경색은 우리가 보라색 사각형에 지정한 색상으로, WebGL 에서 보라 - 검은 중첩이 발생하지 않았음을 분명히 볼 수 있습니다). 보라색 사각형 뒤쪽 (z 좌표가 -0.1) 에 검은색 사각형을 하나 더 그려보았지만, 색상은 여전히 중첩되지 않았습니다. 이는특별히 주의해야 합니다

오.정리

API 사용 단계로 보면 WebGL 은 이토록 번거롭습니다. 사각형 하나를 그리는 데 이토록 많은 코드를 작성해야 합니다. 실제로 우리는 하나의 점 (약간 큰 점) 만 그렸을 뿐이며, 사각형을 그리려면 더욱 번거롭고, 4 개의 정점을 사용하여 2 개의 삼각형 조각을 결정한 후 각각 색을 채워야 합니다

정점 셰이더, 프래그먼트 셰이더란 무엇인가? 어떤 역할이 있는가?조금 더 복잡한 예로 설명해야 합니다. 이후 노트에서 설명하겠습니다

참고 자료

  • 『WebGL 프로그래밍 가이드》

댓글

아직 댓글이 없습니다

댓글 작성