2. Graphic Systems

햄스터·2025년 6월 10일

ComputerGraphics

목록 보기
2/11

Image Formation

그래픽스에선 이미지를 형성할 때 하나의 '점'이 Image Plane에서 어디에 투영되는지 정하는 것을 중요하게 여깁니다.

보통은 카메라를 상정합니다.
따라서 카메라로 찰칵 찍었을 때 어떤 점이 어디에 위치할 지를 정하는 것이 Image Formation의 Geometry가 하는 역할입니다.

화면에 물체를 표시할 때, 물체의 모양을 표시하고, 재질을 표시합니다.
우리는 3D 세상의 물체를 2D 화면에 투영시킬 때,
특정 점의 밝기가 어떻게 되는지를 알고 싶어 합니다.

그걸 illuminationsurface property를 이용해 조절합니다.

이런 일련의 simulation, light과 surface를 자르는 simulation을 rendering이라고 합니다.

rendering을 완료하면, 디스플레이에 보낼 이미지가 Frame Buffer에 쌓이게 됩니다.

보통 rendering의 목표는 photorealism을 추구합니다.

사진과 같이 그리는게 목표라는거죠.

Computer Graphics에선 물리적인 과정을 통해 Modeling / Simulation을 진행합니다.

사람 눈과 비슷하게 보이는 것을 추구하기 때문에, 물리를 시뮬레이션 해서 이미지를 만들어 냅니다.

Single Light Source에서 나온 빛이, 우선 표면에 반사가 됩니다.
이 때 Surface와 직교하는 Surface Normal이 존재하고,

Surface에 들어오는 빛의 wavelength에 따라 반사량을 다르게 구현하는
Surface Reflectance가 존재합니다.

그렇게 반사되어 나온 빛이 Radiance,
카메라 센서쪽에 들어가는 빛이 Irradiance이고,
카메라에 흡수되게 됩니다.

Three elements of Image Formation

Image formation에는 Light Source, Object, Camera의 3가지 요소가 중요합니다.

광원이 좀 크다고 여겨질 경우에는 광원 내에 랜덤하게 Light Source들을 샘플링하여,
렌더링 과정을 여러번 가집니다.

그리고 카메라는 보통 1개지만, 뭐 눈 2개를 모델링하고 싶으면 2개를 사용해도 됩니다.

Light Source

(Visible) Light는, 우리 인간 시각 시스템에 반응을 일으키는 Electromagnetic Spectrum의 일부라고 정의합니다.

그래픽스에선 광자를 쏴서, 그 광자가 이동하는 것을 simulate한다고 생각하면 됩니다.

일반적으로 color를 모델링할 때에는 R,G,B로 모델링합니다.

우리가 Graphics에서 빛을 그리는 데에 실제로 필요한 옵션들은,
Direction, Position, colors 가 필요합니다.

보통 direction과 position은 같이 쓰이구요.
colors는 보통 백색광을 사용하고, 필요할 때에는 filter를 달아서 색깔을 줍니다.

Objects

Object는 Set of Geometry입니다.
그리고 그 geometry set의 표현을 수학적으로 simulation하구요.

보통 object를 저장할 때에는 Vector Graphics를 사용합니다.
Vector Graphics로 받고 rendering해서, 최종적으로는 Raster Graphic으로 출력하는게 목표라는거죠.

기하적으로 봤을 때, 물체는 위치 (Position)에 의해 결정되구요.
그 외에 Normal Vector도 필요합니다.
Normal Vector는 자신과 자신을 감싸는 surface의 모양을 정의할 수 있죠.

이런 위치적 정보 말고도,
빛과 일어나는 surface interaction을 구현하기 위해 surface property도 사용합니다.

가령, Blinn-Phong 모델의 경우
ambient, diffuse, specular color를 이용하여
물체의 재질을 표현합니다.

ambient는 마치 빛이 우리 주변에 항상 살고있다는 느낌으로,
'기본적인 빛의 양'을 더합니다. (??)

diffuse는 광원과 물체의 관계를 표현하고,
specular는 눈과 관계가 있어 거울반사적 측면만을 표현합니다.

그럼 저 파란 색은요?

그건 object의 reflectance! 어떤 빛을 얼마나 흡수하고 얼마나 반사하냐를 정의한 내용입니다.

Camera

카메라는 Pinhole Camera model을 많이 사용합니다.


바늘구멍이 얼마나 작냐? 개념적으로 1 pixel정도로 정의합니다.

카메라가 커지면 여러 pinhole camera를 붙여 sampling이 가능하게 하게끔,
작은 모델을 기본치로 사용합니다.

카메라의 원리 상으로, 상이 반대로 맺히게 됩니다.
그걸 다시 돌려놓는 과정도 필요합니다.
(사람 눈에선 그걸 자동으로 해주죠.)

일반적으로,

카메라가 어딜 보고있고, 어디 위치해 있는지 등이
extrinsic factor, 즉 카메라의 3D transformation을 의미하구요.

카메라의 움직임과 관련 없는 것들.
시야각(FOV), sensor의 가로-세로비,
object의 depth (어디서 어디까지만 그릴 지. 실제 카메란 그런거 없겠죠)

이것들을 intrinsic factor라고 정의합니다.

Graphic Systems

Physical Approach

= Global Illumination.
모든 빛과 surface간의 관계를 simulation하기 위해 다뤄지는 방식입니다.

surface와 surface간의 관계도 고려를 합니다.

지금 보면, surface에서 반사된 빛이 또 surface로 가서 거기서 반사가 되죠?
그것까지 고려를 합니다. 그니까 실제 세상과 가장 흡사한거죠.

엄청 복잡하고, 계산량이 엄청 많고, 엄청 느립니다.

대표적 예시가 Ray Tracing이고, 보통 software에 구현되어 있습니다.
RTX 시리즈가 Ray Tracing Extension의 줄임말이죠.

보통은 High Quality film production에 사용합니다.

Pipeline Approach

global로 그리자니 너무 많습니다.
그래서 Indirect Reflection, 즉 surface간의 reflection을 무시하자라는 approach가
pipeline approach입니다.

줄여서 Local / Direct Illumination이라고 부릅니다.

ray를 쓰지 않고, rasterization 기반으로 작동합니다.
그래서 GPU를 사용할 수 있습니다.


보통은 OpenGL과 DirectX에서 이 기법을 사용합니다.

앞선 approach보다 좀 더 경량화되었기 때문에,
Inter-object reflection, refraction, shadow가 빠져 있습니다.

물론, 요즘 게임들에는 shadow나 refraction같은게 보이죠?
그건 똑같은 방법론으로 매우 간단한 수준으로 approximate시킨 기법들인겁니다.

Raster Pipeline

Raster Pipeline은 Local Illumination model을 구현하는 pipeline입니다.

무조건 GPU를 포함합니다. GPU 없이는 못그립니다.

GPU의 key concept는 병렬입니다.
object간의 dependency가 없죠.
광원과 카메라는 object으로 변하지 않기 때문에 병렬처리가 가능합니다.

3차원 공간의 배치 위치에 관계 없이 그림을 넣을 수 있습니다.

GPU는 코어가 여러개고, 코어가 병렬으로 돌아가기 때문에,
적개는 수십개에서 많게는 수만개까지 한번에 돌아갑니다.

가령 4K로 그림을 그린다면, 1600만 픽셀이 필요한데,
10000개씩 그릴 수 있다면 1600번의 계산만 하면 된다는 뜻입니다.

GPU는 CPU용 서버라고 생각을 하면 됩니다.
GPU에다가 작업할 내용과 데이터를 미리 보내면, GPU가 작업한 내용을 나중에 받아볼 수 있는? 그런 느낌입니다.

GPU 자체가 애초에 Cache, Memory, Bus, 다 갖고 있는, Disk 제외 전부 갖고 있는
하나의 별도의 컴퓨터입니다.

하나하나 파이프라인을 뜯어봅시다.

Before Vertex Processing

host application (CPU)가 본인 메모리의 data를 GPU 메모리로 보냅니다.

즉, 처리하기 위한 데이터는 전부 GPU 메모리에 진즉에 올라가 있어야 합니다.

GPU로 보내는 데이터, 즉 Vertex Buffer를 GPU에 미리 보내놓습니다.

얘네는 막 프레임별로 변하는게 아니라, 한 번 띄워놓고 쭉 작업합니다.

Vertex Processing : 3D Transformation

Pipeline 상에는 크게 2개의 shader가 존재합니다.
vertex shader와 fragment shader인데, 얘네는 우리가 script language를 이용해 프로그래밍이 가능합니다.

게임엔진 같은 곳에서 vertex shader나 fragment shader를 안 짜도 다 돌아간다면,
누군가 진즉에 다 짜 놓은 것입니다.

암튼.

Vertex라는건 기본으로 3D Position, normal vector, texture coord의 3가지 요소를 갖습니다.

vertex shader의 기본 역할은,
local coord에서 정의된 object를 world object coord로 바꿔주고,

그 world object coord를 카메라 기준의 coord로 바꿔주는 역할을 합니다.

카메라는 변하죠?
뭐 이동이나 점프를 해도 변하니까요.

이 좌표간의 변환은 4x4의 행렬곱을 통해 변합니다.

Vertex Processing : Projection

Projection은 3D 좌표를 2D로 보내는 과정입니다.

Perspective Projection은 우리 시야를 고려해서, 물체의 사이즈를 고려하는 것이고
Parallel Projection은 그런 것 고려하지 않는 것입니다.

이것도 4x4 행렬곱을 사용하고,
역시 Vertex Shader에서 사용합니다.

Primitive Assembly

우리가 알 부분은 아니긴 합니다.

Vertex를 왕창 보내야 하는데,
이제 Array처럼 쭉 나열해서 보냅니다.

그리고 만약 이걸 'Line으로 해석해라' 하고 얘기를 했다면,
Vertex를 2개씩 끊어 Line으로 해석하는거고,

'Triangle로 해석해라' 라고 했었다면
3개씩 끊어 Triangle로 해석하겠죠.

Clipper

Near와 Far 사이만 보자. 라는 게 카메라 설정에서 가능했습니다.
그럼 그림 상 그 View 내부에 해당하지 않으면, 보이지 않아야 겠죠.


만약 Triangle를 그렸는데 화면 상 잘렸다?
그럼 그걸 잘 잘라서 버려버리는 역할을 해주는게 Clipper입니다.

그렇게 눈에 보이지 않는 것, 카메라에 없는 것을 보내도
화면에 뜨지 않는 것은 Clipper가 일을 잘 해주고 있는 것입니다.

Rasterization

Vector Primitive를 Raster (Pixel)로 바꿔주는 거죠.

Clipper를 통과한 object를 potential pixel, = fragment로 바꿔줍니다.

그 fragment 중 실제로 보이는걸 pixel이라 부르기 때문에,

fragment shader는 input으로 fragment를 받고, output으로 pixel을 뱉습니다.

암튼.

그래서 rasterizer는,

다음과 같이 삼각형을 그리고 싶을 때,
픽셀별로 sampling point를 중앙에 잡고,
그 sampling point가 삼각형의 내부에 있으면 보이고, 아니면 안 보이게 기준을 잡구요.


다음과 같이 fragment로 바꿔주는 걸 rasterize라고 합니다.

또,

2번째 역할로는 Vertex 단위로 색깔을 넘겨줄 수 있는데,
Linear Interpolation을 통해 색상을 쭉 퍼뜨려줄 수 있습니다.

Fragment Processing

color를 결정합니다.
그 결정에는 texture mapping이나 vertex interpolation이나 physical-based shading 등이 쓰일 수 있습니다.

Post-fragment operation

texture를 있는 그대로 그리기만 하면,
Depth가 반영이 안 됩니다.

가령 카메라 기준 더 앞에 있는건 더 뒤에 있는걸 가려야 하는데,

그걸 확인하는 작업을 Depth Buffering이라고 합니다.

frame buffer는 depth buffer를 같이 유지하고 있어서,
항상 fragment의 depth들을 비교합니다.

가까운 depth를 가진 fragment만이 frame buffer로 그려져서 넘어가야 하죠.

Frame Buffer

이 작업을 다 끝내고 살아남은 fragment들을 pixel이라 부릅니다.

이 pixel들이 frame buffer로 넘어갑니다.

Frame buffer는 이 pixel들을 저장하는 2D memory area입니다.
그들의 resolution과 bit depth (8bpp, 16bpp, 32bpp ..)에 맞게 정의되어 있습니다.

Display

Display가 이제 준비되었으니 이미지를 달라. 라고 하면
Frame buffer에서 display로 이미지를 쏩니다.

이 transfer는 display의 refresh rate과 맞춰져서,
60Hz로 refresh된다면, 그 60Hz의 주사 단계 사이 사이에 빈틈에
display가 framebuffer에게서 image를 가져옵니다.

그리고 여담이지만

실제로 frame buffer는 2개를 씁니다.
그리는거 하나, 보여주는거 하나.

그림을 그리면서 보여주게 된다면 그 그리는 과정이 다 보이게 되겠죠?
그럼 UX측면에서 구려집니다.

따라서 그리는 buffer와 보여주는 buffer의 내용을 swap하며 유저에게 보여주게 됩니다.


이게 이제 총망라한 pipeline입니다.

좌표정보를 받아서,
Vertex Shader에서 camera view로 바꿔주고,
primitive assembler에서 삼각형 단위로 묶고,
clipper는 화면 밖에 그려지는 image를 잘라주고,
rasterizer에서 fragment들로 바꿔주고,
fragment shader들은 fragment개수만큼 돌면서 색을 결정합니다.

profile
햄스터가 세상을 지배한다.

0개의 댓글