Skip to content

Latest commit

 

History

History
95 lines (72 loc) · 18.5 KB

File metadata and controls

95 lines (72 loc) · 18.5 KB

레퍼런스


1.3 하드웨어와 소프트웨어

단독 CPU 시절 및 GPU의 등장

3D 그래픽스 프로그래밍의 기본 기반으로 OpenGL을 사용할 것이다. OpenGL의 최초 버전은 1992년에 그래픽 워크스테이션(그래픽 애플리케이션을 위해 설계된 고가의 강력한 컴퓨터)으로 유명한 실리콘 그래픽스라는 회사에서 출시했다.(오늘날에는 스마트폰의 그래픽 연산 능력이 더 뛰어나다.) OpenGL은 대부분의 현대 컴퓨팅 장치들, 데스크탑 컴퓨터, 랩톱, 그리고 많은 모바일 장치들에서 그래픽 하드웨어에 의해 지원된다. WebGL 형태로, 웹 페이지에서 대부분의 3D 그래픽스에 사용된다. 이 섹션에서는 OpenGL의 역사와 이를 지원하는 그래픽 하드웨어에 관한 배경 지식을 설명한다. 최초의 데스크탑 컴퓨터에서, 화면의 내용은 CPU에 의해 직접 관리되었다. 예를 들어, 화면에 선분을 그리기 위해, CPU는 선을 따라 있는 각 픽셀의 색상을 설정하기 위해 반복문을 실행해야 했다. 불필요하게 말하자면, 그래픽은 CPU의 많은 시간을 차지할 수 있었다. 그리고 그래픽 성능은 오늘날 우리가 기대하는 것에 비해 매우 느렸다. 그래서 무엇이 변했나? 컴퓨터는 일반적으로 훨씬 빨라졌고, 물론이지만, 큰 변화는 현대 컴퓨터에서 그래픽 처리가 GPU, 즉 그래픽 처리 장치라는 전문화된 구성 요소에 의해 수행된다는 것이다. GPU는 그래픽 계산을 수행하는 프로세서를 포함하고 있으며, 사실 그러한 프로세서들의 대규모 병렬 작업을 통해 그래픽 연산을 크게 가속화할 수 있다. 또한 이미지와 좌표 목록 같은 것들을 저장하기 위한 자체 전용 메모리를 포함한다. GPU 프로세서는 컴퓨터의 메인 메모리에 저장된 데이터에 액세스하는 것보다 훨씬 빠른 속도로 GPU 메모리에 저장된 데이터에 액세스할 수 있다.

  • OpenGL의 시작: 1992년에 그래픽 워크스테이션으로 유명한 '실리콘 그래픽스'에서 처음 출시됐음
  • 지원 범위: 데스크탑, 랩톱, 모바일 기기 등 대부분의 현대 컴퓨팅 장치에서 하드웨어적으로 지원됨
  • 웹에서도 사용: WebGL 형태로 웹 브라우저 내부에서도 3D 그래픽스를 구현하는 데 널리 쓰임
  • 초창기 그래픽 처리: 옛날 데스크탑에서는 CPU가 화면에 선을 그리기 위해 픽셀 하나하나 색상을 지정하는 반복문을 돌려야 했음(매우 느렸음)
  • GPU의 등장: 이제는 그래픽 처리 전담 장치(GPU)가 있어서 대규모 병렬 연산으로 그래픽을 훨씬 빠르고 효율적으로 처리할 수 있게 됐음
  • 전용 메모리: GPU에는 전용 메모리가 있어서, 그래픽 데이터(이미지, 좌표 등)를 빠르게 불러와서 처리할 수 있음
  • 결과: CPU 부담을 줄이고, 동시에 더욱 높은 성능의 그래픽 렌더링이 가능해졌음

GPU 시대의 다양한 그래픽 API

선을 그리거나 다른 그래픽 작업을 수행하기 위해, CPU는 단순히 명령과 필요한 데이터를 GPU에 보내면 되며, GPU는 그 명령을 실제로 수행하는 책임이 있다. CPU는 그래픽 작업의 대부분을 GPU에 오프로드하는데, GPU는 그 작업을 매우 빠르게 수행하도록 최적화되어 있다. GPU가 이해하는 명령의 집합은 GPU의 API를 구성한다. OpenGL은 그래픽 API의 한 예이며, 대부분의 GPU는 OpenGL 명령을 이해할 수 있거나 적어도 OpenGL 명령을 GPU가 이해할 수 있는 명령으로 효율적으로 변환할 수 있다는 의미에서 OpenGL을 지원한다. OpenGL은 유일한 그래픽 API가 아니다. 사실, 더 현대적인 대안들로 대체되는 과정에 있다. 여기에는 같은 그룹이 OpenGL을 담당하는 Vulkan, Apple과 Microsoft의 독점 API인 Metal과 Direct3D가 포함된다. 웹의 경우, WebGPU라는 새로운 API가 어느 정도 개발되어 있으며 일부 웹 브라우저에서 이미 구현되어 있다. 이러한 새로운 API들은 복잡하고 저수준이다. 그들은 사용의 용이성보다는 속도와 효율성을 더 설계되었다. Metal, Direct3D, Vulkan은 이 교과서에서 다루지 않지만, WebGPU는 9장에서 소개된다. 대부분의 책에서는 3D 그래픽스에 대한 쉬운 소개를 제공하고, 웹 브라우저에서 여전히 주요 API인 WebGL을 사용하기 때문에 OpenGL을 사용할 것이다.

  • CPU에서 선을 그리거나 다른 그래픽 작업을 할 때는, 필요한 명령과 데이터를 GPU로 보내기만 했음
  • GPU는 이 명령을 실제로 실행하고, 화면에 그려내는 역할을 맡고 있음
  • CPU는 그래픽 관련 연산 대부분을 GPU에 맡겨서, GPU가 빠르게 처리하도록 했음
  • GPU가 이해하는 명령들의 모음을 보통 ‘GPU의 API’라 부름
  • OpenGL은 이런 그래픽 API의 예시이며, 대부분의 GPU가 OpenGL 명령을 직접 이해하거나 변환해 처리할 수 있었음
  • OpenGL만 있는 것은 아니고, 더 최신의 다른 그래픽 API들이 있었음 (예: Vulkan, Metal, Direct3D, WebGPU 등)
  • 이런 새로운 API들은 더 빠른 속도와 효율성에 중점을 두고 있어서, 다소 복잡하고 저수준으로 설계되었음
  • 이 교재에서는 Metal, Direct3D, Vulkan은 다루지 않지만, WebGPU는 9장에서 소개될 예정임
  • 많은 책에서는 여전히 3D 그래픽스 입문을 위해 WebGL과 OpenGL을 사용하고 있기 때문에, 여기서도 OpenGL을 활용할 예정이었음

OpenGL, 계속 진화해온 그래픽 API와 클라이언트/서버 구조

OpenGL이 API라고 언급했지만, 사실 그것은 반복적인 확장과 수정을 거친 일련의 API들이다. 2023년 현재(아마도 최종) 버전은 2017년에 처음 출시된 4.6이다. 이는 1992년의 1.0 버전과 매우 다르다. 또한, 모바일 폰과 태블릿과 같은 ‘임베디드 시스템’을 위한 특수 버전인 OpenGL ES가 있다. 그리고 웹 브라우저에서 사용되는 WebGL이 있는데, 이는 기본적으로 OpenGL ES의 포트이다. OpenGL이 어떻게 그리고 왜 변화했는지 알아두는 것이 유용하다. 먼저, OpenGL이 ‘클라이언트/서버’ 시스템으로 설계되었다는 것을 알아야 한다. 컴퓨터의 디스플레이를 제어하고 그래픽 계산을 수행하는 서버는 클라이언트가 발행한 명령을 실행한다. 일반적으로, 서버는 그래픽 프로세서와 메모리를 포함하는 GPU이다. 서버는 OpenGL 명령을 실행한다. 클라이언트는 같은 컴퓨터의 CPU와 그것이 실행하는 응용 프로그램이다. OpenGL 명령은 CPU에서 실행되는 프로그램에서 나온다. 그러나, 실제로는 OpenGL 프로그램을 네트워크를 통해 원격으로 실행할 수 있다. 즉, 원격 컴퓨터(OpenGL 클라이언트)에서 응용 프로그램을 실행할 수 있고, 그래픽 계산과 디스플레이는 실제로 사용하는 컴퓨터(OpenGL 서버)에서 수행될 수 있다. 핵심 아이디어는, 클라이언트와 서버가 별개의 구성 요소이며, 이러한 구성 요소 사이에는 통신 채널이 있다는 것이다. OpenGL 명령과 그들이 필요로 하는 데이터는 그 채널을 통해 클라이언트(CPU)에서 서버(GPU)로 전달된다. 채널의 용량은 그래픽 성능의 제한 요소가 될 수 있다. 화면에 이미지를 그리는 것을 생각해 보라. GPU가 이미지를 마이크로초 단위로 그릴 수 있다면, 이미지 데이터를 CPU에서 GPU로 전송하는 데 밀리초가 걸린다면, GPU의 놀라운 속도는 관련이 없다—이미지를 그리는 데 걸리는 시간의 대부분은 통신 시간이다.

  • OpenGL은 계속 바뀌어온 여러 버전의 그래픽 API들이 합쳐진 개념이었음
  • 2023년 기준 최신은 2017년에 나온 4.6 버전으로, 처음(1992년 1.0)과는 많이 달라졌음
  • 모바일이나 태블릿 같은 임베디드 기기를 위한 OpenGL ES가 있었고, 웹 브라우저에서 쓰이는 WebGL은 사실상 OpenGL ES를 옮겨온 것임
  • OpenGL은 기본적으로 ‘클라이언트/서버’ 구조로 동작했음
    • 클라이언트는 CPU와 이를 통해 돌아가는 프로그램이었음
    • 서버는 GPU로, 그래픽 계산과 디스플레이를 담당했음
  • 프로그램(클라이언트)이 내린 명령과 데이터가 GPU(서버)로 전달되고, GPU가 이를 실행해 화면을 그림
  • 참고로, 네트워크를 통해 원격으로 GPU가 있는 컴퓨터에 명령을 내려 그릴 수도 있었음
  • 클라이언트(프로그램)와 서버(GPU) 사이에는 데이터가 오가는 통로가 있었고, 이 통로가 느리면 GPU가 빨라도 전체 성능이 떨어질 수 있었음

OpenGL 데이터 전송

이러한 이유로, OpenGL의 진화에서 주요한 동력 중 하나는 CPU와 GPU 사이의 통신이 필요한 양을 제한하려는 욕구였다. 한 가지 접근법은 정보를 GPU의 메모리에 저장하는 것이다. 어떤 데이터가 여러 번 사용될 것이라면, 한 번 전송하여 GPU의 메모리에 저장할 수 있으며, 그곳에서 GPU가 즉시 접근할 수 있다. 또 다른 접근법은 주어진 이미지를 그리기 위해 GPU로 전송해야 하는 OpenGL 명령의 수를 줄이려는 시도이다. OpenGL은 삼각형과 같은 기하학적 원시 도형을 그린다. 원시 도형을 지정한다는 것은 각각의 정점에 대해 좌표와 속성을 지정하는 것을 의미한다. 원래 OpenGL 1.0에서는 각 정점의 좌표를 지정하기 위해 별도의 명령이 사용되었고, 속성의 값이 변경될 때마다 명령이 필요했다. 단일 삼각형을 그리는 데 세 개 이상의 명령이 필요했다. 수천 개의 삼각형으로 구성된 복잡한 객체를 그리는 데는 수천 개의 명령이 필요했다. OpenGL 1.1에서도 객체를 수천 개의 명령 대신 단일 명령으로 그릴 수 있게 되었다. 객체에 대한 모든 데이터는 배열에 로드되어 한 번에 GPU로 전송될 수 있었다. 불행히도, 객체가 여러 번 그려질 경우, 매번 객체가 그려질 때마다 데이터를 다시 전송해야 했다. 이 문제는 OpenGL 1.5에서 정점 버퍼 객체로 해결되었다. VBO는 GPU의 메모리 블록으로, 정점 세트의 좌표나 속성 값이 저장될 수 있다. 이를 통해 데이터를 재전송하지 않고도 데이터를 재사용할 수 있다. 마찬가지로, OpenGL 1.1은 텍스처 객체를 도입하여 텍스처로 사용될 여러 이미지를 GPU에 저장할 수 있게 했다. 이는 텍스처 이미지가 여러 번 재사용될 경우 한 번에 GPU로 로드하여 GPU가 이미지 간에 쉽게 전환할 수 있도록 한다.

  • OpenGL이 발전해온 중요한 이유 중 하나는 CPU에서 GPU로 데이터를 전달해야 하는 횟수를 줄이려는 것이었음
    • 예를 들어, 물건을 자주 써야 하는데, 매번 차로 옮겨온다면 번거롭고 시간도 오래 걸림
    • 그래서 필요한 물건을 아예 창고(=GPU 메모리)에 저장해두고 계속 꺼내 쓰는 것처럼, 그래픽 데이터도 GPU에 한 번 올려두면 편해졌음
  • GPU 메모리에 데이터를 저장하는 방법
    • 자주 쓰는 정보(정점 좌표, 색상 등)는 한 번 GPU로 옮겨 놓으면, 매번 CPU에서 다시 전송할 필요가 없어졌음
    • 이렇게 하면 CPU와 GPU 사이의 데이터 이동 자체를 줄여, 그래픽 처리 속도를 높일 수 있었음
  • OpenGL 초창기(1.0)에는 한 삼각형을 그리기 위해서도 정점을 각각 명령으로 전달해야 했음
    • 수많은 삼각형을 그리면, 그만큼 CPU가 명령을 일일이 보내야 했음
    • a이건 마치, 물건을 한 개씩 차에 실어 옮기는 것과 같았음
  • OpenGL 1.1 이후, 정점 데이터를 배열에 넣어 한 번에 전송할 수 있게 됐음
    • 하지만 객체를 여러 번 그릴 때마다 같은 데이터를 계속 보내야 했음
    • 즉, 창고가 있긴 해도 한 번 쓰고 나면 다시 밖으로 빼놨다가, 또 들여놨다가 하는 번거로움이 있었음
  • OpenGL 1.5에서 정점 버퍼 객체(VBO)가 도입되었음
    • 이건 GPU 안에 있는 ‘전용 보관함’으로, 미리 데이터를 넣어두면 필요할 때마다 바로 꺼내 쓸 수 있었음
    • 한 번 올려둔 정보(삼각형 정점 좌표 등)를 재전송하지 않고도 여러 번 재사용할 수 있었음
  • OpenGL 1.1에 추가된 텍스처 객체도 같은 원리였음
    • 자주 쓰는 이미지를 한 번 GPU에 올려두고, 필요할 때마다 가져다 씀
    • 마치 창고에 쌓아둔 여러 물건을 필요할 때 꺼내 쓰는 것처럼, 텍스처 이미지를 손쉽게 전환해 쓸 수 있었음

셰이더

OpenGL에 새로운 기능이 추가됨에 따라 API의 크기가 커졌다. 하지만 이러한 성장은 여전히 그래픽을 수행하는 데 있어 새롭고 더 정교한 기술의 발명에 뒤처졌다. 이러한 새로운 기술 중 일부는 OpenGL에 추가되었지만, 문제는 아무리 많은 기능을 추가해도 항상 새로운 기능에 대한 요구가 있을 것이고, 모든 새로운 기능이 너무 복잡하다는 불만이 있을 것이라는 점이다. OpenGL은 새로운 부품이 계속 추가되는 거대한 기계였지만, 여전히 모두를 만족시키지는 못했다. 진정한 해결책은 기계를 프로그래밍 가능하게 만드는 것이었다. OpenGL 2.0에서는 GPU에서 그래픽 계산의 일부로 실행될 프로그램을 작성할 수 있게 되었다. 프로그램은 GPU 속도로 GPU에서 실행된다. 새로운 그래픽 기술을 사용하고자 하는 프로그래머는 해당 기능을 구현하는 프로그램을 작성하여 GPU에 제공하기만 하면 된다. OpenGL API를 변경할 필요가 없다. API가 지원해야 하는 유일한 것은 프로그램을 GPU로 전송하여 실행할 수 있는 기능이다. 프로그램은 셰이더라고 불린다(비록 대부분 실제로 하는 일을 제대로 설명하지는 않는 용어이다). 소개된 첫 번째 셰이더는 정점 셰이더와 프래그먼트 셰이더였다. 원시 도형이 그려질 때, 원시 도형의 각 정점에서 일부 작업이 수행되어야 한다. 예를 들어, 정점 좌표에 기하학적 변환을 적용하거나 속성과 전역 조명 환경을 사용하여 해당 정점의 색상을 계산하는 등의 작업이다. 정점 셰이더는 이러한 "정점별" 계산을 수행하는 작업을 맡을 수 있는 프로그램이다. 마찬가지로, 원시 도형 내부의 각 픽셀에 대해서도 일부 작업이 수행되어야 한다. 프래그먼트 셰이더는 이러한 ‘픽셀별’ 계산을 수행하는 작업을 맡을 수 있다.(프래그먼트 셰이더는 픽셀 셰이더라고도 불린다.) 프로그래밍 가능한 그래픽 하드웨어의 아이디어는 매우 성공적이었다—그 결과 OpenGL 3.0에서는 일반적인 정점별 및 프래그먼트별 처리가 폐기(사용을 권장하지 않는다는 의미)되었고, OpenGL 3.1에서는 OpenGL 표준에서 제거되었지만, 선택적 확장으로 여전히 존재한다. 실제로, OpenGL의 원래 기능은 데스크탑 버전의 OpenGL에서 여전히 지원되며 아마도 미래에도 계속 사용될 것이다. 그러나, 임베디드 시스템 측면에서는, OpenGL ES 2.0 이후로 셰이더 사용이 의무화되었고 OpenGL 1.1 API의 큰 부분이 완전히 제거되었다. 웹 브라우저에서 사용되는 OpenGL 버전인 WebGL은 OpenGL ES를 기반으로 하며, 무언가를 얻기 위해서는 셰이더 사용이 필수적이다. 그럼에도 불구하고, 우리는 OpenGL 1.1 버전으로 3D 그래픽 프로그래밍을 새로 시작하는 사람에게 더 쉬운 진입점을 제공한다는 점에서 여전히 유용하며, 그 버전에서의 개념과 많은 세부 사항이 여전히 관련이 있기 때문에, 그 버전으로 OpenGL의 학습을 시작할 것이다. OpenGL 셰이더는 GLSL(OpenGL Shading Language)로 작성된다. OpenGL 자체처럼, GLSL은 여러 버전을 거쳐왔다. 우리는 나중에 과정에서 WebGL과 OpenGL ES에서 사용되는 버전인 GLSL ES를 공부하는 데 시간을 할애할 것이다. GLSL은 C 프로그래밍 언어와 유사한 문법을 사용한다.

  • 원래 단순한 기능만 있어도 됐음
  • 그런데 시간이 지날수록 다양한 기능에 대한 요구가 늘어남
  • OpenGL도 처음엔 정해진 기능(고정 파이프라인)만 있었는데, 한계가 분명했음
  • 내가 원하는 특수 기법을 직접 GPU에 코딩해서 실행할 수 있도록 셰이더(Shader) 개념이 추가
  • 셰이더는 정점(버텍스) 단계, 프래그먼트(픽셀) 단계 등에서 GPU가 실행할 코드를 짤 수 있게 해주어, 굉장히 유연하고 혁신적인 그래픽 표현을 가능하게 만듦
  • 2D 일러스트 툴에서도 브러시 엔진을 수정하거나 필터를 만들 때, 별도의 스크립트 언어를 쓰곤 함
  • OpenGL 셰이더를 작성하려면 GLSL이라는 전용 언어를 사용함
  • 문법은 C와 비슷하지만, 벡터/행렬 연산 등 그래픽 특화 기능을 내장하고 있어 GPU 전용 붓 스크립트를 작성하는 느낌

병렬 렌더링

GPU 하드웨어에 대한 마지막 언급으로, 서로 다른 정점에 대해 수행되는 계산은 거의 독립적이므로 잠재적으로 병렬로 수행될 수 있다는 점에 유의해야 한다. 서로 다른 프래그먼트에 대한 계산도 마찬가지다. 실제로 GPU에는 병렬로 작동할 수 있는 수백 또는 수천 개의 프로세서가 있을 수 있다. 물론 개별 프로세서는 CPU보다 훨씬 덜 강력하지만, 일반적인 버텍스별 및 프래그먼트별 계산은 그리 복잡하지 않다. 많은 수의 프로세서와 그래픽 연산에서 가능한 많은 양의 병렬 처리 덕분에 상당히 저렴한 GPU에서도 인상적인 그래픽 성능을 보인다.

  • GPU도 마찬가지로, 엄청난 양의 소형 프로세서가 동시에 정점·픽셀 처리를 분담함
  • 그래서 CPU 하나가 혼자 그릴 때보다 훨씬 빠르게, 복잡한 3D 장면도 실시간으로 뚝딱 완성해낼 수 있음

마치며

  • 키워드들: CPU-GPU 협업 구조, VBO·텍스처 활용, 셰이더 코딩, 병렬 렌더링