10

私は現在、C++ と OpenGL で小さなプロジェクトに取り組んでおり、以下のように、Photoshop に似た色選択ツールを実装しようとしています。

ここに画像の説明を入力

ただし、大きな正方形の補間に問題があります。私のデスクトップ コンピューターで 8800 GTS を使用した場合、結果は同様でしたが、ブレンドはそれほどスムーズではありませんでした。

これは私が使用しているコードです:

GLfloat swatch[] = { 0,0,0, 1,1,1, mR,mG,mB, 0,0,0 };
GLint swatchVert[] = { 400,700, 400,500, 600,500, 600,700 };

glVertexPointer(2, GL_INT, 0, swatchVert);
glColorPointer(3, GL_FLOAT, 0, swatch);
glDrawArrays(GL_QUADS, 0, 4);

Intel Graphics HD 3000 を搭載したラップトップに移ると、この結果はコードを変更しないとさらに悪化しました。

http://i.imgur.com/wSJI2.png

四角形を 2 つの三角形に分割する OpenGL だと思ったので、三角形を使用してレンダリングし、正方形の真ん中の色を自分で補間しようとしましたが、期待した結果とはまだ一致しません。

ここに画像の説明を入力

4

3 に答える 3

14

OpenGL は、頂点シェーダーからフラグメント シェーダーのフラグメントごとの入力値に出力される値に対して重心補間を使用します。そこに表示されているのは、すでに正しく推測したように、クワッドを三角形に分割した効果です。

それを見てみましょう: 赤が入った右上の三角形があり、赤の量でうまく補間されています。そして、左下の三角形にはグレーのみが含まれています。もちろん、右上の三角形の赤は、左下の三角形の灰色には寄与しません。

問題は、補間が RGB 空間で発生することですが、目的の結果を得るには、H (色相) が一定に保たれる HSV または HSL 空間に配置する必要があります。

しかし、それは単なる補間の問題ではありません。同様に邪魔になるのは、色が線形に補間されないことです。ほとんどのディスプレイには、「ガンマ」とも呼ばれる非線形関数が適用されています (実際には、ガンマは入力値に適用される電力の指数です)。

OpenGL コンテキストは、色補正された色空間にある場合とない場合があります。これをテストする必要があります。次に、フレーム バッファが存在するカラー スペースがわかれば、重心座標 (頂点シェーダを使用して評価) のフラグメントごとの変換を、フラグメント シェーダを使用してフラグメントごとの値に適用する必要があります。

于 2012-11-03T16:59:25.597 に答える
11

色を正常に補間する頂点/フラグメント シェーダーをすばやく作成しました。

#ifdef GL_ES
precision highp float;
#endif

uniform vec2 resolution;

void main(void)
{
    vec2 p = gl_FragCoord.xy / resolution.xy;
    float gray = 1.0 - p.x;
    float red = p.y;
    gl_FragColor = vec4(red, gray*red, gray*red, 1.0);
}

結果は次のとおりです。 ここに画像の説明を入力

xこれをクワッドに適用すると、正しい結果が得られるようになりました。これは、補間がおよびy座標を使用してサーフェス全体で実際に行われるためです。これが機能する理由については、@datenwolf の詳細な説明を参照してください。

EDIT 1機能的なカラー ピッカーで全範囲の色を取得するために、色相をインタラクティブに変更することができます ( https://stackoverflow.com/a/9234854/570738を参照)。

ライブ オンライン デモ: http://goo.gl/Ivirl

#ifdef GL_ES
precision highp float;
#endif

uniform float time;
uniform vec2 resolution;

const vec4  kRGBToYPrime = vec4 (0.299, 0.587, 0.114, 0.0);
const vec4  kRGBToI     = vec4 (0.596, -0.275, -0.321, 0.0);
const vec4  kRGBToQ     = vec4 (0.212, -0.523, 0.311, 0.0);

const vec4  kYIQToR   = vec4 (1.0, 0.956, 0.621, 0.0);
const vec4  kYIQToG   = vec4 (1.0, -0.272, -0.647, 0.0);
const vec4  kYIQToB   = vec4 (1.0, -1.107, 1.704, 0.0);

const float PI = 3.14159265358979323846264;

void adjustHue(inout vec4 color, float hueAdjust) {
    // Convert to YIQ
    float   YPrime  = dot (color, kRGBToYPrime);
    float   I      = dot (color, kRGBToI);
    float   Q      = dot (color, kRGBToQ);

    // Calculate the hue and chroma
    float   hue     = atan (Q, I);
    float   chroma  = sqrt (I * I + Q * Q);

    // Make the user's adjustments
    hue += hueAdjust;

    // Convert back to YIQ
    Q = chroma * sin (hue);
    I = chroma * cos (hue);

    // Convert back to RGB
    vec4 yIQ   = vec4 (YPrime, I, Q, 0.0);
    color.r = dot (yIQ, kYIQToR);
    color.g = dot (yIQ, kYIQToG);
    color.b = dot (yIQ, kYIQToB);
}

void main(void)
{
    vec2 p = gl_FragCoord.xy / resolution.xy;
    float gray = 1.0 - p.x;
    float red = p.y;
    vec4 color = vec4(red, gray*red, gray*red, 1.0);
    adjustHue(color, mod(time, 2.0*PI));
    gl_FragColor = color;
}

EDIT 2 : 必要に応じて、テクスチャ座標で使用するシェーダー (テクスチャ座標が 0 から 1 のクワッドに適用するため) は、次のようになります。未テスト。

フラグメント シェーダー:

void main(void)
{
    vec2 p = gl_TexCoord[0].st;
    float gray = 1.0 - p.x;
    float red = p.y;
    gl_FragColor = vec4(red, gray*red, gray*red, 1.0);
}

パススルー頂点シェーダー:

void main()
{
    gl_TexCoord[0]=gl_MultiTexCoord0; 
    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
于 2012-11-03T16:58:21.730 に答える
3

投票数が最も多い回答は文字通りであり、単純な回答では赤にのみ焦点を当てているため、気に入らなかったため、頂点の色に基づいた単純な代替案を用意したかったのです。

#version 330 core

smooth in vec4 color;
smooth in vec2 uv;

out vec4 colorResult;

void main(){
    float uvX = 1.0 - uv.st.x;
    float uvY = uv.st.y;

    vec4 white = vec4(1, 1, 1, 1);
    vec4 black = vec4(0, 0, 0, 1);

    colorResult = mix(mix(color, white, uvX), black, uvY);
}

これは説明と推論が簡単です。mix は線形補間、または glsl の lerp です。そのため、頂点の色と逆の x 軸に沿って白を混ぜます。これにより、左上に白、右上に純粋な色が表示されます。その結果、y 軸に沿って黒に混ざり、底が黒くなります。

これが役に立てば幸いです。私はこの回答に出くわし、ハードベイクされた赤、緑、または青のパレット以外のものを取得するのにあまり役に立たないことがわかりました。

基本的にこれを機能させるには、すべての頂点の色を目的の色に設定し、UV 座標が設定されていることを確認します。

TL: UV(0, 0), COLOR(YOUR DESIRED COLOR)
BL: UV(0, 1), COLOR(YOUR DESIRED COLOR)
BR: UV(1, 1), COLOR(YOUR DESIRED COLOR)
TR: UV(1, 0), COLOR(YOUR DESIRED COLOR)
于 2015-04-18T13:02:45.583 に答える