Proofread Russian translation.

pull/190/head
Sergey Karchevsky 7 years ago
parent 6b830bd008
commit 8a049e9611

@ -2,7 +2,7 @@
<canvas id="custom" class="canvas" data-fragment-url="cmyk-halftone.frag" data-textures="vangogh.jpg" width="700px" height="320px"></canvas>
Изображения выше были получены различными способами. Первое Ван Гог написал вручную, нанося краску слой за слоем. Он потратил на это часы. Второе было получено за секунды смешиванием четырёх наборов пикселей: сине-зелёного, пурпурного, жёлтого и чёрного. Ключевое отличие в том, что второе изображение получено непоследовательным способом, то есть не шаг за шагом, а всё за раз.
Изображения выше были получены двумя различными способами. Первое Ван Гог написал вручную, нанося краску слой за слоем. Он потратил на это несколько часов. Второе было получено за секунды смешиванием четырёх наборов пикселей: сине-зелёного, пурпурного, жёлтого и чёрного. Ключевое отличие в том, что второе изображение получено непоследовательным способом, то есть не шаг за шагом, а всё за раз.
Эта книга повествует о революционной компьютерной технологии - *фрагментных шейдерах*, которые выводят генерируемые компьютером изображения на новый уровень. Их можно назвать эквивалентом печатного станка Гутенберга в мире графики.
@ -12,17 +12,17 @@
![Игра Journey от That Game Company](journey.jpg)
В следующих главах вы увидите насколько это мощная технология, и как её можно применить в ваших рабочих и личных проектах.
В следующих главах вы увидите, какую мощь предоставляет эта технология, и как её можно использовать для рабочих и личных проектов.
## Для кого эта книга?
Эта книга написана для творческих программистов, разработчиков игр и инженеров с опытом программирования, базовыми знаниями линейной алгебры и тригонометрии, и имеющих желание придать своей работе качественно новый уровень графических эффектов. Если вы ещё только хотите научиться программировать, я бы рекомендовал вам начать с сайта [Processing](https://processing.org/), и вернуться позже, когда освоитесь.
Эта книга написана для творческих программистов, разработчиков игр и инженеров с опытом программирования, базовыми знаниями линейной алгебры и тригонометрии, а так же желающих вывести свою работу на качественно новый уровень графических эффектов. Если вы ещё только хотите научиться программировать, я бы рекомендовал вам начать с сайта [Processing](https://processing.org/), и вернуться позже, когда освоитесь.
Эта книга научит вас использовать шейдеры и встраивать их ваши проекты, повышать их производительность и качество получаемой картинки. Поскольку шейдеры на GLSL (языке шейдеров OpenGL) компилируются и запускаются на самых разных платформах, вы сможете применять полученные знания в любой среде, где есть OpenGL, OpenGL ES или WebGL. Другими словами, вы сможете применить эти умения в скетчах на [Processing](https://processing.org/), приложениях на [openFrameworks](http://openframeworks.cc/), интерактивных инсталляциях [Cinder](http://libcinder.org/), сайтах на [Three.js](http://threejs.org/) или мобильных iOS/Android-играх.
## Какие темы освещает эта книга?
Эта книга уделяет основное внимание пиксельным шейдерам на GLSL. Сначала мы определим что такое шейдеры, а затем научимся создавать с их помощью процедурные геометрические фигуры, узоры, текстуры и анимации. Вы изучите основы языка шейдеров и примените их к задачам, возникающим на практике: обработке изображений (логические операции над изображениями, размытие, свёртки с ядром, цветовые фильтры, таблицы значений и другие эффекты) и симуляциям (игра «Жизнь» Конвея, реакция типа Белоусова-Жаботинского, рябь на поверхности воды, акварельные эффекты, диаграммы Вороного и т.п.). Ближе к концу книги будут изложены продвинутые методы на основе алгоритмов трассировки лучей.
Эта книга уделяет основное внимание пиксельным шейдерам на GLSL. Сначала мы изучим что такое шейдеры, а затем научимся создавать с их помощью процедурные геометрические фигуры, узоры, текстуры и анимации. Вы изучите основы языка шейдеров и примените их к задачам, возникающим на практике: обработке изображений (логические операции над изображениями, размытие, свёртки с ядром, цветовые фильтры, таблицы значений и другие эффекты) и созданию симуляций (игра «Жизнь» Конвея, модель Грея-Скотта для реакции-диффузии, рябь на поверхности воды, акварельные эффекты, диаграммы Вороного и т.п.). Ближе к концу книги будут изложены продвинутые методы на основе алгоритмов трассировки лучей.
*Каждый параграф содержит интерактивные примеры*. Изменения в них показываются непосредственно при редактировании кода. Излагаемые принципы могут быть довольно абстрактными, поэтому интерактивные примеры очень полезны при изучении материала. Чем быстрее вы увидите изучаемые концепции в действии, тем проще будет процесс обучения.

@ -35,7 +35,7 @@
## Что такое GLSL?
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)
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)
## Почему программирование шейдеров - это боль?
@ -43,6 +43,6 @@ GLSL расшифровывается как OpenGL Shading Language (язык
Для параллельной работы каждый поток не должен зависеть от остальных потоков. Потоки «слепы» по отношению к тому, чем занимаются другие потоки. Из этого ограничения следует, что все данные должны перемещаться в одном направлении. Поэтому невозможно использовать результат соседнего потока, изменить входные данные или направить выход одного потока на вход другого. Попытка организации межпотокового взаимодействия несёт риск нарушения целостности данных.
Кроме того, GPU постоянно поддерживает свои процессоры в занятом состоянии. Как только они освобождаются, они тут же получают новую порцию данных для обработки. Поток не может узнать что он делал в предыдущий момент времени. Он мог рисовать кнопку для графического интерфейса операционной системы, затем рисовать кусок неба в игре, а потом отображать текст почтового сообщения. Каждый поток не только **слеп**, но ещё и **лишён памяти**. Наряду с абстракцией функции, изменяющей свой результат в зависимости от положения пикселя, слепота и беспамятство потоков не добавляют шейдерам популярности среди начинающих программистов.
Кроме того, GPU постоянно поддерживает свои процессоры в занятом состоянии. Как только процессор освобождается, он сразу же получает новую порцию данных для обработки. Поток не может узнать что он делал в предыдущий момент времени. Он мог рисовать кнопку для графического интерфейса операционной системы, затем рисовать кусок неба в игре, а потом отображать текст почтового сообщения. Каждый поток не только **слеп**, но ещё и **лишён памяти**. Наряду с представлением в виде абстрактной функции, изменяющей свой результат в зависимости от положения пикселя, слепота и беспамятство потоков не добавляют шейдерам популярности среди начинающих программистов.
Не волнуйтесь! В следующих главах мы пошагово рассмотрим шейдерные вычисления, начиная с самых простых. Если вы читаете книгу в современном браузере, вы оцените возможность поиграться с интерактивными примерами. Так давайте же не откладывать веселье в долгий ящик! Нажмите *Next >>* чтобы перейти к программированию!

@ -6,9 +6,9 @@
<div class="codeAndCanvas" data="hello_world.frag"></div>
С кодом выше можно взаимодействовать, если вы читаете книгу в браузере. Это означает, что вы можете изменить любую часть кода по вашему желанию. Изменения будут видны сразу же благодаря архитектуре GPU, которая компилирует и заменяет шейдеры *на лету*. Попробуйте изменить значения в строке 6.
С кодом выше можно взаимодействовать, если вы читаете книгу в браузере. Это означает, что вы можете изменить любую часть кода по вашему желанию. Благодаря архитектуре GPU, которая компилирует и запускает шейдеры *на лету*, вы увидите изменения незамедлительно. Попробуйте изменить значения в строке 6.
Эти несколько строчек кода не выглядят чем-то существенным, но мы можем извлечь из них кое-какие знания:
Эти несколько строчек кода не похожи на нормальную, зрелую программу, но мы можем извлечь из них кое-какие знания:
1. Язык шейдеров содержит функцию `main`, которая возвращает цвет по окончании работы. Это напоминает C.
@ -50,4 +50,4 @@ vec4 red(){
vec4 color = vec4(vec3(1.0,0.0,1.0),1.0);
```
Очевидно, это не самый крутой шейдер. Это всего лишь самый базовый пример, который красит все пиксели экрана в один цвет. В следующей главе мы покажем как изменять цвет пикселя в зависимости от двух типов входных данных: пространственных (положение пикселя на экране) и временных (количество секунд с момента загрузки страницы).
Очевидно, это не самый крутой шейдер. Это всего лишь базовый пример, который красит все пиксели экрана в один цвет. В следующей главе мы покажем как изменять цвет пикселя в зависимости от двух типов входных данных: пространственных (положение пикселя на экране) и временных (количество секунд с момента загрузки страницы).

@ -1,6 +1,6 @@
## Uniform-переменные
В предыдущей главе мы увидели каким образом GPU управляет большим количеством параллельных потоков, каждый из которых отвечает за назначение цвета небольшой части изображения. Каждый из параллельных потоков не знает о состоянии остальных, но всё же нам бывает нужно передавать входные данные от CPU ко всем потокам одновременно. Из-за особенностей архитектуры графических карт, все такие входные данные будут одинаковыми (однородными, *uniform*) для всех потоков и доступными *только для чтения*. Другими словами, каждый поток принимает на вход одни и те же данные, которые он может прочитать и не может перезаписать.
В предыдущей главе мы увидели каким образом GPU управляет большим количеством параллельных потоков, каждый из которых отвечает за назначение цвета небольшой части изображения. Каждый из параллельных потоков не знает о состоянии остальных, но всё же нам бывает нужно передавать входные данные от CPU ко всем потокам одновременно. Из-за особенностей архитектуры графических карт, все такие входные данные будут одинаковыми для всех потоков (однородными, *uniform*) и доступными *только для чтения*. Другими словами, каждый поток принимает на вход одни и те же данные, которые он может прочитать и не может перезаписать.
Эти входы называются однородными (`uniform`) и могут иметь практически любой из поддерживаемых типов: `float`, `vec2`, `vec3`, `vec4`, `mat2`, `mat3`, `mat4`, `sampler2D` и `samplerCube`. Uniform-переменные с указанием своих типов объявляются вначале шейдера сразу после указания точности по умолчанию.
@ -44,7 +44,7 @@ uniform float iGlobalTime; // время работы шейдера (секу
В коде выше мы нормализуем координаты фрагмента, разделяя их на разрешение изображения. В результате значения переходят в диапазон между `0.0` и `1.0`, что упрощает отображение значений X и Y на красный и зелёный каналы.
В мире шейдеров у нас нет нормальных отладочных инструментов, поэтому приходится задавать переменным яркие цвета и пытаться извлечь из них смысл. Вы увидите, что иногда программирование на GLSL похоже на засовывание корабля в бутылку. Оно столь же сложно, сколько красиво и захватывающе.
В мире шейдеров у нас нет нормальных отладочных инструментов, поэтому приходится задавать переменным яркие цвета и пытаться извлечь из них смысл. Вы увидите, что иногда программирование на GLSL похоже на засовывание корабля в бутылку. Оно столь же сложно, сколь красиво и захватывающе.
![](08.png)
@ -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`.

@ -1,6 +1,6 @@
## Запуск шейдера
В процессе создания этой книги и просто из любви к искусству я создал набор инструментов для создания, отображения, опубликования и сопровождения шейдеров. Эти инструменты работают одинаково на Linux, MacOS, [Raspberry Pi](https://www.raspberrypi.org/) и в браузерах без необходимости изменения кода.
В процессе создания этой книги и просто из любви к искусству я создал набор инструментов для создания, отображения, опубликования и сопровождения шейдеров. Эти инструменты работают одинаково на Linux, MacOS, [Raspberry Pi](https://www.raspberrypi.org/) и в браузерах без переписывания кода.
**Отображение**: все интерактивные примеры в книге показываются с помощью [glslCanvas](https://github.com/patriciogonzalezvivo/glslCanvas), который делает процесс запуска отдельных шейдеров невероятно простым.
@ -16,7 +16,7 @@
glslViewer yourShader.frag yourInputImage.png —w 500 -h 500 -s 1 -o yourOutputImage.png
```
**Создание**: чтобы сделать опыт создания шейдеров более ярким, я сделал онлайн-редактор [glslEditor](https://github.com/patriciogonzalezvivo/glslEditor). Этот редактор используется для демонстрации интерактивных примеров в книге и предоставляет набор виджетов, делающих абстрактный код на GLSL более осязаемым. ЕГо так же можно запустить в отдельном веб-приложении по адресу [editor.thebookofshaders.com/](http://editor.thebookofshaders.com/). Подробнее о редакторе [здесь](https://github.com/patriciogonzalezvivo/glslEditor).
**Создание**: чтобы сделать опыт создания шейдеров более ярким, я сделал онлайн-редактор [glslEditor](https://github.com/patriciogonzalezvivo/glslEditor). Этот редактор используется для демонстрации интерактивных примеров в книге и предоставляет набор виджетов, делающих абстрактный код на GLSL более осязаемым. Его так же можно запустить в отдельном веб-приложении по адресу [editor.thebookofshaders.com](http://editor.thebookofshaders.com/). Подробнее о редакторе [здесь](https://github.com/patriciogonzalezvivo/glslEditor).
![](glslEditor-01.gif)
@ -38,7 +38,7 @@ glslViewer yourShader.frag yourInputImage.png —w 500 -h 500 -s 1 -o yourOutput
### **Three.js**
Рикардо Кабелло (aka [MrDoob](https://twitter.com/mrdoob) ) - отличный парень разработавший при содействии группы [единомышленников](https://github.com/mrdoob/three.js/graphs/contributors) один из лучших WebGL-фреймворков под названием [Three.js](http://threejs.org/). Там вы найдёте множество примеров, учебных курсов и книг с помощью которых вы сможете создавать крутую 3D-графику на JavaScript.
Рикардо Кабелло (aka [MrDoob](https://twitter.com/mrdoob) ) и группа [единомышленников](https://github.com/mrdoob/three.js/graphs/contributors) разработали один из лучших WebGL-фреймворков под названием [Three.js](http://threejs.org/). Там вы найдёте множество примеров, учебных курсов и книг с помощью которых можно создавать крутую 3D-графику на JavaScript.
Ниже вы видите минимальный пример HTML и JS-кода, позволяющего начать использовать шейдеры на three.js. Обратите внимание на скрипт с `id="fragmentShader"`. Именно благодаря ему вы можете копировать шейдеры из книги.
@ -172,7 +172,7 @@ void main() {
### **openFrameworks**
У каждого человека есть место, где ему комфортно, и для меня таковым остаётся [сообщество openFrameworks](http://openframeworks.cc/). Этот C++-фреймворк оборачивает OpenGL и другие C++-библиотеки. Он во многом похож на Processing с поправкой на особенности использования компиляторов языка C++. Как и Processing, openFramework ищет шейдеры в папке `data`, поэтому не забудьте скопировать в него и файлы с расширением `.frag` и переименовывать их перед использованием.
У каждого человека есть место, где ему комфортно, и для меня таковым остаётся [сообщество openFrameworks](http://openframeworks.cc/). Этот C++-фреймворк является обёрткой для OpenGL и других библиотек на C++. Он во многом похож на Processing с поправкой на особенности использования компиляторов языка C++. Как и Processing, openFramework ищет шейдеры в папке `data`, поэтому не забудьте скопировать в него и файлы с расширением `.frag` и переименовывать их перед использованием.
```cpp
void ofApp::draw(){

@ -37,7 +37,7 @@
<div class="codeAndCanvas" data="smoothstep.frag"></div>
В строке 12 приведённого выше кода мы используем smoothstep для рисования зелёной линии в функции `plot()`. Для каждой точки вдоль оси *x* эта функция делает *всплеск* при нужно значении *y*. Как? Через соединение двух [`smoothstep()`](../glossary/?search=smoothstep). Рассмотрите следующую функцию, ставьте её вместо строки 20 в коде выше и вообразите, что это вертикальный разрез. Фон выглядит как линия, не так ли?
В строке 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);
@ -47,7 +47,7 @@ float y = smoothstep(0.2,0.5,st.x) - smoothstep(0.5,0.8,st.x);
Синус и косинус - ваши лучшие друзья, когда вы используете математику для анимации, построения фигур или смешивания значений.
Эти две базовые тригонометрические функции удобны как швейцарский армейский нож при построении кругов, и обычно они используются в паре. Очень важно знать как они себя ведут и какими способами могут быть скомбинированны. Вкратце, они принимают угол в радианах и возвращают координаты *x* ([косинус](../glossary/?search=cos)) и *y* ([синус](../glossary/?search=sin)) точки на окружности единичного радиуса. Тот факт, что они возвращают нормализованные значения (между -1 и 1) и при этом являются достаточно гладкими, делает их незаменимым инструментом.
Эти две базовые тригонометрические функции при построении кругов удобны, как швейцарский армейский нож, и обычно они используются в паре. Очень важно знать как они себя ведут и какими способами могут быть скомбинированны. Вкратце, они принимают угол в радианах и возвращают координаты *x* ([косинус](../glossary/?search=cos)) и *y* ([синус](../glossary/?search=sin)) точки на окружности единичного радиуса. Тот факт, что они возвращают нормализованные значения (между -1 и 1) и при этом являются достаточно гладкими, делает их незаменимым инструментом.
![](sincos.gif)
@ -57,7 +57,7 @@ float y = smoothstep(0.2,0.5,st.x) - smoothstep(0.5,0.8,st.x);
Внимательно присмотритесь к этой синусоидальной волне. Обратите внимание на плавное изменение значения *y* между -1 и 1. Как мы видели в примере со временем в предыдущем параграфе, это ритмичное поведение синуса [`sin()`](../glossary/?search=sin) можно использовать в анимациях. Если вы читаете этот пример в браузере, вы можете поизменять формулу выше и пронаблюдать как изменяется волна. Не забывайте ставить точку с запятой в конце строки.
Попробуйте проделать следующие и посмотрите что происходит:
Попробуйте проделать следующие действия и посмотрите что происходит:
* Прибавьте время (`u_time`) к *x* перед вычислением синуса. Вы увидите *движение* вдоль *x*.
@ -107,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>

@ -2,7 +2,7 @@
## Цвета
Ранее мы не очень подробно раскрывали тему работы с векторами в GLSL. Прежде чем двинуться дальше, не помешает изучить эту тему поглубже, и тема цветов как нельзя лучше подходит для этого.
Ранее мы не очень подробно раскрывали тему работы с векторами в GLSL. Прежде чем двинуться дальше, не помешает изучить векторы поглубже, и работа с цветом как нельзя лучше подходит для этого.
Если вы знакомы с объектно-ориентированной парадигмой программирования, вы скорее всего заметили, что мы обращаемся к данным в векторах на манер структур (`struct`) в языке С.
@ -25,7 +25,7 @@ vector[2] = vector.b = vector.z = vector.p;
vector[3] = vector.a = vector.w = vector.q;
```
Все эти способы - всего лишь дополнительные имена для доступа к одним и тем же данным, которые призваны помочь вам в создании более понятного кода. Эта гибкость языка шейдеров станет путеводной нитью, помогающей вам мыслить о координатах цвета и пространства как о взаимозаменяемых сущностях.
Все эти способы - всего лишь дополнительные имена для доступа к одним и тем же данным, которые призваны помочь вам в создании более понятного кода. Эта гибкость языка шейдеров станет путеводной нитью, помогающей вам мыслить о значениях цвета и координатах пространства как о взаимозаменяемых сущностях.
Другое замечательное свойство векторных типов GLSL - это возможность доступа к координатам в произвольном порядке, которая упрощает преобразование и смешивание значений.
@ -104,9 +104,9 @@ vec4({{rn}},{{gn}},{{bn}},1.0)
Пространство HSB изначально было разработано для представления в полярных координатах (на основе угла и радиуса), вместо декартовых (x и y). Чтобы перевести нашу функцию HSB в полярные координаты, нужно вычислить угол и расстояние, используя центр области рисования и декартовы координаты пикселя. Для этого мы используем функцию вычисления расстояния [`length()`](../glossary/?search=length) и двухаргументный арктангенс ([`atan(y,x)`](../glossary/?search=atan), GLSL-версия известной функции `atan2(y,x)`).
Тригонометрические функции от векторного аргумента вычисляются покомпонетно, даже если вы используете векторы для представления цветов. Мы начинаем использовать цвета и векторы одинаковым способом. Вы увидите, что такая концептуальная гибкость является очень мощным инструментом.
Тригонометрические функции от векторного аргумента вычисляются покомпонетно, даже если вы используете векторы для представления цветов. Мы начинаем использовать цвета и векторы одинаковым способом. Такая концептуальная гибкость является очень мощным инструментом.
**На заметку:** Возможно, вы спросите, какие ещё есть геометрически функции кроме длины ([`length`](../glossary/?search=length)). Их довольно много: [`dot()`](../glossary/?search=dot) (скалярное произведение), [`cross`](../glossary/?search=cross) (векторное произведение), [`normalize()`](../glossary/?search=normalize) (привести вектор к единичной длине), [`faceforward()`](../glossary/?search=faceforward) (вернуть вектор, указывающий в то же полупространство, что и данный), [`reflect()`](../glossary/?search=reflect) (отражение) и [`refract()`](../glossary/?search=refract) (преломление). Так же в GLSL есть векторные функции сравнения: [`lessThan()`](../glossary/?search=lessThan) (меньше), [`lessThanEqual()`](../glossary/?search=lessThanEqual) (меньше либо равно), [`greaterThan()`](../glossary/?search=greaterThan) (больше), [`greaterThanEqual()`](../glossary/?search=greaterThanEqual) (больше либо равно), [`equal()`](../glossary/?search=equal) (равно) и [`notEqual()`](../glossary/?search=notEqual) (не равно).
**На заметку:** Возможно, вы спросите, какие ещё есть геометрически функции кроме длины ([`length`](../glossary/?search=length))? Их довольно много: [`dot()`](../glossary/?search=dot) (скалярное произведение), [`cross`](../glossary/?search=cross) (векторное произведение), [`normalize()`](../glossary/?search=normalize) (привести вектор к единичной длине), [`faceforward()`](../glossary/?search=faceforward) (вернуть вектор, указывающий в то же полупространство, что и данный), [`reflect()`](../glossary/?search=reflect) (отражение) и [`refract()`](../glossary/?search=refract) (преломление). Так же в GLSL есть векторные функции сравнения: [`lessThan()`](../glossary/?search=lessThan) (меньше), [`lessThanEqual()`](../glossary/?search=lessThanEqual) (меньше либо равно), [`greaterThan()`](../glossary/?search=greaterThan) (больше), [`greaterThanEqual()`](../glossary/?search=greaterThanEqual) (больше либо равно), [`equal()`](../glossary/?search=equal) (равно) и [`notEqual()`](../glossary/?search=notEqual) (не равно).
Получив угол и длину, мы должны нормировать их значения в интервал от 0.0 до 1.0. В строке 27 [`atan(y,x)`](../glossary/?search=atan) возвращает угол в радианах от минус пи до пи, поэтому сначала мы разделим его на удвоенное пи (`TWO_PI`, определено вначале кода), и к полученному числу от -0.5 до 0.5 прибавим 0.5, чтобы перейти в нужный интервал от 0.0 до 1.0. Максимальный радиус будет равен 0.5 (мы вычисляли расстояние от центра окна), поэтому его нужно удвоить.
@ -122,7 +122,7 @@ vec4({{rn}},{{gn}},{{bn}},1.0)
![Вильям Хоум Лизарс - Красный, синий и жёлтый спектры в составе солнечного спектра (1834)](spectrums.jpg)
* Если вы присмотритесь к цветовому кругу в программах для подбора цвета (изображён ниже), вы увидите что он пострен на основе красного, жёлтого и синего цветов. Например, напротив красного должен быть зелёный, но в нашем примере выше там находится голубой. Исправьте пример, так чтобы он выглядел в точности как изображение ниже (подсказка: используйте функции формы).
* Присмотревшись к цветовому кругу в программах для подбора цвета (изображён ниже), можно заметить, что он пострен на основе красного, жёлтого и синего цветов. Например, напротив красного должен быть зелёный, но в нашем примере выше там находится голубой. Исправьте пример, так чтобы он выглядел в точности как изображение ниже (подсказка: используйте функции формы).
![](colorwheel.png)

@ -72,7 +72,7 @@ color = vec3(bl.x * bl.y * tr.x * tr.y);
Интересно? Вся эта техника направлена на использование [`step()`](../glossary/?search=step), умножения в качестве логической операции и переворот координат.
Перед тем как продолжит, попробуйте выполнить упражнения:
Перед тем как продолжить, попробуйте выполнить упражнения:
* Измените размер и пропорции прямоугольника.
@ -222,10 +222,10 @@ float a = atan(pos.y,pos.x);
* Используя этот пример, напишите функцию, которая принимает положение и количество углов многогранника и возвращает значение поля расстояний в точке.
* Смешайте поля расстояний с помощью функции [`min()`](../glossary/?search=min) and [`max()`](../glossary/?search=max).
* Смешайте поля расстояний с помощью функции [`min()`](../glossary/?search=min) и [`max()`](../glossary/?search=max).
* Найдите какой-нибудь логотип из геометрических фигур и воспроизведите его в виде поля расстояний.
Поздравляю! Мы прошли трудную тему! Вдохните и дайте изученным принципам устояться. Рисовать фигуры - это просто в Processing, но не здесь. В мире шейдеров алгоритмы рисования фигур очень заковыристы, и адаптация к такой парадигме программирования может стоить значительных усилий.
Теперь, когда вы умеете рисовать фигуры, новые идеи будут появляться в вашей с огромной скоростью. В следующей главе мы научимся двигать, вращать и масштабировать фигуры. Вы сможете делать композиции!
Теперь, когда вы умеете рисовать фигуры, новые идеи будут появляться в вашей голове с огромной скоростью. В следующей главе мы научимся двигать, вращать и масштабировать фигуры. Вы сможете делать композиции!

@ -65,7 +65,7 @@ mat2 rotate2d(float _angle){
![](scale.png)
Следуя этой формуле, можно понять как делается двумерная матрица масштабирования.
Следуя этой формуле, можно сконструировать двумерную матрицу масштабирования.
```glsl
mat2 scale(vec2 _scale){

@ -4,9 +4,9 @@
[ ![Нина Вормердам - Проект IMPRINT (2013)](warmerdam.jpg) ](../edit.php#09/dots5.frag)
В этой главе мы собираемся применить всё что мы изучили ранее и повторить это в изображении несколько раз. Как и в предыдущих главах, наша стратегия будет основана на умножении пространственных координат (между 0.0 и 1.0), так чтобы фигуры, которые мы рисуем между 0.0 и 1.0 повторялись несколько раз, образуя решётку.
В этой главе мы собираемся применить весь ранее изученный материал, и повторить это в изображении несколько раз. Как и в предыдущих главах, наша стратегия будет основана на умножении пространственных координат (между 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, поэтому нет смысла делать что-то вроде:
@ -108,9 +108,9 @@ void main(){
<a href="../edit.php#09/iching-01.frag"><canvas id="custom" class="canvas" data-fragment-url="iching-01.frag" width="520px" height="200px"></canvas></a>
## СОздавайте свои собственные правила
## Создавайте свои собственные правила
Создание процедурных узоров - это умственное упражнение по поиску минимальных повторно используемых элементов. Это древняя практика. Мы, как биологический вид, издавна использовали узоры для украшения тканей, пола и границ объектов: вспомните извилистые узоры древней Греции или решётчатые узоры из Китая. Магия повторений и вариаций всегда захватывала наше изображение. Рассмотрите [декоративные](https://archive.org/stream/traditionalmetho00chririch#page/130/mode/2up) [узоры](https://www.pinterest.com/patriciogonzv/paterns/) и обратите внимание как художники и дизайнеры балансируют между предсказуемостью порядка и внезапностью изменчивости и хаоса. От арабских геометрических узоров и до бесподобных африканских тканей раскинулась целая вселенная узоров, среди которых есть что изучить.
Создание процедурных узоров - это умственное упражнение по поиску минимальных повторно используемых элементов. Это древняя практика. Мы, как биологический вид, издавна использовали узоры для украшения тканей, пола и границ объектов: вспомните извилистые узоры древней Греции или решётчатые узоры из Китая. Магия повторений и вариаций всегда захватывала наше воображение. Рассмотрите [декоративные](https://archive.org/stream/traditionalmetho00chririch#page/130/mode/2up) [узоры](https://www.pinterest.com/patriciogonzv/paterns/) и обратите внимание как художники и дизайнеры балансируют между предсказуемостью порядка и внезапностью изменчивости хаоса. От арабских геометрических узоров и до бесподобных африканских тканей раскинулась целая вселенная узоров, среди которых есть что изучить.
![Франц Сейлс Мейер - Учебник орнамента (1920)](geometricpatters.png)

@ -53,7 +53,7 @@
<div class="codeAndCanvas" data="2d-random-mosaic.frag"></div>
После увеличения пространства в 10 раз (строка 21), мы отделяем целые части координат от дробных. Эта операция нам уже знакома по разбиению пространства на клетки, в каждой из которых координаты изменяются от ```0.0``` до ```1.0```. Извлекая целую часть координаты мы получаем общее число для всех пикселей одной клетки. Далее мы можем использовать это целое число чтобы сгенерировать случайное число для всей клетки. Так как случайная функция детерминирована, это случайное число получится одинаковым для всех пикселей клетки.
После увеличения пространства в 10 раз (строка 21), мы отделяем целые части координат от дробных. Эта операция нам уже знакома по разбиению пространства на клетки, в каждой из которых координаты изменяются от ```0.0``` до ```1.0```. Извлекая целую часть координаты, мы получаем общее число для всех пикселей одной клетки. Далее мы можем использовать это целое число чтобы сгенерировать случайное число для всей клетки. Так как случайная функция детерминирована, это случайное число получится одинаковым для всех пикселей клетки.
Раскомментируйте строку 29, где мы используем сохранённую дробную часть координат пикселя в качестве нормированных координат внутри клетки.
@ -75,7 +75,7 @@
Посмотрите на работу [Икеды](http://www.ryojiikeda.com/) и попробуйте выполнить следующее:
* СДелайте горизонтальное движение клеток со случайными значениями в противоположных направлениях. Показывайте только наиболее светлые клетки. Изменяйте скорость движения клеток со временем.
* Сделайте горизонтальное движение клеток со случайными значениями в противоположных направлениях. Показывайте только наиболее светлые клетки. Изменяйте скорость движения клеток со временем.
<a href="../edit.php#10/ikeda-00.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-00.frag" width="520px" height="200px"></canvas></a>
@ -83,10 +83,10 @@
<a href="../edit.php#10/ikeda-03.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-03.frag" width="520px" height="200px"></canvas></a>
* Create other interesting effects.
* Создайте ещё какие-нибудь эффекты.
<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>
Сделать беспорядок эстетически привлекательным непросто, особенно если вы хотите делать симуляции, которые выглядят естественно. Он слишком хаотичен, и очень немногие из реальных вещей выглядят действительно случайно. Такие хаотичные вещи, как капли дождя или график биржевых котировок, не выглядят похожими на случайный рисунок, который мы делали вначале главы. К чём причина? Ну, случайные значения никак не коррелируют друг с другом, в то время как реальные вещи обычно «помнят» о своих предыдущих состояниях.
Сделать беспорядок эстетически привлекательным непросто, особенно если вы хотите делать симуляции, которые выглядят естественно. Беспорядок слишком хаотичен, и очень немногие из реальных вещей выглядят действительно случайно. Такие хаотичные вещи, как капли дождя или график биржевых котировок, не выглядят похожими на случайный рисунок, который мы делали вначале главы. В чём причина? Ну, случайные значения никак не коррелируют друг с другом, в то время как реальные вещи обычно «помнят» о своих предыдущих состояниях.
В следующей главе мы изучим шум. Это способ создания хаоса с помощью компьютера, который выглядит плавно и естественно.

@ -5,7 +5,7 @@
Время передохнуть! Мы игрались со случайными значениями, которые выглядят как белый шум в расстроенном телевизоре, голова всё ещё кружится от раздумий о шейдерах, а глаза устали. Давайте выберемся на прогулку!
Мы ощущаем ветер нашей кожей, солнце светит в лицо. Мир - очень яркое и богатое место. Цвета, фактуры, звуки. На прогулке мы не можем не заметить поверхности дорог, камней, деревьев и облаков.
Мы ощущаем ветер кожей, солнце светит в лицо. Мир - очень яркое и богатое место. Цвета, фактуры, звуки. На прогулке мы не можем не заметить поверхности дорог, камней, деревьев и облаков.
![](texture-00.jpg)
![](texture-01.jpg)
@ -17,7 +17,7 @@
Непредсказуемость этих текстур можно было бы назвать случайной, но они не выглядят как та беспорядночность, с которой мы играли ранее. «Реальный мир» - это настолько богатое и сложное место! Как аппроксимировать это разнообразие с помощью вычислений?
Этот вопрос [Кен Перлин](https://mrl.nyu.edu/~perlin/) пытался разрешить в начале 1980-ых, когда он занимался генерацией более реалистичных текстур для фильма «Трон». В итоге, он предложил элегантный оскароносный алгоритм генерации шума.
Этот вопрос [Кен Перлин](https://mrl.nyu.edu/~perlin/) пытался разрешить в начале 1980-ых, когда он занимался генерацией реалистичных текстур для фильма «Трон». В итоге, он предложил элегантный оскароносный алгоритм генерации шума.
![Дисней - Трон (1982)](tron.jpg)
@ -33,15 +33,15 @@ y = rand(i); // функция rand() описана в предыдущей г
В этих строках мы делаем что-то похожее на код из предыдущей главы. Мы разбиваем непрерывное значение с плавающей точкой (```x```) на целую (```i```) и дробную (```f```) части. Для получения целой части мы используем [```floor()```](../glossary/?search=floor), а для дробной - [```fract()```](../glossary/?search=fract). Затем мы применяем ```rand()``` к целой части ```x```, получая уникальное случайное число для каждого нового целого.
Далее идут две закомментированные строки. Первая линейно интерполирует каждое случайное значение.
Далее идут две закомментированные строки. Первая из них линейно интерполирует каждое случайное значение.
```glsl
y = mix(rand(i), rand(i + 1.0), f);
```
Раскомментируйте строку и посмотрите как это выглядит. Мы используем значение [```fract()```](../glossary/?search=fract) из `f` для смешивания ([```mix()```](../glossary/?search=mix)) двух случайных значений.
Раскомментируйте строку и посмотрите что получится. Мы используем значение [```fract()```](../glossary/?search=fract) из переменной `f` для смешивания ([```mix()```](../glossary/?search=mix)) двух случайных значений.
Ранее мы уже видели лучшее решение, чем линейная интерполяция, не так ли? Попробуйте раскомментировать следующую строку, в которой используется гладкая ([```smoothstep()```](../glossary/?search=smoothstep)) интерполяция вместо линейной:
Ранее мы уже видели лучшее решение, чем линейная интерполяция, не так ли? Попробуйте раскомментировать следующую строку, в которой используется гладкая интерполяция ([```smoothstep()```](../glossary/?search=smoothstep)) вместо линейной:
```glsl
y = mix(rand(i), rand(i + 1.0), smoothstep(0.,1.,f));
@ -54,17 +54,17 @@ float u = f * f * (3.0 - 2.0 * f ); // самописная кубическая
y = mix(rand(i), rand(i + 1.0), u); // её использование в интерполяции
```
Эта *плавная беспорядочность* существенно меняет расклад. Она даёт графическим инженерам и художникам генерировать изображения и геометрию с ощущением целостности. Алгоритм шума Перлина был многократно реализован на разных языках и для различных размерностей, помогая создавать завораживающие произведения искусства в самых разных видах творческой деятельности.
Эта *плавная беспорядочность* существенно меняет расклад. Она помогает графическим инженерам и художникам генерировать изображения и геометрию с ощущением целостности. Алгоритм шума Перлина был многократно реализован на разных языках и для различных размерностей, помогая создавать завораживающие произведения искусства в самых разных видах творческой деятельности.
![Роберт Ходжин - Написанные изображения (2010)](robert_hodgin.jpg)
Теперь ваш ход:
* Напишите свою собственную функцию ```float noise(float x)```
* Напишите свою собственную функцию ```float noise(float x);```
* Используйте вашу функцию шума для анимации. Двигайте, вращайте и масштабируйте изображение.
* Используйте свою функцию шума для анимации. Двигайте, вращайте и масштабируйте изображение.
* Создайте анимированую композицию из нескольких фигур, «танцующих» вместе с помощью шума.
* Создайте анимированую композицию из нескольких фигур, «танцующих» с помощью шума.
* Сконструируйте «органические» формы с помощью шума.
@ -90,7 +90,7 @@ y = mix(rand(i), rand(i + 1.0), u); // её использование в инт
<div class="codeAndCanvas" data="2d-noise.frag"></div>
Мы начинаем с масштабирования пространства в 5 раз (строка 45), чтобы увидеть интерполяцию между клетками решётки. Далее в функции шума мы разделяем пространство на клетки. Мы сохраняем как дробную координату внутри клетки, так и целочисленную координату самой клетки. Целочисленная координата используется для вычисления четырёх координат четырёх углов и получения случайного значения для каждого из них (строки 23-26). Наконец, в строке 35 мы интерполируем между четырьмя случайными значениями в углах, используя ранее сохранённые дробные части координат.
Мы начинаем с масштабирования пространства в 5 раз (строка 45), чтобы увидеть интерполяцию между клетками решётки. Далее в функции шума мы разделяем пространство на клетки. Дробную часть координаты пикселя мы сохраняем для использования в качестве нормализованной координаты внутри клетки, а целую часть - как координату самой клетки. Целочисленная координата используется для вычисления четырёх координат четырёх углов и получения случайного значения для каждого из них (строки 23-26). Наконец, в строке 35 мы интерполируем между четырьмя случайными значениями в углах, используя ранее сохранённые дробные части координат.
Теперь ваш ход. Выполните следующие упражнения:
@ -166,7 +166,7 @@ y = x*x*(3.0-2.0*x);
## Симплексный шум
Перлин не удовлетворился успехом своего алгоритма. Он хотел лучшей производительности. На Siggraph 2001 он представил «симплексный шум», который вносит следующие улучшения относительно предыдущего алгоритма:
Перлин не удовлетворился успехом своего алгоритма. Он хотел более высокой производительности. На Siggraph 2001 он представил «симплексный шум», который вносит следующие улучшения относительно предыдущего алгоритма:
* Меньшая вычислительная сложность и меньше умножений.
* Шум масштабируется в высшие размерности с меньшей потерей производительности.

@ -10,7 +10,7 @@
### Точки в поле расстояний
Клеточный шум основан на поле расстояний до ближайшей из множества опорных точек. Представим, что мы делаем поле расстояний на четырёх точках. Чтор нужно сделать? **Для каждого пикселя нужно посчитать расстояние до ближайшей точки**. Это значит, что нам нужно пройти по всем точкам, рассчитать расстояние до каждой из них и сохранить расстояние до ближайшей.
Клеточный шум основан на поле расстояний до ближайшей из множества опорных точек. Представим, что мы делаем поле расстояний на четырёх точках. Что нужно сделать? **Для каждого пикселя нужно посчитать расстояние до ближайшей точки**. Это значит, что нам нужно пройти по всем точкам, рассчитать расстояние до каждой из них и сохранить расстояние до ближайшей.
```glsl
float min_dist = 100.; // Переменная под расстояние до ближайшей точки.
@ -116,13 +116,13 @@ for (int y= -1; y <= 1; y++) {
Этот код навеян следующим отрывком из [статьи Иниго Квилеса](http://www.iquilezles.org/www/articles/smoothvoronoi/smoothvoronoi.htm):
*«... стоит обратить внимание на красивый трюк в коде выше. Большинство реализаций страдают потерей точности потому что они генерируют случайные точки в «глобальном» пространстве («мировом» или «пространстве объекта»), а значит эти точки могут быть сколь угодно далеко от начала координат. Проблему можно решить, пересадив весь код на более высокоточный тип данных, или просто немного подумав. Моя реализация генерирует точки не в «мировом» пространстве, а в пространстве клетки: как только координата пикселя разделена на целую и дробную части, а значит каждой клетке назначены свои координаты, мы можем размышлять только о происходящем внутри клетки, то есть отбросить целую часть координаты пикселя и сберечь несколько бит точности. Фактически, в наивной реализации диаграмм Вороного целые части координат точек взаимно уничтожаются при вычитании опорной точки из текущей точки. В реализации выше мы предотвращаем взаимное уничтожение, перенося все вычисления в пространство клетки. Этот подход позволяет текстурировать диаграммой Вороного хоть целую планету, если увеличить точность входных координат до двойной, вычислить floor() и fract(), а затем перейти к одинарной точности, не тратя вычислительные ресурсы на перевод всего алгоритма на двойную точность. Разумеется, аналогичный трюк применим и к шумам Перлина (но я не видел ни описания ни реализации подобного алгоритма).»*
*«... стоит обратить внимание на красивый трюк в коде выше. Большинство реализаций страдают потерей точности потому что они генерируют случайные точки в «глобальном» пространстве («мировом» или «пространстве объекта»), а значит эти точки могут быть сколь угодно далеко от начала координат. Проблему можно решить, пересадив весь код на более высокоточный тип данных, или просто немного подумав. Моя реализация генерирует точки не в «мировом» пространстве, а в пространстве клетки: как только координата пикселя разделена на целую и дробную части, а значит каждой клетке назначены свои координаты, мы можем размышлять только о происходящем внутри клетки, то есть отбросить целую часть координаты пикселя и сберечь несколько бит точности. Фактически, в наивной реализации диаграмм Вороного, целые части координат точек взаимно уничтожаются при вычитании опорной точки из текущей точки. В реализации выше мы предотвращаем взаимное уничтожение, перенося все вычисления в пространство клетки. Этот подход позволяет текстурировать диаграммой Вороного хоть целую планету, если увеличить точность входных координат до двойной, вычислить floor() и fract(), а затем перейти к одинарной точности, не тратя вычислительные ресурсы на перевод всего алгоритма на двойную точность. Разумеется, аналогичный трюк применим и к шумам Перлина (но я не видел ни описания ни реализации подобного алгоритма).»*
Резюмируя: разбиваем пространство на клетки. Каждый пиксель вычисляет расстояние до точки в своей клетке и в окружающих восьми клетках, сохраняет кратчайшее из них. Получается поле расстояний, выглядящее примерно так:
Резюме: сначала разбиваем пространство на клетки. Каждый пиксель вычисляет расстояние до точки в своей клетке и в окружающих восьми клетках, сохраняет кратчайшее из них. Получается поле расстояний, выглядящее примерно так:
<div class="codeAndCanvas" data="cellnoise-02.frag"></div>
Исследуйте его, выполнив следующее:
Исследуйте этот пример, выполнив следующее:
- Масштабируйте пространство на различную величину.
- Придумайте другие способы анимировать точки.

@ -58,7 +58,7 @@ for (int i = 0; i < octaves; i++) {
* Увеличивайте количество октав, так чтобы цикл совершал 1, 2, 4 8 и 10 итераций. Наблюдайте за происходящим.
* При количестве октав больше четырёх попробуйте изменить значение лакунарности.
* Так же при количестве октав больше четырёх измените усиление (gain) и посмотрите что произойдёт.
* Так же, при количестве октав больше четырёх, измените усиление (gain) и посмотрите что произойдёт.
Обратите внимание, как с каждой новой октавой кривая становится более детализированной. Так же обратите внимание на самоподобность, появляющуюся с увеличением количества октав. При увеличении кривой боле мелкие части выглядят почти так же, как и вся кривая, а любой участок кривой выглядит похожим на любой другой участок. Это важное свойство математических фракталов, которое мы симулируем с помощью цикла. Ме не создаём *настоящий* фрактал, потому что мы останавливаем симуляцию после нескольких итераций, но, теоретически, мы могли бы получить настоящий фрактал, если бы позволили циклу крутиться бесконечно долго, складывая бесконечно много компонент шума. В компьютерной графике всегда есть предел того, насколько малые объекты мы можем различить. Например, некоторые детали могут стать меньше размера пикселя, поэтому нет никакого смысла обсчитывать бесконечные суммы для создания математически точного фрактала. Иногда может пригодиться очень много слагаемых, но их никогда не понадобится бесконечно много.
@ -108,4 +108,4 @@ for (int i = 0; i < OCTAVES; i++) {
<div class='codeAndCanvas' data='clouds.frag'></div>
Оборачивание текстурных координат шумом - это весело, полезно и возможно не просто в изучении. Это мощный инструмент, требующий некоторого опыта в использовании. Например, можно вмещать координаты на величину производной (градиента) шума. На этой идее основана [знаменитая статья Кена Перлина и Фабриса Нере под названием «Шум потока»](http://evasion.imag.fr/Publications/2001/PN01/). Некоторые современные реализации шума Перлина основаны на вычислении функции и её аналитического градиента. Иногда «настоящий» градиент для процедурной функции вычислить невозможно, но всегда можно вычислить его приближение конечной разностью, хотя результат будет менее точным и потребует больше вычислений.
Оборачивание текстурных координат шумом - это весело, полезно и возможно не просто в изучении. Это мощный инструмент, требующий некоторого опыта в использовании. Например, можно смещать координаты на величину производной (градиента) шума. На этой идее основана [знаменитая статья Кена Перлина и Фабриса Нере под названием «Шум потока»](http://evasion.imag.fr/Publications/2001/PN01/). Некоторые современные реализации шума Перлина основаны на вычислении функции и её аналитического градиента. Иногда «настоящий» градиент для процедурной функции вычислить невозможно, но всегда можно вычислить его приближение конечной разностью, хотя результат будет менее точным и потребует больше вычислений.

Loading…
Cancel
Save