执行 Alpha 混合的正确方法是什么? (三)
我正在编写一个非常简单的图形库,并且我正在尝试找出如何进行 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;
}
抱歉,我无法包含输出,因为我是新用户。不过,以下是链接:
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您的 alpha 混合函数是正确的;考虑 Alpha 混合的另一种方式是,它基于 Alpha 在两个颜色值之间进行插值,因此它应该是 [0, 1] 中的值。
但是,您不应该将颜色分量作为
char
传递,默认情况下它是有符号的。您应该将它们作为unsigned char
或更广泛的整数类型传递。发生的情况是,您没有像您期望的那样传入255
,而是传入-1
。换句话说,将您的颜色分量存储为
unsigned char
,以确保您没有符号恶作剧(请参阅EDIT2)。编辑:请注意,如果您的 alpha 位于 [0, 255] 中,则应将其标准化为 [0, 1] 以执行 alpha 混合操作。
EDIT2:另外,如果您将像素存储为
char
而不是unsigned char
,这可以解释我看到的奇怪的夹紧: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 asunsigned char
or as a wider integer type. What is happening is that instead of passing in255
as you expect, you are passing in-1
.In other words, store your color components as
unsigned char
s 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 ofunsigned char
, this would explain the odd clamping I saw:我认为问题在于你处理颜色的方式。维基百科中使用的方法假设 0 为黑色,1 为白色,0.5 位于中间。但是您的代码使用的是整数,因此我假设您将 0 定义为黑色,将 255 定义为白色。
所以正确的代码是:
您可能还会遭受编译器舍入的困扰,而您认为不会。我会将函数中的代码更改为:
一般来说,使用浮点数而不是整数来处理图像是很常见的。如今,许多程序将整个图像转换为浮点数,对其进行处理,然后再将其转换回来。您可以通过这种方式避免一些错误。
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:
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:
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.
最好坚持使用单一数据类型:要么全是浮点数,要么全是整数;这减少了混乱的可能性,并避免了一类性能陷阱。
对于所有整数,您需要记住重新缩放整数,以便结果适合原始范围:
对于全浮点数,您需要记住根据 [0..255] 中的原始值标准化整数输入(或无论如何)到 [0.0 .. 1.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:
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.
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.