0

特定の状況では、ユーザーが画像をズームインしているときに、最も短いエッジが画面全体を占めるまでテクスチャを拡張したい場合があります (標準のギャラリーで見られるように)。ただし、OpenGL シェーダーへの入力は ImageView からのマトリックスであるため、特定の変換を配置するだけでなく、さまざまな空間の補正計算を行う必要があります。

編集:

頂点シェーダー コードは単純化されており、次のようになります。

private final String vertexShader_ =
        "attribute vec4 a_position;\n" +
        "attribute vec4 a_texCoord;\n" +
        "varying vec2 v_texCoord;\n" +
        "void main() {\n" +
        "  gl_Position = a_position;\n" +
        "  v_texCoord = a_texCoord.xy;\n" +
        "}\n";

ここで、a_position は以下の verticesData_ を参照します。画面にロードするとき、画像の位置は、ディスプレイの幅と占有する画面の部分からの計算に基づいて正確になります。

さらに、次のフラグメント シェーダー コードがあります。

private final String fragmentShader_ =
        "precision mediump float;\n" +
        "varying vec2 v_texCoord;\n" +
        "uniform sampler2D texture;\n" +
        "uniform mat3 transform;\n" +
        "void main() {\n" +
        "  vec2 uv = (transform * vec3(v_texCoord, 1.0)).xy;\n" +
        "  gl_FragColor = texture2D( texture, uv );\n" +
        "}\n";

ここで、mat3 変換の入力は、ImageView からの入力です。基本的に、OpenGLSurfaceView の下に ImageView があります。これははるかに低い解像度の画像であり、ユーザーがスワイプすると、SurfaceView が非表示になり、ユーザーが SurfaceView にいたのと同じ位置で ImageView がその下に表示されます。

ただし、後でこのテクスチャを拡張したい場合、予期しない結果になることに気付きます。

前後にパンすると、画面が移動すると予想される場所に移動します。したがって、マトリックスからの x 座標と y 座標の変換は問題ありません。ただし、ズームインすると、画像が破れます。テクスチャの境界が画面のサイズを超えて大きくなった場合にのみ、ティアリングが発生します。以下に見られるように、高さは成長するときに余分なピクセルを導入していませんが、幅は進行するにつれて引き裂かれています.

適切な行列を渡すために、ImageView から OpenGL SurfaceView への値が反転され、転置されました。これは、適切な変換のためにスペースが必要だったためです。倍率は、アクティビティがリッスンしているリスナーに渡されます。

@Override
public void onTranslation(float imageScaleF, float[] matrixTranslationF)
{   
    currentScaleForImage_ = imageScaleF;
    //If we are currently using the entire screen, then there is not need anymore to resize the texture itself.
    if(surfaceViewWidth_ * imageScaleF > displayViewWidth_ && surfaceViewHeight_ * imageScaleF > displayViewHeight_)
    {

    }
    else //Expand the size of the texture to be displayed on the screen.
    {
        maximumScaleForImage_ = imageScaleF;
        photoViewRenderer_.updateTextureSize(imageScaleF);
        photoSurfaceView_.requestRender();
    }
    matrixTranslationF = updateTranslationBasedOnTextureSize(matrixTranslationF);
    photoViewRenderer_.updateTranslationOfTexture(matrixTranslationF);
    photoSurfaceView_.requestRender();
}

しかし、上記の次のコードでは、スクロールする画像の部分が常に短くなり、翻訳を更新するためにそこの行のコメントを外してスケールに基づいて修正しようとすると、涙が発生します。そのため、この時点で、以下のユーザーの入力により、私はより良い位置にいるように見えますが、私はまだ一歩離れており、この機能の範囲内にあると思います. 以下の更新されたコードは、ImageView の行列と OpenGL テクスチャ/座標との間の適切な変換を提供するために必要な修正を行いました。

private float[] updateTranslationBasedOnTextureSize(float[] matrixTranslationsF)
    { 
        if(scaleDirection_ == ScaleDirection.WIDTH)
        {
            matrixTranslationsF[0] = matrixTranslationsF[0] * maximumScaleForImage_;
        }
        else if(scaleDirection_ == ScaleDirection.HEIGHT)
        {
            matrixTranslationsF[4] = matrixTranslationsF[4] * maximumScaleForImage_;
        }

        return matrixTranslationsF;
    }

以下に示すように、最初の繰り返しで写真に裂け目が生じていました。または、2 つのスペース間の変換が不適切なため、写真が切り取られていました。

ユーザー インタラクションによるテクスチャ サイズの更新前 テクスチャサイズ更新後

幅は維持されているように見えるので、標準の範囲外の値で何かが起こる可能性があることを示唆していますが、何を試したらよいかわかりません。

4

1 に答える 1

2

このストリーキングは、テクスチャ ラップがエッジにクランプするように設定されているために発生しています。次のようなものがあります。

    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );

GL_REPEAT がデフォルトで、画像を繰り返します。しかし、あなたは一番下に「効果」がないことを望んでいます。この底はあなたの数学のために起こっています。横ではなく、底だけに筋が入っています。誰かがコメントで言ったように、頂点の範囲が正しくありません。最小値と最大値は 0 と 1 です。-1 と 1 ではありません。したがって、画像の下部では、0-1 境界からテクスチャ座標番号を計算し、おそらく <0 領域に入るため、縞模様になっています。または >1 エリア。texture2D(u_textureSampler, v_fragmentTexCoord0) が 0 ~ 1 の範囲外のテクスチャ座標を取得すると、その WRAP_S / WRAP_T 設定に当てはまり、与える値を知ることになります。GL_CLAMP_TO_EDGEがあるため、エッジピクセルを繰り返しています。

ただし、実際の問題はこの設定ではありません。そもそも範囲外の S または T を計算しているということです。そこに黒を書いていたとしても、フラグメントシェーダーで無駄なサイクルが発生し、アプリの速度が低下します。基本的に、あなたの式は間違っています。

数学のズームに関しては... それは数学です。何をしようとしても、それを解決する必要があります。(または、助けを求めてください。) 次のようなことができます。これは、私が新しい写真編集アプリで行っていることの単純化されたバージョンです。

    GLfloat aspect = photo.width / photo.height;
    if (aspect > 1) {
      displayWidth = panelWidth;
      displayHeight = panelWidth/aspect;
    }
    else {
      displayHeight = panelHeight;
      displayWidth = panelHeight*destAspect;
    }

ここで、panelWidth と panelHeight は、写真を表示する領域のサイズです。これは、全画面またはそれよりも小さいサイズです。次に、ディスプレイの高さと幅に基づいて頂点を構築します。パネルのサイズを超えてズームインする場合は、表示の幅または高さ (大きすぎる方) を縮小し、テクスチャ座標をマッチ。これは非常に大雑把ですが、次のようなものです。

GLfloat sSize = (destSMax-destSMin) / panelZoom;
GLfloat tSize = (destTMax-destTMin) / panelZoom;

displaySMin = destSMin + panelPanX/coreTextureSize;
displayTMin = destTMin + panelPanY/coreTextureSize;

displaySMax = displaySMin + sSize;
displayTMax = displayTMin + tSize;

次に、もちろん頂点データを作成し、行列演算を使用して正方形を必要な場所に移動し、描画します。

私が上でやっていることに似たものを使用するためのアプローチを再考することをお勧めします。写真が画面から消えても、写真の一部を削除しないでください。代わりに、何が表示されるかを計算し、それに基づいてデータを構築します。

編集 - また、自分のコードが ios コードであり、これは android の質問であることに気付きました。ただし、同じ概念が適用されます。glTexParameteri 呼び出しはわずかに異なる可能性があり、GLfloat ではなく float である可能性があります。

于 2013-10-06T10:55:05.067 に答える