执行 Alpha 混合的正确方法是什么? (三)

发布于 2024-09-07 08:26:20 字数 2048 浏览 6 评论 0 原文

我正在编写一个非常简单的图形库,并且我正在尝试找出如何进行 Alpha 混合。我尝试了几次,但结果并不令人满意。根据维基百科,我应该这样做:

Value = (1-alpha)Value0 + alphavalue1

但这根本不起作用。也许我做错了什么?

我包含的代码绘制了一幅彩色图片(即“邻近”函数),然后尝试在 (100,100) 处绘制一个部分透明的框。然而,我得到的不是白色半透明的盒子,而是图像看起来很奇怪的扭曲(我会尝试将它们放在帖子的底部)。有什么建议吗?这是我的代码:

#include "hgl.h"

void proximity()
{
    int x = 0, y = 0, d1, d2, d3, dcenter;

    while(x < WIDTH){
        while(y < HEIGHT){
            d1 = distance(x, y, (WIDTH/2) - 200, (HEIGHT/2) + 200);
            d2 = distance(x, y, (WIDTH/2) + 200, (HEIGHT/2) + 200);
            d3 = distance(x, y, (WIDTH/2), (HEIGHT/2) - 150);
            dcenter = distance(x, y, WIDTH/2, HEIGHT/2);
            putpixel(x, y, d1, d2, d3);
            y++;
        }
        y = 0;
        x++;
    }
}

int alpha_transparency(float alpha, float value1, float value2)
{
    return (1-alpha) * value1 + alpha * value2;
}

void transparent_box(int pos_x, int pos_y, int width, int height, float alpha, char r, char g, char b)
{
    int x = 0, y = 0;
    while(x < width)
    {
        while(y < height)
        {
            int rr, rg, rb;
            rr = alpha_transparency(alpha, p.bitmap[x+pos_x][y+pos_y].r, r);
            rg = alpha_transparency(alpha, p.bitmap[x+pos_x][y+pos_y].g, g);
            rb = alpha_transparency(alpha, p.bitmap[x+pos_x][y+pos_y].b, b);
            putpixel(pos_x + x, pos_y + y, rr, rg, rb);
            y++;
        }
        x++;
        y = 0;
    }
}

int main()
{
    fp = fopen("out.bmp","wb");

    set_dimensions(1440, 900);
    insert_header();

    white_screen();

    proximity();
    transparent_box(100, 100, 500, 500, .9, 255, 255, 255);

    insert_image();
    fclose(fp);
    return 0;
}

抱歉,我无法包含输出,因为我是新用户。不过,以下是链接:

原始图片 原始图片

带有“透明”框的图片

I'm writing a very simple graphics library, and I'm trying to figure out how to do alpha blending. I tried it a few times, but my results were less than satisfactory. According to Wikipedia, I should do:

Value = (1-alpha)Value0 + alphavalue1

This, however is not working at all. Maybe I'm doing something wrong?

The code I've included draws a colorful picture (that's the 'proximity' function), then attempts to draw a partially transparent box at (100,100). However, instead of a white translucent box, I get a weird-looking distortion to the image (I'll try to have them at the bottom of my post). Any suggestions? Here is my code:

#include "hgl.h"

void proximity()
{
    int x = 0, y = 0, d1, d2, d3, dcenter;

    while(x < WIDTH){
        while(y < HEIGHT){
            d1 = distance(x, y, (WIDTH/2) - 200, (HEIGHT/2) + 200);
            d2 = distance(x, y, (WIDTH/2) + 200, (HEIGHT/2) + 200);
            d3 = distance(x, y, (WIDTH/2), (HEIGHT/2) - 150);
            dcenter = distance(x, y, WIDTH/2, HEIGHT/2);
            putpixel(x, y, d1, d2, d3);
            y++;
        }
        y = 0;
        x++;
    }
}

int alpha_transparency(float alpha, float value1, float value2)
{
    return (1-alpha) * value1 + alpha * value2;
}

void transparent_box(int pos_x, int pos_y, int width, int height, float alpha, char r, char g, char b)
{
    int x = 0, y = 0;
    while(x < width)
    {
        while(y < height)
        {
            int rr, rg, rb;
            rr = alpha_transparency(alpha, p.bitmap[x+pos_x][y+pos_y].r, r);
            rg = alpha_transparency(alpha, p.bitmap[x+pos_x][y+pos_y].g, g);
            rb = alpha_transparency(alpha, p.bitmap[x+pos_x][y+pos_y].b, b);
            putpixel(pos_x + x, pos_y + y, rr, rg, rb);
            y++;
        }
        x++;
        y = 0;
    }
}

int main()
{
    fp = fopen("out.bmp","wb");

    set_dimensions(1440, 900);
    insert_header();

    white_screen();

    proximity();
    transparent_box(100, 100, 500, 500, .9, 255, 255, 255);

    insert_image();
    fclose(fp);
    return 0;
}

Sorry, I couldn't include the output because I'm a new user. However, here are the links:

Original Picture

Picture with "transparent" box

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

与风相奔跑 2024-09-14 08:26:20

您的 alpha 混合函数是正确的;考虑 Alpha 混合的另一种方式是,它基于 Alpha 在两个颜色值之间进行插值,因此它应该是 [0, 1] 中的值。

但是,您不应该将颜色分量作为 char 传递,默认情况下它是有符号的。您应该将它们作为 unsigned char 或更广泛的整数类型传递。发生的情况是,您没有像您期望的那样传入 255,而是传入 -1

换句话说,将您的颜色分量存储为 unsigned char ,以确保您没有符号恶作剧(请参阅EDIT2)。

编辑:请注意,如果您的 alpha 位于 [0, 255] 中,则应将其标准化为 [0, 1] 以执行 alpha 混合操作。

EDIT2:另外,如果您将像素存储为 char 而不是 unsigned char,这可以解释我看到的奇怪的夹紧:

alpha_transparency(0.9, (char)255, (char)255)
== alpha_transparency(0.9f, -1.0f, -1.0f)
== -1.0f
== 0xff (cast to char)

alpha_transparency(0.9, (char)128, (char)255)
== alpha_transparency(0.9f, -128.0f, -1.0f)
== -13.7f
== 0xf3

alpha_transparency(0.9, (char)127, (char)255)
== alpha_transparency(0.9f, 127.0f, -1.0f)
== -11.80f
== 0x0b

alpha_transparency(0.9, (char)0, (char)255)
== alpha_transparency(0.9f, 0.0f, -1.0f)
== -0.9f
== 0x00

Your alpha blend function is correct; another way to think of alpha blending is that it interpolates between two color values based on alpha, so it should be a value in [0, 1].

However, you shouldn't be passing the color components as char, which is signed by default. You should pass them either as unsigned char or as a wider integer type. What is happening is that instead of passing in 255 as you expect, you are passing in -1.

In other words, store your color components as unsigned chars to ensure you don't have signedness shenanigans (see EDIT2).

EDIT: Note that if your alpha is in [0, 255], you should normalize it to [0, 1] to perform the alpha blending operation.

EDIT2: Also, if you are storing your pixels as char instead of unsigned char, this would explain the odd clamping I saw:

alpha_transparency(0.9, (char)255, (char)255)
== alpha_transparency(0.9f, -1.0f, -1.0f)
== -1.0f
== 0xff (cast to char)

alpha_transparency(0.9, (char)128, (char)255)
== alpha_transparency(0.9f, -128.0f, -1.0f)
== -13.7f
== 0xf3

alpha_transparency(0.9, (char)127, (char)255)
== alpha_transparency(0.9f, 127.0f, -1.0f)
== -11.80f
== 0x0b

alpha_transparency(0.9, (char)0, (char)255)
== alpha_transparency(0.9f, 0.0f, -1.0f)
== -0.9f
== 0x00
但可醉心 2024-09-14 08:26:20

我认为问题在于你处理颜色的方式。维基百科中使用的方法假设 0 为黑色,1 为白色,0.5 位于中间。但是您的代码使用的是整数,因此我假设您将 0 定义为黑色,将 255 定义为白色。

所以正确的代码是:

return (255-alpha)*value1+alpha*value2;

您可能还会遭受编译器舍入的困扰,而您认为不会。我会将函数中的代码更改为:

float result = (255.0f-alpha)*value1+alpha*value2;
return (int) result;

一般来说,使用浮点数而不是整数来处理图像是很常见的。如今,许多程序将整个图像转换为浮点数,对其进行处理,然后再将其转换回来。您可以通过这种方式避免一些错误。

The issue I think is in the way you're dealing with colors. The method used in Wikipedia assumes that 0 is black and 1 is white, with 0.5 being in the middle. However your code is using ints, so I assume that you're defining 0 as black and 255 as white.

So the correct code is:

return (255-alpha)*value1+alpha*value2;

You may be also suffering from the compiler rounding where you don't think it would. I would change the code in your function to this:

float result = (255.0f-alpha)*value1+alpha*value2;
return (int) result;

In general it's very common to work with images using floats instead of ints. Many programs today convert the entire image to floats, process it then convert it back. You can avoid several bugs this way.

只等公子 2024-09-14 08:26:20

最好坚持使用单一数据类型:要么全是浮点数,要么全是整数;这减少了混乱的可能性,并避免了一类性能陷阱。

对于所有整数,您需要记住重新缩放整数,以便结果适合原始范围:

int alpha_transparency(int alpha, int value1, int value2) {
  int unscaled= (255-alpha)*value1 + alpha*value2;
  return unscaled / 255;  /* integer division */
}

对于全浮点数,您需要记住根据 [0..255] 中的原始值标准化整数输入(或无论如何)到 [0.0 .. 1.0] 中的浮点数,进行所有处理,然后仅在最后转换回整数。

float input_raw_value(unsigned char value)  { return value/255.0; }
...
float alpha_transparency(float alpha, float value1, float value2) {
  return (1.0-alpha)*value1 + alpha*value2;
}
...
unsigned char output_raw_value(float value) { return value*255.0; }

请注意,我忽略了每种方法中的舍入问题;一旦你掌握了基本的数学知识并运行起来,你就应该注意这一点。此外,还有各种技巧可以通过乘法或位调整来代替除法(这可能相对较慢)。

It is probably best to stick with a single datatype: either all floats, or all integers; this reduces the potential for confusion, and avoids a class of performance pitfalls.

For all ints, you need to remember to re-scale the integer so the result fits back into the original range:

int alpha_transparency(int alpha, int value1, int value2) {
  int unscaled= (255-alpha)*value1 + alpha*value2;
  return unscaled / 255;  /* integer division */
}

For all-floats, you need to remember to normalize integer inputs from a raw value in [0..255] (or whatever) to a float in [0.0 .. 1.0], do all the processing, then convert back to integer only at the end.

float input_raw_value(unsigned char value)  { return value/255.0; }
...
float alpha_transparency(float alpha, float value1, float value2) {
  return (1.0-alpha)*value1 + alpha*value2;
}
...
unsigned char output_raw_value(float value) { return value*255.0; }

Note that I have ignored rounding issues in each method; once you've got the basic math up and running, you should pay some attention to that. Also, there are various tricks to replace the divisions (which can be relatively slow) by multiplication or bit-fiddling.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文