web-dev-qa-db-ja.com

C / C ++のOpenGLシェーダー用の簡単なフレームワーク

フラットイメージでいくつかのシェーダーを試してみたかっただけです。単にテクスチャとして写真を撮って適用するCプログラムを書くこと、ガウスぼかしを言うことがわかります。その上のフラグメントシェーダーはそれほど簡単ではないためです。100行のコードのようなOpenGLを初期化する必要があります。 GLBuffersなどを理解する。また、ウィンドウシステムと通信するには、別のフレームワークであるGLUTを使用する必要があります。

NvidiaのFx composerは、シェーダーで操作するのに最適です。しかし、特定のフラグメントシェーダーを画像に適用して結果を表示する単純なCまたはC++プログラムが欲しい。誰かに例がありますか、それともフレームワークはありますか?

31
Nils

まず第一に、過食症の使用は避けます-バギーであり、およそ10年間更新されておらず、そのデザインは、ほとんどの人が今日望んでいるもの(例えば、あなたcanアニメーションに使用できます。これは、主に静的な表示を生成することを目的としています)。 以前の回答 で、過剰に代替する方法をいくつか指摘しました。

これにより、(ほとんど)コードをコンパイル、リンク、およびシェーダーを使用することができます。私はこの目的に便利な小さなクラスを書きました:

_class shader_prog {
    GLuint vertex_shader, fragment_shader, prog;

    template <int N>
    GLuint compile(GLuint type, char const *(&source)[N]) {
        GLuint shader = glCreateShader(type);
        glShaderSource(shader, N, source, NULL);
        glCompileShader(shader);
        GLint compiled;
        glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
        if (!compiled) {
            GLint length;
            glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length);
            std::string log(length, ' ');
            glGetShaderInfoLog(shader, length, &length, &log[0]);
            throw std::logic_error(log);
            return false;
        }
        return shader;
    }
public:
    template <int N, int M>
    shader_prog(GLchar const *(&v_source)[N], GLchar const *(&f_source)[M]) {
        vertex_shader = compile(GL_VERTEX_SHADER, v_source);
        fragment_shader = compile(GL_FRAGMENT_SHADER, f_source);
        prog = glCreateProgram();
        glAttachShader(prog, vertex_shader);
        glAttachShader(prog, fragment_shader);
        glLinkProgram(prog);
    }

    operator GLuint() { return prog; }
    void operator()() { glUseProgram(prog); }

    ~shader_prog() {
        glDeleteProgram(prog);
        glDeleteShader(vertex_shader);
        glDeleteShader(fragment_shader);
    }
};
_

簡単なデモの場合、いくつかの「パススルー」シェーダー(固定機能パイプラインを模倣するだけ):

_const GLchar *vertex_shader[] = {
    "void main(void) {\n",
    "    gl_Position = ftransform();\n",
    "    gl_FrontColor = gl_Color;\n",
    "}"
};

const GLchar *color_shader[] = {
    "void main() {\n",
    "    gl_FragColor = gl_Color;\n",
    "}"
};
_

次のようなものを使用します:

_void draw() { 
    // compile and link the specified shaders:
    static shader_prog prog(vertex_shader, color_shader);

    // Use the compiled shaders:    
    prog(); 

    // Draw something:
    glBegin(GL_TRIANGLES);
        glColor3f(0.0f, 0.0f, 1.0f);
        glVertex3f(-1.0f, 0.0f, -1.0f);
        glColor3f(0.0f, 1.0f, 0.0f);
        glVertex3f(1.0f, 0.0f, -1.0f);
        glColor3f(1.0f, 0.0f, 0.0f);
        glVertex3d(0.0, -1.0, -1.0);
    glEnd();
}
_

たとえば、シーンを描画する過程でいくつかの異なるフラグメントシェーダーを使用する場合は、それぞれに静的オブジェクトを定義してから、prog1();prog2();、など、各シェーダーでシェーディングするオブジェクトを描画する直前。例えば。、

_void draw() { 
    static shader_prog wall_shader("wall_vertex", "wall_frag");
    static shader_prog skin_shader("skin_vertex", "skin_frag");

    wall_shader();
    draw_walls();

    skin_shader();
    draw_skin();
}
_

編集:@rotoglupがかなり正確に指摘しているように、static変数のこの使用は、OpenGLコンテキストが破棄されるまで破棄を遅らせるため、デストラクタがglDeleteProgram/glDeleteShaderを使用しようとすると、結果は予測できません(せいぜい)。

これはデモプログラムでは許されないかもしれませんが、実際の使用では明らかに望ましくありません。同時に、通常、シェーダーを使用する関数を入力するたびにシェーダーを再コンパイルする必要はありません。

両方の問題を回避するには、通常、シェーダーオブジェクトをクラスインスタンスのメンバーとして作成し、そのライフタイムは、シェーディングするすべてのライフタイムに関連付けられます。

_class some_character_type { 
    shader_prog skin_shader;
public:
    // ...
};
_

これにより、そのタイプのキャラクターを作成するときにシェーダープログラムが1回コンパイル/リンクされ、そのキャラクターを破棄すると破棄されます。

もちろん、いくつかのケースでは、これも正確には望ましくありません。ほんの一例として、ガラガやムカデのような古代の「たくさんのターゲットを殺す」ゲームの3Dバージョンを考えてみましょう。このようなゲームでは、本質的に同一の多数のターゲットを比較的迅速に作成および破棄します。本質的に同一のターゲットが多数ある場合、_shared_ptr<shader_prog>_のようなものを使用して、特定のターゲットタイプのすべてのインスタンス間で共有されるシェーダーの単一のインスタンスを作成します。同じターゲットタイプを何度も再利用する場合は、それよりも少し先に進み、特定のタイプのターゲットが表示されているときだけでなく、ゲーム全体を通して同じシェーダーを維持することができます。

いずれにせよ、ここでは少し軌道が外れています。ポイントは、シェーダーのコンパイルとリンクはかなりコストのかかるプロセスであるため、通常はライフタイムを管理して、シェーダーの作成と破壊を頻繁に回避する必要があるということです本当に必要なものよりも(ゲームの最初にすべてを作成し、最後にそれらを破壊することも重要だと言っているわけではありません)。

55
Jerry Coffin

1年半ほど前と同じような立場でした。 GLSLを使用するための簡単なチュートリアルとソースコードをすぐに見つけました。しかし、GLUTとGLEWを機能させる必要があり、そのうちの1つを自分でコンパイルしたと思います。私はWindowsを使用していたので(Windowsは非標準の特殊なケースであり、オープンプロジェクトで完全に扱われることはめったにありません)、DLLとヘッダーファイルを特定の一般的なファイルに手動でコピーして貼り付けると予想されていたとんでもないプロセスも伴いました場所。それは常に苦痛であり、私はそのようなことをする私の人生のかなりの部分を失いましたが、私は指示通りにプロセスを踏襲し、一般的にそうであるように結局はうまくいきました。

とにかく、今すぐ見つけることができるGLSLを使用した最も便利なシェーダーの例はこれです http://www.lighthouse3d.com/opengl/glsl/index.php?minimal

特に希望どおりにテクスチャを変更するわけではありません。しかし、私の経験では、このようなコードを取得すると、エクスペリエンスのコンパイルと実行がより楽しくなり、すぐに進歩し、他のチュートリアルの断片をつなぎ合わせることができます。必要であれば。サンプルを実行したら、同じフレームワークを使用して、自宅や職場で多くの問題をすばやく解決したと言えます。

GLUTとGLEWを使用しているとは申し訳ありません。この質問へのより良い答えが得られれば、私もコードを提供しているサイトのファンになります。幸運を。

4
guesser

この tutorial は便利かもしれません(古いCgのほかにGLSLマテリアルが含まれていることに注意してください)。

グラフィカルではないGPGPUタイプのものを実装するシェーダーを作成することは、最近の時代遅れのアプローチになると考えていることに注意してください。 OpenCLまたはCUDAは明らかに将来の道のりです。

2
timday

ターゲットではありませんが、Noel LlopisのOpenGL ES 2.0シェーダーサンプルから何かを得る可能性があります。 http://www.mobileorchard.com/getting-started-with-opengl-es-20-on-the- iphone-3gs /

1
Matt Curtis