C# 添加混合绘制图像

C# 添加混合绘制图像,c#,winforms,system.drawing,color-blending,C#,Winforms,System.drawing,Color Blending,问题得到了回答。有关更多信息,请查看本文末尾的编辑4。 我们目前正在开发一个游戏制作引擎,进展非常顺利。我正在为动画创作者工作,想知道是否有可能用加法混合绘制图像 让我解释一下 我们使用C#的System.Drawing库,并使用Windows窗体。现在,用户可以通过导入带边框的动画图像(包含动画的每一帧的图像)来创建动画,并且用户可以将这些帧拖放到任何他想要的地方 实际的问题是,我们不知道如何使用加法混合绘制帧 下面是一个例子,如果你不太明白添加剂混合是什么。我不会怪你的,我很难用英语写作。

问题得到了回答。有关更多信息,请查看本文末尾的编辑4。

我们目前正在开发一个游戏制作引擎,进展非常顺利。我正在为动画创作者工作,想知道是否有可能用加法混合绘制图像

让我解释一下

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

实际的问题是,我们不知道如何使用加法混合绘制帧

下面是一个例子,如果你不太明白添加剂混合是什么。我不会怪你的,我很难用英语写作。

我们使用以下方法在面板上或直接在窗体上绘制。例如,下面是为地图编辑器绘制平铺地图的代码。由于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);
        }
有没有一种可能的方法可以用加法绘图来绘制图像?如果有,如果有人能告诉我怎么做,我将永远感激。多谢各位

编辑#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}
    }
);
    // 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);
也许颜色矩阵可以用于添加剂混合

编辑#2:

刚找到Mahesh Chand的文章

在进一步浏览之后,可能无法使用颜色矩阵,即使它可以完成许多有关颜色变换的操作。 如果找到解决方案,我将回答我自己的问题

谢谢你的帮助

编辑#3:

XNA有很多关于混合的文档。我找到了用于对图像的每个像素进行加法混合的公式

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

也许有一种方法可以在当前环境下使用这个公式? 我将在下一次编辑时开始50悬赏,我们真的需要这个来工作

再次感谢您抽出时间

编辑#4

多亏了axon,现在问题解决了。使用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)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);
    }
}
有不同的方法可用于访问纹理。以下操作也将起作用(取决于您希望XNA代码绑定到着色器参数的方式):

使用上述哪种着色器将取决于是否要使用“sampler2D”或“texture”HLSL接口来访问纹理

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


XNA还有内置的BlendStates,可用于指定重叠纹理的组合方式。例如BlendState.Additive(请参阅更新后的原始帖子)。

我不确定这在直接升级的winforms中是否可能,而无需操纵像素值本身。是否可以为图形使用WPF承载的构件?WPF能够更轻松地设置混合模式。@Dervall我们搜索得越多,就越觉得使用WPF托管的组件是一个非常好的主意。如果不直接在Winforms中使用逐像素算法,可能没有解决方案。或者有人会创造奇迹?可能是@HansPassant的复制品谢谢你提供的信息。这个问题很相似,但不尽相同。您提供的链接是关于更改不透明度的,通过更改颜色矩阵的[3][3]或[4][3]值可以轻松完成。我想做的是混合图像和下面的图像,但是使用一个加法混合,参见图像示例。这不是关于不透明度,而是关于混合。糟糕的标题,经常发生。
cm.Matrix33=1F-mBlend语句”是您遗漏的内容。让我在明天与团队取得联系后立即尝试。这很有道理;需要源图像才能将像素与要绘制的像素混合。即使我们决定不使用XNA,我也可以用您提供的这个非常完善的答案说服他们。好的,所以团队最终决定将XNA与WinForms结合使用。先生,你刚刚挽救了这一天。非常感谢。但我要等14个小时才能给你50分。谢谢,我添加了第四个编辑,显示了如果你想查看它,如何绘制所有的图形。我注意到你找到了内置的BlendState.addition(这比在着色器中进行要容易一点)。希望纹理采样不是问题(如果您的精灵绘制的大小不是其原始大小,那么加法混合也会由于像素的重新采样而改变图像)。
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);
}
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);
}