netlib.narod.ru | < Назад | Оглавление | Далее > |
Изменение рамки экрана, добавление фонового неба и инвертирование или преобразование в черно-белую вашей карты сцены, конечно, прекрасно, но существует гораздо больше возможностей и способы создать несравненно более мощные постэкранные шейдеры. Игра Rocket Commander использует очень сложный шейдер засветки, имитирующий HDR-освещение с использованием альфа-канала и выполняющий несколько проходов, добавляя радиальное размытие движущихся объектов путем смешивания результата с картой сцены из предыдущего кадра, и формируя несколько эффектов засветки.
Шейдер называется PostScreenGlow и находится в папке с исходными кодами игры Rocket Commander (как в версии для DirectX, так и в версии для XNA). На рис. 8.13 показана базовая функциональность; шейдер еще более сложен, потому что он поддерживает несколько шейдерных моделей (1.1 и 2.0) и некоторую оптимизацию, встроенную для улучшения производительности на медленных компьютерах. В игре Rocket Commander вы можете выбирать шейдерную модель или отключать постэкранные шейдеры для очень медленных компьютеров, но без этих крутых эффектов игра теряет половину своей привлекательности.
Рис. 8.13 |
Действительно, все это выглядит слегка сложнее и потребует много работы для достижения правильного результата, но вам доступно множество хороших книг по шейдерам и часто можно быстро достичь чего-нибудь просто взяв откуда-нибудь готовый шейдер и поэкспериментировав с ним.
Эффект от этого шейдера становится очевидным, если вы поместите перед собой рядом два изображения — одно без шейдера, а другое с постэкранным шейдером засветки (рис. 8.14).
Рис. 8.14 |
Вы уже видели простой способ выполнения размытия движущихся объектов с постэкранным шейдером в примере PostScreenGlow. Он может показаться вам непростым, но эффект размытия движущихся объектов — простейшая часть засветки и он может быть слегка расширен путем использования так называемого эффекта попиксельного размытия движущихся объектов (per pixel motion blur). Для попиксельного размытия движущихся объектов вы сохраняете скорость каждого пикселя в сцене с помощью специального шейдера. Вы не визуализируете геометрию полностью со всеми шейдерами, а визуализируете только объекты, которые перемещаются, оставляя остальную область специальной текстуры черной (неиспользуемой).
Затем в постэкранном шейдере размытия движущихся объектов вы берете каждый пиксель и определяете, насколько он должен быть размыт, основываясь на текущем направлении взгляда игрока и общей скорости, а также на скорости и направлении движения пикселя. Некоторые новые гоночные игры реализуют эту технику и некоторые будущие игровые движки позволят делать такие эффекты, но это непросто реализовать, визуализация в специальную текстуру сильно влияет на производительность, а для работы постэкранного шейдера потребуется достаточно много шейдерных инструкций.
Способ размытия движущихся объектов, реализованный ранее в этой главе, работает только с такими большими значениями размытия движущихся объектов, потому что вы всегда повторно используете предыдущий экран. Это означает, что вы добавляете только 10% нового размытия движущихся объектов и повторно используете 90% размытия движущихся объектов из предыдущего экрана. Попиксельное размытие движущихся объектов также может делать это, но следить будет гораздо труднее, если все находится в движении (вы, множество объектов на сцене и т.д.) и, следовательно, наверное, лучше вычислить размытие движущихся объектов в целом со множеством пикселей, тянущихся за каждым объектом, в шейдере попиксельного размытия движущихся объектов.
Один относительно простой трюк заключается в разделении экранного квадрата, который вы собираетесь визуализировать для постэкранного шейдера, на сетку 10 × 10, и затем визуализировать эти части, подобно большому квадрату, но позволить вершинному шейдеру использовать различные веса, зависящие от того, насколько сильным должно быть размытие движущихся объектов для каждой вершины. Взгляните на рис. 8.15, чтобы сравнить приведенные в качестве примера визуализацию размытия движущихся объектов с общим экранным квадратом и с использованием сетки 5 × 5. При использовании сетки 10 × 10 результат будет выглядеть еще лучше.
Рис. 8.15 |
Я часто применяю цветокоррекцию в моих постэкранных шейдерах, поскольку ее легко использовать и она может значительно влиять на то, как будет выглядеть конечный результат. Она также помогает сделать все текстуры выглядящими более равномерно и более реалистично, потому что цветокоррекция влияет на все пиксели, подобно реальному освещению во внешнем мире. Например, если вы находитесь в пещере, внутри, скорее всего, темно и лишь немного света проникает из внешнего мира. Но если в пещере есть какие-то яркие минералы, они могут выглядеть намного ярче, чем остальная часть пещеры. Использование шейдера цветокоррекции для визуализированной сцены не исправит эту проблему (поскольку вы действительно должны поместить в вашей пещере лучшие объекты, чтобы они совмещались оптимальным образом), но может сделать сцену чуть лучше, немного изменив все цветовые значения. Например, если вы сделаете, чтобы вся сцена выглядела темнее (понизите яркость), и добавите немного синего цвета к каждому пикселю, то все материалы будут лучше совмещаться, поскольку их цветовые значения будут более похожи (рис. 8.16). Сцена на рисунке взята из прототипа игры, который я программировал год назад. Она разрабатывалась вообще без шейдеров цветокоррекции, и однажды я сказал — эй, почему бы не сделать все похожим на кино, где каждый использует много шейдеров цветокоррекции и настройки контрастности. Результат получился очень хорошим и теперь во всех новых играх у меня есть шейдер цветокоррекции.
Рис. 8.16 |
Базовый код выполняет эффект увеличения яркости просто умножая цвет на константу (или на переменную, если вам нужно динамическое изменение). Например, 0.5 делает изображение наполовину темнее, 2.0 делает его в два раза ярче, 0.0 — делает полностью черным и т.д. Приведенная ниже строка кода занимает всего одну шейдерную инструкцию, и вы можете даже комбинировать ее с шейдерами цветокоррекции, что мы сделаем позже в этой главе, и она будет по-прежнему занимать только одну шейдерную инструкцию. На рис. 8.17 показан эффект яркости в действии.
return brightness * originalColorValue;
Рис. 8.17 |
Изменение контрастности немного хитрее. Значение 1.0 должно оставлять все неизменным, 0.0 должно делать изображение полностью серым (контрастность отсутствует), а большие значения должны делать изображение немного более резким. Чтобы достичь этого, вы должны сначала вычесть 0.5 из каждого цветового канала, затем умножить результат на значение контрастности и в конце снова добавить 0.5 к каждому цветовому каналу.
Таким образом, значения меньше 1.0 приведут к частичной потере цветов на изображении, а 0.0 сделает его полностью серым (поскольку в каждом из результирующих каналов будет только добавляемое в конце 0.5). Большие значения сделают изображение более контрастным, затемняя темные значения и увеличивая значения ярких цветов (рис. 8.18).
return (originalColorValue-float3(0.5, 0.5, 0.5)) * contrast + float3(0.5, 0.5, 0.5);
Рис. 8.18 |
Чтобы достичь эффекта, показанного на рис. 8.16 используйте для яркости и контрастности значения очень близкие к 1. Я использовал значение 0.96 для яркости и 1.2 для контрастности. В дополнение вы можете изменить цвет, добавив немного цвета к каждому результирующему пикселю.
Знайте, что зеленый цвет более визуально значим, чем синий или красный, и если вы добавите или вычтете в каждом цветовом канале более 0.1, результат будет выглядеть слишком цветастым; используйте очень маленькие значения между 0.01 и 0.05. Представленный ниже код делает выходное изображение чуть более синеватым и слегка снимает красноту. Это может использоваться для арктических миров. В пустыне вы, вероятно, захотите, чтобы преобладали оранжевые тона, в вулканическом мире можно использовать больше красного цвета и т.д.
inputColor = float3( inputColor.r-0.04f, inputColor.g, inputColor.b+0.05f);
Это только очень простой способ изменения цветов; если вы захотите выполнять более сложные операции или даже оптимизировать отвечающую за цветокоррекцию часть вашего шейдера, можно использовать для каждой операции матрицы, затем заранее вычислять результат комбинации всех этих матриц, и уже тогда однократно модифицировать каждый пиксел. Вот примеры некоторых матриц:
brightnessMatrix = float4x4( brightness, 0, 0, 0, 0, brightness, 0, 0, 0, 0, brightness, 0, 0, 0, 0, 1); contrastMatrix = float4x4( 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.5, -0.5, -0.5, 1); contrastMatrix *= float4x4( contrast, 0, 0, 0, 0, contrast, 0, 0, 0, 0, contrast, 0, 0, 0, 0, 1); contrastMatrix *= float4x4( 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, +0.5, +0.5, +0.5, 1); addColorMatrix = float4x4( 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.04, 0.0, +0.05, 1);
Очевидно, вы можете делать с матрицами гораздо больше вещей, таких как вращение, масштабирование только одной строки и т.д. Вращение может быть полезно для полного изменения цветов и переключения цветовых каналов; неоднородное масштабирование строк может использоваться для достижения обсуждавшегося ранее эффекта подсветки. Не бойтесь экспериментировать.
В последней игре из этой книги вы будете также использовать постэкранный эффект в меню, очень похожий на шейдер PostScreenGlow, который вы видели раньше. Он не делает такую сильную засветку, но эффект засветки все еще остается видимым. Более важно, что в него добавлен фильмоподобный эффект, делающий изображение похожим на старое кино, что выглядит забавно, если смотреть на его анимацию. Возможно, это не вписывается в каждую игру, но весьма круто смотрится в гоночной игре и соответствует музыке в меню.
Основная идея здесь — взять шумовую текстуру, пройти по ней очень медленно и повышать значения так, что только самые яркие точки будут использованы. Затем целый эффект растягивается сверху до низу и поверх него добавляется несколько других эффектов (рис. 8.19).
Рис. 8.19 |
У постэкранных шейдеров есть еще множество возможностей, и иногда вы даже не будете хотеть делать что-нибудь в постэкранном пространстве, а лишь просто запомнить и пометить данные некоторых пикселей в процессе визуализации, а эатем позже использовать их в постэкранном шейдере для показа некоторых эффектов с ними. Мало того, что это может быть быстрее, чем выполнение сложных вычислений для каждого пикселя, но в постэкранном пространстве гораздо проще сделать такие вещи, как смешивание вместе соседних пикселей, перемещение нескольких пикселей и смена цветов. Например, для шейдера наложения теней вы сначала визуализируете сцену в специальном визуализаторе теневой карты, с ракурса источника света, а затем визуализируете сцену снова и используете карту теней для сравнения. Это замечательно работает, но если у вас много различных шейдеров, вам необходимо модифицировать их все и часто шейдеры, особенно для пиксельных шейдеров 1.1, подходят к пределу количества инструкций и вы не можете добавить в них еще больше команд. Тогда вы должны добавить другой проход и визуализировать геометрию еще раз.
Другой подход заключается в визуализации затененной сцены еще раз, но теперь только для карты теней сцены, которая позже будет использоваться в постэкранном шейдере для смешивания нормально визуализированной сцены без каких бы то ни было шейдеров теней с тенями. Благодаря экранному пространству вы можете потом легко выполнить размытие теней или изменить интенсивность теней, не сталкиваясь с особыми проблемами.
Чтобы больше узнать о шейдерах, постэкранных шейдерах, наложении теней и техниках визуализации для современного оборудования, я рекомендую вам прочитать одну из множества хороших профессиональных книг о шейдерах (да, если вы прочитали все это, то стали достаточно подкованным в шейдерах парнем), таких как «Shader X», «GPU Gems», «Game Programming Gems», и множество книг о вершинных и пиксельных шейдерах, которые вы легко найдете на таких сайтах, как Amazon. Я также рекомендую загрузить Nvidia SDK и изучить поставляемые с ним многочисленные примеры шейдеров.
netlib.narod.ru | < Назад | Оглавление | Далее > |