«

»

9月 05

Stage3Dで画像をぷるぷる

このエントリーを含むはてなブックマークはてなブックマーク - Stage3Dで画像をぷるぷる Googleブックマークに追加 このエントリをつぶやくこのWebページのtweets

今回はこれまで学んだ知識を活かして画像を変形しぷるぷるさせるものを実装してみました。


デモ

参考書

ソース

package 
{
	import com.adobe.utils.AGALMiniAssembler;
	import com.adobe.utils.PerspectiveMatrix3D;
	
	import flash.display.*;
	import flash.display3D.*;
	import flash.display3D.textures.Texture;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.*;
	import flash.net.URLRequest;
	
	[SWF(width="500", height="500", frameRate="60")]
	public class Stage3D_09 extends Sprite
	{
		private const WIDTH:Number = 500;
		private const HEIGHT:Number = 500;
		
		private var stage3D:Stage3D;
		private var context3D:Context3D;
		
		//テクスチャに使う画像
		[Embed(source="texture.jpg")]
		private var Img:Class;
		
		// 頂点シェーダ
		private const VERTEX_SHADER:String =
			"m44 op, va0, vc0 \n" +		
			"mov v0, va1 \n";			//ピクセルシェーダへ
		
		// ピクセルシェーダ
		private const FRAGMENT_SHADER:String =
			//テクスチャ情報の処理
			"tex oc, v0, fs0<2d, linear>";	//色情報の取得  v2:uv座標, fs0:画像データ
		
		//頂点データ
		private var indices:IndexBuffer3D;
		private var vertices:VertexBuffer3D;
		private var vertexData:Vector.<Number>;
		private var indexData:Vector.<uint>;
		
		//ばね定数
		private const k:Number = 1.2;
		//減衰定数
		private const a:Number = 0.9;
		//この値が大きいほど、よりマウスに引き付けられる
		private const rep:Number = 50;
		//頂点の個数
		private var W:int = 30, H:int = 30;
		//一マスの大きさ
		private var MASSW:Number, MASSH:Number;
		//頂点の位置情報と加わっている力を格納する配列
		private var forces:Vector.<Number>;
		
		public function Stage3D_09()
		{
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.align = StageAlign.TOP_LEFT;
			
			stage3D = this.stage.stage3Ds[0];
			
			stage3D.addEventListener(Event.CONTEXT3D_CREATE, onContext3DCreate);
			stage3D.requestContext3D(Context3DRenderMode.AUTO);
		}
		
		private function initVertex():void{
			MASSH = 2.0/(W-1);
			MASSW = 2.0/(H-1);
			
			forces = new Vector.<Number>();
			vertexData = new Vector.<Number>();
			indexData = new Vector.<uint>();
			
			//頂点の座標とuv座標の設定
			for(var i:int = 0; i < H; i++){
				for(var j:int = 0; j < W; j++){
					vertexData.push(
						j/(W-1)*2.0-1.0,	//x
						-(i/(H-1)*2.0-1.0),	//y
						0.0,				//z
						j/(W-1),			//u
						i/(H-1)				//v
					);
					forces.push(0, 0);
				}
			}
			
			//各三角形にインデックスを割り振る
			for(i = 0; i < H-1; i++){
				for(j = 0; j < W-1; j++){
					indexData.push(j+i*(W), (j+1)+i*(W), j+(i+1)*(W));
					indexData.push((j+1)+i*(W), j+(i+1)*(W), (j+1)+(i+1)*(W));
				}
			}
			
			// 5個(頂点座標(x,y,z)、uv座標(u,v))の値を持つ頂点用の頂点バッファを作成
			vertices = context3D.createVertexBuffer(W*H, 5);
			// 頂点データをアップロード
			vertices.uploadFromVector(vertexData, 0, W*H);
			// 最初の属性は座標の情報:Float型の数値が3つ これを属性レジスタ0に入れる
			context3D.setVertexBufferAt(0, vertices, 0, Context3DVertexBufferFormat.FLOAT_3);
			// uv情報 Float型が2つ これを属性レジスタ1に入れる
			context3D.setVertexBufferAt(1, vertices, 3, Context3DVertexBufferFormat.FLOAT_2);
			//インデックスバッファを作成
			indices = context3D.createIndexBuffer(indexData.length);
			//インデックス情報をアップロード
			indices.uploadFromVector(indexData, 0, indexData.length);
			
			var texture:Texture = context3D.createTexture(512, 512, Context3DTextureFormat.BGRA, false);
			texture.uploadFromBitmapData((new Img() as Bitmap).bitmapData);
			
			//fs0に設定
			context3D.setTextureAt(0, texture);
			
			//レジスタに登録
			context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, new Matrix3D());
			
			addEventListener(Event.ENTER_FRAME, onEnterFrame);
		}
		
		private function onContext3DCreate(event:Event):void
		{
			context3D = Stage3D(event.target).context3D;
			
			context3D.enableErrorChecking = true;
			context3D.configureBackBuffer(WIDTH, HEIGHT, 2, false);
			
			// 頂点シェーダをコンパイル;
			var vertexAssembly:AGALMiniAssembler = new AGALMiniAssembler();
			vertexAssembly.assemble(Context3DProgramType.VERTEX, VERTEX_SHADER);
			// ピクセルシェーダをコンパイル;
			var fragmentAssembly:AGALMiniAssembler = new AGALMiniAssembler();
			fragmentAssembly.assemble(Context3DProgramType.FRAGMENT, FRAGMENT_SHADER);
			
			var programPair:Program3D;
			// Program3Dのインスタンスを取得
			programPair = context3D.createProgram();
			// 頂点シェーダとピクセルシェーダのコードをGPUにアップロード
			programPair.upload(vertexAssembly.agalcode, fragmentAssembly.agalcode);
			// 使用するシェーダのペアを指定;
			context3D.setProgram(programPair);
			
			initVertex();
		}
		
		private function onEnterFrame(e:Event):void{
			// drawTriangles()を呼ぶ前に必ずバッファをクリア;
			context3D.clear(0, 0, 0);
			
			doPuyo(mouseX/stage.stageWidth*2.0-1.0, -(mouseY/stage.stageHeight*2.0-1.0), 300.0);
			for(var i:int = 0; i < H; i++){
				for(var j:int = 0; j < W; j++){
					var x:Number = vertexData[(i*W+j)*5], y:Number = vertexData[(i*W+j)*5+1];
					var fx:Number = forces[(i*W+j)*2], fy:Number = forces[(i*W+j)*2+1];
					fx = fx*a + (j*MASSW-1.0-x)*k;
					fy = fy*a + (-(i*MASSH-1.0)-y)*k;
					x += fx/10;
					y += fy/10;
					
					vertexData[(i*W+j)*5] = x;
					vertexData[(i*W+j)*5+1] = y;
					forces[(i*W+j)*2] = fx;
					forces[(i*W+j)*2+1] = fy;
				}
			}
			
			// 頂点データをアップロード
			vertices.uploadFromVector(vertexData, 0, W*H);
			
			// 3角形を全て描画する;
			context3D.drawTriangles(indices, 0, -1);
			//ビューポートに表示;
			context3D.present();
		}
		
		//指定した座標を中心にプルプルさせる。
		public function doPuyo(xx:Number, yy:Number, strength:Number):void{
			for(var i:int = 0; i < H; i++){
				for(var j:int = 0; j < W; j++){
					var x:Number = vertexData[(i*W+j)*5], y:Number = vertexData[(i*W+j)*5+1];
					var d:Number = new Point(x-xx,y-yy).length*100;
					//マウスの位置に引き寄せる
					forces[(i*W+j)*2] += ((xx - x)/(d/strength) + (j*MASSW - x)/rep)/30;
					forces[(i*W+j)*2+1] += ((yy - y)/(d/strength) + (i*MASSH - y)/rep)/30;
				}
			}
		}
	}
}

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

次の HTMLタグおよび属性が使えます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>