2012-08-29 124 views
8

问题已回答。有关更多信息,请参阅本文末尾的编辑#4。添加剂混合的绘图图像

我们目前正在研发一款相当不错的游戏制作引擎。我正在研究动画创作者,并想知道是否可以使用添加混合来绘制图像。

让我解释一下。

我们使用C#的System.Drawing库,并使用Windows窗体。目前,用户能够通过导入带框的动画图像(包含动画的每个帧的图像)来创建他的动画,并且用户能够将这些框架拖放到他想要的任何地方。

实际的问题是,我们无法弄清楚如何绘制一个框架与添加剂混合。

下面是添加剂混合的例子,如果你不太明白。我不会责怪你,我很难用英文写作。 Additive blending exemple

我们使用以下方法在Panel上或直接在窗体上绘图。例如,这里是为地图编辑器绘制平铺地图的代码。由于AnimationManager代码乱七八糟,这个例子会更清晰。

 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); 
     } 

有没有用添加剂绘图如果是绘制图像的一个可行的办法,我会永远感激,如果有人可以点我如何。谢谢。

EDIT#1:

对于绘制图像,我们用的是彩色矩阵来设置这样的色调和ALPH(不透明度):

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} 
    } 
); 

也许彩色矩阵可用于添加剂混合?

编辑#2:

刚刚发现this文章马赫什昌。

在进一步浏览之后,即使色彩转换可以完成很多工作,也可能无法使用色彩矩阵。 如果找到解决方案,我会回答我自己的问题。

谢谢你的帮助。

编辑#3:

XNA有很多关于混合文档here的。我找到了用于在图像的每个像素上完成叠加混合的公式。

PixelColor =(源* [1,1,1,1])+(目的地* [1,1,1,1])

也许有在当前上下文中使用此公式的一个方法是什么? 我将在下次编辑时启动50条赏金,我们确实需要这个工作。

再次感谢您的时间。

编辑#4

由于轴突,现在的问题就解决了。使用XNA和Spritebatch,你可以做到添加剂调合这样做:

首先创建的GraphicsDevice和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); 

然后,当它的时间上绘制控件或形式(与OnPaint事件例如),则可以使用下面的代码块

// 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

我不确定这是可能的直接向上的winforms,而不诉诸于自己的像素值操纵。将使用WPF托管组件作为图纸是一个选项吗?WPF有能力更容易地设置混合模式。 – Dervall

+0

@Dervall我们越搜索越多,我们开始认为使用WPF托管组件是一个非常好的主意。也许没有直接使用Winforms使用逐像素算法的解决方案。或者,也许有人会想出一些奇迹? –

+0

[.DrawImage with opacity?]的可能重复?(http://stackoverflow.com/questions/5519956/drawimage-with-opacity) –

回答

7

既可以使用1)XNA(推荐速度),或2)在C#使用像素的操作。可能还有其他方法,但其中任何一种都可以工作(我将它们分别用于我所维护的3D效果和图像分析应用程序)。

C#中的像素操作: 使用3位图; bmpA,bmpB,bmpC,您想要在bmpC中存储bmpA + bmpB的位置。

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); 
    } 
} 

上面的代码非常慢。在C#更快的溶液可以使用指针这样的:

// 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); 

上述方法需要指针,并因此必须与“不安全”的指令进行编译。对于R,G和B中的每一个,也假定1字节。更改代码以适合您的像素格式。

使用XNA由于硬件加速(通过GPU),速度更快(性能)。它基本上由以下几部分组成: 1.创建绘制图像所需的几何图形(矩形,很可能是全屏四边形)。 2.编写顶点着色器和像素着色器。顶点着色器可以简单地通过未修改的几何体。或者你可以应用一个正交投影(取决于你想使用四边形的坐标)。像素着色器将具有以下行(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); 
} 

存在用于访问纹理的不同方法。下面的方式也将工作(这取决于你想要怎样的XNA代码绑定到着色器参数):

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); 
} 

您使用将取决于你是否要使用“sampler2D”或“上述着色器纹理“HLSL接口访问纹理。

您还应该小心使用适当的采样器设置,以确保在查找颜色值时不使用采样(例如线性插值),除非您需要这样做(在这种情况下,使用更高质量/更高阶的物体)。

XNA还具有内置的BlendStates,您可以使用它来指定重叠纹理将如何组合。即BlendState.Additive(请参阅更新的原始帖子)。

+0

明天我就跟团队一起试试吧。这很有意义;您需要源图像才能将像素与要绘制的像素混合。尽管我们决定不使用XNA,但我可以用你提供的这个非常完善的答案来说服他们。 –

+0

好的,所以团队最终决定将XNA与WinForms结合使用。先生,你刚刚救了一天。谢谢!要等14个小时,直到我可以给你50pts。 –

+0

Nooo问题。祝你好运! – axon