4.2 오픈지엘 API
Reporting Date: October. 2, 2024
OpenGL을 기반으로 한 그래픽스 렌더링 과정에 대해 다루고자 한다.
목차01 OpenGL
02 게임 핵심 요소
03 렌더링 파이프라인
04 인생네컷 사진촬영
05 상태 변수
06 벡터 배열 타입
01 OpenGL
(Open Graphics Library)
그래픽 하드웨어에 대한 인터페이스.
애플리케이션이 그래픽을 처리할 때 OpenGL의 그래픽 전용 함수를 호출하면,
이 API가 해당 작업을 처리하고 GPU가 이를 받아 화면에 출력하는 과정을 거친다.
이것은 그래픽 처리만을 위한 API이며, 그래픽 외의 다른 기능은 포함되지 않는다.
이와 유사한 API로는 DirectX가 있으며, 주로 게임 개발에 사용된다.
DirectX의 일부 기능은 그래픽 처리와 관련이 있으며,
OpenGL은 이와 유사한 역할을 한다.
또한 OpenGL은 다양한 플랫폼에서 동일하게
실행될 수 있도록 표준화 작업이 이루어진다.
ATI, HP, Intel, Microsoft 등 여러 기업이 협력하여 OpenGL을
개발하고 있으며, 이들은 공통된 라이브러리를 사용하여 구동한다.
다만, 이럴 경우 이윤을 추구하는 기업의 특성상 차별화를 두기 위해
각 기업은 OpenGL 외에도 자사 하드웨어의 성능을 극대화를 위한
독자적인 추가 개발을 진행하기도 한다.
게임에서 가장 중요한 요소
02 실시간성과 실사 렌더링
(Real-Time, Photo-Realism)
게임은 실시간으로 반응해야 하며,
그래픽은 가능한 한 현실감 있게 표현되어야 하기 때문이다.
이를 구현하기 위해 다양한 그래픽 라이브러리와 기술들이 사용된다.
1 . 그래픽 라이브러리
과거에는 그래픽 라이브러리가 운영체제에 별도로 설치되어야 했지만,
최근에는 많은 경우 이미 기본적으로 설치되어 있는 상태이다.
또한, 특정 회사가 라이브러리의 일부를 개발하고 제공하는 경우도 많다.
2 . 시각화 라이브러리
파이썬에서는 다양한 시각화 라이브러리가 존재하는데,
이는 여러 개발자들이 제공한 제 3자 패키지들이다.
이러한 패키지들은 각기 다른 목적으로 사용되며,
사용자가 필요에 맞는 패키지를 선택할 수 있다.
matplotlib, seaborn, plotly 등이 그 예이다.
3 . 다양한 패키지의 존재 이유
개인 개발자들이 각기 다른 요구에 맞춰 라이브러리를 만들기 때문이다.
많은 개발자들이 자신만의 방식으로 라이브러리를 개발하고,
이를 오픈 소스로 제공하기 때문에 여러 가지 선택지가 생긴다.
4 . 패키지 관리 문제
다양한 패키지가 존재한다는 것은
체계적인 관리가 부족하다는 문제도 함께 존재한다.
각 회사나 개발자가 독립적으로 라이브러리를 개발하고 관리하므로,
때때로 업데이트나 수정이 일관되지 않거나 호환성 문제가 발생할 수 있다.
따라서 개발자는 종종 직접 코드를 수정하거나 문제를 해결해야 하는 상황이 발생한다.
이와 비슷한 문제는 OpenGL과 같은 그래픽 라이브러리에서도 발생할 수 있으며,
다양한 벤더들이 자신의 하드웨어에 최적화된 드라이버나
기능을 추가하기 위해 추가 개발을 한다.
결국, 파이썬과 같은 언어나 그래픽 라이브러리들은
다양한 개발자와 회사들에 의해 제공되므로,
그만큼 관리와 표준화가 중요한 과제가 된다.
이러한 패키지들을 효과적으로 관리하려면 개발자가 지속적으로
업데이트하며, 버전 관리 시스템을 통해 일관된 개발 환경을 유지해야 한다.
03 렌더링 파이프라인
그래픽을 그리기 위한 일련의 과정을
자동적으로 처리하는 흐름.
1초에 24프레임 이상을 달성해야 부드러운 움직임을 구현할 수 있다.
이 과정은 물 흐르듯이 기계적으로 연속적으로 수행되며,
각 부분이 일정한 속도로 처리되어야 일관성 있게 렌더링이 가능하다.
1 . 렌더링 파이프라인
① 24프레임 이상
1초에 최소 24프레임 이상이 필요하며,
현대의 디지털 그래픽은 보통 30프레임이나 60프레임을 목표로 한다.
이를 위해 모든 처리 단계를 가능한 한 빨리, 효율적으로 실행해야 한다.
② 동일한 처리 시간
렌더링 파이프라인의 각 단계는 일정한 시간 내에 처리되며,
그래픽 작업의 모든 부분에서 시간 분배가 균일해야 한다.
2 . 물체 모델링 방법
① 솔리드 프레임
(Solid Frame)
물체의 표면을 채워서 표현하는 방식.
물체의 내부가 보이지 않으며, 실체감 있는 3D 모델을 만들 때 사용된다.
표면과 재질을 시뮬레이션하여 현실감 있는 렌더링을 가능하게 한다.
② 와이어 프레임
(Wireframe)
물체를 선(Edge)과 점(Vertex)으로만 표현하는 방식.
이 방식은 물체의 구조와 형태를 간단히 나타내며,
복잡한 연산 없이 빠르게 렌더링할 수 있다.
설계 과정에서 주로 사용되며,
전체 모델의 기본적인 윤곽을 확인할 때 유용하다.
04 인생네컷 사진촬영
3D 그래픽스 렌더링 파이프라인의 개념을
인생네컷 사진 촬영과 연결하여 설명할 수 있다.
이 과정은 객체 모델링, 좌표계 변환, 투영 등을 통해
3D에서 2D로 변환하여 최종 이미지를 만드는 방식이다.
1-1 . 객체 모델링
(Object Modeling Stage)
인생네컷에서 사람이 포즈를 취하는 과정은
컴퓨터 그래픽에서 객체를 모델링하는 것과 같다.
여기서는 대상 객체인 사람과 소품들이 3D 모델링으로 표현된다.
각각의 객체는 독립적으로 공간에 존재하며,
로컬 좌표계 안에서 각자 다른 움직임을 가질 수 있다.
1-2 . 로컬 좌표계
(Local Coordinate System)
각 객체는 자신의 좌표계(기준점)를 가지고 있으며,
이 안에서 이동하거나 회전할 수 있다.
예를 들어, 한 사람이 포즈를 취하는 동안
다른 소품도 각기 다른 좌표계에서 움직일 수 있다.
로컬 좌표계 ⇨ 월드 좌표계
2-1 . 전역(월드) 좌표계
[World OR Global] Coordinate System
사람들이 의자로 모이는 과정은 개별 객체가
전역 좌표계로 변환되어 배치되는 것을 의미한다.
이 과정에서 각 객체는 변환행렬을 통해 이동되며,
이동뿐만 아니라 회전, 크기 조정도 가능하다.
즉, 인물과 소품들이 특정 위치로
정렬되거나 배치되는 과정을 말한다.
2-2 . 변환행렬
(Transformation Matrix)
객체의 위치, 방향, 크기를 변경하는 수학적 도구.
- 이동 (Translation)
- 회전 (Rotation)
- 크기 조정 (Scaling)
이 변환행렬은 행렬 곱셈을 통해 좌표를 변경한다.
월드 좌표계 ⇨ 뷰 좌표계
3-1 . 뷰 좌표계
(View Coordinate System)
카메라의 위치와 방향을 기준으로 한 좌표계.
3D 장면의 객체들은 월드 좌표계에서 배치되지만,
최종적으로 카메라가 보는 관점에서 좌표가 변환되어야 한다.
이 과정에서도 변환행렬이 사용되며
3D 객체의 위치를 카메라 시점으로 조정하는 역할을 한다.
결과적으로, 카메라 시점에서 보이는 모습만 남게 된다.
3-2 . 카메라 연산
전역 좌표계에서 카메라 시점으로 변환되며,
객체들이 카메라에서 어떻게 보일지 결정된다.
모든 객체를 카메라 원점(0,0,0)으로 이동한 뒤,
월드 좌표계의 방향과 일치하도록 회전시킨다.
카메라의 앞 방향(lookAt), 위쪽 방향(up),
오른쪽 방향(right)을 고려하여 회전행렬을 정의한다.
3D 객체 ⇨ 2D 이미지
4 . 투영 변환
(Projection Transformation)
셔터를 누르는 과정은
3D 객체를 2D 이미지로 변환하는 과정과 같으며,
이 과정은 원근 투영 또는 평행 투영을 통해 이루어진다.
이 과정에서 객체의 Z축(깊이) 정보는 투영되지만,
화면상에는 2D 좌표계로 표현된다.
결과적으로 Z축 정보는 화면에 직접적으로 나타나지 않게 된다.
Z축 제거 방법
3D 그래픽에서 2D 화면으로
물체를 투영하는 방식에 따라 달라진다.
4-1 . 원근 투영
(Perspective Projection)
원근법을 적용하여, 멀리 있는
물체는 작게, 가까이 있는 물체는 크게 그린다.
최종적으로 X, Y 좌표로 변환하며,
깊이 정보는 Z-buffer 등으로 내부적으로 유지 가능하다.
인간의 눈으로 보는 방식과 유사하여 현실감 있는 결과를 제공한다.
Z축의 깊이를 고려하여, 객체 간의 거리 차이를 반영한다.
FPS, VR/AR, VFX 등에서 사용된다.
4-2 . 평행 투영
(Parallel Projection)
직교 투영과 사영 투영을 포함하는 방식으로,
모든 투영선을 평행하게 설정하여 변환한다.
따라서 모든 물체를 동일한 크기로 표현하며,
Z축의 깊이를 고려하지 않는다.
실제 크기와 비례 관계를 유지하며,
원근이 적용되지 않아 왜곡 없이 물체를 나타낼 수 있다.
기계 설계, 건축 도면, GIS 등에서 사용된다.
① 직교 투영
(Orthographic Projection)
원근감 없이 모든 객체가 동일한 크기로 표현된다.
Z축 정보는 유지되지만, 크기 변화 없이 그대로 투영된다.
결과적으로 Z축을 반영하지 않고 X, Y 좌표만 남기는 방식이다.
② 사영 투영
(Oblique Projection)
투영선이 Z축과 경사진 비수직으로,
특정한 각도로 기울어진 상태에서 투영된다.
Z축 방향의 깊이 정보를 어느 정도 반영하되,
원근 투영처럼 크기 변화를 적용하지 않는다.
즉, Z축을 완전히 제거하지 않고,
기울기를 주어 X, Y 좌표에 일부 반영한다.
투영 변환 ⇨ 클립 좌표계
5-1 . 클립 좌표계
(Clip Coordinate System)
3D 그래픽 변환을 쉽게 하기 위해 동차 좌표로 표현되며,
추가적인 W 성분을 포함하여 (x, y, z, w) 형태로 표현한다.
(Homogeneous Coordinates)
5-2 . 클리핑
(Clipping)
화면에 표시되지 않는 부분을 잘라내는 작업을 의미한다.
예를 들어, 카메라 시야 밖에 있는 물체는
표시되지 않으므로 이 부분을 제거한다.
클립 좌표계 ⇨ 정규화된 기하 좌표계
6 . 정규화된 기하 좌표계
(NDC, Normalized Device Coordinates)
클리핑 후 남은 좌표를 W로 나누어 정규화하고,
좌표 범위를 [−1, +1]로 변환하여 장치 독립적인 형태로 만든다.
정규화된 기하 좌표계 ⇨ 뷰포트 좌표계
7 . 뷰포트 좌표계
(Viewport Coordinates System)
정규화된 좌표를 사용자가 설정한
뷰포트 크기에 맞게 조정하여 픽셀 단위의 좌표로 변환한다.
이 과정까지는 벡터 그래픽의 마지막 형태이며,
이후 단계에서 개별 픽셀로 변환된다.
뷰포트 좌표계 ⇨ 장치 좌표계
8-1 . 장치 좌표계
(Device Coordinates System)
모든 투영이 완료된 후,
실제 화면 해상도에 맞춰 장치 좌표계로 변환된다.
이 과정에서 최종 이미지가 GPU의 프레임 버퍼에 저장된다.
8-2 . 래스터화 및 후처리
(Rasterization & Post-Processing)
픽셀 단위로 이미지를 변환하고 셰이딩, 텍스처링 등을 적용된다.
이는 사진의 필터나 색감 보정이 적용하는 것과 유사하다.
3D 물체를 2D로 변환하여, VRAM에 저장된 픽셀 형태로 바꾸며,
이 과정에서 물체의 색상, 깊이 정보 등이 처리된다.
8-3 . 출력
(Rendering & Printing)
최종 렌더링을 거쳐 화면에 이미지를 출력한다.
이는 편집이 완료된 사진이 프린터로 전송되어 인쇄되거나,
QR 코드로 디지털 파일이 제공되는 것과 같다.
05 상태 변수
(State Variable)
그래픽 시스템에서 현재 설정된 값.
이 값들은 렌더링 과정에서
물체를 그리거나, 투영할 때 참고된다.
렌더링 파이프라인이 계속해서 움직이는 동안,
특정 상태를 검색하거나 변경하는 데 사용된다.
1 . GL
그래픽을 그리기 위한 가장 기본적인 API.
gl.h 라는 헤더 파일을 통해, 함수의 프로토타입이 정의되어 있으며,
이 파일은 함수들의 메타 정보(크기, 데이터 타입 등)를 포함하고 있다.
함수의 본체는 미리 번역되어 있으며, 함수 호출 시 본체와 연결된다.
2 . GLU
(OpenGL Utility Library)
OpenGL의 유틸리티 기능을 추가해주는 라이브러리.
그래픽 연산에 유용한 기능을 제공하며,
뷰 변환과 투영 행렬을 처리하는 고수준 함수들을 포함한다.
함수의 괄호 안에 있는 쉼표(,)는 인자를 나타낸다.
이 인자들은 외부에서 함수로 전달되는 값들이며,
함수가 어떻게 작동할지를 결정한다.
예를 들어, f는 실수형(float) 인자를 나타내며,
d는 더블형(double), i는 정수형(int)을 나타낸다.
3 . GLUT
(OpenGL Utility Toolkit)
그래픽 프로그램에서 윈도우 시스템과의
상호작용을 간편하게 해주는 라이브러리.
예를 들어, 창을 만들고, 마우스 클릭과 키보드 입력을 처리하는 기능을 제공한다.
개인적으로 만들 수 있는 함수로 자유롭게 사용할 수 있으며, 별도의 설치가 필요하다.
OpenGL로 2D 및 3D 애플리케이션을 제작할 때
그래픽 환경을 관리하고, 필요한 이벤트를 처리하는 데 도움을 준다.
예를 들어, 사용자는 키보드나 마우스를 이용하여 프로그램을 제어할 수 있다.
4 . GL 명령어 구조
gl 로 시작하며, 그 뒤에 함수 이름과
파라미터의 수 및 데이터 타입을 포함한 접미사가 따라온다.
glColor3f(float red, float green, float blue)에서
glColor()는 색을 설정하는 함수, 3은 파라미터의 수를,
f는 부동소수(float)를 의미한다.
5 . 다양한 접미사
- f loat (부동소수점 실수)
- double (더블 정밀도 실수)
- i nt (정수)
- short (짧은 정수)
- byte (바이트)
- unsigned byte (부호 없는 바이트)
이 명령어들은 그래픽 파이프라인을 통해 데이터를 GPU로 전달하여,
화면에 최종적으로 그래픽을 렌더링하는 작업을 수행한다.
OpenGL은 이런 방식으로 다양한 장치 및 플랫폼에서
크로스 플랫폼 그래픽 애플리케이션을 만들 수 있게 해준다.
06 벡터 배열 타입
주로 그래픽 프로그래밍이나 수치 계산에서 많이 사용되며,
효율적으로 데이터를 다루는 방법 중 하나이다.
이 방식은 특히 벡터(점, 방향 등)를 다룰 때 유용하다.
1 . 개념
변수는 데이터 값을 저장하는 임시 공간이다.
기본적으로 메모리에 데이터를 저장하고, 필요할 때 꺼내 사용하는 용도로 사용된다.
배열은 같은 타입의 데이터를 여러 개 저장할 수 있는 공간이다.
각 데이터는 배열 내에서 인덱스를 통해 접근된다.
배열은 메모리 내에서 연속된 주소 공간을 차지하지 않을 수도 있는데,
운영체제는 메모리 할당 시 빈 공간에 맞게 데이터들을 저장한다.
2 . 특징
x, y, z 값과 같은 3D 좌표 값을 다룰 때 매우 유용하다.
각 좌표 값을 개별적으로 저장하지 않고도,
하나의 배열로 묶어서 관리할 수 있다.
인덱스나 포인터를 통해 배열의 값을 참조할 수 있다.
이 방식은 각각의 값을 개별적으로 관리하는 것이 아니라,
메모리 상의 주소를 참조함으로써 더욱 효율적인 데이터 접근을 가능하게 한다.
포인터는 실제 데이터를 저장하는 것이 아니라, 메모리 주소를 가리킨다.
이는 메모리 사용을 효율화하고, 메모리 관리를 쉽게 할 수 있도록 해준다.
3 . 장점
① 메모리 접근 효율성
연속적으로 데이터를 저장하지 않아도
포인터를 이용해 빠르게 원하는 데이터를 찾을 수 있다.
포인터가 메모리 주소를 가리키고 있으므로,
직접적으로 x, y, z 값을 관리하지 않아도
벡터의 모든 좌표값을 동시에 참조할 수 있다.
② 계산 효율성
벡터 배열 타입을 사용하면
벡터에 대해 여러 연산을 쉽게 할 수 있다.
예를 들어, 벡터의 덧셈, 곱셈 같은
연산을 처리할 때 개별 좌표에 대해 연산하는 대신
배열 전체에 대한 연산을 처리하게 된다.
4 . C 언어 관점
포인터는 배열의 시작 주소를 가리키며,
메모리 상의 데이터를 직접적으로 참조한다.
따라서 포인터를 통해 벡터 배열의 값을 빠르게 접근할 수 있다.
C 언어에서는 벡터 배열을 포인터를 통해 효율적으로 관리할 수 있으며,
이는 저수준 언어의 특징을 잘 활용한 방식이다.
예를 들어, float* v와 같은 방식으로 벡터 배열을 선언하면,
v 는 배열의 첫 번째 원소의 주소를 가리키게 된다.
이후 인덱스나 포인터 연산을 통해 해당 값들을 참조할 수 있다.
5 . 속성 할당 방법
벡터 배열의 값을 할당할 때,
각 좌표값을 배열 인덱스 또는 포인터를 통해 저장한다.
v[0] = 1.0, v[1] = 2.0, v[2] = 3.0와 같이 배열에 값을 저장하거나,
*v = 1.0처럼 포인터를 통해 값을 할당할 수 있다.
이 방식은 그래픽 프로그래밍에서 점, 선, 면과 같은 3D 객체를
효율적으로 처리하는 데 중요한 역할을 한다.
교제: OpenGL로 배우는 3차원 컴퓨터 그래픽스(p.135)