Unity3d / VectorField
VectorFieldをつくってみました。
ofAmsterdam WorkShopでつくられたものからの移植です。
「d」キーでデバッグ用のUIの表示非表示切り替え。
v: ポイントにかかる力の係数
radius: ドラッグを与える範囲
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 ); } };Please Leave a Reply
TrackBack URL :