イントロダクション
LWJGL GitBookのChapterを写経しながら理解していきます。Git(ソース)はこちら。。。
前回は、Chapter7-1をやりました。
今回は前回作詞したCubeにテクスチャを貼り付けます。
コードを読む(Read Code)
今までに見て来た部分は割愛します。
- Chapter1[外枠の表示のみ]
- Chapter2-1〜クラスの構成〜
- Chapter2-2〜インターフェースの使い方と詳細〜
- Chapter2-3〜GameEngineクラス(サンプルクラス)〜/li>
- Chapter2-4〜Windowクラス(サンプルクラス)〜
- Chapter3〜描画処理を読む〜
- Chapter4〜シェーダについて〜
- Chapter5-1〜レンダリングについて〜
- Chapter5-2〜レンダリング詳細〜
- Chapter6〜Projection(投影)〜
- 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ファイルができると思いますが、「バージョン」に関しては調べる必要あります。今回追加するのは
なのでそのサイトへいきバージョンを調べます。
ちょっとわかりづらいけど「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の説明には以下の様にありました。
次のステップは、テクスチャをグラフィックスカードメモリにアップロードすることです。まず、新しいテクスチャ識別子を作成する必要があります。テクスチャに関連する各操作はその識別子を使用するため、バインドする必要があります。
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)までの点に対して順序を指定します。
<頂点イメージ>
<テクスチャとモデルの関係>
<テクスチャ>
テクスチャを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に頂点座標を渡して描画する様です。
- 必要な頂点座標(配列)を用意する
- 各頂点をOpenGLにバインド(結びつける)
- 描画用に使用する値を「シェーダプログラム」に渡す
-
// Create uniforms for world and projection matrices and texture
shaderProgram.createUniform("projectionMatrix");
shaderProgram.createUniform("worldMatrix");
shaderProgram.createUniform("texture_sampler");
- 描画
の様な順序で処理していました。実際に触って見るのが良いと思います。
Gitにソースアップしています。(上のリンクからもダウンロードできます。)わかりやすい方でどうぞ。
追伸:自分の写経したソースは、resourcesフォルダにfs, vsファイルがあるのでそちらもダウンロードされたし。。。。
次のChapterが楽しみです。
でわでわ。。。
上記であげた違いの部分=このChapterで追加した部分を見て行きます。
上の様に比較するとわかりやすいです。ソースを選択して(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で見て来たので割愛しますが、大雑把に次の様な順で動きます。
- Main#main(): DummyGame(IGameLogic)とGameEngineをインスタンス化
- GameEngineを別スレッドで起動
- WindowとTimerをインスタンス化
- スレッド開始(GameEngine.start())
- GameEngineのinitとgameLoopを起動する
こんな感じです。
前回は、面を作るだけだったのですが単純に頂点の数と順序の数を追加しただけの様に見えます。
ポイント
- 3Dモデルの描画では、平面の時より頂点数が増えるだけ
- 無限ループの中で入力があった時に画面を更新(updateは常に行なっている)
- Windowクラスの「vSync」がきになるが値を変更している処理はない。。。
疑問点
これで、単純な3Dモデル(立方体、四角錐)は作成できる様になったけど、球体はどの様にやるのだろうか?