LoginSignup
16
17

More than 5 years have passed since last update.

blenderで作成したjsonモデルをthree.jsでパーティクル

Last updated at Posted at 2017-05-05

このようなパーティクルができます

想定される読者

Jsonモデルにシェーダーをアタッチしたい方
blenderのJsonモデルをいじってみたい方
three.jsでGLSLを利用してみたい方
パーティクルを作ってみたい方

blenderの操作

blenderでエクスポートしたJson(three.js)をパーティクルにします。
デフォルトでは、blenderにJson(three.js)のエクスポートがないのでアドオンの追加をお願いします。

blenderのエクスポート設定、今回は頂点とUVを利用するので、画像のようにそれ以外のチェックを外しています。
タイプは、BufferGeometryに設定します。

ある程度の頂点数があるモデルのほうがパーティクルが映えます。

b.png

Jsonモデルのテクスチャを用意する方法はこの記事などをご参考ください。
blenderのテクスチャ、ベイクなどで調べていただくと出てくるかと思います。
テクスチャのサイズは、1024x1024を使用しています。

VertexShader

uniform float time;
uniform float size;
attribute vec3 velocity;
attribute float delay;
varying vec2 vUv;

void main()
    {
        /* min(max(time-delay, 0), 10.0) */
        /* clamp(比べたい値, 最小値, 最大値) */
        /* 0.0~10.0 パーティクルが飛び散る範囲 */
        float par = clamp(time - delay, 0.0, 10.0 );   //  clamp(x, a, b) = min(max(x, a), b);

        par = ( sin( par / 100.0 * 3.1416  - 3.1416 * 0.5 ) + 1.0 ) * 0.5 * 100.0;
        par *= par;

        vec3 pos = position + velocity * par;
        vUv = uv;
        vec4 mvPosition = modelViewMatrix * vec4( pos, 1.0 );
        gl_PointSize = size * (50.0 / length(mvPosition.xyz));
        gl_Position = projectionMatrix * modelViewMatrix * vec4( pos, 1.0 );
    }

FragmentShader

uniform sampler2D texture;
uniform sampler2D circle;
uniform float time;
varying vec2 vUv;

    void main()
    {
        vec4 color = texture2D( texture, vUv );
        vec4 tex = texture2D( circle, gl_PointCoord.xy );

        gl_FragColor = tex * vec4( color.rgb, 1.0 );
    }

three.js部分

function initScene() {

            var _mesh, _geometry, _material;

            var scene = new THREE.Scene();
            var camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 0.1, 1000);

            var renderer = new THREE.WebGLRenderer();
            renderer.setSize(window.innerWidth, window.innerHeight);
            document.body.appendChild(renderer.domElement);

            var ambientLight = new THREE.AmbientLight(0x333333);
            scene.add(ambientLight);

            var directionalLight = new THREE.PointLight(0xCCCCCC);
            scene.add(directionalLight);
            directionalLight.position.set(10, 10, 10);

            camera.position.z = 8;

            var controls = new THREE.OrbitControls(camera, renderer.domElement);

            var _ldr = new THREE.TextureLoader();
            _ldr.load('uv_image2b.png', function(_texture) {

                // 読み込んだ画像サイズを取得。
                var _width = _texture.image.width;
                var _height = _texture.image.height;

                // 画像のサイズから1px 1particle になるように数を計算
                var PARTICLES = _width * _height;

                // ベクトル、遅延係数などを設定
                var vectors = new Float32Array(PARTICLES * 3);
                var delay = new Float32Array(PARTICLES * 1);

                // geometry生成
                var _geometry = new THREE.BufferGeometry();

                var len = PARTICLES;
                var _speed = 16.0;
                for (var i = 0; i < len; i++) {

                    // ベクトル
                    var _rad0 = Math.random() * Math.PI * 2.0 - Math.PI;
                    var _rad1 = Math.random() * Math.PI * 2.0 - Math.PI;
                    var _r = (1.0 - Math.random() * Math.random()) * _speed;
                    vectors[i * 3 + 0] = Math.cos(_rad1) * Math.cos(_rad0) * _r;
                    vectors[i * 3 + 1] = Math.sin(_rad0) * _r;
                    vectors[i * 3 + 2] = Math.sin(_rad1) * Math.cos(_rad0) * _r;

                    // 遅延係数
                    delay[i] = ((i % _width) + Math.floor(i / _width)) / len * 10000.0;
                }

                // JSONの読み取り
                var request = new XMLHttpRequest();
                request.open("GET", "suza_uv.json", false);
                request.send(null);
                var json_o = JSON.parse(request.responseText);

                // ここでJSONの配列を変数に代入しています!
                var vertices = new Float32Array(json_o["data"]["attributes"]["position"]["array"]);
                var uv_suzan = new Float32Array(json_o["data"]["attributes"]["uv"]["array"]);

                // geometryにattributeの値を設定
                _geometry.addAttribute('position', new THREE.BufferAttribute(vertices, 3));
                _geometry.addAttribute('velocity', new THREE.BufferAttribute(vectors, 3));
                _geometry.addAttribute('uv', new THREE.BufferAttribute(uv_suzan, 2));
                _geometry.addAttribute('delay', new THREE.BufferAttribute(delay, 1));

                // material生成
                _material = new THREE.ShaderMaterial({
                    uniforms: {
                        time: {
                            value: 0.0
                        },
                        size: {
                            value: 1.0
                        },
                        texture: {
                            value: _texture
                        },
                        circle: {
                            value: new THREE.TextureLoader().load('star.png')
                        }
                    },
                    vertexShader: document.getElementById('vertexshader').textContent,
                    fragmentShader: document.getElementById('fragmentshader').textContent,
                    transparent: true,
                    depthTest: false,
                    depthWrite: false
                });

                _mesh = new THREE.Points(_geometry, _material);
                scene.add(_mesh);

                console.log('PARTICLES: ', PARTICLES);

            })

            // 画面のリサイズ
            window.onresize = resize;

            function resize() {
                var width = window.innerWidth;
                var height = window.innerHeight;
                renderer.setSize(width, height);
                if (camera.aspect) {
                    camera.aspect = width / height;
                } else {
                    camera.left = -width * 0.5;
                    camera.right = width * 0.5;
                    camera.bottom = -height * 0.5;
                    camera.top = height * 0.5;
                }

                camera.updateProjectionMatrix();
            }

            // レンダー
            var render = function() {
                window.requestAnimationFrame(render);
                controls.update();

                if (_mesh) {
                    _mesh.material.uniforms.time.value = (Math.sin(new Date().getTime() * 0.0005) + 1.0) * 0.5 * 50;
                    _mesh.rotation.y += 0.02;
                }

                renderer.render(scene, camera);
            };

            render();
        }

        window.onload = initScene;

読み込むJson(記載は一部)

{
"data": {
        "attributes": {
            "uv": {
                "type": "Float32Array",
                "array": [0.187943,0.627341,0.18371,......

終わりに

パーティクルの動きなど、nulldesignさんのおかげで出来ました。ありがとうございます。
ご指摘、ご質問ありましたら、コメントやツイッターでいただけると助かります。

16
17
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
17