Skip to content

Commit 86b342b

Browse files
vanzo16-githubgreggman
authored andcommitted
Create webgl-smallest-programs
Hello. It's full translate article.
1 parent 6a2f2ce commit 86b342b

File tree

1 file changed

+327
-0
lines changed

1 file changed

+327
-0
lines changed
+327
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,327 @@
1+
Title: Небольшие программы WebGL
2+
Description: Небольшие программы WebGL
3+
TOC: Небольшие программы WebGL
4+
5+
В этой статье предполагается, что вы прочитали множество других статей, начиная с основ. Если вы их не читали, сначала начните с них.
6+
7+
Я действительно не знаю, где разместить эту статью, потому что она преследует две цели.
8+
9+
1. Показать вам небольшие программы WebGL.
10+
11+
Эти методы очень полезны для тестирования чего-либо или при создании [MCVE for Stack Overflow](https://meta.stackoverflow.com/a/349790/128511) или при попытке выявить ошибку.
12+
13+
2. Учимся мыслить нестандартно.
14+
15+
Я надеюсь написать еще несколько статей на эту тему, которые помогут вам увидеть более широкую картину, а не просто общие закономерности.
16+
[Вот один](webgl-drawing-without-data.html).
17+
18+
## Просто очищение
19+
20+
Вот самая маленькая программа WebGL, которая действительно что-то делает
21+
22+
```js
23+
const gl = document.querySelector('canvas').getContext('webgl');
24+
gl.clearColor(1, 0, 0, 1); // red
25+
gl.clear(gl.COLOR_BUFFER_BIT);
26+
```
27+
28+
Все, что делает эта программа, это очищает холст до красного цвета, но на самом деле она что-то делает.
29+
30+
Подумайте об этом. Только с помощью этого вы действительно можете протестировать некоторые вещи. Допустим, вы выполняете
31+
[рендеринг текстуры](webgl-render-to-texture.html), но что-то не работает.
32+
Допустим, это так же, как пример [в этой статье](webgl-render-to-texture.html).
33+
Вы визуализируете один или несколько трехмерных объектов в текстуру, а затем визуализируете результат в куб.
34+
35+
Вы ничего не видите. Ну, в качестве простого теста: прекратите рендеринг текстуры с помощью шейдеров, просто очистите текстуру до известного цвета.
36+
37+
```js
38+
gl.bindFramebuffer(gl.FRAMEBUFFER, framebufferWithTexture)
39+
gl.clearColor(1, 0, 1, 1); // magenta
40+
gl.clear(gl.COLOR_BUFFER_BIT);
41+
```
42+
43+
Теперь выполните рендеринг с использованием текстуры из фреймбуфера.
44+
Ваш куб стал пурпурным? Если нет, то ваша проблема не в рендеринге текстурной части, а в чем-то другом.
45+
46+
## Использование `SCISSOR_TEST` и `gl.clear`
47+
48+
`SCISSOR_TEST` отсекает как рисование, так и очистку дочернего прямоугольника холста (или текущего фреймбуфера).
49+
50+
Вы включаете "ножничный тест" с помощью
51+
52+
```js
53+
gl.enable(gl.SCISSOR_TEST);
54+
```
55+
56+
а затем вы устанавливаете прямоугольник "scissor" в пикселях относительно нижнего левого угла. Он использует те же параметры, что и `gl.viewport`.
57+
58+
```js
59+
gl.scissor(x, y, width, height);
60+
```
61+
62+
Используя это, можно рисовать прямоугольники с помощью `SCISSOR_TEST` и `gl.clear`.
63+
64+
Пример
65+
66+
```js
67+
const gl = document.querySelector('#c').getContext('webgl');
68+
69+
gl.enable(gl.SCISSOR_TEST);
70+
71+
function drawRect(x, y, width, height, color) {
72+
gl.scissor(x, y, width, height);
73+
gl.clearColor(...color);
74+
gl.clear(gl.COLOR_BUFFER_BIT);
75+
}
76+
77+
for (let i = 0; i < 100; ++i) {
78+
const x = rand(0, 300);
79+
const y = rand(0, 150);
80+
const width = rand(0, 300 - x);
81+
const height = rand(0, 150 - y);
82+
drawRect(x, y, width, height, [rand(1), rand(1), rand(1), 1]);
83+
}
84+
85+
86+
function rand(min, max) {
87+
if (max === undefined) {
88+
max = min;
89+
min = 0;
90+
}
91+
return Math.random() * (max - min) + min;
92+
}
93+
```
94+
95+
{{{example url="../webgl-simple-scissor.html"}}}
96+
97+
Не скажу, что этот конкретный вариант очень полезен, но все же его полезно знать.
98+
99+
## Использование одного большого `gl.POINTS`
100+
101+
Как показывает большинство примеров, наиболее распространенной задачей в WebGL является создание буферов.
102+
Поместите данные вершин в эти буферы. Создавайте шейдеры с атрибутами. Настройте атрибуты для извлечения данных из этих буферов.
103+
Затем нарисуйте, возможно, с униформой и текстурой, также используемыми вашими шейдерами.
104+
105+
Но иногда хочется просто проверить. Допустим, вы хотите просто увидеть что-нибудь нарисованное.
106+
107+
Как насчет этого набора шейдеров?
108+
109+
```glsl
110+
// vertex shader
111+
void main() {
112+
gl_Position = vec4(0, 0, 0, 1); // center
113+
gl_PointSize = 120.0;
114+
}
115+
```
116+
117+
```glsl
118+
// fragment shader
119+
precision mediump float;
120+
121+
void main() {
122+
gl_FragColor = vec4(1, 0, 0, 1); // red
123+
}
124+
```
125+
126+
И вот код для его использования
127+
128+
```js
129+
// setup GLSL program
130+
const program = webglUtils.createProgramFromSources(gl, [vs, fs]);
131+
132+
gl.useProgram(program);
133+
134+
const offset = 0;
135+
const count = 1;
136+
gl.drawArrays(gl.POINTS, offset, count);
137+
```
138+
139+
Не нужно создавать буферы, не нужно настраивать униформы, и мы получаем одну точку в середине холста.
140+
141+
{{{example url="../webgl-simple-point.html"}}}
142+
143+
> ПРИМЕЧАНИЕ. Safari до 15 версии не прошел [тесты на соответствие WebGL](https://www.khronos.org/registry/webgl/sdk/tests/conformance/rendering/point-no-attributes.html?webglVersion=1&quiet=0) для этой функции.
144+
145+
О `gl.POINTS`: Когда вы передаете `gl.POINTS` в `gl.drawArrays` вам также необходимо установить размер в пикселях
146+
`gl_PointSize` в вашем вершинном шейдере. Важно отметить, что разные графические
147+
процессоры/драйверы имеют разный максимальный размер точек, который вы можете использовать.
148+
Вы можете запросить этот максимальный размер с помощью
149+
150+
gl.POINTS в gl.drawArrays, gl_PointSize
151+
```
152+
const [minSize, maxSize] = gl.getParameter(gl.ALIASED_POINT_SIZE_RANGE);
153+
```
154+
155+
Спецификация WebGL требует только максимальный размер 1.0. К счастью,
156+
[большинство, если не все графические процессоры и драйверы поддерживают больший размер](https://web3dsurvey.com/webgl/parameters/ALIASED_POINT_SIZE_RANGE).
157+
158+
После того, как вы установили `gl_PointSize` затем, когда вершинный шейдер завершит работу, какое бы значение вы ни установили `gl_Position`
159+
преобразуется в пространство экрана/холста в пикселях, затем вокруг этой позиции генерируется квадрат, равный +/- gl_PointSize / 2 во всех 4 направлениях.
160+
161+
Хорошо, я слышу, ну и что, кто хочет нарисовать одну точку.
162+
163+
Ну а точки автоматически получают свободные [текстурные координаты](webgl-3d-textures.html).
164+
Они доступны во фрагментном шейдере с помощью специальной переменной `gl_PointCoord`.
165+
Итак, давайте нарисуем текстуру в этой точке.
166+
167+
Сначала давайте изменим фрагментный шейдер.
168+
169+
```glsl
170+
// fragment shader
171+
precision mediump float;
172+
173+
+uniform sampler2D tex;
174+
175+
void main() {
176+
- gl_FragColor = vec4(1, 0, 0, 1); // red
177+
+ gl_FragColor = texture2D(tex, gl_PointCoord.xy);
178+
}
179+
```
180+
181+
Теперь, чтобы упростить задачу, давайте создадим текстуру с необработанными данными, как описано
182+
[в статье о текстурах данных](webgl-data-textures.html).
183+
184+
```js
185+
// 2x2 pixel data
186+
const pixels = new Uint8Array([
187+
0xFF, 0x00, 0x00, 0xFF, // red
188+
0x00, 0xFF, 0x00, 0xFF, // green
189+
0x00, 0x00, 0xFF, 0xFF, // blue
190+
0xFF, 0x00, 0xFF, 0xFF, // magenta
191+
]);
192+
const tex = gl.createTexture();
193+
gl.bindTexture(gl.TEXTURE_2D, tex);
194+
gl.texImage2D(
195+
gl.TEXTURE_2D,
196+
0, // level
197+
gl.RGBA, // internal format
198+
2, // width
199+
2, // height
200+
0, // border
201+
gl.RGBA, // format
202+
gl.UNSIGNED_BYTE, // type
203+
pixels, // data
204+
);
205+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
206+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
207+
```
208+
209+
Поскольку WebGL по умолчанию использует текстурный блок 0, а униформы по умолчанию равны 0, больше нечего настраивать.
210+
211+
{{{example url="../webgl-simple-point-w-texture.html"}}}
212+
213+
Это может быть отличным способом проверить проблемы, связанные с текстурами.
214+
Мы по-прежнему не используем ни буферов, ни атрибутов, и нам не нужно искать и устанавливать какие-либо униформы.
215+
Например, если мы загрузили изображение, оно не отображается. Что, если мы попробуем шейдер выше, покажет ли он изображение в точке?
216+
Мы выполнили рендеринг текстуры, а затем хотим просмотреть текстуру.
217+
Обычно мы настраиваем некоторую геометрию с помощью буферов и атрибутов,
218+
но мы можем визуализировать текстуру, просто показывая ее в этой единственной точке.
219+
220+
## Использование нескольких одиночных `POINTS`
221+
222+
Еще одно простое изменение в приведенном выше примере. Мы можем изменить вершинный шейдер на этот
223+
224+
```glsl
225+
// vertex shader
226+
227+
+attribute vec4 position;
228+
229+
void main() {
230+
- gl_Position = vec4(0, 0, 0, 1);
231+
+ gl_Position = position;
232+
gl_PointSize = 120.0;
233+
}
234+
```
235+
236+
атрибуты имеют значение по умолчанию `0, 0, 0, 1,` поэтому даже с этим изменением, приведенные выше примеры все равно продолжат работать.
237+
Но теперь мы получаем возможность устанавливать позицию, если захотим.
238+
239+
```js
240+
+const program = webglUtils.createProgramFromSources(gl, [vs, fs]);
241+
const positionLoc = gl.getAttribLocation(program, 'position');
242+
243+
...
244+
245+
+const numPoints = 5;
246+
+for (let i = 0; i < numPoints; ++i) {
247+
+ const u = i / (numPoints - 1); // 0 to 1
248+
+ const clipspace = u * 1.6 - 0.8; // -0.8 to +0.8
249+
+ gl.vertexAttrib2f(positionLoc, clipspace, clipspace);
250+
251+
* const offset = 0;
252+
* const count = 1;
253+
* gl.drawArrays(gl.POINTS, offset, count);
254+
+}
255+
```
256+
257+
Прежде чем мы запустим, давайте уменьшим точку.
258+
259+
```glsl
260+
// vertex shader
261+
262+
attribute vec4 position;
263+
+uniform float size;
264+
265+
void main() {
266+
gl_Position = position;
267+
- gl_PointSize = 120.0;
268+
+ gl_PointSize = 20.0;
269+
}
270+
```
271+
272+
И давайте сделаем так, чтобы мы могли установить цвет точки. (примечание: я вернулся к коду без текстуры).
273+
274+
```glsl
275+
precision mediump float;
276+
277+
+uniform vec4 color;
278+
279+
void main() {
280+
- gl_FragColor = vec4(1, 0, 0, 1); // red
281+
+ gl_FragColor = color;
282+
}
283+
```
284+
285+
и нам нужно найти местоположение цвета
286+
287+
```js
288+
// setup GLSL program
289+
const program = webglUtils.createProgramFromSources(gl, [vs, fs]);
290+
const positionLoc = gl.getAttribLocation(program, 'position');
291+
+const colorLoc = gl.getUniformLocation(program, 'color');
292+
```
293+
294+
И используем их
295+
296+
```js
297+
gl.useProgram(program);
298+
299+
const numPoints = 5;
300+
for (let i = 0; i < numPoints; ++i) {
301+
const u = i / (numPoints - 1); // 0 to 1
302+
const clipspace = u * 1.6 - 0.8; // -0.8 to +0.8
303+
gl.vertexAttrib2f(positionLoc, clipspace, clipspace);
304+
305+
+ gl.uniform4f(colorLoc, u, 0, 1 - u, 1);
306+
307+
const offset = 0;
308+
const count = 1;
309+
gl.drawArrays(gl.POINTS, offset, count);
310+
}
311+
```
312+
313+
И теперь мы получаем 5 точек с 5 цветами, и нам по-прежнему не нужно настраивать какие-либо буферы или атрибуты.
314+
315+
{{{example url="../webgl-simple-points.html"}}}
316+
317+
Конечно, это **НЕ** тот способ, которым вы должны рисовать много точек в WebGL.
318+
Если вы хотите нарисовать много точек, вам следует сделать что-то вроде установки
319+
атрибута с позицией для каждой точки и цветом для каждой точки и нарисовать все точки за один вызов отрисовки.
320+
321+
НО!, для тестирования, для отладки, для создания [MCVE](https://meta.stackoverflow.com/a/349790/128511)
322+
это отличный способ **минимизировать** код. В качестве другого примера предположим,
323+
что мы рисуем текстуры для постобработки и хотим их визуализировать.
324+
Мы могли бы просто нарисовать по одной большой точке для каждой,
325+
используя комбинацию этого примера и предыдущего с текстурой.
326+
Не требуется никаких сложных действий с буферами и атрибутами,
327+
что отлично подходит для отладки.

0 commit comments

Comments
 (0)