Як саме працює SpriteBatch XNA?


38

Якщо бути точнішим, якщо мені потрібно було б відтворити цю функціональність з нуля в іншому API (наприклад, у OpenGL), що б воно було здатне робити?

У мене є загальне уявлення про деякі етапи, наприклад, як він готує матрицю ортографічного проектування та створює квадратик для кожного дзвінка.

Я не надто знайомий із самим процесом дозування. Чи всі квадратики зберігаються в одному вершинному буфері? Чи потрібен буфер індексів? Як обробляються різні текстури?

Якщо можливо, я буду вдячний, якщо ви могли б провести мене через процес, коли SpriteBatch.Begin () викликається до SpriteBatch.End (), принаймні при використанні режиму відкладеного за замовчуванням.


Подивіться , як це реалізовано в MonoGame через OpenGL: github.com/mono/MonoGame/blob/master/MonoGame.Framework / ...
Ден

Чи намагалися ви використовувати DotPeek або Reflector на Microsoft.Xna.Graphics (я не пропоную нічого незаконного робити :)?
День

Дякуємо за посилання І ця думка перехрестила мене (у мене навіть під рукою є dotPeek), але я подумав, що може бути кращою ідеєю попросити загальний опис у того, хто, можливо, вже робив це раніше і знає процедуру на згадку. Таким чином відповідь буде збережена тут і доступна для інших людей. У випадку, якщо ніхто не надасть такого опису, я зроблю аналіз сам і опублікую відповідь на власне запитання, але поки зачекаю трохи більше.
Девід Гувейя

Так, саме тому я використав коментар. Я думаю, що Ендрю Рассел може бути хорошою людиною, щоб відповісти на це питання. Він активний у цій спільноті та працює над ExEn, яка є альтернативою MonoGame: andrewrussell.net/exen .
День

Так, це питання, на яке Ендрю Рассел (або Шон Харгрівз на форумі XNA) зазвичай міг би дати багато розуміння.
Девід Гувейя

Відповіді:


42

Я начебто повторив поведінку SpriteBatch у відкладеному режимі для міжплатформового двигуна, над яким я працюю, тому ось кроки, які я розробив реверс досі:

  1. SpriteBatch конструктор: створює DynamicIndexBuffer, DynamicVertexBufferі масив VertexPositionColorTextureфіксованого розміру (в даному випадку, максимальний розмір пакету - 2048 для спрайтів і 8192 для вершин).

    • Буфер індексів заповнюється вершинними індексами квадратиків, які будуть намальовані (0-1-2, 0-2-3, 4-5-6, 4-6-7 тощо).
    • Створюється також внутрішній масив SpriteInfoструктур. Це збереже параметри тимчасового спрайту, які будуть використовуватися під час пакетної обробки.
  2. SpriteBatch.Begin: внутрішньо зберігає значення BlendState, SamplerStateі т.д. , зазначені і перевірки , якщо вона була викликана двічі без SpriteBatch.Endміж ними.

  3. SpriteBatch.Draw: приймає всю інформацію про спрайт (текстуру, положення, колір) і копіює її в a SpriteInfo. Якщо буде досягнуто максимального розміру партії, вся партія витягується, щоб звільнити місце для нових спрайтів.

    • SpriteBatch.DrawStringпросто видає а Drawдля кожного символу рядка з урахуванням кернінгу та інтервалу.
  4. SpriteBatch.End: виконує такі операції:

    • Встановлює стани візуалізації, зазначені в Begin.
    • Створює матрицю ортографічної проекції.
    • Застосовує SpriteBatchшейдер.
    • Пов'язує DynamicVertexBufferі DynamicIndexBuffer.
    • Виконує наступну операцію дозування:

      startingOffset = 0;
      currentTexture, oldTexture = null;
      
      // Iterate through all sprites
      foreach SpriteInfo in SpriteBuffer
      {
          // Store sprite index and texture
          spriteIndex = SpriteBuffer.IndexOf(SpriteInfo);
          currentTexture = SpriteInfo.Texture;
      
          // Issue draw call if batch count > 0 and there is a texture change
          if (currentTexture != oldTexture)
          {
              if (spriteIndex > startingOffset)
              {
                  RenderBatch(currentTexture, SpriteBuffer, startingOffset,
                              spriteIndex - startingOffset);
              }
              startingOffset = spriteIndex;
              oldTexture = currentTexture;
          }
      }
      
      // Draw remaining batch and clear the sprite data
      RenderBatch(currentTexture, SpriteBuffer, startingOffset,
                  SpriteBuffer.Count - startingOffset);
      SpriteBuffer.Clear();
  5. SpriteBatch.RenderBatch: виконує наступні операції для кожної з SpriteInfoпартії:

    • Приймає положення спрайта і обчислює остаточне положення чотирьох вершин відповідно до походження та розміру. Застосовує існуючі обертання.
    • Обчислює координати УФ та застосовується SpriteEffectsдо них.
    • Копіює спрайт-колір.
    • Ці значення потім зберігаються у масиві VertexPositionColorTextureстворених раніше елементів. Коли всі спрайти були обчислені, SetDataвикликається на DynamicVertexBufferі DrawIndexedPrimitivesвиклик.
    • Вершина шейдера виконує лише транформаційну операцію, а піксельний шейдер застосовує відтінок на колір, отриманий з текстури.

Це саме те, що мені було потрібно, велике спасибі! Змусив мене трохи засвоїти все це, але, думаю, я нарешті отримав усі деталі. Те, що привернуло мою увагу, і про яке я раніше не був усвідомлений, - це те, що навіть у відкладеному режимі я зможу якось сортувати свої дзвінки SpriteBatch.Draw за допомогою Texture (тобто якщо я не переймаюся порядком цих дзвінків), це буде зменшити кількість операцій RenderBatch та, ймовірно, пришвидшити візуалізацію.
Девід Гувейя

До речі, я не можу знайти в документації - яку межу викликів Draw ви можете здійснити, перш ніж буфер заповниться? І чи викликає DrawText заповнює цей буфер на всю кількість символів у рядку?
Девід Гувейя

Вражає! Як ваш двигун буде порівнювати з ExEn та MonoGame? Чи буде потрібно також MonoTouch та MonoAndroid? Спасибі.
День

@DavidGouveia: 2048 спрайтів - це максимальний розмір партії - відредагував відповідь та додав її для зручності. Так, для сортування за текстурою та дозволу z-буфера піклуватися про упорядкування спрайтів використовували б мінімальні дзвінки дзвінків. Якщо вам це потрібно, спробуйте виконати, SpriteSortMode.Textureщоб намалювати в будь-якому порядку, і хочете SpriteBatchзробити сортування.
r2d2rigo

1
@Den: ExEn і MonoGame - API нижчого рівня, які дозволяють запускати XNA-код у платформі, яка не є Windows. Проект, над яким я працюю, - це компонент на базі компонентів, суто написаний на C #, але нам потрібен дуже тонкий шар, щоб забезпечити доступ до системних API (саме там я працюю). Ми просто обрали для повторного виконання SpriteBatchдля зручності. І так, для цього потрібен MonoTouch / MonoDroid, оскільки це все C #!
r2d2rigo

13

Просто хочу зазначити, що код XNA SpriteBatch був перенесений на DirectX та випущений як OSS попереднім членом команди XNA. Тож, якщо ви хочете побачити, як саме це працює в XNA, перейдіть сюди.


+1 за "код, який я ніколи не використовуватиму, але цікаво його перебирати"
Кайл Баран

Останню версію XNA SpriteBatch як рідного C ++ можна знайти на GitHub h / cpp . В якості бонусу також доступна версія DirectX12 h / cpp
Чак Уолборн
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.