컨텐츠 내 위젯


WEBGL - 변환 WEBGL & 3D

여기서는 위치에 따른 객체의 변환 방법을 사용하며 그걸 연산하기 위해 미리 어떤걸 공부해야 하는지 쓰도록 한다. 이번 예제 에서는 총 4가지의 항목을 변화시키도록 해 보겠다.

1. X 좌표.
webgl 에서는 기존 web의 px 좌표계(?)를 쓰지 않고 캔버스 뷰포트 별로 0 ~ 1 단위로 계산하는 방식으로 한다. 그리고 좌표의 증가방향은 좌 - > 우로 향한다.

2. Y 좌표.
X좌표와 마찬가지로 webgl 에서는 기존 web의 px 좌표계(?)를 쓰지 않고 캔버스 뷰포트 별로 0 ~ 1 단위로 계산하는 방식으로 한다. 그리고 좌표의 증가방향은 하 - > 상으로 향한다.

3. 스케일
CSS의 scale와 마찬가지로 1 부터 시작한다. 그보다 작아지면 축소, 커지면 확대 이다.

4. 회전
CSS의 rotate와 마찬가지이다. 우리가 일반적으로 쓰는 n도 표기를 rad로 변환하여야 한다. rad는 부채꼴의 호와 지름의 길이가 일치할 때의 내각을 의미한다. 자세한 표기와 변환법에 대한 해석을 보고 싶으면 아래 링크를 참조. https://darkpgmr.tistory.com/26

이 네가지 변환은 결국 각 버텍스(정점의) 위치가 변경된다는 공통점을 가지고 있다. 따라서 위 행위의 계산 결과를 각 정점에다가 적용함으로써 원하는 셰이더를 만들어낼 수 있다.

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  <script id="vertex-shader" type="x-shader/x-vertex">
// 점선면의 기하학적 위치를 셰이딩
// 문법적으로 vertex-shader 라는 C#의 클래스 라고 보자

// 문서에서 설명한 속성.. 나중에 WEBGL에서 이 내용을 참조할 것이다.
// float, vec2, vec3, vec4, mat2, mat3, mat4 의 타입을 사용할 수 있다.
attribute vec4 a_position;
attribute vec4 a_color;
varying vec4 my_color;
uniform mat4 u_matrix;


// 처음 로딩하고 생성할 때.
void main() {
// gl_Position은 특수기능을 하는 녀석이다.
gl_Position = u_matrix * a_position;
my_color = a_color;
}
</script>


이 코드를 보면 u_matrix 라고 있는데, 웬 갑자기 행열(matrix)이 나오는지 어리둥절 하는 분들도 계실 것이다. 4*4 행열의 특정 원소가 각 연산을 수행하기 위한 상수라고 보면 된다. 본인은 처음에 저 행열의 특정 원소가 스케일 등에 바로 사용된다고 생각했다가 몇일을 헤맸다(...) 다만 각 연산을 어떻게 수행하는길래 결과과 나오는지는 glsl 내부에서 수행한다는데 이에 대해서 자세히 알고 싶은 분은 다음 링크를 참조.  https://www.khronos.org/files/opengles_shading_language.pdf

이제 우리의 문제는 연산하고자 하는 경우를 복합적으로 어떻게 행열을 지정해 놓느냐는 건데, 이걸 좀 더 쉽게 이해하기 위해서는 행열에 대해 아는 것이 필요하다. 깊숙히 알 필요까진 없지만 최소한 행열의 곱셈까진 이해하는 것이 좋기에 하기 링크 부터 행열에 대한 이해를 하는 것이 필요하다. (본인은 4시간 정도 쭈욱 읽었다.) https://mathbang.net/557

일단 처음에는 아무런 변화도 없는 단위행렬로 시작한다. (곱해도 기존 값이 그대로 유지되는 행열). 여기에 이동 관련한 행열 변화를 실행해 보자.

 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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
/* window.m4의 일부 발췌 */
multiply: function (a, b) {
var a00 = a[0 * 4 + 0];
var a01 = a[0 * 4 + 1];
var a02 = a[0 * 4 + 2];
var a03 = a[0 * 4 + 3];
var a10 = a[1 * 4 + 0];
var a11 = a[1 * 4 + 1];
var a12 = a[1 * 4 + 2];
var a13 = a[1 * 4 + 3];
var a20 = a[2 * 4 + 0];
var a21 = a[2 * 4 + 1];
var a22 = a[2 * 4 + 2];
var a23 = a[2 * 4 + 3];
var a30 = a[3 * 4 + 0];
var a31 = a[3 * 4 + 1];
var a32 = a[3 * 4 + 2];
var a33 = a[3 * 4 + 3];
var b00 = b[0 * 4 + 0];
var b01 = b[0 * 4 + 1];
var b02 = b[0 * 4 + 2];
var b03 = b[0 * 4 + 3];
var b10 = b[1 * 4 + 0];
var b11 = b[1 * 4 + 1];
var b12 = b[1 * 4 + 2];
var b13 = b[1 * 4 + 3];
var b20 = b[2 * 4 + 0];
var b21 = b[2 * 4 + 1];
var b22 = b[2 * 4 + 2];
var b23 = b[2 * 4 + 3];
var b30 = b[3 * 4 + 0];
var b31 = b[3 * 4 + 1];
var b32 = b[3 * 4 + 2];
var b33 = b[3 * 4 + 3];
return [
b00 * a00 + b01 * a10 + b02 * a20 + b03 * a30,
b00 * a01 + b01 * a11 + b02 * a21 + b03 * a31,
b00 * a02 + b01 * a12 + b02 * a22 + b03 * a32,
b00 * a03 + b01 * a13 + b02 * a23 + b03 * a33,
b10 * a00 + b11 * a10 + b12 * a20 + b13 * a30,
b10 * a01 + b11 * a11 + b12 * a21 + b13 * a31,
b10 * a02 + b11 * a12 + b12 * a22 + b13 * a32,
b10 * a03 + b11 * a13 + b12 * a23 + b13 * a33,
b20 * a00 + b21 * a10 + b22 * a20 + b23 * a30,
b20 * a01 + b21 * a11 + b22 * a21 + b23 * a31,
b20 * a02 + b21 * a12 + b22 * a22 + b23 * a32,
b20 * a03 + b21 * a13 + b22 * a23 + b23 * a33,
b30 * a00 + b31 * a10 + b32 * a20 + b33 * a30,
b30 * a01 + b31 * a11 + b32 * a21 + b33 * a31,
b30 * a02 + b31 * a12 + b32 * a22 + b33 * a32,
b30 * a03 + b31 * a13 + b32 * a23 + b33 * a33,
];
}
// 단위 행렬에서 움직이고자 한 값만 변경.
translation: function (tx, ty, tz) {
return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, tx, ty, tz, 1];
}
// 기존의 행열과 변경한 단위행렬(?)을 곱
translate: function (m, tx, ty, tz) {
return m4.multiply(m, m4.translation(tx, ty, tz));
}
// 기존의 행열과 변경한 단위행렬(?)을 곱
xRotate: function (m, angleInRadians) {
return m4.multiply(m, m4.xRotation(angleInRadians));
}
// 기존의 행열과 변경한 단위행렬(?)을 곱
scale: function (m, sx, sy, sz) {
return m4.multiply(m, m4.scaling(sx, sy, sz));
}
/* 일부 발췌 */

var matrix = window.m4.translate(
[1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
translation[0],
translation[1],
translation[2]
);

matrix = window.m4.xRotate(matrix, rotation[0]);
matrix = window.m4.yRotate(matrix, rotation[1]);
matrix = window.m4.zRotate(matrix, rotation[2]);
matrix = window.m4.scale(matrix, scale[0], scale[1], scale[2]);

gl.uniformMatrix4fv(matrixUniformLocation, false, matrix);

위치변화에 관련한 부분은 72 ~ 77 라인이다. 위의 예제를 보고 유추할 수 있는 것은 모든 변환의 경우 단위행열에 변환하고자 하는 값만 지정하고 다른 변환과 연동하기 위해 그를 곱한다는 것이다. 변환하고자 하는 내용과 행열 원소의 위치를 대번에 파악 하기 위해 이미지로 설명을 대체한다. 


sx, sy의 경우 위치 이동의 변형판이라 생각하면 된다. 다만 여기서 어려운 것은 회전인데, 이 회전이 어떻게 이뤄지는 지에 대해서는 삼각함수에 대한 지식이 조금 있어야 한다. https://mathbang.net/498

삼각함수와 행열에 대한 지식은 다음글에 또 다시 정리하는 시간을 가지도록 할 것이다. 회전의 경우는 결국 삼각함수(코싸인 탄젠트..)등을 이용해 기준점으로부터 벗어난 점A의 다음 이동 위치를 알아낸다는 것이다.

이렇게 정리(?)한 함수를 uniformMatrix4fv 를 통해 다시 쉐이더의 속성에 넣어주면 된다.

예제파일 다운로드 step06.zip

덧글

댓글 입력 영역