본문 바로가기
Computer/WebGL

004. WebGL - Prac03 - DrawPoints, DrawLines, DrawTriangles.

by DogBull 2010. 4. 5.
   이번에는 WebGL에서 기본적인 primitives(Point, Line, Triangle, Rectangle, Polygon etc)를 그리는 방법에 대해 알아보도록 하겠다. About WebGL에서 설명한 바와 같이 WebGL은 OpenGL ES 2.0을 기반으로 한다. 이는 이전 버전의 OpenGL API를 다루어본 사람에게는 중요한 사항인데, 그 이유는 OpenGL ES 2.0이 Programmable Pipeline를 지원한다는 점이다. 반면에 OpenGL ES 1.1은 Fixed Function Pipeline 만을 지원하며, 1.0 버전과 완벽한 호환이 가능하다. 하지만, 2.0버전 부터는 1.x 버전과의 완벽한 호환을 지원하지 않는다.

중요한 점은, OpenGL ES 2.0 부터는 더 이상 Fixed Function Pipeline을 지원하지 않는다는 것이다.

따라서, 이전 버전의 OpenGL API를 접해본 이들로서는 익숙한 Fixed Function Pipeline의 사용이 불가능 함에 따른 약간의 혼동이 발생할 것이며, OpenGL API를 접해본 이들로서는 보다 복잡해진 Rendering을 위한 초기화 작업에 어려움을 느낄 것이다.
   OpenGL ES 2.0 부터는 Vertex Shader와 Fragment Shader를 작성 및 컴파일 하여, 해당 응용프로그램에 연결 시켜 주는 작업을 해 주어야 한다.( 더 이상 glBegin(...), glEnd(), glVertex2f(...), glVertex3f(...), glTranslatef(...) 등의 함수를 사용할 수 없음에 유의한다. OpenGL과 대응되는 DirectX의 Direct3D역시, Directx10 버전 부터 더이상 Fixed Function Pipeline을 지원하지 않는다.) Vertex Shader, Fragment Shader는 GLSL(OpenGL Shading Language)을 이용하여 프로그래밍을 해야하므로 이에 대한 약간의 지식이 필요하다. 여기서는 GLSL에 대한 많은 지식없어 몇개의 명령어들만 이용할 것이므로 GLSL에 대한 내용은 비중있게 다루지 않겠다.(사실 나도 모른다)
   Vertex Shader는 말 그대로 렌더링될 물체의 Vertex에 대한 처리를 담당한다. 이 Vertex에는 물체(이하 mesh/메쉬)를 구성하고 있는 정점(Vertex, 삼각형의 각 꼭지점, 라인의 시작점과 끝점 등)의 위치정보 뿐만 아니라, 법선정보, 색상정보, 텍스처좌표 정보등을 포함하고 있다. Vertex Shader에 작성된 GLSL구문 함수는 버텍스의 개수 만큼 호출될 것이며, 이를 지오메트리 단계라고 부른다.
   Fragment Shader(DirectX Direct3D의 Pixel Shader와 동일)에서는 Frame Buffer에 대한 Writing 작업을 수행한다. 만약 삼각형을 렌더링 하는 과정이라면, Vertex Shader에서는 3개의 각 정점에 대한 처리가 수행되고 난 후, Fragment Shader에서 그 삼각형이 화면에 보여지게 될 때의 각 Pixel에 대한 처리를 수행한다. 이를 레스터라이저 단계라고 부른다.
   참고로, Vertex가 아주 많은 mesh라 하더라도 그 메쉬가 Frame Buffer에 렌더링 될 때 차지하게될 Pixel의 개수가 0개 라면(아주 작은, 혹은 원근투영에 의해 시점에서 아주 멀리 위치하여 그 크기가 0이 된다면) Fragment Shader는 렌더링될, 그 mesh에 대해서 아무런 작업도 하지 않을 것이다.


아래는 가장 간단한 Vertex Shader의 코드를 보여주고 있다.
attribute vec3 aPos;
void main(){
     gl_Position= vec4(aPos, 1.0);
}

CPU로 부터 전송 받은 3차원 Vertex(변수명 aPos)에 대해, 4차원의 Homogeneous Coordinate로 변환하여 Fragment Shader에 넘겨주는 아주 간단한 코드이다. (gl_Position 키워드는 Vertex Shader로 넘겨지게 될 Vertex의 위치값을 나타낸다.)


아래는 간단한 Fragment Shader의 코드를 보여주고 있다.
void main(){
    gl_FragColor=    vec4(1.0, 0.0, 0.0, 1.0);
}

Vertex Shader로 부터 전송 받은 Primitive 단위로 부터, Frame Buffer 쓰기 작업을 수행하는데 단순히 붉은색으로 색상을 채운다.(gl_FragColor 키워드는 Frame Buffer에 쓰여지게될 색상값이다.) 이 Shader 코드를 바탕으로 WebGL을 이용하여 삼각형을 렌더링해 보도록 하겠다. 프로그램의 대략적인 흐름은 아래와 같다.




Vertex/Fragment Shader를 사용하기 위한 40 Lines 정도의 꽤 긴 함수 이다. Fixed Function Pipeline에서는 수행할 필요가 없는 이러한 긴 명령어들이 수고스럽기는 하지만, Programmable Function Pipeline을 통해 얻을 수 있는 많은 장점을 간과하지 말아야 할 것이다. 비록 코드의 길이가 길지만, 흐름의 구조는 비교적 간단하다.




전체의 코드는 아래와 같다.


<html>
<head>
    <title>WebGL - Prac02 - DrawTriangle</title>
    <script type="text/javascript">
        var gl;
        var vertexPositionAttribute;

        function start(){
            if( !initWebGL() ){
                return;
            }

            if( !initShader() ){
                return;
            }

            var verAry=    [
                0.0, 0.0, 0.0,
                0.5, 0.0, 0.0,
                0.0, 1.0, 0.0
            ];

            var triangleVertAry=    gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertAry);
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(verAry), gl.STATIC_DRAW);

            gl.clearColor(0.4, 0.5, 0.6, 1.0);
            gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT);

            gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertAry);
            gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0);
            gl.drawArrays(gl.TRIANGLES, 0, 3);

            alert("finish!");
        }

        function initWebGL(){
            var canvas=    document.getElementById("WebGL-Canvas");
            if( !canvas ){
                alert("Can't find WebGL-Canvas.");
                return false;
            }

            try{
                gl=    canvas.getContext("experimental-webgl");
            }catch(e){
            }

            if( !gl ){
                alert("Unable to initialize WebGL. Your browser may not support it.");
                return false;
            }

            return true;
        }

        /* Vertex/Fragment Shader를 생성한다.*/
        function initShader(){
            //#1. Create Vertex Shader
            var vertexShaderDesc=
                "attribute vec3 aPos; void main(){ gl_Position=vec4(aPos, 1.0); }";
            var vertexShader=        gl.createShader(gl.VERTEX_SHADER);
            gl.shaderSource(vertexShader, vertexShaderDesc);
            gl.compileShader(vertexShader);
            if( !gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS) ){
                alert("Error(VertexShader): " + gl.getShaderInfoLog(vertexShader));
                return false;
            }

            //#2. Create Fragment Shader
            var fragmentShaderDesc=
                "void main(){ gl_FragColor= vec4(1.0, 0.0, 0.0, 1.0); }";
            var fragmentShader=        gl.createShader(gl.FRAGMENT_SHADER);
            gl.shaderSource(fragmentShader, fragmentShaderDesc);
            gl.compileShader(fragmentShader);
            if( !gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS) ){
                alert("Error(PixelShader): " + gl.getShaderInfoLog(fragmentShader));
                return false;
            }
            
            //#3. Create Shader Program and attach Shaders(Pixel/Fragment Shader)
            var shaderProgram=    gl.createProgram();
            gl.attachShader(shaderProgram, vertexShader);
            gl.attachShader(shaderProgram, fragmentShader);

            //#4. Link Shader Program
            gl.linkProgram(shaderProgram);
            if( !gl.getProgramParameter(shaderProgram, gl.LINK_STATUS) ){
                alert("Unagle to initialize the shader program.");
                return false;
            }

            //#5. User Shader Program
            gl.useProgram(shaderProgram);

            //#6. Shader에 있는 "aPos"라는 이름의 변수명에 대한 핸들을 얻음
            vertexPositionAttribute=    gl.getAttribLocation(shaderProgram, "aPos");
            if( vertexPositionAttribute==-1 ){
                alert("Unable to find Vertex Attribute Location.");
                return false;
            }
            gl.enableVertexAttribArray(vertexPositionAttribute);
            
            return true;
        }
    </script>
</head>

<body onload="start()">
    <canvas id="WebGL-Canvas" style="border: 1px solid green" width="400" height="300">
        Your browser doesn't appear to support the HTML5 <code>&lt;canvas&gt;</code> element.
    </canvas>
</body>

</html>
위 코드의 결과는 아래와 같다.




WebGL_Prac02_DrawTriangle.html