2012.12.01 | 

ここに書くのが適切か分かりませんが、酔ってて忘れそうなので、メモ。
こんばんは。へべれけmuchoです。
本日、Cinder勉強会#1に参加してきました。
コレは勉強会後の話です。

モック/プロトタイプを提案時に制作するという話から、
デザイナーってなんだ?という話になりました。
WEBに関わる業界内では、WEBデザイナー、グラフィックデザイナー、
モーションデザイナー、インタラクティブデザイナーと
いろいろな肩書きがありますが、
バックエンドだって、変数名ひとつにしても、
担当する人間は気を配って決めています。
つまりデザインしているんです。
一方で、リッチなインタラクティブコンテンツの完成図を伝えるにあたって
photoshopで作られた静止画ベースではカバーしきれなくなってきていると感じます。
漠然とそんな風に思っていましたが、今日の勉強会に参加された方々も
そういう認識で、やっぱりそういう流れなのかなと改めて考えさせられました。

組織によってはすでにそうなってるかもしれないが、
インタラクティブコンテンツに関しては、表現や仕組みがリッチであればあるほど、
デザインしなければならない要素は、psdだけではなく、もっと多岐に渡り、
今後のデザイナーはそれらを俯瞰的、総合的に把握できる能力が
求められるのかもしれない。お話を伺いながらそんな風に思いました。

とはいえ、そんな人材はそうそういないので、
どこもグラフィック、インタラクティブ、システムと
それぞれ担当が分かれがちだと思いますが、、
その結果、たとえばモーションだけが突出したような、
いびつなアウトプットになってしまいがちな気がします。

個人的には、モック/プロトタイプを作る立場が統括的にデザインするのが、
インタラクティブコンテンツに関しては自然かなと。
そんな風に思った勉強会アフターでした。

主催の糸数さんをはじめ、参加者のみなさんお疲れさまでした。
また情報共有等々してきましょう!

2012.04.05 | 

こんばんは。muchoです。
今日はほかの会社の方とランチしながら情報交換しました。
やっぱりSublime Text 2アツいですよね。
coffee scriptやらねばー。
お忙しい中ありがとうございました。
また情報交換とか勉強会とかお花見とかやりましょう。

が、しかしブログネタはFlash+Cinderです。
前回のOSC通信に続き、AIRとCinderで連携させてみました。
http://bubblebird.at/tuioflash/で提供されているライブラリを使いました。TUIOプロトコル用のライブラリですが、OSCプロトコルのライブラリが同梱されています。

今回は送信側をAIRアプリ、受信側をCinderにしてテストしました。

受信側のCinderは前回の記事と同じです。



package 
{
	import flash.display.Sprite;
	import flash.events.MouseEvent;

	import org.tuio.connectors.UDPConnector;
	import org.tuio.osc.OSCManager;
	import org.tuio.osc.OSCMessage;

	public class Main extends Sprite 
	{
		private var prefix:String = "/devjam_osc";
		private var oscManager:OSCManager;
		
		public function Main():void 
		{
			oscManager = new OSCManager(null, new UDPConnector("127.0.0.1", 3000, false));
			stage.addEventListener(MouseEvent.CLICK, onClick);
		}

		private function onClick(e:MouseEvent):void
		{
			
			// とりあえずこれだけ・・・
			sendArgument("fullscreen", 1, "i");
		}

		private function sendArgument(address:String, value:*, type:String = "f"):void
		{
			/*
			 * type
			 * s:A string
			 * i:An integer
			 * f:A float
			*/
			var msg:OSCMessage = new OSCMessage();
			msg.address = prefix + "/" + address; //アドレスと
			msg.addArgument(type, value); //形式と値を入れて
			oscManager.sendOSCPacket(msg); //送信!なんてシンプル
		}
	}
}


cinder側に入ってたOSCライブラリでは、データ形式にString,Interger,Float
の3種類しかやり取りできないようなことが書いてありましたが、Tuio as3 libraryだと
もっとたくさんの形式に対応してるようです。Cinder側にもTUIOライブラリがあったので、
そっちを使うといろんなデータのやり取りができるかも?
s A string
i An integer
f A float
b A blob / ByteArray
t An OSCTimetag
d A double
c An ascii character
r A RGBA color
T A boolean true. You don't have to specify a value for this type.
F A boolean false. You don't have to specify a value for this type.
N A null value. You don't have to specify a value for this type.
I Infinity. You don't have to specify a value for this type.

また、私が作ったサンプルだと1データで送信してますが、まとめて送信もできるようです。




    var myBundle:OSCBundle = new OSCBundle();
    myBundle.addPacket(myMsg); //OSCMessage
    myBundle.addPacket(myMsg2); //OSCMessage
    [...]
     
    oscManager.sendOSCPacket(myBundle);


以上、OSC通信のテストでした!

2012.04.01 | 

こんにちは。muchoです。もうすぐお花見のシーズンですね。

さてさて、cinderにはフルスクリーンになる機能があるんですが、
プロジェクターや別のモニターでフルスクリーンにして、
手元のPCでフルスクリーン画面の方をコントロールしたい!
ということでアプリが2個になってしまうのですが、
描画プログラムとパラメーターを制御するプログラムと分割してみました。

OSCはデフォルトのままでは動かないので、blocks以下にライブラリを入れてください。
ライブラリはこちらから
https://github.com/cinder/Cinder/tree/master/blocks/osc

追加リンカファイル
$(CINDER_PATH)/blocks/osc/lib/macosx/osc.a

追加インクルード
$(CINDER_PATH)/blocks/osc/include

OSCでの通信は以下のデータを使います。
・送信先(IPアドレス)
・ポート
・アドレス(変数名のようなもの)
・値の型(int,float,stringのいずれか)
・値

以下の抜粋サンプルではアドレスをプログラムの固有IDと変数名から生成するような
形にしてます。



//-----------------------------------------------------
// senderApp

#include "OscSender.h"

class senderAPP : public AppBasic {
  public:
	void setup();
	void update();
	
	void sendString(string address, vector arg);
	void sendInt(string address, vector arg);
	void sendFloat(string address, vector arg);

	osc::Sender	sender;
	std::string	host;
	std::string	prefix;
	int		port;
};

void senderAPP::setup()
{
	host = "localhost";
	prefix = "/devjam_osc";
	port = 3000;
	sender.setup(host, port);
}

void senderApp::update()
{
	sendFloat("quat", wxyz);
	sendFloat("colora", rgba);
	sendFloat("position", xyz);
}

void senderApp::sendString(string address, vector arg)
{
	osc::Message message;
	message.setRemoteEndpoint(host, port);
	message.setAddress(prefix + "/" + address);
	for(int i = 0; i < arg.size(); i++){
		message.addStringArg(arg[i]);
	}
	sender.sendMessage(message);
}

void senderApp::sendInt(string address, vector arg)
{
	osc::Message message;
	message.setRemoteEndpoint(host, port);
	message.setAddress(prefix + "/" + address);
	for(int i = 0; i < arg.size(); i++){
		message.addIntArg(arg[i]);
	}
	sender.sendMessage(message);
}

void senderApp::sendFloat(string address, vector arg)
{
	osc::Message message;
	message.setRemoteEndpoint(host, port);
	message.setAddress(prefix + "/" + address);
	for(int i = 0; i < arg.size(); i++){
		message.addFloatArg(arg[i]);
	}
	sender.sendMessage(message);
}

//-----------------------------------------------------
// listenerApp

#include "OscListener.h"

class listenerApp : public AppBasic {
  public:
	void prepareSettings( Settings *settings );
	void setup();
	void update();

	osc::Listener	listener;
	std::string	prefix;
};

void listenerApp::prepareSettings( Settings *settings )
{
	settings->enableSecondaryDisplayBlanking( false );
}

void listenerApp::setup()
{
	prefix = "devjam_osc";
	listener.setup(3000);
}

void listenerApp::update()
{
	while (listener.hasWaitingMessages()) {
		osc::Message message;
		listener.getNextMessage(&message);
		string adr = message.getAddress();
		vector substrings = split(adr, "/");
		if(substrings[0] == prefix){

			if(substrings[1] == "quat") {
				float qw = message.getArgAsFloat(0);
				float qx = message.getArgAsFloat(1);
				float qy = message.getArgAsFloat(2);
				float qz = message.getArgAsFloat(3);
				Quaternion oscQuat = Quaternion(qw, qx, qy, qz);
			}
			
			if(substrings[1] == "colora") {
				float vr = message.getArgAsFloat(0);
				float vg = message.getArgAsFloat(1);
				float vb = message.getArgAsFloat(2);
				float va = message.getArgAsFloat(3);
				ColorA oscColor = ColorA(vr, vg, vb, va);
			}
			
			if(substrings[1] == "position") {
				float px = message.getArgAsFloat(0);
				float py = message.getArgAsFloat(1);
				float pz = message.getArgAsFloat(2);
				Vec3f oscPos = Vec3f(px, py, pz);
			}
		}
	}
}


そして・・・できたサンプルがこちら左が送信側プログラム、右が受信側です。

にしてもインターフェースを作るにはcinderは不向きなので、
コントローラー側はflashで作ろうと思います。

2012.02.25 | 


記事とは関係ないけど、Fractal Noiseで遊んでみた動画
というのもHSV変換は絵がないので。
こんばんはこんばんは。muchoです。こんばんは。

以前 ActionScriptでもやったので、全然目新しくないですが、
GLSLでも変換する必要があったので作りました。




// vertical Shader

#version 120
vec3 HSVtoRGB(vec3 hsv)
{
	vec3 col;
	float hue = mod(hsv.r, 360.0);
	float s = max(0, min(1, hsv.g));
	float v = max(0, min(1, hsv.b));
	if(s > 0.0) {
		int h = int(floor(hue / 60.0));
		float f = hue / 60.0 - float(h);
		float p = v * (1.0 - s);
		float q = v * (1.0 - f * s);
		float r = v * (1.0 - (1.0 - f) * s);

		if(h == 0) col = vec3(v, r, p);
		else if(h == 1) col = vec3(q, v, p);
		else if(h == 2) col = vec3(p, v, r);
		else if(h == 3) col = vec3(p, q, v);
		else if(h == 4) col = vec3(r, p, v);
		else col = vec3(v, p, q);
	}else{
		col = vec3(v);
	}
	return col;
}

void main()
{
	gl_FragColor.rgb = HSVtoRGB(vec3(gl_TexCoord[0].x * 360.0, gl_TexCoord[0].y, 1.0));
	gl_FragColor.a = 1.0;
}


こんなかんじで、カラーピッカーもどきなグラデが描画されます。
こういうシンプルなのは今後はWEBGLでブログにアップしたいです。
今後ってのがいつになるやら・・・

2012.02.16 | 

こんにちは。muchoです。
前回のGLSL Gaussian Blur もどき part1に引き続き、GLSLでBlurをかけてみました。

前回、ガウス分布を求める公式がおかしいなーと思いしらべてみたところ
やっぱりおかしかったんですが、ガウス分布はやっぱり正弦波のようなので
結果的には一緒だろうということで、前回と変わらず。
変えたところは、ブラーのサイズによって、計算回数が増えていくんですが、
見た目と速度の具合で、調整しました。

ブラーサイズ50pxで60fps

ブラーサイズ100pxで60fps

ブラーサイズ300pxだと40fpsを切ってしまった。

前回とロジックは一緒で
一度横にブラーをかけ、その後、縦のブラーをかけています。
フレームバッファオブジェクトを使ったらかなり高速になりました。
以下抜粋ソースです。なにかの参考になれば。



//-----------------------------------------------------
//cpp
#include "cinder/app/AppBasic.h"
#include "cinder/Capture.h"
#include "cinder/gl/Fbo.h"
#include "cinder/gl/gl.h"
#include "cinder/gl/GlslProg.h"
#include "cinder/gl/Texture.h"
#include "Resources.h"

static const int X_RES  = 640;
static const int Y_RES  = 480;

using namespace ci;
using namespace ci::app;
using namespace std;

class GSLS_GaussianBlurApp : public AppBasic {
  public:
	void setup();
	void update();
	void draw();
	
	Color		backGround;
	ColorA		drawColor;
	float		mBlurSize;
	float		mBloom;
	float		fps;
	
	Capture		mCapture;
	
	// TEXTURE AND SHADER AND FBO
	gl::Texture	mTexture;
	gl::Fbo		mFbo;
	gl::GlslProg	mShaderH, mShaderV;
};

void GSLS_GaussianBlurApp::setup()
{
	backGround = Color(0,0,0);
	drawColor = ColorA( 1.0f, 1.0f, 1.0f, 1.0f );

	try {
		mCapture = Capture( X_RES, Y_RES );
		mCapture.start();
	}
	catch( ... ) {
		console() << "Failed to initialize capture" << endl;
	}
	
	// SETUP PARAMS
	mParams = params::InterfaceGl( "params", Vec2i( 200, 85 ) );
	mParams.addParam( "FPS", &fps );
	mParams.addParam( "BlurSize", &mBlurSize, "min=0.0 max=500.0 step=1.0" );
	mParams.addParam( "Bloom", &mBloom, "min=0.0 max=2.0 step=0.01" );
	
	// SETUP SHADER
	mShaderH	= gl::GlslProg( loadResource( RES_VERT_ID ), loadResource( RES_HFRAG_ID ) );
	mShaderV	= gl::GlslProg( loadResource( RES_VERT_ID ), loadResource( RES_VFRAG_ID ) );
	
	// SETUP FBO
	gl::Fbo::Format format;
	mFbo = gl::Fbo( X_RES, Y_RES, format );
	
	// GL setup
	gl::enableAlphaBlending();
	glEnable(GL_LINE_SMOOTH);
	glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
	gl::clear( backGround, true );	
}

void GSLS_GaussianBlurApp::update()
{
	if( !mCapture || !mCapture.checkNewFrame()) return;
	mTexture = mCapture.getSurface();
	fps = getAverageFps();
}

void GSLS_GaussianBlurApp::draw()
{
	gl::clear( backGround, true );
	if(!mTexture) return;
	//
	// テクスチャーから横ブラーをFBOへドロー
	gl::pushMatrices();
	gl::setMatricesWindow(X_RES, Y_RES, false);
	mFbo.bindFramebuffer();
	gl::clear( backGround, true );
	mTexture.bind( 0 );
	mShaderH.bind();
	mShaderH.uniform( "texture0", 0 );
	mShaderH.uniform( "width", X_RES );
	mShaderH.uniform( "blurSize", mBlurSize );
	mShaderH.uniform( "bloom", mBloom );
	gl::drawSolidRect( getWindowBounds() );
	gl::popMatrices();
	mShaderH.unbind();
	mTexture.unbind();
	mFbo.unbindFramebuffer();
	gl::popModelView();
	//
	// FBOから縦ブラーをシーンにドロー
	gl::pushModelView();
	mFbo.bindTexture(0);
	mShaderV.bind();
	mShaderV.uniform( "texture0", 0 );
	mShaderV.uniform( "height", Y_RES );
	mShaderV.uniform( "blurSize", mBlurSize );
	mShaderV.uniform( "bloom", mBloom );
	gl::drawSolidRect( getWindowBounds() );
	mShaderV.unbind();
	mFbo.unbindTexture();
	gl::popModelView();
}





//-----------------------------------------------------
// Vertex Shader
void main()
{
	gl_TexCoord[0] = gl_MultiTexCoord0;
	gl_Position = ftransform();
}





//-----------------------------------------------------
// Fragment Shader horizontal

#version 120
uniform sampler2D texture0;
uniform int width;
uniform float blurSize;
uniform float bloom;

void main()
{
	float v;
	float pi = 3.141592653589793;
	float e_step = 1.0 / width;
	float radius = blurSize;
	if ( radius < 0 ) radius = 0;
	int steps = int(min(radius * 0.7, sqrt(radius) * pi));
	float r = radius / steps;
	float t = bloom / (steps * 2 + 1);
	float x = gl_TexCoord[0].x;
	float y = gl_TexCoord[0].y;
	vec4 sum = texture2D(texture0, vec2(x, y)) * t;
	int i;
	for(i = 1; i <= steps; i++){
		v = (cos(i / (steps + 1) / pi) + 1) * 0.5;
		sum += texture2D(texture0, vec2(x + i * e_step * r, y)) * v * t;
		sum += texture2D(texture0, vec2(x - i * e_step * r, y)) * v * t;
	}
 
  gl_FragColor = sum;
	
}





//-----------------------------------------------------
// vertical Shader horizontal

#version 120
uniform sampler2D texture0;
uniform int height;
uniform float blurSize;
uniform float bloom;

void main()
{
	float v;
	float pi = 3.141592653589793;
	float e_step = 1.0 / height; 
	float radius = blurSize;
	if ( radius < 0 ) radius = 0;
	int steps = int(min(radius * 0.7, sqrt(radius) * pi));
	float r = radius / steps;
	float t = bloom / (steps * 2 + 1);
	float x = gl_TexCoord[0].x;
	float y = gl_TexCoord[0].y;
	vec4 sum = texture2D(texture0, vec2(x, y)) * t;
	int i;
	for(i = 1; i <= steps; i++){
		v = (cos(i / (steps + 1) / pi) + 1) * 0.5;
		sum += texture2D(texture0, vec2(x, y + i * e_step * r)) * v * t;
		sum += texture2D(texture0, vec2(x, y - i * e_step * r)) * v * t;
	}
 
  gl_FragColor = sum;
	
}



個人的には100ピクセルぼかして60fps出せたので大満足!