カテゴリー別アーカイブ: Blender

Three.js の Blender エクスポータを利用する


Three.js には Blender 用のエクスポートスクリプトが付属しています。これを利用すると Blender で作成したモデルデータを Three.js で簡単に使うことが出来ます。

エクスポータのインストール

さっそく Blender でエクスポータを使えるようにしてみましょう。

エクスポータの設置

エクスポータは “three.js/utils/exporters/blender” 以下に入っています。このディレクトリの下に Blender のバージョンに合わせたディレクトリがあり、その下の “scripts” ディレクトリを Blender の “scripts” ディレクトリにコピーします。(バージョンが合っていなくても大抵は問題なく動作すると思います)

エクスポータの有効化

次に設置したエクスポータを有効化します。 Blender を起動し “User Preferences” で “Addons” タブを選択し、 “Import-Export” カテゴリを選択します。

恐らく一番下に “Import-Export: three.js format” という項目があると思うので、右側のチェックボックスを有効にします。

すると “File” メニューの “Export” に “Three.js(.js)” が追加されます。あとは適当なオプションを選択して保存すれば OK です。とりあえず今回はデフォルトの状態で出力をしました。

モデルデータを WordPress(サーバ) にアップロードする

それでは作成したモデルデータ(.js ファイル)を WordPress にアップロードしてみましょう(外部からアクセス出来るのであればレンタルサーバなどにアップロードしても構いません)。

「メディア」 → 「新規追加」から対象のファイルをアップロードします。

アップロードしたモデルデータを Three.js から利用する

それではアップロードしたモデルデータを利用してレンダリングを行ってみます。解説は省きますがそれぞれに対応する行数を書いておきます。

  • ライトを追加する(16 から 18 行目)
  • JSONLoader を使ってモデルデータをロードする(24 から 25 行目)
  • ロードしたジオメトリデータにマテリアルを付けてシーンに追加する(30 から 35 行目)

今回はモデルデータが小さかったので、 33 行目で 100 倍に拡大するように指定しています。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<div id="div_canvas" style="text-align:center;">
<script>
var camera, scene, renderer,
    geometry, material, mesh;
 
init();
animate();
 
function init() {
    scene = new THREE.Scene();
 
    camera = new THREE.PerspectiveCamera( 75, 300 / 300, 1, 10000 );
    camera.position.z = 1000;
    scene.add( camera );
 
    light = new THREE.DirectionalLight( 0xffffff );
    light.position.set( 0, 0, 1 ).normalize();
    scene.add( light );
 
    div_canvas = document.getElementById( 'div_canvas' );
    renderer = new THREE.WebGLRenderer( { antialias: true } );
    renderer.setSize( 300, 300 );
 
    var loader = new THREE.JSONLoader();
    loader.load( "http://www.soft-syokunin.com/wp-content/uploads/happy_buddha.js", createScene );
 
    div_canvas.appendChild( renderer.domElement );
}
 
function createScene( geometry ) {
    var material = new THREE.MeshFaceMaterial();
    mesh = new THREE.Mesh( geometry, material );
    mesh.scale.x = mesh.scale.y = mesh.scale.z = 100;
    scene.add( mesh );
}
 
function animate() {
    // note: three.js includes requestAnimationFrame shim
    requestAnimationFrame( animate );
    render();
}
 
function render() {
    mesh.rotation.x += 0.01;
    mesh.rotation.y += 0.02;
 
    renderer.render( scene, camera );
}
</script>
</div>

今回は下記のリンクから “Happy Buddha” の一番解像度の荒いデータを使ってみました。
The Stanford 3D Scanning Repository
モデルデータを用意することや、それを読み込んで使うことは 3D グラフィクスを始めたばかりの障壁の一つでしたが全てフリーで出来るというのは嬉しいことですね。

Blender でのポリゴンリダクション(Decimator)


モデリングをしたり、ハイトマップから自動生成する過程ではポリゴン数が多くなりがちです。本職のモデラーさんならばポリゴン数を意識しながらモデリングするのも簡単かもしれません。ですが、私のようなプログラマーがテストデータとして作っている場合は難しいといえます。

そんな場合はポリゴン数が多くなっても形を作ってからポリゴン数を削減(これを一般的にポリゴンリダクションと言います)するという方法が簡単です。 Blender では Modifier 中の Decimator という機能を使うと実現出来ます。

オブジェクトモードで操作をします。 “Modifier” メニューから “Decimator” を追加します。

“Decimator” を追加したら “Ratio” を操作してポリゴン(Face) の数を調節します。 “Ratio” が 1.00 の時はポリゴン数が “968” です。

“Ratio” を 0.3 くらいまで下げるとポリゴン数が 300 台まで下がります。ちょうど良い数まで下がったら “Apply” ボタンを押してオブジェクトに適用します。

Decimator は「なるべく」元の形を保ったままポリゴン数を少なくする機能なので、ポリゴン数と見た目のバランスがちょうど良い点を見つけるのがポイントになります。あるいは Decimator でポリゴンを削減してから形を整えるというアプローチも良いと思います。

注意

ただし、地形のモデルなどを綺麗なグリッド状に保ちたい場合、 Decimator を使うと形が崩れてしまいますので注意が必要です。

Blender 2.5 Python スクリプトによるエクスポータの作成 – UV座標の出力 –


せっかくここまでやったので、UV 座標の出力もやってしまいます。今まで通りドキュメントを参照しながらやるのですが、UV まわりは少しデータ構造が複雑ですね。テクスチャを複数持っていたり、一つの頂点が複数の UV 座標を持っていることもあるようです。とりあえず今回は最初のデータのみを扱うことにしました。

これだけのデータがあれば、簡単なプログラムには使えそうですね。

def write_some_data(context, filepath, use_some_setting):
    print("running write_some_data...")
    f = open(filepath, 'w')
    obj = bpy.context.selected_objects[0]
    mesh = obj.data
 
    if obj.mode == 'OBJECT':
        bpy.ops.object.editmode_toggle()
    bpy.ops.mesh.quads_convert_to_tris()
    bpy.ops.object.editmode_toggle()
 
    f.write( "Model : \"%s\" {\n" % obj.name )
 
    # Material
    material = obj.active_material
    if material:
        f.write( "Material : \"%s\" {\n" % material.name )
        f.write( "Diffuse : { %f, %f, %f }\n" % ( material.diffuse_color.r, material.diffuse_color.g, material.diffuse_color.b ) )
        f.write( "Specular : { %f, %f, %f }\n" % ( material.specular_color.r, material.specular_color.g, material.specular_color.b ) )
        f.write( "Translucency : { %f }\n" % material.translucency )
        f.write( "Fresnel : { %f, %f }\n" % ( material.diffuse_fresnel, material.diffuse_fresnel_factor ) )
        f.write( "} // Material\n" )
 
    # Index
    f.write( "Faces : %d {\n" % len( mesh.faces ) )
    for face in mesh.faces:
        f.write( "%d { " % len( face.vertices ) )
        for index in face.vertices:
            f.write( "%d, " % index )
        f.write( "}\n" )
    f.write( "} // Faces\n" )
 
    # Vertex
    f.write( "Vertexes : %d {\n" % len( mesh.vertices ) )
    for vertex in mesh.vertices:
        f.write( "%d { %f, %f, %f }\n" % ( vertex.index, vertex.co.x, vertex.co.y, vertex.co.z ) )
    f.write( "} // Vertexes\n" )
 
    # Normals
    f.write( "Normals : %d {\n" % len( mesh.vertices ) )
    for vertex in mesh.vertices:
        f.write( "%d { %f, %f, %f }\n" % ( vertex.index, vertex.normal.x, vertex.normal.y, vertex.normal.z ) )
    f.write( "} // Normals\n" )    
 
    # UVs
    if mesh.uv_textures:
        f.write( "UVs : %d{\n" % len( mesh.uv_textures[0].data ) )
        index = 0
        for face in mesh.uv_textures[0].data:
            f.write( "%d { %f, %f }\n" % ( index, face.uv1.x, 1.0 - face.uv1.y ) )
            index += 1
            f.write( "%d { %f, %f }\n" % ( index, face.uv2.x, 1.0 - face.uv2.y ) )
            index += 1
            f.write( "%d { %f, %f }\n" % ( index, face.uv3.x, 1.0 - face.uv3.y ) )
            index += 1
        f.write( "} // UVs\n" )
 
    f.write( "} // Model\n")
    f.close()
    return {'FINISHED'}