В настоящее время я пытаюсь замаскировать некоторые спрайты. Вместо того, чтобы объяснить это словами, я составил несколько иллюстраций:
Область для маскировки (белого цвета)
Теперь красный спрайт, который нужно обрезать.
Конечный результат.
Теперь я знаю, что в XNA вы можете сделать две вещи для достижения этой цели:
- Используйте трафаретный буфер.
- Используйте пиксельный шейдер.
Я попытался сделать пиксельный шейдер, который по сути сделал это:
float4 main(float2 texCoord : TEXCOORD0) : COLOR0
{
float4 tex = tex2D(BaseTexture, texCoord);
float4 bitMask = tex2D(MaskTexture, texCoord);
if (bitMask.a > 0)
{
return float4(tex.r, tex.g, tex.b, tex.a);
}
else
{
return float4(0, 0, 0, 0);
}
}
Кажется, что это обрезает изображения (хотя и не правильно, как только изображение начинает двигаться), но моя проблема в том, что изображения постоянно движутся (они не статичны), поэтому обрезка должна быть динамичной.
Есть ли способ, которым я мог бы изменить код шейдера, чтобы учесть его положение?
Кроме того, я читал об использовании буфера трафарета, но большинство примеров, похоже, зависят от цели рендеринга, чего я действительно не хочу делать. (Я уже использую 3 или 4 для остальной части игры, и добавление еще одного сверху кажется излишним)
Единственное учебное пособие, которое я нашел, но не использует Rendertargets, - это одно из блога Шона Харгривза здесь. Проблема с этим, однако, в том, что он для XNA 3.1, и, похоже, не очень хорошо подходит для XNA 4.0.
Мне кажется, что пиксельный шейдер - это путь, но я не уверен, как правильно расположить его. Я считаю, что мне придется изменить мои экранные координаты (что-то вроде 500, 500), чтобы быть между 0 и 1 для координат шейдера. Моя единственная проблема - попытаться понять, как правильно использовать преобразованные координаты.
Заранее благодарю за любую помощь!
Ответы:
Вы можете использовать свой подход пиксельных шейдеров, но он неполон.
Вам также нужно будет добавить в него некоторые параметры, которые сообщат пиксельному шейдеру, где вы хотите разместить трафарет.
В вашем коде C # вы передаете переменные в пиксельный шейдер до
SpriteBatch.Draw
вызова следующим образом:источник
maskCoord
расчете один из X должен был быть Y. Я отредактировал свой первоначальный ответ с исправлением и включил настройки УФ-зажима, которые вам понадобятсяMaskTexture sampler
.Первый шаг - сообщить графической карте, что нам нужен буфер трафарета. Чтобы сделать это при создании GraphicsDeviceManager, мы устанавливаем для PreferredDepthStencilFormat значение DepthFormat.Depth24Stencil8, поэтому на самом деле существует трафарет для записи.
AlphaTestEffect используется для установки системы координат и фильтрации пикселей с альфа-каналом, которые проходят альфа-тестирование. Мы не собираемся устанавливать какие-либо фильтры и устанавливать систему координат для порта просмотра.
Далее нам нужно настроить два DepthStencilStates. Эти состояния определяют, когда SpriteBatch рендерится по трафарету и когда SpriteBatch рендерится в BackBuffer. Нас в первую очередь интересуют две переменные StencilFunction и StencilPass.
Для первого DepthStencilState мы устанавливаем StencilFunction в CompareFunction. Это приводит к успешному выполнению StencilTest, и когда StencilTest SpriteBatch отображает этот пиксель. StencilPass установлен в StencilOperation. Замените значение, означающее, что при успешном выполнении StencilTest этот пиксель будет записан в StencilBuffer со значением ReferenceStencil.
Таким образом, StencilTest всегда проходит, изображение отображается на экране в обычном режиме, а для пикселей, отображаемых на экране, значение 1 сохраняется в StencilBuffer.
Второй DepthStencilState немного сложнее. На этот раз мы хотим рисовать только на экране, когда значение в StencilBuffer равно. Чтобы достичь этого, мы устанавливаем StencilFunction в CompareFunction.LessEqual и ReferenceStencil в 1. Это означает, что когда значение в буфере трафарета равно 1, StencilTest будет успешным. Установка StencilPass в StencilOperation. Keep заставляет StencilBuffer не обновляться. Это позволяет нам рисовать несколько раз, используя одну и ту же маску.
Таким образом, StencilTest проходит только тогда, когда StencilBuffer меньше 1 (альфа-пиксели от маски) и не влияет на StencilBuffer.
Теперь, когда у нас настроены DepthStencilStates. Мы можем рисовать с помощью маски. Просто нарисуйте маску, используя первый DepthStencilState. Это повлияет как на BackBuffer, так и на StencilBuffer. Теперь, когда буфер трафарета имеет значение 0, где вы маскируете прозрачность, и 1, где оно содержит цвет, мы можем использовать StencilBuffer для маскировки последующих изображений.
Второй SpriteBatch использует второй DepthStencilStates. Независимо от того, что вы рисуете, только пиксели, для которых StencilBuffer установлен в 1, пройдут проверку трафарета и будут отображены на экране.
Ниже приведен полный код метода Draw, не забудьте установить PreferredDepthStencilFormat = DepthFormat.Depth24Stencil8 в конструкторе игры.
источник
В ответе выше некоторые странные вещи произошли, когда я сделал именно так, как объяснено.
Единственное, что мне было нужно, это оба
DepthStencilStates
.И ничья без
AlphaTestEffect
.И все было хорошо, как и ожидалось.
источник