Edited at

EmissiveMapを使ってマイクラのエンチャントを再現する


EmissiveMapとは

テクスチャマップを部分的に光っているようにみせることができるマッピング画像で、Three.jsでは MeshPhongMaterialや、MeshLambertMaterialなどのoptionで指定することができます。

AlphaMapと同じグレイスケールの画像で、白い部分はemissiveプロパティに指定した色で光って見えるようになり、黒い部分は元のまま表示されます。

EmissiveMapは、テクスチャに部分的に色を乗せるイメージではなく、半透明レイヤーのように独立しているイメージです。

またMeshBasicMaterialのようにライティングの影響を受けないため、暗いライティングでも光って見えます。


指定方法

    // マテリアルの作成

let material = new THREE.MeshLambertMaterial({
map: texture,
emissiveMap: emissiveTexture, /// グレイスケールのTHREE.Textureオブジェクト
emissive: "#FF0000", /// 光らせたい色
})

ざんねんながら、複数の色は指定できません。

    // マテリアルの作成

let material = new THREE.MeshLambertMaterial({
map: texture,
emissiveMap: [emissiveTexture1, emissiveTexture2],
emissive: ["#FF0000", "#0000FF"]
})
//////// 何も起きない ////////


See the Pen
Minecraft Enchanted item
by Urushibara (@pneuma01)
on CodePen.


解説のようなもの


モデルの初期化

ゆらめく様にアニメーションさせたいので、あらかじめアニメーション分のEmissiveMapを作成しておきます。


// Sceneの初期化
const scene = new THREE.Scene();

// Geometryの作成
let img = document.querySelector("#item");
let canvas = createCanvas(img.width, img.height);
canvas.context.drawImage(img, 0, 0);
let geometry = new THREE.PixelArtGeometry(canvas.canvas, 1);
canvas.canvas.remove();

// 基本テクスチャの作成
let texture = new THREE.TextureLoader().load(img.src);
texture.magFilter = THREE.NearestFilter;
texture.minFilter = THREE.NearestFilter;
texture.type = THREE.FloatType;

// アニメーション Emissive Map の作成
let emissiveMaps = [];
for(let i=0; i<56; i++){
let canvas = drawAnimated(i);
let texture = new THREE.CanvasTexture(canvas);
texture.magFilter = THREE.NearestFilter;
texture.minFilter = THREE.NearestFilter;
texture.type = THREE.FloatType;
emissiveMaps.push(texture);
}

// マテリアルの作成
let material = new THREE.MeshLambertMaterial({
map: texture,
emissiveMap: emissiveMaps[0],
emissive: "#7F007F", /// Emissive Color
transparent: false,
side: THREE.DoubleSide,
alphaTest: 0.99
})

// Mesh の作成
let box = new THREE.Mesh(
geometry,
material
);
box.scale.set(10, 10, 10);
main_object = box;

// Sceneへ追加
scene.add(main_object);


アニメーション

描画tick呼び出し時に、emissiveMapを次々と入れ替えます。

CanvasTextureを使ってリアルタイムでEmissiveMapを描画するのも試してみましたが、かなり重たかったのでやめました。


//// Tick ////
function tick() {

// オブジェクトの位置
pos_cycle = pos_cycle < 360 ? pos_cycle + 1 : 0;
var y = Math.sin(deg2rad(pos_cycle * 2)) * 20;
main_object.position.set(0, -y, 0);
main_object.rotation.set(0, deg2rad(pos_cycle * 1), 0);

// Emissive Map の切り替え
main_object.material.emissiveMap = emissiveMaps[Math.floor(glint_cycle)];
glint_cycle = glint_cycle < emissiveMaps.length ? glint_cycle + 0.1 : 0;

// 描画
renderer.render(scene, camera);
requestAnimationFrame(tick);
}