본문 바로가기
Computer/WebGL

005. WebGL - Prac04 - Shader를 위한 Script 구문

by DogBull 2010. 4. 6.
이전 글에서 Vertex/Fragment Sahder를 이용하여 렌더링하는 방법에 대해 알아 보았다. 이 글에서 사용된 GLSL은 매우 짧아서,
var vertexShaderDesc=    "attribute vec3 aPos; void main(){ gl_Position=vec4(aPos, 1.0); }";
var fragmentShaderDesc=  "void main(){ gl_FragColor= vec4(1.0, 0.0, 0.0, 1.0); }"; 

와 같은 방식으로 사용해도 코딩에 큰 어려움이 없었다. 하지만, 복잡한 렌더링 기능을 구현함에 따라 Shader 코드가 길어진다면 위와 같은 방식은 가독성에 악영향을 미칠 것이다. 또한 Shader 코드를 다른 파일로 구현해 두고자 한다면 이번에 소개할 방식을 따르는 것이 더 나을 것이다.

1. Vertex Shader Script
<script id="VertexShader" type="x-shader/x-vertex">
    attribute vec3 aPos;

    void main(){
        gl_Position=vec4(aPos, 1.0);
    }
</script>

위와 같이, Vertex Shader에 대한 Script Code를 작성한다. 스크립트의 id가 "VertexShader"임을 기억해 두자.

2. Fragment Shader Script
<script id="FragmentShader" type="x-shader/x-vertex">
    void main(){
        gl_FragColor= vec4(1.0, 0.0, 0.0, 1.0);
    }
</script>
위와 같이, Fragment Shader에 대한 Script Code를 작성한다. 스크립트의 id가 "FragmentShader"임을 기억해 두자. 상기한 두 스크립트코드는 *.html 파일의 어디에 위치해도 상관 없다. 각 Script Code의 맨 윗라인과 맨아랫라인의 html을 제외하면 이전에 사용하였던 Shader Code와 동일하다. 이제 JavaScript를 이용하여 읽어들이는 함수를 추가해 주면 된다.
function getShader(id){
    var shaderScript=    document.getElementById(id);
    if( !shaderScript ){
        alert("Unable to get shader script. id='" + id + "'");
        return null;
    }

    var shaderDesc=    "";
    var currentChild=    shaderScript.firstChild;
    while( currentChild ){
        if( currentChild.nodeType==3 ){
            shaderDesc+=    currentChild.textContent;
        }
        currentChild=    currentChild.nextSibling;
    }

    return shaderDesc;
}
위의 getShader함수는 script tag의 내용일 읽어들이는 javascript함수이다. 이 함수의 매개변수로 넘어가는 id는 앞서 작성하였던 Vertex/Fragment Shader 스크립트의 id가 사용된다. 즉 getShader("VertexShader"); 또는 getShader("FragmentShader")와 같이 사용하면 된다. 이제
var vertexShaderDesc=    "attribute vec3 aPos; void main(){ gl_Position=vec4(aPos, 1.0); }";
var fragmentShaderDesc=  "void main(){ gl_FragColor= vec4(1.0, 0.0, 0.0, 1.0); }"; 
와 같이 사용했던 부분을,
var vertexShaderDesc=    getShader("VertexShader");
var fragmentShaderDesc=    getShader("FragmentShader");
와 같은 형태로 사용할 수 있다. 지금 소개한 방법은 코딩을 위한 편의를 제공하기 위함이며 필수 구현사항이 아니다. 실행하면 결과는 이전 글과 같은 결과를 얻을 수 있어야 한다. 전체 코드는 아래와 같다.
<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;
        }

        function initShader(){
            var vertexShaderDesc=    getShader("VertexShader");
            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;
            }

            var fragmentShaderDesc=    getShader("FragmentShader");
            var fragmentShader=        gl.createShader(gl.FRAGMENT_SHADER);
            gl.shaderSource(fragmentShader, fragmentShaderDesc);
            gl.compileShader(fragmentShader);
            if( !gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS) ){
                alert("Error(PixelShade)r: " + gl.getShaderInfoLog(fragmentShader));
                return false;
            }

            var shaderProgram=    gl.createProgram();
            gl.attachShader(shaderProgram, vertexShader);
            gl.attachShader(shaderProgram, fragmentShader);
            gl.linkProgram(shaderProgram);

            if( !gl.getProgramParameter(shaderProgram, gl.LINK_STATUS) ){
                alert("Unagle to initialize the shader program.");
                return false;
            }

            gl.useProgram(shaderProgram);

            vertexPositionAttribute=    gl.getAttribLocation(shaderProgram, "aPos");
            if( vertexPositionAttribute==-1 ){
                alert("Unable to find Vertex Attribute Location.");
                return false;
            }
            gl.enableVertexAttribArray(vertexPositionAttribute);
            
            return true;
        }

        /* Shader Script를 읽어들이는 함수.
          * 매개변수 "id"는 122Line과 130Line의 <script id= 에 들어간 값과 같아야 함 */
        function getShader(id){
            var shaderScript=    document.getElementById(id);
            if( !shaderScript ){
                alert("Unable to get shader script. id='" + id + "'");
                return null;
            }

            var shaderDesc=    "";
            var currentChild=    shaderScript.firstChild;
            while( currentChild ){
                if( currentChild.nodeType==3 ){
                    shaderDesc+=    currentChild.textContent;
                }
                currentChild=    currentChild.nextSibling;
            }

            return shaderDesc;
        }

    </script>

    <!--Vertex Shader-->
    <script id="VertexShader" type="x-shader/x-vertex">
        attribute vec3 aPos;

        void main(){
            gl_Position=vec4(aPos, 1.0);
        }
    </script>

    <!--Fragment Shader-->
    <script id="FragmentShader" type="x-shader/x-vertex">
        void main(){
            gl_FragColor= vec4(1.0, 0.0, 0.0, 1.0);
        }
    </script>
</head>
<body>
    <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>