のぐそんブログ

暗いおじさんがシコシコ書くブログです。

THREE.jsでグネグネしたメタボールを作成する

こちらを目指して、グネグネしたメタボール(呼び方がメタボールであってるのかわかりません)を作ってみたいと思い挑戦してみました。 結果としては、実現することが出来ませんでした。。。

実際に作成できたのはこちらです。

難しい。

JS

global.THREE = require('three');
const glslify = require('glslify');
const vertexShader = glslify('./src/js/shaders/circle/vertexShader.vert');
const fragmentShader = glslify('./src/js/shaders/circle/fragmentShader.frag');

const clock = new THREE.Clock();

let time = 0.0;
let windowWidth = window.innerWidth;
let windowHeight = window.innerHeight;
let dpr = window.devicePixelRatio;

const app = {
  renderer: new THREE.WebGLRenderer(),
  scene: new THREE.Scene(),
  camera: new THREE.PerspectiveCamera(60, windowWidth / windowHeight, 0.1, 1000)
};
const body = document.getElementsByTagName('body')[0];

app.renderer.setClearColor(new THREE.Color(0xffffff), 1.0);
app.renderer.setPixelRatio(window.devicePixelRatio || 1);

// canvasをbodyに追加
body.appendChild(app.renderer.domElement);

// canvasをリサイズ
app.renderer.setSize(windowWidth, windowHeight);

//LIGHTS
let light = new THREE.AmbientLight(0xffffff, 1.0);
app.scene.add(light);

app.camera.position.z = 1.5;

// Geometory作成
let geometry = new THREE.IcosahedronGeometry(0.5, 4);

// Material作成
let material = new THREE.ShaderMaterial({
  uniforms: {
    'time': {
      type: 'f',
      value: time
    },
    'resolution': {
      type: 'v2',
      value: new THREE.Vector2(windowWidth * dpr, windowHeight * dpr)
    }
  },
  vertexShader: vertexShader,
  fragmentShader: fragmentShader,
  //wireframe:true
});

let mesh = new THREE.Mesh(geometry, material);

app.scene.add(mesh);

render();

function render() {
  time = clock.getElapsedTime();
  mesh.material.uniforms.time.value = time;
  app.renderer.render(app.scene,app.camera);
  requestAnimationFrame(render);
}

頂点シェーダー

ノイズはglsl-noiseを利用させていただいています。

#pragma glslify: cnoise = require(glsl-noise/classic/3d)

varying float noise;
uniform float time;
uniform vec2 resolution;

//グネグネの振り幅
const float amplitude = 0.5;
//グネグネのスピード
const float speed = 0.25;

void main() {
    noise = cnoise( vec3(normal * amplitude + time * speed));

    vec3 p = position + normal * noise * 0.2;
    gl_Position = projectionMatrix * modelViewMatrix * vec4( p, 1.0 );
}

フラグメントシェーダー

#ifdef GL_ES
precision highp float;
#endif

uniform float time;

void main(){
     gl_FragColor = vec4(vUv, sin(time), 1.0);
}

まとめ

やってみて感じたポイントとしては、以下2点でした。

  • geometryにIcosahedronGeometry(正20面体)を利用すること
  • 頂点の歪みは法線(normal)を利用すること

頂点シェーダーだけがんばればグネグネさせることができました。

球体同士がつながるところはどのようにしたらいいのかがわかりませんでした。