libGDXの基礎１０ シェーダを設定する

libGDXとはWindows、Linux、Mac、Android、iPhone、HTMLに対応したクロスプラットフォームゲームライブラリです。有名なIngressでも利用されています。欧米ではわりとメジャーどころです。

#シェーダとは
シェーダとはGPUのリソースを使って描画前に演算をはさめる、フィルタのようなものです。

今回のサンプルはフラグメントシェーダのみ変更してテクスチャの色を強制的に白、αはそのままという処理をしています。

通常何も考えず処理しようと思えば白いバージョンのテクスチャファイルを用意する、テクスチャのロード時にピクセル情報を白に変換して転送するなどが必要になりますが、ここは動的に描画するときに変更しているということです。


#ソース
「libGDXの基礎２　~テクスチャを表示する」と同じソースです。それにシェーダを設定しただけです。実行結果の違いを見てください。

```java

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());
    }
}

```


#実行

![20141109-01.png](https://qiita-image-store.s3.amazonaws.com/0/46371/eb52212e-39b0-e2a1-1a05-c84a480433c7.png)


#解説

```ShaderProgram``` というクラスがシェーダを保持します。コンストラクタにバーテックスシェーダとフラグメントシェーダの文字列、またはファイルを指定することで作成できます。

直にOpenGLを使う場合はこいつの```begin```と```end```メソッドを呼び出すことで設定されます。

とはいえ、OpenGLは直には扱うことはあまりありません。アプリ開発時点では高レベルAPIなどを利用しているためです。このサンプルに限らずStageなど高レベルAPIは通常は```Batch```インターフェースで描画を管理します。

その場合、```Batch#setShader()```メソッドで```ShaderProgram```インスタンスを渡すことで設定されます。

何もしていない場合はデフォルトシェーダが設定されています。nullを渡すことでデフォルトシェーダに戻せます。

シェーダを切り替えれるのは良いのですが、この単純なシェーダの切り替えを利用する場合、パラメータが一致しないといけません。

```SpriteBatch```を利用した場合、デフォルトシェーダは```SpriteBatch#createDefaultShader ()```で生成されます。```PolygonSpriteBatch```もこのメソッドを呼び出しているため、ここにあるシェーダのコードをコピーするとよいでしょう。

このサンプルのシェーダもバーテックスシェーダはそのまま、フラグメントシェーダもデフォルトシェーダを少しいじっただけのものとなっています。

このパラメータを渡しているのは```SpriteBatch#setupMatrices()```です。一応見ておくとよいでしょう。現時点でのバージョンのソースを載せておきます。

```java
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()```を呼んでいるのがわかります。

```java
@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を指定することが可能ですのでそこで設定してください。