이전까지는 objects와 eye 좌표를 3차원에서 다루는 것에 집중했다면, eye로 봤을 때의 2D image로 나타내는 과정을 알아보겠습니다. camera가 eye frame et의 origin에 위치해있으며, eye coordinates의 negative z축을 향해 카메라가 바라보고 있다고 가정하겠습니다. 또한, eye coordinates의 point를 [xe,ye,ze,1]t로 표현합니다.
Pinhole Camera
가장 구현인 단순한 카메라는 pinhole camera입니다. 빛이 film plane(image plane)으로 올 때 대부분은 불투명한 표면 ze=0에 의해 막힙니다. 하지만 surface 센터(at the point with eye coordinates [0,0,0,1]t에 아주 작은 구멍을 뚫어 film plane에 light rays가 point를 지나 기록되게 합니다.
Flipping
이때 film plane이 뒤집히는 것을 방지하기 위해, pinhole 앞에 flim plane ze=−1이 위치하도록 합니다.
Basic Mathmatical Model
pinhole camera를 수학적으로 모델링하는 과정입니다. scene 내의 point p~를 eye coordinates [xe,ye,ze,1]t에 두겠습니다. p~로부터 온 ray가 film plane [xn,yn]t에 닿으면 xn=ze−xe,yn=ze−ye에 해당 포인트가 위치합니다. (두 닮은 삼각형의 삼각비로 간단하게 구해집니다. xn:xe=−1:ze의 비율입니다. y축도 동일합니다.)
−: don't care
위의 행렬이 projection matrix가 됩니다. 또한 행렬 곱의 raw output인 ⎣⎢⎢⎢⎡xeyeze1⎦⎥⎥⎥⎤은 clip coordinates가 됩니다. wn=wc는 새로운 변수가 되며, w-cooridnate로 불립니다.
normalized device coordinates
pinhole camera의 output coordinates를 normalized device coordinates라고 합니다. NDC는 image의 points를 pixels의 수와 상관 없이 표현합니다. 또한, 모든 이미지 데이터를 canonical space −1≤xn≤+1,−1≤yn≤+1에 저장한 후에, screen window에 매핑합니다.
Scales
우리는 또한, 초점 거리를 ze=n으로 증가시킬 수 있습니다. (lens를 zoom하는 것과 동일합니다.) 이때, normalized coordinates 좌표는 xn=zexen,yn=zeyen이 됩니다. (n은 어떤 음수 값입니다.)
homogeneous 좌표계의 성질입니다.
1. 두 선은 하나의 unique point에서만 만납니다.
- 해당 point는 infinite일 수 있습니다.
2. 두 점은 하나의 unique line을 결정합니다.
- 해당 line은 infinity에 놓여 있을 수 있습니다.
Depth
다음은, object A가 object B보다 앞에 있을 때, object B로부터의 빛이 A에 의해 막히는 물리적 현상을 카메라에서 모델링하는 과정입니다. 이번에 살펴볼 방법은, rasterization에서 사용되는 z-buffer입니다. 해당 방법은 각 픽셀의 frame buffer에 color 뿐만 아니라 current depth 정보를 함께 저장합니다. 각 삼각형을 그릴 때 depth 정보를 비교하여 rewriting합니다.
sx>0,sy>0
z-buffer를 고려하여 3D projective transformation을 표현하면 다음과 같습니다. 이때 znwn=zc=1이므로, z-buffer에 사용하는 값은 zn=ze−1입니다. 해당 z-buffer를 사용해도 되는 이유는 두 포인트의 각 ze값에 대한 부등식 ze2<ze1이 zn에서도 ze2−1<ze1−1와 같이 유지되기 때문입니다. 즉, projective 변환 적용 후에도 object의 depth 순서가 유지됩니다.
Projective transform
Projective transform은 지금까지 살펴본 것처럼, eye coordinates 포인트들을 normalized device coodinates로 옮기는 과정입니다. 하지만, linear하지도, affine하지도 않습니다.
위의 figure처럼, distances가 보존되지 않는 것을 확인하실 수 있습니다. 또한, film plane의 evenly spaced pixel이 geometry(3D space)에서는 evenly spaced pixel이 아니게 됩니다.
대신 Projective transform은 co-linearity와 co-planarity 성질을 가집니다. 추가적으로, NDC에서는 evenly spaces pixels이 유지됩니다. 즉, linear interpolation이 가능합니다.
Co-linearity
three or more points가 하나의 선에 놓여있을 때, 변환된 points도 어떤 하나의 선에 놓입니다.
∣(p2−p1)×(p1−p3)∣=0 -> 세 점이 같은 선에 놓여 있으므로, 두 선의 기울기는 일치.
Co-planarity
3D projective transform 적용 시, planar objects in 3D를 planar objects in 3D로 mapping
예를 들어, 한 triangle in 3D에 projective 변환 시, 삼각형의 zn 값은 (xn,yn)의 affine function입니다.
eye로부터 너무 멀리 있는 point ze=n의 경우, zn=−ze1의 값이 0으로 수렴해버립니다. 또한, 8bit의 값으로 ze를 표현하는 것이 불가능한 경우도 존재합니다. 따라서, α,β 값을 지정하여, near plane과 far plane을 지정합니다.
triangle의 3 vertices를 구하고 나면, 해당 vertices는 모두 vertex shader로 들어가게 됩니다. 이때 Rasterization은 1️⃣ triangle이 screen 어디에 위치해야 하는지, 2️⃣ 어떤 pixels가 삼각형 안에 위치해야 하는지, 3️⃣ varing variable data의 적절한 interpolated 값을 정하는 역할을 합니다. 해당 과정을 거친 후에, fragment shader는 final color를 정하는 역할을 수행합니다.
Shaders
Vertex shader
모든 vertex position의 object 좌표들을 얻습니다.
해당 좌표들을 eye coordinates와 vertex의 normal 좌표들로 반환합니다.
vNormal = vec3(uNormalMatrix * vec4(aNormal, 0.0));
// send position (eye coordinates) to fragment shader
vec4 tPosition = uModelViewMatrix * vec4(aPosition, 1.0);
vPosition = vec3(tPosition);
gl_Position = uProjMatrix * tPosition;
eye coordinates의 position 정보와 normal 정보를 vertex shader로부터 받습니다.
normal과 light가 Object로 들어오는 벡터의 내적으로 diffuse를 구합니다.
해당 fragment shader에서는 BRDF 중 diffuse만 구현되어 있습니다.
uColor는 3개의 정점으로부터 보간된 color 값입니다.
final color 값을 저장합니다.
FragColor의 네 번째 값은 opacity입니다.
Clipping
만약 vertex가 우리 뒤-eye 뒤에 위치-에 있을 경우, 해당 vertex를 사영하면 잘못된 지역이 window에 그려집니다. 이때 우리는 image의 bottom(figure에서 오렌지색 부분)의 값으로 interpolation해야 합니다. 즉, clipping은 viewing frustum 밖에 있는 triangle을 다루는 부분입니다. viewing frustum의 six faces를 통해 geometry를 slice up하여 문제를 해결합니다. six faces는 다음과 같습니다: near plane, far plane, image boundaries(left, right, top and bottom).
clip coordinates를 eye space로 설정하면, camera parameters를 필요로 하게 되고(비효율적) normalized device cooridnates로 설정하게 되면 wc로 나눌 때 해당 값이 0인 경우 문제가 발생합니다.
−wc<xc<wc−wc<yc<wc−wc<zc<wc
따라서, clip coodinates의 조건은 위와 같이 설정되고, Eye coordinates(projected) -> clip coordinates -> normalized device coordinates (NDCs)의 과정으로 픽셀이 표현됩니다.즉, clipping 이후에 wc=−ze로 나누어 NDC 값을 얻습니다.
Backface Culling
solid 물체를 그릴 때, 우리는 각 triangle의 앞면만 보게 됩니다. 따라서 삼각형의 세 정점을 그릴 때 피사체의 앞면을 보는 경우에 counterclockwise로 신호를 얻어 backface culling을 실행할 수 있습니다.
Math of Backface Culling
a=p~3−p~2
b=p~1−p~2
c=a×b
두 벡터의 외적인 c의 방향으로 피사체가 정면을 바라보는지 확인할 수 있습니다. 만약 세 정점이 counterclockwise인 경우 해당 벡터가 +zn방향에 놓이게 됩니다. (frontface)
해당 단계에서는 vertices를 window 안에 위치시키는 것입니다. [-1, -1] ~ [1, 1] 범위에 존재하는 NDC 값을 [-0.5, -0.5] ~ [W-0.5, H-0.5]에 위치시키는 것입니다. 따라서 viewport의 범위가 (0,0,W,H)가 됩니다.
세 번째 행 값은 near/far field를 고려하여 설정됩니다. NDC에서 -1(far)<z<1(near)인 점을 고려하여, window에서의 z는 0과 1 사이 값을 갖게 합니다.
Math of rasterization
위의 window coordinates에서의 세 정점으로부터 rasterizer는 어떤 픽셀 centers가 triangle 안에 있는지 찾아야 합니다. (per primitive) 삼각형의 각 변을 edge functions e=axw+byw+c로 두어, 세 edge function이 모두 positive일 때 pixel이 triangle 안에 있는 것으로 파악할 수 있습니다.
또한, rasterization의 Input으로 각 vertex는 임의의 데이터를 가집니다. 해당 데이터는 zw 값을 포함합니다. (varing variables과는 다릅니다.) vertex의 값을 삼각형 내에서 선형적으로 보간하는 것도 rasterizer의 역할입니다.
Varing Variables
varing variables는 vertex shader와 fragment shader 간의 interface를 제공합니다. primitives(triangles)가 모이고 fragments가 연산될 때, 각 fragment는 variables의 집합을 갖게 됩니다-해당 variables는 보간된 후에 fragment shader에 제공됩니다.
예를 들어, fragment에 제공되는 color 값은 3 vertices의 colors 값의 보간된 값입니다.
Barycentric Interpolation
triangle에서 point p를 보간하는 방법은 다음과 같습니다:
기존 보간법과 동일하지만, 넓이의 비율을 고려해야 합니다.
percent red = are of red / total area
다른 색상도 동일
따라서, p의 수식은 다음과 같이 정의됩니다-p=(A1x1+A2x2+A3x3)/A.
하지만 위의 보간 방법을 우리는 window coordinates에 바로 적용할 수 없습니다. projective 과정을 거친 좌표들은 (w로 나누어졌기 때문에) 비선형성 값을 갖기 때문입니다. 즉, 보간을 직접적으로 적용했을 때, 피사체를 왜곡하여 표현하게 됩니다.
3D Affine
하지만 우리는 (xo,yo,zo)의 3D 좌표를 갖는 triangle에 대해 보간해야 하며-보간 시 affine 특성을 유지해야 합니다.-texture coordinates [xt,yt]가 unique interpolant functions를 갖도록 해야 합니다.
이때 v(x,y,z)=ax+by+cz+d의 3D affine 함수를 flat triangle에서 사용하기 위해 z를 (x,y)에 대한 affine function으로 표현합니다.
최종적으로 wnv과 wn1이 normalized device coordinates의 affine functions이 되는 것을 확인하실 수 있습니다.
Overviews of Rasterization
each vertex에 대해 vertex shader가 clip coordinates를 계산하고, varying variables를 계산합니다.
clipping을 각 triangle에 적용합니다-새로운 정점들이 생성될 수 있습니다. linear interpolation이 clip coordinates에 적용됩니다.
각 vertex와 varying variable v에 대해 internal variable wnv과 wn1을 생성합니다.
xn=wcxc,yn=wcyc,zn=wczc와 같이 division이 계산됩니다. 즉, normalized device coordinates로 vertex를 옮깁니다.
각 vertex에 대해, NDC를 window coordinates로 변환합니다.
[xw,yW]좌표들을 triangle을 screen에 나타내는 데에 사용합니다.
triangle 안에 있는 픽셀들에 대해 (edge functions의 positive pixels) linear interpolation을 적용합니다. 해당 보간으로 zw,wnv,wn1의 보간된 값을 얻습니다.
각 픽셀에 대해 보간된 zw 값을 z-buffering에 사용합니다.
각 픽셀과 모든 varying variables에 대해 division을 수행하여 varying variable v=(wnv)/(wn1)를 얻습니다.
varying variable v를 fragment shader에 전송합니다.
Materials
다양한 물체들에 의해 빛이 반사되며, 이를 fragment shader에서 최대한 처리하는 것이 목표입니다. Uniform variabels를 통하여 light sources의 위치 값을 표현하여 fragment shader의 input으로 넣어주게 됩니다. 또한, Varing variables로 eye frame의 point와 normal 정보를 input으로 넣어줍니다. 두 material parameters로 fragment shader가 어떻게 빛이 물체에서 반사되는지 고려하여 image의 color를 정합니다.
실제로는 반사되는 여러 빛들도 고려해야 하지만, 해당 파트에서는 single point light source만 고려하겠습니다.
PVC blob
plastic에서의 light scattering을 고려하는 파트입니다. plastic에서는 빛이 bounce되는 부분에서 더 밝아지는 현상을 띕니다. (Figure에서 빨간 부분일 수록 밝은 부분입니다.)
mirror reflection과 동일하게 고려하여, bounce vector B(w)=2(w⋅n)n−w로 구할 수 있습니다.
1. 임의의 벡터 w를 unit normal vector n에 정사영합니다: (w⋅n)n
2. 해당 벡터를 두 배로 늘려줍니다.
3. −w만큼을 더하여 bounce vector를 구해줍니다.
Diffuse
diffuse 물체는 위에서 빛과 닿으면 더 밝고, grazing angle에서 빛이 닿으면 더 어둡습니다. 해당 특징을 고려하여 surface normal과 light vector 간의 cosθ=n⋅l을 diffuse 계수로 사용합니다.
Specularity
diffuse하지 않은 물체들도 존재합니다. 이때는 light의 bounce vector B(l)을 계산한 후에 v와의 각도를 계산하는 방법으로 color 값을 계산합니다.
Halfway vector
h=normalize(v+l)
이를 간단하게 구현하기 위해 halfway vector를 도입합니다. halfway vector와 normal과의 각도 cosϕ(h⋅n)을 계산하는 방법으로 계산됩니다.
Texture Mapping은 pixel의 color가 임의의 의미지 texture로부터 어떻게 형성되는지와 관련된 과정입니다. fragment shader를 통해 (xt,yt)의 texture coordinates로부터 각 triangle의 vertex와 연결시킵니다.
그림에서 볼 수 있듯이, triangle의 각 vertex가 x,y texture 좌표계에서 주어집니다. 해당 좌표값들이 varying variables로 interpolate되고, fragment shader에서 color를 가져오게 됩니다.
Bump Mapping
geometry 자체를 변환하는 대신에, surface normal을 변형하여 bumps를 구현하는 매핑 방법입니다. 직접 bumps를 폴리곤으로 그리는 것은 매우 어렵기 때문입니다.