のぐそんブログ

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

TouchDesignerで使いそうなデータ制御の基礎基礎メモ1

あくまで自分用ですが、使いそうだけど、すぐ忘れてしまいそうな基本的なデータの制御です。

一定の時間でカウントアップする

特定の処理をループしたい場合など、一定時間でカウントアップした値を作成したい場合はLFO CHOPを利用します。

正の整数だけ利用したい場合は、TypeをRampを選択します。

参考:LFO Saw Wave ?

カウントアップはCount CHOPを利用します。 カウントアップの閾値Trigger Thresholdを利用します。ここでは0.98を設定しています。

閾値を設定するとカウントアップされるようになりますが、どこかの値で0に戻したい場合は、LimitLoop Min/Maxにします。 そして、Limit Maximumに最大値を設定します。

オーディオデータの制御

オーディをデータはそのままだと、使いづらい場合があります。 Audio spectrum CHOPを利用することで、オーディオデータを正の整数にすることができます。 また、analyze CHOPを利用すると、特定の値を取得することができます。 analyze CHOPのFunctionをMaximumにすることで最大値のみ取得することができます。

ランダムな値を作る

ランダムな値をつくるにはNoise CHOPを利用します。

10月-14-2017 19-03-57.gif

Noise CHOPはcommonタブの、Time SliceをONにしないと動きが発生しません。

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)を利用すること

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

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

フラグメントシェーダーで複数の円をグルグル回転させる

メタボールをやってみたいので、 まずは、フラグメントシェーダーで、複数の円をグルグル回転させてみたいと思います。

#ifdef GL_ES
precision highp float;
#endif
 
uniform float time;
uniform vec2 resolution;
 
#define XSpeed 1.50
#define YSpeed 1.50
#define size 0.1
#define count 10.0
const float PI = 3.1415926535897932384626433832795;


void main( void ) 
{
    //座標を正規化
    vec2 pos =(gl_FragCoord.xy * 2.0 - resolution) / min(resolution.x, resolution.y);;
    
    float c = 0.0;
    float rad = (PI*2.0) /count;

    for( float i = 1.0; i < count+1.0; ++i )
    {   
        //X軸の移動
        float px = cos( time * XSpeed + (i * rad)) ;

        //Y軸の移動 
        float py = sin( time * YSpeed + (i* rad));

        //circleの座標
        vec2 circlePos = vec2( px , py );

        //円のサイズを変更
        float d = size / length(pos - circlePos);

        //円のボケ幅を調整
        c += pow( d, 5.0 );
    }
 
    gl_FragColor = vec4(vec3(c), 1.0 );
 
}

動作はこちらで確認できます。

THREE.DataTextureメモ

THREE.DataTexture

THREE.DataTextureを利用することで、配列データからテクスチャを作ることができます。 GPGPUなどでテクスチャをデータの格納場所として使う場合に使えそうなので、少し調べてみたいと思います。

基本的な使い方

new THREE.DataTexture( 
    data, //ArrayBufferもしくは配列
    width, 
    height, 
    format, //THREE.LuminanceFormatとか、THREE.RGBFormatとか
    type,//THREE.UnsignedByteTypeとかTHREE.FloatTypeとか
    mapping, 
    wrapS,
    wrapT,
    magFilter,
    minFilter,
    anisotropy
)

一部抜粋ですが、実際に使う場合はこんな感じ。

var w = 32;

var l = Math.pow(w, 2);
var data = new Uint8Array(l);

for (var i = 0; i < l; i++) {
  data[i] = Math.random() * 255;
}

var dataTex = new THREE.DataTexture(data, w, w, THREE.LuminanceFormat, THREE.UnsignedByteType);

dataTex.magFilter = THREE.NearestFilter;
dataTex.needsUpdate = true;//必ず必要

var geometry = new THREE.PlaneBufferGeometry(1, 1);
var material = new THREE.MeshBasicMaterial({
  map: dataTex,
  side: THREE.DoubleSide
});
var mesh = new THREE.Mesh(geometry, material);

RGBデータを入れてみる

rgbそれぞれ値を設定してみます。 ポイントは、データ配列の長さを3倍にしているところです。

var w = 32;

var l = Math.pow(w, 2);
//幅は同じだが、配列の数を3倍にする
var data = new Uint8Array(l * 3);

for (var i = 0; i < l; i++) {

  let stride = i * 3;
  //RGBそれぞれに設定する
  data[stride] = Math.random() * 256;//R
  data[stride + 1] = Math.random() * 256;//G
  data[stride + 2] = Math.random() * 256;//B
}

var dataTex = new THREE.DataTexture(data, w, w, THREE.RGBFormat, THREE.UnsignedByteType);

dataTex.magFilter = THREE.NearestFilter;
dataTex.needsUpdate = true;

var geometry = new THREE.PlaneBufferGeometry(1, 1);
var material = new THREE.MeshBasicMaterial({
  map: dataTex,
  side: THREE.DoubleSide
});
var mesh = new THREE.Mesh(geometry, material);

1テクセルにRGBの値が入ったテクスチャができあがりました。

RGBAデータを入れてみる

アルファをつかする場合はデータの配列を四倍します。

var w = 32;

var l = Math.pow(w, 2);
var data = new Uint8Array(l * 4);

for (var i = 0; i < l; i++) {

  let stride = i * 4;

  data[stride] = Math.random() * 256;
  data[stride + 1] = Math.random() * 256;
  data[stride + 2] = Math.random() * 256;
  data[stride + 3] = Math.random() * 256;
}

var dataTex = new THREE.DataTexture(data, w, w, THREE.RGBAFormat, THREE.UnsignedByteType);

dataTex.magFilter = THREE.NearestFilter;
dataTex.needsUpdate = true;

var geometry = new THREE.PlaneBufferGeometry(1, 1);
var material = new THREE.MeshBasicMaterial({
  map: dataTex,
  side: THREE.DoubleSide
});
var mesh = new THREE.Mesh(geometry, material);

GPGPUで使う場合

GPGPUのデータ格納場所として使う場合は、Uint8Arrayだと8バイトの整数(符号なし)しか使えないので、Float32Arrayを利用したほうがよさそうです。

こんな感じです。

var w = 32;
var l = Math.pow(w, 2);
var dataTex = new Float32Array(l * 4);
var dataTex = new THREE.DataTexture( data, w, h, THREE.RGBAFormat, THREE.FloatType );

ただし、画像として利用する場合は符号などは必要ないので8バイトで十分です。 あくまで、データ受け渡し用のテクスチャになります。

webpack-dev-serverを使ってみる

webpack-dev-server

開発用サーバを立てることができるモジュールです。

詳しくは公式のドキュメントを見てみてください。

手順

モジュールをインストール。

npm install --save-dev webpack-dev-server

package.jsonにscriptを追加。

"scripts": {
      "start": "webpack-dev-server"
}

サーバーを起動する。

npm start

http://localhost:8080でアクセスできるようになる。

サーバーの設定をする

サーバーの設定は、package.jsonにも指定できるが、webpack.config.jsに記載すると見やすい気がする。

サーバーに設定できるオプションで、私がよく使うのは下記。

オプション 説明
contentBase サーバの起点ディレクトリを指定。未指定の場合はカレントディレクトリが起点になる。
port 未指定の場合は8080が初期値になる。
open サーバー起動時にブラウザを開く。true or falseで指定。
inline ライブリロードを行うための設定。true or falseで指定。未指定の場合はtrue
hot Hot Module Replacement(HMR)を有効にします。true or falseで指定。未指定の場合はtrue。

※hotについては、webpack.config.jsに指定するとエラーとなるのでpackage.jsonにwebpack-dev-server --hotとして渡す必要があるようです。

webpack.config.jsに指定する場合
  devServer: {
    contentBase: path.resolve(__dirname, 'dist'),
    port: 3000,
    open:true
  },
package.json指定する場合
"scripts": {
  "start": "webpack-dev-server client.js --inline --hot --colors --content-base dist/ --port=3000"
}

live reloadを利用する

ライブリロードを行う場合は、webpack.config.jsのoutputpathの設定と、devServercontentBaseの設定を同じにする必要があるようです。

ただし、同じにした場合に少し問題があります。 出力先のフォルダにindex.htmlを入れることになるので、ファイル構成上変な感じがします(私だけかもしれませんが。。。)

そこで、outputpublicPathを設定することで、pathcontentBaseが異なってもライブリロードを行うことができます。 publicPathはwebpack-dev-serverを利用する際は、指定したほうが良さそうです。

const path = require('path');

module.exports = {
  //ビルドするファイル
  entry: path.resolve(__dirname, './src/main.js'), 
  //ビルドしたファイルの出力先
  output: {
    path: path.resolve(__dirname, './dist'),
    filename: 'bundle.js',
    publicPath: '/assets'
  },
  //開発サーバーの設定
  devServer: {
    contentBase: path.resolve(__dirname, './src'),
    inline: true,
    open: true
  },
  devtool: 'source-map'
};

webpack-dev-serverでのビルド

webpack-dev-serverでも、webpack.config.js設定にあわせてファイルのビルドが行われます。 ただしこのビルドは、実際のnpm run buildとは異なり、ファイルはメモリ上に保存されます。

例)webpack.config.jsの設定が上記の場合はこんな感じ

.
├── node_modules
├── package-lock.json
├── package.json
├── src
│   ├── index.html
│   ├── main.js
│   └── assets //メモリ上に存在する
│        └── dist.js //メモリ上に存在する
└── webpack.config.js

その他

webpack-dev-serverはipはそのままの設定ではプライベートIPでアクセスすることができません。

プライベートIPでアクセスする為、host:0.0.0.0disableHostCheck: trueを追加します。

  devServer: {
    contentBase: path.resolve(__dirname, 'dist'),
    port: 3000,
    open:true,
    host 0.0.0.0,
    disableHostCheck: true
  },

まとめ

メモリ上にファイルが保存されるのがよくわからず、ハマりました。

2018年の目標

新年を迎えて、遅くなりましたが2017年の振り返りと、2018年の目標を立てたいと思います。 あまり目標を文字に起こしたことがなかったので、今年はブログに書いておくことにします。

続きを読む