[[TableOfContents]] [http://www.khronos.org/registry/webgl/specs/latest/1.0/ Spec] == 기본 컨셉 == OpenGLì—ì„œ ì •ë§ ì‹¤ë¬´ì—ì„œ 쓰는 부분만 ë”°ë¡œ 떼어낸 OpenGL ES(Embeded System)ì˜ Javascript 구현체ì´ë©° HTML5 Canvas를 통해 나타난다. ë”°ë¼ì„œ 초보ìžê°€ 쉽게 배우는ë°ì— ì´ˆì ì´ ë§žì¶”ì–´ì ¸ 있지 ì•Šê³ ì˜¤ì§ ì „ë¬¸ê°€ê°€ êµ¬í˜„ì„ í•˜ëŠ”ë°ì— ì´ˆì ì´ ë§žì¶”ì–´ì ¸ 있다. == 특징 == Javascriptìž„ì—ë„ ë¶ˆêµ¬í•˜ê³ ë§ˆì¹˜ Cí”„ë¡œê·¸ëž˜ë° ìŠ¤íƒ€ì¼ì˜ í•¨ìˆ˜ë“¤ì´ ì¡´ìž¬í•œë‹¤. [WinAPI]ê°€ C스타ì¼ì˜ [OOP]ì´ë“¯ WebGL ë˜í•œ C스타ì¼ì˜ OOPì´ë‹¤. ëª¨ë“ í•¨ìˆ˜ëŠ” WebGLcontextë¼ëŠ” ê°ì²´ì— ìžˆëŠ”ë° ë³´ë©´ 그냥 ì ‘ë‘어를 붙ì´ëŠ” ëŠë‚Œì´ë‹¤. {{{ var gl = canvas.getContext("experimental-webgl"); gl.attachShader(shaderProgram, fragmentShader); gl.attachShader(shaderProgram, vertexShader); }}} ìœ„ì˜ ì½”ë“œë¥¼ ë³´ë©´ ì‰ì´ë” í”„ë¡œê·¸ëž¨ì— fragmentShader와 vertexShader를 Link 시키는 구문ì¸ë° ì£¼ì²´ì¸ shaderProgramì€ ì²«ë²ˆì¨° ì¸ìžì´ê³ glì€ ê·¸ëƒ¥ ì ‘ë‘ì–´ 처럼 ë³´ì¸ë‹¤. ì € 구문만 ê·¸ëŸ°ê²ƒì´ ì•„ë‹ˆë¼ ë‹¤ë¥¸ ëª¨ë“ í•¨ìˆ˜ë“¤ì´ ì € gl ê°ì²´ì— 붙어있다. 하지만 ì •ìž‘ glì´ ì£¼ì²´ê°€ ì•„ë‹Œ ê²ƒë“¤ì´ ë§Žë‹¤. ë”°ë¼ì„œ 래핑한 ê°ì²´ë¥¼ 만들어 쓰는 ê²ƒì´ ì†íŽ¸í•œë° 어설프게 했다가는 무척 꼬ì´ê²Œ ëœë‹¤. ì´ ê´€ìŠµì€ OpenGLì´ ê¸°ë³¸ì 으로 Cë¼ì´ë¸ŒëŸ¬ë¦¬ì´ë¼ 그런듯 하다. ì‹¤ì œ ëž˜í•‘ì„ ì§„í–‰í•´ë³¸ê²°ê³¼ 마치 MFC를 보는듯한 ëŠë‚Œì„ 강하게 ë°›ê³ ìžˆë‹¤. === OpenGLê³¼ ì°¨ì´ì === * WebGLì€ ê¸°ì¡´ OpenGLê³¼ 다르게 ì§ì ‘ 그리기가 지ì›ë˜ì§€ 않는다. ê¸°ì¡´ì˜ glBegin()와 glEnd()사ì´ì—ì„œ ê°’ì„ ê³„ì†ì 으로 ì „ë‹¬í•˜ìˆ˜ ì—†ê³ ì˜¤ì§ glDrawElement()를 통한 ë°°ì—´ì„ í•œêº¼ë²ˆì— ì „ë‹¬í•˜ëŠ” 것'만' 지ì›í•œë‹¤. 초보ìžë“¤ì˜ 첫난관ì´ë‹¤. * 사ê°í˜•ê·¸ë¦¬ê¸° ë° ë‹¤ê°í˜• 그리기가 지ì›ë˜ì§€ 않는다. ì‹¤ì œë¡œ 다ê°í˜• 그리기는 연습시ì—만 ìžì£¼ ì“°ê³ ì‹¤ì œ 코드ì—서는 삼ê°í˜•ìœ¼ë¡œ ì´ë£¨ì–´ì§„ 모ë¸ì„ ê°€ì ¸ë‹¤ 쓰기 ë•Œë¬¸ì¸ ê²ƒìœ¼ë¡œ ë³´ì¸ë‹¤. ê·¸ë¦¬ê³ ë‹¤ê°í˜•ì€ 삼ê°í˜•ì˜ 집합으로 í‘œí˜„í• ìˆ˜ 있다. * ìœ í‹¸ë¼ì´ë¸ŒëŸ¬ë¦¬ë¡œ ì œê³µë˜ëŠ” í브, 구, 실린ë”, í‹°í¬íŠ¸ê°€ ëª¨ë‘ ì§€ì›ë˜ì§€ 않는다. ì—ì‹œ ì˜ˆì œì—만 ì“°ì´ê³ 쓰지 않기 ë•Œë¬¸ì— ê³¼ê°ížˆ ì œê±°í•œê²ƒìœ¼ë¡œ ë³´ì¸ë‹¤. * ê´‘ì›, ì¹´ë©”ë¼ ì¡°ìž‘, íšŒì „ ë“±ì´ ì œê³µë˜ì§€ 않는다. ëª¨ë‘ ìžì‹ ì´ ì§ì ‘ ì—°ì‚°ì„ í†µí•´ í–‰ë ¬ì„ êµ¬í•´ì£¼ì–´ì•¼ 한다. 초보ìžë“¤ì˜ 둘쨰 난관ì´ë‹¤. * í…ìŠ¤ì³ ëª¨ë“œì™€ 조명 모드가 매우 ì œí•œë˜ì–´ 있다. * ì‰ì´ë”를 짜지않으면 쓸수가 없다. 심지어 í…스ì³ë¥¼ 입히는 ê²ƒë„ ì‰ì´ë”ì—ì„œ 처리한다. 그냥 단색으로 처리하는 ì½”ë“œë„ ì‰ì´ë” 코드를 짜지 않으면 그냥 í–ì–€ 것만 보게ëœë‹¤. ê·¸ë¦¬ê³ ê·¸ê²ƒë„ íšŒì „ì‹œí‚¬ìˆ˜ë„ ì—†ë‹¤. == 주요요소 == === 파ì´í”„ ë¼ì¸ === WebGLì€ ì¼ì •í•œ í름구조를 만들어 ë‘ê³ ê·¸ ê°ë¶€ë¶„ì„ ë§Œë“¤ìˆ˜ 있ë„ë¡ í•´ ë‘었다. 아마 최ì 화가 쉬운 íƒ“ì— ê·¸ëŸ¬í–ˆìœ¼ë¦¬ê³ ìƒê°ëœë‹¤. [[attachment:WebGL.png WebGl파ì´í”„ë¼ì¸]] Attribute는 ê° í¬ì¸íŠ¸ 별로 ì „ë‹¬ë˜ëŠ” ì •ë³´ì´ê³ uniform ì€ ì „ì²´ì—ì„œ 공통ì ì¸ ì •ë³´ì´ë‹¤. ì¼ë°˜ì 으로 Attribute는 ê° ì •ì ì˜ ìœ„ì¹˜ ì •ë³´ì™€ ê° ì§€ì ì˜ ë²•ì„ ë²¡í„° ì •ë³´ë¥¼ì„ ì „ë‹¬í•œë‹¤. uniformì€ ì¼ë°˜ì 으로 ì¹´ë©”ë¼ì˜ 위치나 í™˜ê²½ê´‘ì˜ ìœ„ì¹˜ì²˜ëŸ¼ ì „ì²´ì ì¸ ê²ƒì„ ì „ë‹¬í•œë‹¤. Attribute나 uniformì€ ì¼ì¢…ì˜ ë³€ìˆ˜ì¸ë° í•¸ë“¤ì„ ì–»ì–´ì™€ì„œ ê·¸ê²ƒì„ í†µí•´ ê°’ì„ ì „ë‹¬í• ìˆ˜ 있다. 즉 Atrribute나 Uniformì€ Javascript측ì—ì„œ ì‰ì´ë”ë¡œ ì •ë³´ë¥¼ 보내는 것ì´ë‹¤. varyingì€ ì‰ì´ë” ê°„ì˜ ì •ë³´ ì „ë‹¬ì— ì‚¬ìš©ëœë‹¤. vertex shaderì—ì„œ fragment shaderë¡œ ê°’ì´ ì „ë‹¬ë˜ë©° 반대는 불가능하다(파ì´í”„ë¼ì¸ êµ¬ì¡°ìƒ ë‹¹ì—°í•œ 것ì´ë‹¤). ì´ë•Œ vertex shader는 ê° ì •ì (ê¼ì§€ì ) fragment shader는 ê° í”½ì…€ì— í•œë²ˆ 호출ë˜ê²Œ ë˜ëŠ”ë° ê° ì •ì 사ì´ì˜ ê°’ë“¤ì€ ë³´ê°„ë²•ì„ ê±°ì³ ì „ë‹¬ë˜ê²Œ ëœë‹¤(ê·¸ë¼ë””언트 ê°™ì€ ëŠë‚Œì´ë‹¤ ì¤‘ê°„ê°’ì„ ì•Œì•„ì„œ 만들어 준다). ê° ì‰ì´ë”는 ë™ì‹œì— ë™ìž‘í• ìˆ˜ ìžˆëŠ”ë° ë‹¹ì—°ížˆ ì´ë“¤ì€ ì„œë¡œê°„ì— ë…립ì ì´ì–´ì•¼ 한다. === ì‰ì´ë” === ì‰ì´ë”는 ì‰ì´ë” 언어로 ë”°ë¡œ ì§œì£¼ê³ ì»´íŒŒì¼ í•´ì•¼í•˜ë©° 심지어 ë§í¬ê¹Œì§€ 시켜주어야 한다. GPUì˜ ê°•ë ¥í•œ í–‰ë ¬ì—°ì‚° ëŠ¥ë ¥ì„ ê°€ì ¸ë‹¤ 쓰기 위해서ì¸ê²ƒìœ¼ë¡œ ë³´ì´ëŠ”ë° ì´ê²ƒì„ 사용하지 ì•Šê³ ì„œëŠ” ì˜ˆì œíŒŒì¼ë„ ëŒë ¤ë³¼ìˆ˜ê°€ 없다. ë‹¤í–‰ì´ ì–¸ì–´ëŠ” C언어와 매우 ìœ ì‚¬í•˜ê³ í–‰ë ¬ì—°ì‚°ì´ ëª¨ë‘ ìžˆê¸° ë•Œë¬¸ì— ë”±ížˆ ì–´ë µê±°ë‚˜ 하진 않다. 다만 ì–´ëŠë¶€ë¶„ì—ì„œ 어디와 ì—°ê²°ë˜ëŠ”지 ì´í•´í•˜ëŠ”ë° ì‹œê°„ì´ ê±¸ë¦°ë‹¤. ==== vertex shader ==== ê° ì •ì (vertex, ê¼ì§€ì )마다 호출ë˜ë©° 주로 ê¼ì§€ì ì˜ ìœ„ì¹˜ë¥¼ ì—°ì‚°í•˜ê³ ì‹¤ì œ Viewì— íˆ¬ì˜í•˜ëŠ” ì—°ì‚°ì„ ì£¼ë¡œ 하게 ëœë‹¤. 한마디로 모ë¸ì˜ 위치 변환과 ì¹´ë©”ë¼ ì‹œì ì— ë”°ë¥¸ 변환 ì›ê·¼ë²•ì„ ì 용하는 ë³€í™˜ë“±ì„ ìˆ˜í–‰í•œë‹¤. ==== fragment shader ==== ê° ì •ì 사ì´ì— 있는 픽셀 마다 호출ëœë‹¤. 주로 ê´‘ì›íš¨ê³¼ë¥¼ ì ìš©í•œ í”½ì…€ì˜ ìµœì¢…ì ì¸ ìƒ‰ê¹”ì´ë‚˜ í…ìŠ¤ì³ ì—°ì‚°ì— ì‚¬ìš©ëœë‹¤. varying변수를 vertex shaderì—ì„œ fragment shaderë¡œ 넘겨주면 ê° ì •ì 사ì´ì—는 보간법으로 ë³€í™˜ëœ ê°’ì´ ë„˜ì–´ 온다. == ì˜ˆì œì½”ë“œ == 아래 코드는 ì •í™•í•˜ì§€ 않으며 연습 ë„ì¤‘ì˜ ì½”ë“œìž…ë‹ˆë‹¤. ë˜í•œ WebGLì˜ íŠ¹ì„±ìƒ ì½”ë“œê°€ 분산ë˜ì–´ 있습니다. 현재 ê°ì²´ ëž˜í•‘ì„ ì§„í–‰ì¤‘ìž…ë‹ˆë‹¤. 현재 ê°ì²´ 래핑중 중대한 ë¬¸ì œì— ë´‰ì°©. ëŒ€ë¶€ë¶„ì˜ ëª¨ë“ˆê³¼ 세ì´ë” 코드는 콜백으로 호출ë˜ëŠ”ë° ì´ê²ƒì„ ì ì ˆížˆ ëž˜í•‘í• ë°©ë²•ì´ ì—†ë‹¤. webGL과는 하등 ì—°ê´€ì´ ì—†ëŠ” 부분ì´ë¼ì„œ ê°ìž 알아서 구현하ë„ë¡ í•´ë„ ë˜ì§€ë§Œ ëŒ€ë¶€ë¶„ì˜ ê²½ìš° ê°™ì€ ì½”ë“œë¥¼ 다스 ì§œê³ ìžˆëŠ” 나를 보게 ëœë‹¤. ì´ê²ƒì„ 어떻게 해야 잘한 래핑ì´ë¼ í• ìˆ˜ 있ì„까? ==== vertexShader ==== {{{ attribute vec3 aVertexPosition; attribute vec3 aVertexNormal; uniform mat4 matCamara; uniform mat4 matProject; uniform vec3 lightPos; uniform vec3 lightDirection; uniform vec4 materialDiffuse; uniform vec4 lightDiffuse; varying vec4 vFinalColor; varying vec3 vNormal; void main(void){ vec3 N = normalize((vec4(aVertexNormal, 1.0) * matCamara).xyz);//nomal compute vec3 L = normalize(lightDirection); //lightDrection float lambertTerm = max(dot(N, -L), 0.0); vec4 Id = lightDiffuse * materialDiffuse * lambertTerm; vNormal = normalize((vec4(aVertexNormal, 1.0) * matCamara).xyz); vFinalColor = Id; vFinalColor.a = 1.0; gl_Position = matProject * matCamara * vec4((aVertexPosition), 1.0); gl_Position.w = 1.0; } }}} ==== fragmentShader ==== {{{ #ifdef GL_ES precision highp float; #endif uniform vec3 lightPos; uniform vec3 lightDirection; uniform vec4 materialDiffuse; uniform vec4 lightDiffuse; varying vec4 vFinalColor; varying vec3 vNormal; void main(void) { vec3 L = normalize(lightDirection); gl_FragColor = vFinalColor + vec4(0.01,0.01,0.01, 1.0); } }}} ==== javascript ==== {{{ var cube = { "vertices": [ 0.2, 0.2, 0.2, //0 0.2, 0.2,-0.2, //1 0.2,-0.2, 0.2, //2 0.2,-0.2,-0.2, //3 -0.2, 0.2,-0.2, //4 -0.2, 0.2, 0.2, //2 -0.2,-0.2,-0.2, //6 -0.2,-0.2, 0.2 //7 ], "normals": [ 1, 1, 1, //0 1, 1,-1, //1 1,-1, 1, //2 1,-1,-1, //3 -1, 1,-1, //4 -1, 1, 1, //5 -1,-1,-1, //6 -1,-1, 1 //7 ], "indices" : [ 0,2,3, 0,3,1, 4,6,7, 4,7,5, 4,5,0, 4,0,1, 7,6,3, 7,3,2, 5,7,2, 5,2,0, 1,3,6, 1,6,4 ] } var init = function(){ var gl = getGLContext(); var cubeBuffer = new GLBuffer(gl, cube); var shader; async.parallel([ function(callback){ var url = document.getElementById("vertexShader").getAttribute("src"); ajax(url, callback); }, function(callback){ var url = document.getElementById("fragmentShader").getAttribute("src"); ajax(url, callback); }, ], function (err, data){ shader = new GLShader(gl, data[0], data[1]); gl.useProgram(shader.program); shader.aVertexPosition = gl.getAttribLocation(shader.program, "aVertexPosition"); shader.aVertexNormal = gl.getAttribLocation(shader.program, "aVertexNormal"); var cam = gl.getUniformLocation(shader.program, "matCamara"); var camMat = mat4.identity(mat4.create()); mat4.translate(camMat, camMat, [0, 0, 0.1]); mat4.rotate(camMat, camMat, Math.PI/4, [1,0.5,0.5]); gl.uniformMatrix4fv(cam, false, camMat); var lightPos = shader.getUniformLocation("lightPos"); gl.uniform3fv(lightPos, [0.1,0.1,0.1]); var lightDirection = shader.getUniformLocation("lightDirection"); gl.uniform3fv(lightDirection, [-1, -1, -1]); var materialDiffuse = shader.getUniformLocation("materialDiffuse"); gl.uniform4fv(materialDiffuse, [0.8, 0.2, 0.2, 1.0]); var lightDiffuse = shader.getUniformLocation("lightDiffuse"); gl.uniform4fv(lightDiffuse, [1,1,1,1]); var matProject = mat4.identity(mat4.create());//2PI = 360d -> 1d = PI/180 mat4.perspective(matProject, Math.PI/180 * 80, 1, 0, 1); gl.uniformMatrix4fv( shader.getUniformLocation("matProject"), false, matProject ); onReady(gl, cubeBuffer, shader); }); } setTimeout(init, 0); function onReady(gl, buffer, shader){ onDraw(); function onDraw(){ gl.clearColor(0.0, 0.0, 0.0, 1.0); gl.enable(gl.DEPTH_TEST); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); gl.viewport(0,0,300,300); gl.enableVertexAttribArray(shader.aVertexPosition); gl.enableVertexAttribArray(shader.aVertexNormal); gl.bindBuffer(gl.ARRAY_BUFFER, buffer.vertex); gl.vertexAttribPointer(shader.aVertexPosition, 3, gl.FLOAT, false, 0, 0); gl.bindBuffer(gl.ARRAY_BUFFER, buffer.normal); gl.vertexAttribPointer(shader.aVertexNormal, 3, gl.FLOAT, false, 0, 0); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer.index); gl.drawElements(gl.TRIANGLES, buffer.index.length, gl.UNSIGNED_SHORT, 0); } } function ajax(url, callback){ var ajax = new XMLHttpRequest(); ajax.onreadystatechange = function(){ if(ajax.readyState === 4){ //complete requset if(ajax.status === 200){ //not error callback(null, ajax.responseText); } } } ajax.open("GET", url, true);//if need Sync method set false; ajax.send(null); } //Lib function function getGLContext(){ var canvas = document.getElementsByTagName("canvas"); canvas = [].filter.call(canvas, function(element){ if(element.getAttribute("WebGL") != null) return true; else return false; }); canvas = canvas[0]; return canvas.getContext("experimental-webgl"); } //Lib Class function GLBuffer(gl, model){ this.model = model; try { //only binded buffer can send data //vertex is coord of points var vertexBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(model.vertices), gl.STATIC_DRAW); gl.bindBuffer(gl.ARRAY_BUFFER, null); //index is triangle point index of suface var indexBuffer = gl.createBuffer(); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(model.indices), gl.STATIC_DRAW); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); //normals Buffer var normalBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(model.normals), gl.STATIC_DRAW); gl.bindBuffer(gl.ARRAY_BUFFER, null); this.vertex = vertexBuffer; this.index = indexBuffer; this.index.length = model.indices.length; this.normal = normalBuffer; } catch(e){ throw Error("Can not create Buffer"); } } function GLShader(gl, vertexSource, fragmentSource){ var shaderProgram = gl.createProgram(); //compile Source var vertexShader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vertexShader, vertexSource); gl.compileShader(vertexShader); checkCompile(vertexShader); var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(fragmentShader, fragmentSource); gl.compileShader(fragmentShader); checkCompile(fragmentShader); //attach shader gl.attachShader(shaderProgram, fragmentShader); gl.attachShader(shaderProgram, vertexShader); //link gl.linkProgram(shaderProgram); this.program = shaderProgram; this._private = { gl : gl } function checkCompile(shader){ if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { throw Error(gl.getShaderInfoLog(shader)); } } } GLShader.prototype.getUniformLocation = function(name){ return this._private.gl.getUniformLocation(this.program, name); } GLShader.prototype.uniform4fv = function(name, arr){ this._private.gl.uniform4fv(this.getUniformLocation(name), arr); } GLShader.prototype.uniform3fv = function(name, arr){ this._private.gl.uniform3fv(this.getUniformLocation(name), arr); } GLShader.prototype.uniformMatrix4fv = function(name, arr){ this._private.gl.uniformMatrix4fv(this.getUniformLocation(name), false, arr); } }}} == WebGLì˜ ì¢Œí‘œê³„ == OpenGLê³¼ ë™ì¼í•œ -1.0 ~ 1.0ì´ë©° ì´ë¥¼ 넘어갈시ì—는 표현ë˜ì§€ 않는다. 매트ë¦ìŠ¤ ì—°ì‚°ì„ ì§ì ‘해야만 하는 WebGLì—서는 ì´ì ì´ ê°„ê³¼ë˜ê¸° 쉬워서 ì´ë¯¸ 그린 페ì´ì§€ê°€ ì–´ë””ì— ìžˆëŠ”ì§€ 찾는 현ìƒì´ ë°œìƒí•˜ê²Œ ëœë‹¤. ê³„ì† ìž‘ì„±ì¤‘.