OpenGL でグレースケール レンダリングを実装するにはどうすればよいですか?
質問
テクスチャ付きポリゴンのシーンをレンダリングするときに、元のカラーでのレンダリングと「グレースケール」モードを切り替えられるようにしたいと考えています。私はブレンディングとカラーマトリックスの操作を使用してこれを達成しようと試みてきました。どれも機能しませんでした(ブレンディングでは、私が望んでいたものとリモートで似ているものを実現する glBlendFunc() が見つかりませんでした。カラーマトリックス操作 ...ここで議論されています).
思い浮かぶ解決策 (ただし、かなり高価です) は、フレームごとに画面をキャプチャし、結果のテクスチャをグレースケールのテクスチャに変換して、代わりにそれを表示することです。(グレースケールと言ったのは、実際には彩度の低いものを指しますが、考えられる解決策のほとんどではグレースケールとそれほど変わらないと思います)。
他にどのような選択肢がありますか?
解決
デフォルトの 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 フラグメント シェーダを使用すると次のようになります。
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);
}
他のヒント
十分に最新の OpenGL を相手に作業している場合は、次のように言います。 ピクセルシェーダ ここでは非常に適切な解決策です。レンダリング時に各ポリゴンのシェーディングにフックするか、各ピクセルを読み取り、グレースケールに変換して書き戻すだけの 2 番目のパスで単一のフルスクリーン クワッドを実行するかのいずれかです。解像度、グラフィックス ハードウェア、およびターゲット フレームレートが何らかの形で「極端」でない限り、最近ではほとんどの場合に実行可能です。
ほとんどのデスクトップでは、Render-To-Texture はもうそれほど高価ではありません。compiz、aero など、および最近のタイトルで見られるブルームや被写界深度などのエフェクトはすべてこれに依存しています。
実際には、スクリーン テクスチャ自体をグレースケールに変換するのではなく、テクスチャと値をグレースケールに変換するフラグメント シェーダを使用して、スクリーン サイズのクワッドを描画する必要があります。
もう 1 つのオプションは、三角形に 2 セットのフラグメント シェーダを用意することです。1 つは固定関数パイラインのように gl_FrontColor 属性をコピーするだけで、もう 1 つはグレースケール値をスクリーン バッファに書き込むものです。
グレースケール パレットを設定する場合、3 番目のオプションはインデックス付きカラー モードになる可能性がありますが、そのモードは現在非推奨であり、サポートが不十分である可能性があります。さらに、私の記憶が正しければ、ブレンドなどの多くの機能が失われます。