three.js で 環境マッピング
three.js/examples/ の materials/cars/camaro のようなことがやりたくて色々試したのでほぼ自分用のメモ。
公式からダウンロードのthreejsフォルダ/utils/convertres.obj/convert_obj_three.py
を使用して obj 形式のモデルデータ(今回モデルはこちらを使用させていただきました)を下記コマンドで json に変換。
python convert_obj_three.py -i hogehoge.obj -o hagehage.js
頂点バッファをバイナリ形式で分離する場合は -t パラメータに binary を指定すれば公式サンプルと同じ形式にできます。
python convert_obj_three.py -i hogehoge.obj -o hagehage.js -t binary
変換した json データ(上の python コマンド例だと hagehage.js )はこんな感じ。
{ "metadata" : { "formatVersion" : 3.1, "sourceFile" : "hogehoge.obj", "generatedBy" : "OBJConverter", "vertices" : 29340, "faces" : 26193, "normals" : 19819, "colors" : 0, "uvs" : 22171, "materials" : 23 }, "scale" : 1.000000, "materials": [ { "DbgColor" : 15658734, "DbgIndex" : 0, "DbgName" : "body" }, { "DbgColor" : 15597568, "DbgIndex" : 1, "DbgName" : "tire" }, { "DbgColor" : 60928, "DbgIndex" : 2, "DbgName" : "window" }, { "DbgColor" : 238, "DbgIndex" : 3, "DbgName" : "Disk" }, . . . }], "vertices": [頂点バッファ] }
頂点バッファをバイナリで変換した場合は “vertices”: [頂点バッファ] の部分が “buffers”: “hagehage.bin” となります。
この中でのマテリアルに関係するデータは、
.metadata.materials に マテリアルの数。
.materials[n].DbgIndex が マテリアルのインデックス。
.materials[n].DbgName が モデル上で設定されたマテリアルの名前。
この materials[n] にオーサリング上で設定したマテリアルを動的に適用すれば、読み込んだモデルに対して threejs 側で設定したマテリアルを動的に適用できます。
今回のサンプルでの three.js 側の主要なコードはこんな感じ。
環境マップ用テクスチャをロードして textureCube を作成
var r = "cube_tex/"; var urls = [ r + "px.jpg", r + "nx.jpg", r + "py.jpg", r + "ny.jpg", r + "pz.jpg", r + "nz.jpg" ]; var textureCube = THREE.ImageUtils.loadTextureCube( urls );
各マテリアルを作成
ボディ・グラス・クロームには textureCube を適用して反射させる
carMaterials = { body: { Orange: new THREE.MeshLambertMaterial( { color: 0xff6600, envMap: textureCube, combine: THREE.MixOperation, reflectivity: 0.3, side: THREE.DoubleSide } ), Blue: new THREE.MeshLambertMaterial( { color: 0x226699, envMap: textureCube, combine: THREE.MixOperation, reflectivity: 0.3, side: THREE.DoubleSide } ) . . . }, chrome: new THREE.MeshLambertMaterial( { color: 0xffffff, envMap: textureCube, side: THREE.DoubleSide } ), . . . black: new THREE.MeshLambertMaterial( { color: 0x000000 } ) };
THREE.JSONLoader でモデルを読み込み
var loader = new THREE.JSONLoader(); loader.load( "obj/hagehage.js", onLoad); function onLoad( geometry ) { createScene( geometry, carMaterials ); }
頂点バッファをバイナリ化した場合は THREE.BinaryLoader でモデルを読み込み
var loader = new THREE.BinaryLoader(); loader.load( "obj/hagehage.js", onLoad); function onLoad( geometry ) { createScene( geometry, carMaterials ); }
両方試してみたところ、頂点をバイナリ化するほうが少し読み込みが早い気がしましたが、これは気のせいかもしれません。
モデルの各部にマテリアルを適用
function createScene( geometry, materials ) { var s = 200, m = new THREE.MeshFaceMaterial(); m.materials[ 0 ] = materials.body[ "Orange" ]; m.materials[ 1 ] = materials.tire; m.materials[ 2 ] = materials.glass; m.materials[ 3 ] = materials.chrome; m.materials[ 4 ] = materials.chrome; m.materials[ 5 ] = materials.black; m.materials[ 6 ] = materials.orange; . . . // メッシュを作成 var mesh = new THREE.Mesh( geometry, m ); mesh.rotation.y = 1; mesh.scale.set( s, s, s ); scene.add( mesh ); }
今回のデモでは、車のモデルのポリゴンがおかしくなってしまっている部分がありますが、今回はテストということでダウンロードしたモデルを Blender で読み込んでマテリアルだけ設定して Obj で書き出して json 変換しただけなのでそこはご愛敬ということで…
※ 2015.08.13 車のボディまわり他のマテリアルが表面しか描画しない設定になっていた部分を両面描画に修正しました。