TouchDesignerで使いそうなデータ制御の基礎基礎メモ1
あくまで自分用ですが、使いそうだけど、すぐ忘れてしまいそうな基本的なデータの制御です。
一定の時間でカウントアップする
特定の処理をループしたい場合など、一定時間でカウントアップした値を作成したい場合はLFO CHOPを利用します。
正の整数だけ利用したい場合は、TypeをRampを選択します。
カウントアップはCount CHOPを利用します。 カウントアップの閾値はTrigger Thresholdを利用します。ここでは0.98を設定しています。
閾値を設定するとカウントアップされるようになりますが、どこかの値で0に戻したい場合は、LimitをLoop Min/Maxにします。 そして、Limit Maximumに最大値を設定します。
オーディオデータの制御
オーディをデータはそのままだと、使いづらい場合があります。 Audio spectrum CHOPを利用することで、オーディオデータを正の整数にすることができます。 また、analyze CHOPを利用すると、特定の値を取得することができます。 analyze CHOPのFunctionをMaximumにすることで最大値のみ取得することができます。
ランダムな値を作る
ランダムな値をつくるにはNoise CHOPを利用します。
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のoutput
のpath
の設定と、devServer
のcontentBase
の設定を同じにする必要があるようです。
ただし、同じにした場合に少し問題があります。 出力先のフォルダにindex.htmlを入れることになるので、ファイル構成上変な感じがします(私だけかもしれませんが。。。)
そこで、output
のpublicPath
を設定することで、path
とcontentBase
が異なってもライブリロードを行うことができます。 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.0とdisableHostCheck: trueを追加します。
devServer: { contentBase: path.resolve(__dirname, 'dist'), port: 3000, open:true, host 0.0.0.0, disableHostCheck: true },
まとめ
メモリ上にファイルが保存されるのがよくわからず、ハマりました。