フラグメントシェーダーでテクスチャの平均輝度を見つける方法を知っている人はいますか? RGB テクスチャと YUV テクスチャの両方にアクセスできます。YUV の Y コンポーネントは配列であり、この配列から平均値を取得したいと考えています。
3 に答える
私は最近、OpenGL ES テクスチャとして持っていた入力画像とビデオ フレームに対して、自分でこれを行う必要がありました。2 の累乗でないテクスチャを使用していたため、これらのミップマップを生成しませんでした。また、iOS の OpenGL ES 2.0 では NPOT テクスチャのミップマップを生成できません。
代わりに、ミップマップ生成に似た多段階削減を行いましたが、若干の微調整を行いました。ステップダウンするたびに、画像のサイズは、ミップマップに使用される通常の係数 2 ではなく、幅と高さの両方で 4 分の 1 に縮小されました。これは、上位レベルの画像で 4x4 の領域を構成する 4 つのピクセルからなる 4 つの正方形の中央にある 4 つのテクスチャ位置からサンプリングすることによって行いました。これは、ハードウェア テクスチャ補間を利用して 4 つのピクセルの 4 つのセットを平均化します。次に、これらの 4 つのピクセルを平均化して、1 つのステップでピクセルを 16 分の 1 に削減する必要がありました。
最初の段階で、RGB 値と vec3 (0.2125, 0.7154, 0.0721) の内積を使用して、画像を輝度に変換しました。これにより、後続の各リダクション ステージのレッド チャネルを読み取るだけで済み、iOS ハードウェアで非常に役立ちます。Y チャンネルの輝度テクスチャを既に使用している場合、これは必要ないことに注意してください。しかし、私は RGB 画像を扱っていました。
画像が十分に小さいサイズに縮小されたら、そこからピクセルを CPU に読み込んで、残りのいくつかのピクセルに対して最後の反復処理を行い、最終的な明度値に到達しました。
640x480 のビデオ フレームの場合、このプロセスは iPhone 4 で約 6 ミリ秒で明度値を生成します。少し調整することで、その処理時間を 1 ~ 2 ミリ秒短縮できると思います。私の経験では、iOS デバイスが通常そのサイズの 2 のべき乗の画像のミップマップを生成するよりも高速に思えますが、それを裏付ける確かな数値はありません。
この動作を確認したい場合は、私のオープン ソースGPUImageフレームワーク (および GPUImageAverageColor スーパークラス) の GPUImageLuminosity クラスのコードを確認してください。FilterShowcase の例は、この輝度抽出器の動作を示しています。
通常、これはシェーダーだけでは行いません。
より一般的な方法の1つは、完全なミップマップを使用してバッファテクスチャを作成することです(1x1まで、これは重要です)。光度を調べたい場合は、バックバッファーをこのバッファーにコピーしてから、最近傍アルゴリズムを使用してmipを再生成します。下のピクセルは表面全体の平均色を持ち、次のようなものを通して平均的なlumを見つけるために使用できます(c.r * 0.6) + (c.g * 0.3) + (c.b * 0.1)
(編集:YUVがある場合は、同様の操作を行い、Yを使用します。トリックはテクスチャを平均化するだけです。 mipsが行う単一の値)。
これは正確な手法ではありませんが、特に内部でミップマップを生成できるハードウェアでは、かなり高速です。
ミップ マップの生成が YUV テクスチャで機能するかどうかわからないため、ここでは RGB テクスチャのソリューションを提示します。
最初のステップは、まだ存在しない場合、テクスチャのミップマップを作成することです。
glGenerateMipmapOES(GL_TEXTURE_2D);
texture2D
これで、サンプラー関数のオプションの 3 番目の引数である「バイアス」を使用して、フラグメント シェーダーから最小ミップマップ レベルの RGB 値にアクセスできます。
vec4 color = texture2D(sampler, vec2(0.5, 0.5), 8.0);
これにより、ミップマップ レベルが 8 レベル上にシフトされ、はるかに小さいレベルでサンプリングされます。
256x256 のテクスチャがあり、スケール 1 でレンダリングする場合、8.0 のバイアスは、選択されたミップマップを効果的に最小の 1x1 レベル (256 / 2^8 == 1) に減らします。もちろん、最小レベルをサンプリングするには、条件のバイアスを調整する必要があります。
OK、これで画像全体の平均 RGB 値が得られました。3 番目のステップは、RGB を明度に下げることです。
float lum = dot(vec3(0.30, 0.59, 0.11), color.xyz);
内積は、加重合計を計算するための単なる凝った (そして高速な) 方法です。