サイトのトップへ戻る

libGDX ドキュメント 日本語訳

サイト内検索

libGDXでマテリアルを使用する

このチュートリアルでは、libGDXでマテリアルを使用する方法について見ていきます。 マテリアルはシェーダーで使用されるので、このチュートリアルは前回のチュートリアルでやったカスタムシェーダー作成からの続きになります。 前回のチュートリアルを読んでいないのであれば、読んでおくことをお勧めします。

このチュートリアルの全source素材 と実行可能なテストは この github リポジトリにあります。

前回はRenderable と Shaderを使って作成したシェーダーをテストするだけでした。 これは何が起こっているのかを非常に簡単に見ることができるので、テストにおいては最適です。 ですが、最終的にはやっぱり以前のModelInstance とModelBatch を使ったやり方のほうが良いでしょう。 その方が、複数のシェーダーとモデルが簡単に使用できるので。 幸いなことにこれを行うことはとても簡単なので、少しコードを変更してみましょう。 参考までに、以下が ShaderTest.javaの全コードです。以下の変更点について説明しましょう:


public class ShaderTest implements ApplicationListener {
   public PerspectiveCamera cam;
   public CameraInputController camController;
   public Shader shader;
   public Model model;
   public Array<ModelInstance> instances = new Array<ModelInstance>();
   public ModelBatch modelBatch;

   @Override
   public void create () {
       cam = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
       cam.position.set(0f, 8f, 8f);
       cam.lookAt(0,0,0);
       cam.near = 1f;
       cam.far = 300f;
       cam.update();

       camController = new CameraInputController(cam);
       Gdx.input.setInputProcessor(camController);

       ModelBuilder modelBuilder = new ModelBuilder();
       model = modelBuilder.createSphere(2f, 2f, 2f, 20, 20,
         new Material(),
         Usage.Position | Usage.Normal | Usage.TextureCoordinates);

       for (int x = -5; x <= 5; x+=2) {
         for (int z = -5; z<=5; z+=2) {
             instances.add(new ModelInstance(model, x, 0, z));
         }
       }

       shader = new TestShader();
       shader.init();

       modelBatch = new ModelBatch();
   }

   @Override
   public void render () {
    camController.update();

    Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
    Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);

    modelBatch.begin(cam);
    for (ModelInstance instance : instances)
        modelBatch.render(instance, shader);
    modelBatch.end();
   }

   @Override
   public void dispose () {
       shader.dispose();
       model.dispose();
       modelBatch.dispose();
   }

    @Override
    public void resume () {
    }

    @Override
    public void resize (int width, int height) {
    }

    @Override
    public void pause () {
    }
}

githubにある全ソースコードを見てください

まず最初に、カメラを原点から少しは離れた位置に移動させ、作成するシーンがカメラの視界に収まるようにします。 次に、renderableを削除しました。代わりにModelInstanceの配列を使って、XZ平面上に球体を格子状に配置しています。 またRenderContextも削除し、今回は代わりにModelBatchを作成します。 ModelBatchは破棄をする必要があるので、disposeメソッドにも1行追加しています。 前回のチュートリアルを既に読んだのであれば、これらの変更は全て簡単にできるでしょう。 このコードの唯一新しい点は、 render メソッド内にあります:


modelBatch.begin(cam);
for (ModelInstance instance : instances)
    modelBatch.render(instance, shader);
modelBatch.end();

上記コードでは、引数として渡したModelInstanceに対して使用してほしいシェーダーをModelBatch に指定しています。 これは、カスタムシェーダーをModelBatchへ渡す最も簡単な方法です。 それではこれを実行して描写結果を見てみましょう:

shadertest8

今度は、各球体に異なる色を適用してみます。 これを行うには、まずシェーダーを変更する必要があります。 前回見たように、シェーダーは CPU 部分 (TestShader.java ファイル) とGPU 部分で構成されています。 その GPU 部分は、各頂点に対して実行されるコード (test.vertex.glsl) と各フラグメントに対して実行されるコード (test.fragment.glsl)で構成されています。

以下は編集した test.vertex.glsl ファイルです:

attribute vec3 a_position;
attribute vec3 a_normal;
attribute vec2 a_texCoord0;

uniform mat4 u_worldTrans;
uniform mat4 u_projTrans;

void main() {
    gl_Position = u_projTrans * u_worldTrans * vec4(a_position, 1.0);
}

githubにある全ソースコードを見てください

そこまで変更はしておらず、 v_texCoord0 varyingを削除しただけです。これはフラグメントシェーダーがもう必要ないからです。 以下が the test.fragment.glsl ファイルです:


#ifdef GL_ES 
precision mediump float;
#endif

uniform vec3 u_color;

void main() {
    gl_FragColor = vec4(u_color, 1.0);
}

githubにある全ソースコードを見てください

上記コードでも v_texCoord0 varying を削除して、代わりに uniform u_colorを追加しています。 この uniform はフラグメントの色を設定するのに使用されます。 そのためTestShader クラス内からそのuniform を設定する必要があります。 参考までに、以下がTestShader の全コードです:


public class TestShader implements Shader {
    ShaderProgram program;
    Camera camera;
    RenderContext context;
    int u_projTrans;
    int u_worldTrans;
    int u_color;

    @Override
    public void init () {
        String vert = Gdx.files.internal("data/test.vertex.glsl").readString();
        String frag = Gdx.files.internal("data/test.fragment.glsl").readString();
        program = new ShaderProgram(vert, frag);
        if (!program.isCompiled())
            throw new GdxRuntimeException(program.getLog());
        u_projTrans = program.getUniformLocation("u_projTrans");
        u_worldTrans = program.getUniformLocation("u_worldTrans");
        u_color = program.getUniformLocation("u_color");
    }

    @Override
    public void dispose () {
        program.dispose();
    }

    @Override
    public void begin (Camera camera, RenderContext context) {
        this.camera = camera;
        this.context = context;
        program.begin();
        program.setUniformMatrix(u_projTrans, camera.combined);
        context.setDepthTest(true, GL20.GL_LEQUAL);
        context.setCullFace(GL20.GL_BACK);
    }

    @Override
    public void render (Renderable renderable) {
        program.setUniformMatrix(u_worldTrans, renderable.worldTransform);
        program.setUniformf(u_color, MathUtils.random(), MathUtils.random(), MathUtils.random());
        renderable.mesh.render(program,
            renderable.primitiveType,
            renderable.meshPartOffset,
            renderable.meshPartSize);
    }

    @Override
    public void end () {
        program.end();
    }

    @Override
    public int compareTo (Shader other) {
        return 0;
    }
    @Override
    public boolean canRender (Renderable instance) {
        return true;
    }
}

githubにある全ソースコードを見てください

変更点は、uniform locationを保持するu_color 値を追加し、render メソッド内でそれにランダムな色を設定していることだけです。 それでは実行してみましょう:

shadertest9

ランダムな色を使用しているためこちらであまり制御することができません。 各renderableに使用する色をシェーダーへ通知する手段が必要です。 これを行うための最も基本的な手段は、ModelInstanceのuserData 値を使用することです。 ShaderTestでこれを行う方法は、以下の通りです:


   public void create () {
       ...
       for (int x = -5; x <= 5; x+=2) {
         for (int z = -5; z<=5; z+=2) {
             ModelInstance instance = new ModelInstance(model, x, 0, z);
             instance.userData = new Color((x+5f)/10f, (z+5f)/10f, 0, 1);
             instances.add(instance);
         }
       }
       ...
   }

githubにある全ソースコードを見てください

以下コードでは、シェーダーで使用したい色をuserData に設定しているだけです。この例では、インスタンスの位置によって色が変わります。 次に、シェーダー内でその値を使用する必要があります:


    public void render (Renderable renderable) {
        ...
        Color color = (Color)renderable.userData;
        program.setUniformf(u_color, color.r, color.g, color.b);
        ...
    }

githubにある全ソースコードを見てください

shadertest10

こうして、userData 値を使ってシェーダーにデータを渡すことができます。 しかしuniform が複数ある場合にこれは本当に面倒なことになり、複数のシェーダーを使用する場合にはuniform 値を追跡するのさえ苦痛になります。 Modelインスタンスにuniform 値を設定するのにもう少し良い方法が必要です。

まず、ここで実際にやろうとしていることを簡単に見てみましょう。 上で作成したシェーダーは三つの uniform (u_projTransu_worldTransu_color)を持っています。 一つ目はカメラはに依存し、二つ目(renderable.worldTransform)はと三番目はrenderableに依存します。 一般的に、これらのユニフォーム変数は三つのグループに分けることができます:

  • Global: これらは全て、 shaderの begin メソッドで設定できる値です。これらは全ての renderableに対して同じ値となり、begin() と end()の呼び出しの間で変更されることは決してありません。例えば、 u_projTrans などはGlobal ユニフォーム変数です。
  • Environmental: これらの値は全てグローバルではありませんが、またModelInstanceと直接関係もありません。一般的にこれらの値は、シーン内でのModelInstance の位置に依存します。 例えば、適用される照明(renderable.lights)などは典型的なenvironmental 値です。
  • Specific: これらは全て、ModelInstance (NodePart)固有の値です。 シーンやシーン内の位置とは関係なく、これらの値は常に適用されます。例えば、u_worldTrans とu_colorなどは specific 値です。

ここでは、specific という用語は ModelInstanceの一部であるspecific 値のことを指しています。 local 値や model 値ということもあります。

これらのグループに含まれているのはユニフォーム変数だけではないので注意してください。 例えば、頂点属性は常にrenderable (MeshPart)のspecific 値です。 シェーダーの begin()メソッド内で設定した、RenderContext のDepthTest値とCullFace値はシェーダーのglobal 値です。 これらすべての設定と値で、GLSL が実行されるコンテキストを定義します。

Shader (the CPU 部分)を作成する時は、どの値がどのグループに属しているか、それらがどれくらいの頻度で変更されうるかを常に頭に留めておく必要があります。 例えば、ShadowMap テクスチャはglobal 値とenvironmental 値になることはありますが、specific 値になることはほぼありません。

このチュートリアルでは、上記 ユニフォーム変数u_color のようなspecific 値についてのみ見ていきます。 これはマテリアルに関わってくる値です。

マテリアルにはspecifc 値のみ含まれています。renderable の MeshPart では、描写する形状を定義しています。 同様に、マテリアルはそのenvironmentに関係なく、形状がどのように描写されるかを定義します(例えば、その形状に適用する色など)。 renderable は常にマテリアルを持っています(nullにすることはできません)。 マテリアルは、基本的にmaterial 属性の配列になります:


class Material {
    Array<Material.Attribute> attributes = new Array<Attribute>();
    ...
}

マテリアルは決してnullにはできませんが、マテリアルの中身自体は空配列にできるので注意してください。

最も簡単な形式は、どの値どのユニフォーム変数に設定するかをAttribute に記述することです。 もちろん、値の型は異なってもかまいません。例えば、ユニフォーム変数はcolor型やfloat型やtexture型にできます。 したがって、Attribute 各型ごとに拡張する必要があります。 LibGDX でも最も基本的な型が実装されています。例えば:


package com.badlogic.gdx.graphics.g3d.materials;
...
public class ColorAttribute extends Attribute {
    public final Color color = new Color();
    ...
}

同様に、 FloatAttribute と TextureAttribute (そして他にもいくつか)があります。 そのため、その値を設定するのは以下のように簡単にできます。:


colorAttribute.color.set(Color.RED);

ユニフォーム変数を設定するため、各 Attribute は type 値を持っています:


public class Material implements Iterable<Attribute>, Comparator<Attribute> {
    public static abstract class Attribute {
        public final long type;
        ...
    }
    ...
}

シェーダーが同じ名前のユニフォーム変数を一つしか持つことができないのと同じ様に、マテリアルは同じtype 値を持つ属性は一つしか持つことができません。 しかしユニフォーム変数名は1つのシェーダーでのみ使用されますが、マテリアル属性は多くのシェーダーから使用できます。 したがって、マテリアル属性はシェーダーからは独立しています。 例えば、上の方で作成したシェーダーでは、u_colorという名前のユニフォーム変数を実装しています。 ただ単に色と定義された属性を実装してしまうと、それが何に使われるのか読み取れませんん。 そうではなくて、そのマテリアルの実際の目的をより明確にする必要があります。例えば、 "この属性では全体的に完全不透明なディフューズ色を設定します"など。 LibGDX には既にこうしたマテリアル属性が実装されており、これのインスタンスは以下のようにして作成できます:

ColorAttribute attribute = new ColorAttribute(ColorAttribute.Diffuse, Color.RED);

便宜上、以下のようにして属性を作成することもできます:


ColorAttribute attribute = ColorAttribute.createDiffuse(Color.RED);

上記コードでは、ColorAttribute.DiffuseColor が type 値です。そのため、同様の方法でこのマテリアルから属性を取得することができます:


Material.Attribute attribute = material.get(ColorAttribute.Diffuse);

定義上は、ColorAttribute.Diffuseのtype 値を持つ属性は常にColorAttributeへキャスト可能でなければなりません。 (例えば、 new TextureAttribute(ColorAttribute.Diffuse);を呼び出すことはできません。それは無意味でしょう)。 ですから、その属性は安心してColorAttributeへキャストして戻すことができます。:


ColorAttribute attribute = (ColorAttribute)material.get(ColorAttribute.Diffuse);

それでは実際にやってみて、ShaderTestを編集しましょう:

   public void create () {
       ...
       for (int x = -5; x <= 5; x+=2) {
         for (int z = -5; z<=5; z+=2) {
             ModelInstance instance = new ModelInstance(model, x, 0, z);
             ColorAttribute attr = ColorAttribute.createDiffuse((x+5f)/10f, (z+5f)/10f, 0, 1);
             instance.materials.get(0).set(attr);
             instances.add(instance);
         }
       }
       ...
   }

githubにある全ソースコードを見てください

上記コードでは、格子状の位置に応じて色が変わるColorAttribute.Diffuse型のColorAttribute を新規に作成しています。 次に、インスタンスの一つ目の(そして唯一の)マテリアルに色を追加しています。 このメソッドは同じ型を持つ既存の属性を上書きするので、set(addではなく)という名前になっているので注意してください。

それでは、この値を使ってユニフォーム変数 u_colorを設定するように TestShader を変更します。:


    public void render (Renderable renderable) {
        program.setUniformMatrix(u_worldTrans, renderable.worldTransform);
        Color color = ((ColorAttribute)renderable.material.get(ColorAttribute.Diffuse)).color;
        program.setUniformf(u_color, color.r, color.g, color.b);
        ...
    }

githubにある全ソースコードを見てください

上記コードでは、ColorAttribute.Diffuse型のマテリアル属性を取得し、それをColorAttribute へキャストしてそのcolor値を取得しています。 次に、color 値を使ってユニフォーム変数uniformを設定します。 このコードを実行すると、前回と全く同じ描写内容が表示されます。 しかし今回はuserDataではなくマテリアルを使用しています。

先ほど変更したrenderメソッドを注意深く見てみると、マテリアルがColorAttribute.Diffuse型の属性を含んでいない場合には酷い結果になってしまうことが分かるでしょう。 例えば、以下のようなチェック処理を追加できます。:


ColorAttribute attr = (ColorAttribute)renderable.material.get(ColorAttribute.Diffuse);
if (attr != null)
    ... set the uniform
else
    ... fall back to a default color

いくつかの場合では、これは非常に便利です。 しかしそれ以外の場面では(我々が作成したシェーダーのような)、特定のrenderableに別のシェーダー(もしくは既定のシェーダー)を使用した方が良いかもしれません。 canRender メソッドを実装して、作成したシェーダーを特定のマテリアル属性を含んだrenderable に対してのみ使用するようにできます。 それではcanRender メソッドをTestShaderに追加してみましょう。:


    public boolean canRender (Renderable renderable) {
        return renderable.material.has(ColorAttribute.Diffuse);
    }

githubにある全ソースコードを見てください

これで、マテリアルが ColorAttribute.Diffuse 属性を含んでいる場合のみこのシェーダーが使用されます。 それ以外の場合は既定のシェーダーに戻ります。

より複雑なシェーダーを作成する場合、既定では含まれていないマテリアル属性が必要になるでしょう。 それでは、より複雑なシェーダーを作成してみましょう(しかしまだ非常に簡単です)。 まずは頂点シェーダーをv_texCoord0 varyingに戻します(test.vertex.glsl):


attribute vec3 a_position;
attribute vec3 a_normal;
attribute vec2 a_texCoord0;

uniform mat4 u_worldTrans;
uniform mat4 u_projTrans;

varying vec2 v_texCoord0;

void main() {
    v_texCoord0 = a_texCoord0;
    gl_Position = u_projTrans * u_worldTrans * vec4(a_position, 1.0);
}

githubにある全ソースコードを見てください

次に、テクスチャ座標を変更して色を設定するよう、フラグメントフェーダーを変更します。 (test.fragment.glsl):


#ifdef GL_ES 
precision mediump float;
#endif

uniform vec3 u_colorU;
uniform vec3 u_colorV;

varying vec2 v_texCoord0;

void main() {
    gl_FragColor = vec4(v_texCoord0.x * u_colorU + v_texCoord0.y * u_colorV, 1.0);
}

githubにある全ソースコードを見てください

上記コードでは、ユニフォーム変数を一つではなく二つ使ってピクセル色を作成します。 一つの色はX (U)テクスチャ座標に依存し、もう一つの色は Y (V)テクスチャ座標に依存しています。 最後に、これらのユニフォーム変数を両方設定するようTestShader を変更する必要があります。:


public class TestShader implements Shader {
    ShaderProgram program;
    Camera camera;
    RenderContext context;
    int u_projTrans;
    int u_worldTrans;
    int u_colorU;
    int u_colorV;

    @Override
    public void init () {
        ...
        u_worldTrans = program.getUniformLocation("u_worldTrans");
        u_colorU = program.getUniformLocation("u_colorU");
        u_colorV = program.getUniformLocation("u_colorV");
    }
    ...
    @Override
    public void render (Renderable renderable) {
        program.setUniformMatrix(u_worldTrans, renderable.worldTransform);
        Color colorU = ((ColorAttribute)renderable.material.get(ColorAttribute.Diffuse)).color;
        Color colorV = Color.BLUE;
        program.setUniformf(u_colorU, colorU.r, colorU.g, colorU.b);
        program.setUniformf(u_colorV, colorV.r, colorV.g, colorV.b);
        renderable.mesh.render(program,
            renderable.primitiveType,
            renderable.meshPartOffset,
            renderable.meshPartSize);
    }
    ...
}

githubにある全ソースコードを見てください

ユニフォーム変数u_colorUu_colorV両方のユニフォームロケーションを取得します。 そしてu_colorUにディフューズ色属性の色を設定し、u_colorVColor.BLUEを設定します。

shadertest11

このシェーダーでは今回、Xテクスチャ座標に応じて変わる色には異なるディフューズ色属性を使用しています。 そしてYテクスチャ座標に応じて変わる色としてColor.BLUEを使用しています。 ですが、両方ともマテリアル属性を使用して設定可能な方が良いでしょう。 そのため、マテリアル属性を二つ作成する必要があります。一つはU値に基づいたディフューズ色のもので、もう一つはY値に基づいたディフューズ色のもの これを行うための最も簡単な方法は、ColorAttribute クラスを拡張して追加のtype 値を登録することです。ではやってみましょう:


public class TestShader implements Shader {
    public static class TestColorAttribute extends ColorAttribute {
        public final static String DiffuseUAlias = "diffuseUColor";
        public final static long DiffuseU = register(DiffuseUAlias);

        public final static String DiffuseVAlias = "diffuseVColor";
        public final static long DiffuseV = register(DiffuseVAlias);

        static {
            Mask = Mask | DiffuseU | DiffuseV;
        }

        public TestColorAttribute (long type, float r, float g, float b, float a) {
            super(type, r, g, b, a);
        }
    }
    ...
}

githubにある全ソースコードを見てください

これはこのように小さなクラスなので、新しくJavaファイルを作成はしませんが、代わりにTestShaderのstatic サブクラスを作成します。 このクラスはColorAttributeを拡張しています。これは、ColorAttributeが登録する必要のある属性のtypeだからです。 このクラス内には、いくつかの public final static メンバがあります。 一つ目のメンバはDiffuseUAliasで、これは定義しようとしている属性typeの名前です。 この値は、例えば attribute.toString()を呼び出した時に戻り値として返されます。 次の行では、その名前を属性typeとして登録します。 register メソッドは、type 値(この値はグローバル範囲でも重複しません)を戻り値として返すので、それを使ってDiffuseU 値を初期化します。 これにより、ColorAttribute.Diffuse値と同様に、 TestColorAttribute.DiffuseUも属性typeとして使用できます。 次にDiffuseVAliasについても同じことを行います。登録するのはtype DiffuseVです。

今回、 DiffuseU とDiffuseVという二つの新しいマテリアル属性を登録しました。 しかしColorAttribute クラスを拡張しているので、そのクラスにこれらの新しい属性を受け入れるように通知する必要があります。 この処理については次の行 (Mask = Mask | DiffuseU | DiffuseV)で行っています。 最後にコンストラクタを作成して、これで新しく作成したマテリアル属性のインスタンスを実際に作成することができます。

それではこれら二つの新しいマテリアル属性を使用してみましょう。まずは、ShaderTest クラスを変更します:


   public void create () {
       ...
       for (int x = -5; x <= 5; x+=2) {
         for (int z = -5; z<=5; z+=2) {
             ModelInstance instance = new ModelInstance(model, x, 0, z);
             ColorAttribute attrU = new TestColorAttribute(TestColorAttribute.DiffuseU, (x+5f)/10f, 1f - (z+5f)/10f, 0, 1);
             instance.materials.get(0).set(attrU);
             ColorAttribute attrV = new TestColorAttribute(TestColorAttribute.DiffuseV, 1f - (x+5f)/10f, 0, (z+5f)/10f, 1);
             instance.materials.get(0).set(attrV);
             instances.add(instance);
         }
       }
       ...
   }

githubにある全ソースコードを見てください

上記コードでは、二つのTestColorAttributeを作成しています。一つはDiffuseU タイプのもので、もう一つはDiffuseVタイプのものです。 そして格子状の位置に応じて両方に色を設定します。 最後は前回と同じ様に、それらにマテリアルを追加します。 今度は、実際にそれらを使うようにTestShader を変更する必要があります:


    public void render (Renderable renderable) {
        program.setUniformMatrix(u_worldTrans, renderable.worldTransform);
        Color colorU = ((ColorAttribute)renderable.material.get(TestColorAttribute.DiffuseU)).color;
        Color colorV = ((ColorAttribute)renderable.material.get(TestColorAttribute.DiffuseV)).color;
        program.setUniformf(u_colorU, colorU.r, colorU.g, colorU.b);
        program.setUniformf(u_colorV, colorV.r, colorV.g, colorV.b);
        renderable.mesh.render(program,
            renderable.primitiveType,
            renderable.meshPartOffset,
            renderable.meshPartSize);
    }

githubにある全ソースコードを見てください

render メソッド内では、両方の属性の色を取得し、それに応じてユニフォーム変数を設定します。 ほとんどこれまでと同じです。 これを実行すると、以下のようになります:

shadertest12

なんと、想定していた結果とは違うものになりました。 これはcanRender メソッドをまだ更新していないためです。 現状ではマテリアルには ColorAttribute.Diffuse属性が含まれていないため、ModelBatch は既定のシェーダーへと切り戻しを行ったのです。 modelBatch.render(instance, shader);を実行する時に明示的にシェーダーを指定していたとしても、ModelBatch は使用できないシェーダーを誤使用してしまうことを防いでくれるのです。 TestShader クラスのcanRender メソッドを変更し、新しいマテリアル属性を受け入れるようModelBatch に通知する必要があります。:

    public boolean canRender (Renderable renderable) {
        return renderable.material.has(TestColorAttribute.DiffuseU | TestColorAttribute.DiffuseV);
    }

githubにある全ソースコードを見てください

それでは、実行してどのように描写されるか見てみましょう:

shadertest13

前よりも良くなりました。これでカスタムマテリアル属性を使ってユニフォーム変数を制御することができます。

複数のマテリアル属性タイプを組み合わせるのにビット単位の演算子を使用していることに気づいたかもしれません。例えば:


return renderable.material.has(TestColorAttribute.DiffuseU | TestColorAttribute.DiffuseV);

これは、マテリアルがDiffuseU 属性とDiffuseV 属性の両方を持っている場合にのみtrue を返します。 今のところはこれ以上詳しく説明しませんが、マテリアル属性を組み合わせて比較をより早く行えるということを覚えておいてください。

最後の説明になりますが、マテリアル属性はユニフォーム変数を設定する必要はありません。 例えば、LibGDX にはIntAttribute.CullFaceという属性タイプが実装されています。 これはcontext.setCullFace(...)と一緒に使用でき、ユニフォーム値を設定する必要はありません。 同様に、属性には一つの値だけを設定する必要はありません。 例えば、前述のシェーダーでは二つのカラー属性DiffuseU とDiffuseVを使用しました。 より良いやり方としては、二つのカラー値を含んだ一つの属性を作成する方法があります。 例えば:


public class DoubleColorAttribute extends Attribute {
    public final static String DiffuseUVAlias = "diffuseUVColor";
    public final static long DiffuseUV = register(DiffuseUVAlias);

    public final Color color1 = new Color();
    public final Color color2 = new Color();

    protected DoubleColorAttribute (long type, Color c1, Color c2) {
        super(type);
        color1.set(c1);
        color2.set(c2);
    }

    @Override
    public Attribute copy () {
        return new DoubleColorAttribute(type, color1, color2);
    }

    @Override
    protected boolean equals (Attribute other) {
        DoubleColorAttribute attr = (DoubleColorAttribute)other;
        return type == other.type && color1.equals(attr.color1) && color2.equals(attr.color2);
    }

    @Override
    public int compareTo (Attribute other) {
        if (type != other.type)
            return (int) (type - other.type);
        DoubleColorAttribute attr = (DoubleColorAttribute) other;
        return color1.equals(attr.color1)
                ? attr.color2.toIntBits() - color2.toIntBits()
                : attr.color1.toIntBits() - color1.toIntBits();
    }
}

githubにある全ソースコードを見てください

次: libGdxでの3D視錐台カリング