개인적으로는 OpenGL ES 3.0을 비롯한 OpenGL 계열을 가르치지 않겠음. 상태 머신과 형편없는 오류 처리 때문에 버그를 추적하기 어려워서임. 대신 이식성이 좋고 오류 메시지가 훌륭한 WebGPU를 가르치겠음.
WebGPU는 이해하기 쉽고 상태 머신이 없으며, C++, Rust, JS/TS 등 여러 언어에서 사용 가능함. 나중에 Metal, D3D12, Vulkan으로 넘어갈 때도 현대적인 GPU 프로그래밍에 더 가까운 출발점이 됨. 학생들이 OpenGL과 ES에서 겪을 수 있는 버그와 호환성 문제도 해결해 줌.
다만 글에서 다루는 Apple 플랫폼에 묶인 학생이 있다면 ANGLE을 사용할 수 있음.
GL의 프로그래밍 모델에 대한 비판에는 대체로 동의하지만, WebGL2에서 해결된 부분도 있음. 검증과 오류 보고가 훨씬 좋아졌고, 브라우저 콘솔에 유용한 오류 메시지도 표시됨. 현재의 WebGPU까지 포함해도 버그가 가장 적고 이식성이 가장 좋은 3D API 구현일 가능성이 큼.
교육에는 대체로 브라우저의 WebGL2나 WebGPU를 쓰겠음. 첫 삼각형을 그리기까지 필요한 준비 코드가 훨씬 적고, 수정·테스트 주기가 거의 즉각적이기 때문임.
하지만 WebGPU는 웹용으로 설계되고 대략 2015년 무렵의 하드웨어를 대상으로 삼은 API라는 한계를 계속 안고 갈 것임. OpenGL보다는 낫지만, Chrome 팀이 추진하고 싶어 하는 것 외에 큰 발전을 기대하기는 어려워 보임. 버전 1.0 이후의 방향에 Firefox나 Safari 팀이 얼마나 발언권을 가질지도 의문임.
동의하지 않음. WebGPU는 이미 낡았고 쓸모없는 중간 계층이며, 이것 때문에 WebGL의 컴퓨트 셰이더 제안이 무산된 것도 불만임. 브라우저에서 별도 설정 없이 작동하지 않는 경우도 여전함.
WebAssembly에서 WebGPU를 호출하면 WebGL보다 느릴 때도 많음. API가 더 저수준이라 JS 쪽의 가비지 컬렉션 부담이 커지기 때문임.
수많은 뛰어난 프로그래머가 왜 OpenGL에 매료됐는지 도무지 이해할 수 없음. 내가 라이브러리를 설계할 때 지키려는 가장 중요한 원칙인 숨겨진 상태 최소화를 정면으로 위반하기 때문임.
OpenGL은 온통 숨겨진 상태뿐임. 수천 줄 앞이나 접근할 수도 없는 다른 모듈에서 내부 상태를 어떻게 설정했는지 모두 확인하지 않고서는, 어떤 코드가 무엇을 렌더링할지 알 수 없음.
1990년대에 훌륭한 OpenGL 강좌를 만든 NeHe(Neon Helium) 에게 감사를 전하고 싶음. 나뿐 아니라 많은 사람이 그 강좌에서 큰 도움을 받았을 것임.
내 경력 자체를 NeHe 강좌 덕분이라고 할 수 있을 듯함. 예전 고정 기능 파이프라인 OpenGL API를 배워 게임을 만들었고, 그 게임을 본 가족이 자기 회사의 인턴 면접을 권해 줬음. 그렇게 시작한 소프트웨어 엔지니어 경력이 지금까지 20년 이어지고 있음.
강좌와 실습 예제 덕분에 GPU 프로그래밍을 이해하게 됐음. 당시 Khronos는 초기 단계였고 GLES 확장과 WebGL 생태계도 막 시작하던 때라 특히 좋았음. 고정 기능 파이프라인, 버퍼·텍스처·유니폼의 제약처럼 지금 기술이 어디에서 출발했는지 먼저 배우는 것도 의미가 큼. 왜, 언제 이런 구조가 필요한지 이해하면 셰이더 연산 파이프라인 관점으로 생각하기가 훨씬 쉬워짐.
어떤 API를 사용할 때보다 소프트웨어 래스터라이저와 광선 추적기를 직접 작성하면서 그래픽스를 더 많이 배웠음. GPU 렌더링 자체를 가르치는 목적이라면 OpenGL이 다른 API보다 교육에 적합함. 하지만 그래픽스와 렌더링 개념을 가르치는 목적이라면 OpenGL 같은 API는 괴롭고 오히려 역효과를 냄.
그래도 최신 API보다는 OpenGL이 낫고, 레거시 OpenGL은 더 낫다고 봄. 원하는 부분은 소프트웨어로 구현하고 래스터화 같은 특정 부분만 GL에 맡기면 준비 코드가 거의 필요 없음.
최신 API에서는 본론에 도달하려고 수백 줄을 써야 함. 무게중심 좌표를 배워야 할 시간에 디스크립터 집합을 배우고 있는 셈임. 그러면서도 래스터화, 텍스처 매핑, 클리핑, 면 컬링은 고정 기능으로 남아 있어 내부를 배우지 못함.
CPU 렌더링만으로 배우는 방식의 단점은 멋진 결과를 보기까지 인내심이 필요하다는 것임. 그 결과에 필요한 모든 것을 먼저 배우고 구현해야 하기 때문임.
동의하지 않음. 그래픽스 전체를 OpenGL만으로 설명하려 하면 당연히 실패하며, 래스터화 중심인 OpenGL에서 광선 추적을 하려면 지저분한 우회 방법이 필요함.
하지만 나를 고성능 그래픽스와 병렬 영상 처리로 이끈 깨달음은 프래그먼트 셰이더의 제약에서 나왔음. 같은 출력 버퍼의 이웃 픽셀에 접근하지 못한 채, 최종 픽셀을 각각 독립적으로 계산하도록 사고를 바꿔야 했음.
Vulkan보다 단순하면서도 어느 정도 작업을 대신 처리해 주고, 순수 소프트웨어 구현과 달리 정해진 셰이더 파이프라인의 제약이 있는 OpenGL이 내게는 꼭 맞았음. 그래픽스 프로그래밍의 훌륭한 입문점이라고 봄.
OpenGL, DirectX, Vulkan, WebGL은 애초에 그래픽스 개념을 가르치려는 도구가 아님. 학습 편의성보다 성능을 우선해 설계된 API임.
교육용으로는 소프트웨어로 래스터화와 광선 추적을 먼저 구현하는 방식이 좋음. 개념을 먼저 익힐 수 있고, 더 빠르게 실행하고 싶어질 때 그래픽스 API를 배울 동기도 생김.
소프트웨어 래스터라이저와 광선 추적기 구현을 배울 만한 자료를 추천해 줄 수 있나요?
한 학기 그래픽스 수업에서는 OpenGL 4.5/4.6이 적절한 균형점이라고 느꼈음. 다른 선택지로는 SDL_GPU도 괜찮은 중간 단계라고 봄.
나도 그래픽스 개념과 함께 새 API인 OpenGL, 새 패러다임인 GPU 프로그래밍, 새 언어인 GLSL을 한꺼번에 배우는 일이 버거웠음. OpenGL 4.5와 4.6은 입문하기에 충분히 현대적이며, 기반으로 삼을 만한 3.3 이상 버전의 학습 자료도 좋음. 이런 선택의 이유를 다룬 논문은 내 웹사이트 mshah.io에서 볼 수 있음.
한동안 4.6 기반 강좌를 만들고 있음. 첫 공개가 가까워지면 검토를 부탁하고 싶음.
예전 GL은 작업마다 전용 함수가 많아 초보자에게 단계별로 가르치기 편해 보임. 하지만 실제로 전달하는 내용 상당수는 숨겨진 상태 머신을 조작하는 복잡한 인터페이스에 관한 것임. 하나씩 처리하는 옛 인터페이스는 본격적인 규모에서 통하지 않는 나쁜 습관도 만들기 쉬움.
4.6에서는 Direct State Access, 프로그래밍 가능한 정점 가져오기, MultidrawIndirect를 사용할 수 있음. 수많은 상태 조작 함수 대신, malloc과 memcpy로 구조체 배열을 다루는 것과 비슷한 소수의 함수로 복잡성을 옮길 수 있음.
간단하지는 않지만 익숙한 방식이어야 함. 상태가 눈에 보이고 유연하며 규모 확장에도 적합해서, 3D 렌더링의 기초를 익힌 뒤 Vulkan으로 넘어가기에도 좋음.
Adam Sawicki처럼 OpenGL의 현재 중요성이 줄었다고 보지만, 현대 기술이 어디에서 왔는지 학생들에게 가르치는 가치는 있다고 봄.
다만 한 달 정도의 입문을 넘어 오래 집중할 이유는 모르겠음. 강좌와 간단한 예제를 거쳐 한계를 보여 준 뒤, Vulkan/WGPU로 넘어가면 됨. DX12와 Metal도 비슷한 원칙을 따른다는 배경을 함께 알려 주면 좋겠음.
그 전환도 나름의 지뢰밭임. DirectX 12는 다른 API와 상당히 다른 독특한 체계이고, Vulkan은 이미 세 차례의 아키텍처 개편으로 복잡해졌음. WGPU는 현재 모델이 아니라 낡은 초기 Vulkan 모델을 기반으로 함. 이 가운데 3D 그래픽스 입문자가 합리적으로 시작할 수 있는 것은 Metal뿐임.
원글 작성자임! 마침 약간의 변형을 가미한 현대적인 Vulkan으로 새 강의 모듈을 준비하고 있음. 이번 전환의 결과를 공유할 날이 기대됨.
CPU에 비해 GPU 프로그래밍은 왜 이렇게 복잡한지 궁금했음. 언어 패러다임 때문인지, 하드웨어 설계의 한계 때문인지? 그래픽스 API, 특히 Vulkan을 볼 때마다 드는 의문임.
대부분 하드웨어 설계의 제약 때문임. GPU 하드웨어는 CPU보다 훨씬 다양하므로 셰이더를 소스 코드나 이식 가능한 중간 표현으로 배포하고, 실행 시 사용자의 GPU에 맞게 컴파일해야 함. 하드웨어마다 동작이 크게 다른 고정 기능도 추상화해야 함.
GPU는 CPU와 병렬로 실행되고 별도 RAM을 쓰는 경우도 많아, 암묵적이든 명시적이든 동기화와 데이터 전송이 필요함. 특히 오류가 발생했을 때 복잡성이 크게 늘어남.
이런 복잡한 요소들이 왜 필요하며 구형 하드웨어 지원을 포기하면 얼마나 제거할 수 있는지 알고 싶다면, Sebastian Aaltonen이 실제 구현까지 한 No Graphics API 글을 보면 됨. https://www.sebastianaaltonen.com/blog/no-graphics-api/
상당 부분은 역사적 우연 때문이라고 봄. CPU의 명령어 집합도 비공개이고 지식재산이 엄격히 보호됐다면, CPU를 사용하려고 드라이버와 OpenCPU API가 필요했을 것임. 공개된 CPU 명령어 집합은 사실상 하드웨어에 내장된 드라이버 인터페이스임.
또 CPU는 단일 스레드 성능에, GPU는 개별 작업은 느리더라도 수천 개의 작은 작업을 병렬로 수행하는 데 초점을 맞춤. 이 때문에 하나의 main 함수로 앱을 실행하는 방식과, 수백 개 스레드에 작은 전문 작업을 분산하는 방식으로 프로그래밍 모델이 갈림. Vulkan은 마법 같은 추상화 없는 명시적 저수준 API이면서, 특히 다양한 모바일 GPU를 포함한 서로 다른 아키텍처도 지원하려 했음. 상충하는 요구를 동시에 해결하려 했으니 이런 복잡한 결과는 예상 가능했음.
일부는 역사적 잔재와 경로 의존성 때문임. GPU는 범용 병렬 연산 장치가 아니라 특정 고정 기능으로 그래픽스를 가속하는 장치에서 출발했음. 점차 파이프라인이 복잡하고 유연해졌고, 그래픽스 외 작업에 활용하던 편법이 CUDA와 OpenCL로 정식화됐음.
둘 중 승자가 된 CUDA는 비공개이며 사실상 NVIDIA 외 GPU에서 사용할 수 없음. NVIDIA도 과학 연산과 이후 AI에서 얻은 경쟁 우위를 경쟁사에 개방하고 싶어 하지 않음.
이후 AMD의 Mantle을 기반으로 나온 Vulkan은 불완전한 추상화와 그래픽스·범용 GPU 연산 간의 분리를 해소하려 했음. 복잡성을 감수하는 대신 더 많은 제어권과 성능 최적화 여지를 주고, 각 애플리케이션에 필요한 추상화는 그 위에 직접 만들라는 접근임.
GPU가 처음부터 게임보다 병렬 연산을 우선했다면 하드웨어와 언어가 지금과 다르고 더 단순했을 가능성이 큼. 두 회사의 경쟁이 방해하지 않았다면 더욱 그랬을 것임. 얼마 전까지도 GPU 드라이버는 특정 게임의 성능을 고치려고 업데이트됐는데, 지금도 그러는지는 모르겠음.
네트워크 애플리케이션으로 생각하니 이해하기 쉬웠음. CPU에서 클라이언트가 실행되고, GPU에서는 서버인 셰이더가 실행되는 구조임.
클라이언트와 서버, 통신 규약을 모두 작성해야 하고 서버를 시작하는 책임까지 클라이언트에 있으니 복잡해짐. 통신이 가장 느린 구간이라는 점도 비슷해서, 네트워크 호출에 해당하는 드로 호출을 최소화해야 함.
여기에 연성 실시간 요구까지 더해져 전체가 성능 중심으로 돌아감. Python으로 Flask 앱을 만드는 것보다는 C++로 사용자 공간 IP 스택을 직접 작성하는 쪽에 가까움.
성능을 끝까지 끌어내려면 CPU도 어렵고, GPU보다 더 어려울 수도 있음. 다만 최적화하지 않은 코드에 대해 GPU가 성능 저하로 치르게 하는 대가가 대체로 더 큼.
GLES는 그래픽스를 가르치기에 나쁘지 않지만 GLES와 Vulkan의 중간 지점이 있으면 좋겠음.
GL의 최악인 부분은 명세를 공부하지 않으면 이해할 수 없는 숨겨진 전역 상태임. 사실상 GL이면서도 내부에 스레드별 전역 싱글턴을 숨겨 두지 않은 API가 필요함.