2015-03-31 7 views
1

В настоящее время я пытаюсь создать класс градиента цвета для моего исследователя Mandelbrot Set.Линейный градиент цвета не работает

Он считывает цветовые ограничения (RGBA8888 цвет и положение между 0 и 1) из текстового файла и добавляет их к вектору, который используется для определения цветов в определенной позиции.

Чтобы вычислить цвет, алгоритм выполняет поиск следующего ограничения с обеих сторон от заданной позиции, разбивает цвет на четыре отдельных канала, а затем для каждого из них выполняет поиск по нижнему из них и добавляет часть разница равна отношению (x-lpos)/(upos-lpos) к нижнему цвету. После этого каналы сдвигаются и ORed вместе, а затем возвращаются как RGBA8888 беззнаковое целое число. (См код ниже.)

EDIT: более или менее я полностью переписал класс градиента, фиксируя некоторые проблемы и делает его более удобным для чтения для отладки (Это становится медленным, как ад, хотя, но -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

Gradient файл:

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

Что я делаю неправильно? Я переписал метод operator[] несколько раз, без каких-либо изменений.

Вопросы для уточнения или общие замечания по моему коду приветствуются.

+0

Вы действительно должны переместить много этого кода из 'operator []' и в метод, который читает ограничения. Используйте отсортированный «вектор» вместо «карты», чтобы удерживать ограничения, и разбирайте их на отдельные компоненты RGBA как можно раньше. – Alnitak

+0

oh, и если вы сохраните свои компоненты RGBA в своем собственном классе, вы, вероятно, должны поместить метод, который выполняет основную математику в тех цветах внутри этого класса. – Alnitak

+0

Почему карта должна быть проблемой? Я фактически использовал вектор раньше, но затем переключился на карту, потому что значения в карте ** сортируются ** по возрастанию ключа, что дает время логарифмического доступа при инициализации. – s3lph

ответ

2

Ваша проблема связана к сложной интерполяционной функции.

При линейной интерполяции в диапазоне a .. b с использованием другого фактора r (с диапазоном 0 .. 1), чтобы указать позицию в этом диапазоне, это совершенно не нужно, чтобы определить, является ли a или b больше. В любом случае вокруг вы можете просто использовать:

result = a + r * (b - a) 

Если r == 0 это тривиально показано, что a, и если r == 1a - 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, чтобы помещать операции зажима в конструктор, а не в отдельные операторы.

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