このようなパーティクルができます
Json Model Particle の勉強です。#threejs #webgl pic.twitter.com/k3EYRpl4DX
— 雪解 (@snowdoke) 2017年3月21日
想定される読者
Jsonモデルにシェーダーをアタッチしたい方
blenderのJsonモデルをいじってみたい方
three.jsでGLSLを利用してみたい方
パーティクルを作ってみたい方
blenderの操作
blenderでエクスポートしたJson(three.js)をパーティクルにします。
デフォルトでは、blenderにJson(three.js)のエクスポートがないのでアドオンの追加をお願いします。
blenderのエクスポート設定、今回は頂点とUVを利用するので、画像のようにそれ以外のチェックを外しています。
タイプは、BufferGeometryに設定します。
ある程度の頂点数があるモデルのほうがパーティクルが映えます。
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さんのおかげで出来ました。ありがとうございます。
ご指摘、ご質問ありましたら、コメントやツイッターでいただけると助かります。