実際、描写時にあなたは何を描写するのか(形状)とどうやって描写するのか(マテリアル)を指定しています。 形状は、Mesh (またはより一般的な MeshPart)を使って指定されます。Mesh ではシェーダーの頂点属性を定義しています。 マテリアルとは、シェーダーのユニフォーム値を指定するのに最もよく使用されるものです。
ユニフォームは、model 固有のユニフォーム変数(例えば、適用されるテクスチャや、ブレンド処理を使用するかどうか)と環境ユニフォーム変数(例えば、適用される光源やキューブ環境マッピング)に分けることができます。同様に、3D api を使うことで material と environmentを設定できます。
Materialはmodel (または modelinstance) 固有のものです。インデックス指定model.materials.get(0)
や名前指定 model.getMaterial("material3")
やnodepart指定 model.nodes.get(0).parts(0).material
を使ってアクセスできます。
ModelInstanceを作成した時Materialはコピーされます、つまり、ModelInstance のマテリアルを変更しても元のModel やその他ModelInstancesには影響を与えません。
Material クラスは Attribute クラスを継承しています。Attributeの詳細については後述する内容を参照してください。
Environment には場所に対するユニフォーム変数が含まれています。 例えば、照明(lights)は Environmentの一部です。 単純なアプリケーションではEnvironmentを1つだけ使用し、より複雑なアプリケーションではModelInstanceの位置に応じた複数のEnvironmentを使用しているかもしれません。 1つのModelInstance (または Renderable) は1つの Environment しか保持することができないにもかかわらずです。
Environment クラスは Attribute クラスを継承しています。Attributeの詳細については後述する内容を参照してください。
バージョン 1.5.7 から、lights は attributesへ移行されました。つまり、environment や materialにlight を取り付けることができます。
environment.add(light)
メソッドを使用してenvironment にlight を追加する方法は今でも行うことができます。
しかし、 DirectionalLightsAttribute
属性とPointLightsAttribute
属性と SpotLightsAttribute
属性を使用することもできます(下記参照)。
これらの各属性には、1つ以上のlightを取り付けるのに使用できる配列を持っています。
ただし、通常はどちらか一方しか使用できないので注意してください。
light をenvironment のPointLightsAttribute
に追加して、それから別のlight をmaterialのPointLightsAttribute
に追加した場合、
DefaultShader
はenvironmentに追加されたpoint lightを無視します。
Light は常に参照によって使用されます。
Lightは重要度によって並べ替える必要があります。通常これは、 lightsは距離によって並べ替える必要があるということを意味します。
例えば、既定では DefaultShader
(設定可能)は、シェーダーライティングに最初の5個のpoint lightを使用します。残りの照明は周囲のキューブマップに追加されますが、これは正確な処理とは言えません。
Environmentクラスと Material クラスは両方とも Attributeクラスを継承しています。 最も一般的には、Attributeクラスはユニフォーム値を設定するのに使用されます。 For example a TextureAttribute can be used to specify an uniform to bound for a shader. However, attributes don't have to be uniforms, for example DepthTestAttribute is used to alter the opengl state and doesn't set an uniform.
Attributeクラスは Setに最も似ています。 ユニフォーム変数に1つの値しか設定できないように、Attributeクラスでは各属性ごとに最大でも1つの値しか持つことができません。 理論的にはMaterial とEnvironment は両方とも同じ属性を持つことができ、この場合の実際の動きがどうなるかは使用するシェーダーに依存します。 ですが、大抵の場合はEnvironment の属性ではなくMaterialの属性が使用されるでしょう。
全ての属性はlong 型の値 type
を持っており、これは属性を識別するのに使用されるビットマスクです。
したがって、完成したmaterial やenvironment は1つのlong値を使って表すことができます。long値の各ビットが属性を表します。
いくつかのattribute クラスは単一のタイプの値(bit)専用です。
それ以外のattribute クラスは複数のタイプの値(bits)のために使用され、この場合コンストラクターでタイプを指定する必要があります。
例えば:
Attribute attribute = new ColorAttribute(ColorAttribute.Diffuse, Color.RED);
ColorAttribute クラスには同じことを行える便利なメソッドが実装されているので覚えておいてください:
Attribute attribute = ColorAttribute.createDiffuse(Color.RED);
属性で行う最も一般的なアクションは、 set
と has
とremove
と get
です。
set
メソッドを使うと属性の追加や変更を行うことができます。If there is already an attribute of the same type if will be first removed, after which the new attribute is added. 例: material.set(FloatAttribute.createAlphaTest(0.25f));
has
メソッドを使用すると、特定の属性タイプが設定されているかを確認できます。
例えば: material.has(FloatAttribute.AlphaTest);
。
また、複数の属性を確認することもできます。例: material.has(FloatAttribute.AlphaTest | ColorAttribute.Diffuse);
。
この場合、has
メソッドは全ての属性が設定されている場合のみtrue
を返します。
has
メソッドはビットマスクを使用しているため非常に高速なので、例えば remove
を呼び出す前にその属性が実際に設定されているかどうかを確認するのに使えます。覚えておいてください。
remove
メソッドを使うことで、特定のタイプの属性を削除することができます。例:material.remove(FloatAttribute.AlphaTest);
。
また一度に複数の属性を削除することもできます。例: material.remove(FloatAttribute.AlphaTest | ColorAttribute.Diffuse);
。
get
メソッドを使って特定のタイプの属性を取得することができます。例: material.get(FloatAttribute.AlphaTest);
。
その属性が設定されていなかった場合、get
メソッドは null
を返します。
タイプはクラスを示しているので、型の確認をしなくても安全に戻り値をキャストできます: (FloatAttribute)material.get(FloatAttribute.AlphaTest);
。
便宜上、テンプレートメソッドもあります: material.get(FloatAttribute.class, FloatAttribute.AlphaTest);
。
それ以外にも、clear
(全ての属性を削除する)を使ったり、反復処理(Iterable<Attribute>
を実装しています)を行ったり、属性の比較を行ったり (Comparator<Attribute>
を実装していますが、same
メソッドでは追加のオブションがあります)もできます。
getMask()
メソッドを使うと、全ての属性を含むマスクにアクセスできます。例えば material.getMask() & FloatAttribute.AlphaTest == FloatAttribute.AlphaTest
は material.has(FloatAttribute.AlphaTest)
と同じです。
新たなカスタムタイプに標準のattribute クラスを使用することが可能です。 例えば、ColorAttribute クラスを使って色のカスタムタイプを設定したい場合です。 この場合、考慮しなければならないことが三つあります:
attribute.toString()
を呼び出す時。
ColorAttribute.Diffuse
タイプのみを受け入れるので、(ColorAttribute)material.get(ColorAttribute.Diffuse)
は問題なく動作します。
ステップ 2とステップ3のために、Attribute クラスを拡張してカスタム属性タイプを追加する必要があります。:
public class CustomColorTypes extends ColorAttribute { public final static String AlbedoColorAlias = "AlbedoColor"; // step 1: name the type public final static long AlbedoColor = register(AlbedoColorAlias); // step 2: register the type static { Mask |= AlbedoColor; // step 3: Make ColorAttribute accept the type } /** Prevent instantiating this class */ private CustomColorTypes() { super(0); } }
カスタム属性タイプを作成するには以下のようにします:
Attribute attribute = new ColorAttribute(CustomColorTypes.AlbedoColor, Color.RED);
カスタム属性を作成することが可能です。この場合の手順は上記のものとあまり変わりません。 Attribute クラスを継承し、少なくとも1つのタイプを登録し、そしてもちろんシェーダーに渡すいくつかのデータを追加する必要があります。 例えば、シェーダーにdouble 値を渡すための属性を追加するには:
public class DoubleAttribute extends Attribute { public final static String MyDouble1Alias = "myDouble1"; public final static long MyDouble1 = register(MyDouble1Alias); public final static String MyDouble2Alias = "myDouble2"; public final static long MyDouble2 = register(MyDouble2Alias); protected static long Mask = MyDouble1 | MyDouble2; /** Method to check whether the specified type is a valid DoubleAttribute type */ public static Boolean is(final long type) { return (type & Mask) != 0; } public double value; public DoubleAttribute (final long type) { super(type); if (!is(type)) throw new GdxRuntimeException("Invalid type specified"); } public DoubleAttribute (final long type, final double value) { this(type); this.value = value; } /** copy constructor */ public DoubleAttribute (DoubleAttribute other) { this(other.type, other.value); } @Override public Attribute copy () { return new DoubleAttribute(this); } @Override public int hashCode () { final int prime = /* pick a prime number and use it here */; final long v = NumberUtils.doubleToLongBits(value); return prime * super.hashCode() + (int)(v^(v>>>32)); } @Override public int compareTo (Attribute o) { if (type != o.type) return type < o.type ? -1 : 1; double otherValue = ((DoubleAttribute)o).value; return value == otherValue ? 0 : (value < otherValue ? -1 : 1); } }
もちろん、 MyDouble1Alias
と MyDouble1
と "myDouble1"
と MyDouble2Alias
と MyDouble2
と "myDouble2"
はより意味のある名前に変えたほうが良いでしょう。
例えば、 Model
の ModelInstance
を作成した時にはcopy()
メソッドが呼び出されるので注意してください。
このメソッドではコピーされる属性に関わらず、編集可能な同一の属性インスタンスを返すべきです。
必須ではありませんが、 一般的にコピーコンストラクタはこれを実装するための良い方法です。
属性とマテリアルの比較に使用されるので、hashCode()
メソッドを実装する必要があります。
例えば、二つのマテリアルが同じ属性(タイプ)を持ち、 equals()
メソッドが属性タイプの各組み合わせごとにtrue を返した場合、それらは同じものと見なされます。
既定では、この場合 Attribute
クラスのequals()
メソッドは両方の属性の hashCode()
を比較します。
マテリアルに基づいてrender 呼び出しを並び替えるために、compareTo(Attribute)
メソッドを実装しなければなりません。
一般的にこの実装では常に最初にif (type != o.type) return type < o.type ? -1 : 1;
の行を記述し、確実に同じタイプの属性を比較するようにします。
Attribute クラスは小さく自己完結型であるようにする必要があり、したがって常に
Attribute
クラスを直接継承するのがベストです。 Attribute クラスのサブクラスを継承して追加情報を追加するのは避けるようにしてください。
上記のようにカスタム属性を作成することは可能です。しかし既に用意されている属性がいくつかあります。それらを以下に一覧表記します。
既定では、 3D api は全てのものが不透明であると想定しています。BlendingAttribute
はマテリアルで最も一般的に使用されるもので (environmentの場合は既定動作が変わります。)、そのマテリアルにブレンド処理を行うかどうかを指定するのに使います。
BlendingAttribute
では作成時にタイプを指定する必要はなく、拡張していなければそのタイプは常にBlendingAttribute.Type
となります。
BlendingAttribute
は設定可能な四つのプロパティを持っています:
blended
はこのマテリアルをブレンド物として扱うかどうかを示します。これは主に並び替えで使用されます。例えば、透明なオブジェクトを描写するより前に不透明なオブジェクトの描写が行われます。sourceFunction
は OpenGL の列挙体です。 赤、緑、青、透明度といったソースブレンドファクター(新たに描写するもの)をどのように計算するのかを設定します。
既定では GL_SRC_ALPHAが設定されています。destFunction
は OpenGL の列挙体です。赤、緑、青、透明度といったデスティネーションブレンドファクター(既に描写されているもの)をどのように計算するのかを設定します。
既定では GL_ONE_MINUS_SRC_ALPHAが設定されています。
加算ブレンド処理の場合はGL_ONEを設定した方がいいでしょう。
opacity
は透明度の値です(ソースのアルファ値)。範囲は0(完全に透明)から1(完全に不透明)になります。
ColorAttribute
を使うとシェーダーに色を渡すことができます。そのため、これは1つのプロパティ: .color
を持っているだけです。
インスタンス作成時(値で設定します)、もしくは.color.set(...)
メソッドを使用して色を設定できます。
ColorAttribute
では属性タイプを設定する必要があります、既定では以下のタイプが使用できます:
ColorAttribute.Diffuse
ColorAttribute.Specular
ColorAttribute.Ambient
ColorAttribute.Emissive
ColorAttribute.Reflection
ColorAttribute.AmbientLight
ColorAttribute.Fog
最後の二つはEnvironmentで最もよく使用され、他はMaterialで最もよく使用されます。
Cubemap
をシェーダーに渡すためには、CubemapAttribute
が使用できます。
textureDescription
メンバを使って、Cubemap
と一緒にテクスチャ関連値を設定します。
CubemapAttribute
では属性タイプを設定する必要があります、既定ではCubemapAttribute.EnvironmentMap
のみが使用できます:
BlendingAttribute
と同じ様に、DepthTestAttribute
では属性タイプを必要としません。
属性タイプは常にDepthTestAttribute.Type
となります。DepthTestAttribute
は深度テストや深度バッファへの書き込みを指定するのに使われます。
以下のプロパティが使用できます。:
depthFunc
は深度テスト機能を表し、 0 (もしくは GL_NONE)の場合は深度テストは無効になります。既定では GL20.GL_LEQUALとなっています。depthRangeNear
はニアクリッププレーンとウィンドウ座標との紐付けで、既定は0.0です。depthRangeFar
はファークリッププレーンとウィンドウ座標との紐付けで、既定は 1.0です。depthMask
は深度バッファ への書き込みを行うかどうかで、既定では有効となっています。
DirectionalLightsAttribute は属性タイプを必要としません。属性タイプは常にDirectionalLightsAttribute.Type
となります。
DirectionalLightsAttribute
を使用してDirectionalLight
インスタンスの配列を設定できます。
以下のプロパティを使って設定します。:
lights
:lightの配列。重要度に応じて並べられる必要があります。
1つの浮動小数点値をシェーダーへ渡すためには、FloatAttribute
を使用できます。
この値はインスタンス作成時か、もしくは .value
メンバを使って設定できます。
FloatAttribute
では属性タイプが必要で、既定では以下が設定できます:
FloatAttribute.Shininess
は面鏡照明に使用されます。
FloatAttribute.AlphaTest
はアルファ値が指定した値以下だった場合にピクセルを破棄するために使用されます。
FloatAttribute
クラスと同様に、 IntAttribute
を使うと整数値をシェーダーに渡すことができます。
同様に .value
メンバを使用したり、インスタンス作成時に値を設定したりできます。
IntAttribute
では属性タイプが必要で、既定では以下が設定できます:
IntAttribute.CullFace
はOpenGL の列挙体で、GL_NONE (カリング処理はしない)か GL_FRONT (背面のみ描写する) か GL_BACK (前面のみ描写する)で面カリングを設定します。
既定値はシェーダーによって変わり、デフォルトシェーダーでは既定ではGL_BACKを使用します。
PointLightsAttribute は属性タイプを必要としません。属性タイプは常にPointLightsAttribute.Type
となります。
PointLightsAttribute
を使用してPointLight
インスタンスの配列を設定できます。
以下のプロパティを使って設定します:
lights
:lightの配列。重要度に応じて並べられる必要があります。
デフォルトシェーダーではサポートされていません。SpotLightsAttribute は属性タイプを必要としません。
属性タイプは常に SpotLightsAttribute.Type
となります。
SpotLightsAttribute
を使ってSpotLight
インスタンスの配列を設定できます。以下のプロパティを使って設定します:
lights
:lightの配列。重要度に応じて並べられる必要があります。
TextureAttribute
を使用して Texture
をシェーダーへ渡すことができます。
CubemapAttribute
と同様に、TextureAttribute
はtextureDescription
メンバを持っています。
これを使ってTexture
と一緒に repeat や filterのようなテクスチャ関連値を設定できます。
さらに、使用するテクスチャの領域(テクスチャ座標変換)を設定するのに使用できるoffsetU
メンバと offsetV
メンバと scaleU
メンバと scaleY
メンバを持っています。
また、どのテクスチャ座標を使用するのかを設定するのに使用できる uvIndex
メンバ(既定値は0)も持っています。
現時点では、デフォルトシェーダーはこのuvIndexメンバを無視して常に最初のテクスチャ座標を使用するので注意してください。
TextureAttribute
は属性タイプが必要で、既定では以下のうち1つを設定できます:
TextureAttribute.Diffuse
TextureAttribute.Specular
TextureAttribute.Bump
TextureAttribute.Normal