как да внедря изобразяване в сивата скала в OpenGL?

Когато изобразявам сцена от текстурирани полигони, бих искал да мога да превключвам между изобразяване в оригиналните цветове и режим "в сивата скала". Опитвах се да постигна това, използвайки операции за смесване и цветна матрица; нищо от това не работи (със смесване не можах да намеря glBlendFunc(), който постигна нещо, отдалечено наподобяващо това, което исках, и операции с цветна матрица ...се обсъждат тук).

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

Какви други опции имам?


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


Отговори (3)


OpenGL framebuffer по подразбиране използва цветовото пространство 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, изглежда, че това всъщност трябва да е стойност. Също така крайният оператор за случай (случай 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, а друг записва стойности на сивото в буфера на екрана.

Трета опция може да са индексирани цветови режими, ако зададете палитра в сивата скала, но този режим може да е отхвърлен и слабо поддържан досега; освен това губите много функционалности като смесване, ако си спомням правилно.

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