Imagem de desenho com mistura aditiva
a pergunta foi respondida. Para mais informações, confira O EDIT # 4 no final deste texto.
Estamos a trabalhar num motor de gamemaking que está a correr muito bem. Estou a trabalhar no Criador da animação e estava a pensar se seria possível desenhar uma imagem com mistura aditiva. Deixa-me explicar. Usamos o sistema.Biblioteca de desenho de C# e nós trabalhamos com formulários do Windows. Por agora, o Usuário é capaz de criar sua animação, importando Um framed imagem de animação (uma imagem contendo cada moldura da animação) e o Usuário é capaz de arrastar e largar essas molduras onde quiser. O verdadeiro problema é que não conseguimos descobrir como desenhar um quadro com a mistura aditivo. Eis uma amostra do que é a mistura aditiva, se não a compreenderes. Não te censuro, tenho dificuldade em escrever em inglês.estamos a usar o seguinte método para desenhar num painel ou directamente no formulário. Por exemplo aqui está o código para desenhar um mapa em mosaico para o editor de mapas. Uma vez que o código AnimationManager é uma confusão, será mais claro com este exemplo.
using (Graphics g = Graphics.FromImage(MapBuffer as Image))
using (Brush brush = new SolidBrush(Color.White))
using (Pen pen = new Pen(Color.FromArgb(255, 0, 0, 0), 1))
{
g.FillRectangle(brush, new Rectangle(new Point(0, 0), new Size(CurrentMap.MapSize.Width * TileSize, CurrentMap.MapSize.Height * TileSize)));
Tile tile = CurrentMap.Tiles[l, x, y];
if (tile.Background != null) g.DrawImage(tile.Background, new Point(tile.X * TileSize, tile.Y * TileSize));
g.DrawRectangle(pen, x * TileSize, y * TileSize, TileSize, TileSize);
}
Há uma maneira possível de desenhar uma imagem com um desenho aditivo e, se assim for, ficaria eternamente grato se alguém me pudesse indicar como. Obrigado.
Editar #1 :
para desenhar imagens, estamos a usar uma matriz de cores para definir matiz e alph (opacidade) como esta:
ColorMatrix matrix = new ColorMatrix
(
new Single[][]
{
new Single[] {r, 0, 0, 0, 0},
new Single[] {0, g, 0, 0, 0},
new Single[] {0, 0, b, 0, 0},
new Single[] {0, 0, 0, a, 0},
new Single[] {0, 0, 0, 0, 1}
}
);
talvez a matriz de cores possa ser usada para mistura aditiva?
Editar #2 :
Encontrei este artigo de Mahesh Chand.Depois de mais navegação, pode não ser possível com uma matriz de cores, mesmo que possa realizar muito em relação às transformações de cores. Responderei à minha pergunta se for encontrada uma solução.
Obrigado pela ajuda.editar #3 :
XNA tem muita documentaçãoaqui sobre mistura. Encontrei a fórmula usada para realizar mistura aditiva em cada pixels de uma imagem.
PixelColor = (fonte * [1, 1, 1, 1]) + (destino * [1, 1, 1, 1])
Talvez haja uma maneira de usar esta fórmula no contexto actual? Vou começar uma recompensa de 50 na próxima edição, precisamos mesmo que isto funcione. Obrigado novamente pelo seu tempo.editar #4
Graças ao axon, agora o problema está resolvido. Usando o XNA e o seu Spritebatch, poderá conseguir uma mistura aditiva ao fazê - lo:Primeiro de tudo o que cria é um GraphicsDevice e um SpriteBatch
// In the following example, we want to draw inside a Panel called PN_Canvas.
// If you want to draw directly on the form, simply use "this" if you
// write the following code in your form class
PresentationParameters pp = new PresentationParameters();
// Replace PN_Canvas with the control to be drawn on
pp.BackBufferHeight = PN_Canvas.Height;
pp.BackBufferWidth = PN_Canvas.Width;
pp.DeviceWindowHandle = PN_Canvas.Handle;
pp.IsFullScreen = false;
device = new GraphicsDevice(GraphicsAdapter.DefaultAdapter, GraphicsProfile.Reach, pp);
batch = new SpriteBatch(device);
então, quando for a altura de desenhar no controlo ou no formulário (com o evento OnPaint, por exemplo), poderá usar o seguinte bloco de código
// You should always clear the GraphicsDevice first
device.Clear(Microsoft.Xna.Framework.Color.Black);
// Note the last parameter of Begin method
batch.Begin(SpriteSortMode.BackToFront, BlendState.Additive);
batch.draw( /* Things you want to draw, positions and other infos */ );
batch.End();
// The Present method will draw buffer onto control or form
device.Present();
1 answers
Use 1) XNA (recomendado para velocidade), ou 2) use pixel-operations em C#. Pode haver outros métodos ,mas qualquer um destes trabalhos (estou usando cada um deles para efeitos 3D e aplicações de análise de imagem (respectivamente) que eu manter).
Operações de pixels em C#: Usando 3 bitmaps; bmpA, bmpB, bmpC, onde você deseja armazenar bmpA+bmpB em bmpC.
for (int y = 0; y < bmp.Height; y++)
{
for (int x = 0; x < bmp.Width; x++)
{
Color cA = bmpA.GetPixel(x,y);
Color cB = bmpB.GetPixel(x,y);
Color cC = Color.FromArgb(cA.A, cA.R + cB.R, cA.G + cB.G, cA.B + cB.B);
bmpC.SetPixel(x, y, cC);
}
}
O código acima é muito lento. Uma solução mais rápida em C# poderia usar ponteiros como este:
// Assumes all bitmaps are the same size and same pixel format
BitmapData bmpDataA = bmpA.LockBits(new Rectangle(0, 0, bmpA.Width, bmpA.Height), ImageLockMode.ReadOnly, bmpA.PixelFormat);
BitmapData bmpDataB = bmpB.LockBits(new Rectangle(0, 0, bmpA.Width, bmpA.Height), ImageLockMode.ReadOnly, bmpA.PixelFormat);
BitmapData bmpDataC = bmpC.LockBits(new Rectangle(0, 0, bmpA.Width, bmpA.Height), ImageLockMode.WriteOnly, bmpA.PixelFormat);
void* pBmpA = bmpDataA.Scan0.ToPointer();
void* pBmpB = bmpDataB.Scan0.ToPointer();
void* pBmpC = bmpDataC.Scan0.ToPointer();
int bytesPerPix = bmpDataA.Stride / bmpA.Width;
for (int y = 0; y < bmp.Height; y++)
{
for (int x = 0; x < bmp.Width; x++, pBmpA += bytesPerPix, pBmpB += bytesPerPix, pBmpC += bytesPerPix)
{
*(byte*)(pBmpC) = *(byte*)(pBmpA) + *(byte*)(pBmpB); // R
*(byte*)(pBmpC + 1) = *(byte*)(pBmpA + 1) + *(byte*)(pBmpB + 1); // G
*(byte*)(pBmpC + 2) = *(byte*)(pBmpA + 2) + *(byte*)(pBmpB + 2); // B
}
}
bmpA.UnlockBits(bmpDataA);
bmpB.UnlockBits(bmpDataB);
bmpC.UnlockBits(bmpDataC);
O método acima descrito exige ponteiros e, portanto, deve ser compilado com a Directiva "insegura". Também assume 1 byte para cada um de R,G, E B. mude o código para se adequar ao seu formato de pixel.
O uso do XNA é muito mais rápido (desempenho), uma vez que é o hardware acelerado (pela GPU). Trata-se essencialmente do seguinte:: 1. Crie a geometria necessária para desenhar a imagem (um rectângulo, provavelmente um quad de ecrã completo). 2. Escreva um 'vertex-shader' e um 'pixel-shader'. O vértice-sombreado pode simplesmente passar-através do geometria não modificada. Ou pode aplicar uma projecção ortogonal (dependendo das coordenadas com que deseja trabalhar para o quad). O sombreador de pixels terá as seguintes linhas (HLSL):
float4 ps(vertexOutput IN) : COLOR
{
float3 a = tex2D(ColorSampler,IN.UV).rgb;
float3 b = tex2D(ColorSampler2,IN.UV).rgb;
return float4(a + b,1.0f);
}
Existem diferentes métodos disponíveis para aceder a texturas. O seguinte também irá funcionar (dependendo de como você quer que o código XNA se vincule aos parâmetros shader):
float4 ps(vertexOutput IN) : COLOR
{
float3 a = texA.Sample(samplerState, IN.UV).xyz;
float3 b = texB.Sample(samplerState, IN.UV).xyz;
return float4(a + b,1.0f);
}
Qual dos shaders acima que você usa irá depender se você quer usar o "sampler2D " ou" texture " HLSL interfaces para aceder às texturas.
Deve também ter o cuidado de utilizar um sistema de amostragem adequado para garantir que não é utilizada nenhuma amostragem (por exemplo, interpolação linear) ao procurar valores de cor, a menos que seja algo que queira (neste caso, utilize algo de qualidade superior/ordem superior).
O XNA também tem BlendStates incorporados que pode usar para especificar como as texturas sobrepostas serão combinadas. Ou seja, BlendState.Aditivo (ver publicação original actualizada).