Как реализовать рендеринг в оттенках серого в OpenGL? - PullRequest
5 голосов
/ 15 мая 2009

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

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

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

Ответы [ 3 ]

9 голосов
/ 15 мая 2009

Стандартный кадровый буфер 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);
}
2 голосов
/ 15 мая 2009

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

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

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

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

2 голосов
/ 15 мая 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...