线性颜色渐变不起作用
我目前正在尝试为我的Mandelbrot Set浏览器创建颜色渐变类。线性颜色渐变不起作用
它从文本文件读取颜色约束(RGBA8888
颜色和0到1之间的位置),并将它们添加到矢量中,该矢量稍后用于确定某个位置的颜色。
为了计算颜色,算法从给定位置搜索下一个约束条件,将颜色分成四个单独的通道,然后对每个通道搜索两者中较低的一个,并添加一部分差值等于(x-lpos)/(upos-lpos)
与较低颜色的比率。之后,通道被移位并进行或运算,然后返回为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
或反之亦然无关紧要。
在你的情况下,优选制剂,因为它避免了b - a
减法是可能的命中范围夹紧限制是:
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
类,以便将钳位操作放入构造函数中,而不是放在各个操作符中。
经过一番大量的反复试验后,我终于设法让它工作。 (在这一点上非常感谢@Alnitak,他建议使用一个单独的RGBA颜色类。)
主要问题是,当上部约束的颜色值低于下部颜色时,我仍然乘以比率(x-l)/(u-l)
,相反,我应该使用它的挂件1 - (x-l)/(u-l)
来指代上限约束的颜色作为新约束的基础。
这里遵循RGBA类的实现和固定梯度类:
class RGBA {
private:
unsigned int red = 0, green = 0, blue = 0, alpha = 0;
public:
static RGBA mid(RGBA a, RGBA b, double r) {
RGBA color;
if (a.red < b.red) color.red = a.red + (b.red - a.red) * r;
else color.red = b.red + (a.red - b.red) * (1-r);
if (a.green < b.green) color.green = a.green + (b.green - a.green) * r;
else color.green = b.green + (a.green - b.green) * (1-r);
if (a.blue < b.blue) color.blue = a.blue + (b.blue - a.blue) * r;
else color.blue = b.blue + (a.blue - b.blue) * (1-r);
if (a.alpha < b.alpha) color.alpha = a.alpha + (b.alpha - a.alpha) * r;
else color.alpha = b.alpha + (a.alpha - b.alpha) * (1-r);
return color;
}
RGBA() {};
RGBA(unsigned char _red, unsigned char _green, unsigned char _blue, unsigned char _alpha) :
red(_red), green(_green), blue(_blue), alpha(_alpha) {};
RGBA(unsigned int _rgba) {
red = (_rgba >> 24) & 0xff;
green = (_rgba >> 16) & 0xff;
blue = (_rgba >> 8) & 0xff;
alpha = _rgba & 0xff;
};
operator unsigned int() {
return (red << 24) | (green << 16) | (blue << 8) | alpha;
}
RGBA operator+(const RGBA& o) const {
return RGBA((red + o.red) & 0xff, (green + o.green) & 0xff, (blue + o.blue) & 0xff, (alpha + o.alpha) & 0xff);
}
RGBA operator-(const RGBA& o) const {
return RGBA(min(red - o.red, 0u), min(green - o.green, 0u), min(blue - o.blue, 0u), min(alpha - o.alpha, 0u));
}
RGBA operator~() {
return RGBA(0xff - red, 0xff - green, 0xff - blue, 0xff - alpha);
}
RGBA operator*(double _f) {
return RGBA((unsigned int) min(red * _f, 0.) & 0xff, (unsigned int) min(green * _f, 0.) & 0xff,
(unsigned int) min(blue * _f, 0.) & 0xff, (unsigned int) min(alpha * _f, 0.) & 0xff);
}
};
class Gradient {
private:
map<double, RGBA> constraints;
public:
Gradient() {
constraints[0] = RGBA(0x007700ff);
constraints[1] = RGBA(0xffffffff);
}
~Gradient() {}
void addConstraint(RGBA color, double position) {
constraints[position] = color;
}
void reset() {
constraints.clear();
}
unsigned int operator[](double value) {
if (value < 0 || value > 1+1E-10) return 0xff;
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;
RGBA lower = lowerC->second;
RGBA upper = upperC->second;
RGBA color = RGBA::mid(lower, upper, (value-lpos)/(upos-lpos));
return color;
}
size_t size() {
return constraints.size();
}
};
这是结果:
你实际上不需要比较开始和结束组件来插入它们。如果'r'是插值的程度(范围在0 ... 1),那么你只需要'val =(1-r)* start + r * end'。事实上,你的整个'mid(a,b,r)'函数可以变成'return(1-r)* a + r * b'。 – Alnitak 2015-04-02 07:29:32
您确实应该将大量代码从'operator []'中移出,并移入读取约束的方法中。使用排序的'vector'而不是'map'来保存约束条件,并尽早将这些约束条件解析为单独的RGBA组件。 – Alnitak 2015-04-01 16:16:47
哦,如果你将你的RGBA组件存储在它自己的类中,那么你应该把这个基本数学运算方法放在这个类中的那些颜色上。 – Alnitak 2015-04-01 16:19:31
地图为什么要成为问题?实际上我之前使用过一个矢量,但随后切换到了一张地图,因为地图**中的值是按键升序排序的,这在初始化时给出了对数访问时间。 – s3lph 2015-04-01 16:19:39