我目前正在尝试为我的 Mandelbrot 集浏览器创建一个颜色渐变类。
它从文本文件中读取颜色约束(RGBA8888
颜色和 0 到 1 之间的位置)并将它们添加到一个 vector 中,该 vector 稍后用于确定特定位置的颜色。
为了计算颜色,该算法从给定位置搜索下一个约束到任一侧,将颜色分成四个单 channel ,然后,对于每个 channel ,搜索两个 channel 中较低的一个并添加一部分差异等于 (x-lpos)/(upos-lpos)
与较低颜色的比率。之后, channel 被移位并一起进行或运算,然后返回为 RGBA8888
无符号整数。 (请参阅下面的代码。)
编辑:我完全重写了渐变类,修复了一些问题并使其更具可读性以进行调试(尽管它变得很慢,但是-Os
或多或少会解决这个问题)。然而,它仍然不是它应该的样子。
class Gradient { //remade, Some irrelevant methods and de-/constructors removed
private:
map<double, unsigned int> constraints;
public:
unsigned int operator[](double value) {
//Forbid out-of-range values, return black
if (value < 0 || value > 1+1E-10) return 0xff;
//Find upper and lower constraint
auto upperC = constraints.lower_bound(value);
if (upperC == constraints.end()) upperC = constraints.begin();
auto lowerC = upperC == constraints.begin() ? prev(constraints.end(), 1) : prev(upperC, 1);
if (value == lowerC->first) return lowerC->second;
double lpos = lowerC->first;
double upos = upperC->first;
if (upos < lpos) upos += 1;
//lower color channels
unsigned char lred = (lowerC->second >> 24) & 0xff;
unsigned char lgreen = (lowerC->second >> 16) & 0xff;
unsigned char lblue = (lowerC->second >> 8) & 0xff;
unsigned char lalpha = lowerC->second & 0xff;
//upper color channels
unsigned char ured = (upperC->second >> 24) & 0xff;
unsigned char ugreen = (upperC->second >> 16) & 0xff;
unsigned char ublue = (upperC->second >> 8) & 0xff;
unsigned char ualpha = upperC->second & 0xff;
unsigned char red = 0, green = 0, blue = 0, alpha = 0xff;
//Compute each channel using
// lower color + dist(lower, x)/dist(lower, upper) * diff(lower color, upper color)
if (lred < ured)
red = lred + (value - lpos)/(upos - lpos) * (ured - lred);
else red = ured + (upos - value)/(upos - lpos) * (ured - lred);
if (lgreen < ugreen)
green = lgreen + (value - lpos)/(upos - lpos) * (ugreen - green);
else green = ugreen + (upos - value)/(upos - lpos) * (ugreen - lgreen);
if (lblue < ublue)
blue = lblue + (value - lpos)/(upos - lpos) * (ublue - lblue);
else blue = ublue + (upos - value)/(upos - lpos) * (ublue - lblue);
if (lalpha < ualpha)
alpha = lalpha + (value - lpos)/(upos - lpos) * (ualpha - lalpha);
else alpha = ualpha + (upos - value)/(upos - lpos) * (ualpha - lalpha);
//Merge channels together and return
return (red << 24) | (green << 16) | (blue << 8 ) | alpha;
}
void addConstraint(unsigned int color, double position) {
constraints[position] = color;
}
};
更新方法中的用法:
image[r + rres*i] = grd[ratio];
//With image being a vector<unsigned int>, which is then used as data source for a `SDL_Texture` using `SDL_UpdateTexture`
不过,它只部分起作用。当我只使用黑/白渐变时,生成的图像符合预期:
渐变文件:
2
0 000000ff
1 ffffffff
然而,当我使用更丰富多彩的渐变(Ultra Fractal gradient 的线性版本,下面的输入文件)时,图像与预期结果相去甚远图像仍然没有显示所需的颜色:
渐变文件:
5
0 000764ff
.16 206bcbff
.42 edffffff
.6425 ffaa00ff
0.8575 000200ff
我做错了什么?我多次重写了 operator[]
方法,没有任何改变。
欢迎对我的代码提出澄清或一般性评论的问题。
最佳答案
您的问题是由于插值函数过于复杂。
当使用另一个因子 r
(范围 0 .. 1
)在范围 a .. b
中进行线性插值时,以指示位置在该范围内,完全没有必要确定 a
或 b
哪个更大。无论哪种方式,您都可以使用:
result = a + r * (b - a)
如果 r == 0
这通常显示为 a
,如果 r == 1
a - a
取消,只留下 b
。同样,如果 r == 0.5
,则结果为 (a + b)/2
。 a > b
或反之亦然都没有关系。
您的情况下的首选公式是:
result = (1 - r) * a + r * b;
在您的新 RGBA
类上给定适当的 *
和 +
运算符可以实现您的 mid
函数(不需要每个组件的操作,因为它们是在这些操作符中处理的):
static RGBA mid(const RGBA& a, const RGBA& b, double r) {
return (1.0 - r) * a + r * b;
}
参见 https://gist.github.com/raybellis/4f69345d8e0c4e83411b ,其中我还重构了您的 RGBA
类,将钳制操作放在构造函数中,而不是放在各个运算符中。
关于c++ - 线性颜色渐变不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29377491/