逆さまの世界

新しいプロジェクトを開発するために、Cube Art Project はテスト駆動開発手法を採用しました。このアプローチでは、アプリケーションの特定の機能のテストが最初に実装され、次に特定の機能が実装されます。このアプローチの大きな利点は、機能の開発が開始される前に、実装の詳細にできるだけ関与しない最終インターフェイスの実装であると考えています。このアプローチでは、インターフェイスが特定の実装のコントラクトである場合、テストによってさらなる実装が決定され、コントラクト プログラミングの利点がすべて追加されます。
キューブアートプロジェクト –ユーザーが立方体からフィギュアを構築する 3D エディターは、少し前までは非常に人気がありました。これはグラフィカルなアプリケーションなので、スクリーンショット検証を含むテストを追加することにしました。
スクリーンショットを検証するには、OpenGL コンテキストからスクリーンショットを取得する必要があります。これは glReadPixels 関数を使用して行われます。関数の引数の説明は簡単です –開始位置、幅、高さ、形式 (RGB/RGBA/など)、出力バッファへのポインタ。SDL を使用したことがある人、または C でデータ バッファを使用した経験がある人は、必要な引数を単純に置き換えることができます。ただし、glReadPixels 出力バッファの興味深い機能について説明する必要があると思います。SDL_Surface ではすべての基本操作が上から下に行われるのに対し、ピクセルは下から上に保存されます。
つまり、png ファイルから参照スクリーンショットをロードしたのですが、2 つのバッファーの 1 つが上下逆だったため、2 つのバッファーを直接比較できませんでした。
OpenGL から出力バッファーを反転するには、Y 座標のスクリーンショットの高さを減算して出力バッファーを埋める必要がありますが、埋めるときに 1 を減算しないとバッファーの制限を超える可能性があることを考慮する価値があります。メモリ破損の原因となります。
私は常に C のようなポインタによる直接メモリ アクセスではなく、「インターフェイスによるプログラミング」という OOP パラダイムを使用しようとしているため、バッファの外にデータを書き込もうとすると、メソッドの境界検証のおかげでオブジェクトがこれを通知してくれました。 。
トップダウン スタイルでスクリーンショットを取得するメソッドの最終コード:

    auto width = params->width;
    auto height = params->height;

    auto colorComponentsCount = 3;
    GLubyte *bytes = (GLubyte *)malloc(colorComponentsCount * width * height);
    glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, bytes);

    auto screenshot = make_shared(width, height);

    for (auto y = 0; y < height; y++) {
        for (auto x = 0; x < width; x++) {
            auto byteX = x * colorComponentsCount;
            auto byteIndex = byteX + (y * (width * colorComponentsCount));
            auto redColorByte = bytes[byteIndex];
            auto greenColorByte = bytes[byteIndex + 1];
            auto blueColorByte = bytes[byteIndex + 2];
            auto color = make_shared(redColorByte, greenColorByte, blueColorByte, 255);
            screenshot->setColorAtXY(color, x, height - y - 1);
        }
    }

    free(bytes);

ソース

https://community.khronos.org/ t/glreadpixels-fliped-image/26561
https://stackoverflow.com/questions/8346115/why-are-bmps-stored-upside-down

ソースコード

https://gitlab.com/demensdeum/cube-アートプロジェクトブートストラップ

WebGL + SDL + Emscripten

最終的に、SDL 1 と Emscripten を使用して、Mika を WebGL に移植しました。

次に、JavaScript でのビルドを正常に完了するためにコードの何を変更する必要があるかを説明します。

<オル>

  • SDL 2 の代わりに SDL 1 を使用します。現時点では、emscripten 用の SDL 2 ポートがありますが、emscripten に組み込まれている SDL 1 を使用する方が適切であることがわかりました。コンテキストはウィンドウ内では初期化されませんが、SDL_SetVideoMode と SDL_OPENGL フラグを使用して初期化されます。バッファは SDL_GL_SwapBuffers() コマンドを使用して描画されます
  • JavaScript のループ方法による –レンダリングは別の関数に配置され、その定期的な呼び出しは emscripten_set_main_loop
  • 関数を使用して行われます。

  • アセンブリはキー “-s FULL_ES2=1“ を使用して実行する必要もあります
  • assimp ライブラリを放棄し、ファイル システムからモデルをロードし、ディスクからテクスチャをロードする必要がありました。必要なバッファはすべてデスクトップ バージョンにロードされ、emscripten を使用してアセンブリ用の C ヘッダー ファイルに挿入されました。
  • コード:
    https://github.com/demensdeum/OpenGLES3-Experiments/tree/master/9-sdl-gles-obj-textured-assimp-miku-webgl/mikuWebGL

    記事:
    http://blog.scottlogic.com/2014/03/12/native-code-emscripten-webgl-simmer-gently.html
    https://kripken.github.io/emscripten-site/docs/porting/multimedia_and_graphics/OpenGL-support.html

    モデル:
    https://sketchfab.com/models/7310aaeb8370428e966bdcff414273e7

    ミクしかいない

    OpenGL ES とコードを使用して FSGL ライブラリを操作した結果:

    次に、すべてがどのようにプログラムされ、 さまざまな興味深い問題が解決されたかを説明します。

    最初に、前の投稿で書いたように、OpenGL ES コンテキストを初期化します。さらに、コードのレンダリングと簡単な説明のみを考慮します。

    マトリックスがあなたを見ています

    ビデオ内のこのミクの図は三角形で構成されています。 OpenGL で三角形を描くには、座標 x、y、z で 3 つの点を指定する必要があります。 OpenGL コンテキストの 2D 座標で。
    3D 座標を含む図形を描画する必要があるため、射影行列を使用する必要があります。また、モデルを回転したり、ズームインしたり、その他の操作を行う必要もあります。この目的のためにモデル マトリックスが使用されます。実際、OpenGL にはカメラの概念がありません。オブジェクトは静的なカメラの周りを回転します。このために、ビュー マトリックスが使用されます。

    OpenGL ES の実装を簡素化するため –行列データは含まれません。 GLM など、不足している機能を追加するライブラリを使用できます。

    シェーダー

    開発者があらゆる方法であらゆるものを描画できるようにするには、OpenGL ES は頂点シェーダーとフラグメント シェーダーを実装する必要があります。頂点シェーダーは、レンダリング座標を入力として受け取り、行列を使用して変換を実行し、その座標を gl_Position に渡す必要があります。フラグメントまたはピクセル シェーダ –すでにカラー/テクスチャを描画し、オーバーレイを適用しています。

    GLSL でシェーダーを作成しました。現在の実装では、シェーダはメイン アプリケーション コードに C-string として直接組み込まれています。

    バッファ

    頂点バッファには頂点 (頂点) の座標が含まれており、このバッファにはテクスチャリング用の座標やシェーダに必要なその他のデータも含まれています。頂点バッファーを生成した後、ポインターを頂点シェーダーのデータにバインドする必要があります。これは、glVertexAttribPointer コマンドを使用して行われます。このコマンドでは、要素の数、データの先頭へのポインター、およびバッファーをトラバースするために使用されるステップ サイズを指定する必要があります。私の実装では、ピクセル シェーダの頂点座標とテクスチャ座標のバインドが行われます。ただし、フラグメント シェーダーへのデータ (テクスチャ座標) の転送は頂点シェーダーを通じて実行されることは言うまでもありません。これを実現するために、 座標は変数を使用して宣言されます。

    これにより、OpenGL は三角形の点をどの順序で描画するかを認識できるようになります –インデックスバッファ(インデックス)が必要になります。インデックス バッファには、そのような 3 つのインデックスを使用して配列内の頂点番号が含まれており、三角形が取得されます。

    テクスチャ

    まず、OpenGL 用のテクスチャをロード/生成する必要があります。このために、テクスチャは bmp ファイルからロードされる SDL_LoadBMP を使用しました。ただし、24 ビット BMP のみが適切であり、その色は通常の RGB 順序ではなく BGR で保存されることに注意してください。つまり、ロード後に赤チャンネルを青チャンネルに置き換える必要があります
    。テクスチャ座標は の形式で指定します。 UV、つまり、2 つの座標を転送するだけで済みます。テクスチャの出力はフラグメント シェーダーで行われます。これを行うには、テクスチャをフラグメント シェーダにバインドする必要があります。

    余分なものは何もありません

    私たちの指示によれば、OpenGL は 2D を通じて 3D を描画するため、–次に深度を実装し、非表示の三角形を選択します –カリングと深度バッファ (Z バッファ) を使用する必要があります。私の実装では、glEnable(GL_DEPTH_TEST); という 2 つのコマンドを使用して、深度バッファの手動生成を回避できました。および選択 glEnable(GL_CULL_FACE);
    また、射影行列の近平面がゼロより大きいことも必ず確認してください。ヌル近傍平面を使用した深さのチェックは機能しません。

    レンダリング

    頂点バッファ、インデックス バッファを意識的なもの (ミク モデルなど) で埋めるには、このモデルをロードする必要があります。このために、assimp ライブラリを使用しました。ミクは Wavefront OBJ 形式ファイルに配置され、assimp を使用してロードされ、assimp から頂点およびインデックス バッファーへのデータ変換が実装されました。

    レンダリングはいくつかの段階で行われます:

    <オル>

  • モデル行列の回転を使用してミクを回転します
  • 画面と深度バッファをクリアする
  • glDrawElements コマンドを使用して三角形を描画します。
  • 次のステージ – Emscripten を使用した WebGL でのレンダリングの実装。

    ソースコード:
    https://github.com/demensdeum/OpenGLES3-Experiments/tree/master/8-sdl-gles-obj-textured-assimp-miku
    モデル:
    https://sketchfab.com/models/7310aaeb8370428e966bdcff414273e7

     

    投影してみよう

    赤いティーポットを 3D で描いたので、その作り方を簡単に説明するのが私の義務だと考えています。

    最新の OpenGL は 3D を描画せず、2D 画面座標で三角形や点などを描画するだけです。
    OpenGL を使用して少なくとも何かを出力するには、頂点バッファを提供し、頂点シェーダーを作成し、必要なすべての行列 (投影、モデル、ビュー) を頂点シェーダーに追加し、 すべての入力データを関連付ける必要があります。シェーダでは、OpenGL でメソッド レンダリングを呼び出します。シンプルに見えますか?


    頂点バッファとは何ですか?描画する座標(x,y,z)のリスト
    頂点シェーダーは GPU にどの座標を描画するかを指示します。
    ピクセル シェーダーは何を描画するか (色、テクスチャ、ブレンディングなど) を指示します。
    マトリックスは、3D 座標をレンダリングできる 2D OpenGL 座標に変換します。

    次の記事では、コード例と結果を示します。

    SDL2 – OpenGL ES

    I love Panda3D game engine. But right now this engine is very hard to compile and debug on Microsoft Windows operation system. So as I said some time ago, I begin to develop my own graphics library. Right now it’s based on OpenGL ES and SDL2.
    In this article I am going to tell how to initialize OpenGL ES context and how SDL2 helps in this task. We are going to show nothing.

    King Nothing

    First of all you need to install OpenGL ES3 – GLES 3 libraries. This operation is platform dependant, for Ubuntu Linux you can just type sudo apt-get install libgles2-mesa-dev. To work with OpenGL you need to initialize OpenGL context. There is many ways to do that, by using one of libraries – SDL2, GLFW, GLFM etc. Actually there is no one right way to initialize OpenGL context, but I chose SDL2 because it’s cross-platform solution, code will look same for Windows/*nix/HTML5/iOS/Android/etc.

    To install sdl2 on Ubuntu use this command sudo apt-get install libsdl2-dev

    So here is OpenGL context initialization code with SDL2:

        SDL_Window *window = SDL_CreateWindow(
                "SDL2 - OGLES",
                SDL_WINDOWPOS_UNDEFINED,
                SDL_WINDOWPOS_UNDEFINED,
                640,
                480,
                SDL_WINDOW_OPENGL
                );
    	    
    
        SDL_GLContext glContext = SDL_GL_CreateContext(window);
    

    After that, you can use any OpenGL calls in that context.

    Here is example code for this article:
    https://github.com/demensdeum/OpenGLES3-Experiments/tree/master/3sdl-gles
    https://github.com/demensdeum/OpenGLES3-Experiments/blob/master/3sdl-gles/sdlgles.cpp

    You can build and test it with command cmake . && make && ./SDLGles