그래픽/WebGL

webGL 작동방식(부연 설명)

hyongti 2022. 2. 20. 15:55

 

https://webglfundamentals.org/webgl/lessons/ko/webgl-how-it-works.html

 

WebGL 작동 방식

WebGL이 실제로 하는 일

webglfundamentals.org

 

이곳에 아주 잘 정리돼있지만, 내가 이해가 잘 안 되는 부분이나 정리하고 싶은 부분을 다시 정리하면서 봐야겠다.

 

GPU에는 기본적으로 2가지 파트가 있다고 한다. 한 파트는 vertex를 처리하고, 다른 한 파트는 처리된 걸 그리는 파트다. 아래 그림에서 윗부분이 vertex를 처리하는 부분, 아랫 부분이 그리는 부분인 것 같다. 즉, GPU는 크게 vertex shader와 pixel shader가 있는 것.

https://ko.wikipedia.org/wiki/%EA%B7%B8%EB%9E%98%ED%94%BD_%EC%B2%98%EB%A6%AC_%EC%9E%A5%EC%B9%98

 

 

WebGL 기초에서 vertexShaderSource라는 것을 작성하고 그것을 바탕으로 vertex shader를 만든 다음, fragment shader를 엮어서 program이라는 걸 만들었다. 우리가 GPU에 정점 데이터들을 제공하면, vertexShaderSource 내부에 있는 함수가 각 정점에 대해 한 번씩 호출된다고 한다. 함수 내부에서는 선언한 변수 등을 통해 정점에 대한 clip space의 값인 gl_Position을 계산한다.

https://webglfundamentals.org/webgl/lessons/ko/webgl-how-it-works.html

만약 삼각형을 그린다고 가정하면, 맨 처음부터 정점 3개를 생성할 때마다 GPU가 이걸 이용해 삼각형을 만드는 것이다. 어떤 픽셀이 삼각형의 각 점에 해당하는지 확인한 다음, 삼각형을 래스터화(rasterization, "픽셀로 그리기")한다. 이때, 각 픽셀마다 fragment shader를 호출해서 어떤 색상으로 만들지도 묻는다. fragmentShaderSource에서 작성한 gl_FragColor가 해당 픽셀의 색이다.

 


webgl fundamental을 아무리 봐도 세팅 부분이 복잡해서, 간단하게 순서를 정리해야겠다.

 

간단하게 생각하자. 결국 셋팅 -> 그리기다. 생소할 뿐이다.

 

1. webGL context생성

2. GLSL로 작성된 shader 소스를 바탕으로 shader, shader를 바탕으로 program 생성(셰이더에 대한 자세한 내용은 여기)

3. 프로그램의 속성(attribute) 위치를 탐색(정점 데이터는 버퍼를 통해 GPU에 전달된다. 버퍼에 데이터가 있으면 어떻게 데이터를 가져와 정점 셰이더의 속성에 제공할지 webgl에게 알려줘야 한다. 이를 위해, 속성을 할당한 위치를 webgl에게 물어봐야 한다.). 

1 ~ 3의 작업은 보통 초기화할 때 수행한다.

 

  // setup GLSL program
  var program = webglUtils.createProgram(gl, vertexShader, fragmentShader);

  // look up where the vertex data needs to go.
  var positionAttributeLocation = gl.getAttribLocation(program, "a_position");

  // lookup uniforms
  var matrixLocation = gl.getUniformLocation(program, "u_matrix");

4. 버퍼 생성 후 gl.ARRAY_BUFFER에 바인드(ARRAY_BUFFER는 webgl 내부 전역 변수라고 생각하면 되고, 모든 함수가 ARRAY_BUFFER를 통해 리소스를 참조한다고 한다)한 뒤, ARRAY_BUFFER 에 데이터 저장 (ex. 정점의 위치, 정점의 색깔)

  // Create a buffer.
  var positionBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
  gl.bufferData(
    gl.ARRAY_BUFFER,
    new Float32Array([0, -100, 150, 125, -175, 100]),
    gl.STATIC_DRAW
  );
  • gl.createBuffer는 버퍼를 생성하고,
  • gl.bindBuffer는 해당 버퍼를 작업할 버퍼로 설정한다.
  • gl.bufferData는 데이터를 버퍼로 복사한다.

cf. 세 개는 한 작업에 대해 연속으로 실행돼야 한다.

// 이건 되지만!
  var positionBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
  setGeometry(gl); // 5에 해당하는 작업

  var colorBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
  setColor(gl); // 5에 해당하는 작업(gl.BufferData()를 이용함)
  
  
// 이건 안 됨.
  var positionBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);

  var colorBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);

  setGeometry(gl); // 5에 해당하는 작업
  setColor(gl); // 5에 해당하는 작업

 

 

6. 그리기

    // Tell WebGL how to convert from clip space to pixels
    gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

    // Clear the canvas.
    gl.clear(gl.COLOR_BUFFER_BIT);

    // Tell it to use our program (pair of shaders)
    gl.useProgram(program);

    // 3번에서 저장해두었던 속성의 위치를 가지고 webgl에게 '버퍼에서 데이터를 제공하길 원한다'고 알려줌.
    gl.enableVertexAttribArray(positionAttributeLocation);

    // ARRAY_BUFFER 바인드 포인트에 버퍼를 할당함.
    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);

    // 현재 ARRAY_BUFFER 바인드 포인트에 바인딩된 버퍼에서 데이터를 가져오기 위해,
    // 정점마다 얼마나 많은 컴포넌트(1 - 4)가 있는지,
    // 데이터 타입(BYTE, FLOAT, INT, UNSIGNED_SHORT, 등등...)은 무엇인지,
    // 스트라이드는 한 데이터에서 다음 데이터를 가져오기 위해 몇 바이트를 건너뛰어야 하는지,
    // 오프셋은 버퍼에서 데이터가 얼마나 멀리 있는지 등을 WebGL에 알려줌.

	// 컴포넌트의 숫자는 항상 1에서 4까지의 범위를 가짐.
    var size = 2; // (x, y) 값, 만약 rgba라면 size = 4일 것
    var type = gl.FLOAT; // the data is 32bit floats
    var normalize = false; // don't normalize the data
    // 만약 데이터의 타입마다 1개의 버퍼를 쓴다면 스트라이드와 오프셋은 항상 0일 수 있음.(아직 무슨 말인지 잘 모르겠따..)
    var stride = 0; // 스트라이드가 0이면 "타입 크기에 맞는 스트라이드 사용"을 의미
    var offset = 0; // 오프셋이 0이면 "버퍼의 처음부터 시작"을 의미
    // (0 이외의 다른 값으로 설정하는 건 더욱 복잡하고 성능 면에서 어느 정도 이점이 있긴 하지만,
    // WebGL을 한계까지 몰아붙이기 위한 게 아니라면 복잡함을 감수할만한 가치는 없을 것 같습니다)라고 한다..
    gl.vertexAttribPointer(
      positionAttributeLocation,
      size,
      type,
      normalize,
      stride,
      offset
    );

    // Compute the matrix
    // 정점 데이터를 움직임
    var matrix = m3.projection(gl.canvas.clientWidth, gl.canvas.clientHeight);
    matrix = m3.translate(matrix, translation[0], translation[1]);
    matrix = m3.rotate(matrix, angleInRadians);
    matrix = m3.scale(matrix, scale[0], scale[1]);

    // Set the matrix.
    gl.uniformMatrix3fv(matrixLocation, false, matrix);

    // Draw the geometry.
    var primitiveType = gl.TRIANGLES;
    var offset = 0;
    var count = 3;
    gl.drawArrays(primitiveType, offset, count);
  }

 


어렵다!!!!!!!!