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

## このようなパーティクルができます
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">Json Model Particle の勉強です。<a href="https://twitter.com/hashtag/threejs?src=hash">#threejs</a> <a href="https://twitter.com/hashtag/webgl?src=hash">#webgl</a> <a href="https://t.co/k3EYRpl4DX">pic.twitter.com/k3EYRpl4DX</a></p>&mdash; 雪解 (@snowdoke) <a href="https://twitter.com/snowdoke/status/844132556337422336">2017年3月21日</a></blockquote> <script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

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

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

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

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

![b.png](https://qiita-image-store.s3.amazonaws.com/0/150548/c5c61ed8-c357-0add-a716-73bdc070179d.png)

Jsonモデルのテクスチャを用意する方法は[この記事](http://qiita.com/yukidoke/items/f06a18cd31d4aac48226)などをご参考ください。
blenderのテクスチャ、ベイクなどで調べていただくと出てくるかと思います。
テクスチャのサイズは、1024x1024を使用しています。

## VertexShader
```glsl
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
```glsl
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部分
```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（記載は一部）
```json
{
"data": {
        "attributes": {
            "uv": {
                "type": "Float32Array",
                "array": [0.187943,0.627341,0.18371,......
```

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