WebGL-005: Shader를 위한 Script 구문
Post

WebGL-005: Shader를 위한 Script 구문

이전 글에서 Vertex/Fragment Shader를 이용한 렌더링 방법에 대해 알아 보았다. 해당 게시글에서 사용된 GLSL은 매우 짧아서,

1
2
var vertexShaderDesc = "attribute vec3 aPos; void main(){ gl_Position=vec4(aPos, 1.0); }";
var fragmentShaderDes c = "void main(){ gl_FragColor= vec4(1.0, 0.0, 0.0, 1.0); }"; 

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

  1. Vertex Shader Script
    1
    2
    3
    4
    5
    6
    7
    
    <script id="VertexShader" type="x-shader/x-vertex">
     attribute vec3 aPos;
    
     void main(){
         gl_Position=vec4(aPos, 1.0);
     }
    </script>
    

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

  1. Fragment Shader Script
    1
    2
    3
    4
    5
    
    <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를 작성한다. 스크립트의 idFragmentShader임을 기억해 두자. 상기한 두 스크립트코드는 *.html 파일의 어디에 위치해도 상관 없다. 각 Script Code의 맨 윗 라인과 맨 아랫 라인의 html을 제외하면 이전에 사용하였던 Shader Code와 동일하다. 이제 JavaScript를 이용하여 읽어들이는 함수를 추가해 주면 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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");와 같이 사용하면 된다. 이제

1
2
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); }"; 

와 같이 사용했던 부분을,

1
2
var vertexShaderDesc = getShader("VertexShader");
var fragmentShaderDesc = getShader("FragmentShader");

와 같은 형태로 사용할 수 있다. 지금 소개한 방법은 코딩을 위한 편의를 제공하기 위함이며 필수 구현사항이 아니다. 실행하면 결과는 이전 글과 같은 결과를 얻을 수 있어야 한다. 전체 코드는 아래와 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
<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>