Как нарисовать радугу в Freeglut?

Я пытаюсь нарисовать радужную легенду сюжета в openGL. Вот что у меня есть до сих пор:

glBegin(GL_QUADS);
for (int i = 0; i != legendElements; ++i)
{
    GLfloat const cellColorIntensity = (GLfloat) i / (GLfloat) legendElements;
    OpenGL::pSetHSV(cellColorIntensity*360.0f, 1.0f, 1.0f);

    // draw the ith legend element
    GLdouble const xLeft = xBeginRight - legendWidth;
    GLdouble const xRight = xBeginRight;
    GLdouble const yBottom = (GLdouble)i * legendHeight /
    (GLdouble)legendElements + legendHeight;
    GLdouble const yTop = yBottom + legendHeight;

    glVertex2d(xLeft, yTop); // top-left
    glVertex2d(xRight, yTop); // top-right
    glVertex2d(xRight, yBottom); // bottom-right
    glVertex2d(xLeft, yBottom); // bottom-left
}

glEnd();

legendElements - это количество дискретных квадратов, которые составляют "радугу". xLeft,xRight,yBottom и yTop являются вершинами, которые составляют каждый из квадратов.

где функция OpenGL::pSetHSV выглядит так:

void pSetHSV(float h, float s, float v) 
{
    // H [0, 360] S and V [0.0, 1.0].
    int i = (int)floor(h / 60.0f) % 6;
    float f = h / 60.0f - floor(h / 60.0f);
    float p = v * (float)(1 - s);
    float q = v * (float)(1 - s * f);
    float t = v * (float)(1 - (1 - f) * s);
    switch (i) 
    {
        case 0: glColor3f(v, t, p);
            break;
        case 1: glColor3f(q, v, p);
            break;
        case 2: glColor3f(p, v, t);
            break;
        case 3: glColor3f(p, q, v);
            break;
        case 4: glColor3f(t, p, v);
            break;
        case 5: glColor3f(v, p, q);
    }
}

я получил эту функцию от http://forum.openframeworks.cc/t/hsv-color-setting/770

однако, когда я рисую это выглядит так:

enter image description here

то, что я хотел бы, это спектр красного,зеленого,синего,индиго, фиолетового (поэтому я хочу повторить линейно через Hue. Тем не менее, это не похоже на то, что происходит.

Я действительно не понимаю, как преобразование RGB/HSV в pSetHSV() работает, поэтому мне трудно идентифицировать проблема..

EDIT: вот фиксированная версия, вдохновленная Jongware (прямоугольники были нарисованы неправильно):

// draw legend elements
glBegin(GL_QUADS);
for (int i = 0; i != legendElements; ++i)
{
    GLfloat const cellColorIntensity = (GLfloat) i / (GLfloat) legendElements;
    OpenGL::pSetHSV(cellColorIntensity * 360.0f, 1.0f, 1.0f);

    // draw the ith legend element
    GLdouble const xLeft = xBeginRight - legendWidth;
    GLdouble const xRight = xBeginRight;
    GLdouble const yBottom = (GLdouble)i * legendHeight /
    (GLdouble)legendElements + legendHeight + yBeginBottom;
    GLdouble const yTop = yBottom + legendHeight / legendElements;

    glVertex2d(xLeft, yTop); // top-left
    glVertex2d(xRight, yTop); // top-right
    glVertex2d(xRight, yBottom); // bottom-right
    glVertex2d(xLeft, yBottom); // bottom-left
}

glEnd();

2 ответов


я генерирую спектральные цвета, как это:

void spectral_color(double &r,double &g,double &b,double l) // RGB <- lambda l = < 380,780 > [nm]
    {
         if (l<380.0) r=     0.00;
    else if (l<400.0) r=0.05-0.05*sin(M_PI*(l-366.0)/ 33.0);
    else if (l<435.0) r=     0.31*sin(M_PI*(l-395.0)/ 81.0);
    else if (l<460.0) r=     0.31*sin(M_PI*(l-412.0)/ 48.0);
    else if (l<540.0) r=     0.00;
    else if (l<590.0) r=     0.99*sin(M_PI*(l-540.0)/104.0);
    else if (l<670.0) r=     1.00*sin(M_PI*(l-507.0)/182.0);
    else if (l<730.0) r=0.32-0.32*sin(M_PI*(l-670.0)/128.0);
    else              r=     0.00;
         if (l<454.0) g=     0.00;
    else if (l<617.0) g=     0.78*sin(M_PI*(l-454.0)/163.0);
    else              g=     0.00;
         if (l<380.0) b=     0.00;
    else if (l<400.0) b=0.14-0.14*sin(M_PI*(l-364.0)/ 35.0);
    else if (l<445.0) b=     0.96*sin(M_PI*(l-395.0)/104.0);
    else if (l<510.0) b=     0.96*sin(M_PI*(l-377.0)/133.0);
    else              b=     0.00;
    }
  • l ввод длина волны [nm] < 380,780 >
  • r,g,b цвет RGB выхода < 0,1 >

это просто грубо sin приближение волны реальный спектральный цвет. Вы также можете создать таблицу из этого и интерполировать ее или использовать текстуру ... выходные цвета:

spectral_colors

существуют также различные подходы например:

  1. линейные цветовые композитные градиенты

  2. человеческий глаз X,Y,Z интеграция кривых чувствительности

    вы должны иметь очень точные X,Y,Z кривые, даже небольшое отклонение вызывает "нереалистичные" цвета, как в этом пример

    human eye XYZ sensitivity

чтобы сделать его лучше, вам нужно нормализовать цвета и добавить экспоненциальные поправки чувствительности. Также эти кривые изменяются с каждым поколением и различны в разных регионах мира. Поэтому, если вы не делаете каких-то специальных медицинских/физических смягчений, это не хорошая идея идти этим путем.

spectral colors comparison

| <- 380nm ----------------------------------------------------------------- 780nm -> |

[edit1] здесь is мое новое физически более точное преобразование

я настоятельно рекомендую использовать этот подход вместо этого (это более точно и лучше в любом случае)


Ну, не совсем верно. Здесь я сделал пример javascript. Желтый цвет натрия (589nm) слишком померанцовый и красный цвет Halpha (656nm) слишком коричнев....

Сохраните этот пример в HTML-файл (требуется jquery) и загрузите его в браузер: страница.HTML-код?l=[нанометры]

<!DOCTYPE html>
<html><head>
<script src='jquery.js'></script>
<script>


/*
 * Return parameter value of name (case sensitive !)
 */
function get_value(parametername)
{
    readvalue=(location.search ? location.search.substring(1) : false);

    if (readvalue)
    {
       parameter=readvalue.split('&');
       for (i=0; i<parameter.length; i++)
       {
           if (parameter[i].split('=')[0] == parametername)
             return parameter[i].split('=')[1];
       }
    }
    return false;
}

function spectral_color(l) // RGB <- lambda l = < 380,780 > [nm]
{
  var M_PI=Math.PI;
  var r=0,g,b;
  if (l<380.0) r=     0.00;
  else if (l<400.0) r=0.05-0.05*Math.sin(M_PI*(l-366.0)/ 33.0);
  else if (l<435.0) r=     0.31*Math.sin(M_PI*(l-395.0)/ 81.0);
  else if (l<460.0) r=     0.31*Math.sin(M_PI*(l-412.0)/ 48.0);
  else if (l<540.0) r=     0.00;
  else if (l<590.0) r=     0.99*Math.sin(M_PI*(l-540.0)/104.0);
  else if (l<670.0) r=     1.00*Math.sin(M_PI*(l-507.0)/182.0);
  else if (l<730.0) r=0.32-0.32*Math.sin(M_PI*(l-670.0)/128.0);
  else              r=     0.00;
       if (l<454.0) g=     0.00;
  else if (l<617.0) g=     0.78*Math.sin(M_PI*(l-454.0)/163.0);
  else              g=     0.00;
       if (l<380.0) b=     0.00;
  else if (l<400.0) b=0.14-0.14*Math.sin(M_PI*(l-364.0)/ 35.0);
  else if (l<445.0) b=     0.96*Math.sin(M_PI*(l-395.0)/104.0);
  else if (l<510.0) b=     0.96*Math.sin(M_PI*(l-377.0)/133.0);
  else              b=     0.00;
  var rgb = Math.floor(r*256)*65536+Math.floor(g*256)*256 + Math.floor(b*256);
  rgb = '000000' + rgb.toString(16);
  rgb = '#' + rgb.substr(-6).toUpperCase();

  $('#color').html([r,g,b,rgb,l]);
  $('body').css('background-color', rgb);
}

</script>
</head><body>
<div id='color'></div>
<script>
spectral_color(get_value('l'));
</script>
</body>
</html>