Unity3d / VectorField

2011.04.16 | unity3d

VectorFieldをつくってみました。
ofAmsterdam WorkShopでつくられたものからの移植です。
「d」キーでデバッグ用のUIの表示非表示切り替え。
v: ポイントにかかる力の係数
radius: ドラッグを与える範囲

[ demo ]


C#で書いていてParticle 10,000個で自分の環境ではWebPlayer100fpsくらい。 Mac StandAloneだと200fpsくらい。
StandAloneだとoFでつくったものも同じくらいの速度でているようです。
途中、javascriptでつくっていたのですが、どうもC#で書いたほうが若干速いようです。
それと最適化の手段としてはわりとflashと同じような感じです。
ループする箇所は foreach( .. in ..) を使ってほうが若干速いとか、参照が浅いほうが若干速かったり。
flashっぽくTexture2DのSetPixelをつかってbitmapに描画してみたんですが、これはかなり重くなった。違うアプローチ・最適化の仕方があるかもしれませんが。

unityやるには、particle描画を自前で描画するのはあまりやらなそうですが、モバイル系の場合には最適化の手法が重要になりそうです。
ひとまずC#の方が速そうなので、処理が重くなりそうなやつは今後C#をメインで、簡単な動作なものはjavascriptでやっていこうかと。

unity3d Proでのみ使用できるGL関数をつかっています。
Pro以外でこういうのつくるときに何かいい方法ないかな?

using UnityEngine;
using System.Collections;

public class MainCs: MonoBehaviour {

	/**
		lineMaterial
		ラインを描画するためのマテリアルとシェーダーの設定
	*/
	static Material lineMaterial ;

	private float widowSizeHalf;

	public int splitW = 30;
	public int splitH = 30;

	public Color gridColor  = Color.red;
	public float drawPeak = 40f;
	public float radius = 0.3f;
	public bool drawLine = true;

	private VectorField field;

	static void CreateLineMaterial() {
	    if( !lineMaterial ) {
	        lineMaterial = new Material( "Shader \"Lines/Colored Blended\" {" +
	            "SubShader { Pass { " +
	            "    Blend SrcAlpha OneMinusSrcAlpha " +
	            "    ZWrite On Cull Off Fog { Mode Off } " +
	            "    BindChannels {" +
	            "      Bind \"vertex\", vertex Bind \"color\", color }" +
	            "} } }" );
	        lineMaterial.hideFlags = HideFlags.HideAndDontSave;
	        lineMaterial.shader.hideFlags = HideFlags.HideAndDontSave;
	    }
	}

	public static float ofGetWidth()
	{
		return 600.0f;
	}
	public static float ofGetHeight()
	{
		return 600.0f;
	}

	//
	/*****************************
		描画
	******************************/
	//
	Vector2 screenOffset;

	void _drawPoints()
	{
		if(drawLine){
			GL.Color( gridColor );
			field.draw(-w/2,-h/2,w,h, 10f);
		}
	}

	/*****************************
		vector Field
	******************************/

	void _initVectorField(){
		field = new VectorField(splitW, splitH);
	}

	void addIntoField(float x, float y, Vector2 vec, float radius)
	{
		field.addIntoField(x, y, vec, radius);
	}

	public void blur(float amnt){
		field.blur(amnt);
	}

	// x,y : 0 - 1
	Vector2 readFromField(float x, float y)
	{
		return field.readFromField(x, y);
	}

	public void clear()
	{
		field.clear();
	}

	public static float ofRandom(float min , float max)
	{
		return Random.Range(min, max);
	}

	//
	public int NUM_PARTICLES = 5000;
	private particle[] p;

	public float per = 0.015f;
	public Color particleColor = Color.red;

	private float w;
	private float h;

	private void _initParticles()
	{
		w = ofGetWidth();
		h = ofGetHeight();

		p = new particle[NUM_PARTICLES];
		for(int i = 0; i < NUM_PARTICLES; i++){
			p[i] = new particle();
			p[i].setup(ofRandom(0f, w), ofRandom(0f, h), ofRandom(0.87f, 0.985f));
		}
	}

	private void _updateParticles()
	{
		Vector2 point;
		Vector2 fieldForce;
		foreach (particle pt in p){
			point = readFromField(pt.pos_x / w, pt.pos_y / h);
			fieldForce  = point * drawPeak;
			pt.updateVel(fieldForce * per, w, h);
			//pt.draw();
		}

	}

	private void _drawParticles()
	{
		foreach (particle pt in p){
			pt.draw();
		}
	}

	// Use this for initialization
	void Start () {
		widowSizeHalf = ofGetWidth()/2;

		CreateLineMaterial();

		_initVectorField();

		_initParticles();
	}

	// レンダリング時によばれる
	void OnPostRender() {

		lineMaterial.SetPass( 0 );

		GL.Begin( GL.LINES );

	    _drawPoints();

	    GL.Color(particleColor);
		_updateParticles();
		//_drawParticles();

		GL.End();

	}

	void OnGUI()
	{

		Event currentEvent = Event.current;
		if(currentEvent.type == EventType.MouseDown){
			_OnMouseDown(currentEvent);
		}
		if(currentEvent.type == EventType.MouseUp){
			_OnMouseUp();
		}

		if(_isMouseDown){
			_OnMouseMove(currentEvent);
		}
	}

	private bool _isMouseDown = false;
	private Vector2 previousMouse = new Vector2(0,0);
	private Vector2 currentMouse = new Vector2(0,0);

	void _OnMouseDown(Event currentEvent) {
		float x = currentEvent.mousePosition.x - Screen.width / 2 + ofGetWidth()/2;
		float y = Screen.height - currentEvent.mousePosition.y - Screen.height /2 + ofGetWidth()/2;

		previousMouse.x = x / ofGetWidth();
		previousMouse.y = y / ofGetHeight();

		_isMouseDown = true;
	}

	void _OnMouseUp() {
		_isMouseDown = false;
	}

	private void _OnMouseMove(Event currentEvent) {

		float x = currentEvent.mousePosition.x - Screen.width / 2 + ofGetWidth()/2;
		float y = Screen.height - currentEvent.mousePosition.y - Screen.height /2  + ofGetHeight()/2;

		//need to get mouse coords into a zero to one ratio
		currentMouse.x = x / ofGetWidth();
		currentMouse.y = y / ofGetHeight();

		//the direction of our mouse is the difference between the
		//current and last position
		Vector2 motionVector = (currentMouse - previousMouse) * 3;

		//we add into our field with a radius of 30%
		addIntoField(currentMouse.x, currentMouse.y, motionVector, radius);

		previousMouse = currentMouse;
	}
}

/******************************
	GraphicEx
	グラフィックの描画
*******************************/
class GraphicEx{
	public void circle(Vector2 position , float size, int split)
	{
		float r = Mathf.PI*2 / split;
		for(int i = 0; i < split; i++){
			float x = size * Mathf.Sin(r * i);
			float y = size * Mathf.Cos(r * i);
			float x1;
			float y1;
			if(i == split-1){
				x1 = size * Mathf.Sin(r * 0);
				y1 = size * Mathf.Cos(r * 0);
			}else{
				x1 = size * Mathf.Sin(r * (i+1));
				y1 = size * Mathf.Cos(r * (i+1));
			}
			GL.Vertex3(x + position.x, y + position.y, 0 );
			GL.Vertex3(x1 + position.x, y1 + position.y, 0 );
		}
	}
	public void cross(Vector2 position, float size)
	{
		GL.Vertex3( position.x, position.y, 0f );
	    GL.Vertex3( position.x + 1, position.y, 0f );
	}
}

/*****************************
	particle
******************************/
class particle{

	Vector2 vel;
	float drag;
	public float pos_x = 0f;
	public float pos_y = 0f;

	private Vector2 _offsetPos;

	private GraphicEx graph = new GraphicEx();

	public particle(){
		_offsetPos = new Vector2(MainCs.ofGetWidth()/2, MainCs.ofGetWidth()/2);

		//pos = new Vector2();
		vel = new Vector2();
	}

	public void setup(float x, float y, float dragIn)
	{
		nPos = new Vector2();

		//pos.x = x;
		//pos.y = y;
		pos_x = x;
		pos_y = y;
		drag = dragIn;
	}

	public void updateVel(Vector2 force, float w, float h){

		//update our speed with some drag
		vel *= drag;

		//update our speed with the force being applied
		vel += force;

		//update our position
		//pos += vel;
		pos_x += vel.x;
		pos_y += vel.y;

		if(pos_x > w){
			pos_x -= w;
		}else if(pos_x < 0){
			pos_x += w;
		}
		if(pos_y > h){
			pos_y -= h;
		}else if(pos_y < 0){
			pos_y += h;
		}

		draw();
	}

	private void drawGraphic(Vector2 position, float size, int split)
	{
		//graph.circle(position, size, split);
		graph.cross(position, size);
	}

	Vector2 nPos;
	public void draw()
	{
		//var color: Color = Color.Lerp(Color.yellow, Color.cyan, v);
		//GL.Color(color);
		nPos.x = pos_x -_offsetPos.x;
		nPos.y = pos_y -_offsetPos.y;

		drawGraphic(nPos, 4, 3);
	}

}
/*
 http://wiki.openframeworks.cc/index.php?title=OfAmsterdam
 Workshop 006 - vectors and vector fields
 */

using UnityEngine;
using System.Collections;

public class VectorField{
				
		public int NUM_BINS_X = 30;
		public int NUM_BINS_Y = 30;
		
		Vector2[, ] field;
		float binW;
		float binH;
	
		public VectorField(int x, int y){
			NUM_BINS_X = x;
			NUM_BINS_Y = y;
			field = new Vector2[NUM_BINS_X, NUM_BINS_Y];
			binW = 1.0f/NUM_BINS_X;
			binH = 1.0f/NUM_BINS_Y;
		}
		
		//-------------------------------------------------
		public void addIntoField(float x, float y, Vector2 vec, float radius){
			
			if(x >= 1.0 || x < 0 || y >= 1.0 || y < 0){
				 return;
			}
			
			Vector2 pos = new Vector2(x,y);
			Vector2 binPos;
			Vector2 distVec;
			float dist = 0f; 
			float amnt = 0f;
												
			for(int yy = 0; yy < NUM_BINS_Y; yy++){
				for(int xx = 0; xx < NUM_BINS_X; xx++){

					binPos.x = ( (float)xx * binW);
					binPos.y = ( (float)yy * binH);
					distVec = binPos - pos;
					dist = distVec.magnitude;	
					
					if(dist > radius) continue;
					
					amnt = 1.0f - dist;
					
					//amnt * amnt makes the rolloff less linear
					field[xx, yy] += (amnt * amnt * amnt) * vec; 
				}
			}
		}
		
		//-------------------------------------------------
		public void blur(float amnt){
			
			if(amnt > 1.0f)amnt = 1.0f;
			else if(amnt < 0.0f)amnt = 0.0f;
			
			Vector2 blur;
							
			float ramnt = (1.0f - amnt) * 0.25f;				
							
			for(int yy = 1; yy < NUM_BINS_Y-1; yy++){
				for(int xx = 1; xx < NUM_BINS_X-1; xx++){

					blur = field[xx, yy] * amnt + field[xx+1, yy] * ramnt +  field[xx-1, yy] * ramnt + field[xx, yy+1] * ramnt + field[xx, yy-1] * ramnt; 
					field[xx, yy] = blur;	
				}
			}
		}
		
		
		//-------------------------------------------------
		public Vector2 readFromField(float x, float y){
			
			if(x >= 1.0 || x < 0 || y >= 1.0 || y < 0){
				 return new Vector2(0,0);
			}
			
			int rasterX = (int)(x * NUM_BINS_X);
			int rasterY = (int)(y * NUM_BINS_Y);
			
			Vector2 vec = field[rasterX, rasterY]; 
			
			return vec;
		}		
			
		
		//-------------------------------------------------
		void normalize(){
						
			for(int yy = 0; yy < NUM_BINS_Y; yy++){
				for(int xx = 0; xx < NUM_BINS_X; xx++){
					field[xx, yy].Normalize();
				}
			}
		}
		
		//-------------------------------------------------
		public void clear(){
						
			for(int yy = 0; yy < NUM_BINS_Y; yy++){
				for(int xx = 0; xx < NUM_BINS_X; xx++){
					Vector2 v = field[xx, yy];
					v.x = 0;
					v.y = 0;
					field[xx, yy] = v;
				}
			}
		}					
		
		//-------------------------------------------------		
		public void draw(float x, float y, float width, float height, float scale){
			
			Vector2 pixelPos;	
			Vector2 vec;
		
			for(int yy = 0; yy < NUM_BINS_Y; yy++){
				for(int xx = 0; xx < NUM_BINS_X; xx++){
					
					pixelPos.x = ( (float)xx * binW * width ) + x;
					pixelPos.y = ( (float)yy * binH * height ) + y;
					vec = (field[xx, yy]) * scale;
					
					ofLine(pixelPos.x, pixelPos.y, pixelPos.x + vec.x, pixelPos.y + vec.y);
				}
			}
			
		}		
		
		// openframework
		void ofLine(float x0, float y0, float x1, float y1)
		{
			GL.Vertex3( x0, y0, 0 );
		    GL.Vertex3( x1, y1, 0 );
		}
};

There are no comments.

Please Leave a Reply

TrackBack URL :

pagetop