3

フラグメントシェーダーのピクセル間にシャープなトランジション効果を作成したいのですが、どうすればよいかわかりません。

頂点シェーダーにはとがvarying float x;あり、フラグメントシェーダーでは、この値を使用して色の不透明度を設定します。現在の値を量子化して、レイヤー効果を生み出します。私がやりたいのは、非常に最小限のレベルの効果で、明確な境界線(完全に異なる色)を生成することです。たとえば、x>0.1隣接するピクセルx<0.1の場合、結果の色は黒になります。

GLSLには、隣接するピクセルにアクセスする方法がありません(間違っている可能性があります)。どうすればそのような効果を達成できますか。私はOpenGL-ES2.0に制限されています(ただし、このバージョンではまったく不可能な場合は、任意のソリューションが役立ちます)。

4

3 に答える 3

5

隣接するピクセルにアクセスできないのは正しいです。これは、ピクセルがどの順序で書き込まれるかは保証されておらず、すべて並列に描画されるためです。フレームバッファ内の隣接するピクセルにアクセスできる場合、一貫性のない結果が得られます。

ただし、必要に応じて、後処理でこれを行うことができます。シーン全体をフレームバッファテクスチャに描画してから、フィルタリングシェーダーを使用してそのテクスチャを画面に描画します。

シェーダーのテクスチャから描画する場合、隣接するテクセルを必要なだけサンプリングできるため、2つの隣接するテクセル間のデルタを簡単に比較できます。

于 2012-08-19T21:25:08.967 に答える
3

OpenGL ES実装がOES_standard_derivatives拡張機能をサポートしている場合、影付きの2×2クワッド内の隣接するピクセルとの前方/後方差分によって変数の変化率を取得できます。

float outline(float t, float threshold, float width)
{
    return clamp(width - abs(threshold - t) / fwidth(t), 0.0, 1.0);
}

この関数は、カットオフからの距離を決定するために使用して、指定された幅のラインのtカバレッジを返します。これは、対角線を太くする可能性のあるマンハッタン距離と同等であり、幅を計算することに注意してください。ユークリッド距離を好む場合:thresholdfwidthfwidth(t)abs(dFdx(t)) + abs(dFdy(t))

float outline(float t, float threshold, float width)
{
    float dx = dFdx(t);
    float dy = dFdy(t);
    float ewidth = sqrt(dx * dx + dy * dy);
    return clamp(width - abs(threshold - t) / ewidth, 0.0, 1.0);
}
于 2012-08-20T00:48:03.607 に答える
1

導関数に基づくピボットの実装に加えて、ソースのピクセル寸法に基づくオフセットを使用して、ソース画像から隣接するピクセルを取得できます。ピクセル単位の幅または高さの逆数は、ここで使用する必要がある現在のテクスチャ座標からのオフセットです。

たとえば、中央のピクセルを囲む8つのピクセルのこれらのオフセットを計算するために使用した頂点シェーダーは次のとおりです。

 attribute vec4 position;
 attribute vec4 inputTextureCoordinate;

 uniform highp float texelWidth; 
 uniform highp float texelHeight; 

 varying vec2 textureCoordinate;
 varying vec2 leftTextureCoordinate;
 varying vec2 rightTextureCoordinate;

 varying vec2 topTextureCoordinate;
 varying vec2 topLeftTextureCoordinate;
 varying vec2 topRightTextureCoordinate;

 varying vec2 bottomTextureCoordinate;
 varying vec2 bottomLeftTextureCoordinate;
 varying vec2 bottomRightTextureCoordinate;

 void main()
 {
     gl_Position = position;

     vec2 widthStep = vec2(texelWidth, 0.0);
     vec2 heightStep = vec2(0.0, texelHeight);
     vec2 widthHeightStep = vec2(texelWidth, texelHeight);
     vec2 widthNegativeHeightStep = vec2(texelWidth, -texelHeight);

     textureCoordinate = inputTextureCoordinate.xy;
     leftTextureCoordinate = inputTextureCoordinate.xy - widthStep;
     rightTextureCoordinate = inputTextureCoordinate.xy + widthStep;

     topTextureCoordinate = inputTextureCoordinate.xy - heightStep;
     topLeftTextureCoordinate = inputTextureCoordinate.xy - widthHeightStep;
     topRightTextureCoordinate = inputTextureCoordinate.xy + widthNegativeHeightStep;

     bottomTextureCoordinate = inputTextureCoordinate.xy + heightStep;
     bottomLeftTextureCoordinate = inputTextureCoordinate.xy - widthNegativeHeightStep;
     bottomRightTextureCoordinate = inputTextureCoordinate.xy + widthHeightStep;
 }

これを使用してSobelエッジ検出を実行するフラグメントシェーダーは次のとおりです。

 precision mediump float;

 varying vec2 textureCoordinate;
 varying vec2 leftTextureCoordinate;
 varying vec2 rightTextureCoordinate;

 varying vec2 topTextureCoordinate;
 varying vec2 topLeftTextureCoordinate;
 varying vec2 topRightTextureCoordinate;

 varying vec2 bottomTextureCoordinate;
 varying vec2 bottomLeftTextureCoordinate;
 varying vec2 bottomRightTextureCoordinate;

 uniform sampler2D inputImageTexture;

 void main()
 {
    float bottomLeftIntensity = texture2D(inputImageTexture, bottomLeftTextureCoordinate).r;
    float topRightIntensity = texture2D(inputImageTexture, topRightTextureCoordinate).r;
    float topLeftIntensity = texture2D(inputImageTexture, topLeftTextureCoordinate).r;
    float bottomRightIntensity = texture2D(inputImageTexture, bottomRightTextureCoordinate).r;
    float leftIntensity = texture2D(inputImageTexture, leftTextureCoordinate).r;
    float rightIntensity = texture2D(inputImageTexture, rightTextureCoordinate).r;
    float bottomIntensity = texture2D(inputImageTexture, bottomTextureCoordinate).r;
    float topIntensity = texture2D(inputImageTexture, topTextureCoordinate).r;
    float h = -topLeftIntensity - 2.0 * topIntensity - topRightIntensity + bottomLeftIntensity + 2.0 * bottomIntensity + bottomRightIntensity;
    float v = -bottomLeftIntensity - 2.0 * leftIntensity - topLeftIntensity + bottomRightIntensity + 2.0 * rightIntensity + topRightIntensity;

    float mag = length(vec2(h, v));

    gl_FragColor = vec4(vec3(mag), 1.0);
 }

画像の幅1/高さのユニフォームtexelWidthとユニフォームを渡します。texelHeightこれには、入力画像の幅と高さを追跡する必要がありますが、派生拡張機能を備えたデバイスだけでなく、すべてのOpenGLESデバイスで機能するはずです。

頂点シェーダーでテクスチャオフセットの計算を行う理由は2つあります。オフセット計算は、フラグメントごとに1回ではなく、頂点ごとに1回だけ実行する必要があります。さらに重要なのは、タイルベースの遅延レンダラーの一部が依存するテクスチャにほとんど反応しないためです。テクスチャオフセットがフラグメントシェーダーで計算される場所を読み取ります。これらのデバイスでこれらの依存するテクスチャ読み取りを削除するシェーダープログラムのパフォーマンスは、最大20倍高くなる可能性があります。

于 2012-08-20T17:37:20.693 に答える