Skip to content

Commit 0935064

Browse files
author
태재영
committed
ko: smallest programs
1 parent accd284 commit 0935064

File tree

2 files changed

+331
-3
lines changed

2 files changed

+331
-3
lines changed

webgl/lessons/ko/webgl-planar-projection-mapping.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,12 +96,12 @@ gl.bindTexture(gl.TEXTURE_2D, checkerboardTexture);
9696
gl.texImage2D(
9797
gl.TEXTURE_2D,
9898
0, // mip level
99-
gl.LUMINANCE, // internal format
99+
gl.LUMINANCE, // 내부 포맷
100100
8, // 너비
101101
8, // 높이
102102
0, // 테두리
103-
gl.LUMINANCE, // format
104-
gl.UNSIGNED_BYTE, // type
103+
gl.LUMINANCE, // 포맷
104+
gl.UNSIGNED_BYTE, // 타입
105105
new Uint8Array([ // 데이터
106106
0xFF, 0xCC, 0xFF, 0xCC, 0xFF, 0xCC, 0xFF, 0xCC,
107107
0xCC, 0xFF, 0xCC, 0xFF, 0xCC, 0xFF, 0xCC, 0xFF,
Lines changed: 328 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,328 @@
1+
Title: WebGL 최소 프로그램
2+
Description: 테스트를 위한 최소한의 코드
3+
TOC: 최소 프로그램
4+
5+
이 글은 [기초](webgl-fundamentals.html)로 시작하여 여러 다른 글을 읽었다고 가정합니다.
6+
아직 읽지 않았다면 거기부터 시작해주세요.
7+
8+
두 가지 목적이 있어서 이 글을 어떻게 정리해야 할지 잘 모르겠습니다.
9+
10+
1. 최소한의 WebGL 프로그램을 보여 드립니다.
11+
12+
이러한 기술은 무언가를 테스트하거나 [Stack Overflow를 위한 MCVE](https://meta.stackoverflow.com/a/349790/128511)를 만들 때 혹은 버그의 범위를 좁히려고 할 때 굉장히 유용합니다.
13+
14+
2. 틀 밖에서 생각하는 방법 배웁니다.
15+
16+
일반적인 패턴 뿐 아니라 더 큰 그림을 볼 때 도움이 되도록 이에 대한 더 많은 글을 작성하려 합니다.
17+
하나는 [여기](webgl-drawing-without-data.html)에 있습니다.
18+
19+
## 그냥 지우기
20+
21+
다음은 무언가를 하는 최소 WebGL 프로그램입니다.
22+
23+
```js
24+
const gl = document.querySelector('canvas').getContext('webgl');
25+
gl.clearColor(1, 0, 0, 1); // 빨간색
26+
gl.clear(gl.COLOR_BUFFER_BIT);
27+
```
28+
29+
이 프로그램이 하는 것은 캔버스를 빨간색으로 지우는 게 전부지만 실제로는 무언가 했습니다.
30+
31+
이것만으로도 실제로 몇 가지 테스트를 할 수 있는데요.
32+
[텍스처에 렌더링](webgl-render-to-texture.html)하는데 작동하지 않는다고 가정해봅시다.
33+
그리고 [해당 글](webgl-render-to-texture.html)의 예제와 같다고 가정해봅시다.
34+
하나 이상의 3D 물체를 텍스처에 렌더링한 다음 그 결과를 큐브에 렌더링합니다.
35+
36+
아무것도 안 보이는데요.
37+
간단한 테스트로서 알려진 색상으로 텍스처를 지우는 셰이더로 텍스처에 렌더링하는 것을 멈춰봅시다.
38+
39+
```js
40+
gl.bindFramebuffer(gl.FRAMEBUFFER, framebufferWithTexture)
41+
gl.clearColor(1, 0, 1, 1); // 자홍색
42+
gl.clear(gl.COLOR_BUFFER_BIT);
43+
```
44+
45+
이제 프레임버퍼의 텍스처로 렌더링합니다.
46+
큐브가 자홍색으로 돌아갔나요?
47+
아니라면 텍스처에 렌더링하는 부분이 아니라 다른 문제입니다.
48+
49+
## `SCISSOR_TEST` 그리고 `gl.clear` 사용
50+
51+
`SCISSOR_TEST`는 그리기와 지우기 모두 캔버스(혹은 현재 프레임버퍼)의 하위 사각형으로 자릅니다.
52+
다음과 같이 작성하여 scissor test를 활성화합니다.
53+
54+
```js
55+
gl.enable(gl.SCISSOR_TEST);
56+
```
57+
58+
그런 다음 왼쪽 하단 모서리를 기준으로 scissor rectangle을 픽셀 단위로 설정합니다.
59+
이는 `gl.viewport`와 같은 매개 변수를 사용합니다.
60+
61+
```js
62+
gl.scissor(x, y, width, height);
63+
```
64+
65+
이렇게 하면 `SCISSOR_TEST``gl.clear`를 사용하여 사각형을 그릴 수 있습니다.
66+
67+
예시:
68+
69+
```js
70+
const gl = document.querySelector('#c').getContext('webgl');
71+
72+
gl.enable(gl.SCISSOR_TEST);
73+
74+
function drawRect(x, y, width, height, color) {
75+
gl.scissor(x, y, width, height);
76+
gl.clearColor(...color);
77+
gl.clear(gl.COLOR_BUFFER_BIT);
78+
}
79+
80+
for (let i = 0; i < 100; ++i) {
81+
const x = rand(0, 300);
82+
const y = rand(0, 150);
83+
const width = rand(0, 300 - x);
84+
const height = rand(0, 150 - y);
85+
drawRect(x, y, width, height, [rand(1), rand(1), rand(1), 1]);
86+
}
87+
88+
89+
function rand(min, max) {
90+
if (max === undefined) {
91+
max = min;
92+
min = 0;
93+
}
94+
return Math.random() * (max - min) + min;
95+
}
96+
```
97+
98+
{{{example url="../webgl-simple-scissor.html"}}}
99+
100+
Not saying that particular one is all that useful but still it's good to know.
101+
102+
## `gl.POINTS` 하나 사용
103+
104+
대부분의 예제에서 볼 수 있듯이, WebGL에서 가장 일반적인 수행하는 작업은 버퍼 생성입니다.
105+
이러한 버퍼에 정점 데이터를 넣습니다.
106+
속성이 있는 셰이더를 만듭니다.
107+
버퍼에서 데이터를 가져오도록 속성을 설정합니다.
108+
그런 다음 셰이더에서도 사용되는 uniform과 텍스처를 사용하여 그립니다.
109+
110+
하지만 간혹 테스트하고 싶을 때가 있습니다.
111+
무언가 그리는 걸 보고 싶다고 가정해봅시다.
112+
113+
이런 셰이더 세트는 어떨까요?
114+
115+
```glsl
116+
// vertex shader
117+
void main() {
118+
gl_Position = vec4(0, 0, 0, 1); // 중앙
119+
gl_PointSize = 120.0;
120+
}
121+
```
122+
123+
```glsl
124+
// fragment shader
125+
precision mediump float;
126+
127+
void main() {
128+
gl_FragColor = vec4(1, 0, 0, 1); // 빨간색
129+
}
130+
```
131+
132+
그리고 이를 사용하는 코드는 다음과 같습니다.
133+
134+
```js
135+
// GLSL 프로그램 설정
136+
const program = webglUtils.createProgramFromSources(gl, [vs, fs]);
137+
138+
gl.useProgram(program);
139+
140+
const offset = 0;
141+
const count = 1;
142+
gl.drawArrays(gl.POINTS, offset, count);
143+
```
144+
145+
생성하는 버퍼도 없고, 설정하는 uniform도 없으며, 캔버스 중앙에 하나의 점만 있습니다.
146+
147+
{{{example url="../webgl-simple-point.html"}}}
148+
149+
> NOTE: Safari pre 15는 이 기능에 대한 [WebGL 적합성 테스트](https://www.khronos.org/registry/webgl/sdk/tests/conformance/rendering/point-no-attributes.html?webglVersion=1&quiet=0)를 통과하지 못 했습니다.
150+
151+
`gl.POINTS` 정보: `gl.POINTS``gl.drawArrays`로 전달할 때 픽셀 단위의 크기로 정점 셰이더의 `gl_PointSize`도 설정해야 합니다.
152+
GPU/드라이버마다 사용할 수 있는 최대 점 크기가 다르다는 점에 주의하세요.
153+
최대 크기는 다음과 같이 쿼리할 수 있습니다.
154+
155+
```
156+
const [minSize, maxSize] = gl.getParameter(gl.ALIASED_POINT_SIZE_RANGE);
157+
```
158+
159+
WebGL 스펙은 오직 최대 크기 1.0을 필요로 합니다.
160+
다행히도 [모든 GPU와 드라이버는 아니지만 대부분이 더 큰 크기를 지원](https://webglstats.com/webgl/parameter/ALIASED_POINT_SIZE_RANGE)합니다.
161+
162+
`gl_PointSize`를 설정한 다음 정점 셰이더가 종료될 때, `gl_Position`에 설정한 모든 값은 픽셀 단위의 화면/캔버스 공간으로 변환되며, 네 방향 모두에 +/- gl_PointSize / 2인 위치에 정사각형이 생성됩니다.
163+
164+
누가 점 하나 그리는 걸 원하겠다고 생각하실 수 있습니다.
165+
166+
점은 자동적으로 비어있는 [텍스처 좌표](webgl-3d-textures.html)를 가집니다.
167+
Fragment shader의 특수 변수 `gl_PointCoord`로 사용할 수 있는데요.
168+
해당 점의 텍스처를 그려봅시다.
169+
170+
먼저 fragment shader를 변경합니다.
171+
172+
```glsl
173+
// fragment shader
174+
precision mediump float;
175+
176+
+uniform sampler2D tex;
177+
178+
void main() {
179+
- gl_FragColor = vec4(1, 0, 0, 1); // 빨간색
180+
+ gl_FragColor = texture2D(tex, gl_PointCoord.xy);
181+
}
182+
```
183+
184+
이제 이를 단순하게 유지하기 위해 [데이터 텍스처에 대한 글](webgl-data-textures.html)에서 다룬 것처럼 원본 데이터로 만들어 보겠습니다.
185+
186+
```js
187+
// 2x2 pixel data
188+
const pixels = new Uint8Array([
189+
0xFF, 0x00, 0x00, 0xFF, // 빨간색
190+
0x00, 0xFF, 0x00, 0xFF, // 초록색
191+
0x00, 0x00, 0xFF, 0xFF, // 파란색
192+
0xFF, 0x00, 0xFF, 0xFF, // 자홍색
193+
]);
194+
const tex = gl.createTexture();
195+
gl.bindTexture(gl.TEXTURE_2D, tex);
196+
gl.texImage2D(
197+
gl.TEXTURE_2D,
198+
0, // 레벨
199+
gl.RGBA, // 내부 포맷
200+
2, // 너비
201+
2, // 높이
202+
0, // 테두리
203+
gl.RGBA, // 포맷
204+
gl.UNSIGNED_BYTE, // 타입
205+
pixels, // 데이터
206+
);
207+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
208+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
209+
```
210+
211+
WebGL은 기본적으로 texture unit 0을 사용하고 uniform은 기본적으로 0이므로 따로 설정할 것은 없습니다.
212+
213+
{{{example url="../webgl-simple-point-w-texture.html"}}}
214+
215+
이건 텍스처 관련 문제를 테스트하는 좋은 방법이 될 수 있습니다.
216+
버퍼도 안 쓰고, 속성도 없으며, uniform을 찾아 설정하지 않아도 됩니다.
217+
예를 들어 이미지를 로드한다면 표시되지 않는데요.
218+
위 셰이더를 사용하면 점에 이미지가 표시될까요?
219+
텍스처에 렌더링한 다음 텍스처를 보려고 합니다.
220+
일반적으로 버퍼와 속성을 통해 geometry를 설정하지만 이 단일 점에 표시하는 것만으로 텍스처를 렌더링할 수 있습니다.
221+
222+
## 여러 단일 `POINTS` 사용
223+
224+
위 예제를 기반으로 한 간단한 변경입니다.
225+
정점 셰이더를 이렇게 바꿀 수 있는데요.
226+
227+
```glsl
228+
// vertex shader
229+
230+
+attribute vec4 position;
231+
232+
void main() {
233+
- gl_Position = vec4(0, 0, 0, 1);
234+
+ gl_Position = position;
235+
gl_PointSize = 120.0;
236+
}
237+
```
238+
239+
속성은 기본값으로 `0, 0, 0, 1`을 가지므로 위 예제로 변경해도 여전히 작동합니다.
240+
하지만 이제 우리가 원하는 위치를 설정할 수 있는 능력을 얻었습니다.
241+
242+
```js
243+
+const program = webglUtils.createProgramFromSources(gl, [vs, fs]);
244+
const positionLoc = gl.getAttribLocation(program, 'position');
245+
246+
...
247+
248+
+const numPoints = 5;
249+
+for (let i = 0; i < numPoints; ++i) {
250+
+ const u = i / (numPoints - 1); // 0 ~ 1
251+
+ const clipspace = u * 1.6 - 0.8; // -0.8 ~ +0.8
252+
+ gl.vertexAttrib2f(positionLoc, clipspace, clipspace);
253+
254+
* const offset = 0;
255+
* const count = 1;
256+
* gl.drawArrays(gl.POINTS, offset, count);
257+
+}
258+
```
259+
260+
실행하기 전에 점을 더 작게 만듭시다.
261+
262+
```glsl
263+
// vertex shader
264+
265+
attribute vec4 position;
266+
+uniform float size;
267+
268+
void main() {
269+
gl_Position = position;
270+
- gl_PointSize = 120.0;
271+
+ gl_PointSize = 20.0;
272+
}
273+
```
274+
275+
그리고 점의 색상을 설정할 수 있도록 만들어 보겠습니다.
276+
(참고: 텍스처가 없는 코드로 다시 전환했습니다)
277+
278+
```glsl
279+
precision mediump float;
280+
281+
+uniform vec4 color;
282+
283+
void main() {
284+
- gl_FragColor = vec4(1, 0, 0, 1); // 빨간색
285+
+ gl_FragColor = color;
286+
}
287+
```
288+
289+
그리고 color location을 찾아야 합니다.
290+
291+
```js
292+
// GLSL 프로그램 설정
293+
const program = webglUtils.createProgramFromSources(gl, [vs, fs]);
294+
const positionLoc = gl.getAttribLocation(program, 'position');
295+
+const colorLoc = gl.getUniformLocation(program, 'color');
296+
```
297+
298+
그런 다음 그것들을 사용합니다.
299+
300+
```js
301+
gl.useProgram(program);
302+
303+
const numPoints = 5;
304+
for (let i = 0; i < numPoints; ++i) {
305+
const u = i / (numPoints - 1); // 0 ~ 1
306+
const clipspace = u * 1.6 - 0.8; // -0.8 ~ +0.8
307+
gl.vertexAttrib2f(positionLoc, clipspace, clipspace);
308+
309+
+ gl.uniform4f(colorLoc, u, 0, 1 - u, 1);
310+
311+
const offset = 0;
312+
const count = 1;
313+
gl.drawArrays(gl.POINTS, offset, count);
314+
}
315+
```
316+
317+
이제 5개의 색상을 가진 5개의 점이 있으며 여전히 어떤 버퍼나 속성도 설정하지 않았습니다.
318+
319+
{{{example url="../webgl-simple-points.html"}}}
320+
321+
물론 이건 WebGL에서 많은 양의 점을 그려야 하는 방식이 아닙니다.
322+
많은 점을 그리려면 각 점의 위치와 색상으로 속성을 설정하고 단일 그리기 호출에서 모든 점을 그려야 합니다.
323+
324+
하지만! 테스트, 디버깅, [MCVE](https://meta.stackoverflow.com/a/349790/128511)를 만들기 위해 코드를 최소화하기 좋은 방법입니다.
325+
또 다른 예로 후처리 효과를 위해 텍스처에 그리는 중이고 이를 시각화하고 싶다고 가정해보겠습니다.
326+
이 예제와 텍스처를 사용한 이전 예제의 조합을 사용하여 각각 하나의 큰 점을 그릴 수 있습니다.
327+
복잡한 버퍼와 속성 과정이 필요하지 않아 디버깅에 적합합니다.
328+

0 commit comments

Comments
 (0)