Fix Russian translation typesetting.

pull/190/head
Sergey Karchevsky 7 years ago
parent ca4dd64671
commit 65568eb3c6

@ -4,15 +4,15 @@
Изображения выше были получены различными способами. Первое Ван Гог написал вручную, нанося краску слой за слоем. Он потратил на это часы. Второе было получено за секунды смешиванием четырёх наборов пикселей: сине-зелёного, пурпурного, жёлтого и чёрного. Ключевое отличие в том, что второе изображение получено непоследовательным способом, то есть не шаг за шагом, а всё за раз.
Эта книга повествует о революционной компьютерной технологии - *фрагментных шейдерах*, которые выводят генерируемые компьютером изображения на новый уровень. Они могут считаться эквивалентом печатного станка Гутенберга в мире графики.
Эта книга повествует о революционной компьютерной технологии - *фрагментных шейдерах*, которые выводят генерируемые компьютером изображения на новый уровень. Их можно назвать эквивалентом печатного станка Гутенберга в мире графики.
![печатный станок Гутенберга](gutenpress.jpg)
![Печатный станок Гутенберга](gutenpress.jpg)
Фрагментные шейдеры дают вам полный контроль над пикселями на экране на сверхбыстрой скорости. Поэтому они используются в самых разных случаях, от видеофильтров на телефоне до современнейших 3D-игр.
Фрагментные шейдеры дают вам полный контроль над пикселями на экране на сверхбыстрой скорости. Поэтому они используются повсеместно, от фильтров для видео на телефоне до современнейших 3D-игр.
![игра Journey от That Game Company](journey.jpg)
![Игра Journey от That Game Company](journey.jpg)
В следующих главах вы увидите насколько это мощная технология, и как её можно применить для ваших рабочих и личных проектов.
В следующих главах вы увидите насколько это мощная технология, и как её можно применить в ваших рабочих и личных проектах.
## Для кого эта книга?
@ -22,7 +22,7 @@
## Какие темы освещает эта книга?
Эта книга уделяет основное внимание пиксельным шейдерам на GLSL. Сначала мы определим что такое шейдеры, а затем научимся создавать с их помощью процедурные геометрические фигуры, узоры, текстуры и анимации. Вы изучите основы языка шейдеров и примените их так же к более практичным задачам: обработке изображений (логические операции над изображениями, размытие, свёртки с ядром, цветовые фильтры, таблицы значений и другие эффекты) и симуляциям (игра «Жизнь» Конвея, реакция типа Белоусова-Жаботинского, рябь на поверхности воды, акварельные эффекты, диаграммы Вороного и т.п.). Ближе к концу книги будут изложены продвинутые методы на основе алгоритмов трассировки лучей.
Эта книга уделяет основное внимание пиксельным шейдерам на GLSL. Сначала мы определим что такое шейдеры, а затем научимся создавать с их помощью процедурные геометрические фигуры, узоры, текстуры и анимации. Вы изучите основы языка шейдеров и примените их к задачам, возникающим на практике: обработке изображений (логические операции над изображениями, размытие, свёртки с ядром, цветовые фильтры, таблицы значений и другие эффекты) и симуляциям (игра «Жизнь» Конвея, реакция типа Белоусова-Жаботинского, рябь на поверхности воды, акварельные эффекты, диаграммы Вороного и т.п.). Ближе к концу книги будут изложены продвинутые методы на основе алгоритмов трассировки лучей.
*Каждый параграф содержит интерактивные примеры*. Изменения в них показываются непосредственно при редактировании кода. Излагаемые принципы могут быть довольно абстрактными, поэтому интерактивные примеры очень полезны при изучении материала. Чем быстрее вы увидите изучаемые концепции в действии, тем проще будет процесс обучения.
@ -30,7 +30,7 @@
* Эта книга не об OpenGL или WebGL. OpenGL/WebGL - боле обширная тема, чем GLSL и фрагментные шейдеры. Для изучения OpenGL/WebGL я бы рекомендовал [ведение в OpenGL](https://open.gl/introduction), [8 издание руководства по программированию на OpenGL](http://www.amazon.com/OpenGL-Programming-Guide-Official-Learning/dp/0321773039/ref=sr_1_1?s=books&ie=UTF8&qid=1424007417&sr=1-1&keywords=open+gl+programming+guide) (она же красная книга) или [WebGL: Up and Running](http://www.amazon.com/WebGL-Up-Running-Tony-Parisi/dp/144932357X/ref=sr_1_4?s=books&ie=UTF8&qid=1425147254&sr=1-4&keywords=webgl)
* Это не учебник по математике. Мы изложим некоторые методы, основанные на алгебре и тригонометрии, но мы не будем углубляться в детали. По математическим вопросам я бы рекомендовал обратиться к следующим книгам: [3 издание «математики для 3D-программирования и компьютерной графики»](http://www.amazon.com/Mathematics-Programming-Computer-Graphics-Third/dp/1435458869/ref=sr_1_1?ie=UTF8&qid=1424007839&sr=8-1&keywords=mathematics+for+games) или [второе издание «математики для игр и интерактивных приложений»](http://www.amazon.com/Essential-Mathematics-Games-Interactive-Applications/dp/0123742978/ref=sr_1_1?ie=UTF8&qid=1424007889&sr=8-1&keywords=essentials+mathematics+for+developers)
* Это *не* учебник по математике. Мы изложим некоторые методы, основанные на алгебре и тригонометрии, но мы не будем углубляться в детали. По математическим вопросам я бы рекомендовал обратиться к следующим книгам: [третье издание «математики для 3D-программирования и компьютерной графики»](http://www.amazon.com/Mathematics-Programming-Computer-Graphics-Third/dp/1435458869/ref=sr_1_1?ie=UTF8&qid=1424007839&sr=8-1&keywords=mathematics+for+games) или [второе издание «математики для игр и интерактивных приложений»](http://www.amazon.com/Essential-Mathematics-Games-Interactive-Applications/dp/0123742978/ref=sr_1_1?ie=UTF8&qid=1424007889&sr=8-1&keywords=essentials+mathematics+for+developers)
## Что нужно чтобы начать?

@ -1,13 +1,13 @@
# Введение
## Что такое фрагментный шейдер?
В предыдущем параграфе мы описали шейдеры как эквивалент печатного станка Гутенберга для графики. Почему? Ч вообще, что такое шейдер?
В предыдущем параграфе мы описали шейдеры как эквивалент печатного станка Гутенберга для графики. Почему? И вообще, что такое шейдер?
![Слева: буква за буквой (монах-переписчик за работой, Вильям Блэйдс, 1891). Справа: страница за страницей (печатный станок, Ролт-Уилер, 1920).](print.png)
Если у вас уже есть опыт рисования с помощью компьютера, вы скорее всего сначала рисовали круг, затем прямоугольник, линию, несколько треугольников, получая в итоге желаемую композицию. Процесс похож на написание письма или книги вручную - это набор инструкций, которые последовательно решают задачи одну за другой.
Шейдеры так же являются наборами инструкций, и эти инструкции исполняются одновременно для каждого пикселя на экране. Это означает, что код должен работать по разному в зависимости от положения пикселя на экране. Подобно печатному станку, ваша программа будет работать как функция, принимающая на вход положение и возвращающая цвет. Будучи скомпилированной, она будет работать невероятно быстро.
Шейдеры так же являются наборами инструкций, и эти инструкции исполняются одновременно для каждого пикселя на экране. Это означает, что код должен работать по разному в зависимости от положения пикселя на экране. Подобно печатному станку, ваша программа будет работать как функция, принимающая на вход координаты пикселя, и возвращающая цвет. После компиляции она будет работать невероятно быстро.
![Китайский наборный шрифт](typepress.jpg)
@ -15,13 +15,13 @@
Ответить на этот вопрос помогут *параллельные вычисления*.
Представьте процессор компьютера в виде трубы, а каждую задачу как что-то проходящее через неё, как на фабричной производственной линии. Некоторые задачи больше остальных, то есть требуют больше времени и энергии на выполнение. В наших терминах, они требуют больше вычислительной мощности. Из-за особенностей архитектуры компьютера задачи запускаются последовательно, и каждая задача должна быть завершена вовремя. Современные компьютеры обычно содержат несколько процессоров, которые работают как такие трубы, выполняя задания одно за другим, чтобы создать иллюзию плавности и непрерывности работы. Каждая такая труба называется *потоком*.
Представьте процессор компьютера в виде трубы, а каждую задачу как что-то проходящее через неё, как на фабричной производственной линии. Некоторые задачи больше остальных, то есть требуют больше времени и энергии на выполнение. В наших терминах, они требуют больше вычислительной мощности. Из-за особенностей архитектуры компьютера задачи запускаются последовательно, и каждая задача должна быть завершена вовремя. Современные компьютеры обычно содержат несколько процессоров, каждый из которых можно представить в виде трубы, обрабатывающей задания одно за другим, создавая иллюзию плавности и непрерывности работы. Каждая такая труба называется *потоком*.
![Центральный процессор](00.jpeg)
Видеоигры и другие графические приложения требуют намного больше вычислительной мощности, чем другие программы. Они вынуждены совершать огромное количество попиксельных операций над графическим контентом. Каждый пиксель на экране должен быть обсчитан, а в 3D-играх нужно рассчитать ещё и геометрию с перспективой.
Давайте вернёмся к нашей метафоре с трубами и задачами. Каждый пиксель на экране является небольшой простой задачей. По отдельности такие задачи не представляют трудности для CPU, но проблема в том, что эти небольшие задания должны быть выполнены для каждого пикселя на экране. Таким образом, даже для старого экрана с разрешением 800х600, нужно обработать 480 000 пикселей, то есть произвести 14 400 000 вычислений в секунду! И это становится непосильной задачей для центрального процессора. На современном ретина-дисплее с разрешением 2880х1800 вывод видео с частотой 60 кадров в секунду увеличит количество вычислений до 311 040 000 в секунду. Как же инженеры графических систем решают эту проблему?
Давайте вернёмся к нашей метафоре с трубами и задачами. Каждый пиксель на экране является небольшой простой задачей. По отдельности такие задачи не представляют трудности для CPU, но проблема в том, что эти небольшие задания должны быть выполнены для каждого пикселя на экране. Таким образом, даже для старого экрана с разрешением 800х600 нужно обработать 480 000 пикселей, то есть произвести 14 400 000 вычислений в секунду! И это становится непосильной задачей для центрального процессора. На современном ретина-дисплее с разрешением 2880х1800 вывод видео с частотой 60 кадров в секунду увеличит количество вычислений до 311 040 000 в секунду. Как же инженеры графических систем решают эту проблему?
![](03.jpeg)
@ -35,14 +35,14 @@
## Что такое GLSL?
GLSL расшифровывается как OpenGL Shading Language (язык шейдеров OpnGL), является стандартизированным языком для написания шейдерных программ, с которыми вы встретитесь далее. Существуют различные типы шейдеров, зависящие от аппаратуры и операционной системы. Эта книга опирается на спецификацию OpenGL, издаваемую [Khronos Group](https://www.khronos.org/opengl/). Понимание истории OpenGL может быть полезным для понимания многих странных соглашений, принятых в ней. Для этого вы можете пройти по следующей ссылке: [openglbook.com/chapter-0-preface-what-is-opengl.html](http://openglbook.com/chapter-0-preface-what-is-opengl.html)
GLSL расшифровывается как OpenGL Shading Language (язык шейдеров OpenGL) и является стандартизированным языком для написания шейдерных программ, с которыми вы встретитесь далее. Существуют различные типы шейдеров, зависящие от аппаратуры и операционной системы. Эта книга опирается на спецификацию OpenGL, издаваемую [Khronos Group](https://www.khronos.org/opengl/). Понимание истории OpenGL может быть полезным для понимания многих странных соглашений, принятых в ней. Для этого вы можете пройти по следующей ссылке: [openglbook.com/chapter-0-preface-what-is-opengl.html](http://openglbook.com/chapter-0-preface-what-is-opengl.html)
## Почему программирование шейдеров - это боль?
К параллельным вычислениям применим известный афоризм: "большая власть влечёт большую ответственность". Мощь архитектуры графических процессоров накладывает некоторые ограничения.
К параллельным вычислениям применим известный афоризм: «большая власть влечёт большую ответственность». Мощь архитектуры графических процессоров накладывает некоторые ограничения.
Для параллельной работы каждый поток не должен зависеть от остальных потоков. Потоки "слепы" по отношению к тому, чем занимаются другие потоки. Из этого ограничения следует, что все данные должны перемещаться в одном направлении. Поэтому невозможно использовать результат соседнего потока, изменить входные данные или направить выход одного потока на вход другого. Организация межпотокового взаимодействия несёт риск нарушения целостности данных.
Для параллельной работы каждый поток не должен зависеть от остальных потоков. Потоки «слепы» по отношению к тому, чем занимаются другие потоки. Из этого ограничения следует, что все данные должны перемещаться в одном направлении. Поэтому невозможно использовать результат соседнего потока, изменить входные данные или направить выход одного потока на вход другого. Попытка организации межпотокового взаимодействия несёт риск нарушения целостности данных.
Кроме того, GPU постоянно поддерживает свои процессоры в занятом состоянии. Как только они освобождаются, они тут же получают новую порцию данных для обработки. Поток не может узнать что он делал в предыдущий момент времени. Он мог рисовать кнопку з графического интерфейса операционной системы, затем рисовать кусок неба в игре, а потом отображать текст почтового сообщения. Каждый поток не только **слеп**, но ещё и **лишён памяти**. Помимо абстракции в виде функции, изменяющей свой результат в зависимости от положения пикселя, слепота и беспамятство потоков не добавляют шейдерам популярности среди начинающих программистов.
Кроме того, GPU постоянно поддерживает свои процессоры в занятом состоянии. Как только они освобождаются, они тут же получают новую порцию данных для обработки. Поток не может узнать что он делал в предыдущий момент времени. Он мог рисовать кнопку для графического интерфейса операционной системы, затем рисовать кусок неба в игре, а потом отображать текст почтового сообщения. Каждый поток не только **слеп**, но ещё и **лишён памяти**. Наряду с абстракцией функции, изменяющей свой результат в зависимости от положения пикселя, слепота и беспамятство потоков не добавляют шейдерам популярности среди начинающих программистов.
Не волнуйтесь! В следующих главах мы пошагово рассмотрим шейдерные вычисления, начиная с самых простых. Если вы читаете книгу в современном браузере, вы оцените возможность поиграться с интерактивными примерами. Так давайте же не откладывать веселье в долгий ящик! Нажмите *Next >>* чтобы перейти к программированию!

@ -1,28 +1,28 @@
## Hello World
Обычно программа "Hello world!" является первым шагом при изучении нового языка. Как известно, это простая однострочная программа, выводящая приветствие.
Обычно программа «Hello world!» является первым шагом при изучении нового языка. Все знают, что это простая однострочная программа, которая выводит на экран фразу приветствия.
В мире GPU рисование текста - слишком сложная задача для первого шага. Вместо этого мы выберем яркий, жизнерадостный цвет!
<div class="codeAndCanvas" data="hello_world.frag"></div>
Кад выше интерактивен, если вы читаете книгу в браузере. Это означает, что вы можете изменить любую часть кода по вашему желанию. Изменения будут видны сразу же благодаря архитектуре GPU, которая компилирует и заменяет шейдеры *на лету*. Попробуйте изменить значения в строке 6.
С кодом выше можно взаимодействовать, если вы читаете книгу в браузере. Это означает, что вы можете изменить любую часть кода по вашему желанию. Изменения будут видны сразу же благодаря архитектуре GPU, которая компилирует и заменяет шейдеры *на лету*. Попробуйте изменить значения в строке 6.
Эти несколько строчек кода не выглядят чем-то существенным, но мы можем извлечь из них кое-какие знания:
1. Язык шейдеров содержит функцию main, которая возвращает цвет в конце. Это напоминает C.
1. Язык шейдеров содержит функцию `main`, которая возвращает цвет по окончании работы. Это напоминает C.
2. Конечный цвет пикселя записывается в зарезервированную переменную `gl_FragColor`.
3. В этом C-подобном языке есть встроенные *переменные* (такие, как `gl_FragColor`), *функции* и *типы*. В этом примере мы только что познакомились с типом `vec4`, то есть четырёхкомпонентным вектором значений с плавающей точкой. Ниже мы встретим такие типы, как `vec3` и `vec2`, а так же более популярные `float`, `int` и `bool`.
3. В этом C-подобном языке есть встроенные *переменные* (такие как `gl_FragColor`), *функции* и *типы*. В этом примере мы только что познакомились с типом `vec4`, то есть четырёхкомпонентным вектором значений с плавающей точкой. Ниже мы встретим такие типы, как `vec3` и `vec2`, а так же более популярные `float`, `int` и `bool`.
4. Присмотревшись к типу `vec4`, можно догадаться, что его компоненты соответствуют красному, зелёному, синему и альфа каналам. Так же видно, что эти значения нормализованы, то есть лежат в диапазоне от `0.0` до `1.0`. ПОзднее мы увидим, что нормализация значений позволяет проще преобразовывать значения между переменными.
4. Присмотревшись к типу `vec4`, можно догадаться, что его компоненты соответствуют красному, зелёному, синему и альфа каналам. Так же видно, что эти значения нормализованы, то есть лежат в диапазоне от `0.0` до `1.0`. Позднее мы увидим как нормализация значений позволяет проще преобразовывать значения между переменными.
5. Этот пример так же демонстрирует наличие макросов препроцессора - ещё одно важное свойство, пришедшее из языка C. Макросы раскрываются перед компиляцией. С их помощью можно определить глобальные значения (`#define`) и выполнить простые условные операции, используя `#ifdef` и `#endif`. Все макрокоманды начинаются с решётки (`#`). Препроцессор работает непосредственно перед компиляцией, подставляя определения из директив `#define` и проверяя условия `#ifdef` (если определено) и `#ifndef` (если не определено). Так, в примере выше строка 2 вставляется в код, только если определено `GL_ES`. Это как правило случается на мобильных платформах и браузерах.
6. Типы чисел с плавающей точкой играют ключевую роль в шейдерах, поэтому важно помнить о *точности*. Более низкая точность позволяет выиграть в скорости работы за счёт качества. Если вы достаточно дотошны, вы можете указывать точность каждой переменной. В первой строке примера мы установили по умолчанию среднюю точность для всех чисел с плавающей точкой (`precision mediump float;`). Так же можно установить низкую (`precision lowp float;`) или высокую (`precision highp float;`) точность.
7. ПОследняя, и, возможно, важнейшая деталь: спецификация GLSL не гарантирует автоматического приведения типов. Что это означает? Производители используют различные подходы для ускорения работы видеокарт, но они вынуждены обеспечивать соответствие какому-то минимальному набору требований. Автоматическое приведение в этот набор не входит. В нашем примере `vec4` содержит значения с плавающей точкой, поэтому он должен быть инициализирован соответствующими числами. Если вы хотите писать хороший, целостный код и не тратить многие часы на отладку белых экранов, возьмите себе ха правило использовать точку (`.`) в значениях с плавающей точкой. Следующий код не везде будет работать корректно:
7. Последняя, и возможно, важнейшая деталь: спецификация GLSL не гарантирует автоматического приведения типов. Что это означает? Производители оборудования используют различные подходы для ускорения работы видеокарт, но они вынуждены обеспечивать соответствие какому-то минимальному набору требований. Автоматическое приведение в этот набор не входит. В нашем примере тип `vec4` содержит значения с плавающей точкой, поэтому он должен быть инициализирован соответствующими числами. Если вы хотите писать хороший, целостный код и не тратить многие часы на отладку белых экранов, возьмите себе за правило использовать точку (`.`) в значениях с плавающей точкой. Следующий код не везде будет работать корректно:
```glsl
void main() {
@ -30,7 +30,7 @@ void main() {
}
```
К этому моменту мы описали основные элементы программы "hello world!", а значит, теперь самое время нажать на блок кода и начать применять полученные знания. При возникновении ошибок программа не скомпилируется и покажет белый экран. Попробуйте проделать следующее:
К этому моменту мы описали основные элементы программы «hello world!», а значит, теперь самое время нажать на блок кода и начать применять полученные знания. При возникновении ошибок программа не скомпилируется и покажет белый экран. Попробуйте проделать следующее:
* Замените числа с плавающей точкой целыми. Ваша графическая карта может безошибочно воспринять такое поведение, а может и нет.

@ -54,7 +54,7 @@ uniform float iGlobalTime; // время работы шейдера (секу
* Как насчёт `(1.0, 0.0)`, `(0.0, 1.0)`, `(0.5, 0.5)` и `(1.0, 1.0)`?
* Догадайтесь как использовать `u_mouse`, зная, что координаты даны в пикселях и не нормализованы. Можете ли вы двигать цвета с помощью этой переменной?
* Догадайтесь как использовать `u_mouse`, зная, что координаты даны в пикселях и НЕ нормализованы. Можете ли вы двигать цвета с помощью этой переменной?
* Придумайте какой-нибудь интересный способ изменения цветов с помощью `u_time` и `u_mouse`.

@ -24,7 +24,7 @@ glslViewer yourShader.frag yourInputImage.png —w 500 -h 500 -s 1 -o yourOutput
![](glslViewer.gif)
**Публикация**: онлайн-редактор ([editor.thebookofshaders.com/](http://editor.thebookofshaders.com/)) может опубликовать ваш шейдер! Как встраиваемая, так и отдельная версия содержат кнопку "экспорт", которая выдаёт уникальные URL каждому шейдеру. Так же есть возможность выгружать шейдеры сразу на [openFrame.io](http://openframe.io/).
**Публикация**: онлайн-редактор ([editor.thebookofshaders.com/](http://editor.thebookofshaders.com/)) может опубликовать ваш шейдер! Как встраиваемая, так и отдельная версия содержат кнопку «экспорт», которая выдаёт уникальные URL каждому шейдеру. Так же есть возможность выгружать шейдеры сразу на [openFrame.io](http://openframe.io/).
![](glslEditor-00.gif)
@ -128,7 +128,7 @@ glslViewer yourShader.frag yourInputImage.png —w 500 -h 500 -s 1 -o yourOutput
### **Processing**
[Processing](https://processing.org/) - необычайная простая и мощная среда, созданная [Беном Фраем](http://benfry.com/) и [Кэси Рис](http://reas.com/) в 2001 году. Она хорошо подходит для того, чтобы сделать ваши первые шаги в программировании (по крайней мере, так было у меня). [Андре Колубри](https://codeanticode.wordpress.com/) добавил в Processing поддержку OpenGL и видео, из-за чего играть с шейдерами в ней стало проще простого. Processing ищет файл с именем `"shader.frag"` в папке `data` вашего скетча. Таким образом, вы можете просто скопировать пример отсюда и переименовать файл.
[Processing](https://processing.org/) - необычайная простая и мощная среда, созданная [Беном Фраем](http://benfry.com/) и [Кэси Рис](http://reas.com/) в 2001 году. Она хорошо подходит для того, чтобы сделать ваши первые шаги в программировании (по крайней мере, так было у меня). [Андре Колубри](https://codeanticode.wordpress.com/) добавил в Processing поддержку OpenGL и видео, из-за чего играть с шейдерами в ней стало проще простого. Processing ищет файл с именем `shader.frag` в папке `data` вашего скетча. Таким образом, вы можете просто скопировать пример отсюда и переименовать файл.
```cpp
PShader shader;

@ -1,15 +1,15 @@
# Алгоритмическое рисование
## Формообразующие функции
Эта глава могла бы называться "Урок покраски забора от мистера Мияги". Ранее мы отобразили нормализованные координаты *x* и *y* в красный и зелёный цветовые каналы. По сути, мы сделали функцию, которая принимает двумерный вектор (x и y) и возвращает четырёхмерный вектор (r, g, b и а). Но прежде чем мы погрузимся глубже в трансформацию данных между измерениями, не помешает начать с более простых вещей. То есть с понимания способов конструирования одномерных функций. Чем больше времени и энергии вы потратите на освоение этого материала, тем сильнее будет ваше шейдерное карате.
Эта глава могла бы называться «Урок с забором от мистера Мияги». Ранее мы отобразили нормализованные координаты *x* и *y* в красный и зелёный цветовые каналы. По сути, мы сделали функцию, которая принимает двумерный вектор (x и y) и возвращает четырёхмерный вектор (r, g, b и а). Но прежде чем мы погрузимся глубже в трансформацию данных между измерениями, не помешает начать с более простых вещей. То есть с понимания способов конструирования одномерных функций. Чем больше времени и энергии вы потратите на освоение этого материала, тем сильнее будет ваше шейдерное карате.
![Парень-каратист (1984)](mr_miyagi.jpg)
Следующий код будет нашим забором. В нём мы визуализируем нормализованное значение координаты *x* (`st.x`) двумя способами: с помощью яркости (обратите внимание на градиент от чёрного к белому) и путём построения зелёной линии поверх (в этом случае значение *x* записывается напрямую в *y*). Вы можете пока не вникать в функцию построения графика. Она будет детально рассмотрена далее.
Следующий код будет нашим забором. В нём мы визуализируем нормированное значение координаты *x* (`st.x`) двумя способами: с помощью яркости (обратите внимание на градиент от чёрного к белому) и путём построения зелёной линии поверх (в этом случае значение *x* записывается напрямую в *y*). Вы можете пока не вникать в функцию построения графика. Она будет детально рассмотрена далее.
<div class="codeAndCanvas" data="linear.frag"></div>
**На заметку**: Конструктор типа `vec3` "понимает", что вы хотите присвоить одно и то же значение всем трём каналам, а `vec4` понимает, что четырёхмерный вектор нужно собрать из трёхмерного вектора и одного числа. Это число в данном случае отвечает за альфа канал, или прозрачность. Примеры этого поведения вы можете видеть в строках 20 и 26.
**На заметку**: Конструктор типа `vec3` «понимает», что вы хотите присвоить одно и то же значение всем трём каналам, а `vec4` понимает, что четырёхмерный вектор нужно собрать из трёхмерного вектора и одного числа. Это число в данном случае отвечает за альфа канал, или прозрачность. Примеры этого поведения вы можете видеть в строках 20 и 26.
Этот код - это ваш забор; важно видеть и понимать его. Вы раз за разом будете возвращаться в пространство между *0.0* и *1.0*. Вы изучите искусство смешивания и формирования линий.
@ -37,14 +37,12 @@
<div class="codeAndCanvas" data="smoothstep.frag"></div>
В строке 12 приведённого выше кода мы используем smoothstep для рисования зелёной линии в функции `plot()`. Для каждой точки вдоль оси *x* эта функция делает *всплеск* при нужно значении *y*. Как? Через соединение двух [`smoothstep()`](../glossary/?search=smoothstep). Рассмотрите следующую функцию:
В строке 12 приведённого выше кода мы используем smoothstep для рисования зелёной линии в функции `plot()`. Для каждой точки вдоль оси *x* эта функция делает *всплеск* при нужно значении *y*. Как? Через соединение двух [`smoothstep()`](../glossary/?search=smoothstep). Рассмотрите следующую функцию, ставьте её вместо строки 20 в коде выше и вообразите, что это вертикальный разрез. Фон выглядит как линия, не так ли?
```glsl
float y = smoothstep(0.2,0.5,st.x) - smoothstep(0.5,0.8,st.x);
```
Вставьте её вместо функции на строке 20 в коде выше и вообразите, что это вертикальный разрез. Фон выглядит похожим на линию, не так ли?
### Синус и косинус
Синус и косинус - ваши лучшие друзья, когда вы используете математику для анимации, построения фигур или смешивания значений.
@ -75,11 +73,11 @@ float y = smoothstep(0.2,0.5,st.x) - smoothstep(0.5,0.8,st.x);
* Возьмите дробную часть от [`sin(x)`](../glossary/?search=sin) с помощью [`fract()`](../glossary/?search=fract).
* Сложите результаты вычисления синуса, округлённые до целого в большую ([`ceil()`](../glossary/?search=ceil)) и в меньшую ([`floor()`](../glossary/?search=floor)) стороны. Получится "цифровой" прямоугольный сигнал со значениями 1 и -1.
* Сложите результаты вычисления синуса, округлённые до целого в большую ([`ceil()`](../glossary/?search=ceil)) и в меньшую ([`floor()`](../glossary/?search=floor)) стороны. Получится «цифровой» прямоугольный сигнал со значениями 1 и -1.
### Другие полезные функции
В конце предыдущего упражнения мы затронули несколько новых функций. Теперь давайте поэкспериментируем. Попробуйте раскомментировать по строки в коде ниже по одной. Запомните эти функции и изучите их поведение. Возможно, вы спросите, зачем это нужно? Быстрый поиск в google по запросу "generative art" даст ответ. Помните, что пока мы осваиваем перемещение в одном измерении, вверх и вниз. Но скоро мы перейдём к двум, трём и даже четырём измерениям!
В конце предыдущего упражнения мы затронули несколько новых функций. Теперь давайте поэкспериментируем. Попробуйте раскомментировать строки в коде ниже по одной. Запомните эти функции и изучите их поведение. Возможно, вы спросите, зачем это нужно? Быстрый поиск в google по запросу «generative art» даст ответ. Помните, что пока мы осваиваем перемещение в одном измерении, вверх и вниз. Но скоро мы перейдём к двум, трём и даже четырём измерениям!
![Anthony Mattox (2009)](anthony-mattox-ribbon.jpg)
@ -109,7 +107,7 @@ float y = smoothstep(0.2,0.5,st.x) - smoothstep(0.5,0.8,st.x);
Подобно поварам, собирающим специи и экзотические ингридиенты, цифровые художники уделяют особое внимание работе над своими собственными формообразующими функциями.
[Иньиго Квилез](http://www.iquilezles.org/) собрал хорошую коллекцию [полезных функций](http://www.iquilezles.org/www/articles/functions/functions.htm). После прочтения [статьи](http://www.iquilezles.org/www/articles/functions/functions.htm) посмотрите на реализацию этих функций на GLSL. Обратите внимание на незначительность потребовавшихся изменений. Например, использование точки в числах с плавающей точкой и замену функций из *C* на их GLSL-аналоги: `pow()` вместо `powf()` и т.п.
[Иниго Квилес](http://www.iquilezles.org/) собрал хорошую коллекцию [полезных функций](http://www.iquilezles.org/www/articles/functions/functions.htm). После прочтения [статьи](http://www.iquilezles.org/www/articles/functions/functions.htm) посмотрите на реализацию этих функций на GLSL. Обратите внимание на незначительность потребовавшихся изменений. Например, использование точки в числах с плавающей точкой и замену функций из *C* на их GLSL-аналоги: `pow()` вместо `powf()` и т.п.
<div class="glslGallery" data="05/impulse,05/cubicpulse,05/expo,05/expstep,05/parabola,05/pcurve" data-properties="clickRun:editor,hoverPreview:false"></div>
@ -133,9 +131,9 @@ float y = smoothstep(0.2,0.5,st.x) - smoothstep(0.5,0.8,st.x);
![Grapher в OS X (2004)](grapher.png)
* [GraphToy](http://www.iquilezles.org/apps/graphtoy/): уже знакомый нам [Иньиго Квилез](http://www.iquilezles.org) написал инструмент для визуализации GLSL-функций в WebGL.
* [GraphToy](http://www.iquilezles.org/apps/graphtoy/): уже знакомый нам [Иниго Квилес](http://www.iquilezles.org) написал инструмент для визуализации GLSL-функций в WebGL.
![Иньиго Квилез - GraphToy (2010)](graphtoy.png)
![Иниго Квилес - GraphToy (2010)](graphtoy.png)
* [Shadershop](http://tobyschachman.com/Shadershop/): этот замечательный инструмент, созданный [Тоби Шахманом](http://tobyschachman.com/), научит вас конструировать сложные функции необычайно наглядным и интуитивным способом.

@ -1,4 +1,4 @@
![Пол КЛи - диаграмма цвета (1931)](klee.jpg)
![Пол Кли - диаграмма цвета (1931)](klee.jpg)
## Цвета
@ -94,7 +94,7 @@ vec4({{rn}},{{gn}},{{bn}},1.0)
Нельзя рассказать о цветах, не упомянув цветовое пространство. Как вы возможно знаете, есть множество способов задания цвета кроме красного, зелёного и синего каналов.
[HSB](http://en.wikipedia.org/wiki/HSL_and_HSV) расшифровывается как оттенок (Hue), насыщенность (Saturation) и яркость (Brightness или Value), и является более интуитивным способом представления цвета. Прочитайте код функций `rgb2hsv()` и `hsv2rgb()` в примере ниже.
[HSB](https://ru.wikipedia.org/wiki/HSV_(%D1%86%D0%B2%D0%B5%D1%82%D0%BE%D0%B2%D0%B0%D1%8F_%D0%BC%D0%BE%D0%B4%D0%B5%D0%BB%D1%8C)) расшифровывается как оттенок (Hue), насыщенность (Saturation) и яркость (Brightness или Value), и является более интуитивным способом представления цвета. Прочитайте код функций `rgb2hsv()` и `hsv2rgb()` в примере ниже.
Отображая координату `x` в оттенок, а координату `y` - в яркость, мы получаем красивый спектр видимых цветов. Такое пространственное распределение цветов очень удобно. Выбор цветов в пространстве HSB более интуитивен, чем RGB.
@ -126,7 +126,7 @@ vec4({{rn}},{{gn}},{{bn}},1.0)
![](colorwheel.png)
* Прочитайте книгу ["Взаимодействие цветов" Джозефа Альберса](http://www.goodreads.com/book/show/111113.Interaction_of_Color) и воспользуйтесь следующим примером для практики.
* Прочитайте книгу [«Взаимодействие цветов» Джозефа Альберса](http://www.goodreads.com/book/show/111113.Interaction_of_Color) и воспользуйтесь следующим примером для практики.
<div class="glslGallery" data="160505191155,160505193939,160505200330,160509131554,160509131509,160509131420,160509131240" data-properties="clickRun:editor,openFrameIcon:false,showAuthor:false"></div>
@ -135,9 +135,9 @@ vec4({{rn}},{{gn}},{{bn}},1.0)
Перед тем как нырнуть в следующую главу, давайте остановимся и немного отмотаем назад. Вернитесь и взгляните на функции в предыдущих примерах. Вы заметите слово `in` перед типами аргументов. Это - [*квалификатор*](http://www.shaderific.com/glsl-qualifiers/#inputqualifier), и в данном случае он означает, что переменная предназначена только для чтения. В последующих примерах мы увидим так же аргументы с квалификаторами `out` и `inout`. Последний эквивалентен передаче переменной по ссылке, при которой мы можем изменить переданное значение так, что изменения становятся видны за пределами функции.
```glsl
int newFunction(in vec4 aVec4, // read-only
out vec3 aVec3, // write-only
inout int aInt); // read-write
int newFunction(in vec4 aVec4, // только для чтения
out vec3 aVec3, // только на запись
inout int aInt); // чтение и запись
```
Вы не поверите, но мы уже изучили всё необходимое для создания крутой графики. В следующей главе мы научимся комбинировать все эти трюки для создания геометрических фигур с помощью смешивания пространства. Именно, *смешивание* пространства!

@ -84,7 +84,7 @@ color = vec3(bl.x * bl.y * tr.x * tr.y);
* Напишите функцию, которая рисует только контур прямоугольника.
* Каким образом вы будете рисовать несколько прямоугольников в одном окне и перемещать их? Если догадаетесь как это делается, попробуйте повторить композицию [Пита Мондриана](http://en.wikipedia.org/wiki/Piet_Mondrian).
* Каким образом вы будете рисовать несколько прямоугольников в одном окне и перемещать их? Если догадаетесь как это делается, попробуйте повторить композицию [Пита Мондриана](https://ru.wikipedia.org/wiki/%D0%9C%D0%BE%D0%BD%D0%B4%D1%80%D0%B8%D0%B0%D0%BD,_%D0%9F%D0%B8%D1%82).
![Пит Мондриан - Композиция (1921)](mondrian.jpg)
@ -124,7 +124,7 @@ color = vec3(bl.x * bl.y * tr.x * tr.y);
![](distance-field.jpg)
Фактически, мы интерпретируем пространство в терминах расстояния до центра чтобы рисовать фигуры. Этот подход, известный как "поле расстояний", используется во многих областях, начиная от контуров шрифтов и заканчивая 3D-графикой.
Фактически, мы интерпретируем пространство в терминах расстояния до центра чтобы рисовать фигуры. Этот подход, известный как «поле расстояний», используется во многих областях, начиная от контуров шрифтов и заканчивая 3D-графикой.
Выполните следующие упражнения:

@ -34,7 +34,7 @@
![](rotmat.png)
Взгляните на код функции, которая конструирует двумерную матрицу поворота. Эта функция повторяет приведённую выше [формулу](http://en.wikipedia.org/wiki/Rotation_matrix) для вращения вектора вокруг точки ```vec2(0.0)```.
Взгляните на код функции, которая конструирует двумерную матрицу поворота. Эта функция повторяет приведённую выше [формулу](https://ru.wikipedia.org/wiki/%D0%9C%D0%B0%D1%82%D1%80%D0%B8%D1%86%D0%B0_%D0%BF%D0%BE%D0%B2%D0%BE%D1%80%D0%BE%D1%82%D0%B0) для вращения вектора вокруг точки ```vec2(0.0)```.
```glsl
mat2 rotate2d(float _angle){
@ -90,12 +90,12 @@ mat2 scale(vec2 _scale){
### Другие применения матриц: цвет в пространстве YUV
[YUV](http://en.wikipedia.org/wiki/YUV) - цветовое пространство для аналогового кодирования фото и видео, разработанное с учётом особенностей восприятия человека чтобы снизить требования к каналу передачи компонентов цвета.
[YUV](https://ru.wikipedia.org/wiki/YUV) - цветовое пространство для аналогового кодирования фото и видео, разработанное с учётом особенностей восприятия человека чтобы снизить требования к каналу передачи компонентов цвета.
В следующем коде матричные операции GLSL используются весьма интересно - с их помощью сделано преобразование из одного цветового пространства в другое.
<div class="codeAndCanvas" data="yuv.frag"></div>
Здесь мы трактуем цвета как векторы и умножаем их на матрицы. Таким образом, мы "перемещаем" значения цвета.
Здесь мы трактуем цвета как векторы и умножаем их на матрицы. Таким образом, мы «перемещаем» значения цвета.
В этой главе мы научились использовать матричные преобразования для сдвига, поворота и масштабирования векторов. Эти трансформации очень важны при построении композиций из фигур, которые мы рисовали в предыдущей главе. А в следующей главе мы используем все полученные знания для создания красивых процедурных узоров. Вы увидите, что программирование повторений и изменений может быть захватывающим занятием.

@ -6,7 +6,7 @@
В этой главе мы собираемся применить всё что мы изучили ранее и повторить это в изображении несколько раз. Как и в предыдущих главах, наша стратегия будет основана на умножении пространственных координат (между 0.0 и 1.0), так чтобы фигуры, которые мы рисуем между 0.0 и 1.0 повторялись несколько раз, образуя решётку.
*"Регулярная решётка - это то, с чем человеческой интуиции и изобретательности проще всего работать. Повторяющиеся элементы вступают в контраст с хаосом мироздания и создают ощущение порядка. Люди всегда старались украсить и разнообразить окружающее пространство с помощю повторяющихся элементов. Это прослеживается от доисторических узоров на керамике и до геометрических мозаик римских бань."* [*10 PRINT*, Издательство MIT, (2013)](http://10print.org/)
*«Регулярная решётка - это то, с чем человеческой интуиции и изобретательности проще всего работать. Повторяющиеся элементы вступают в контраст с хаосом мироздания и создают ощущение порядка. Люди всегда старались украсить и разнообразить окружающее пространство с помощю повторяющихся элементов. Это прослеживается от доисторических узоров на керамике и до геометрических мозаик римских бань.»* [*10 PRINT*, Издательство MIT, (2013)](http://10print.org/)
Для начала давайте вспомним функцию [```fract()```](../glossary/?search=fract). Она возвращает дробную часть числа, то есть работает как взятие остатка от деления на единицу ([```mod(x,1.0)```](../glossary/?search=mod)). Другими словами, [```fract()```](../glossary/?search=fract) возвращает число справа от точки. Переменная с нормализованными координатами (```st```) уже пробегает значения от 0.0 до 1.0, поэтому нет смысла делать что-то вроде:
@ -66,7 +66,7 @@ void main(){
Как видите, мы могли бы использовать [тернарный оператор](https://ru.wikipedia.org/wiki/%D0%A2%D0%B5%D1%80%D0%BD%D0%B0%D1%80%D0%BD%D0%B0%D1%8F_%D1%83%D1%81%D0%BB%D0%BE%D0%B2%D0%BD%D0%B0%D1%8F_%D0%BE%D0%BF%D0%B5%D1%80%D0%B0%D1%86%D0%B8%D1%8F) для сравнения значения по модулю ```2.0``` с единицей, но того же эффекта можно достичь с помощью [```step()```](../glossary/?search=step), которая работает быстрее. Почему? Хотя мы и не знаем как графические карты оптимизирует код, безопаснее будет предположить что встроенные функции работает быстрее, чем не встроенные. Если у вас есть возможность использовать встроенную функцию - используйте!
Теперь у нас есть формула вычисления чётности и мы можем сдвинуть нечётные строки для создания эффекта кирпичной стены. В 14 строке следующего кода мы используем эту формулу для "обнаружения" нечётных строк. Как видите, для чётных строк функция возвращает ```0.0```, что при умножении на сдвиг ```0.5``` так же даёт ```0.0```, а значит чётные строки не сдвигаются. В нечётных же строках ```0.5``` умножается на ```1.0```, поэтому в них пространство сдвигается на ```0.5``` по оси x.
Теперь у нас есть формула вычисления чётности и мы можем сдвинуть нечётные строки для создания эффекта кирпичной стены. В 14 строке следующего кода мы используем эту формулу для «обнаружения» нечётных строк. Как видите, для чётных строк функция возвращает ```0.0```, что при умножении на сдвиг ```0.5``` так же даёт ```0.0```, а значит чётные строки не сдвигаются. В нечётных же строках ```0.5``` умножается на ```1.0```, поэтому в них пространство сдвигается на ```0.5``` по оси x.
Теперь попробуйте раскомментировать строку 32. Это сделает соотношение сторон похожим на соотношение сторон кирпича. А раскомментировав 40 строку вы увидите отображение координат в красный и зелёный цвета.

@ -2,23 +2,23 @@
Не удивительно, что после стольких повторений и порядка, автор вынужден привнести немного хаоса.
## Случайность
## Беспорядок
[![Рёдзи Икеда - тестовый шаблон (2008) ](ryoji-ikeda.jpg) ](http://www.ryojiikeda.com/project/testpattern/#testpattern_live_set)
Случайность есть сильнейшее проявление энтропии. Но как получить случайность в казалось бы предсказуемом и строгом программном окружении?
Случайность есть сильнейшее проявление энтропии. Но как получить беспорядок в казалось бы предсказуемом и строгом программном окружении?
Давайте начнём со следующей функции:
<div class="simpleFunction" data="y = fract(sin(x)*1.0);"></div>
Выше мы извлекаем дробную часть синусоиды. Таким образом, значения синуса, плавно изменяющиеся от ```-1.0``` до ```1.0```, урезается до положительного диапазона от ```0.0``` до ```1.0```. Этот эффект можно использовать для получения псевдослучайных значений, "разбивая" синусоиду на меньшие кусочки. Как? Умножением значения синуса на большие числа. Попробуйте добавить нулей в функцию выше.
Выше мы извлекаем дробную часть синусоиды. Таким образом, значения синуса, плавно изменяющиеся от ```-1.0``` до ```1.0```, урезается до положительного диапазона от ```0.0``` до ```1.0```. Этот эффект можно использовать для получения псевдослучайных значений, разбивая синусоиду на меньшие кусочки. Как? Умножением значения синуса на большие числа. Попробуйте добавить нулей в функцию выше.
Когда коэффициент достигнет ```100000.0``` (то есть когда функция примет вид ```y = fract(sin(x)*100000.0)```), волны синусоиды станут неразличимыми. Дискретность дробной части повредила плавное течение синусоидальной волны, превратив её в псевдослучайный хаос.
## Управление хаосом
Использование случайности может стать непростой задачей. Она одновременно бывает слишком хаотичной и не слишком случайной. Посмотрите на следующий график. В нём мы используем функцию rand(), реализованную в точности как показано выше.
Использование беспорядка может стать непростой задачей. Он одновременно бывает слишком хаотичным и не слишком случайным. Посмотрите на следующий график. В нём мы используем функцию rand(), реализованную в точности как показано выше.
Присмотревшись, можно увидеть гребень синусоидальной волны в точках ```-1.5707``` и ```1.5707```. Легко понять почему: именно в этих точках синус достигает максимума и минимума.
@ -29,13 +29,13 @@
//y = sqrt(rand(x));
//y = pow(rand(x),5.);"></div>
Некоторое время назад [Pixelero](https://pixelero.wordpress.com) опубликовал [интересную статью о случайном распределении](https://pixelero.wordpress.com/2008/04/24/various-functions-and-various-distributions-with-mathrandom/). Я добавил несколько функций из неё в график выше, чтобы вы могли поиграться с ними и посмотреть как меняется распределение значений. Раскомментируйте функции и посмотрите что произойдёт.
Некоторое время назад [Pixelero](https://pixelero.wordpress.com) опубликовал [интересную статью о вероятностных распределениях](https://pixelero.wordpress.com/2008/04/24/various-functions-and-various-distributions-with-mathrandom/). Я добавил несколько функций из неё в график выше, чтобы вы могли поиграться с ними и посмотреть как меняется распределение значений. Раскомментируйте функции и посмотрите что произойдёт.
Читая [статью Pixelero](https://pixelero.wordpress.com/2008/04/24/various-functions-and-various-distributions-with-mathrandom/), важно помнить, что функция ```rand()``` является детерминированной, или псевдослучайной. Это означает, что, к примеру, ```rand(1.)``` всегда возвращает одно и то же значение. [Pixelero](https://pixelero.wordpress.com/2008/04/24/various-functions-and-various-distributions-with-mathrandom/) упоминает недетерминированную функцию ```Math.random()``` из ActionScript, которая каждый раз возвращает разные значения.
## Двумерная случайность
## Двумерный беспорядок
Теперь у нас есть лучшее понимание случайности, и мы можем применить её в двумерном пространстве, к осям ```x``` и ```y``` одновременно. Для этого нам нужен способ преобразования двумерного вектора в одномерное значение с плавающей точкой. Можно придумать много способов сделать это, например использовать скалярное произведение ([```dot()```](../glossary/?search=dot)). В коде ниже функция от скалярного произведения возвращает единственное число в диапазоне от ```0.0``` до ```1.0``` в зависимости от взаимного расположения векторов.
Теперь у нас есть лучшее понимание беспорядка, и мы можем применить его в двумерном пространстве, к осям ```x``` и ```y``` одновременно. Для этого нам нужен способ преобразования двумерного вектора в одномерное значение с плавающей точкой. Можно придумать много способов сделать это, например использовать скалярное произведение ([```dot()```](../glossary/?search=dot)). В коде ниже функция от скалярного произведения возвращает единственное число в диапазоне от ```0.0``` до ```1.0``` в зависимости от взаимного расположения векторов.
<div class="codeAndCanvas" data="2d-random.frag"></div>
@ -67,9 +67,9 @@
Раскомментируя строки с 50 по 53, можно получить ещё один интересный узор, а убрав комментарии со строк 35 и 36, вы увидите анимацию.
## Мастер Случайность
## Мастер Беспорядка
Японский электронный музыкант и художник [Рёдзи Икеда](http://www.ryojiikeda.com/) преуспел в использовании случайности. Сложно противостоять очарованию и гипнотизму его работ. Случайность особым образом вплетена в его работы: там она не создаёт раздражающий хаос, а отражает сложность нашей технологической культуры.
Японский электронный музыкант и художник [Рёдзи Икеда](http://www.ryojiikeda.com/) преуспел в использовании беспорядка. Сложно противостоять очарованию и гипнотизму его работ. Беспорядок особым образом вплетён в его работы: там он не создаёт раздражающий хаос, а отражает сложность нашей технологической культуры.
<iframe src="https://player.vimeo.com/video/76813693?title=0&byline=0&portrait=0" width="800" height="450" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
@ -87,6 +87,6 @@
<a href="../edit.php#10/ikeda-04.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-04.frag" width="520px" height="200px"></canvas></a>
Сделать беспорядок эстетически привлекательным непросто, особенно если вы хотите делать симуляции, которые выглядят естественно. Он слишком хаотичен, и очень немногие из реальных вещей выглядят действительно случайно. Такие хаотичные вещи, как капли дождя или график биржевых котировок, не выглядят похожими на случайный рисунок, который мы делали вначале главы. К чём причина? Ну, случайные значения никак не коррелируют друг с другом, в то время как реальные вещи обычно "помнят"" о своих предыдущих состояниях.
Сделать беспорядок эстетически привлекательным непросто, особенно если вы хотите делать симуляции, которые выглядят естественно. Он слишком хаотичен, и очень немногие из реальных вещей выглядят действительно случайно. Такие хаотичные вещи, как капли дождя или график биржевых котировок, не выглядят похожими на случайный рисунок, который мы делали вначале главы. К чём причина? Ну, случайные значения никак не коррелируют друг с другом, в то время как реальные вещи обычно «помнят» о своих предыдущих состояниях.
В следующей главе мы изучим шум. Это способ создания хаоса с помощью компьютера, который выглядит плавно и естественно.

@ -15,9 +15,9 @@
![](texture-05.jpg)
![](texture-06.jpg)
Непредсказуемость этих текстур можно было бы назвать случайной, но они не выглядят как та беспорядночность, с которой мы играли ранее. "Реальный мир" - это настолько богатое и сложное место! Как аппроксимировать это разнообразие с помощью вычислений?
Непредсказуемость этих текстур можно было бы назвать случайной, но они не выглядят как та беспорядночность, с которой мы играли ранее. «Реальный мир» - это настолько богатое и сложное место! Как аппроксимировать это разнообразие с помощью вычислений?
Этот вопрос [Кен Перлин](https://mrl.nyu.edu/~perlin/) пытался разрешить в начале 1980-ых, когда он занимался генерацией более реалистичных текстур для фильма "Трон". В итоге, он предложил элегантный оскароносный алгоритм генерации шума.
Этот вопрос [Кен Перлин](https://mrl.nyu.edu/~perlin/) пытался разрешить в начале 1980-ых, когда он занимался генерацией более реалистичных текстур для фильма «Трон». В итоге, он предложил элегантный оскароносный алгоритм генерации шума.
![Дисней - Трон (1982)](tron.jpg)
@ -64,11 +64,11 @@ y = mix(rand(i), rand(i + 1.0), u); // её использование в инт
* Используйте вашу функцию шума для анимации. Двигайте, вращайте и масштабируйте изображение.
* Создайте анимированую композицию из нескольких фигур, "танцующих" вместе с помощью шума.
* Создайте анимированую композицию из нескольких фигур, «танцующих» вместе с помощью шума.
* Сконструируйте "органические" фигуры с помощью шума.
* Сконструируйте «органические» формы с помощью шума.
* Возьмите получившееся "существо" и развивайте его дальше. Превратите его в полноценный персонаж, добавив анимации.
* Возьмите получившееся «существо» и развивайте его дальше. Превратите его в полноценный персонаж, добавив анимации.
## Двумерный шум
@ -104,21 +104,21 @@ y = mix(rand(i), rand(i + 1.0), u); // её использование в инт
* Что если интерпретировать градиент шума как поле расстояний? Сделайте с ним что-нибудь интересное.
* Теперь вы в какой-то степени управляете хаосом и порядком, и можете применить эти знания на практике. Сделайте композицию из прямоугольников, цвета и шума, которая подражает сложности картины [Марка Ротко](http://en.wikipedia.org/wiki/Mark_Rothko).
* Теперь вы в какой-то степени управляете хаосом и порядком, и можете применить эти знания на практике. Сделайте композицию из прямоугольников, цвета и шума, которая подражает сложности картины [Марка Ротко](https://ru.wikipedia.org/wiki/%D0%A0%D0%BE%D1%82%D0%BA%D0%BE,_%D0%9C%D0%B0%D1%80%D0%BA).
![Марк Ротко - Три - Three (1950)](rothko.jpg)
![Марк Ротко - Три (1950)](rothko.jpg)
## Использование шума в генеративном дизайне
Изначально алгоритмы шума разрабатывались чтобы придать естественное *что-то* цифровым текстурам. Одно- и двумерные реализаци, которые мы рассмотрели выше, работали на интерполяции шума *значений*, из-за чего мы называем их *шумом значений*. Но существуют и другие способы получения шума...
[ ![Иниго Квилез - шум значений](value-noise.png) ](../edit.php#11/2d-vnoise.frag)
[ ![Иниго Квилес - шум значений](value-noise.png) ](../edit.php#11/2d-vnoise.frag)
Как мы выяснили в предыдущих упражнениях, шум значений выглядит "блочно". Чтобы справиться с этим блочным эффектом, [Кен Перлин](https://mrl.nyu.edu/~perlin/) в 1985 году разработал другую реализацию алгоритма под названием *градиентный шум*. Кен додумался как интерполировать случайные *градиенты* вместо значений. Эти градиенты возвращаются двумерной случайной функцией, которая возвращает направления (в виде ```vec2```) вместо скалярных значений. Щёлкните по следующему изображению чтобы увидеть код и результат его работы.
Как мы выяснили в предыдущих упражнениях, шум значений выглядит «блочно». Чтобы справиться с этим блочным эффектом, [Кен Перлин](https://mrl.nyu.edu/~perlin/) в 1985 году разработал другую реализацию алгоритма под названием *градиентный шум*. Кен додумался как интерполировать случайные *градиенты* вместо значений. Эти градиенты возвращаются двумерной случайной функцией, которая возвращает направления (в виде ```vec2```) вместо скалярных значений. Щёлкните по следующему изображению чтобы увидеть код и результат его работы.
[ ![Иниго Квилез - градиентный шум](gradient-noise.png) ](../edit.php#11/2d-gnoise.frag)
[ ![Иниго Квилес - градиентный шум](gradient-noise.png) ](../edit.php#11/2d-gnoise.frag)
Посмотрите на эти два примера шума, написанные [Иниго Квилезом](http://www.iquilezles.org/), и обратите внимание на разницу между [шумом значений](https://www.shadertoy.com/view/lsf3WH) и [градиентным шумом](https://www.shadertoy.com/view/XdXGW8).
Посмотрите на эти два примера шума, написанные [Иниго Квилесом](http://www.iquilezles.org/), и обратите внимание на разницу между [шумом значений](https://www.shadertoy.com/view/lsf3WH) и [градиентным шумом](https://www.shadertoy.com/view/XdXGW8).
Как и художники, понимающие принцип работы пигментов своих красок, чем больше мы знаем о реализации шумовых алгоритмов - тем лучше мы сможем их использовать. Например, если повернуть двумерное изображение прямых линий на значение двумерного шума, получится следующий эффект закрутки, имитирующий древесину. Кликните на изображение, чтобы увидеть код.
@ -129,7 +129,7 @@ y = mix(rand(i), rand(i + 1.0), u); // её использование в инт
pattern = lines(pos,.5); // рисуем линии
```
Другой способ получить интересные картинки - это представить шум в виде поля расстояний и применить какие-нибудь трюки из [главы о фигурах](../07/).
Другой способ получить интересные картинки - это представить шум в виде поля расстояний и применить какие-нибудь трюки из [главы о фигурах](../07/?lan=ru).
[ ![Текстура поверхности воды](splatter-long.png) ](../edit.php#11/splatter.frag)
@ -146,14 +146,14 @@ y = mix(rand(i), rand(i + 1.0), u); // её использование в инт
* Какие ещё генеративные узоры вы можете сделать? Может быть гранит? Мрамор? Магма? Вода? Найдите три текстурных изображения на ваш вкус и реализуйте их алгоритмически с помощью шума.
* Используйте шум для модулирования формы.
* Как насчёт использования шума в движении? Вернитесь к [главе про матрицы](../08/). Используйте пример про сдвиг белого креста и добавьте к движению немного *беспорядка* и *шума*.
* Как насчёт использования шума в движении? Вернитесь к [главе про матрицы](../08/?lan=ru). Используйте пример про сдвиг белого креста и добавьте к движению немного *беспорядка* и *шума*.
* Сгенерируйте картину Джексона Поллока.
![Джексон Поллок - Серый №14 (1948)](pollock.jpg)
## Улучшенный шум
Перлин улучшил свой непростой шум, предложив *симплексный шум*, в котором заменил кубическую эрмитову кривую ( _f(x) = 3x^2-2x^3_ , которая идентична функции [```smoothstep()```](../glossary/?search=smoothstep)) кривой пятой степени ( _f(x) = 6x^5-15x^4+10x^3_ ). При этом оба конца кривой становятся более "плоскими", поэтому границы более плавно сшиваются друг с другом. Другими словами, получается более непрерывный переход между клетками. Посмотрите на результат, раскомментировав вторую формулу в следующем графике (или два уравнения по отдельности [здесь](https://www.desmos.com/calculator/2xvlk5xp8b)).
Перлин улучшил свой непростой шум, предложив *симплексный шум*, в котором заменил кубическую эрмитову кривую ( _f(x) = 3x^2-2x^3_ , которая идентична функции [```smoothstep()```](../glossary/?search=smoothstep)) кривой пятой степени ( _f(x) = 6x^5-15x^4+10x^3_ ). При этом оба конца кривой становятся более «плоскими», поэтому границы более плавно сшиваются друг с другом. Другими словами, получается более непрерывный переход между клетками. Посмотрите на результат, раскомментировав вторую формулу в следующем графике (или два уравнения по отдельности [здесь](https://www.desmos.com/calculator/2xvlk5xp8b)).
<div class="simpleFunction" data="
// Кубическая эрмитова кривая. То же что и SmoothStep()
@ -166,7 +166,7 @@ y = x*x*(3.0-2.0*x);
## Симплексный шум
Перлин не удовлетворился успехом своего алгоритма. Он хотел лучшей производительности. На Siggraph 2001 он представил "простой шум", который вносит следующие улучшения относительно предыдущего алгоритма:
Перлин не удовлетворился успехом своего алгоритма. Он хотел лучшей производительности. На Siggraph 2001 он представил «симплексный шум», который вносит следующие улучшения относительно предыдущего алгоритма:
* Меньшая вычислительная сложность и меньше умножений.
* Шум масштабируется в высшие размерности с меньшей потерей производительности.
@ -174,7 +174,7 @@ y = x*x*(3.0-2.0*x);
* Шум имеет хорошо определённый непрерывный градиент, который легко вычислить.
* Алгоритм достаточно прост для аппаратной реализации.
Я знаю о чём вы думаете... "Кто этот человек?" Да, он делает фантастические вещи! Но серьёзно, как он улучшил алгоритм? Смотрите: если для двух измерений мы интерполируем 4 точки (углы квадрата), то для трёх ([пример реализации здесь](../edit.php#11/3d-noise.frag)) и четырёх измерений придётся интерполировать 8 и 16 точек. Так? Другими словами, для N измерений нужно гладко интерполировать 2 в степени N точек. Но Кен вспомнил, что хоть квадрат и является удобной формой для заполнения пространства, но всё же простейшая фигура в двумерном пространстве - это равносторонний треугольник. Поэтому он начал с замены квадратной решётки (которую мы использовали ранее) на симплексную решётку, то есть равносторонние треугольники в двумерном случае.
Я знаю о чём вы думаете... «Кто этот человек?» Да, он делает фантастические вещи! Но серьёзно, как он улучшил алгоритм? Смотрите: если для двух измерений мы интерполируем 4 точки (углы квадрата), то для трёх ([пример реализации здесь](../edit.php#11/3d-noise.frag)) и четырёх измерений придётся интерполировать 8 и 16 точек. Так? Другими словами, для N измерений нужно гладко интерполировать 2 в степени N точек. Но Кен вспомнил, что хоть квадрат и является удобной формой для заполнения пространства, но всё же простейшая фигура в двумерном пространстве - это равносторонний треугольник. Поэтому он начал с замены квадратной решётки (которую мы использовали ранее) на симплексную решётку, то есть равносторонние треугольники в двумерном случае.
![](simplex-grid-00.png)

@ -114,9 +114,9 @@ for (int y= -1; y <= 1; y++) {
...
```
Этот код навеян следующим отрывком из [статьи Иниго Квилеза](http://www.iquilezles.org/www/articles/smoothvoronoi/smoothvoronoi.htm):
Этот код навеян следующим отрывком из [статьи Иниго Квилеса](http://www.iquilezles.org/www/articles/smoothvoronoi/smoothvoronoi.htm):
*«... стоит обратить внимание на красивый трюк в коде выше. Большинство реализаций страдают потерей точности потому что они генерируют случайные точки в «глобальном» пространстве («мировом» или «пространстве объекта»), а значит эти точки могут быть сколь угодно далеко от начала координат. Проблему можно решить, пересадив весь код на более высокоточный тип данных, или просто немного подумав. Моя реализация генерирует точки не в "мировом" пространстве, а в пространстве клетки: как только координата пикселя разделена на целую и дробную части, а значит каждой клетке назначены свои координаты, мы можем размышлять только о происходящем внутри клетки, то есть отбросить целую часть координаты пикселя и сберечь несколько бит точности. Фактически, в наивной реализации диаграмм Вороного целые части координат точек взаимно уничтожаются при вычитании опорной точки из текущей точки. В реализации выше мы предотвращаем взаимное уничтожение, перенося все вычисления в пространство клетки. Этот подход позволяет текстурировать диаграммой Вороного хоть целую планету, если увеличить точность входных координат до двойной, вычислить floor() и fract(), а затем перейти к одинарной точности, не тратя вычислительные ресурсы на перевод всего алгоритма на двойную точность. Разумеется, аналогичный трюк применим и к шумам Перлина (но я не видел ни описания ни реализации подобного алгоритма).»*
*«... стоит обратить внимание на красивый трюк в коде выше. Большинство реализаций страдают потерей точности потому что они генерируют случайные точки в «глобальном» пространстве («мировом» или «пространстве объекта»), а значит эти точки могут быть сколь угодно далеко от начала координат. Проблему можно решить, пересадив весь код на более высокоточный тип данных, или просто немного подумав. Моя реализация генерирует точки не в «мировом» пространстве, а в пространстве клетки: как только координата пикселя разделена на целую и дробную части, а значит каждой клетке назначены свои координаты, мы можем размышлять только о происходящем внутри клетки, то есть отбросить целую часть координаты пикселя и сберечь несколько бит точности. Фактически, в наивной реализации диаграмм Вороного целые части координат точек взаимно уничтожаются при вычитании опорной точки из текущей точки. В реализации выше мы предотвращаем взаимное уничтожение, перенося все вычисления в пространство клетки. Этот подход позволяет текстурировать диаграммой Вороного хоть целую планету, если увеличить точность входных координат до двойной, вычислить floor() и fract(), а затем перейти к одинарной точности, не тратя вычислительные ресурсы на перевод всего алгоритма на двойную точность. Разумеется, аналогичный трюк применим и к шумам Перлина (но я не видел ни описания ни реализации подобного алгоритма).»*
Резюмируя: разбиваем пространство на клетки. Каждый пиксель вычисляет расстояние до точки в своей клетке и в окружающих восьми клетках, сохраняет кратчайшее из них. Получается поле расстояний, выглядящее примерно так:
@ -173,7 +173,7 @@ for (int y= -1; y <= 1; y++) {
<div class="glslGallery" data="12/2d-cnoise-2x2,12/2d-cnoise-2x2x2,12/2d-cnoise,12/3d-cnoise" data-properties="clickRun:editor,openFrameIcon:false"></div>
Позже, в 2012 году, [Иниго Квилез написал статью о создании точных границ ячеек диаграммы Вороного](http://www.iquilezles.org/www/articles/voronoilines/voronoilines.htm).
Позже, в 2012 году, [Иниго Квилес написал статью о создании точных границ ячеек диаграммы Вороного](http://www.iquilezles.org/www/articles/voronoilines/voronoilines.htm).
<a href="../edit.php#12/2d-voronoi.frag"><img src="2d-voronoi.gif" width="520px" height="200px"></img></a>

@ -27,7 +27,7 @@
* [Узоры](09/?lan=ru)
* Генеративный дизайн
* [Случайность](10/?lan=ru)
* [Беспорядок](10/?lan=ru)
* [Шум](11/?lan=ru)
* [Клеточный шум](12/?lan=ru)
* [Фрактальное броуновское движение](13/?lan=ru)
@ -42,7 +42,7 @@
* Симуляция
* Пинг-понг
* Игра "Жизнь" Конвея
* Игра «Жизнь» Конвея
* Рябь
* Вода
* Реакционно-диффузная модель

@ -2,9 +2,7 @@
Несколько лет назад было бы слишком опрометчиво предположить, что у каждого есть компьютер с графическим ускорителем. Теперь же большинство компьютеров содержат GPU, но требование обязательного его наличия является завышенным для учебной лаборатории или класса.
Благодаря [Raspberry Pi Foundation](http://www.raspberrypi.org/), в учебных классах появился новый тип небольших и дешёвых компьютеров (около $35 за штуку). Что более важно для данной книги, [Raspberry Pi](http://www.raspberrypi.org/) поставляется с приличным GPU фирмы Broadcom, который доступен напрямую из консоли.
Я написал гибкий инструмент для программирования на GLSL в реальном времени под названием [**glslViewer**](https://github.com/patriciogonzalezvivo/glslViewer). С его помощью можно запустить все примеры из этой книги. Эта программа может выполнять обновление автоматически когда пользователь сохраняет изменения в коде. Что это означает? Каждый раз, когда вы сохраняете шейдер в процессе редактирования, он будет перезапущен и перерисует изображение.
Благодаря [Raspberry Pi Foundation](http://www.raspberrypi.org/), в учебных классах появился новый тип небольших и дешёвых компьютеров (около $35 за штуку). Что более важно для данной книги, [Raspberry Pi](http://www.raspberrypi.org/) поставляется с приличным GPU фирмы Broadcom, который доступен напрямую из консоли. Я написал гибкий инструмент для программирования на GLSL в реальном времени под названием [**glslViewer**](https://github.com/patriciogonzalezvivo/glslViewer). С его помощью можно запустить все примеры из этой книги. Эта программа может выполнять обновление автоматически когда пользователь сохраняет изменения в коде. Что это означает? Каждый раз, когда вы сохраняете шейдер в процессе редактирования, он будет перезапущен и перерисует изображение.
Сделав локальную копию репозитория книги (см. [предыдущий параграф](../00/?lan=ru)) и установив [`glslViewer`](https://github.com/patriciogonzalezvivo/glslViewer), вы можете запустить примеры. Используя флаг `-l`, вы можете рендерить примеры в углу экрана прямо во время редактирования любым редактором (`nano`, `pico`, `vi`, `vim` или `emacs`). Это так же работает при подключении по ssh или sftp.

@ -2,6 +2,7 @@
Допустим, вам не нужна навигация по тексту или взаимодействие с примерами, и вы хотите просто почитать книгу на пляже или по пути в город. В таком случае вы можете напечатать книгу.
#### Установка glslViewer
Чтобы напечатать книгу, её нужно сначала распарсить. Для этого потребуется [`glslViewer`](https://github.com/patriciogonzalezvivo/glslViewer) - консольный инструмент, который скомпилирует примеры шейдеров и преобразует их в изображения.

@ -1,6 +1,7 @@
## Введение для JavaScript-программистов
автор [Николя Баррадо](http://www.barradeau.com/)
Если вы JavaScript-разработчик, велика вероятность, что вы будете немного озадаченЫ, читая эту книгу. В самом деле, есть множество различий между манипулированием высокоуровневыми абстракциями на JS и ковырянием в шейдерах. Но, в отличие от лежащего на более низком уровне языка ассемблера, GLSL является человекочитаемым, и я уверен, что разобравшись с его особенностями, вы быстро сможете начать его использовать.
Я предполагаю, что у вас есть хотя бы поверхностные знания JavaScript и Canvas API. Если это не так - ничего страшного. Вам всё равно будет интересно читать большую часть этой главы.
@ -52,7 +53,7 @@ JS: var f = 3.14159; GLSL: float f = 3.14159;
В GLSL есть тип `void`, который приблизительно соответствует `null`. Он используется в качестве возвращаемого типа для метода, который не возвращает ничего, и вы не можете объявить переменную этого типа.
#### boolean
Как вам известно, булевы значения в основном используются для проверки условий: `if( myBoolean == true ){}else{}`. Условное ветвление очень легко использовать на CPU, но [параллельная природа](http://thebookofshaders/01/) GLSL делает это утверждение не совсем верным. Как правило, использование условных переходов не рекомендуется, и в книге описано несколько способов обойти это ограничение.
Как вам известно, булевы значения в основном используются для проверки условий: `if( myBoolean == true ){}else{}`. Условное ветвление очень легко использовать на CPU, но [параллельная природа](http://thebookofshaders/01/?lan=ru) GLSL делает это утверждение не совсем верным. Как правило, использование условных переходов не рекомендуется, и в книге описано несколько способов обойти это ограничение.
#### приведение типов
Как говорил [Боромир](https://ru.wikipedia.org/wiki/%D0%91%D0%BE%D1%80%D0%BE%D0%BC%D0%B8%D1%80), нельзя просто так взять и смешать типизированные примитивы. В отличие от JavaScript, GLSL не позволит вам выполнять операции между переменными различных типов.
@ -115,7 +116,7 @@ var p = new Point( 100,100 );
Вдумчивый читатель заметит, что каждому примитивному типу соответствует **векторный** тип. Из написанного выше легко вывести, что `bvec2` содержит два булевых значения, а `vec4` будет содержать четыре значения в плавающей точкой.
Так же векторы вводят такую величину, как размерность. Это не означает, что вы должны использовать 2D-вектор при отрисовке 2D-графики и 3D при рисовании 3D-изображений. Для чего в таком случае используется четырёхмерный вектор? (ну, на самом деле это называется "тессеракт" или "гиперкуб")
Так же векторы вводят такую величину, как размерность. Это не означает, что вы должны использовать 2D-вектор при отрисовке 2D-графики и 3D при рисовании 3D-изображений. Для чего в таком случае используется четырёхмерный вектор? (ну, на самом деле это называется «тессеракт» или «гиперкуб»)
Нет, **размерность** указывает на количество **компонентов** или **переменных**, хранимых в **векторе**:
```glsl

Loading…
Cancel
Save