2016-10-28 21 views
5

画像処理用のマルチパス計算シェーダを実装しようとしています。 各パスに入力イメージと出力イメージがあります。 次のパスの '入力イメージは前のものです'出力です。OpenGL計算シェーダ - 奇妙な結果

私はOpenGLで計算シェーダを使用しているため、セットアップに問題が発生することがあります。 私はOpenCVのMatをコンテナとして使用して、読み取り/コピー操作を行っています。

問題に関連していないコードの一部が含まれていますが、これは含まれていません。これらの部分の中には、画像のロードやコンテキストの初期化などがあります。

初期化:

//texture init 
glGenTextures(1, &feedbackTexture_); 
glActiveTexture(GL_TEXTURE0); 
glBindTexture(GL_TEXTURE_2D, feedbackTexture_); 
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 
glBindTexture(GL_TEXTURE_2D, 0); 

glGenTextures(1, &resultTexture_); 
glActiveTexture(GL_TEXTURE0+1); 
glBindTexture(GL_TEXTURE_2D, resultTexture_); 
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 
glBindTexture(GL_TEXTURE_2D, 0); 

// shader init 
computeShaderID = glCreateShader(GL_COMPUTE_SHADER); 
glShaderSource(computeShaderID, 1, &computeShaderSourcePtr, &computeShaderLength); 
glCompileShader(computeShaderID); 
programID = glCreateProgram(); 
glAttachShader(programID, computeShaderID); 
glLinkProgram(programID); 
glDeleteShader(computeShaderID); 

シェーダコード:

//shader code (simple invert) 
#version 430 
layout (local_size_x = 1, local_size_y = 1) in; 

layout (location = 0, binding = 0, /*format*/ rgba32f) uniform readonly image2D inImage; 
layout (location = 1, binding = 1, /*format*/ rgba32f) uniform writeonly image2D resultImage; 

uniform writeonly image2D image; 

void main() 
{ 
    // Acquire the coordinates to the texel we are to process. 
    ivec2 texelCoords = ivec2(gl_GlobalInvocationID.xy); 

    // Read the pixel from the first texture. 
    vec4 pixel = imageLoad(inImage, texelCoords); 

    pixel.rgb = 1. - pixel.rgb; 

    imageStore(resultImage, texelCoords, pixel); 
} 

使用法:

cv::Mat image = loadImage().clone(); 
cv::Mat result(image.rows,image.cols,image.type()); 
// These get the appropriate enums used by glTexImage2D 
GLenum internalformat = GLUtils::getMatOpenGLImageFormat(image); 
GLenum format = GLUtils::getMatOpenGLFormat(image); 
GLenum type = GLUtils::getMatOpenGLType(image); 

int dispatchX = 1; 
int dispatchY = 1; 

for (int i = 0; i < shaderPasses_.size(); ++i) 
{ 
    // Update textures 
    glBindTexture(GL_TEXTURE_2D, feedbackTexture_); 
    glTexImage2D(GL_TEXTURE_2D, 0, internalformat, result.cols, result.rows, 0, format, type, result.data); 
    glBindTexture(GL_TEXTURE_2D, resultTexture_); 
    glTexImage2D(GL_TEXTURE_2D, 0, internalformat, image.cols, image.rows, 0, format, type, 0); 
    glBindTexture(GL_TEXTURE_2D, 0); 

    glClear(GL_COLOR_BUFFER_BIT); 
    std::shared_ptr<Shader> shaderPtr = shaderPasses_[i]; 
    // Enable shader 
    shaderPtr->enable(); 
    { 
     // Bind textures 
     // location = 0, binding = 0 
     glUniform1i(0,0); 
     // binding = 0 
     glBindImageTexture(0, feedbackTexture_, 0, GL_FALSE, 0, GL_READ_ONLY, internalformat); 
     // location = 1, binding = 1 
     glUniform1i(1,1); 
     // binding = 1 
     glBindImageTexture(1, resultTexture_, 0, GL_FALSE, 0, GL_WRITE_ONLY, internalformat); 

     // Dispatch rendering 
     glDispatchCompute((GLuint)image.cols/dispatchX,(GLuint)image.rows/dispatchY,1); 
     // Barrier will synchronize 
     glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT); 
    } 
    // disable shader 
    shaderPtr->disable(); 

    // Here result is now the result of the last pass. 
} 

時々私はまた奇妙な結果(グリッチテクスチャ、部分的にレンダリングされたテクスチャ)、最初の画素を得ます(0,0)が書き込まれないことがあります。 すべてを正しく設定しましたか、何か不足していますか? テクスチャを使ったこの方法は実際には遅いようですが、パフォーマンスを向上させる方法はありますか?

Edit1:メモリバリアフラグが変更されました。

答えて

3

私はこの問題を最終的に解決することができました!

問題はcv :: Matのコンストラクタで嘘でした。 次の行にのみCV ::マットのヘッダーを作成します。

cv::Mat result(image.rows,image.cols,image.type()); 

それは、データを割り当てるが、それはではないが、私はこれらの奇妙な結果を得た理由は、データという、それはだ初期化していません。それは記憶のごみでした。 を割り当て任意の関数を使用して

は、このデータが問題を解決し初期化します

cv::Mat::zeros 
cv::Mat::ones 
cv::Mat::create 
+1

厳密に言えば、最初の 'cv :: Mat result(image.rows、image.cols、image.type());'は 'cv :: Mat :: create'と同じイメージバッファを_allocate_する必要がありますが、ピクセル値を初期化しません。 'cv :: Mat :: zeros'と' cv :: Mat :: ones'は両方ともピクセル値を割り当て、初期化します。 – AldurDisciple

+0

はい、私の答えを更新します。 –

5
glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); 

これは間違った障壁です。障壁はhow you intend to access the data after the incoherent accessesを指定します。 glGetTexImageでテクスチャを読み込もうとする場合は、GL_TEXTURE_UPDATE_BARRIER_BITを使用する必要があります。

+0

ああ、私は、明確にするためのおかげでその部分を逃しました。また、アップロード/ダウンロード部分の速度を上げる方法はありますか?私の画像はプログラムによってサイズが変わることはめったになく、約30 fpsで来ます。 –

+0

さて、フラグの変更は何も起こらなかったようです。最初のピクセルはまだちらつきがあり、時には奇妙なアーティファクトが画像全体に表示されます。 –

3

これで問題が解決するかどうかはわかりませんが、テクスチャの設定を初期化するためのフラグが間違っているとは思われません。あなたのコードを私のプロジェクトと比較するとき、それは私の注意を引いたAPIコールの順序でした。あなたのソースでは、この順序があります。

glGenTextures(...); // Generate 
glActiveTexture(...); // Set Active 
glBindTexture(...); // Bind Texture 
glTexParameteri(...); // Wrap Setting 
glTexParameteri(...); // Wrap Setting 
glTexParameteri(...); // Mipmap Setting 
glTexParameteri(...); // Mipmap Setting 
glBindTexture(...); // Bind/Unbind 

を、あなたはテクスチャ変数の受け渡しとid値の上昇を除いて各テクスチャのためにこれを繰り返します。

私はそれが私のエンジンと私が設定した論理的な経路に従うことで違いを生むかどうか分かりません。このためにそれをやって試してみて、それが何の違い

glGenTextures(...); // Generate 
glBindTexture(...); // Bind Texture 
glTexParameteri(...); // Wrap Setting 
glTexParameteri(...); // Wrap Setting 
glTexParameteri(...); // Mipmap Setting 
glTexParameteri(...); // Mipmap Setting 

glActiveTexture(...); // Set Active 
glBindTexture(...); // Bind/Unbind 

を行う場合、私は計算シェーダを使用していないんだけど、私のエンジンの中に、私は別のものを管理していくつかのクラスを持っている参照してください。私はすべてのアセットを画像のテクスチャを含むメモリデータベースに保存するAsset Storageを持っていますが、現在は頂点シェーダとフラグメントシェーダを使用している異なるシェーダを管理するShaderManagerクラスがあります。シェーダファイルを読み込んでコンパイルし、シェーダプログラムを作成し、属性とユニフォームを設定し、プログラムをリンクし、シェーダを実行します。私はバッチクラスとバッチマネージャクラスを持っていて、異なるタイプのプリミティブをレンダリングするバッチプロセスを使用しています。だから、私が私のソリューションを見て、パスやロジックの流れに従っていたとき、これが私のコードで見ていたものです。

テクスチャのプロパティを設定していたのはAssetStorageクラスで、メモリにテクスチャを追加する関数のadd()関数内で、この順番でこれらのAPI呼び出しを呼び出していました。

glGenTextures(...); 
glBindTextures(...); 
glTexParameteri(...); 
glTexParameteri(...); 
glTexParameteri(...); 
glTexParameteri(...); 

その後AssetStorageは

glPixelStorei(...); 
glTexImage2D(...) 

、同様にこれらを呼んでいたとAssetStorageにテクスチャを追加する機能は、最終的にはTextureInfoオブジェクトのカスタム構造を返します。

render()ファンクションコールでバッチクラスを確認したとき、ShaderManagerの関数を呼び出して、テクスチャを使用するようにユニフォームを設定してから、ShaderManagerの関数を呼び出してテクスチャを設定してから、テクスチャにはアルファチャンネルが含まれていたsetTexture()関数のShaderMangerクラス内では、これはglActiveTexture()glBindTexture()が最後に呼び出された場所です。

短い要約では、glActiveTexture()コールを最後のglTexParameter()と最後のglBindTexture()の間に置くようにしてください。両方のテクスチャが呼び出されます。私はそれもあなたがそれをレンダリングしようとしているようにテクスチャをアクティブにしたいという事実のために、これらの2つの呼び出しの後にも来るべきだと思っています。glPixelStorei() & glTexImage2D()

以前に言及したように、これはあなたの問題の根本的な原因であるかどうかは100%確信していませんが、それがあなたを助けてくれるかどうかを見てみる価値はあると思います。これを試してみると何が起こるか教えてください。私は、これらのAPI呼び出しの順序がそれに何らかの影響を与えるかどうかを知りたいと思います。私は自分の解決策でそれを試してみたいと思いますが、クラスやプロジェクトを中断したくないので、現在は適切に機能しています。

テクスチャ設定のフラグが付いているのは、ラップ/リピートのセクションだけです。代わりにGL_CLAMP_TO_EDGEを使用する代わりに、最初の2回のglTexParameteri()コールにGL_REPEATを使用して試してみることができます。あなたが出てくることを教えてください。最後の2回のglTexParameteri()コールのミップマップ設定について心配する必要はありません使用している設定からミップマップを使用します。

+1

時間をかけて問題を解決してくれてありがとう。私はずっと前に解決策を得ました。答えは実際にはgl呼び出しやテクスチャパラメータの順序ではなく、OpenCVのMatコンストラクタです。私はまもなく答えを出します。 –

+0

@GáborFeketeああ、ええ、外部ソースやサードパーティのライブラリからエラーが発生する可能性は常にあります。 –

+0

opencvはopenglよりもデバッグが簡単です。あなたが言及した 'glActiveTexture'といくつかのフラグを除いて、openglの状態は大丈夫でした。そして、私は図書館の1つに頼って間違いを作った... 絶対信用しないで! :D –