У меня есть стандарт 800x600 окно в моем проекте XNA. Моя цель состоит в том, чтобы окрасить каждый отдельный пиксель на основе прямоугольного массива, который содержит булевы значения. В настоящее время я использую 1x1 Структура и тяну каждый спрайт в своем массиве.
Я очень плохо знаком с XNA и происхожу из среды GDI, таким образом, я делаю то, что я сделал бы в GDI, но это не масштабируется очень хорошо. Мне сказали в другом вопросе использовать Программу построения теней, но после большого исследования, я все еще не смог узнать, как выполнить эту цель.
Мои циклы приложения через координаты X и Y моей прямоугольной антенной решетки, делает вычисления на основе каждого значения и повторно присваивает/перемещает массив вокруг. В конце я должен обновить свой "Холст" с новыми значениями. Меньший образец моего массива был бы похож:
0,0,0,0,0,0,0
0,0,0,0,0,0,0
0,0,0,0,0,0,0
1,1,1,1,1,1,1
1,1,1,1,1,1,1
Как я могу использовать программу построения теней для окраски каждого пикселя?
Очень упрощенная версия вычислений была бы:
for (int y = _horizon; y >= 0; y--) // _horizon is my ending point
{
for (int x = _width; x >= 0; x--) // _width is obviously my x length.
{
if (grains[x, y] > 0)
{
if (grains[x, y + 1] == 0)
{
grains[x, y + 1] = grains[x, y];
grains[x, y] = 0;
}
}
}
}
.. каждый раз, когда метод обновления называют, вычисления выполняются и в примере вышеупомянутого цикла, обновление может быть похожим:
Начальная буква:
0,0,0,1,0,0,0
0,0,0,0,0,0,0
0,0,0,0,0,0,0
1,1,1,0,1,1,1
1,1,1,1,1,1,1
Во-первых:
0,0,0,0,0,0,0
0,0,0,1,0,0,0
0,0,0,0,0,0,0
1,1,1,0,1,1,1
1,1,1,1,1,1,1
Во-вторых:
0,0,0,0,0,0,0
0,0,0,0,0,0,0
0,0,0,1,0,0,0
1,1,1,0,1,1,1
1,1,1,1,1,1,1
Финал:
0,0,0,0,0,0,0
0,0,0,0,0,0,0
0,0,0,0,0,0,0
1,1,1,1,1,1,1
1,1,1,1,1,1,1
Обновление:
После применения кода Render2DTarget и размещения моих пикселей, я заканчиваю с нежелательной границей на моих пикселях, всегда налево. Как я могу удалить это?
сопроводительный текст http://www.refuctored.com/borders.png
сопроводительный текст http://www.refuctored.com/fallingdirt.png
Часть кода для применения структур:
RenderTarget2D target;
Texture2D texture;
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
texture = Content.Load("grain");
_width = this.Window.ClientBounds.Width - 1;
_height = this.Window.ClientBounds.Height - 1;
target = new RenderTarget2D(this.GraphicsDevice,_width, _height, 1, SurfaceFormat.Color,RenderTargetUsage.PreserveContents);
}
protected override void Draw(GameTime gameTime)
{
this.GraphicsDevice.SetRenderTarget(0, target);
this.GraphicsDevice.SetRenderTarget(0, null);
this.GraphicsDevice.Clear(Color.SkyBlue);
this.spriteBatch.Begin(SpriteBlendMode.None,SpriteSortMode.Deferred,SaveStateMode.None);
SetPixels(texture);
this.spriteBatch.End();
}
private void SetPixels(Texture2D texture)
{
for (int y = _grains.Height -1; y > 0; y--)
{
for (int x = _grains.Width-1; x > 0; x--)
{
if (_grains.GetGrain(x, y) >0)
{
this.spriteBatch.Draw(texture, new Vector2(x,y),null, _grains.GetGrainColor(x, y));
}
}
}
}
Этот метод не использует пиксельные шейдеры, но если вы хотите использовать метод SetData Texture2D вместо вызова SpriteBatch.Draw () для каждый пиксель, вы можете найти это полезным. Я использовал массив uint вместо bool для представления ваших цветов. Если вы можете обойтись 8-битной цветной текстурой, вы можете ускорить это, изменив формат текстуры.
public class Game1 : Microsoft.Xna.Framework.Game
{
// Set width, height
const int WIDTH = 800;
const int HEIGHT = 600;
// Used to randomly fill in initial data, not necessary
Random rand;
// Graphics and spritebatch
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
// Texture you will regenerate each call to update
Texture2D texture;
// Data array you perform calculations on
uint[] data;
// Colors are represented in the texture as 0xAARRGGBB where:
// AA = alpha
// RR = red
// GG = green
// BB = blue
// Set the first color to red
const uint COLOR0 = 0xFFFF0000;
// Set the second color to blue
const uint COLOR1 = 0xFF0000FF;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
// Set width, height
graphics.PreferredBackBufferWidth = WIDTH;
graphics.PreferredBackBufferHeight = HEIGHT;
}
protected override void Initialize()
{
base.Initialize();
// Seed random, initialize array with random picks of the 2 colors
rand = new Random((int)DateTime.Now.Ticks);
data = new uint[WIDTH * HEIGHT];
loadInitialData();
}
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
// Create a new texture
texture = new Texture2D(GraphicsDevice, WIDTH, HEIGHT);
}
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
// Run-time error without this
// Complains you can't modify a texture that has been set on the device
GraphicsDevice.Textures[0] = null;
// Do the calculations
updateData();
// Update the texture for the next time it is drawn to the screen
texture.SetData(data);
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
// Draw the texture once
spriteBatch.Begin();
spriteBatch.Draw(texture, Vector2.Zero, Color.Purple);
spriteBatch.End();
base.Draw(gameTime);
}
private void loadInitialData()
{
// Don't know where the initial data comes from
// Just populate the array with a random selection of the two colors
for (int i = 0; i < WIDTH; i++)
for (int j = 0; j < HEIGHT; j++)
data[i * HEIGHT + j] = rand.Next(2) == 0 ? COLOR0 : COLOR1;
}
private void updateData()
{
// Rough approximation of calculations
for(int y = HEIGHT - 1; y >= 0; y--)
for (int x = WIDTH - 1; x >= 0; x--)
if (data[x * HEIGHT + y] == COLOR1)
if (y + 1 < HEIGHT && data[x * HEIGHT + (y + 1)] == COLOR0)
{
data[x * HEIGHT + (y + 1)] = data[x * HEIGHT + y];
data[x * HEIGHT + y] = COLOR0;
}
}
}
Как насчет этого ...
Создайте две текстуры (800x600)
Инициализируйте одну из них до начальных значений.
Для каждого кадра вы визуализируете одну текстуру в другую, обновляя значения в пиксельном шейдере.
После рендеринга получившейся текстуры на экран вы меняете их местами, чтобы они были готовы к следующему кадру.
Изменить:
Вам понадобятся два экземпляра RenderTarget2D и создайте их с помощью RenderTargetUsage.PreserveContents. Вы можете начать с SurfaceFormat.Color и использовать черный для 0 и белый для 1. (Вы также можете найти 8-битный формат для экономии видеопамяти.)
new RenderTarget2D(_device, 800, 600, 1, SurfaceFormat.Color, RenderTargetUsage.PreserveContents);
Вы назначаете их тегу рендеринга следующим образом:
_device.SetRenderTarget(0, myRenderTarget);
Вы используете RenderTarget2D в качестве текстуры, например:
_device.Textures[0] = myRenderTarget.GetTexture();
Надеюсь, что это поможет ... Я могу извлечь больше из своего движка, так что просто спросите.
То, что вы пытаетесь сделать (рисовать пиксель за пикселем), - это то, что делает DirectX. XNA - это слой, созданный поверх DirectX, поэтому вам не нужно рисовать пиксель за пикселем. Если это действительно то, чем вы хотите заниматься, то вам, вероятно, следует изучать DirectX вместо XNA. Возможно, вам будет намного проще ...
Добавьте рекламный щит к вашей сцене (прямо перед камерой, чтобы он занимал до 800x600 пикселей).
Свяжите двоичный массив как текстуру.
В шейдере фрагмента (/ пикселя) вычисляют цвет фрагмента, используя 2D-положение фрагмента и текстуру.