Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

Goldwing Studios

Treue Seele

  • »Goldwing Studios« ist der Autor dieses Themas

Beiträge: 359

Wohnort: Heidelberg

Beruf: Softwareentwickler, Vertriebler

  • Private Nachricht senden

1

10.11.2016, 11:35

Pixel auf Textur zeichnen in XNA

Hallo,

ist es möglich in XNA in der Update-Methode eine beliebige Anzahl an farbigen Pixeln auf eine bereits bestehende Textur zu zeichnen?

Möchte ein Test-Wetter-System basteln und hierzu z.B. "Schnee"/Weiße Pixel auf eine Gras-Textur zu zeichnen. Ist das nur innerhalb der Draw() möglich, oder irgendwie auch über die Update()-Methode?

Will hier keine Kommentare zum Thema "Warum nutzt du noch XNA?" hören, lasst das meine Sorge sein ;)

Danke :)

Wirago

Alter Hase

Beiträge: 1 193

Wohnort: Stockerau

Beruf: CRM Application Manager

  • Private Nachricht senden

2

10.11.2016, 13:47

Jein. Sollte aber nicht in der Update() sondern in der Draw() stattfinden. Kein zeichnen in der Update() !

Einen einzelnen Pixel zu zeichnen ist aber nicht wirklich möglich - zumindest nicht ohne große Umwege. Am einfachsten ist es ein 1x1 Pixel große Textur zu verwenden und die zu zeichnen. Ist der gleiche Effekt.

Edit:
ist zwar für MonoGame, sollte aber in XNA gleich sein:

C#-Quelltext

1
2
whiteRectangle = new Texture2D(GraphicsDevice, 1, 1);
whiteRectangle.SetData(new[] { Color.White });

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Wirago« (10.11.2016, 13:56)


TGGC

1x Rätselkönig

Beiträge: 1 799

Beruf: Software Entwickler

  • Private Nachricht senden

3

10.11.2016, 15:50

Das ist kein sehr effektiver Ansatz. Normalerweise macht man das ueber eine Schneetextur mit Alphamaske. Auf der GPU wird das dann dynamisch zusammengeblendet, das ist quasi kostenlos. Das loest auch automatisch gleich alle Aliasing Probleme mit, da das gleich in der Hardware schon quasi "mitverbaut" ist. Akzeptabel wäre so ein Ansatz eigentlich nur für 2D Grafik die weder gescaled noch interpoliert wird.

Wirago

Alter Hase

Beiträge: 1 193

Wohnort: Stockerau

Beruf: CRM Application Manager

  • Private Nachricht senden

4

10.11.2016, 16:59

Eine Schneetextur ist der bessere Ansatz, da hast du natürlich Recht.

Hier ist- zumindest Anfangs - die Rede von "farbigen Pixeln". In welcher Form auch immer. Mein Beispiel war ein wenig quick and dirty.

Schorsch

Supermoderator

Beiträge: 5 145

Wohnort: Wickede

Beruf: Softwareentwickler

  • Private Nachricht senden

5

10.11.2016, 19:50

Hier findest du die Member von Texture2d. Da gibt es zum Beispiel getData und setData. Damit kannst du die Pixel der Textur selbst manipulieren. Wie aber bereits gesagt wurde ist das keine besonders tolle Idee. Du solltest das auf jeden Fall per Shader lösen. Entweder schreibst du einen Shader der dir die gewünschten Pixel manipuliert oder du nimmst den Ansatz von TGGC.
„Es ist doch so. Zwei und zwei macht irgendwas, und vier und vier macht irgendwas. Leider nicht dasselbe, dann wär's leicht.
Das ist aber auch schon höhere Mathematik.“

Goldwing Studios

Treue Seele

  • »Goldwing Studios« ist der Autor dieses Themas

Beiträge: 359

Wohnort: Heidelberg

Beruf: Softwareentwickler, Vertriebler

  • Private Nachricht senden

6

15.11.2016, 09:12

So, jetzt will ich mich auch noch mal melden.

Ich mache das nun so:

C#-Quelltext

1
2
3
4
5
6
7
8
9
10
11
                        double wi_probability = 0.001;
                        if (rand.NextDouble() < wi_probability)
                        {
                            Color[] data = new Color[texture.Width * texture.Height];
                            texture.GetData(data);


                            data[randomNumber] = Color.White;

                            texture.SetData(data);
                        }


Da seid ihr ja aber der Meinung, dass das keine gute Idee ist, aber warum? Gibt mir einen guten Performance schub, wenn ich nur die bereits vorgefertigten Texturen malen muss.

Durch meinen Ansatz male ich aber nicht auf eine Referenz, sondern auf die Original-Texture und das fuchst mich etwas, weil ich nicht verstehe warum.

Hier mal mein Kompletter Code:

C#-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
namespace StrategicGame
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch generalSpriteBatch;

        Vector4 battlefield = new Vector4();

        int _g;

        Dictionary<int, string> seasons = new Dictionary<int, string>();
        int currentSeasonIndex = 1;
        KeyboardState oldState;
        List<Tile> battlefieldList = new List<Tile>();
        Dictionary<string, Texture2D> textureList;
        Dictionary<string, SpriteFont> fontList;

        Random rand = new Random();

        Vector2 numberPosition = new Vector2(25, 25);

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            textureList = ContentLoader.LoadListContent<Texture2D>(Content, "Sprites");
            fontList = ContentLoader.LoadListContent<SpriteFont>(Content, "Fonts");

            generalSpriteBatch = new SpriteBatch(GraphicsDevice);

            battlefield.X = 0;
            battlefield.Y = 0;
            battlefield.W = 100; //width
            battlefield.Z = 100; //height


            seasons.Add(1, "Spring");
            seasons.Add(2, "Summer");
            seasons.Add(3, "Autumn");
            seasons.Add(4, "Winter");



            Texture2D grass = new Texture2D(GraphicsDevice, 1, 1);
            grass = textureList["grass"];

            Color[] dataPainted = new Color[textureList["grass"].Width * textureList["grass"].Height];
            textureList["grass"].GetData(dataPainted);

            _g = dataPainted.GetHashCode();

            double texHeight = grass.Height, texWidth = grass.Width;

            double numberOfTilesVertical = (double)GraphicsDevice.Viewport.Height / (double)texHeight;
            double numberOfTilesHorizontal = (double)GraphicsDevice.Viewport.Width / (double)texWidth;

            double digitsAfterDotVert = Math.Round(numberOfTilesVertical - Math.Round(numberOfTilesVertical, 0), 3), digitsAfterDotHori = Math.Round(numberOfTilesHorizontal - Math.Round(numberOfTilesHorizontal, 0), 3);

            numberOfTilesVertical = numberOfTilesVertical - digitsAfterDotVert;
            numberOfTilesHorizontal = numberOfTilesHorizontal - digitsAfterDotHori;

            texHeight = (double)GraphicsDevice.Viewport.Height / numberOfTilesVertical;
            texWidth = (double)GraphicsDevice.Viewport.Width / numberOfTilesHorizontal;
            Vector2 proportions = new Vector2(((float)texWidth / (float)grass.Width), ((float)texHeight / (float)grass.Height));

            for (int i = 0; i < numberOfTilesHorizontal; i++)
            {
                for (int j = 0; j < numberOfTilesVertical; j++)
                {
                    float posX = (float)texWidth * i;
                    float posY = (float)texHeight * j;
                    Tile _tile = new Tile();
                    _tile.Position = new Vector2(posX, posY);
                    _tile.Proportions = proportions;
                    _tile.Texture = grass;
                    _tile.Texture.Name = "grass";
                    battlefieldList.Add(_tile);
                }
            }

        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            UpdateInput(gameTime);
            GraphicsDevice.Textures[0] = null;

            for (int i = 0; i < battlefieldList.Count; i++)
            {
                Tile _tile = new Tile(battlefieldList[i]);
                Texture2D texture = _tile.Texture;
                int randomNumber = rand.Next(0, texture.Width * texture.Height);
                switch (seasons[currentSeasonIndex])
                {
                    case "Spring":
                        double sp_probability = 0.000001;
                        if (rand.NextDouble() < sp_probability)
                        {
                            Color[] data = new Color[texture.Width * texture.Height];
                            texture.GetData(data);
                            data[randomNumber] = Color.White;
                            texture.SetData(data);
                        }
                        break;
                    case "Summer":
                        double su_probability = 1;
                        if (rand.NextDouble() < su_probability)
                        {
                            Texture2D originalTexture = new Texture2D(GraphicsDevice, 1, 1);
                            originalTexture = textureList[texture.Name];
                            Color[] dataPainted = new Color[texture.Width * texture.Height];
                            Color[] dataOriginal = new Color[originalTexture.Width * originalTexture.Height];



                            //texture.GetData(dataPainted);
                            originalTexture.GetData(dataOriginal);
                            int sdfgsdf = dataOriginal.GetHashCode();
                            //Color dO = dataOriginal[randomNumber];
                            //dataPainted[randomNumber] = dO;
                            //texture.SetData(dataPainted);
                            texture = originalTexture;
                        }
                        break;
                    case "Autumn":
                        double au_probability = 0.001;
                        if (rand.NextDouble() < au_probability)
                        {
                            Color[] data = new Color[texture.Width * texture.Height];
                            texture.GetData(data);
                            data[randomNumber] = Color.Blue;
                            texture.SetData(data);
                        }
                        break;
                    case "Winter":
                        double wi_probability = 0.001;
                        if (rand.NextDouble() < wi_probability)
                        {
                            Color[] data = new Color[texture.Width * texture.Height];
                            texture.GetData(data);


                            data[randomNumber] = Color.White;

                            texture.SetData(data);
                        }
                        break;

                }
                battlefieldList[i] = _tile;
            }


            // TODO: Add your update logic here

            base.Update(gameTime);
        }

        private void UpdateInput(GameTime gameTime)
        {
            KeyboardState newState = Keyboard.GetState();

            if (oldState != newState && newState.IsKeyDown(Keys.Up))
            {
                currentSeasonIndex += 1;
                if (currentSeasonIndex > seasons.Count)
                {
                    currentSeasonIndex = 1;
                }
            }
            else if (oldState != newState && newState.IsKeyDown(Keys.Down))
            {
                currentSeasonIndex -= 1;
                if (currentSeasonIndex <= 0)
                {
                    currentSeasonIndex = seasons.Count;
                }
            }

            oldState = newState;

        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            generalSpriteBatch.Begin();

            for (int i = 0; i < battlefieldList.Count; i++)
            {
                generalSpriteBatch.Draw(battlefieldList[i].Texture, battlefieldList[i].Position, null, Color.White, 0f, battlefieldList[i].Proportions / 2, battlefieldList[i].Proportions, SpriteEffects.None, battlefieldList[i].drawIndex);
            }

            generalSpriteBatch.DrawString(fontList["SpriteFont1"], currentSeasonIndex.ToString(), numberPosition, Color.White, 0, fontList["SpriteFont1"].MeasureString(currentSeasonIndex.ToString()) / 2, 1.0f, SpriteEffects.None, 1.0f);

            generalSpriteBatch.End();

            base.Draw(gameTime);
        }
    }
}

TrommlBomml

Community-Fossil

Beiträge: 2 117

Wohnort: Berlin

Beruf: Software-Entwickler

  • Private Nachricht senden

7

15.11.2016, 10:04

Da seid ihr ja aber der Meinung, dass das keine gute Idee ist, aber warum?


Weil du bei jedem GetData() die Daten von der Grafikkarte runterlädst und bei SetData() wieder hochlädst. Die Performance merkst du nicht wirklich, aber es ist vergleichsweise teuer, da kannst du uns einfach mal glauben das dem so ist :) Das ist einfach Waste of Resources.

Alternativ kannst du im SpriteBatch einfach eine 1x1 weiße Textur verwenden und jedes mal eine andere Modulationsfarbe angeben, kommt auf das selbe Hinaus und ist deutlich besser.

C#-Quelltext

1
2
//Siehe Argument : Orange Flocken!
generalSpriteBatch.Draw(battlefieldList[i].Texture, battlefieldList[i].Position, null, Color.Orange, 0f, battlefieldList[i].Proportions / 2, battlefieldList[i].Proportions, SpriteEffects.None, battlefieldList[i].drawIndex);

Goldwing Studios

Treue Seele

  • »Goldwing Studios« ist der Autor dieses Themas

Beiträge: 359

Wohnort: Heidelberg

Beruf: Softwareentwickler, Vertriebler

  • Private Nachricht senden

8

15.11.2016, 11:05

Einfach so glauben ist ja schön und gut, wenn es funktioniert.

Aber Schneetextur? Ist das nicht das selbe, was ich versuche? Eine Textur mit Pixeln bemalen und dann aufs Window malen?

Das Nutzen von Assets für Schnee oder Wasser oder was weiß ich, ist nich so geil, ich will nicht immer neue Grafiken machen müssen. Klar es gibt die Pipeline, aber ich will es trotzdem nicht machen.

Und das hier war nicht dein Ernst oder?

Zitat

generalSpriteBatch.Draw(battlefieldList.Texture, battlefieldList[i].Position, null, Color.Orange, 0f, battlefieldList[i].Proportions / 2, battlefieldList[i].Proportions, SpriteEffects.None, battlefieldList[i].drawIndex);


Und mit der 1x1-Textur müsste ich ja dann Update-Logik in die Draw-Methode verlagern, weil ich ja dann schauen muss ob es Sommer, Herbst, ... ist. Oder verstehe ich euch da falsch?

TrommlBomml

Community-Fossil

Beiträge: 2 117

Wohnort: Berlin

Beruf: Software-Entwickler

  • Private Nachricht senden

9

15.11.2016, 16:00

Einfach so glauben ist ja schön und gut, wenn es funktioniert.


Das hat was mit Erfahrung auf dem Gebiet Grafikprogrammierung zu tun, man kann es so machen, ist aber nicht üblich und nicht schön. Mit der Aussage "funktioniert doch" sind schon etliche Schrottprogramme entstanden, die keiner Warten konnte. Man kann auch einfach die Kabel quer durch den Raum legen an statt ordentlich in der Wand zu verputzen, funktioniert doch auch? :) Hoffe du weißt, worauf ich hinaus will.

Na ich hab dir ja nicht alles vorgegeben, bisschen selber Denken hatte ich vorausgesetzt: An Stelle einer Liste von Texturen nimm doch eine einzelne Textur und eine Liste von Color-Instanzen, die du im Update pflegst. Die Lösung hat folgende Vorteile:

1) Alles wird in einem DrawCall durch Spritebatch abgesetzt (weil Texture Wiederverwendet, super!)
2) Du sparst enorm Grafikspeicher, weil du nur eine Textur für alles und nicht pro Flocke eine Textur hast.

C#-Quelltext

1
2
3
4
for (int i = 0; i < battlefieldList.Count; i++)
{
    generalSpriteBatch.Draw(mainTexture, battlefieldList[i].Position, null, battlefieldList[i].Color, 0f, battlefieldList[i].Proportions / 2, battlefieldList[i].Proportions, SpriteEffects.None, battlefieldList[i].drawIndex);
}


Wenn du mehrere Texturen willst, würde man am effizientesten einen Texturatlas benutzen und pro Eintrag zusätzlich in deinem Battlefieldlist ein SourceRectangle anlegen. Dann ist es super performant.

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »TrommlBomml« (15.11.2016, 16:12)


Goldwing Studios

Treue Seele

  • »Goldwing Studios« ist der Autor dieses Themas

Beiträge: 359

Wohnort: Heidelberg

Beruf: Softwareentwickler, Vertriebler

  • Private Nachricht senden

10

15.11.2016, 16:49

Hi TrommlBomml,

ich hatte deshalb was zu deinem Verfahren gesagt, weil ich die Tiles (welche ich hier zeichne) nicht mit einer andere Farbe bemalen will. Ich möchte die Tiles Stück für Stück mit farbigen Pixeln bemalen. Und bei deinem Ansatz werden die Tiles einfach in einer bestimmten anderen Farbe ausgegeben.

Werbeanzeige