Java 3DGame LWJGL Retry Lv1 〜動かして理解する〜

イントロダクション

LWJGLのGitBookを読み進めChapter8まできましたが、ここからよく解らなくなってしまいました。

原因は、3Dモデル描画(レンダリング)の部分をちゃんと理解していないためです。

なので、Chapter5-2からやり直します。

幸いにも、GitからPULLしてきたファイルがあるのでそいつをカスタムして理解していきます。

余談

EclipseでGitからPULLする場合は、ChapterX("X"には数字)のフォルダをPULLするとプロジェクトが作成されてちょうど良い感じです。

フォルダ(ディレクトリ)の直下に「.project」ファイルがあるとEclipseはそのフォルダを「プロジェクト」として読み込んでくれます。

今回のGitリポジトリは「pom.xml」ファイルがあるのでそれはMevenプロジェクトとして読み込んでくれるようです。

*****************************************


*****************************************

Chapter5-2で表示

***************************************


***************************************

  ソースで遊ぶ

前回表示したのは、シンプルに四角でした。こいつを以下のようにしてみます。

1.色を色々と変えてみる

色を決めているであろう、ソースコードは下の部分になります。

DummtGame#init()

       // 追記 2018/10/27
        float[] colors = new float[] {
        	    0.5f, 0.0f, 0.0f, // T01
        	    0.0f, 0.5f, 0.0f, // T02
        	    0.0f, 0.0f, 0.5f, // T03
        	    0.0f, 0.5f, 0.5f,}; // T04

これのT01行の値(「0.5f, 0.0f, 0.0f,」)を変更してみます。

「0.0f, 0.0f, 0.0f,」

「0.0f, 0.5f, 0.0f,」

「0.0f, 0.0f, 0.5f,」

どうやら「0.0f」は黒「0.5f」はRGBの3つ並んだうちの色を出すようです。

→「0.0f, 0.0f, 0.0f,」黒、「0.5f, 0.0f, 0.0f,」赤、「0.0f, 0.5f, 0.0f,」緑、「0.0f, 0.0f, 0.5f,」青

逆に1.0fの値は白になるだろうか?

「1.0f(R), 0.0f(G), 0.0f(B),」は左上が白くなるであろうと仮説を立てます。

<実行結果>

明るい赤でした。。。でも上記の配列はRGBの順番でそれぞれの色の強さを示していることがわかりました。

次は二段目、T02行の値(「0.0f, 0.5f, 0.0f,」)を変更してみます。T01の行と同様に値を変えていきます。

「0.5f, 0.0f, 0.0f,」

「0.5f, 0.0f, 0.0f,」

「0.0f, 0.0f, 0.5f,」

ここまでやって、はっきりしました。

 

       // 追記 2018/10/27
        float[] colors = new float[] {
        	    0.5f, 0.0f, 0.0f, // T01:左上(赤)
        	    0.0f, 0.5f, 0.0f, // T02:左下(緑)
        	    0.0f, 0.0f, 0.5f, // T03:右下(青)
        	    0.8f, 0.8f, 0.0f,}; // T04:(黄)

 

こんな感じで、color部分に関しては理解できました。

次回

今度は、positionをいじって遊ぼうと思います。

関連ページ一覧

Java 3DGame LWJGL GitBook chapter5-2〜レンダリング詳細〜

<開発準備>

Java Install Eclipse〜開発ツールのインストール〜

<LWJGLのGitBokに関して>

  1. Chapter1[外枠の表示のみ]
  2. Chapter2-1〜クラスの構成〜
  3. Chapter2-2〜インターフェースの使い方と詳細〜
  4. Chapter2-3〜GameEngineクラス(サンプルクラス)〜/li>
  5. Chapter2-4〜Windowクラス(サンプルクラス)〜
  6. Chapter3〜描画処理を読む〜
  7. Chapter4〜シェーダについて〜
  8. Chapter5-1〜レンダリングについて〜

<Java Basic>

  1. Java Basic Level 1 〜Hello Java〜
  2. Java Basic Level2 〜Arithmetic Calculate〜
  3. Java Basic Level3 〜About String class〜
  4. Java Basic Level 4〜Boolean〜
  5. Java Basic Level 5〜If Statement〜
  6. Java Basic Summary from Level1 to 5
  7. Java Basic Level 6 〜Traning of If statement〜
  8. Java Basic Level8 〜How to use for statement〜
  9. Java Basic Level 8.5 〜Array〜
  10. Java Basic Level 8.5 〜Array〜
  11. Java Basic Level 10 〜While statement 〜
  12. Java Basic Swing〜オブジェクト指向〜
  13. Java Basic Swing Level 2〜オブジェクト指向2〜


Eclipse アプリ作成 Lv1〜家計簿を作る準備〜

イントロダクション

今までLWJGLを学んできました。これはOpenGLとOpenAL(オーディオ関連ライブラリ)を含んだフレームワークです。本当はまだまだ続きがあります(chapter7/chapter28)

駄菓子菓子!Matrix(行列)が出てきたあたりから解らなくなってきたので、今一度理解するためにアプリを作って理解しようというわけです。

  1.  Chapter1[外枠の表示のみ]
  2. Chapter2-1〜クラスの構成〜
  3. Chapter2-2〜インターフェースの使い方と詳細〜
  4. Chapter2-3〜GameEngineクラス(サンプルクラス)〜/li>
  5. Chapter2-4〜Windowクラス(サンプルクラス)〜
  6. Chapter3〜描画処理を読む〜
  7. Chapter4〜シェーダについて〜
  8. Chapter5-1〜レンダリングについて〜
  9. Chapter5-2〜レンダリング詳細〜
  10. Chapter6〜Projection(投影)〜
  11. Chapter7-1〜Cubeを作る〜
  12. Chapter7-2〜Texture〜

家計簿アプリを作る準備

セットアップ

1.まずはMavenプロジェクトを作成します。

2.そして、プロジェクトのセットアップを行います。

今回は、フレームワークとしてLWJGLを使用します。必要なライブラリをMevenで落としてくるのも良いのですが、バージョンが沢山あるので、今使用しているライブラリを使用します。LWJGLのセットアップ

プロジェクトを右クリックして、properties

Build Pathを選択

ライブラリを追加しますので、ライブラリ(Libraries)を開き「Add Library」

そして、「User libraries」をクリック

「New」でUserLibraryを作成します。

「Add External Jar」で外部(Eclipseの外)ライブラリ(JAR)を読み込みます。

今回はLWJGLなのでダウンロードした、頭に「lwjgl」とついているJARファイルを全てパスに繋ぎます。そしてOK。

Javaのバージョンを1.8以上にします。

Project右クリック > Properties > Java Compiler

でJavaのバージョンを1.8以上にします。

トドメに、「Hello World」を実行

とりあえずは、ここまでで一区切りです。

でわでわ。。。

 関連ページ一覧

<開発ツールのインストール>

<プロジェクトの作成(はじめ方)>

<GitからのPULL方法>

<Javaの基本から応用など>

 


Java 3DGame LWJGL GitBook chapter8〜Camera〜

イントロダクション

LWJGL GitBookChapterを写経しながら理解していきます。Git(ソース)はこちら。。。

前回は、Chapter7-2をやりました。今回は画面を表示するときの「カメラ」に付いてやります。

ゲームで表示されている領域を変えたり、ズームアップしたりするアレです。

コードを読む(Read Code)

今までに見て来た部分は割愛します。

  1.  Chapter1[外枠の表示のみ]
  2. Chapter2-1〜クラスの構成〜
  3. Chapter2-2〜インターフェースの使い方と詳細〜
  4. Chapter2-3〜GameEngineクラス(サンプルクラス)〜/li>
  5. Chapter2-4〜Windowクラス(サンプルクラス)〜
  6. Chapter3〜描画処理を読む〜
  7. Chapter4〜シェーダについて〜
  8. Chapter5-1〜レンダリングについて〜
  9. Chapter5-2〜レンダリング詳細〜
  10. Chapter6〜Projection(投影)〜
  11. Chapter7-1〜Cubeを作る〜
  12. Chapter7-2〜Texture〜

https://ahbejarano.gitbook.io/lwjglgamedev/chapter8

今回の追加ソース

  1. MouseInput
  2. Camera

大きな修正部分

  1. Transformation#getWorldMatrix()を修正していました。
  2. DummyGame#init()
  3. Renderer#render()

写経後に起動するアプリ

*************************************


*************************************

改めてソースを読む

Main: DummyGame, GameEngineをインスタンス化してGameEngine#start()を実行

DummyGame: Renderer, Camera, Vector3fをインスタンス化して各フィールドに設定

GameEngine:

  1. スレッドを立ち上げ(new Thread(this))、フィールドにWindow, MounseInput, DummyGameのインスタンスを設定する
  2. OSの種類に応じて、GameEngine#run() or GameEngine#start()を切り替えて実行する
  3. フィールドにセットした各クラスの初期処理(init())を起動する
  4. gameLoopを回しながらinput, renderを繰り返し実行する

大雑把にこんな感じの処理をしていました。

今回のキモ

ズバリ、カメラワークでしょう!そして、因縁の三角関数!!

そして、GitBookの説明を読んでもよくわからなかったので、デバッカーを作って実行

スクリーンショット 2018-10-31 20.38.06.png

入力があったときのみコンソールに出力する様に処理を追加→欠陥品でした。。。1回目しか出力しません。。。

スクリーンショット 2018-10-31 21.47.10.png

出力結果

2018-10-31 21.46.55

とりあえずはここまでやったのだけど、入力した値が逆方向に向いている(-0.05)こと以外はわからなかった次第でございます。

続きはまた後日。

でわでわ。。。

 

Java 3DGame LWJGL GitBook chapter7-2〜Texture〜

イントロダクション

LWJGL GitBookChapterを写経しながら理解していきます。Git(ソース)はこちら。。。

前回は、Chapter7-1をやりました。

今回は前回作詞したCubeにテクスチャを貼り付けます。

コードを読む(Read Code)

今までに見て来た部分は割愛します。

  1.  Chapter1[外枠の表示のみ]
  2. Chapter2-1〜クラスの構成〜
  3. Chapter2-2〜インターフェースの使い方と詳細〜
  4. Chapter2-3〜GameEngineクラス(サンプルクラス)〜/li>
  5. Chapter2-4〜Windowクラス(サンプルクラス)〜
  6. Chapter3〜描画処理を読む〜
  7. Chapter4〜シェーダについて〜
  8. Chapter5-1〜レンダリングについて〜
  9. Chapter5-2〜レンダリング詳細〜
  10. Chapter6〜Projection(投影)〜
  11. Chapter7-1〜Cubeを作る〜

https://ahbejarano.gitbook.io/lwjglgamedev/chapter7

写経前の準備

上のリンクにあるGitBookを読んでみるとMavenでのライブラリ追加に関して記載があります。なのでライブラリの追加(依存関係の追加)を行います。

依存関係の追加(Maven、POMファイル)

上のキャプチャの様にPOMファイルを開きます。

そして、GitBookのページからPOMファイルの記載部分をコピって「ぺっ」ってやります。

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  
  <modelVersion>4.0.0modelVersion>
  <groupId>zenryokuservicegroupId>
  <artifactId>gui.mtmartifactId>
  <version>0.0.1-SNAPSHOTversion>
  <packaging>jarpackaging>
  <name>gui.mtmname>
  <url>http://maven.apache.orgurl>
  <properties>
    <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
  properties>
  
  <dependencies>
    <dependency>
      <groupId>junitgroupId>
      <artifactId>junitartifactId>
      <version>3.8.1version>
      <scope>testscope>
    dependency>
    
    <dependency>
         <groupId>org.jomlgroupId>
         <artifactId>jomlartifactId >
         <version>1.9.6version >
    dependency>
    
    <dependency>
      <groupId>org.l33tlabs.twlgroupId>
      <artifactId>pngdecoderartifactId>
      <version>バージョンを記載version>
    dependency>
  dependencies>
project>

上記の様な感じでPOMファイルができると思いますが、「バージョン」に関しては調べる必要あります。今回追加するのは

PNGDecoder

なのでそのサイトへいきバージョンを調べます。

ちょっとわかりづらいけど「1.0」と記載してありました。

上の様にpom.xmlを右クリック→Maven→Maven installでインストール完了です。

そして実行結果は以下の様になりました。

中身を見る

今回は、前回作成したCube(立方体)にテクスチャを貼り付ける作業になります。

ソースの差分を見るのでなく面倒なのでソースをそのまま読みます。

Main: 変化なし(やってることは変わらない)

DummyGame:Textureクラスを生成、Meshクラスに渡す

Texture texture = new Texture("/textures/grassblock.png");
Mesh mesh = new Mesh(positions, textCoords, indices, texture);

GitBookの説明には以下の様にありました。


次のステップは、テクスチャをグラフィックスカードメモリにアップロードすることです。まず、新しいテクスチャ識別子を作成する必要があります。テクスチャに関連する各操作はその識別子を使用するため、バインドする必要があります。

//新しいOpenGLテクスチャを作成する 
int textureId = glGenTextures ; 
//テクスチャをバインドする
glBindTexture GL_TEXTURE_2D textureId ;

次に、RGBAバイトを解凍する方法をOpenGLに伝える必要があります。各コンポーネントのサイズは1バイトなので、次の行を追加する必要があります。

glPixelStorei GL_UNPACK_ALIGNMENT 1 ; 

最後にテクスチャデータをアップロードすることができます:

glTexImage2D GL_TEXTURE_2D, 0, GL_RGBA,cgetWidth, 
    decoder.getHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, buf; 

Mesh: TextureのVBOのIDを取得して〜とCubeと同じ処理をする

textCoordsBuffer = MemoryUtil.memAllocFloat(textCoords.length);
textCoordsBuffer.put(textCoords).flip();
glBindBuffer(GL_ARRAY_BUFFER, vboId);
glBufferData(GL_ARRAY_BUFFER, textCoordsBuffer, GL_STATIC_DRAW);
glVertexAttribPointer(1, 2, GL_FLOAT, false, 0, 0);

※詳細はChapter5-2を参照してください。

Cubeを作成した時と同様に各頂点(V0〜V7)までの点に対して順序を指定します。

<頂点イメージ>

<テクスチャとモデルの関係>

<テクスチャ>

grassblock

テクスチャをCubeの頂点とOpenGLに結びつけてやる。

        // Load Texture file=>Mavenで追加したライブラリのクラス
        PNGDecoder decoder = new PNGDecoder(Texture.class.getResourceAsStream(fileName));

        // Load texture contents into a byte buffer
        ByteBuffer buf = ByteBuffer.allocateDirect(
                4 * decoder.getWidth() * decoder.getHeight());
        decoder.decode(buf, decoder.getWidth() * 4, Format.RGBA);
        buf.flip();

        // Create a new OpenGL texture 
        int textureId = glGenTextures();
        // Bind the texture
        glBindTexture(GL_TEXTURE_2D, textureId);

        // Tell OpenGL how to unpack the RGBA bytes. Each component is 1 byte size
        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

        //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

        // Upload the texture data
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, decoder.getWidth(), decoder.getHeight(), 0,
                GL_RGBA, GL_UNSIGNED_BYTE, buf);
        // Generate Mip Map
        glGenerateMipmap(GL_TEXTURE_2D);

Cubeの頂点とテクスチャの頂点をセットした配列を用意して、OpenGLに結びつける(Bind)

        // Create the Mesh
        float[] positions = new float[] {
            // V0
            -0.5f, 0.5f, 0.5f,
            // V1
            -0.5f, -0.5f, 0.5f,
            // V2
            0.5f, -0.5f, 0.5f,
            // V3
            0.5f, 0.5f, 0.5f,
            // V4
            -0.5f, 0.5f, -0.5f,
            // V5
            0.5f, 0.5f, -0.5f,
            // V6
            -0.5f, -0.5f, -0.5f,
            // V7
            0.5f, -0.5f, -0.5f,
            
            // For text coords in top face
            // V8: V4 repeated
            -0.5f, 0.5f, -0.5f,
            // V9: V5 repeated
            0.5f, 0.5f, -0.5f,
            // V10: V0 repeated
            -0.5f, 0.5f, 0.5f,
            // V11: V3 repeated
            0.5f, 0.5f, 0.5f,

            // For text coords in right face
            // V12: V3 repeated
            0.5f, 0.5f, 0.5f,
            // V13: V2 repeated
            0.5f, -0.5f, 0.5f,

            // For text coords in left face
            // V14: V0 repeated
            -0.5f, 0.5f, 0.5f,
            // V15: V1 repeated
            -0.5f, -0.5f, 0.5f,

            // For text coords in bottom face
            // V16: V6 repeated
            -0.5f, -0.5f, -0.5f,
            // V17: V7 repeated
            0.5f, -0.5f, -0.5f,
            // V18: V1 repeated
            -0.5f, -0.5f, 0.5f,
            // V19: V2 repeated
            0.5f, -0.5f, 0.5f,
        };
        float[] textCoords = new float[]{
            0.0f, 0.0f,
            0.0f, 0.5f,
            0.5f, 0.5f,
            0.5f, 0.0f,
            
            0.0f, 0.0f,
            0.5f, 0.0f,
            0.0f, 0.5f,
            0.5f, 0.5f,
            
            // For text coords in top face
            0.0f, 0.5f,
            0.5f, 0.5f,
            0.0f, 1.0f,
            0.5f, 1.0f,

            // For text coords in right face
            0.0f, 0.0f,
            0.0f, 0.5f,

            // For text coords in left face
            0.5f, 0.0f,
            0.5f, 0.5f,

            // For text coords in bottom face
            0.5f, 0.0f,
            1.0f, 0.0f,
            0.5f, 0.5f,
            1.0f, 0.5f,
        };
        int[] indices = new int[]{
            // Front face
            0, 1, 3, 3, 1, 2,
            // Top Face
            8, 10, 11, 9, 8, 11,
            // Right face
            12, 13, 7, 5, 12, 7,
            // Left face
            14, 15, 6, 4, 14, 6,
            // Bottom face
            16, 18, 19, 17, 16, 19,
            // Back face
            4, 6, 7, 5, 4, 7,};

テクスチャをアクティブにする(と思う)

        // Activate firs texture bank
        glActiveTexture(GL_TEXTURE0);

他のクラスは変わりなし。。。多分(実行結果からその様に予測。。。)

まとめ

大まかな流れが読めて来ました。テクスチャもCubeなど3DモデルもOpenGLに頂点座標を渡して描画する様です。

  1. 必要な頂点座標(配列)を用意する
  2. 各頂点をOpenGLにバインド(結びつける)
  3. 描画用に使用する値を「シェーダプログラム」に渡す
    1.         // Create uniforms for world and projection matrices and texture
              shaderProgram.createUniform("projectionMatrix");
              shaderProgram.createUniform("worldMatrix");
              shaderProgram.createUniform("texture_sampler");
  4. 描画

の様な順序で処理していました。実際に触って見るのが良いと思います。

Gitにソースアップしています。(上のリンクからもダウンロードできます。)わかりやすい方でどうぞ。

追伸:自分の写経したソースは、resourcesフォルダにfs, vsファイルがあるのでそちらもダウンロードされたし。。。。

次のChapterが楽しみです。

でわでわ。。。


上記であげた違いの部分=このChapterで追加した部分を見て行きます。

スクリーンショット 2018-10-29 21.10.42.png

上の様に比較するとわかりやすいです。ソースを選択して(2つ)→右クリック→CompareWith(比較)→EachOther(相互)で見れます。(Eclipse)

下の様な図にある点(Vertex: V0〜V7)をを定義している様です。

[DummyGame#init()]

    public void init(Window window) throws Exception {
        renderer.init(window);
        // Create the Mesh
        float[] positions = new float[]{
            // VO
            -0.5f,  0.5f,  0.5f,
            // V1
            -0.5f, -0.5f,  0.5f,
            // V2
             0.5f, -0.5f,  0.5f,
            // V3
             0.5f,  0.5f,  0.5f,
            // V4
            -0.5f,  0.5f, -0.5f,
            // V5
             0.5f,  0.5f, -0.5f,
            // V6
            -0.5f, -0.5f, -0.5f,
            // V7
             0.5f, -0.5f, -0.5f,
        };
        float[] colours = new float[]{
            0.5f, 0.0f, 0.0f,
            0.0f, 0.5f, 0.0f,
            0.0f, 0.0f, 0.5f,
            0.0f, 0.5f, 0.5f,
            0.5f, 0.0f, 0.0f,
            0.0f, 0.5f, 0.0f,
            0.0f, 0.0f, 0.5f,
            0.0f, 0.5f, 0.5f,
        };
        int[] indices = new int[]{
            // Front face
            0, 1, 3, 3, 1, 2,
            // Top Face
            4, 0, 3, 5, 4, 3,
            // Right face
            3, 2, 7, 5, 3, 7,
            // Left face
            6, 1, 0, 6, 0, 4,
            // Bottom face
            2, 1, 6, 2, 6, 7,
            // Back face
            7, 6, 4, 7, 4, 5,
        };
        Mesh mesh = new Mesh(positions, colours, indices);
        GameItem gameItem = new GameItem(mesh);
        gameItem.setPosition(0, 0, -2);
        gameItems = new GameItem[] { gameItem };
    }

正直のところ色(colour)に関してはよくわかっていません。。。がポジション(positions)と順番(indices)に関しては理解できたと思っています。紙と鉛筆が必要ですが。。。。

①コメントにある通りpositionsでV0〜V7までの頂点の座標を定義(中央が0.0)して7つ定義しています。

②色はよくわかっていません。。。

③各面(四角形)分の点の順番を定義して行きます。「V0, V1, V3」で1つの三角形「V3, V1, V2」で1つの三角形 = 四角形(Front face)になります。

あとは入力のハンドリング

   @Override
    public void input(Window window) {
        displyInc = 0;
        displxInc = 0;
        displzInc = 0;
        scaleInc = 0;
        if (window.isKeyPressed(GLFW_KEY_UP)) {
            displyInc = 1;
        } else if (window.isKeyPressed(GLFW_KEY_DOWN)) {
            displyInc = -1;
        } else if (window.isKeyPressed(GLFW_KEY_LEFT)) {
            displxInc = -1;
        } else if (window.isKeyPressed(GLFW_KEY_RIGHT)) {
            displxInc = 1;
        } else if (window.isKeyPressed(GLFW_KEY_A)) {
            displzInc = -1;
        } else if (window.isKeyPressed(GLFW_KEY_Q)) {
            displzInc = 1;
        } else if (window.isKeyPressed(GLFW_KEY_Z)) {
            scaleInc = -1;
        } else if (window.isKeyPressed(GLFW_KEY_X)) {
            scaleInc = 1;
        }
    }

コードから察するに十字キーと「A」「Q」「Z」「X」ボタンで動かせる様です。

細かいところは今までのChapterで見て来たので割愛しますが、大雑把に次の様な順で動きます。

  1. Main#main(): DummyGame(IGameLogic)とGameEngineをインスタンス化
  2. GameEngineを別スレッドで起動
  3. WindowとTimerをインスタンス化
  4. スレッド開始(GameEngine.start())
  5. GameEngineのinitとgameLoopを起動する

こんな感じです。

前回は、面を作るだけだったのですが単純に頂点の数と順序の数を追加しただけの様に見えます。

ポイント

  1. 3Dモデルの描画では、平面の時より頂点数が増えるだけ
  2. 無限ループの中で入力があった時に画面を更新(updateは常に行なっている)
  3. Windowクラスの「vSync」がきになるが値を変更している処理はない。。。

疑問点

これで、単純な3Dモデル(立方体、四角錐)は作成できる様になったけど、球体はどの様にやるのだろうか?