как реализовать рендеринг в оттенках серого в OpenGL?

При рендеринге сцены из текстурированных полигонов я хотел бы иметь возможность переключаться между рендерингом в исходных цветах и ​​режимом «оттенки серого». Я пытался добиться этого, используя операции смешивания и цветовой матрицы; ничего из этого не сработало (со смешиванием я не смог найти glBlendFunc (), которая достигла чего-то отдаленно напоминающего то, что я хотел, и операции с цветовой матрицей ... обсуждаются здесь).

Решение, которое приходит на ум (но также довольно дорогое), состоит в том, чтобы захватывать экран каждый кадр и преобразовывать полученную текстуру в оттенки серого и вместо этого отображать это ... (где я сказал оттенки серого, я на самом деле имел в виду все с низкой насыщенностью, но я предполагаю, что для большинства возможных решений он не будет сильно отличаться от оттенков серого).

Какие еще у меня есть варианты?


person zyndor    schedule 15.05.2009    source источник


Ответы (3)


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

Мое предыдущее предложение, которое просто использовало длину вектора RGB для представления 0 яркости, было неверным, так как оно не учитывало масштабирование, прошу прощения.

Кредит за новый короткий фрагмент принадлежит обычному пользователю RTFM_FTW из ## opengl и ## opengl3 на FreeNode / IRC, и он позволяет вам изменять насыщенность напрямую, не вычисляя дорогостоящее преобразование RGB-> HSV-> RGB, которое является именно то, что вы хотите. Хотя код HSV уступает вашему вопросу, я оставлю его.

void main( void ) 
{ 
    vec3 R0 = texture2DRect( S, gl_TexCoord[0].st ).rgb;
    gl_FragColor = vec4( mix( vec3( dot( R0, vec3( 0.2125, 0.7154, 0.0721 ) ) ),
        R0, T ), gl_Color.a ); 
}

Если вам нужен больший контроль, чем просто насыщенность, вам нужно преобразовать в цветовое пространство HSL или HSV. Как показано ниже, с использованием фрагментного шейдера GLSL.

Прочтите спецификации OpenGL 3.0 и GLSL 1.30, доступные на http://www.opengl.org/registry, чтобы узнайте, как использовать функциональность GLSL v1.30.

#version 130
#define RED 0
#define GREEN 1
#define BLUE 2

in vec4 vertexIn;
in vec4 colorIn;
in vec2 tcoordIn;
out vec4 pixel;
Sampler2D tex;
vec4 texel;
const float epsilon = 1e-6;

vec3 RGBtoHSV(vec3 color)
{
    /* hue, saturation and value are all in the range [0,1> here, as opposed to their
       normal ranges of: hue: [0,360>, sat: [0, 100] and value: [0, 256> */
    int sortindex[3] = {RED,GREEN,BLUE};
    float rgbArr[3] = float[3](color.r, color.g, color.b);

    float hue, saturation, value, diff;
    float minCol, maxCol;
    int minIndex, maxIndex;

    if(color.g < color.r)
        swap(sortindex[0], sortindex[1]);
    if(color.b < color.g)
        swap(sortindex[1], sortindex[2]);
    if(color.r < color.b)
        swap(sortindex[2], sortindex[0]);

    minIndex = sortindex[0];
    maxIndex = sortindex[2];
    minCol = rgbArr[minIndex];
    maxCol = rgbArr[maxIndex];

    diff = maxCol - minCol;

    /* Hue */
    if( diff < epsilon){
        hue = 0.0;
    }
    else if(maxIndex == RED){
        hue = ((1.0/6.0) * ( (color.g - color.b) / diff )) + 1.0;
        hue = fract(hue);
    }
    else if(maxIndex == GREEN){
        hue = ((1.0/6.0) * ( (color.b - color.r) / diff )) + (1.0/3.0);
    }
    else if(maxIndex == BLUE){
        hue = ((1.0/6.0) * ( (color.r - color.g) / diff )) + (2.0/3.0);        
    }

    /* Saturation */
    if(maxCol < epsilon)
        saturation = 0;
    else
        saturation = (maxCol - minCol) / maxCol;

    /* Value */
    value = maxCol;

    return vec3(hue, saturation, value);
}
vec3 HSVtoRGB(vec3 color)
{
    float f,p,q,t, hueRound;
    int hueIndex;
    float hue, saturation, value;
    vec3 result;

    /* just for clarity */
    hue = color.r;
    saturation = color.g;
    value = color.b;

    hueRound = floor(hue * 6.0);
    hueIndex = int(hueRound) % 6;
    f = (hue * 6.0) - hueRound;
    p = value * (1.0 - saturation);
    q = value * (1.0 - f*saturation);
    t = value * (1.0 - (1.0 - f)*saturation);

    switch(hueIndex)
    {
        case 0:
            result = vec3(value,t,p);
        break;
        case 1:
            result = vec3(q,value,p);
        break;
        case 2:
            result = vec3(p,value,t);
        break;
        case 3:
            result = vec3(p,q,value);
        break;
        case 4:
            result = vec3(t,p,value);
        break;
        default:
            result = vec3(value,p,q);
        break;
    }
    return result;
}
void main(void)
{
    vec4 srcColor;
    vec3 hsvColor;
    vec3 rgbColor;
    texel = Texture2D(tex, tcoordIn);
    srcColor = texel*colorIn;
    hsvColor = RGBtoHSV(srcColor.rgb);
    /* You can do further changes here, if you want. */
    hsvColor.g = 0; /* Set saturation to zero */
    rgbColor = HSVtoRGB(hsvColor);
    pixel = vec4(rgbColor.r, rgbColor.g, rgbColor.b, srcColor.a);
}
person Community    schedule 15.05.2009
comment
v не определен. Глядя на другие алгоритмы для hsv2rgb, кажется, что это действительно должно быть значение. Кроме того, последний оператор case (case 5) также должен быть случаем по умолчанию. - person Tim Kane; 15.08.2015
comment
Есть ли способ преобразовать эффект серой шкалы в эффект эскиза в openGL? - person Devganiya Hitesh; 20.11.2018

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

person unwind    schedule 15.05.2009

Для большинства рабочих столов Render-To-Texture уже не так уж и дорог, от этого зависят все compiz, aero и т. Д., А также эффекты, такие как цветение или глубина резкости в последних играх.

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

Другой вариант - иметь два набора фрагментных шейдеров для ваших треугольников: один просто копирует атрибут gl_FrontColor, как это сделала бы фиксированная функция pieline, а другой записывает значения оттенков серого в экранный буфер.

Третьим вариантом могут быть индексированные цветовые режимы, если вы установите uüp палитру оттенков серого, но этот режим может быть устаревшим и плохо поддерживается в настоящее время; плюс вы теряете много функциональности, например, смешивание, если я правильно помню.

person heeen    schedule 15.05.2009
comment
да, никакого смешивания с режимом colorindex. что ты имеешь в виду под compiz, aero и т. д.? - person zyndor; 15.05.2009
comment
Он имеет в виду, что средства визуализации окон рабочего стола в настоящее время часто используют рендеринг в текстуру (он, вероятно, программист оконного менеджера). - person Triang3l; 27.07.2013