Программирование шейдеров, том 11

Снежный шейдер для шоколадного кролика

Хорошо, мы знаем немного о том, как манипулировать вершинами. Давайте воспользуемся тем, что мы узнали, для создания шейдера, имитирующего, как объект мог бы выглядеть, если бы на него пошел снег!

Лично я люблю кроликов. Так что давайте наложим на один из них кучу снега.

Я обнаружил, что шейдеру книги не хватает функциональности, поэтому мы также рассмотрим, какие улучшения можно в него внести.

Глава 5.4

Реализация снежного шейдера

Эффект состоит из двух основных этапов: окраска всех треугольников, обращенных вверх, в белый цвет, чтобы они выглядели как снег, а затем выдавливание их вершин вверх для имитации эффекта накопления снега.

Создайте новый шейдер, прикрепите его к новому материалу и примените к объекту по вашему выбору. (Примечание: я бы порекомендовал использовать модель кролика, которую использовал, потому что заметил, что некоторые модели, такие как модель Солдата, которую мы использовали ранее, похоже, используют другой масштаб.)

Вот код шейдера из книги.

Это хорошая отправная точка. Во-первых, объяснение менее очевидных переменных:

  • _SnowDirection: вектор, вдоль которого будет накапливаться снег, обычно (0, 1, 0, 0) в направлении неба.
  • _SnowLevel: определяет, насколько нормаль треугольника или его вершины должны быть выровнены с _SnowDirection, чтобы на них повлиял шейдер снега.

Давайте сначала взглянем на функцию surf (). По сути, все, что он делает, это захватывает нормаль каждого треугольника и определяет, достаточно ли он выровнен с _SnowDirection, как определено переменной _SnowLevel. Если это так, он окрашивает его в белый цвет.

На самом деле функция vert () работает по тем же принципам. В этом случае мы должны сначала преобразовать наш _SnowDirection в мировое пространство. (Примечание: я не согласен с этим, я скоро объясню почему.) Тогда нужно просто выдавить вершину в направлении полувектора _SnowDirection и нормали к вершине. Степень выдавливания определяется переменной _SnowDepth.

А теперь поговорим о том, почему этот шейдер не очень хорош.

Первое, что я заметил в эффектах этого шейдера, это то, что выдавливаются не снежные вершины. Кажется, что толстеет сам кролик, а не просто снег. Взгляните, например, на нос. Или глаз. Эти функции не должны расти.

Вы также заметите, что снега слишком много накапливается наружу. Снег на самом деле так не накапливается - он бы отваливался.

Вот пример того, как в целом выглядит накопление снега:

Обратите внимание, как снег загибается внутрь, когда становится выше. Это то, что я лично хочу видеть в шейдере снега. Или, по крайней мере, у меня должно быть достаточно внешнего контроля над шейдером, чтобы я мог добиться этого вида.

Еще одна проблема, которую я обнаружил, заключается в том, что переменная _SnowLevel влияет на глубину снега, тогда как на самом деле она должна влиять только на покрытие. Это также приводит к отрицательным значениям _SnowLevel, выталкивающим объект внутрь. И это выглядит устрашающе.

Неа. Ничего подобного.

Нет-нет-нет.

Кроме того, не имеет большого смысла, что вектор _SnowDirection не нормализован. И очень обидно, что этот шейдер не работает, когда я вращаю кролика. Хм.

В любом случае, давайте немного поправим.

Вот что я сделал для решения этих проблем:

Прежде всего, вы заметите, что теперь есть переменная _SnowPuffiness. Он определяет, как нормаль вершины влияет на направление, в котором накапливается снег. Это дает нам контроль над тем, может ли снег расширяться наружу, внутрь или просто вверх. Это вычисляется в строке 55.

Преобразование _SnowDirection в пространство объекта в строке 47 помогает улучшить работу шейдера для различных вращений объекта.

В строке 58 я использую вычислить переменную snowFactor, которая принимает величину, на которую _SnowDirection выравнивается с нормалью вершины (от 0 до 1), и вычисляет третья степень, затем использует это значение, чтобы определить, насколько мы должны выдавить вершину, максимум _SnowDepth. Это создает своего рода смягчение выдавливания снега, а не резкую границу между областями, где вершины вытянуты, а вершины - нет.

Помимо этого, я заставил функции surf () и vert () больше согласовывать, следует ли выдавливать вершину или нет, и процесс стал более читабельным.

Давайте взглянем.

Первая переменная, с которой я работаю, - это _SnowLevel. Обратите внимание, как это влияет не на глубину снега, а только на покрытие.

Вторая переменная, с которой я возился, - это _SnowDepth. Он не выдавливает участки, не покрытые снегом - морда нашего кролика остается нетронутой.

Наконец, я изменяю новую переменную _SnowPuffiness, которая позволяет мне изменять способ накопления снега на кролике.

Я думаю, что эти изменения делают шейдер более полезным в других контекстах. Это более обобщенное решение. Мне было очень весело исправлять рекомендованный в книге код шейдера, и я узнал намного больше таким образом, так что миссия выполнена, книга!

Дайте мне знать в комментариях, если придумаете больше улучшений! Я хотел бы увидеть еще лучший шейдер снега. :)

Следующее: Вершинные и фрагментные шейдеры!