2015-03-31 175 views
1

我目前正在嘗試爲我的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` 

它只能部分地,雖然。當我只使用一個黑/白梯度,所得到的圖像作爲意圖:

black-white works flawlessly

梯度文件:

2 
0 000000ff 
1 ffffffff 

然而,當我使用更加豐富多彩的梯度(的線性版本下面Ultra Fractal gradient,輸入文件), 圖像遠離預期的結果 圖像仍然不顯示所期望的着色:

enter image description here

梯度文件:

5 
0  000764ff 
.16  206bcbff 
.42  edffffff 
.6425 ffaa00ff 
0.8575 000200ff 

我在做什麼錯?我多次改寫了operator[]方法,沒有任何改變。

對我的代碼的澄清或一般評論的問題是受歡迎的。

+0

您確實應該將大量代碼從'operator []'中移出,並移入讀取約束的方法中。使用排序的'vector'而不是'map'來保存約束條件,並儘早將這些約束條件解析爲單獨的RGBA組件。 – Alnitak 2015-04-01 16:16:47

+0

哦,如果你將你的RGBA組件存儲在它自己的類中,那麼你應該把這個基本數學運算方法放在這個類中的那些顏色上。 – Alnitak 2015-04-01 16:19:31

+0

地圖爲什麼要成爲問題?實際上我之前使用過一個矢量,但隨後切換到了一張地圖,因爲地圖**中的值是按鍵升序排序的,這在初始化時給出了對數訪問時間。 – s3lph 2015-04-01 16:19:39

回答

2

你的問題是由於過度複雜的插值函數。

使用其他因子r(具有範圍0 .. 1),以指示在該範圍內它是完全不必要的,以確定ab是否大於該位置的範圍內a .. b當線性內插。你身邊無論哪種方式可以只使用:

result = a + r * (b - a) 

如果r == 0這是平凡顯示爲a,如果r == 1a - a取消了,只留下b。同樣如果r == 0.5那麼結果是(a + b)/2a > 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類,以便將鉗位操作放入構造函數中,而不是放在各個操作符中。

1

經過一番大量的反覆試驗後,我終於設法讓它工作。 (在這一點上非常感謝@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(); 
    } 
}; 

這是結果:

elephant valley

+0

你實際上不需要比較開始和結束組件來插入它們。如果'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