사전 WebGL
표준

WebGL

gabury1고친 사람 github-actions[bot]

WebGL 은 웹페이지가 그래픽 카드를 써서 그림을 그리게 해 주는 약속입니다. 자바스크립트에서 함수를 부르면 브라우저가 그 호출을 그래픽 카드로 넘깁니다. 그래서 플러그인을 깔지 않아도 웹페이지 안에서 3D 장면이 빠르게 돕니다. 함수 목록은 데스크톱과 모바일에서 쓰던 그래픽스 규격을 거의 그대로 가져왔습니다.

쉽고 빠른 이해

WebGL 은 웹페이지의 자바스크립트가 그래픽 카드에게 그림을 시키는 함수 목록입니다. 지도 서비스의 3D 건물이나 브라우저에서 도는 게임이 이 함수로 그려집니다.

이게 없으면 웹페이지는 그림을 컴퓨터의 일반 계산 칩으로만 그려야 합니다. 점이 수십만 개인 장면을 매 프레임 다시 그리기에는 너무 느립니다.

어떻게 도나:

  1. 페이지에 그림판 요소를 하나 두고 거기서 WebGL 을 꺼냅니다
  2. 점 좌표를 그래픽 카드 메모리로 올립니다
  3. 그래픽 카드에서 돌 작은 프로그램을 글자로 넘깁니다
  4. 그리라는 함수를 부르면 브라우저가 검사를 거쳐 그래픽 카드에 전달합니다

대가도 있습니다. 함수가 아주 낮은 수준이라 삼각형 하나를 띄우는 데도 코드가 깁니다. 브라우저가 호출마다 검사를 하므로 같은 일을 네이티브 앱에서 할 때보다 부담이 붙습니다.

그러니 3D 장면이 필요하면 보통 WebGL 을 감싼 라이브러리를 씁니다. 그리기 단계를 하나하나 직접 다뤄야 할 때만 WebGL 함수를 바로 부릅니다. 차트나 단순한 2D 그림이라면 WebGL 까지 갈 일이 없습니다.

상세

WebGL(Web Graphics Library, 웹 그래픽스 라이브러리)은 브라우저의 자바스크립트에서 그래픽 카드로 그림을 그리는 API 규격입니다. API(Application Programming Interface, 응용 프로그램 인터페이스)는 프로그램이 다른 소프트웨어의 기능을 부를 때 쓰는 함수 목록입니다.

그래픽 카드 안에서 그림 계산을 맡는 칩을 GPU(Graphics Processing Unit, 그래픽 처리 장치)라고 부릅니다. GPU 는 점 수십만 개의 위치와 픽셀 색을 한꺼번에 나눠 계산합니다. 앞에서 말한 일반 계산 칩, 곧 CPU(Central Processing Unit, 중앙 처리 장치)로만 그릴 때보다 매 프레임 장면을 다시 그리기가 훨씬 수월합니다.

WebGL 은 설치하는 프로그램이 아니라 문서입니다. 함수 이름과 인자, 부른 뒤에 일어날 일을 정합니다. 그 함수를 실제로 돌리는 코드는 각 브라우저가 만들어 넣습니다.

아래에서는 바탕 규격, 그리는 과정, 호출이 거치는 길, 실패하는 경우, 두 판과 대가를 차례로 봅니다.

바탕이 된 규격

WebGL 의 함수 목록은 OpenGL ES 에서 왔습니다. 그 뿌리는 OpenGL(Open Graphics Library, 오픈 그래픽스 라이브러리)입니다. OpenGL 은 데스크톱 컴퓨터에서 오래 써 온 그래픽스 규격입니다.

OpenGL ES(OpenGL for Embedded Systems)는 OpenGL 에서 기능을 덜어 휴대폰 같은 작은 기기에 맞춘 규격입니다. WebGL 과 함수 이름도 거의 같습니다. OpenGL ES 에서 glClear 인 함수가 WebGL 에서는 gl.clear 입니다.

바탕을 새로 만들지 않고 가져온 데는 까닭이 있습니다. 휴대폰까지 포함한 대부분의 기기가 이미 OpenGL ES 를 돌릴 수 있었습니다. 덕분에 브라우저가 WebGL 호출을 기기의 그래픽스 기능으로 옮기기 쉬웠습니다.

두 규격 모두 Khronos Group 이라는 업계 단체가 관리합니다. 이 단체는 문서만 씁니다. 구현은 브라우저 개발사가 합니다.

캔버스에서 컨텍스트를 꺼낸다

WebGL 은 HTML(HyperText Markup Language, 하이퍼텍스트 마크업 언어)의 canvas 요소 위에 그립니다. canvas 요소는 페이지 안에 픽셀로 된 빈 그림판을 하나 만드는 태그입니다. 그림판만 있으면 아무것도 그려지지 않습니다.

그림을 그리려면 그림판에서 렌더링 컨텍스트를 꺼내야 합니다. 렌더링 컨텍스트는 그리기 함수와 지금 켜진 설정을 한데 담은 객체입니다. 같은 canvas 요소에서 2D 용 컨텍스트를 꺼내면 Canvas API 가 됩니다. "webgl" 을 대면 WebGL 이 됩니다.

아래 코드는 컨텍스트를 꺼내 그림판 전체를 검은색으로 칠합니다.

JavaScript
const canvas =
  document.querySelector("canvas");
const gl =
  canvas.getContext("webgl"); // 없으면 null
gl.clearColor(0, 0, 0, 1);    // 검정 지정
gl.clear(gl.COLOR_BUFFER_BIT); // 전체 칠함

getContext 는 기기나 브라우저가 WebGL 을 못 쓰면 오류를 던지지 않고 null 을 돌려줍니다. 실제 코드는 여기서 한 번 확인하고 넘어갑니다. clearColor 는 색만 정해 둘 뿐 칠하지 않습니다. 칠하는 일은 다음 줄의 clear 가 합니다.

데이터와 셰이더를 넘기고 그린다

그리기는 세 단계로 나뉩니다. 먼저 데이터를 올립니다. 다음에 GPU 에서 돌 프로그램을 넘깁니다. 그다음 그리라고 부릅니다.

첫째는 정점 좌표를 GPU 메모리로 올리는 일입니다. 정점은 좌표와 색 같은 값을 가진 점 하나입니다. 삼각형 하나는 정점 셋으로 그립니다.

정점 값을 담아 GPU 메모리에 올려 두는 그릇이 버퍼 객체입니다. 자바스크립트 배열을 매 프레임 넘기면 느립니다. 한 번 올려 두면 GPU 가 거기서 바로 읽습니다.

둘째는 셰이더를 넘기는 일입니다. 셰이더는 GPU 에서 도는 작은 프로그램입니다. 점 수십만 개를 자바스크립트로 하나씩 계산하면 느립니다. 셰이더를 넘기면 GPU 가 점마다 이 프로그램을 동시에 돌립니다.

셰이더는 두 가지를 한 쌍으로 넘깁니다. 정점 셰이더는 정점마다 한 번 돌아서 그 점을 화면 어디에 둘지 정합니다.

프래그먼트 셰이더는 색을 정합니다. 프래그먼트는 삼각형이 덮는 자리마다 생기는 픽셀 후보입니다. 프래그먼트 셰이더는 이 후보마다 한 번 돌아서 무슨 색으로 칠할지 정합니다.

셰이더는 GLSL ES(OpenGL ES Shading Language, OpenGL ES 셰이딩 언어)로 씁니다. C 와 닮은 언어입니다. 자바스크립트는 이 소스를 문자열로 넘깁니다. 브라우저가 그 문자열을 실행 중에 컴파일합니다. 컴파일 오류도 실행 중에야 알 수 있습니다.

셋째는 그리라는 함수를 부르는 일입니다. 함수는 둘입니다. 무엇이 다른지는 정점을 고르는 방법에 있습니다.

함수 정점을 고르는 법
drawArrays 버퍼에 담긴 순서대로 앞에서부터 씁니다
drawElements 따로 둔 인덱스 목록이 가리키는 정점을 씁니다

인덱스는 정점 버퍼 안에서 몇 번째 정점인지를 적은 번호입니다. 사각형은 삼각형 둘로 그립니다. 두 삼각형은 꼭짓점 둘을 나눠 씁니다. drawArrays 로는 정점 여섯 개를 올려야 합니다. drawElements 로는 정점 넷과 번호 여섯 개로 끝납니다.

호출이 GPU 까지 가는 길

자바스크립트는 GPU 에 직접 닿지 못합니다. WebGL 함수를 부르면 브라우저가 그 호출을 받아 한 번 검사합니다. 검사를 통과한 호출만 운영체제의 그래픽스 API 로 옮겨 부릅니다.

운영체제의 그래픽스 API 는 앱이 GPU 를 쓰려고 부르는 함수 목록입니다. Windows 의 Direct3D, macOS 의 Metal 이 그런 예입니다. 그 아래의 그래픽 드라이버는 GPU 제조사가 만든 소프트웨어입니다. 함수 호출을 그 회사 GPU 가 알아듣는 명령으로 바꿉니다.

flowchart TD
    A["페이지의 자바스크립트 · gl.drawArrays 호출"] --> B["브라우저의 WebGL 구현 · 인자와 셰이더 검사"]
    B --> C["운영체제의 그래픽스 API · Direct3D, Metal 등"]
    C --> D["그래픽 드라이버"]
    D --> E["GPU"]

그림에서 페이지 코드는 맨 위 한 칸만 봅니다. 운영체제의 그래픽스 API 가 무엇이든, 드라이버를 어느 회사가 만들었든 같은 함수를 부릅니다. 한 번 짠 코드가 여러 기기에서 도는 것은 이 구조 덕분입니다.

가운데 검사 단계는 WebGL 만의 사정입니다. 웹페이지 코드는 누가 썼는지 믿을 수 없습니다. 브라우저는 버퍼 범위를 넘는 읽기나 잘못된 셰이더가 드라이버까지 닿지 않게 막습니다. 드라이버의 버그를 페이지가 건드려 기기를 멈추거나 남의 메모리를 읽는 일을 막으려는 것입니다.

같은 이유로 다른 사이트의 이미지를 텍스처로 쓰는 데도 제한이 있습니다. 텍스처는 GPU 메모리에 올려 두고 삼각형 표면에 입히는 이미지입니다. 그런데 WebGL 에는 그려진 픽셀 값을 자바스크립트로 읽어 오는 함수 readPixels 가 있습니다.

그래서 그 사이트가 CORS(Cross-Origin Resource Sharing, 교차 출처 리소스 공유)로 허락하지 않은 이미지는 WebGL 에 올릴 수 없습니다. 허락 없이 올리게 두면 그린 뒤 픽셀을 읽어 내 남의 이미지 내용을 빼 갈 수 있기 때문입니다.

오류를 알아내는 법

WebGL 함수는 잘못 불러도 대개 예외를 던지지 않습니다. 호출을 무시하고 내부에 오류 표시만 남깁니다. gl.getError() 를 불러야 그 표시를 읽습니다.

JavaScript
gl.enable(12345); // 없는 설정값
gl.getError();    // gl.INVALID_ENUM
gl.getError();    // gl.NO_ERROR

첫 getError 가 표시를 읽으면서 지웁니다. 두 번째 호출은 오류 없음을 돌려줍니다. 화면이 비어 있고 콘솔도 조용할 때가 있습니다. 대개 이 표시를 안 읽은 경우입니다.

컨텍스트를 잃는 경우

WebGL 에는 일반 웹 API 에 없는 실패가 하나 더 있습니다. 컨텍스트 자체가 사라지는 컨텍스트 손실입니다. GPU 는 여러 탭과 프로그램이 나눠 쓰는 자원이라, 브라우저나 운영체제가 필요할 때 거둬 갑니다.

그래픽 드라이버가 다시 시작되거나 GPU 메모리가 모자랄 때 일어납니다. 그러면 canvas 요소에 webglcontextlost 이벤트가 옵니다. 이때 GPU 메모리에 올려 둔 버퍼와 텍스처와 셰이더는 전부 없어집니다.

컨텍스트가 돌아오면 webglcontextrestored 이벤트가 옵니다. 페이지는 이 이벤트를 받아 데이터와 셰이더를 처음부터 다시 올려야 합니다. 이 처리를 안 해 두면 화면이 한 번 검게 변한 뒤 다시 살아나지 않습니다.

두 판

WebGL 은 두 판이 있습니다. 판마다 바탕으로 삼은 OpenGL ES 의 판이 다릅니다.

판 바탕 규격 꺼낼 때 쓰는 이름
WebGL 1.0 OpenGL ES 2.0 "webgl"
WebGL 2.0 OpenGL ES 3.0 "webgl2"

WebGL 1 에는 확장이라는 덧붙임 기능이 있습니다. 확장은 브라우저마다 있을 수도 없을 수도 있는 기능이라, 페이지가 이름으로 요청해서 있을 때만 씁니다.

WebGL 2 는 이 확장으로만 쓸 수 있던 기능 여럿을 기본으로 넣었습니다. 한 번 그려서 결과 이미지 여러 장을 동시에 채우는 기능이 그 하나입니다. 인스턴싱도 들어갔습니다. 인스턴싱은 같은 모양을 위치만 바꿔 한 번의 호출로 여러 개 그리는 방법입니다.

대가와 뒤를 잇는 규격

WebGL 의 함수는 아주 낮은 수준입니다. 삼각형 하나를 띄우는 데도 버퍼 만들기, 셰이더 컴파일, 버퍼의 값을 셰이더 입력에 잇는 일을 전부 손으로 써야 합니다. 실무에서는 이 과정을 감싼 3D 라이브러리를 얹어 쓰는 경우가 많습니다.

설계도 오래된 규격을 따릅니다. OpenGL ES 처럼 켜 둔 설정이 전역으로 남는 상태 기계 방식이라, 한 곳에서 바꾼 설정이 다른 그리기에 번지기 쉽습니다. GPU 로 그림이 아닌 범용 계산을 돌리는 기능도 거의 없습니다.

이 한계를 두고 새로 만든 브라우저 그래픽스 규격이 WebGPU 입니다. WebGPU 는 OpenGL ES 를 바탕으로 삼지 않습니다. Vulkan 같은 요즘 그래픽스 API 의 설계를 따라 처음부터 다시 정했습니다.

WebGPU 는 두 한계에 각각 답합니다. 그리기 설정을 전역에 켜 두지 않습니다. 미리 만든 설정 묶음 객체를 그릴 때마다 지정합니다. 그림이 아닌 계산만 GPU 에서 돌리는 계산 셰이더도 따로 둡니다.

관련 항목

WebGL 이 속하는 상위 분류

그래픽스 · 렌더링 · API · 그래픽스 API · 웹 API

WebGL 의 바탕이 되거나 나란히 서는 규격

OpenGL · OpenGL ES · WebGL 2 · WebGPU · Vulkan · Khronos Group

WebGL 이 붙는 브라우저 쪽 장치

브라우저 · canvas 요소 · Canvas API · 렌더링 컨텍스트 · OffscreenCanvas · DOM · 자바스크립트

WebGL 로 그리기를 거치는 처리 단계

그래픽스 파이프라인 · 정점 셰이더 · 프리미티브 · 래스터화 · 프래그먼트 셰이더 · 깊이 테스트 · 블렌딩 · 프레임버퍼

WebGL 로 다루는 GPU 쪽 객체

버퍼 객체 · 텍스처 · 셰이더 · 프로그램 객체 · 프레임버퍼 객체 · 유니폼 · 정점 속성

WebGL 의 그리기 함수와 그 입력

drawArrays · drawElements · 정점 · 인덱스 · 인덱스 버퍼 · 인스턴싱

WebGL 셰이더를 쓰는 언어

GLSL ES · GLSL · WGSL

WebGL 이 실패하는 경우와 막는 장치

컨텍스트 손실 · CORS · 동일 출처 정책 · GPU · 그래픽 드라이버

WebGL 위에 얹어 쓰는 라이브러리

3D 라이브러리 · Three.js · Babylon.js · glTF

다른 이름: Web Graphics Library · 웹GL · WebGL API