Спрайты рендеринг размытые со скоростью

9

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

введите описание изображения здесь

Слева - то, что рендерит в моей игре; тот, что справа - это оригинальный спрайт, оклеенный. (Это скриншот из Photoshop, увеличенный в 6 раз.)

Обратите внимание, что края имеют псевдонимы - это выглядит почти как субпиксельный рендеринг. На самом деле, если бы я не заставлял свои спрайты (которые имеют положение и скорость в виде целых чисел) рисовать, используя целочисленные значения, я бы поклялся, что MonoGame рисует со значениями с плавающей запятой. Но это не так.

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

Если быть точным, у моего SpriteComponentкласса есть Vector2 Positionполе. Когда я звоню Draw, я по существу использую new Vector2((int)Math.Round(this.Position.X), (int)Math.Round(this.Position.Y))для позиции.

У меня была ошибка , перед которой даже неподвижные объекты будут дрожать - это было из - за меня , используя прямой Positionвектор , а не округление значения ints. Если я использую Floor/ Ceilingвместо округления, спрайт падает / зависает (разница в один пиксель в любом случае), но все равно рисует размыто.

ashes999
источник
1
Может ли это быть связано с применением фильтрации текстур?
OriginalDaemon
У вас есть код для вашего шейдера? Некоторые люди добавляют половину пикселя по обеим осям, чтобы центрировать пиксели. Не уверен, хотя, я думаю, что это единственное, что DirectX.
Уильям Мариаджер
3
Отключение фильтрации - это обходной путь, а не решение.
Archy
1
Рендеринг ничего не знает о вашей скорости, поэтому позиция, размер или что-то еще, когда вы проходили в SpriteBatch.Draw, отличаются, или перед добавлением скорости была ошибка. Как вы называете SpriteBatch.Draw точно?
Арчи
1
@ ashes999 Вы отладили этот вызов и проверили this.X, this.Y и this.origin? На что установлен this.origin? В принципе, результат рендеринга не отличается, если этот вызов Draw одинаков.
Арчи

Ответы:

3

Ну, это неудобно.

Оказывается, я рисовал не с использованием целочисленных позиций, а с плавающей точкой. Арчи указал мне правильный путь своим комментарием@ashes999 did you debug this call and checked this.X, this.Y and this.origin?

Как только я добавил трассировку, я заметил, что ничего не прослеживается. Оказывается, мой SpriteComponentкласс правильно использовал целочисленные значения, но мои SpriteSheetComponentвсе еще использовали значения с плавающей точкой из необработанных Vector2 Position.

Хотя я не пробовал текстурную фильтрацию и фиксацию, я подозревал, что это неправильные изменения, потому что я рисовал 2D-изображение с той же шириной и высотой, что и исходное изображение (без масштабирования).

ashes999
источник
1
Готово, рад, что вы нашли это!
Арчи
2

XNA использует DirectX9, который использует центр пикселя в качестве своего местоположения. Это заметно при использовании перегрузок на основе Vector2 класса draw. Я считаю, что вычитание (.5, .5) должно решить вашу проблему.

Больше информации здесь.

ClassicThunder
источник
Я не могу технически сделать это, потому что я передаю Vector2 с int, intаргументами. Кроме того, все выглядит отлично, когда у меня нет движущихся объектов.
ashes999
Что вы подразумеваете под объектом в движении? XNA не имеет понятия о движении, она рисует вещи там, где вы говорите это тоже. Это серия стационарных снимков. Также попробуйте использовать перегрузки Rectangle, так как вы все равно округляете до прямоугольника.
ClassicThunder
У меня есть своя реализация скорости. SpriteComponentимеет Vector2позицию, которая увеличивается по мере плавания в зависимости от скорости; когда я рисую, я создаю новую Vector2с целочисленными (округленными) версиями моих positionX и Y. Это не проблема, так как неподвижные объекты без скорости выглядят нормально.
ashes999
Итак, вы говорите, что у вас есть вектор положения p и вектор скорости v, добавьте их, например, p + = v, затем создайте копию p, используя округленные значения. И что это приводит к различным значениям, тогда имеющим позицию, которая совпадает с предыдущим p + = v, несмотря на то, что вызовы отрисовки идентичны? Потому что это не имеет смысла.
ClassicThunder
Да, это то, что @Archy говорит тоже. Позвольте мне отладить и проверить еще раз. Я добавляю p += vво время Update, и рендеринг с new Vector2((int)Math.Round(p.X), (int)Math.Round(p.Y))).
ashes999
2

Рендеринг ничего не знает о вашей скорости, поэтому либо позиция, размер или что-то еще, когда вы проходили через SpriteBatch.Draw, отличаются, либо до того, как вы добавили скорость, возникла ошибка. Как вы называете SpriteBatch.Draw точно?

Вы отладили этот вызов и проверили this.X, this.Y и this.origin? На что установлен this.origin? В принципе, результат рендеринга со скоростью не отличается, если этот вызов Draw одинаков.

Archy
источник
1

Проблема, вероятно, в том, что включена фильтрация текстур. Если вы не уверены, что это значит, рассмотрите ситуацию, когда у вас есть изображение шириной 2 пикселя: первый пиксель черный, второй пиксель белый. Если вы увеличите эту текстуру, если включена фильтрация текстур, вы увидите, что она становится размытой и что область между черным и белым пикселем использует градиентный серый цвет.

Классическим примером игр с фильтрацией и без нее является Super Mario 64, в котором была фильтрация, а в Doom - нет.

Когда вы не используете скорость, ваш Sprite, скорее всего, расположен так, что центр текстуры находится в точке выборки, которую XNA (или любой другой используемый API-интерфейс) захватывает цвет. Когда вы двигаетесь с плавающей скоростью, положение вашего спрайта изменяется, поэтому точка выборки может не совпадать непосредственно с центром пикселя, поэтому в результате получается, что цвет, который является средним из ближайших пикселей, используется для визуализации.

Вы можете проверить, включена ли фильтрация, просто отобразив ваш Sprite очень большим на экране. Если это размыто, то это проблема.

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

Вы также можете прочитать это .

Виктор Челару
источник
Как я уже упоминал в своем вопросе, я уже использую целые числа для своего местоположения и исходного размера изображения, когда я звоню Draw. Так что я не уверен, как это могло быть, но я попробую отрисовать в большом масштабе и посмотреть, поможет ли отключение фильтрации текстур.
ashes999
1

Попробуйте установить SamplerState в SamplerState.PointClamp в вызове SpriteBatch.Begin.

craftworkgames
источник