Chính xác thì SpriteBatch của XNA hoạt động như thế nào?


38

Nói chính xác hơn, nếu tôi cần tạo lại chức năng này từ đầu trong một API khác (ví dụ như trong OpenGL) thì nó cần gì để có khả năng thực hiện?

Tôi có một ý tưởng chung về một số bước, chẳng hạn như cách nó chuẩn bị một ma trận chiếu chính tả và tạo ra một hình tứ giác cho mỗi cuộc gọi vẽ.

Tuy nhiên, tôi không quá quen thuộc với quy trình xử lý. Có phải tất cả các quads được lưu trữ trong cùng một bộ đệm đỉnh? Nó có cần một bộ đệm chỉ mục? Làm thế nào là kết cấu khác nhau được xử lý?

Nếu có thể tôi sẽ biết ơn nếu bạn có thể hướng dẫn tôi thực hiện quy trình từ khi SpriteBatch.Begin () được gọi cho đến khi SpriteBatch.End (), ít nhất là khi sử dụng chế độ Trì hoãn mặc định.


Hãy xem cách nó được triển khai trong MonoGame thông qua OpenGL: github.com/mono/MonoGame/blob/master/MonoGame.Framework/ Lỗi
Den

Bạn đã thử sử dụng DotPeek hoặc Reflector trên Microsoft.Xna.Graphics (Tôi không đề nghị làm bất cứ điều gì bất hợp pháp mặc dù :)?
Den

Cảm ơn các liên kết. Và ý nghĩ đó đã vượt qua tôi (tôi thậm chí có sẵn dotPeek ở đây), nhưng tôi nghĩ rằng có thể nên hỏi mô tả chung từ một người có thể đã làm điều đó trước đây và biết thủ tục bằng bộ nhớ. Bằng cách đó, câu trả lời sẽ được bảo tồn ở đây và có sẵn cho những người khác. Trong trường hợp không ai cung cấp một mô tả như vậy, tôi sẽ tự mình phân tích và đăng câu trả lời cho câu hỏi của riêng tôi, nhưng bây giờ tôi sẽ chờ thêm một chút.
David Gouveia

Vâng, đó là lý do tôi sử dụng một nhận xét. Tôi nghĩ Andrew Russell có thể là một người tốt để trả lời câu hỏi này. Anh hoạt động trong cộng đồng này và đang làm việc trên ExEn, một giải pháp thay thế cho MonoGame: andrewrussell.net/exen .
Den

Vâng, đây là loại câu hỏi mà Andrew Russel (hoặc Shawn Hargreaves trở lại tại diễn đàn XNA) thường có thể cung cấp nhiều thông tin chuyên sâu.
David Gouveia

Câu trả lời:


42

Tôi đã sao chép hành vi của SpriteBatch trong chế độ hoãn lại cho một công cụ đa nền tảng mà tôi đang làm việc, vì vậy đây là các bước tôi đã thiết kế ngược cho đến nay:

  1. SpriteBatch constructor: tạo ra một DynamicIndexBuffer, DynamicVertexBuffervà hàng loạt các VertexPositionColorTexturekích thước cố định (trong trường hợp này, hàng loạt kích thước tối đa - 2048 cho mảng đồ họa, 8192 cho đỉnh).

    • Bộ đệm chỉ mục chứa đầy các chỉ số đỉnh của các hình tứ giác sẽ được vẽ (0-1-2, 0-2-3, 4-5-6, 4-6-7, v.v.).
    • Một mảng nội bộ của các SpriteInfocấu trúc cũng được tạo ra. Điều này sẽ lưu trữ các thiết lập sprite tạm thời sẽ được sử dụng khi tạo khối.
  2. SpriteBatch.Begin: lưu trữ nội bộ các giá trị của BlendState, SamplerStatev.v. được chỉ định và kiểm tra xem nó có được gọi hai lần mà không có một SpriteBatch.Endở giữa không.

  3. SpriteBatch.Draw: lấy tất cả thông tin sprite (kết cấu, vị trí, màu sắc) và sao chép nó vào a SpriteInfo. Nếu đạt được kích thước lô tối đa, toàn bộ lô được rút ra để nhường chỗ cho các họa tiết mới.

    • SpriteBatch.DrawStringchỉ phát hành một Drawký tự cho mỗi ký tự của chuỗi, có tính đến độ sáng và khoảng cách.
  4. SpriteBatch.End: thực hiện các thao tác sau:

    • Đặt trạng thái kết xuất được chỉ định trong Begin.
    • Tạo ma trận chiếu chính tả.
    • Áp dụng các SpriteBatchshader.
    • Ràng buộc DynamicVertexBufferDynamicIndexBuffer.
    • Thực hiện thao tác theo đợt sau:

      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: thực hiện các thao tác sau cho từng thao tác SpriteInfotrong lô:

    • Lấy vị trí của sprite và tính toán vị trí cuối cùng của bốn đỉnh theo nguồn gốc và kích thước. Áp dụng luân chuyển hiện có.
    • Tính toán tọa độ UV và áp dụng SpriteEffectscho chúng.
    • Sao chép màu sprite.
    • Các giá trị này sau đó được lưu trữ trong mảng các VertexPositionColorTexturephần tử được tạo trước đó. Khi tất cả các sprite đã được tính toán, SetDatađược gọi vào DynamicVertexBuffervà một DrawIndexedPrimitivescuộc gọi được phát ra.
    • Trình tạo bóng đỉnh chỉ thực hiện thao tác tranform và trình đổ bóng pixel áp dụng pha màu trên màu được lấy từ kết cấu.

Đó chính xác là những gì tôi cần, cảm ơn rất nhiều! Mất một chút để tiếp thu tất cả những điều đó, nhưng tôi nghĩ rằng cuối cùng tôi đã có được tất cả các chi tiết. Một điều khiến tôi chú ý và tôi hoàn toàn không biết trước đó là ngay cả trong chế độ hoãn, nếu tôi có thể sắp xếp các cuộc gọi SpriteBatch của mình bằng cách nào đó (nghĩa là nếu tôi không quan tâm đến thứ tự của các cuộc gọi này) thì nó sẽ giảm số lượng các hoạt động RenderBatch và có thể tăng tốc độ kết xuất.
David Gouveia

Nhân tiện, dường như tôi không thể tìm thấy trong tài liệu - giới hạn của các cuộc gọi Draw bạn có thể thực hiện trước khi bộ đệm đầy? Và việc gọi DrawText có lấp đầy bộ đệm đó bằng toàn bộ số lượng ký tự trong chuỗi không?
David Gouveia

Ấn tượng! Công cụ của bạn sẽ so sánh với ExEn và MonoGame như thế nào? Nó cũng sẽ yêu cầu MonoTouch và MonoAndroid chứ? Cảm ơn.
Den

@DavidGouveia: 2048 spites là kích thước lô tối đa - chỉnh sửa câu trả lời và thêm nó cho thuận tiện. Có, sắp xếp theo kết cấu và để bộ đệm z đảm nhiệm việc sắp xếp sprite sẽ sử dụng các lệnh gọi rút thăm tối thiểu. Nếu bạn cần nó, hãy thử thực hiện SpriteSortMode.Textuređể vẽ theo bất kỳ thứ tự nào bạn muốn và hãy SpriteBatchthực hiện việc sắp xếp.
r2d2rigo

1
@Den: ExEn và MonoGame là các API cấp thấp hơn cho phép mã XNA chạy trong nền tảng không phải Windows. Dự án tôi đang làm là một công cụ dựa trên thành phần được viết hoàn toàn bằng C #, nhưng chúng tôi cần một lớp rất mỏng để cung cấp quyền truy cập vào API hệ thống (đó là nơi tôi đang làm việc). Chúng tôi chỉ chọn để thực hiện lại SpriteBatchcho thuận tiện. Và vâng, nó yêu cầu MonoTouch / MonoDroid vì tất cả đều là C #!
r2d2rigo

13

Chỉ muốn chỉ ra rằng mã XNA SpriteBatch đã được chuyển sang DirectX và được phát hành dưới dạng OSS bởi một thành viên trước đó của nhóm XNA. Vì vậy, nếu bạn muốn xem chính xác làm thế nào nó hoạt động trong XNA ở đây bạn đi.


+1 cho "mã tôi sẽ không bao giờ sử dụng nhưng rất thú vị để lướt qua"
Kyle Baran

Phiên bản mới nhất của XNA SpriteBatch là C ++ bản địa có thể được tìm thấy trên GitHub h / cpp . Như một phần thưởng, phiên bản DirectX12 cũng có sẵn h / cpp
Chuck Walbourn
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.