Градиент с HSV, а не RGB в OpenGL

OpenGL может раскрашивать прямоугольник градиентом цветов от одной стороны к другой. Я использую для этого следующий код на C ++

glBegin(GL_QUADS);
{
    glColor3d(simulationSettings->hotColour.redF(), simulationSettings->hotColour.greenF(), simulationSettings->hotColour.blueF());
    glVertex2d(keyPosX - keyWidth/2, keyPosY + keyHight/2);
    glColor3d(simulationSettings->coldColour.redF(), simulationSettings->coldColour.greenF(), simulationSettings->coldColour.blueF());
    glVertex2d(keyPosX - keyWidth/2, keyPosY - keyHight/2);
    glColor3d(simulationSettings->coldColour.redF(), simulationSettings->coldColour.greenF(), simulationSettings->coldColour.blueF());
    glVertex2d(keyPosX + keyWidth/2, keyPosY - keyHight/2);
    glColor3d(simulationSettings->hotColour.redF(), simulationSettings->hotColour.greenF(), simulationSettings->hotColour.blueF());
    glVertex2d(keyPosX + keyWidth/2, keyPosY + keyHight/2);
}

Я использую некоторые библиотеки Qt для преобразования между HSV и RGB. Как видно из кода, я рисую прямоугольник с цветовым градиентом от того, что я называю hotColour до coldColour.

Зачем я это делаю? Программа, которую я сделал, рисует трехмерные векторы в пространстве и указывает их длину цветом. Пользователю предлагается выбрать горячий (высокое значение) и холодное (низкое значение) цвета, и программа автоматически выполнит градиент, используя масштабирование HSV.

Почему масштабирование HSV? потому что HSV имеет однозначное значение на цветовой карте, которую я использую, и линейное создание градиентов с его помощью - очень простая задача. Чтобы пользователь мог выбрать цвета, я предлагаю ему цветовую карту QColourDialog

http://qt-project.org/doc/qt-4.8/qcolordialog.html

На этой цветовой карте вы можете видеть, что красный цвет доступен справа и слева, что делает невозможным получение линейной шкалы для этой цветовой карты с RGB. Но с HSV очень легко получить линейную шкалу, где мне просто нужно использовать линейную шкалу от 0 до 360 для значений оттенка.

С помощью этой парадигмы мы можем видеть, что горячие и холодные цвета определяют направление градиента, поэтому, например, если я выберу оттенок равным 0 для холодного и 359 для горячего, HSV даст мне градиент между 0 и 359 и будет включить в градиент весь спектр цветов; в то время как в OpenGL он в основном будет переходить от красного к красному, что не является градиентом !!!!!!

Как я могу заставить OpenGL использовать градиент HSV, а не RGB? Единственная идея, которая приходит мне в голову, - это разрезать прямоугольник, который я хочу раскрасить, и делать много градиентов по меньшим прямоугольникам, но я думаю, что это не самый эффективный способ сделать это.

Любые идеи?


person Sam    schedule 05.03.2012    source источник


Ответы (2)


Как я могу заставить OpenGL использовать градиент HSV, а не RGB?

Я бы не назвал это «принуждением», а «обучением». Метод OpenGL по умолчанию для интерполяции векторов атрибутов вершин - это барицентрическая интерполяция отдельных элементов вектора на основе координат NDC фрагмента.

Вы должны указать OpenGL, как преобразовать эти барицентрические интерполированные значения HSV в RGB.

Для этого мы вводим фрагментный шейдер, который предполагает, что атрибут цветовой вершины не RGB, а HSV.

#version 120
varying vec3 vertex_hsv; /* set this in appropriate vertex shader to the vertex attribute data*/

vec3 hsv2rgb(vec3 hsv)
{
    float h = hsv.x * 6.; /* H in 0°=0 ... 1=360° */
    float s = hsv.y;
    float v = hsv.z;
    float c = v * s;

    vec2 cx = vec2(v*s, c * ( 1 - abs(mod(h, 2.)-1.) ));

    vec3 rgb = vec3(0., 0., 0.);
    if( h < 1. ) {
        rgb.rg = cx;
    } else if( h < 2. ) {
        rgb.gr = cx;
    } else if( h < 3. ) {
        rgb.gb = cx;
    } else if( h < 4. ) {
        rgb.bg = cx;
    } else if( h < 5. ) {
        rgb.br = cx;
    } else {
        rgb.rb = cx;
    }
    return rgb + vec3(v-cx.y);
}

void main()
{
    gl_FragColor = hsv2rgb(vertex_hsv);
}
person datenwolf    schedule 05.03.2012
comment
Откуда взялось число 6 в вашем первом вычислении float h = hsv.x * 6.; и что оно означает? - person tobsen; 30.05.2012
comment
Также где c определяется в строке vec2 cx = vec2(v*s, c * ( 1 - abs(mod(h, 2.)-1.) ));? - person tobsen; 30.05.2012
comment
@tobsen: Букву 'c' я забыл правильно подставить при редактировании. О множителе 6: оттенок в HSL задается как угол, а приведенный выше код предполагает выполнение полного цикла цветового цикла для диапазона значений h в диапазоне 0… 1, что соответствует H в диапазоне 0 °… 360 ° или 0 до 2pi. Итак, у нас есть 6 секторов в цветовом цикле, которые мы извлекаем, увеличивая диапазон значений h на 6 и взяв его по модулю 2 (в вычислении cx), тогда серия операторов if выполняет интерполяцию между основными цветами на круге H. окружение. - person datenwolf; 30.05.2012

Вы можете сделать это с помощью фрагментного шейдера. Вы рисуете четырехугольник и применяете свой фрагментный шейдер, который придает нужную окраску четырехугольнику. Я бы сделал это так, чтобы установить цвета углов в значения HSV, которые вы хотите, а затем в шейдере фрагмента преобразовать интерполированные значения цвета из HSV обратно в RGB. Дополнительную информацию о фрагментных шейдерах см. В документации.

person user1118321    schedule 05.03.2012