티스토리 뷰
모든건 과제를 위한 벼락치기...
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)이다.
'■ 기타 공부들 > ◻ Unity' 카테고리의 다른 글
씬 간의 이동과 플레이어 로드 (0) | 2018.09.13 |
---|---|
LitJson을 이용한 데이터 로드/저장 (+ 안드로이드) (1) | 2018.09.04 |
스크립트의 유기적 관계 (0) | 2018.08.02 |
[Unity2D]2D 애니메이션 적용 과정과 스크립트 (0) | 2018.07.27 |
[Unity2D]정해진 거리만큼을 매끄럽게 이동하기 (0) | 2018.07.27 |