3D オブジェクトのマテリアル ( 表面材質 ) のお世話 後編 ( Android OpenGL フレームワーク “Rajawali” と戯れる #04 )

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

Android OpenGL フレームワーク "Rajawali" と戯れるシリーズ
第 04 回目は、引き続き Rajawali が提供しているマテリアル ( 表面材質 ) のクラスについて説明します。

環境マッピング

環境マッピング (environment mapping) とは、3DCG におけるテクスチャマッピングの手法の 1 つで、3D 形状の表面に擬似的な周囲環境の映り込みを再現する手法です。詳しい説明はこちら (Wikipedia) を参考にしてください。

Rajawali では、下記 2 種類の環境マッピング用マテリアルクラスが用意されています。

CubeMapMaterial

キューブマップのマテリアルです。
対象のオブジェクトの 6 方向 ( +x, -x, +y, -y, +z, -z ) のイメージやライティング効果を表現したテクスチャとして 3D オブジェクトに適用します。

SphereMapMaterial

球状マップのマテリアルです。
対象のオブジェクトの周囲の空間やライティング効果を表現した特殊テクスチャとして 3D オブジェクトに適用します。

環境マッピング用テクスチャの作り方

池田先生のブログで紹介されていた Pano2VR のトライアル版をお試しで使ってみたのですがイイ感じです。
作業の流れについては下記記事を参考にしてください。

CubicVRをPapervision3D 2.0で作るPart.02(Cube編)

CubicVRをPapervision3D 2.0で作るPart.03(Sphere編)

バンプマッピング

バンプマッピング (bump mapping) とは、レンダリングする 3D オブジェクトの面の法線に対する揺らぎをハイトマップ ( 高低マップ ) で調べて、光源計算の完了前に各ピクセルに対して適用する技術です。詳しい説明はこちら (Wikipedia) を参考にしてください。

Rajawali では、BumpmapMaterial という マテリアルクラスが用意されています。

実装例

CubeMapMaterial

Lesson03_01Renderer.java

package jp.classmethod.sample.renderer;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import jp.classmethod.sample.R;
import rajawali.lights.DirectionalLight;
import rajawali.materials.CubeMapMaterial;
import rajawali.materials.SimpleMaterial;
import rajawali.materials.TextureInfo;
import rajawali.materials.TextureManager.TextureType;
import rajawali.materials.TextureManager.WrapType;
import rajawali.primitives.Plane;
import rajawali.primitives.Sphere;
import rajawali.renderer.RajawaliRenderer;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
/**
 * RajawaliRenderer のサブクラス
 */
public class Lesson03_01Renderer extends RajawaliRenderer {
    /**
     * コンストラクタ
     */
    public Lesson03_01Renderer(Context context) {
        super(context);
    }
    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        super.onSurfaceCreated(gl, config);

        Resources r = mContext.getResources();

        // Background
        Bitmap bg = BitmapFactory.decodeResource(r, R.drawable.background);

        TextureInfo backgroundTextureInfo = mTextureManager.addTexture(bg);
        backgroundTextureInfo.setWrapType(WrapType.CLAMP);

        SimpleMaterial simpleMaterial = new SimpleMaterial();
        simpleMaterial.addTexture(backgroundTextureInfo);

        Plane plane = new Plane(5, 10, 1, 1);
        plane.setRotZ(-90);
        plane.setPosition(0.0f, 0.0f, 1.0f);
        plane.setMaterial(simpleMaterial, true);


        // CubeMapMaterial
        Bitmap[] textures = new Bitmap[6];
        int i = 0;
        textures[i++] = BitmapFactory.decodeResource(r, R.drawable.background_cuberight); // +X
        textures[i++] = BitmapFactory.decodeResource(r, R.drawable.background_cubeleft);  // -X
        textures[i++] = BitmapFactory.decodeResource(r, R.drawable.background_cubetop);   // +Y
        textures[i++] = BitmapFactory.decodeResource(r, R.drawable.background_cubebottom);// -Y
        textures[i++] = BitmapFactory.decodeResource(r, R.drawable.background_cubefront); // +Z
        textures[i++] = BitmapFactory.decodeResource(r, R.drawable.background_cubeback);  // -Z

        TextureInfo cubeTextureInfo = mTextureManager.addCubemapTextures(textures);
        cubeTextureInfo.setTextureType(TextureType.CUBE_MAP);
        cubeTextureInfo.setWidth(300);
        cubeTextureInfo.setHeight(300);

        CubeMapMaterial cubeMapMaterial;
        cubeMapMaterial = new CubeMapMaterial();
        cubeMapMaterial.addTexture(cubeTextureInfo);

        DirectionalLight light = new DirectionalLight();
        light.setPower(2f);
        light.setPosition(-0.5f, 0.3f, -5.0f);

        Sphere sphere = new Sphere(1.0f, 36, 36);
        sphere.setMaterial(cubeMapMaterial);
        sphere.addLight(light);

        addChild(plane);
        addChild(sphere);

        startRendering();

    }
}

RajawaliRenderer が保持している mTextureManager というテクスチャ制御マネージャのインスタンスを使用して、テクスチャ情報 (TextureInfo) を作成します。用意したテクスチャ情報をマテリアルにセットしたあと setMaterial() すれば完成…というシンプルな実装方法です。ちなみにキューブマップでは、テクスチャ画像を 6 つ用意する必要があります。

SphereMapMaterial

Lesson03_02Renderer.java

package jp.classmethod.sample.renderer;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import jp.classmethod.sample.R;
import rajawali.lights.DirectionalLight;
import rajawali.materials.SimpleMaterial;
import rajawali.materials.SphereMapMaterial;
import rajawali.materials.TextureInfo;
import rajawali.materials.TextureManager.TextureType;
import rajawali.materials.TextureManager.WrapType;
import rajawali.primitives.Plane;
import rajawali.primitives.Sphere;
import rajawali.renderer.RajawaliRenderer;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
/**
 * RajawaliRenderer のサブクラス
 */
public class Lesson03_02Renderer extends RajawaliRenderer {
    /**
     * コンストラクタ
     */
    public Lesson03_02Renderer(Context context) {
        super(context);
    }
    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        super.onSurfaceCreated(gl, config);

        Resources r = mContext.getResources();

        // Background
        Bitmap bg = BitmapFactory.decodeResource(r, R.drawable.background);

        TextureInfo backgroundTextureInfo = mTextureManager.addTexture(bg);
        backgroundTextureInfo.setWrapType(WrapType.CLAMP);

        SimpleMaterial simpleMaterial = new SimpleMaterial();
        simpleMaterial.addTexture(backgroundTextureInfo);

        Plane plane = new Plane(5, 10, 1, 1);
        plane.setRotZ(-90);
        plane.setPosition(0.0f, 0.0f, 1.0f);
        plane.setMaterial(simpleMaterial, true);


        // SphereMapMaterial
        SphereMapMaterial material = new SphereMapMaterial();
        material.setSphereMapStrength(.4f);
        Bitmap sphereMap = BitmapFactory.decodeResource(r, R.drawable.background);
//        Bitmap texture = BitmapFactory.decodeResource(r, R.drawable.hirai);

        DirectionalLight light = new DirectionalLight();
        light.setPower(2f);
        light.setPosition(-0.5f, 0.3f, -5.0f);

        Sphere sphere = new Sphere(1.0f, 36, 36);
        sphere.setMaterial(material);
        sphere.setColor(0xFF999999);
        sphere.addLight(light);

        sphere.addTexture(mTextureManager.addTexture(sphereMap, TextureType.SPHERE_MAP));
//        sphere.addTexture(mTextureManager.addTexture(texture, TextureType.DIFFUSE));

        sphere.setRotY(45);

        addChild(plane);
        addChild(sphere);

        startRendering();

    }
}

BumpmapMaterial

Lesson03_03Renderer.java

package jp.classmethod.sample.renderer;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import jp.classmethod.sample.R;
import rajawali.BaseObject3D;
import rajawali.animation.Animation3D;
import rajawali.animation.RotateAroundAnimation3D;
import rajawali.lights.PointLight;
import rajawali.materials.BumpmapMaterial;
import rajawali.materials.TextureManager.TextureType;
import rajawali.math.Number3D;
import rajawali.math.Number3D.Axis;
import rajawali.primitives.Plane;
import rajawali.renderer.RajawaliRenderer;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
/**
 * RajawaliRenderer のサブクラス
 */
public class Lesson03_03Renderer extends RajawaliRenderer {
    /**
     * @private
     */
    private Plane mPlane;
    /**
     * コンストラクタ
     */
    public Lesson03_03Renderer(Context context) {
        super(context);
        setFrameRate(60);
        setBackgroundColor(0xffffff);
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        super.onSurfaceCreated(gl, config);

        Resources r = mContext.getResources();

        mCamera.setPosition(0, 0, -6);

        PointLight light = new PointLight();
        light.setPosition(-2, -2, -8);
        light.setPower(4f);

        mPlane = new Plane(8, 8, 1, 1);//objParser.getParsedObject();
        mPlane.addLight(light);
        mPlane.setRotX(-45);
        mPlane.setRotZ(-90);
        addChild(mPlane);

        Bitmap diffuseTexture = BitmapFactory.decodeResource(r, R.drawable.hirai_bg);
        Bitmap bumpTexture = BitmapFactory.decodeResource(r, R.drawable.hirai_bump);

        mPlane.setMaterial(new BumpmapMaterial());
        mPlane.addTexture(mTextureManager.addTexture(diffuseTexture, TextureType.DIFFUSE));
        mPlane.addTexture(mTextureManager.addTexture(bumpTexture, TextureType.BUMP));

        Animation3D lightAnimation = new RotateAroundAnimation3D(new Number3D(0, 0, -4), Axis.Z, 4);
        lightAnimation.setDuration(3000);
        lightAnimation.setRepeatCount(Animation3D.INFINITE);
        lightAnimation.setTransformable3D(light);
        lightAnimation.start();
    }
    @Override
    public void onDrawFrame(GL10 glUnused) {
        super.onDrawFrame(glUnused);
        rotateObject(mPlane);
    }
    /**
     * 3D オブジェクトの回転
     * @param obj 任意の BaseObject3D オブジェクト
     */
    protected void rotateObject(BaseObject3D obj) {
        obj.setRotation(obj.getRotX(),obj.getRotY(), obj.getRotZ() + 0.1f);
    }
}

出力結果

CubeMapMaterial

TextureType.CUBE_MAP

SphereMapMaterial

TextureType.SPHERE_MAP

TextureType.SPHERE_MAP + TextureType.DIFFUSE

BumpmapMaterial

TextureType.BUMP + TextureType.DIFFUSE

若干「コレジャナイ感」を感じますが…

次回

パーティクルについて解説します。