오돌토돌한 표면(Bumpy Surface)
실세계의 많은 물체는 오돌토돌한 표면을 지닌다.
(a)는 천연석으로 포장된 보도 표면에 대한 폴리곤 메시다. (b)의 텍스처가 입혀져 (c)를 얻었다.
(d)는 이를 가까이 본 것인데, 세 개의 점 $a$, $b$, $c$에서의 디퓨즈 반사를 보면, 이는 노멀 $n$과 빛 벡터 $l$에 의해 결정된다.
b의 경우 $n$과 $l$사이의 각도가 작아서 빛을 많이 받는다. 따라서 밝게 보인다. 하지만, $a$와 $c$는 $n$과 $l$ 사이 각도가 커서 빛을 적게 받고 어두워 보인다.
표면 전체에 걸쳐 노멀이 불규칙하게 변하므로, 표면 밝기도 불규칙하게 변해서 오돌토돌한 표면을 잘 표현해 준다.
유일한 단점은 폴리곤 메시의 해상도가 너무 높아서 처리에 많은 시간이 소요되는 것이다.
빠른 연산을 위하여 삼각형 두 개로 구성된 저해상도 메시를 이용하면 오돌토돌한 표면을 잘 표현할 수 없다.
(d)를 보면 고해상도에서의 (d)에 비해 표면 전체에 걸쳐 노멀이 일정하게 유지된다.
여기서 $n$와 $l$ 사이의 각도는 서서히 작아져서 점차 밝게 보이는 문제가 생긴다.
이 문제를 해결하기 위해 고해상도 메시의 노멀을 미리 계산하고, 이를 노멀맵(normal map)이라고 하는 특수한 텍스처에 저장한 후, 런타임에는 저해상도 메시를 처리하되 노멀맵으로부터 노멀을 읽어서 이를 라이팅에 사용하는 특수한 텍스처링 기법을 노멀 매핑(normal mapping) 혹은 범프 매핑(bump mapping)이라 부른다.
14.1 하이트맵
표면을 하이트 필드(height field)로 표현할 수 있다. 이는 2차원 좌표(x, y)가 주어졌을 때, 높이(height) 혹은 z값을 반환하는 함수 h(x ,y)이다.
(a)는 일정한 간격의 (x, y) 좌표에서 샘플된 하이트 필드이다.
(b)는 이러한 높이 값을 저장한 2차원 텍스처인 하이트맵(height map)이다.
높이 값을 회색조(gray-scale) 색상으로 해석하면 하이트맵은 회색조 이미지로 그릴 수 있다.
예를 들어, 높이가 [0, 255] 범위에 있을 때, 0은 검은색, 255는 흰색으로 표현된다.
하이트맵에서 색상이 비교적 일정한 부분은 상대적으로 매끈한 영역을 나타내고, 불규칙한 부분은 오돌토돌한 영역을 나타낸다.
고주파 메시에서 말고도 이미지 텍스처로부터 그래픽스 패키지를 이용해 하이트맵을 만들 수 있다.
예를 들어, 이미지 텍스처 (a)의 RGB 값을 회색조로 바꾸고 평균내서 (b)를 구할 수 있다.
이미지 텍스처를 이용해 생성된 하이트맵은 원본 텍스처와 동일한 해상도를 갖는다.
참고
하이트맵과 지형 렌더링
하이트맵은 지형(terraion)을 표현하는 대표적인 기법이며, 물과 같은 유체의 표면을 표현하는 데에도 많이 사용된다.
(a)는 산악 지형을 표현하는 회색조의 하이트맵이다. (b)는 이를 위한 이미지 텍스처이다.
(c)는 하이트 필드를 구한 것이고, (d)는 (a)의 하이트맵으로부터 생성된 삼각형 메시의 일부다.
(e)는 (b)를 입혀서 완성된 텍스처링된 삼각형 메시이다.
14.2 노멀맵
노멀맵을 만드는 방법 중 하나는 하이트맵을 사용하는 것이다.
h(x, y)는 하이트 필드 함수를 의미한다.
$\left(2,0,\delta h_x\right)$와 $\left(0,2,\delta h_y\right)$는 $\left(x, y, h\left(x, y \right)\right)$에서의 탄젠트 백터로 생각할 수 있는데, 이 둘의 벡터곱 $\left(-2\delta h_x, -2\delta h_y, 4\right)$를 $\left(x, y, h\left( x, y \right) \right)$의 노멀로 취하면 된다. 이를 정규화하여 노멀맵에 저장하면 위 사진에서 우측과 사진과 같다.
이렇게 계산된 노멀은 하이트 필드의 바깥을 가리키며 $\left(x,y,h\left(x,y \right) \right)$ 주변의 표면 경사도를 반영한다.
일반적으로 노멀맵은 하이트맵과 동일한 해상도를 가진다.
정규화된 노멀 $\left( n_x, n_y, n_z \right)$의 각 좌표는 모두 [-1, 1] 범위의 실수값이다.
반면, 노멀을 저장할 텍스처의 RGB 채널은 모두 [0, 1] 범위에 있다. 따라서 위 사진의 수식 같은 범위 전환이 필요하다.
완벽하게 평평한 지면이 있다면, 모든 점에서의 높이는 같다. 이 지면의 하이트맵으로부터 생성한 노멀맵의 텍셀에는 모두 (0, 0, 1)이 저장되어 있을 것이다.
오돌토돌한 지면으로부터 노멀맵을 만들더라도, 그 지면 중 급경사가 있지 않다면, 대부분의 영역에서 $\left( n_x, n_y, n_z \right)$는 (0, 0, 1)과 많이 차이가 나지 않을 것이다.
따라서 위 사진의 좌측 노멀맵처럼 (0, 0, 1)을 조금씩 '흔들어 놓은' 노멀들의 집합으로 이해할 수 있다.
따라서 $n_z$가 $n_x$, $n_y$에 비해 상대적으로 클 것이고, RGB 채널로 변환하면 B 값이 가장 크게 된다.
결국 노멀맵을 이미지 텍스처로 취급하여 그리게 되면 위 사진의 우측 노멀맵처럼 전체적으로 파란색을 띄게 된다.
14.3 노멀 매핑 쉐이더
기초 표면(base surface)
- 노멀 매핑에 입력으로 제공되는 폴리곤 메시
기초 표면은 레스터라이저에 의해 프래그먼트로 분해되고, 각 프래그먼트의 라이팅을 담당하는 프래그먼트 쉐이더는 노멀맵으로부터 노멀을 얻는다.
디퓨즈 항을 생각했을 때, $m_d$는 이미지 텍스처에서 가져오고, $n$은 노멀 맵에서 가져오게 된다.
노멀 매핑을 위한 정점 쉐이더
퐁 라이팅을 위한 정점 쉐이더 예제 코드를 수정한 것이다. 정점 노멀이 없는데, 추후에 노멀맵에서 가져오기 때문이다.
이전과 마찬가지로 texCoord는 v_texCoord에 그대로 복사된다.
프래그먼트 쉐이더는 보간된 텍스처 좌표 v_texCoord를 입력으로 받아서, 이를 이용해 노멀맵으로부터 노멀을 얻어 라이팅에 사용한다.
노멀 매핑을 위한 프래그먼트 쉐이더
퐁 라이팅을 위한 프래그먼트 쉐이더 예제 코들르 수정한 것이다.
이미지 텍스처(colorMap) 말고도 노멀맵(normalMap)이 정의되었다.
v_texCoord를 사용해 필터링되는데, 내장 함수 texture는 RGB 색상을 출력한다. RGB 각 채널은 [0, 1] 범위에 있으므로, [-1, 1] 범위로 전환해야 한다. 정규화된 노멀을 RGB 채널로 전환하던 것을 되돌리는 것과 같다.
노멀맵이 겹선형보간을 통해 필터링되면 위에서 얻은 $\left( n_x, n_y, n_z \right)$는 대체로 그 길이가 1이 아니다. 따라서 normalize를 호출해서 디퓨즈 항 계산 및 스페큘러 항의 반사 벡터 계산에 사용한다.
위 사진의 예에서 사각형이 좌표계의 $xy$ 평면에 놓여있다고 가정한다.
노멀 매핑을 사용하지 않을 경우, 모든 플래그먼트의 노멀은 $z$축을 향하는 단위 벡터인 (0, 0, 1)로 고정된다.
반면, 노멀 매핑을 사용하는 경우, 프래그먼트의 노멀은 모두 노멀맵에서 읽어오는데, 이 노멀은 (0, 0, 1)을 조금씩 '흔들어 놓은' 벡터일 것이다.
따라서 인접한 프래그먼트여도 다른 방향의 노멀을 가질 수 있고, 이에 따라 쉐이딩 결과는 매우 불규칙하게 된다.
여기서 유의할 점은 노멀 매핑은 기초 표면 자체를 오돌토돌하게 바꾸는 것이 아닌, 노멀을 사용하여 오돌토돌한 표면을 '흉내' 내는 것이다. 위 사진에서 고해상도 메시를 이용한 것과 달리 전체 사각형의 테두리가 일직선임을 볼 수 있다. 이 같은 한계는 극복하지 못한다.
참고
하이트 필드와 지형 렌더링
위 사진은 하이트 필드의 단면을 보여준다.
기초 표면의 프래그먼트 $f$를 라이팅 할 때, 하이트 필드 상의 점 $p$에서 계산된 $n_p$를 사용한다.
만약 렌더링을 위해 이 하이트 필들르 직접 사용한다면 카메라가 실제 보는 점은 $q$이므로, 라이팅에 $n_q$가 사용된다.
노멀 매핑 결과가 정확하지 않을 수 있음을 보여준다.
하지만, 노멀 매핑은 위 사진처럼 기초 표면의 면적에 비해서 하이트 필드의 높이 값이 아주 작다고 가정한다.
따라서 $q$의 노멀 $n_q$ 대신, $q$에 아주 가까이 있는 노멀 $n_p$를 사용해도 사람들은 그 차이를 인지하지 못한다.
14.4 탄젠트 공간 노멀 매핑
텍스처링은 물체 표면에 벽지 바르듯이 텍스처를 입히는 것으로 이해하면 된다.
위 그림의 좌측은 노멀맵에 저장된 노멀을 옆에서 본 것이고, 우측은 이 노멀맵을 여러 종류의 표면에 입힌 것이다.
노멀맵이 물체 표면에 벽지 바르듯이 입혀졌다.
이와 같은 노멀 매핑을 위해서는 이미지 텍스처링에서는 불필요했던 한 가지 작업을 더 수행해야 한다.
14.4.1 탄젠트 공간 노멀
물체의 표면의 한 점에서의 노멀을 $N$으로 표기하자.
$N$에 수직인 접면(tangent plane)에는 두 개의 서로 수직인 단위 벡터 $T$와 $B$를 정의할 수 있다(T는 tangent, B는 bitangent 의미).
위 그림 (a)에서 $\left\{ T,B,N \right\}$의 두 가지 예를 보여주는데, 한 점에서의 $\left\{ T,B,N \right\}$은 해당 점과 함께 탄젠트 공간(tangent space)을 구성한다. 표면의 각 점은 자신만의 탄젠트 공간을 갖는다.
위 그림 (b)에서 $p$의 텍스처 좌표 $\left( s_p, t_p \right)$를 이용해 노멀맵에서 가져온 노멀을 $n\left(s_p,t_p\right)$로 표기했다.
노멀 매핑을 사용하지 않았다면, 라이팅에 $N_p$를 사용했겠지만, $N_p$ 대신 $n\left(s_p, t_p \right)$를 사용한다.
$N_p$는 $p$의 탄젠트 공간에서 (0, 0, 1)이고, $n\left(s_p, t_p \right)$는 $N_p$를 약간 흔들어 놓은 것으로 해석할 수 있다. 이 같은 해석은 $n\left(s_p, t_p \right)$를 $p$의 탄젠트 공간에서 정의된 벡터로 보아야 가능하다.
즉, 물체 표면의 어떤 점에 노멀 매핑을 적용하건, 노멀맵에서 읽어온 노멀은 바로 그 점의 탄젠트 공간에서 정의된 벡터로 보면 된다. 이런 관점에서 탄젠트 공간 노멀맵이라는 용어를 사용한다.
14.4.2 탄젠트 공간 노멀 매핑 쉐이더
노멀 매핑을 위한 프래그먼트 쉐이더에서 dot(noraml, light)를 호출했었다. 이는 우연히 성공했을 뿐이다.
normal은 탄젠트 공간 벡터인 반면 light는 월드 공간 벡터이다.
서로 다른 공간에서 정의된 벡터들에 대해 내적 연산을 수행할 수는 없다.
우연히 성공한 이유는 기초 표면인 사각형 전체에 걸쳐 탄젠트 공간 기저(basis)가 월드 공간 기저와 같았기 때문이다.
만약 기초 표면이 기울어지면, 탄젠트 공간과 월드 공간의 기저는 달라지고 dot(normal, light)는 예상치 못한 렌더링 결과를 산출할 것이다.
해결법
- normal을 월드 공간으로 변환하기
- light를 탄젠트 공간으로 변환하기
책에서는 두 번째 방법을 택한다.
물체 표면의 각 점은 자신만의 탄젠트 공간 기저를 가질 수 있다.
따라서 light를 탄젠트 공간으로 변환하는 행렬은 프래그먼트마다 새로 계산되어야 한다.
위 사진처럼 폴리곤 메시의 정점은 자신의 노멀을 가지고 있다. 이를 $N$으로 취하고 여기에 $T$와 $B$를 더하면 정점별 $\left\{ T, B, N \right\}$을 정의할 수 있다.
$\left\{ T, B, N \right\}$은 전처리 단계에서 정점별로 계산되고, 정점 배열에 저장되어 정점 쉐이더로 넘겨진다.
$T$, $B$, $N$은 오브젝트 공간에서 정의된 벡터들이다.
정점 쉐이더는 먼저 월드 변환을 적용하여 이들을 월드 공간으로 옮긴다. 그 다음, 월드 공간 벡터 $T$, $B$, $N$을 행으로 가지는 3 x 3 행렬을 구성한다.
위 행렬은 월드 공간 벡터를 탄젠트 공간으로 변환하는 기저 이전 행렬이다.
탄젠트 공간 노멀 매핑을 위한 정점 쉐이더
$T$, $B$, $N$ 중 $B$를 애트리뷰트로 제공하지 않은 이유는 정점 배열이 커지기 때문이다.
CPU-GPU 간 데이터 전송을 최소화하려면 정점 배열의 크기를 줄이는 것이 좋다.
따라서 $T$, $N$을 이용해 $B$를 계산해서 구한다.
$T$, $B$, $N$으로 행렬을 구성하고, 이는 유니폼으로 제공된 월드 공간의 빛 벡터 lightDir을 탄젠트 공간으로 변환한다.
라이팅 계산에는 노멀과 빛 벡터 말고도 시선 벡터가 필요한데, 역시 탄젠트 공간으로 변환된다.
normal은 $M_{TBN}$을 구성하는 데만 사용되고, 레스터라이저로 출력되지 않는다. 노멀은 나중에 프래그먼트 쉐이더가 노멀맵으로부터 얻어올 것이기 때문이다.
반면, v_lightTS와 v_viewTS는 래스터라이저로 출력되어 보간된 후 프래그먼트 쉐이더에게 제공된다.
탄젠트 공간 노멀 매핑을 위한 프래그먼트 쉐이더
최종적으로 구해진 light, view, normal을 함께 라이팅 계산에 사용할 수 있어서 아래 사진과 같이 다양한 물체에 적용될 수 있다.
14.4.3 탄젠트 공간 계산
폴리곤 메시의 정점마다 $\left\{ T, B, N \right\}$을 계산해야 한다. 엄밀히 말하면, 정점 노멀 $N$이 주어졌을 때, $T$와 $B$를 계산해야 한다.
위 그림 (a)의 삼각형 $<p_0,p_1,_p2>$를 보면, 각 정점 $p_i$는 이미지 텍스처링과 노멀 매핑에 공히 사용될 텍스처 좌표 $\left( s_i, t_i \right)$를 가지고 있다.
위 그림 (b)는 이미지 텍스처의 파라미터 공간에서 $\left( s_i, t_i \right)$를 보여준다.
이 공간의 $s$축은 노멀맵의 $T$축과 일치한다. 마찬가지로 $t$축은 노멀맵의 $B$축과 일치한다.
세 정점에 할당된 텍스처 좌표 $\left( s_i, t_i \right)$를 분석해서 $T$와 $B$축의 방향을 계산한다.
위 그림 (c)는 삼각형 $<p_0,p_1,_p2>$와 $T$와 $B$축을 보여주는데, 여기에서 $T$와 $B$축은 아직 결정되지 않은 상태다.
$p_0$와 $p_1$을 연결하는 벡터를 $q_1$, $p_0$와 $p_2$을 연결하는 벡터를 $q_2$로 표기하면 다음과 같이 정의된다.
$q_1 = \left( s_1 - s_0 \right)T + \left( t_1 - t_0 \right)B$
$q_2 = \left( s_2 - s_0 \right)T + \left( t_2 - t_0 \right)B$
$\left( s_1 - s_0 \right)$, $\left( t_1 - t_0 \right)$, $\left( s_2 - s_0 \right)$, $\left( t_2 - t_0 \right)$를 각각 $s_{10}$, $t_{10}$, $s_{20}$, $t_{20}$으로 표기하면 다음과 같이 고쳐 쓸 수 있다.
$q_1 = s_{10}T + t_{10}B$
$q_2 = s_{20}T + t_{20}B$
$q_1$, $q_2$, $T$, $B$는 모두 3차원 벡터이므로, 총 여섯 개의 미지수와 여섯 개의 방정식을 가지고 있으므로, $T$와 $B$를 계산할 수 있다.
위 수식을 이용해 $p_0$의 $T$와 $B$를 계산했지만, 이는 삼각형 $<p_0,p_1,p_2>$만 고려해서 얻은 것이다.
여러 개의 삼각형이 $p_0$를 공유하므로, 각 삼각형마다 $T$와 $B$를 따로 계산해야 한다.
이렇게 해서 얻은 모든 $T$들의 합을 $T'$로, 모든 $B$들의 합을 $B'$라고 하자.
$T'$, $B'$는 단위 벡터도 아니고 서로 수직도 아닐 것이다. 또한, 이들은 $N$과도 수직이 아닐 것이다.
이러한 $\left\{ T', B', N' \right\}$에 그람-슈미트(Gram-Schmidt) 알고리즘을 적용하면 직교정규 기저를 얻을 수 있고, 이것이 $p_0$에서의 탄젠트 공간의 기저가 된다.
14.5 노멀맵 제작
평면이나 곡률이 낮은 곡면에 입혀질 노멀배은 2차원 그래픽스 패키지를 사용해 만들 수 있다.
하지만, 복잡한 곡면을 위한 노멀맵은 2차원 그래픽스 패키지로 제작하기 어려우므로, 지브러쉬(ZBrush)같은 조각 도구(sculpting tool)를 사용한다.
지브러쉬는 폴리곤 메시의 전체 혹은 일부를 쉽게 변경시킬 수 있다. 아래 사진은 저해상도 모델을 고해상도 모델로 바꾸는 과정이다.
저해상도 모델은 노멀 매핑을 위한 기초 표면(base surface)으로 사용된다.
오돌토돌한 표면을 가진 고해상도 모델은 참조 표면(reference surface)이라 부른다.
위 사진은 기초 표면과 참조 표면을 중첩해서 보여준다(참조 표면의 폴리곤 메시는 보라색으로 그려졌다).
전처리 과정에서 지브러쉬는 이 두 면을 사용하여 자동으로 노멀맵을 만들고, 런타임에서는 노멀 매핑을 통해 이 노멀맵이 기초 표면에 입혀진다.
지브러쉬의 노멀맵 생성 과정
우선 기초 표면을 파라미터화(parameterization) 한다. 이 결과 기초 표면의 모든 정점은 정규화된 텍스처 좌표 $\left( s, t \right)$를 갖게 된다.
만들고자 하는 노멀맵의 해상도가 $r_x \times r_y$라 하면, 좌표 $s$와 $t$에 각각 $r_x$와 $r_y$를 곱해서 새로운 좌표 $\left( r_{x}s, r_{y}t \right)$를 정의한다.
위 그림은 파라미터화된 후 $r_x \times r_y$ 크기를 가지게 된 기초 표면을 보여준다.
기초 표면의 각 삼각형은 스캔 전환(scan conversion)되어 다수의 텍셀을 포함하게 된다.
각 텍셀에 노멀을 할당할 것이다. 그러면 해상도가 $r_x \times r_y$인 노멀맵이 완성된다.
기초 표면은 원래 3차원 폴리곤 메시이므로 각 정점에는 노멀 정보가 저장되어 있다. 이러한 정점 노멀은 스캔 전환 과정에서 보간되어 텍셀들에 '임시로' 할당된다. 위 그림 중 스캔 전환 부분에서 세 정점 노멀은 $n_0$, $n_1$, $n_2$로, $T$에 임시로 할당된 노멀은 $n_T$로 표기되었다.
그림(c)는 중첩된 상태의 기초 표면과 참조 표면의 단면을 보여준다(그림에서는 의도적을 분리해서 그렸지만 실제로는 겹쳐 있다).
기초 표면에서 굵게 그린 삼각형은 $<v_0, v_1, v_2>$에 해당하는 것이고, $p$는 텍셀 $T$에 해당한다.
그림(b)의 2차원 삼각형에 대한 $T$의 무게중심 좌표(barycentric coordinates)를 계산하여, 그림(c)의 3차원 삼각형에 그대로 적용하면 $p$의 3차원 좌표가 계산된다.
노멀 $n_T$방향으로 $p$에서 광선(ray)를 발사한다.
참조 표면의 삼각형과 교차하는데, 이 교차점이 참조 표면 삼각형에 대해 가지는 무게 중심 좌표를 계산한다.
이 무게중심 좌표를 이용하여 그림 (d)에 보인 것처럼 참조 표면 삼각형의 정점 노멀을 보간한다.
보간된 노멀 $n$이 참조 표면 교차점에서의 표면 노멀로 채택되어 그림(b)의 텍셀 $T$에 저장된다. 이는 나중에 텍스처 좌표 $\left( s, t \right)$를 사용해 읽혀진다.
위 계산들은 모두 오브젝트 공간에서 노멀을 계산한 것이다. 이러한 노멀을 저장한 텍스처를 오브젝트 공간 노멀맵이라 한다. 그림(e)는 RGB 색상으로 가시화 된 것이다. 참조 표면의 표면 노멀은 대체로 온갖 방향으로 다양하게 분포되어 있어서 특정 색상이 지배하지 않는다.
오브젝트 공간의 노멀을 탄젠트 공간으로 변환해야 한다. 그림 (c)에서 $p$가 놓인 삼각형의 각 정점에 대해 탄젠트 공간의 기저 $\left\{ T, B, N \right\}$을 계산한 후, $p$의 무게중심 좌표를 사용하여 정점별 기저를 보간하면 $p$의 탄젠트 공간 기저를 얻을 수 있다. 이렇게 얻은 $p$의 $T$, $B$, $N$으로 행렬을 구성하면, 오브젝트 공간 노멀 $n$을 탄젠트 공간으로 변환하는 기저 이전 행렬이 된다.
이 행렬을 적용해 생성된 탄젠트 공간 노멀맵은 그림 (f)이다. 대부분의 탄젠트 공간 노멀배과 마찬가지로 파란색 계열이 지배적인 색상이 된다.
참고
폴리곤 메시 편집
지브러시는 거꾸로 고해상도 메시를 간략하게 만드는데 쓰이기도 한다.
실세계 물체의 복잡한 표면을 디지털화하기 위해서 종종 3차원 스캐닝(3D scanning) 기술을 사용하는데, 대부분 3차원 스캐닝을 통해 얻은 정점의 개수는 매우 많다.
얻은 정점들을 삼각형으로 이으면 위 사진의 (a)처럼 고해상도 메시를 얻는다.
정점의 수가 너무 많을 경우 편집하기도 어렵고 실시간 렌더링에도 부담을 준다. 이런 경우, 지브러쉬를 사용해 자동으로 혹은 반자동으로 메시 해상도를 낮출 수 있다. 저해상도 메시는 편집하기 쉽다.
(b)는 해상도를 낮춘 것을 보여주고, (c)는 3ds Max로 편집한 결과, (d)는 (c)에 이미지 텍스처를 입힌 것이다.
출처
[OpenGL ES를 이용한 3차원 컴퓨터 그래픽스 입문]을 보고 공부하고 정리한 내용
'공부 > 컴퓨터 그래픽스' 카테고리의 다른 글
OpenGL ES를 이용한 3차원 컴퓨터 그래픽스 입문 Chapter 16 전역 조명과 텍스처 (0) | 2024.07.19 |
---|---|
OpenGL ES를 이용한 3차원 컴퓨터 그래픽스 입문 Chapter 15 쉐도우 매핑 (0) | 2024.07.19 |
OpenGL ES를 이용한 3차원 컴퓨터 그래픽스 입문 Chapter 13 캐릭터 애니메이션 (0) | 2024.07.19 |
OpenGL ES를 이용한 3차원 컴퓨터 그래픽스 입문 Chapter 12 스크린 물체 조작 (0) | 2024.07.12 |
OpenGL ES를 이용한 3차원 컴퓨터 그래픽스 입문 Chapter 11 오일러 변환 및 쿼터니언 (0) | 2024.07.12 |