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.
FromStream
mit einem Speicherstrom verwendet, der eine 32-Bit-Bitmap enthält (in PNG gespeichert), genau wie in Ihrem anderen Beispiel, aber diese Methode hat keine vormultiplizierte Textur erstellt. Das explizite Vormultiplizieren jeder Farbe hat den Trick getan, danke.Ich denke, die meisten Teams würden die xnb-Änderungen (nun ja, alle Änderungen wirklich, einschließlich xnb) auf ihren SVN-Server übertragen (der kostenlos eingerichtet werden kann) und anderen (dem Künstler usw.) erlauben, ihre eigenen Arbeitskopien zu aktualisieren.
Tatsächlich wäre dies ein guter Mechanismus für den Künstler, um die Originalkunst (vor xnb) zu versionieren. Er würde Änderungen daran festschreiben, Sie würden Ihre Arbeitskopie davon aktualisieren, sie erstellen (was sie zu einem xnb im Prozess macht), Ihre Änderungen festschreiben, er würde seine Arbeitskopie Ihrer Arbeit aktualisieren und jeder hat alle Änderungen. (Sie haben die neuesten Rohgrafiken, er hat die xnb (s).
Das lässt sich auch sehr gut skalieren.
quelle
Ich habe dies weiter untersucht und werde dies zugunsten von jemandem veröffentlichen, der die gleiche Frage hat.
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 eine kleine Leistung seinen Hit dies etwas ist , dass ich optimieren kann , wenn ich näher an den Veröffentlichungstermin bin.
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.
quelle
Texture2D.FromStream
allein reicht das nicht aus. Der Grund dafür ist, dass XNA in Version 4 mit vormultiplizierten Alpha-Texturen arbeitet und die Inhaltspipeline diese Verarbeitung zwar automatisch für Sie übernimmt, diesTexture2D.FromStream
jedoch nicht der Fall ist, wenn Sie Sprites mit Transparenz zeichnen. Ich kann eine funktionierende Lösung veröffentlichen, wenn Sie möchten.Texture2D.FromStream
unterstützt keine Lade.BMP
Dateien während der Inhalt Pipeline tut. Dies ist etwas, das Sie wahrscheinlich abschrecken würde, wenn Sie zuvor.BMP
Assets verwendet und dann zu gewechselt hättenTexture2D.FromStream
. Ich habe auch eine Problemumgehung für diese Einschränkung. Ich werde einfach weitermachen und es posten.Schauen Sie sich dieses Projekt an .
Damit kann Ihr Künstler XNB aus nahezu allem erstellen, was die Standard-XNA-Inhaltspipeline unterstützt. Das weiterverteilbare XNA-Framework ist weiterhin erforderlich, obwohl Ihr Künstler Visual Studio nicht benötigt.
quelle