带有附加混合的绘图图像 [英] Drawing image with additive blending

查看:107
本文介绍了带有附加混合的绘图图像的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题已回答.有关更多信息,请查看本文末尾的EDIT#4.

我们目前正在开发一款运行良好的游戏引擎.我正在研究Animation Creator,并想知道是否可以通过加法混合绘制图像.

We are currently working on a gamemaking engine which is going pretty well. I am working on the Animation Creator and was wondering if it was possible to draw an image with additive blending.

让我解释一下.

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

We use System.Drawing library of C# and we work with Windows Forms. For now, the user is able to create his animation by importing a framed animation image (an image containing every frame of the animation) and the user is able to drag and drop these frames wherever he wants.

实际的问题是,我们无法弄清楚如何使用添加剂混合绘制框架.

The actual problem is that we can't figure out how to draw a frame with the additive blending.

如果您不太了解,这里就是添加剂混合"的一个示例.我不会怪你,我很难用英语写作.

Here's an exemple of what Additive Blending is if you don't quite get it. I won't blame you, I have a hard time writing in english.

我们正在使用以下方法在面板上或直接在表单上绘制.例如,以下是为地图编辑器绘制平铺地图的代码.由于AnimationManager代码是一团糟,因此此示例将使其更加清晰.

We are using the following method to draw on a Panel or directly on the form. For exemple here's the code to draw a tiled map for the map editor. Since the AnimationManager code is a mess, it'll be clearer with this exemple.

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

有没有可能用附加图来绘制图像,如果可以的话,如果有人能指出我的想法,我将不胜感激.谢谢.

Is there a possible way of drawing an image with an additive drawing and if so, I'd be forever grateful if someone could point me out how. Thank you.

编辑#1:

对于绘制图像,我们使用颜色矩阵来设置色相和Alph(不透明度),如下所示:

For drawing images, we are using a color matrix to set hue and alph (opacity) like this:

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

也许颜色矩阵可以用于添加混合?

Maybe the color matrix can be used for additive blending?

编辑#2:

刚刚找到Mahesh Chand的文章.

Just found this article by Mahesh Chand.

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

After further browsing, it may not be possible with a color matrix even though it can accomplish a lot regarding color transformations. I will answer my own question if solution found.

感谢您的帮助.

编辑#3:

XNA在此处有很多文档.我发现了用于在图像的每个像素上完成加法混合的公式.

XNA has a lot of documentation here about blending. I found the formula used to accomplish additive blending on each pixels of an image.

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

PixelColor = (source * [1, 1, 1, 1]) + (destination * [1, 1, 1, 1])

也许有一种在当前上下文中使用此公式的方法? 我将在下一次编辑时获得50英镑的赏金,我们确实需要这项工作.

Maybe there's a way of using this formula in the current context? I will start a 50 bounty on next edit, we really need this to work.

再次感谢您的时间.

编辑#4

感谢轴突,现在问题已解决.使用XNA及其Spritebatch,可以完成添加混合:

Thanks to axon, now the problem is solved. Using XNA and its Spritebatch, you can accomplish Additive blending doing so :

首先创建一个GraphicsDevice和一个SpriteBatch

First of all you create a GraphicsDevice and a 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事件),可以使用以下代码块

Then, when it's time to draw on the control or on the form (with the OnPaint event for example), you can use the following code block

// 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效果和我维护的图像分析应用程序).

Either use 1) XNA (recommended for speed), or 2) use pixel-operations in C#. There may be other methods, but either of these work (I'm using each of them for 3D effects and image analysis apps (respectively) that I maintain).

C#中的像素操作: 使用3个位图; bmpA,bmpB,bmpC,要在其中将bmpA + bmpB存储在bmpC中.

Pixel Operations in C#: Using 3 bitmaps; bmpA, bmpB, bmpC, where you want to store bmpA+bmpB in 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);
    }
}

上面的代码很慢.在C#中,更快的解决方案可以使用如下所示的指针:

The above code is very slow. A faster solution in C# could use pointers like this:

    // 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字节.更改代码以适合您的像素格式.

The above method requires pointers and hence must be compiled with the "unsafe" directive. Also assumes 1-byte for each of R,G, and B. Change the code to suit your pixel format.

使用XNA 的速度(性能)快得多,因为它是硬件加速的(通过GPU).它主要包括以下内容: 1.创建绘制图像所需的几何形状(矩形,最有可能是全屏方形). 2.编写一个顶点着色器和像素着色器.顶点着色器可以简单地通过未修改的几何.或者,您可以应用正交投影(取决于要为四边形使用的坐标).像素着色器将具有以下几行(HLSL):

Using XNA is a lot faster (performance) since it is hardware accelerated (by the GPU). It basically consists of the following: 1. Create the geometry needed to draw the image (a rectangle, most likely a full-screen quad). 2. Write a vertex-shader and pixel-shader. The vertex-shader can simply pass-through the geometry unmodified. Or you can apply an orthogonal projection (depending on what coordinates you want to work with for the quad). The pixel shader will have the following lines (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代码绑定到着色器参数的方式):

There are different methods available for accessing textures. The following will also work (depending on how you want the XNA code to bind to the shader parameters):

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界面来访问纹理.

Which of the above shaders you use will depend on whether you want to use the "sampler2D" or "texture" HLSL interfaces to access the textures.

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

You should also be careful to use an appropriate sampler setting to ensure that no sampling (e.g. linear interpolation) is used when looking up colour values unless that's something you want (in which case use something higher-quality/higher-order).

XNA还具有内置的BlendState,您可以使用它们指定如何组合重叠的纹理. IE. BlendState.Additive(请参阅更新的原始文章).

XNA also has built-in BlendStates you can use to specify how overlapped textures will be combined. I.e. BlendState.Additive (see updated original post).

这篇关于带有附加混合的绘图图像的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆