5

テクスチャ ポリゴンのシーンをレンダリングするとき、元の色と「グレースケール」モードでのレンダリングを切り替えられるようにしたいと考えています。ブレンディングとカラーマトリックス操作を使用してこれを達成しようとしています。どれも機能しませんでした(ブレンディングでは、私が望んでいたものにリモートで似ている何かを達成した glBlendFunc() を見つけることができませんでした.カラーマトリックス操作については、ここで説明しています)。

頭に浮かぶ解決策 (ただし、かなり高価です) は、フレームごとに画面をキャプチャし、結果のテクスチャをグレースケールのものに変換して、代わりに表示することです...しかし、考えられる解決策のほとんどでは、グレースケールとそれほど変わらないと思います)。

他にどのようなオプションがありますか?

4

3 に答える 3

9

デフォルトの OpenGL フレームバッファは、明示的な彩度を保存しない RGB 色空間を使用します。彩度を抽出して変更し、元に戻すためのアプローチが必要です。

単純に RGB ベクトルの長さを使用して輝度の 0 を表すという以前の提案は、スケーリングが考慮されていなかったため、正しくありませんでした。申し訳ありません。

新しい短いスニペットの功績は、FreeNode/IRC の ##opengl および ##opengl3 からの通常のユーザー「RTFM_FTW」に与えられ、コストのかかる 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 フラグメント シェーダーを使用して以下に示すように。

GLSL v1.30 機能の使用方法については、 http: //www.opengl.org/registryで入手できる OpenGL 3.0 および GLSL 1.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);
}
于 2009-05-15T16:29:22.447 に答える
2

最新の十分な OpenGL に対して作業している場合、ここではピクセル シェーダーが非常に適したソリューションであると言えます。レンダリング時に各ポリゴンのシェーディングにフックするか、各ピクセルを読み取り、グレースケールに変換して書き戻す 2 番目のパスで単一のフルスクリーン クワッドを実行します。解像度、グラフィックス ハードウェア、ターゲット フレームレートが何らかの形で「極端」でない限り、ほとんどの場合、最近では実行できるはずです。

于 2009-05-15T09:03:46.417 に答える
2

ほとんどのデスクトップでは、Render-To-Texture はもはやそれほど高価ではありません。compiz、aero などのすべてと、最近のタイトルで見られるブルームや被写界深度などの効果はそれに依存しています。

実際には、スクリーン テクスチャ自体をグレースケールに変換するのではなく、テクスチャと値をグレースケールに変換するフラグメント シェーダを使用して、スクリーン サイズのクワッドを描画する必要があります。

もう 1 つのオプションは、三角形に 2 セットのフラグメント シェーダーを用意することです。1 つは固定関数の pipeline のように gl_FrontColor 属性をコピーするだけで、もう 1 つはグレースケール値を画面バッファーに書き込みます。

3 番目のオプションは、グレースケール パレットを設定した場合のインデックス カラー モードかもしれませんが、そのモードは推奨されておらず、現在ではサポートが不十分である可能性があります。さらに、私の記憶が正しければ、ブレンドなどの多くの機能が失われます。

于 2009-05-15T09:06:50.840 に答える