1. κΈ°λ³Έ μ»¨μ ¶
OpenGLμμ μ λ§ μ€λ¬΄μμ μ°λ λΆλΆλ§ λ°λ‘ λΌμ΄λΈ OpenGL ES(Embeded System)μ Javascript ꡬν체μ΄λ©° HTML5 Canvasλ₯Ό ν΅ν΄ λνλλ€. λ°λΌμ μ΄λ³΄μκ° μ½κ² λ°°μ°λλ°μ μ΄μ μ΄ λ§μΆμ΄μ Έ μμ§ μκ³ μ€μ§ μ λ¬Έκ°κ° ꡬνμ νλλ°μ μ΄μ μ΄ λ§μΆμ΄μ Έ μλ€.
2. νΉμ§ ¶
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λ₯Ό 보λλ―ν λλμ κ°νκ² λ°κ³ μλ€.
2.1. OpenGLκ³Ό μ°¨μ΄μ ¶
- WebGLμ κΈ°μ‘΄ OpenGLκ³Ό λ€λ₯΄κ² μ§μ κ·Έλ¦¬κΈ°κ° μ§μλμ§ μλλ€. κΈ°μ‘΄μ glBegin()μ glEnd()μ¬μ΄μμ κ°μ κ³μμ μΌλ‘ μ λ¬νμ μκ³ μ€μ§ glDrawElement()λ₯Ό ν΅ν λ°°μ΄μ νκΊΌλ²μ μ λ¬νλ κ²'λ§' μ§μνλ€. μ΄λ³΄μλ€μ 첫λκ΄μ΄λ€.
- μ¬κ°ν그리기 λ° λ€κ°ν κ·Έλ¦¬κΈ°κ° μ§μλμ§ μλλ€. μ€μ λ‘ λ€κ°ν 그리기λ μ°μ΅μμλ§ μμ£Ό μ°κ³ μ€μ μ½λμμλ μΌκ°νμΌλ‘ μ΄λ£¨μ΄μ§ λͺ¨λΈμ κ°μ Έλ€ μ°κΈ° λλ¬ΈμΈ κ²μΌλ‘ 보μΈλ€. κ·Έλ¦¬κ³ λ€κ°νμ μΌκ°νμ μ§ν©μΌλ‘ ννν μ μλ€.
- μ νΈλΌμ΄λΈλ¬λ¦¬λ‘ μ 곡λλ νλΈ, ꡬ, μ€λ¦°λ, ν°ν¬νΈκ° λͺ¨λ μ§μλμ§ μλλ€. μμ μμ μλ§ μ°μ΄κ³ μ°μ§ μκΈ° λλ¬Έμ κ³Όκ°ν μ κ±°νκ²μΌλ‘ 보μΈλ€.
- κ΄μ, μΉ΄λ©λΌ μ‘°μ, νμ λ±μ΄ μ 곡λμ§ μλλ€. λͺ¨λ μμ μ΄ μ§μ μ°μ°μ ν΅ν΄ νλ ¬μ ꡬν΄μ£Όμ΄μΌ νλ€. μ΄λ³΄μλ€μ λμ¨° λκ΄μ΄λ€.
- ν
μ€μ³ λͺ¨λμ μ‘°λͺ
λͺ¨λκ° λ§€μ° μ νλμ΄ μλ€.
- μμ΄λλ₯Ό μ§μ§μμΌλ©΄ μΈμκ° μλ€. μ¬μ§μ΄ ν
μ€μ³λ₯Ό μ
νλ κ²λ μμ΄λμμ μ²λ¦¬νλ€. κ·Έλ₯ λ¨μμΌλ‘ μ²λ¦¬νλ μ½λλ μμ΄λ μ½λλ₯Ό μ§μ§ μμΌλ©΄ κ·Έλ₯ νμ κ²λ§ 보κ²λλ€. κ·Έλ¦¬κ³ κ·Έκ²λ νμ μν¬μλ μλ€.
3.1. νμ΄ν λΌμΈ ¶
WebGLμ μΌμ ν νλ¦κ΅¬μ‘°λ₯Ό λ§λ€μ΄ λκ³ κ·Έ κ°λΆλΆμ λ§λ€μ μλλ‘ ν΄ λμλ€. μλ§ μ΅μ νκ° μ¬μ΄ νμ κ·Έλ¬νμΌλ¦¬κ³ μκ°λλ€.

Attributeλ κ° ν¬μΈνΈ λ³λ‘ μ λ¬λλ μ 보μ΄κ³ uniform μ μ 체μμ 곡ν΅μ μΈ μ 보μ΄λ€. μΌλ°μ μΌλ‘ Attributeλ κ° μ μ μ μμΉ μ 보μ κ° μ§μ μ λ²μ λ²‘ν° μ 보λ₯Όμ μ λ¬νλ€. uniformμ μΌλ°μ μΌλ‘ μΉ΄λ©λΌμ μμΉλ νκ²½κ΄μ μμΉμ²λΌ μ 체μ μΈ κ²μ μ λ¬νλ€. Attributeλ uniformμ μΌμ’ μ λ³μμΈλ° νΈλ€μ μ»μ΄μμ κ·Έκ²μ ν΅ν΄ κ°μ μ λ¬ν μ μλ€. μ¦ Atrributeλ Uniformμ JavascriptμΈ‘μμ μμ΄λλ‘ μ 보λ₯Ό 보λ΄λ κ²μ΄λ€. varyingμ μμ΄λ κ°μ μ 보 μ λ¬μ μ¬μ©λλ€. vertex shaderμμ fragment shaderλ‘ κ°μ΄ μ λ¬λλ©° λ°λλ λΆκ°λ₯νλ€(νμ΄νλΌμΈ ꡬ쑰μ λΉμ°ν κ²μ΄λ€). μ΄λ vertex shaderλ κ° μ μ (κΌμ§μ ) fragment shaderλ κ° ν½μ μ νλ² νΈμΆλκ² λλλ° κ° μ μ μ¬μ΄μ κ°λ€μ 보κ°λ²μ κ±°μ³ μ λ¬λκ² λλ€(κ·ΈλΌλμΈνΈ κ°μ λλμ΄λ€ μ€κ°κ°μ μμμ λ§λ€μ΄ μ€λ€).

[PNG image (52.47 KB)]
Attributeλ κ° ν¬μΈνΈ λ³λ‘ μ λ¬λλ μ 보μ΄κ³ uniform μ μ 체μμ 곡ν΅μ μΈ μ 보μ΄λ€. μΌλ°μ μΌλ‘ Attributeλ κ° μ μ μ μμΉ μ 보μ κ° μ§μ μ λ²μ λ²‘ν° μ 보λ₯Όμ μ λ¬νλ€. uniformμ μΌλ°μ μΌλ‘ μΉ΄λ©λΌμ μμΉλ νκ²½κ΄μ μμΉμ²λΌ μ 체μ μΈ κ²μ μ λ¬νλ€. Attributeλ uniformμ μΌμ’ μ λ³μμΈλ° νΈλ€μ μ»μ΄μμ κ·Έκ²μ ν΅ν΄ κ°μ μ λ¬ν μ μλ€. μ¦ Atrributeλ Uniformμ JavascriptμΈ‘μμ μμ΄λλ‘ μ 보λ₯Ό 보λ΄λ κ²μ΄λ€. varyingμ μμ΄λ κ°μ μ 보 μ λ¬μ μ¬μ©λλ€. vertex shaderμμ fragment shaderλ‘ κ°μ΄ μ λ¬λλ©° λ°λλ λΆκ°λ₯νλ€(νμ΄νλΌμΈ ꡬ쑰μ λΉμ°ν κ²μ΄λ€). μ΄λ vertex shaderλ κ° μ μ (κΌμ§μ ) fragment shaderλ κ° ν½μ μ νλ² νΈμΆλκ² λλλ° κ° μ μ μ¬μ΄μ κ°λ€μ 보κ°λ²μ κ±°μ³ μ λ¬λκ² λλ€(κ·ΈλΌλμΈνΈ κ°μ λλμ΄λ€ μ€κ°κ°μ μμμ λ§λ€μ΄ μ€λ€).
κ° μμ΄λλ λμμ λμν μ μλλ° λΉμ°ν μ΄λ€μ μλ‘κ°μ λ
립μ μ΄μ΄μΌ νλ€.
3.2. μμ΄λ ¶
μμ΄λλ μμ΄λ μΈμ΄λ‘ λ°λ‘ μ§μ£Όκ³ μ»΄νμΌ ν΄μΌνλ©° μ¬μ§μ΄ λ§ν¬κΉμ§ μμΌμ£Όμ΄μΌ νλ€. GPUμ κ°λ ₯ν νλ ¬μ°μ° λ₯λ ₯μ κ°μ Έλ€ μ°κΈ° μν΄μμΈκ²μΌλ‘ 보μ΄λλ° μ΄κ²μ μ¬μ©νμ§ μκ³ μλ μμ νμΌλ λλ €λ³Όμκ° μλ€. λ€νμ΄ μΈμ΄λ CμΈμ΄μ λ§€μ° μ μ¬νκ³ νλ ¬μ°μ°μ΄ λͺ¨λ μκΈ° λλ¬Έμ λ±ν μ΄λ ΅κ±°λ νμ§ μλ€. λ€λ§ μ΄λλΆλΆμμ μ΄λμ μ°κ²°λλμ§ μ΄ν΄νλλ° μκ°μ΄ κ±Έλ¦°λ€.
3.2.1. vertex shader ¶
κ° μ μ (vertex, κΌμ§μ )λ§λ€ νΈμΆλλ©° μ£Όλ‘ κΌμ§μ μ μμΉλ₯Ό μ°μ°νκ³ μ€μ Viewμ ν¬μνλ μ°μ°μ μ£Όλ‘ νκ² λλ€. νλ§λλ‘ λͺ¨λΈμ μμΉ λ³νκ³Ό μΉ΄λ©λΌ μμ μ λ°λ₯Έ λ³ν μκ·Όλ²μ μ μ©νλ λ³νλ±μ μννλ€.
3.2.2. fragment shader ¶
κ° μ μ μ¬μ΄μ μλ ν½μ
λ§λ€ νΈμΆλλ€. μ£Όλ‘ κ΄μν¨κ³Όλ₯Ό μ μ©ν ν½μ
μ μ΅μ’
μ μΈ μκΉμ΄λ ν
μ€μ³ μ°μ°μ μ¬μ©λλ€. varyingλ³μλ₯Ό vertex shaderμμ fragment shaderλ‘ λ겨주면 κ° μ μ μ¬μ΄μλ 보κ°λ²μΌλ‘ λ³νλ κ°μ΄ λμ΄ μ¨λ€.
4. μμ μ½λ ¶
μλ μ½λλ μ ννμ§ μμΌλ©° μ°μ΅ λμ€μ μ½λμ
λλ€. λν WebGLμ νΉμ±μ μ½λκ° λΆμ°λμ΄ μμ΅λλ€. νμ¬ κ°μ²΄ λνμ μ§νμ€μ
λλ€.
νμ¬ κ°μ²΄ λνμ€ μ€λν λ¬Έμ μ λ΄μ°©. λλΆλΆμ λͺ¨λκ³Ό μΈμ΄λ μ½λλ μ½λ°±μΌλ‘ νΈμΆλλλ° μ΄κ²μ μ μ ν λνν λ°©λ²μ΄ μλ€. webGLκ³Όλ νλ± μ°κ΄μ΄ μλ λΆλΆμ΄λΌμ κ°μ μμμ ꡬννλλ‘ ν΄λ λμ§λ§ λλΆλΆμ κ²½μ° κ°μ μ½λλ₯Ό λ€μ€ μ§κ³ μλ λλ₯Ό λ³΄κ² λλ€. μ΄κ²μ μ΄λ»κ² ν΄μΌ μν λνμ΄λΌ ν μ μμκΉ?
4.1. 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; }
4.2. 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); }
4.3. 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); }