'Front-end/WebGL'에 해당되는 글 6

  1. 2011/05/03 zziuni WebGL이전에.. 3D란?
  2. 2011/04/27 zziuni Learning WebGL Lesson 1. 자습서 (4)
  3. 2011/04/24 zziuni Learning WebGL Lesson 1. 자습서 (3)
  4. 2011/04/21 zziuni Learning WebGL Lesson 1. 자습서 (2)
  5. 2011/04/16 zziuni Learning WebGL Lesson 1. 자습서 (1)
  6. 2011/04/08 zziuni WebGL 기초 용어정의 (2)

WebGL이전에.. 3D란?

Front-end/WebGL | 2011/05/03 01:18 | zziuni

WebGL을... 배우면서 글을 쓰다보니 오류도 있고, 소스와 함께 설명을 하니 갈수록 복잡해지는 느낌이 있어, 소스를 떠나 3D에 대한 이야기를 해보려 한다.
용어설명만으로 충분할 거라 생각했지만, 그렇지 않았던 관계로 WebGL을 이용해 모니터에 3D이미지가 뜨는것 까지의 과정을 최대한 쉽게 풀어보겠다. 나름 많은 시간을 들였지만, 아직 안다고 이야기할 정도는 아니다. 잘 아시는 분이 보고 오류가 있다면 코멘트 좀. (굽신) (그리고 이미지들은 이해를 도울 목적으로 어디서 모두 집어왔다. )


2D와 3D의 차이.

당신이 광고회사 다니는데, 애플에서 아이폰5 지면 광고 디자인요청을 받았다. (와우!)
멋드러지게 보이도록 아이폰5 의 전면 이미지를 쓰기로 했다면 어떤 방법이 있을까?

  1. 포토샵으로 이쁘게 그리거나,
  2. 아이폰5 하나 달라고 해서 찰칵! 찍는다.

이렇게 가져갔더니 잡스옹이 맘에 안드신단다. 이번에 MBA처럼 얇은게 핵심이라며, 측면 이미지를 쓰자고 하신다면? 어떻게 해야 할까?

  1. 그렸다면? 측면이미지를 다시 그려야 한다. 이것도 맘에 안들면? 큰일이다.
  2. 찍었다면? 아이폰 눕혀놓고 다시 찰칵! 맘에 안드실지 모르니 1mm씩 움직이며 사진을 한 100장 가져간다.

이 어설픈 예시의 핵심은 우리에게 실제 3D물체가 있다면 2D이미지는 원하는 만큼 얼마든지 만들 수 있다는 거다. (동영상도.)
어떤 입체 느낌이 나는 이미지가 필요할때 그 피사체를 그리면 2D이지만 그 피사체를 만들어서 찍으면 3D이다. 3D는 그저 위치만 바꿔가면서 찍기만 하면 얼마든지 이미지를 만들 수 있지만, 2D는 매번 그려내야 한다.

사진촬영 과정정리과 투영의 의미

사진촬영 과정을 다시 보자. 사진촬영 단계를 두단계로 나누어 보자.

피사체와 카메라를 원하는 곳에 위치시키기

피사체는 배경 좋은 곳에, 카메라는 피사체가 잘보이는 곳에 위치한다. 물론 잘보인다는 것은 원하는 형태로 보이는 곳이다. 아래 사진처럼 멋드러진 풍경을 잡을 위치.

사용자 삽입 이미지

원하는 사진이 나오도록 카메라 설정.

위치를 잡았다면 어떻게 찍을 건지를 정해야 한다. 배경을 날릴건지 줌을 땡길건지 등 말이다. 줌을 사용하면 카메라가 피사체에 다가가지 않아도 클로즈업된 촬영이 가능하고, (위치를 바꾸지 않아도 된다. )
셔터를 누르면 렌즈로 들어온 피사체 이미지가 필름에 상이 맺힌다. 이 상이 우리가 원하는 사진이다.

사용자 삽입 이미지

3D 물체 그리기와 투영하기

그럼 사진이야기를 이제 OpenGL의 용어로 바꾸어서 이야기해보자.

모델(model)과 촬영(view) 위치를 정하기

피사체는 모델(model), 촬영위치를 뷰(view)라 하자. 모델을 가상의 공간에 위치시키고, 바라 볼 뷰의 위치를 정한다. 이렇게 위치시킨 모델과 뷰는 위치만 바꾸면 얼마든지 재사용이 가능하다.

사용자 삽입 이미지

투영법(projection) 결정. 랜더링(rendering)

뷰위치에서 모델을 볼려면 일단 뷰포트(viewport)를 정의해야 하는데 카메라의 CCD(필름)을 생각하면 된다. 3차원 공간을 들여다볼 사각형의 창이라 상상하면 정확하다. 뷰 위치에 뷰포트를 위치시키면(찍을 위치에 카메라를 놓면) 준비가 끝났다. 아, 하나를 더 정해야 하는데, 어떻게 찍을 것인가를 정해야 한다.
뷰 위치에서 모델이 뷰포트에 어떻게 맺힐것인가를 정해야 하는데 이를 투영(projection)이라 한다.
투영법은 크게 정사영법과 원근법이 있는데, 우린 '실감'나는 2d를 원하므로 원근법(perspective projection)를 사용하겠다. 원근법의 설정값을 조절하면 카메라의 렌즈를 조인다던가, 줌을 땡기는 등과 같은 효과가 난다. 이제 셔터에 해당하는 렌더링(rendering)를 하면 모든 조건을 만족하는 2d가 만들어지고, 뷰포트를 통해 화면이 보인다. (이미지는 좌측 원근법, 우측 정사영)

사용자 삽입 이미지사용자 삽입 이미지

그래서 3D를 만든다는 건...

만약 3D로 움직이는 애니메인션을 만든다면, 이 모든 작업이 매 프레임마다 일어난다. 60fps이면 초당 60번 모델과 뷰의 위치, 광원, 텍스처등을 바꾸어 가면 랜더링이 일어나는 것이다. 모든 3D게임이 이과정으로 매 프레임을 만들면 바로 그래픽카드의 GPU가 이와 관련된 계산을 한다.
3D Max가 이런 작업을 위해 있는 툴이며, OpenGL은 이걸 프로그래밍 할 수 있게 하는 API중 하나다.(DirectX도 같은 종류) OpenGL의 embeded system용이 OpenGL ES인데, WebGL은 OpenGL ES를 브라우저안에서 Javascript로 컨트롤 할 수 있도록 javascript에 바인딩한 놈이다. 태생적으로 95%이상 같다.
그러므로 WebGL은 OpenGL를 모르면 이야기가 되지 않고, 기본적인 3D를 다루는 것에 대한 이해가 없으면 어렵다. 기존 웹개발자들이 Javascript로 모든게 가능함에도 WebGL이 넘사벽으로 보이는 이유가 바로 이거다. 웹개발자들은 그래픽을 프로그램 해본적이 없다는 것. 앞으론 웹개발자도 이쪽을 알아야 한다고 생각하기에...

각 단계별로 조금만 더 설명해보겠다. 대부분의 내용이 OpenGL의 개념 그대로이므로 특별한 언급이 없으면 OpenGL에서도 같은 내용이라고 생각하면 된다. 전반적인 흐름을 설명하려는 글이므로 더 상세한건 관련 자습서들을 참고하자.

3D물체 그리기

WebGL에선 쉐이더(Shader)를 이용해 모델을 만든다. 쉐이더는 전용언어인 쉐이더 랭기지(GLSL)를 이용해 짜는데 형태는 C 언어이며, 브라우저가 해석가능한 랭귀지는 아니다.
GLSL 소스를 WebGL에서 컴파일하면 쉐이더 (WebGL)클래스가 생기며, 이걸 이용해 모델을 만든다.

모델링 변환 (Modeing Transformation)

모델을 만들었으면 어디다 놔야겠지? 우리가 수학책에서 흔히 보던 x, y, z축의 3차원 좌표계. 이 가상의 절대 공간에 모델을 위치시키는데 이를 시계좌표계(eye coordinate)라 한다. 시계좌표계에서 우린 모델을 이동(transition), 회전(rotation), 크기변환(scaling)을 할 수 있다.

사용자 삽입 이미지

뷰 변환 (View Transformation)

바라볼 위치를 정하는데 기본값은 -Z 축을 바라보는 위치이다. +z에 있는건 보이지 않으며, 모델을 -z로 움직여야 보인다. 아니면 뷰를+z로 움직이던가. 모델과 뷰의 변환은 상대적인 감이 있다. 관측점인 뷰입장에선 모델이 움직이든 뷰가 움직이든 동일한 상만 볼 수 있으면 되기때문이다. 별도지정이 의미가 없어 Model-View Transformation이라 하여 한번에 지정한다.
일반적으로 모델에 뷰모델 행렬(view-model Matrix)를 곱하여 좌표를 얻는다.

사용자 삽입 이미지

투영변환 (Projection Transformation)

모델과 뷰의 위치가 정해지면 3D원 모델을 2D평면에 투영하여 투영면에 2차원 좌표(z=0)로 바꿔야 한다. 이때 투영하는 방법을 정해야 하는데, 정사영(orthographic Projection)와 원근법(perspective Proejction)이 있다. 투영방법과 옵션에 따라 적절한 행열(projection matrix) 곱하여 좌표를 얻는다.

원근법(Perspective Projection)

원근법은 아래 그림을 참고하자. 일단 관측점 기준으로 화각 fovy과 거리인 near, far를 정의해서 관측 볼륨(perspective viewing volume)을 정한다. 관측볼륨 밖의 모델은 뷰포트에 투영되지 않는다. 원근법을 적용하면 이런 관측볼륨 안에 있는 피사채의 좌표를 2D좌표로 바꿔 줄 수 있는 행열(Matrix)이 projection matrix가 된다.
이 행열을 만들기 위해선 fovy, near, far, 투영면의 width, height이 필요하다. 정사영은 패스.

사용자 삽입 이미지
사용자 삽입 이미지

WebGL and Canvas

위에서 뷰포트 가 나왔었는데, 원근법으로 투영하고자 하는 투영면을 뷰포트로 이해하면 쉽다. (100% 같은 말은 아니지만) 그리고 이 뷰포트가 곧 HTML의 canvas Element가 된다. canvas의 width, height를 뷰포트의 width, height로 설정하면 1:1이지만 이를 변경하면 확대,축소나 래더박스 형태로 보이게 할 수 도 있다.

사용자 삽입 이미지사용자 삽입 이미지

vertex나 fragment등의 설명을 일부러 하지 않았다. 한 겹 더 파고 들어가는건 자습서를 참고하자.

2011/05/03 01:18 2011/05/03 01:18

Comments List

Write a comment.

[로그인][오픈아이디란?]

WebGL Lesson 1 – A triangle and a square 에 대한 자습서 이고, 너무 길어서 나누어 올린다.

Lesson 2부터는 상당히 짧아질 것 같다. NeHe의 Article을 참고 하고 있다. Lesson 1의 소스는 GitHub에 있고, 라이브 데모는 여기. 나도 개 고생중이지만, 일반 웹개발자에게 webgl은 쉽지 않다. webgl이 어려운게 아니라 3D가 어렵다.(생소하다.)

Buffer

WebGL에서 Buffer는 구현상 필수는 아닌듯 하지만 이런 튜토리얼 이 아닌 실제 프로그램을 할려면, 성능 문제로 반드시 써야 하는 것으로 보인다. buffer 는 말그대로 webgl에서 다룰 데이터를 미리 담아두는 공간이다. 담기는 데이터의 크기와 형태에 따라 버퍼사이즈는 자동으로 결정된다. (spec 상으론 명시적 지정도 가능하다.) 도형을 그리기 위한 vertex buffer를 다루는 프로세스는 다음과 같다.

createBuffer -> ARRAY_BUFFER에 Binding -> set Data

재미있는건 binding이란 과정이다.
buffer만이 아닌 webgl에서의 많은 객체들은 안에 무언가 대입을 하려면 먼저 어딘가 정해진 곳에 binding을 해야 한다. buffer의 경우는 ARRAY_BUFFER에 바인딩을 하며, 다룰 buffer가 여러개면 ARRAY_BUFFER에 하나씩 binding을 하며 작업한다.
initBuffer() 소스를 보자.

initBuffer()

var triangleVertexPositionBuffer;
var squareVertexPositionBuffer;

function initBuffers() {
    triangleVertexPositionBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexPositionBuffer);
    var vertices = [
         0.0,  1.0,  0.0,
        -1.0, -1.0,  0.0,
         1.0, -1.0,  0.0
    ];
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
    triangleVertexPositionBuffer.itemSize = 3;      //좌표정보의 축 수.
    triangleVertexPositionBuffer.numItems = 3;      //Vertex 수. 꼭지점 수. 

    squareVertexPositionBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer);
    vertices = [
         1.0,  1.0,  0.0,
        -1.0,  1.0,  0.0,
         1.0, -1.0,  0.0,
        -1.0, -1.0,  0.0
    ];
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
    squareVertexPositionBuffer.itemSize = 3;
    squareVertexPositionBuffer.numItems = 4;
}

triangleVertexPositionBuffer 라는 요즘 유행하는 풀 스펠링 타입의 javascript변수에 createBuffer()로 빈 buffer를 생성한다. 그리고 ARRAY_BUFFER에 바인딩을 한다.
그리고 나서 이 이 레슨에서 처음으로 매트릭스(행열)가 나온다. webgl에서 행열은 행열처럼 보이도록 하고 있지만 javascript로는 그냥 1차원 배열이다.
마치 3x3행열로 보이는 vertices는 length 9의 배열이다. 이를 행열처럼 사용하기 위해선 두가지 정의가 필요한데 바로 의 정의이다. 그것을 레슨에선 itemSize, numItems라 표현한다. 이 두 변수는 buffer객체의 프로퍼티가 아니다. Javascript의 특성을 이용하여 임의로 만든 프로퍼티이다. vertices는 3차원상의 좌표이므로 한 Item(vertex)의 size는 3이며(x,y,z), 삼각형을 그리려면 numItems는 3이 된다. 사각형은 같은 이유로 itemSize는 동일하게 3, numItems는 4이다.

이렇게 선언된 행열(실은 그냥 배열)vertices는 bufferDate()의 인자로 Float32로 형변환이 되어 대입된다. gl.STATIC_DRAW는 buffer data의 형태를 알려주는 파라미터로 정적인 데이터임을 알려주고 있다. STREAM_DRAW, DYNAMIC_DRAW도 있지만 레슨에선 다루지 않고 있다. 이렇게 해서 삼각형과 사각형 버퍼객체를 만들었다. 레슨에선 심플한 구성을 위해 전역변수 2개로 처리했다.

이제 이렇게 만든 buffer를 program에 넘기고(?), 'buffer에 있는걸 그려!' 라고 하면 된다.

다음은 대망의 drawscene() 인데.. 그전에 WebGL이 아닌 3D를 그리는 법에 대해 한번 정리하겠다. 안그러면 drawscene() 설명이 산으로 갈듯.


졸면서 썼더니 배열과 행열을 맘대로 적어서 수정. ㅡㅡ;

2011/04/27 01:10 2011/04/27 01:10

Comments List

Write a comment.

[로그인][오픈아이디란?]

WebGL Lesson 1 – A triangle and a square 에 대한 자습서 이고, 너무 길어서 나누어 올린다.

Lesson 2부터는 상당히 짧아질 것 같다. NeHe의 Article을 참고 하고 있다. Lesson 1의 소스는 GitHub에 있고, 라이브 데모는 여기. 나도 개 고생중이지만, 일반 웹개발자에게 webgl은 쉽지 않다. webgl이 어려운게 아니라 3D가 어렵다.(생소하다.)

getShader() and initShader()

이전 포스트에도 주저리주저리 설명했었는데, shader생성과정은 다음과 같다. WebGL에서 사용하는 용어는 일부러 영어로 적었다. (결과는 조사만 한글..) shaders는 vertex shader, fragment shader 모두를 말한다.

1.Shaders create -> 2. Shaders source로 GLSL string를 load -> 3. Shaders compile -> 4. Program Create -> 5. Program 에 shaders를 attach -> 6. Program안에서 vertex shader와 fragment shader를 link -> 7. Program을 use.

이 과정은 3D를 그리기 위해 거쳐야 하는 프로세스이다. 1~3 가지의 작업이 getShader()에서 일어나고, 4~7까지의 작업이 initShader()에서 일어나며, 정해진 프로세스이므로 함수로 정의하고 사용하면 된다.

getShader()

function getShader(gl, id) {
    //script 로 정의된 shader-fs, shader-vs를 가져오기.
    var shaderScript = document.getElementById(id);
    if (!shaderScript) {
        return null;
    }

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

    //분기를 두어 fragment shader와 vertex shader중 생성. 
    var shader;
    if (shaderScript.type == "x-shader/x-fragment") {
        shader = gl.createShader(gl.FRAGMENT_SHADER);
    } else if (shaderScript.type == "x-shader/x-vertex") {
        shader = gl.createShader(gl.VERTEX_SHADER);
    } else {
        return null;
    }
    //shader source로 GLSL String을 지정. 
    gl.shaderSource(shader, str);
    //compile.
    gl.compileShader(shader);

    if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
        alert(gl.getShaderInfoLog(shader));
        return null;
    }

    return shader;
}

getShader() 함수이다. 이해에 도움이 되도록 주석을 달았다. gl.xxxx형태의 메소드들이 생소해서 그렇지 쉽게 이해될 수 있는 javascript코드이다. DOM id로 element를 가져와서 innerHTML내용(여기선 node fh c)을 string화 하고, gl.createShader()로 생성한 객체(shader)안에 GLSL을 넣고 컴파일 한다. 그러면 가용가능한 shader를 반환한다.

initShader()

var shaderProgram;

function initShaders() {
    //shader 생성
    var fragmentShader = getShader(gl, "shader-fs");
    var vertexShader = getShader(gl, "shader-vs");
    //program에 shader attach, link, use
    shaderProgram = gl.createProgram();
    gl.attachShader(shaderProgram, vertexShader);
    gl.attachShader(shaderProgram, fragmentShader);
    gl.linkProgram(shaderProgram);

    if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
        alert("Could not initialise shaders");
    }

    gl.useProgram(shaderProgram);
    //attributes, Uniform Location가져오기.
    shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
    gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);

    shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix");
    shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");
}

initShaders()이다. 이 함수의 목표는 shaderProgram이란 전역변수를 만드는 것이다. 위에서 설명했듯이 shader는 program이란 개체 안에 attache되고, link되고 use되어야 한다. (주석 찹조) gl.useProgram()까지 실행되면, shader를 사용할 준비가 끝났다. 이제 사용하면 되는데... 잠시 정리를 해보자.

여태까지 우리는 canvas.getContext()를 통해 webgl객체인 gl를 만들고, shader를 만들고 program를 만들었다. 하지만 fragment GLSL의 gl_Color를 제외하곤 구체적인 도형을 정의하지 않았다. 도형을 그리기 위한 프로그램을 만든것이다. (데이터가 아닌)

이제 (shader가 들어있는) program을 이용하여 도형데이터를 행열(Matrix)와 벡터(vector) 로 shader에 넣어주면 된다. 예를 들어 아래처럼?

gl.setSaderDateInProgram(vertexs, uniform);

GLSL에서 정의한 attribute와 uniform에 값을 넣어주면 되는데, 좀 생소하지만 WebGL은 위와 같은 메소드, 방법은 제공하지 않는다. attribute와 uniform의 위치값(location)알아야 값을 넘길 수 있다. (포인터로 이해해야 쉬우려나..) initShaders() 함수의 끝의 네줄은 바로 그 location을 가져오는 부분이다.

attritube 와 uniform은 Location을 가져오는 법이 다르다.

gl.getAttribLocation()는 shaderProgram에 attach된 vertexShader 에서 attribute의 정수형 index값을 가져오지만, gl.getUniformLocation()는 WebGLUniformLocation 객체를 가져온다. 게다가 attribute의 경우 gl.enableVertexAttribArray()를 이용해 해당 attribute를 enable해주어야만 사용가능하다.

자, 그럼 이제 그림을 그리기위한 프로그램 준비가 끝났다. 이제 무얼 그릴건지 그릴 데이터를 만들자. initBuffers();

Reference link

http://www.opengl.org/sdk/docs/tutorials/ClockworkCoders/loading.php

2011/04/24 14:12 2011/04/24 14:12

Comments List

Write a comment.

[로그인][오픈아이디란?]

WebGL Lesson 1 – A triangle and a square 에 대한 자습서 이고, 너무 길어서 나누어 올린다.
Lesson 2부터는 상당히 짧아질 것 같다. NeHe의 Article을 참고 하고 있다. Lesson 1의 소스는 GitHub에 있고, 라이브 데모는 여기. 이전 포스트에서 이어진다.

initShaers()를 보기전에

initShader()를 볼려면, getShadr()를 먼저 봐야 하고, getShader()가 호출하는 shader script(소스상단의 x-shader type의 script)를 먼저 알아야 하고, shader script를 알려면 그전에 shader를 알아야 한다.

Shader and GLSL
3D를 모르는 보통 개발자(?)의 언어로 포현하면... shader란 3D에서 인스턴스를 만들기 위한 class 소스이며, 화면에 뿌릴 객체 자체이기도 하다. shader를 정의하기 위한 전용 language가 있으며, OpenGL Shader를 위한 Shading Language를 GLSL(OpenGL Shading Language)라 한다.
shader를 이용하는 3D엔진은 SL소스를 문자열로 읽어들여서 엔진 내부에서 컴파일을 한다. 그리고 이렇게 컴파일해서 반환되는 shader객체를 이용해서 화면에 표시할 오브젝트들을 만든다. 이 과정은 엔진내부에서 돌아가는 과정을 설명하는게 아니라 '짜야' 한다. 이를 수행하는게 shader script, getShader(), initShaders()이다.

형태로서 GLSL
레슨에선 자바스크립트처럼 script element를 사용하여 정의하였지만, type이 x-shader/x-fragment와 x-shader/x-vertex인 스크립트 소스는 브라우저에서 인지하는 소스가 아니다. WebGL에서 사용하기 위해 GLSL소스를 자바스크립트와 구분하기 위해 적어놓은 String일 뿐이다. 사실 아래와 완전히 같다.


var shader-fs = "#ifdef GL_ES
    precision highp float;
    #endif

    void main(void) {
        gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
    }";
var shader-vs = "attribute vec3 aVertexPosition;

    uniform mat4 uMVMatrix;
    uniform mat4 uPMatrix;

    void main(void) {
        gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
    }";
  
 

레슨의 소스에는 GLSL로 fragment shader와 vertex shader 두가지가 정의 되어있다.  GLSL단계에서 알아야 할 개념들을 정리해본다.

vertex shader
vertex는 도형의 꼭지점을 말한다. vertex shader는 이 꼭지점들을 관리하는 shader이다. (혹은 꼭지점으로만 이루어진 shader) vertex들의 위치, 색상, 사이즈, 꼭지점기반의 텍스처의 좌표, 광원 등을 관리한다. 그걸 관리하기 위한 인자와 메소드 등을 가지고 있는 GLSL로 된 vertex shader 소스가 아래의 레슨 소스이다.


<script id="shader-vs" type="x-shader/x-vertex">
    attribute vec3 aVertexPosition;

    uniform mat4 uMVMatrix;
    uniform mat4 uPMatrix;

    void main(void) {
        gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
    }
</script>



fragment shader
fragment를 우리말로 뭐라 불러야 할지 잘 모르겠지만, '면이 채워진 도형'이라 하자. vertex shader를 넘겨받아서 그 vertex기반으로 색상, 텍스처, 안개처리를 한다. vertex별로 색상이 있다면, 그 vertex들 사이 픽셀을 색으로 채워주고 광원이 있다면 텍스터에 광원효과를 반영한다. GLSL로 된 fragment shader 소스가 아래의 레슨 소스이다.


<script id="shader-fs" type="x-shader/x-fragment">
    #ifdef GL_ES
    precision highp float;
    #endif

    void main(void) {
        gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
    }
</script>


shader input/output type
위에서 shader는 GLSL로 되어있는 화면에 뿌리기위한 객체의 소스라고 이야기했다. GLSL에서는 이런 처리를 위해 3가지의 input/output 타입을 제공한다. attributes, uniforms, varyings 이다.

attributes
attributes는 vertex shader에만 있으며, vertex별로 달라지는 값을 관리한다. 좌표값, 색등이 여기에 해당한다. 이번 레슨에서는 좌표값인 aVertexPostion 만 정의하고 있다.

uniforms
uniforms은 vertex 별로 변경되지 않는 값이다. viewport의 위치, 광원의 위치등 fragement shader에서 렌더링을 할때 변경되지 않는 값을 의미한다. 이번 레슨에서는 그린 도형을 바라볼 위치와 관련된 uMVMatrix, uPMatrix만 정의하고 있다.

varyings
attributes와 uniforms는 shader별 input/output 값인 반면에, varyings은 vertex shader에서 fragment shader로 데이터를 보낼떄 사용한다. vertex shader에서 정의한 gl_Position은 fragment shader에서 사용된다. (소스상에는 없다. ) 이번 레슨에서는 두 shader에서 gl_Position, gl_FragColor가 varyings에 해당한다.
당연한 이야기지만 vertex shader에서 gl_Position varying은 필수 값이다. 꼭지점을 3D공간에 정의한 역활을 하는 shader에서 그 좌표값은 필수일 수 밖에.

정리해보면
vertex shader는 attribute인  aVertexPosition  와 uniform인 uMVMatrix, pMatrix 변수를 통해 javascript 에서 input을 받아서 gl_Position이라는 varying을 정의한다.
그리고 이 vertex shader를 이용하여 fragment shader에서 gl_FragColor 를 흰색으로 정의하고 vertex사이의 색을 칠한다.

다음은 진짜 getShader()와 initShader()

2011/04/21 02:46 2011/04/21 02:46

Comments List

Write a comment.

[로그인][오픈아이디란?]

원문은  WebGL Lesson 1 – A triangle and a square  인데, 이 포스트는 원문을 번역한 것이 아니라 스터디를 하며 공부한 내용으로 작성한 것이다. 오류가 있을 수 있다. 부디 WebGL이나 OpenGL을 잘아는 분이 오류가 있다면 딴지를 걸어주시면 감사. 길어서 쪼개서 올릴 예정.

사용자 삽입 이미지



모든 소스는 GitHub repository에 있고 작동 데모는 WebGL를 지원하는 브라우저 에서만 볼 수 있다.
Learning WebGL를 운영하는 Giles Thomas는 지속적으로 강좌들을 다듬고 수정하는 듯 하다. 하지만 소스만 수정하고 강좌 내용은 수정하지 않는 부분들이 있어서 부분적으론 레슨의 설명과 소스가 일치 하지 않는 부분이 있다. 여기선 소스 기준으로 설명한다.
영문인 원문이 부담되는 분들은 firejune님이 번역중인 것을 참고하면 된다. (최고심!)

이번 레슨은 2차원 도형이므로 데모가 큰 의미는 없고, Javascript가 WebGL과 어떻게 관계를 맺는지 간만 보자.
WebGL과 3D관련 용어들은 이전 포스트에 지속적으로 추가, 갱신할 예정이지만 각 레슨에 관련된 주요 용어는 다시 설명해보겠다.

이번 레슨의 소스는 index.html과 glMatrix-0.9.5.min.js로 구성되어있다.
레슨의 모든 소스는 index.html 에 있으며, glMatrix는 WebGL에서 도형(Object or Shader)을 다룰때 반드시 필요한 수학개념인 행열(Matrix)과 벡터(Vector)를 핸들링 하기위한 오픈소스 라이브러리다. 나를 포함한 대부분의 개발자가 10년이상 전에 배운거라 기억할지 모르겠지만 좌표계에서 도형은 행열의 곱(Matrix multiplication)을 통해서 이동, 회전등의 변환(translate)을 할 수 있다.
이게 왜 라이브러리 형태로 필요한가 하면 행열이라는걸 Javascript는 지원하지 않고, WebGL에선 사용해야 하기때문이다.

index.html을 보자.
소스는 위에서 부터 보지 않고 작동 순서를 따라가면서 보겠다.

1. HTML part.
<canvas>가 있는 간단한 HTML 가 있다. 중요한건 이것 한줄이다. canvas는 3D공간을 들여다보는 창 역활을 할것이다. 적절한 명령어를 통해 WebGL과 연결된다.
일단. 윈도우, 카메라등 이해하기 편한대로 상상하자. 그리고 width, height는 꼭 필요하다.


<canvas id="lesson01-canvas" style="border: none;" width="500" height="500"></canvas>


2. webGLStart()

onload 이벤트로 실행되는 첫 함수다.


    function webGLStart() {
        var canvas = document.getElementById("lesson01-canvas");
        initGL(canvas);
        initShaders();
        initBuffers();

        gl.clearColor(0.0, 0.0, 0.0, 1.0);	//RGB + alpha
        gl.enable(gl.DEPTH_TEST);

        drawScene();
    }


요약하면 생성자함수 세개(GL, Shaders, Buffers)를 실행, gl이란 객체의 메소드를 두개 실행, 그리고 그리기(drawScene) 이다.
생성자함수들는 따로보도록 하고, gl이란 객체가 처음나온다. 이름으로 유추할 수 있듯이 initGL()로 생성된 WebGL객체이다. (정확히는 WebGLRenderingContext interface의 WebGL Context이다.) 복잡한 설명 다 빼고, 앞으로 나오는 대부분(!)의 gl.xxx 는 javascript가 아닌 WebGL의 메소드이거나 프로퍼티이고, 뭐하는 놈인지가 궁금하면 다음 WebGL Specification document에서 확인할 수 있다.

gl.clearColor()는 바탕색을 정의한다. 4개의 아규먼트는 R,G,B,alpah 값이다. 1.0, 0.0, 0.0, 1.0 으로 바꾸면 레슨의 배경은 빨강색이 된다. 여기선 불투명도 100%에 검정색을 깔았다.
gl.enable()은 on/off계념으로 무언가를 활성화시키는 메소드이다. 여기선 z축방향의 '깊이'를 표현하는 gl.DEPTH_TEST 를 활성화하였다. (DEPTH_TEST는 enum타입의 WebGL 상수값이다.) 이번 레슨은 단순한 2차원 도형이기때문에 사실 이 메소드는 아무 변화를 부르지 않는다. 하지만 레슨3 이후 부터는 gl.DEPTH_TEST를 enable하지 않으면 의도한대로 3D가 표현되지 않는다.

다시 정리하면 webGLStart()는 WebGL, Shader, Buffer를 생성하고, 바탕생을 검정색으로 깐다음, 3D옵션(?)을 켜고, 화면을 그리는 함수다. 결론적으론 별내용은 없다. ㅋ



3. initGL()


	var gl;
    //객체생성자 
    function initGL(canvas) {
        try {
            gl = canvas.getContext("experimental-webgl");
            gl.viewportWidth = canvas.width;
            gl.viewportHeight = canvas.height;
        } catch (e){
        }
        if (!gl) {
            alert("Could not initialise WebGL, sorry :-(");
        }
    }

canvas 기본적으로 이제 그림을 그릴 빈 공간이다.
이 공간에 어떤 그림을 그릴지(context)를 결정해야 하는데 .getContext()로 context를 호출할 수 있고 현재 공식적으로 "2d" 밖에 아규먼트 값이 없다.
여기선 "experimental-webgl"로 WebGL Context를 호출하고 있다. experimental이란 prefix에서 알 수 있듯이 브라우저에서 정식기능으로 들어갔지만 WebGL자체는 아직도 '실험적' 이다.
어쨌든 다시 돌아와서 initGL()은 WebGL Context를 javascript 변수 gl에 담는게 목표이다. 이후에 사용하기 위해 canvas의 width, height를 gl 객체의 프로퍼티로 저장한다.
이 세줄에서도 Javascript는 빛을 발하는데, variable type의 javascript 변수는 듣보잡(?)인 WebGL도 담아내고, 거기다 webgl에는 있지도 않은 viewportWith, viewportHeight를 지맘대로 만들어서 값을 저장한다.

4. initShaders()
아.. 이건 어렵다. 다음에. ㅎ


2011/04/16 02:28 2011/04/16 02:28

Comments List

Write a comment.

[로그인][오픈아이디란?]

WebGL 기초 용어정의

Front-end/WebGL | 2011/04/08 03:11 | zziuni


사용자 삽입 이미지
WebGL 에 대한 자료들을 보면 대부분 이미 잘 알고 쓴 글이거나, 해외 데모 소개하는 것 말곤 거의 자료가 없길레 스터디를 시작 해봤습니다. 목표는 WebGL를 통해 OpenGL ES기반의 그래픽 처리에 대한 전반적인 지식 습득과 초간단 데모를(가급적 움직이는 3D오브젝트)를 만들어보는 건데, 현재는 용어 이해에도 급급한 수준입니다. 그래서 일단 lesson을 따라가면서 알아가는 3D, 혹은 그래픽관련 용어들을 먼저 정의하고 갱신해 갈 예정입니다.
개인적으로 그래픽 처리에 대한 지식이 전혀 없습니다. 학습하면서 포스팅 하는 내용이니 오류가 있을 수 있습니다.

WebGL스터디는 공식사이트 보다 유명한 Learning WebGL.comTutorial을 보고 하고 있습니다.
번역보다도... Tutorial lesson을 학습해보고 정리하는 식으로 포스팅할 생각입니다.
이 포스트는 WebGL관련 용어 설명합니다. 그리고 LearningWebGL의 Lesson source는 Github에서 받을 수 있습니다.


WebGL이란?
가장 정확한 정의는 'OpenGL ES 2.0의 Javascript 바인딩 버전' 인거 같습니다. 좀더 쉽게 말하면 '브라우저에서 Javascript로 컨트롤 할 수 있는 OpenGL ES 2.0의 웹 최적화버전' 이 되겠습니다. 사용자(개발자)측면에선 Ajax를 사용하기위해 XMLHttpRequest를 사용하는것과 같습니다. (조금 더 복잡하지만)
OpenGL의 Embedded System용 스펙인 OpenGL ES를 기반으로 하기때문에 OpenGL에서 무겁고 불필요한 부분은 제거되었고, HTML Canvas element에 랜더링 결과를 뿌려주도록 만들어져 있습니다.
저를 포함한 일반 웹개발자들은 3D, OpenGL을 완전 모르기 때문에 살짝 진입장벽이 느껴집니다. (수학 정석책이 필요합니다. ㅡㅡ;)

어디서 작동시켜 볼 수 있나?
얼마전까지만 해도 주류 브라우저들의 개발버전에서나 볼 수 있었으나 현재는 Chrome10, Firefox 4 정식버전에서 지원하고 있습니다.

그럼 용어 정리입니다.




-----------------------------------------------------------------
04-12 : 오타수정. shader script를 GLSL로 변경. 정의 변경.
04-09 : projection matrix 정의 변경. Perspective projection 추가.
-----------------------------------------------------------------


 vertex::
  다각형 도형의 꼭지점을 말한다. vertex가 3개면 3각형. vertex는 3차원 좌표값 3개와 색상을 값으로 가진다.

 shader::
  도형으로 이해하면 무리가 없을듯. vertex로 구성된 OpenGL의 object이다. 이 shader가 '면'이 되고, 색상과 텍스처가 입혀진다.

 vertex-shader::
  vertex( 꼭지점)으로만 이루어진 shader. 면이 없고 좌표계에 점만 찍혀있는 것으로 이해하면 될거 같다. 각각의 위치값과 색상을 가지고 있는 vertex들의 집합.

 fragment-shader::
  vertex-shader가 vertex사이의 면(픽셀)이 채워진 shader를 말한다. vertex들은 각각 고유의 색을 가질 수 있으므로 vertex-shader를  fragment-shader로 변환하는 과정에  중간 픽셀들은 모두 채워지며, 색상차이는 자동으로 그라데이션이 된다.

 shading language (GLSL ES)::
 OpenGL ES's Shading Language. shader를 정의하는 스크맄트.  GLSL소스는 반드시 String형태로 읽혀서 컴파일 후 program에 추가 된다. 해당 튜토리얼에서는 javascript에서 스트링으로 처리하면 가독성에 문제가 있으므로 type을 x-shader로 정의한 <script> 를 별도로 분리해서 사용한다.

 attribute::
 shader의 vertex마다 변하는 값들. verax의 좌표값, 색상이 이에 해당한다. shader를 만들때 vertax단위로 shader script에 넘기는 '인자'

 uniform::
  shader단위로 변하는 값들. shade의 (바라 볼)위치값인 model-view matrix 가 이에 해당한다. 삼각형인 shader가 있다면, vertex는 3개, 그 삼각형의 바라볼 위치가 1개. 전자의 정보가 attribute고 후자의 정보가 uniform이다.

 선형보간::
  vertex-shader가 fragment-shader로 바뀔때 빈 픽셀들이 채워지는 작업.

 matrix::
  행열.  javascript에서 표현될때는 그냥 1차원 배열이지만, 적절한 라이브럴리로 행열로 다루어진다. 예를 들어 length가 9이고 어떤 값이 3개의 값으로 이루어졌다면(3차원 좌표값처럼)  3x3 배열의 값이 된다. vertex들이 위치할 좌표를 지정할때 사용된다. 또한 shade의 이동, 회전, 깊이 등을 표현할때 사용한다.

 matrix multiplication::
  행열 곱. 행열을 곱을 통해 좌표계의 도형을 이동시킬 수 있다. WebGL에서도 행열곱을 통해, vertex들(shader) 최초 위치에서 이동시킬 수 있다. (관련 수학지식이 부족해서 불명확함)

 model-view matrix::
  WebGL에서 shader가 처음에 위치하는 최초의 위치를 말한다. shader의 위치가 변하면 그 바뀐 위치도 상대좌표로 반영 가능하다. 혹은 shader 를 바라보는 관점(카메라, 눈, 모니터)의 위치이기도 하다.

 projection matrix::
  3d인 물체들을 우리는 2D인 스크린(webgl에선 Canvas)를 통해 본다. 그럴려면 3D 피사체가 2D 스크린에 투영(projection)되어야 한다. 이렇게 3D를 2D로 투영변환시킬 때 model-vie matrix와 행열곱을 할 matrix가 projection matrix이다. 쉽게 말하면 피사체를 바라보고 싶은 각도에 스크린을 놓았는데, 거기에 투영되도록 하는 계산할때 사용되는 행열이다.

 Perspective projection::
  원극법. 3D를 2D오 옮길때 원근법과 정사영(正射影 Orthographic Projection) 이 있다. 해당 Lesson에는 원근법이 사용된다. 소스 설명할때도 나오겠지만, shader들을 위치와는 별개로  '어떻게, 어디서'볼것인지를 정한다.  이때 원근법으로 보기를 원하면 적절한 인자를 넘겨 세팅해야 한다.
-----------------------------------------------------------------

학습용 링크들

WebGL공식사이트 KHRONOS

WebGL Specification

Learning WebGL의 Lessons

Javascript 3D Engine three.js

Learning WegGL lesson 번역, lesson0, lesson1, lesson2

WebGL inspector
http://www.songho.ca/opengl/gl_projectionmatrix.html

-----------------------------------------------------------------

WebGL의 놀라운 데모들.

HelloEnjoy의 HelloRacer

Chris Milk의 The wilderness DownTown

WebGL 퀘이크 Q3Tourney2 

Mr.doob 의 WebGL Clouds
2011/04/08 03:11 2011/04/08 03:11

Comments List

  1. roya 2011/05/16 10:32

    잘읽고 갑니다~

    • zziuni 2011/05/27 23:55

      도움이 되셨을라나요.. ㅎ

Write a comment.

[로그인][오픈아이디란?]