티스토리 뷰

모든건 과제를 위한 벼락치기...

https://webgl2fundamentals.org/webgl/lessons/ko/

 

WebGL2 Fundamentals

Learn WebGL2 from the ground up. No magic

webgl2fundamentals.org

기초

GPU에서 실행되기 위해 두개 함수 쌍 형태의 코드를 제공

1. vertex shader : 점의 위치 계산

2. fragment shader : 점의 위치를 래스터화. 그려지고 있는 요소의 픽셀 색상 계산.

 

셰이더가 데이터를 받는 방법

1. Attribute, Buffer, Vertex Array : 버퍼에 위치, 색상, 좌표 등 바이너리로 저장. attribute에서는 버퍼에서 데이터를 가져오는 방법 명시. 

2. Uniform : 전역 변수

3. Texture : 버퍼와 다르게 무작이 접근이 가능한 배열. 주로 이미지 데이터 다룸.

4. Varying : vertex shader -> fragment shader 데이터 전달 방법

 

간단히 말하면 좌표, 색상을 정하고 이걸 WebGL에 제공하는 것.

 

↓ 셰이더를 문자열로 가져오기

var vertexShaderSource = `#version 300 es
 
// attribute는 정점 셰이더에 대한 입력(in)입니다.
// 버퍼로부터 데이터를 받습니다.
in vec4 a_position;
 
// 모든 셰이더는 main 함수를 가지고 있습니다.
void main() {
 
  // gl_Position은 정점 셰이더가 설정해 주어야 하는 내장 변수입니다.
  gl_Position = a_position;
}
`;
 
var fragmentShaderSource = `#version 300 es
 
// 프래그먼트 셰이더는 기본 정밀도를 가지고 있지 않으므로 선언을 해야합니다.
// highp는 기본값으로 적당합니다. "높은 정밀도(high precision)"를 의미합니다.
precision highp float;
 
// 프래그먼트 셰이더는 출력값을 선언 해야합니다.
out vec4 outColor;
 
void main() {
  // 붉은-보라색 상수로 출력값을 설정합니다.
  outColor = vec4(1, 0, 0.5, 1);
}
`;

↓ 셰이더를 만들고, 소스를 업로드하고 셰이더를 컴파일하는 createShader 함수를 선언!

function createShader(gl, type, source) {
  var shader = gl.createShader(type);	
  gl.shaderSource(shader, source);
  gl.compileShader(shader);
  var success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
  if (success) {
    return shader;
  }
 
  console.log(gl.getShaderInfoLog(shader));
  gl.deleteShader(shader);
}

↓ createShader 함수를 이용해 셰이더를 생성할 수 있게 되었다. source로 맨 앞에 작성한 것을 참조하는 것 확인 가능

var vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
var fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);

↓ 생성된 이 셰이더들을 프로그램으로 링크하는 함수 createProgram 선언한다.

function createProgram(gl, vertexShader, fragmentShader) {
  var program = gl.createProgram();
  gl.attachShader(program, vertexShader);
  gl.attachShader(program, fragmentShader);
  gl.linkProgram(program);
  var success = gl.getProgramParameter(program, gl.LINK_STATUS);
  if (success) {
    return program;
  }
 
  console.log(gl.getProgramInfoLog(program));
  gl.deleteProgram(program);
}

↓ 이를 이용해 program을 만들고 vertexShader와 fragmentShader를 Link한다.

var program = createProgram(gl, vertexShader, fragmentShader);

이 과정을 통해 GLSL Program을 GPU에 만드는 데에 성공했다!

다음으로 할 일은 이 program의 attribute를 입력하는 것.

이 예제에서 attribute는 a_position뿐임을 확인할 수 있다. 따라서 a_position location을 입력해줘야 한다.

var positionAttributeLocation = gl.getAttribLocation(program, "a_position");
var positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);

1. attributelocation을 찾기위함

2. attribute는 버퍼에서 데이터를 가져오므로 이 값을 받아올 버퍼를 생성

3. WebGL 식으로 리소스를 바인드하는 과정?? 몰라.. 이 과정을 고치면 bind point를 통해 버퍼를 참조할 수 있어서 비로소 데이터를 넣을 수 있게 된다고 함. 

// 세 개의 2d 점
var positions = [
  0, 0,
  0, 0.5,
  0.7, 0,
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);

1. position에 세 좌표의 위치를 입력

2. WebGL은 strict format data를 필요로 하기 때문에 값을 가공해줌.

position값을 32비트 부동소수점형 배열로 복사하는 과정

gl.bufferData가 이 가공된 배열을 GPU의 positionbuffer에 복사해주는 것.

위에서 보면 gl.ARRAY_BUFFER와 positionbuffer를 바인딩했기 때문에 결과적으로 positionBuffer에 복사된다는 것이다.

3. gl.STATIC_DRAW : WebGL에 이 데이터를 어떻게 사용할지 주는 힌트. 예를들어 해당 매개변수는 데이터 많이 변경 안해~ 라는 힌트다.

 

여기까지 해낸 것 : data를 program의 buffer에 넣어줌

이제 할 것 : program의 attribute가 이 데이터를 어떻게 가져와야하는지 알려주기

 

var vao = gl.createVertexArray();
gl.bindVertexArray(vao);
gl.enableVertexAttribArray(positionAttributeLocation);

1. attribute 상태 집합 생성

2. 이 새로만든 vao를 현재 사용중인 vertex array로 만듦

3. 이 vertex array에 attribute 설정. attribute를 켜서 우리 버퍼에서 데이터 가져올거라고 알려주는 것임.

var size = 2;          // iteration마다 두개 구성 요소 사용
var type = gl.FLOAT;   // 데이터는 32비트 부동 소수점
var normalize = false; // 데이터를 정규화하지 않음
var stride = 0;        // 0인 경우 실행할 때마다 `size * sizeof(type)`만큼 다음 위치로 이동합니다.
var offset = 0;        // 버퍼의 시작부터 데이터를 읽어옴
gl.vertexAttribPointer(
    positionAttributeLocation, size, type, normalize, stride, offset)

데이터를 어떻게 가져올지 명시. 

gl.vertexAttribPointer는 현재 ARRAY_BUFFER를 attribute(positionBuffer)에 바인딩한다.

 

캔버스 처리

webglUtils.resizeCanvasToDisplaySize(gl.canvas);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

// 캔버스 지우기
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);

html 구역에 캔버스 C를 만들었는데, WebGL에게 준 gl_Position을 화면 공간 상 좌표로 변환할 방법을 알려준다. 캔버스 비율에 따라 변환할 것이므로, gl.viewport를 호출하고 현재의 캔버스 크기를 넘긴다.

-1 ~ +1에 해당하는 값이 0 ~ 캔버스 너비/높이 에 해당하게 맵핑.

 

실행 요청

// 우리가 만든 프로그램(셰이더 쌍)을 사용할 것이라고 알려줍니다.
gl.useProgram(program);
// 원하는 attribute/버퍼 집합을 바인딩합니다.
gl.bindVertexArray(vao);

var primitiveType = gl.TRIANGLES;
var offset = 0;
var count = 3;
gl.drawArrays(primitiveType, offset, count);

WebGL에게 실행할 프로그램을 알려주고 attribute/buffer를 binding. 

TRIANGLES로 설정되었으므로 정점 셰이더가 실행될때마다 gl_Position에서 변환된 매핑을 기준으로 삼각형을 그릴 것.

count = 3에 따라 정점 셰이더는 3번 실행되며 attribute 설정에 따라 두개씩 구성요소를 가져오게 된다. 

 

-1~ +1 사이 값으로 지정보다 그래픽은 주로 픽셀 단위로 다뤄지기 때문에, vertex shader에서 해상도 값을 가져와 픽셀 값을 -1~+1 사이로 가공해줌으로써 픽셀로 작업을 할 수 있게 된다. 

 

정리하자면,

GPU는 정점 vertex을 클립공간의 정점으로 변환하고 이를 통해 프래그먼트 셰이더로 색상값을 받아가며 픽셀을 그리는 것(rasterize)이다. 

 

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/09   »
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
글 보관함