libGDXとはWindows、Linux、Mac、Android、iPhone、HTMLに対応したクロスプラットフォームゲームライブラリです。有名なIngressでも利用されています。欧米ではわりとメジャーどころです。
シェーダとは
シェーダとはGPUのリソースを使って描画前に演算をはさめる、フィルタのようなものです。
今回のサンプルはフラグメントシェーダのみ変更してテクスチャの色を強制的に白、αはそのままという処理をしています。
通常何も考えず処理しようと思えば白いバージョンのテクスチャファイルを用意する、テクスチャのロード時にピクセル情報を白に変換して転送するなどが必要になりますが、ここは動的に描画するときに変更しているということです。
ソース
「libGDXの基礎2 ~テクスチャを表示する」と同じソースです。それにシェーダを設定しただけです。実行結果の違いを見てください。
package test.libgdx;
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
import com.badlogic.gdx.math.Interpolation;
import com.badlogic.gdx.scenes.scene2d.Action;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.actions.Actions;
import com.badlogic.gdx.scenes.scene2d.actions.RepeatAction;
import com.badlogic.gdx.scenes.scene2d.actions.SequenceAction;
import com.badlogic.gdx.scenes.scene2d.ui.Image;
public class MainListener9 implements ApplicationListener{
ShaderProgram shaderProgram;
SpriteBatch batch;
Texture texture;
@Override
public void create() {
batch = new SpriteBatch();
texture = new Texture("enemy.png");
//シェーダのセットアップ
{
String vertex = "attribute vec4 " + ShaderProgram.POSITION_ATTRIBUTE + ";\n" //
+ "attribute vec4 " + ShaderProgram.COLOR_ATTRIBUTE + ";\n" //
+ "attribute vec2 " + ShaderProgram.TEXCOORD_ATTRIBUTE + "0;\n" //
+ "uniform mat4 u_projTrans;\n" //
+ "varying vec4 v_color;\n" //
+ "varying vec2 v_texCoords;\n" //
+ "\n" //
+ "void main()\n" //
+ "{\n" //
+ " v_color = " + ShaderProgram.COLOR_ATTRIBUTE + ";\n" //
+ " v_color.a = v_color.a * (256.0/255.0);\n" //
+ " v_texCoords = " + ShaderProgram.TEXCOORD_ATTRIBUTE + "0;\n" //
+ " gl_Position = u_projTrans * " + ShaderProgram.POSITION_ATTRIBUTE + ";\n" //
+ "}\n";
String fragment = "#ifdef GL_ES\n" //
+ "#define LOWP lowp\n" //
+ "precision mediump float;\n" //
+ "#else\n" //
+ "#define LOWP \n" //
+ "#endif\n" //
+ "varying LOWP vec4 v_color;\n" //
+ "varying vec2 v_texCoords;\n" //
+ "uniform sampler2D u_texture;\n" //
+ "void main()\n"//
+ "{\n" //
+ " vec4 c4 = texture2D(u_texture, v_texCoords);\n"
+ " gl_FragColor = v_color * vec4(1, 1, 1, c4.a);\n"
+ "}";
shaderProgram = new ShaderProgram(vertex, fragment);
}
}
@Override
public void resize(int w, int h) {
}
@Override
public void render() {
Gdx.gl.glClearColor(0, 0, 0.2f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
batch.setShader(shaderProgram);
batch.draw(texture, 0, 0);
batch.setShader(null);//デフォルトシェーダに戻す
batch.end();
}
@Override
public void pause() {
}
@Override
public void resume() {
}
@Override
public void dispose() {
}
//起動部分
public static void main(String[] args) {
new LwjglApplication(new MainListener9());
}
}
実行
解説
ShaderProgram
というクラスがシェーダを保持します。コンストラクタにバーテックスシェーダとフラグメントシェーダの文字列、またはファイルを指定することで作成できます。
直にOpenGLを使う場合はこいつのbegin
とend
メソッドを呼び出すことで設定されます。
とはいえ、OpenGLは直には扱うことはあまりありません。アプリ開発時点では高レベルAPIなどを利用しているためです。このサンプルに限らずStageなど高レベルAPIは通常はBatch
インターフェースで描画を管理します。
その場合、Batch#setShader()
メソッドでShaderProgram
インスタンスを渡すことで設定されます。
何もしていない場合はデフォルトシェーダが設定されています。nullを渡すことでデフォルトシェーダに戻せます。
シェーダを切り替えれるのは良いのですが、この単純なシェーダの切り替えを利用する場合、パラメータが一致しないといけません。
SpriteBatch
を利用した場合、デフォルトシェーダはSpriteBatch#createDefaultShader ()
で生成されます。PolygonSpriteBatch
もこのメソッドを呼び出しているため、ここにあるシェーダのコードをコピーするとよいでしょう。
このサンプルのシェーダもバーテックスシェーダはそのまま、フラグメントシェーダもデフォルトシェーダを少しいじっただけのものとなっています。
このパラメータを渡しているのはSpriteBatch#setupMatrices()
です。一応見ておくとよいでしょう。現時点でのバージョンのソースを載せておきます。
private void setupMatrices () {
combinedMatrix.set(projectionMatrix).mul(transformMatrix);
if (customShader != null) {
customShader.setUniformMatrix("u_projTrans", combinedMatrix);
customShader.setUniformi("u_texture", 0);
} else {
shader.setUniformMatrix("u_projTrans", combinedMatrix);
shader.setUniformi("u_texture", 0);
}
}
この辺を使う場合はソースが短いのでsetShader()
も見たほうが良いです。ここでsetupMatrices()
を呼んでいるのがわかります。
@Override
public void setShader (ShaderProgram shader) {
if (drawing) {
flush();
if (customShader != null)
customShader.end();
else
this.shader.end();
}
customShader = shader;
if (drawing) {
if (customShader != null)
customShader.begin();
else
this.shader.begin();
setupMatrices();
}
}
何がどのタイミングで呼ばれているか、利用する場合は軽く把握しておいてください。
というわけで、シェーダをガリガリ使わないといけない用途ではBatch#setShader
を使わないほうがよい(というかつかえない)です。フィールド変数のshader
がprivate
ですので、カスタマイズができません。
出来るだけSpriteBatch
をそのまま使いたい、そういう場合はソースをまるごとコピーしたうえで別クラスとして作成するのが良いと思われます。Scen2D
で利用したい場合はStage
のコンストラクタにBatchを指定することが可能ですのでそこで設定してください。