Die richtige Antwort darauf scheint darin zu bestehen, die ContentPipeline zu überspringen und Texture2D.FromStream zu verwenden, um die Texturen zur Laufzeit zu laden. Diese Methode funktioniert gut in einem PC und obwohl es einen kleinen Leistungseinbruch geben wird, kann ich diese optimieren, sobald ich mich dem Veröffentlichungsdatum nähere. Im Moment ist die Fähigkeit, den Inhalt sowohl für den Editor als auch für das Spiel dynamisch zu ändern, genau das, was ich brauche. Sobald der Inhalt eingefroren ist, kann ich dies optimieren, indem ich zur ContentPipeline zurückkehre.
Da Sie diese Route gewählt haben, muss ich Sie warnen, dass es Texture2D.FromStream
aus zwei Gründen nicht so einfach ist, sie nur zu verwenden :
Problem Nr. 1 - Mangel an vormultiplizierter Alpha-Unterstützung
XNA4 verarbeitet jetzt standardmäßig Texturen mit Farben im vormultiplizierten Alpha-Format. Wenn Sie eine Textur über die Inhaltspipeline laden, erfolgt diese Verarbeitung automatisch für Sie. Leider Texture2D.FromStream
funktioniert nicht dasselbe, sodass Texturen, die ein gewisses Maß an Transparenz erfordern, geladen und falsch gerendert werden. Unten sehen Sie einen Screenshot, um das Problem zu veranschaulichen:
Um die richtigen Ergebnisse zu erhalten, müssen Sie die Verarbeitung selbst durchführen. Die Methode, die ich zeigen werde, verwendet die GPU, um die Verarbeitung durchzuführen, so dass es ziemlich schnell ist. Es basiert auf diesem großartigen Artikel . Natürlich können Sie auch anweisen SpriteBatch
, im alten NonPremultiplyAlpha-Modus zu rendern, aber ich empfehle dies nicht wirklich.
Problem Nr. 2 - Nicht unterstützte Formate
Die Inhaltspipeline unterstützt mehr Formate als Texture2D.FromStream
. Insbesondere Texture2D.FromStream
unterstützt nur png, jpg und gif. Andererseits unterstützt die Inhaltspipeline bmp, dds, dib, hdr, jpg, pfm, png, ppm und tga. Wenn Sie versuchen, ein usuportiertes Format zu laden, erhalten Texture2D.FromStream
Sie ein InvalidOperationException
mit wenig zusätzlichen Informationen.
Ich brauchte wirklich BMP-Unterstützung für meine Engine, also fand ich für diesen speziellen Fall eine Problemumgehung, die in Ordnung zu sein scheint. Ich kenne jedoch keines der anderen Formate. Der Haken bei meiner Methode ist, dass Sie System.Drawing
Ihrem Projekt einen Verweis auf die Assembly hinzufügen müssen , da GDIs verwendet werden, Image.FromStream
die mehr Formate als unterstützen Texture2D.FromStream
.
Wenn Sie sich nicht für die Unterstützung von bmp interessieren, können Sie diesen Teil meiner Lösung einfach löschen und einfach die vormultiplizierte Alpha-Verarbeitung durchführen.
Lösung - Einfache Version (langsamer)
Hier ist zunächst die einfachste Lösung, wenn Sie sich nicht für die Unterstützung von BMPS interessieren. In diesem Beispiel wird die Verarbeitungsstufe vollständig auf der CPU ausgeführt. Es ist etwas langsamer als die Alternative, die ich unten zeigen werde (ich habe beide Lösungen verglichen), aber leichter zu verstehen:
public static Texture2D FromStream(GraphicsDevice graphicsDevice, Stream stream)
{
Texture2D texture = Texture2D.FromStream(graphicsDevice, stream);
Color[] data = new Color[texture.Width * texture.Height];
texture.GetData(data);
for (int i = 0; i != data.Length; ++i)
data[i] = Color.FromNonPremultiplied(data[i].ToVector4());
texture.SetData(data);
return texture;
}
Wenn Sie sich für BMPS interessieren, müssen Sie das Image zuerst mit GDI laden und dann intern in PNG konvertieren, bevor Sie es an übergeben Texture2D.FromStream
. Hier ist der Code, der das macht:
// Load image using GDI because Texture2D.FromStream doesn't support BMP
using (Image image = Image.FromStream(stream))
{
// Now create a MemoryStream which will be passed to Texture2D after converting to PNG internally
using (MemoryStream ms = new MemoryStream())
{
image.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
ms.Seek(0, SeekOrigin.Begin);
texture = Texture2D.FromStream(_graphicsDevice, ms);
}
}
Lösung - Komplexe Version (schneller)
Schließlich verwende ich bei meinen Projekten die GPU, um stattdessen die Verarbeitung durchzuführen. Bei dieser Methode müssen Sie ein Renderziel erstellen, einige Mischzustände ordnungsgemäß einrichten und das Bild zweimal mit einem SpriteBatch zeichnen. Am Ende gehe ich das gesamte RenderTarget2D durch und klone den Inhalt in ein separates Texture2D-Objekt, da das RenderTarget2D flüchtig ist und Dinge wie das Ändern der Backbuffer-Größe nicht überlebt, sodass es sicherer ist, eine Kopie zu erstellen.
Das Lustige ist, dass dieser Ansatz trotz alledem bei meinen Tests etwa dreimal schneller war als der CPU-Ansatz. Es ist also definitiv schneller, als jedes Pixel zu durchlaufen und die Farbe selbst zu berechnen. Der Code ist etwas lang, also habe ich ihn in einen Pastebin gelegt:
http://pastie.org/3651642
Fügen Sie diese Klasse einfach Ihrem Projekt hinzu und verwenden Sie sie so einfach wie:
TextureLoader textureLoader = new TextureLoader(GraphicsDevice);
Texture2D texture = textureLoader.FromFile("Content/texture.png");
Hinweis: Sie müssen nur eine TextureLoader
Instanz für das gesamte Spiel erstellen . Ich verwende auch den BMP-Fix, aber Sie können ihn entfernen, wenn Sie keine Leistung benötigen, oder den needsBmp
Parameter einfach als false belassen.