Mesh.TriangleMeshBuilder を使うと簡単に Mesh を作れますがあまり自由度が
ありません。Mesh.AllocationBuilder を使うとフォーマットやメモリの管理も
自分でコントロールできます。
● TriangleMeshBuilder の例
頂点要素の組み合わせは TriangleMeshBuilder 作成時にフラグで指定します。
// java Mesh.TriangleMeshBuilder tmb= new Mesh.TriangleMeshBuilder( mRS, 2, Mesh.TriangleMeshBuilder.NORMAL|Mesh.TriangleMeshBuilder.TEXTURE_0 ); tmb.setNormal( 0f, 0f, 1f ).setTexture( 0f, 0f ).addVertex( 0f, 0f ); tmb.setNormal( 0f, 0f, 1f ).setTexture( 1f, 0f ).addVertex( 80f, 0f ); tmb.setNormal( 0f, 0f, 1f ).setTexture( 0f, 1f ).addVertex( 0f, 80f ); tmb.setNormal( 0f, 0f, 1f ).setTexture( 1f, 1f ).addVertex( 80f, 80f ); tmb.addTriangle( 0, 2, 1 ); tmb.addTriangle( 1, 2, 3 ); Mesh mesh= tmb.create( true ); // 頂点フォーマット(Element)は mesh から受け取る Element vtype= mesh.getVertexAllocation( 0 ).getType().getElement();
● AllocationBuilder の例
頂点フォーマットを RenderScript の構造体で宣言しておきます。
// RenderScript // 頂点の構造定義 typedef struct __attribute((packed, aligned(4))) VertexType { float3 position; float3 normal; float2 texcoord; } VertexType_t; VertexType_t* VertexType_n; // unuse // Index の定義 typedef struct IndexType { short index; } IndexType_t; IndexType_t* IndexType_n; // unuse
Reflected class を使って頂点バッファとインデックスバッファを作ります。
インデックス側も USAGE_GRAPHICS_VERTEX を指定する点に注意してください。
また Complex ではない、Basic Element ではなぜかうまくいきませんでした。
単一の short (I16) であっても頂点と同じように構造体から定義する必要があります。
// java // VertexBuffer (GL_ARRAY_BUFFER) ScriptFiled_VertexType vbuffer= new ScriptFiled_VertexType( mRS, 4, AllocationBuilder.USAGE_SCRIPT|Allocation.USAGE_GRAPHICS_VERTEX ); // IndexBuffer (GL_ELEMENT_ARRAY_BUFFER) ScriptFiled_IndexType ibuffer= new ScriptFiled_IndexType( mRS, 6, Allocation.USAGE_GRAPHICS_VERTEX ); // Mesh Mesh.AllocationBuilder ab= new Mesh.AllocationBuilder( mRS ); ab.addVertexAllocation( vbuffer.getAllocation() ); ab.addIndexSetAllocation( ibuffer.getAllocation(), Mesh.Primitive.TRIANGLE ); Mesh mesh= ab.create(); // 頂点フォーマット(Element)は vbuffer から取り出せる Element vtype= vbuffer.getElement();
作成した頂点バッファとインデックスバッファを組み合わせて Mesh を作ります。
頂点配列の Allocation 自体を作っているので、これを script に渡して
書き換えることが可能です。
実際に 2D のメッシュでやってみます。
まずは構造体の定義と受け取る変数の宣言。
// RenderScript // VertexBuffer の構造 typedef struct __attribute((packed, aligned(4))) VertexType2D { float2 position; float2 texcoord; } VertexType2D_t; VertexType2D_t* VertexType2D_n; // unuse // IndexBuffer の構造 typedef struct IndexType { short index; } IndexType_t; IndexType_t* IndexType_n; // unuse // VertexShader に渡す Uniform の定義 typedef struct ConstantType2D { float4 Transform; } ConstantType2D_t; ConstantType2D_t* ConstantType2D_n; // unuse rs_program_vertex vsh; rs_program_fragment psh; rs_allocation texture; rs_sampler sampler; rs_mesh mesh; VertexType2D_t* vertex2d; // 頂点アクセス用 ~
必要なリソースを作っていきます。
// java // IndexBuffer ScriptFiled_IndexType ibuffer= new ScriptField_IndexType( mRS, MAX_QUAD*6, Allocation.USAGE_GRAPHICS_VERTEX ); // インデックスをバッファに書き込む ScriptField_IndexType.Item item= new ScriptField_IndexType.Item(); for( int i= 0 ; i< MAX_QUAD ; i++ ){ int ibase= i*6; int vbase= i*4; item.index= (short)(vbase + 0); ibuffer.set( item, ibase++, true ); item.index= (short)(vbase + 2); ibuffer.set( item, ibase++, true ); item.index= (short)(vbase + 1); ibuffer.set( item, ibase++, true ); item.index= (short)(vbase + 1); ibuffer.set( item, ibase++, true ); item.index= (short)(vbase + 2); ibuffer.set( item, ibase++, true ); item.index= (short)(vbase + 3); ibuffer.set( item, ibase++, true ); }
ScriptField_~.Item と set() を使うとパラメータを一度に書き込み出来ます。
set() の最後の引数を true にすると毎回転送が発生します。
低速なので false にして、最後に copyAll() を使えば高速に転送できるはず
なのですがどうもうまくいきません。
// java // VertexBuffer ScriptField_VertexType2D vbuffer= new ScriptField_VertexType2D( mRS, MAX_QUAD*4, Allocation.USAGE_SCRIPT|Allocation.USAGE_GRAPHICS_VERTEX ); // 頂点の初期値作成 float[] vertex_src= { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f }; float[] tex_src= { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f }; ScriptField_VertexType2D.Item item= new ScriptField_VertexType2D.Item(); for( int bi= 0 ; bi< MAX_QUAD ; bi++ ){ float px= (float)Math.random() * mWidth; float py= (float)Math.random() * mHeight; for( int vi= 0 ; vi< 4 ; vi++ ){ int base= vi * 2; item.position.x= px + vertex_src[base+0] * 80.0f; item.position.y= py + vertex_src[base+1] * 80.0f; item.texcoord.x= tex_src[base+0]; item.texcoord.y= tex_src[base+1]; vbuffer.set( item, bi*4+vi, true ); } }
SDK の Sample は PointSprite (Primitive.POINT) ばかりでしたが、
この手順だと Mesh.AllocationBuilder でも Primitive.TRIANGLE + Index
を使うことができます。
// java // Mesh Mesh.AllocationBuilder builder= new Mesh.AllocationBuilder( mRS ); builder.addVertexAllocation( vbuffer.getAllocation() ); builder.addIndexSetAllocation( ibuffer.getAllocation(), Mesh.Primitive.TRIANGLE ); Mesh mesh= builder.create(); // Uniform ScriptField_ConstantType2D vconst= new ScriptField_ConstantType2D( mRS, 1, Allocation.USAGE_GRAPHICS_CONSTANTS ); vconst.set_Transform( 0, new Float4( 2.0f/mWidth, -2.0f/mHeight, -1.0f, 1.0f ), true ); // VertexShader ProgramVertex.Builder vsh_builder= new ProgramVertex.Builder( mRS ); vsh_builder.setShader( LoadResource( mContext, R.raw.mesh2d_vsh ) ); vsh_builder.addConstant( vconst.getType() ); vsh_builder.addInput( vbuffer.getElement() ); ProgramVertex vsh= vsh_builder.create(); vsh.bindConstants( vconst.getAllocation(), 0 );
↓Texture を使うので、ProgramFragment.Builder で addTexture() しておきます。
// java // PixelShader ProgramFragment.Builder psh_builder= new ProgramFragment.Builder( mRS ); psh_builder.setShader( LoadResource( mContext, R.raw.mesh2d_psh ) ); psh_builder.addTexture( Program.TextureType.TEXTURE_2D ); ProgramFragment psh= psh_builder.create();
対応するシェーダー側は↓こんな感じ。
addTexture() により UNI_Tex0 という Uniform が挿入されます。
// GLSL: mesh2d_psh.psh varying vec2 otexcoord0; void main() { gl_FragColor= texture2D( UNI_Tex0, otexcoord0 ); }
↓Bitmap から直接 Allocation を作れるので Texture の生成は簡単です。
// java // Texture Bitmap bitmap= BitmapFactory.decodeResource( mRes, R.drawable.bgimage256b ); Allocation texture= Allocation.createFromBitmap( mRS, bitmap );
作ったデータを script に渡します。
// java // Global mScript.set_vsh( vsh ); mScript.set_psh( psh ); mScript.set_texture( texture ); mScript.set_sampler( Sampler.CLAMP_LINEAR(mRS) ); mScript.set_mesh( mesh ); mScript.bind_vertex2d( vbuffer ); // mesh 内の頂点データ mRS.bindRootScript( mScript );
script 側では受け取った mesh を描画します。
その前に直接頂点を書き換えてみます。
// RenderScript: rendermesh.rs #pragma version(1) #pragma rs java_package_name(jp.flatlib.ap02b) #include "rs_graphics.rsh" #include "rs_cl.rsh" typedef struct __attribute((packed, aligned(4))) VertexType2D { float2 position; float2 texcoord; } VertexType2D_t; VertexType2D_t* VertexType2D_n; // unuse typedef struct IndexType { short index; } IndexType_t; IndexType_t* IndexType_n; // unuse typedef struct ConstantType2D { float4 Transform; } ConstantType2D_t; ConstantType2D_t* ConstantType2D_n; // unuse rs_program_vertex vsh; rs_program_fragment psh; rs_allocation texture; rs_sampler sampler; rs_mesh mesh; VertexType2D_t* vertex; // mesh 内の頂点配列を直接受け取る int root() { rsgClearColor( 0.5f, 0.5f, 0.0f, 0.0f ); // 座標計算の前準備 float2 center= { rsgGetWidth() * 0.5f, rsgGetHeight() * 0.5f }; rs_matrix4x4 rotmatrix; rsMatrixLoadTranslate( &rotmatrix, center.x, center.y, 0.0f ); rsMatrixRotate( &rotmatrix, 1.5f, 0.0f, 0.0f, 1.0f ); rsMatrixTranslate( &rotmatrix, -center.x, -center.y, 0.0f ); float4 ipos= { 0.0f, 0.0f, 0.0f, 1.0f }; // 頂点を書き換えて動かしてみる uint32_t size= rsAllocationGetDimX( rsGetAllocation( vertex ) ) >>2; VertexType2D_t* vptr= vertex; for( uint32_t i= 0 ; i< size ; i++ ){ ipos.xy= vptr->position; float4 opos= rsMatrixMultiply( &rotmatrix, ipos ); float2 pos0= opos.xy; float2 pos1= opos.xy; vptr++->position= pos0; pos1.x+= 30.0f; vptr++->position= pos1; pos0.y+= 30.0f; vptr++->position= pos0; pos1.y+= 30.0f; vptr++->position= pos1; } // HW への転送 rsgAllocationSyncAll( rsGetAllocation( vertex ) ); rsgBindSampler( psh, 0, sampler ); rsgBindTexture( psh, 0, texture ); rsgBindProgramFragment( psh ); rsgBindProgramVertex( vsh ); rsgDrawMesh( mesh ); // 書き換えた Mesh の描画 return 16; }
あまり大したことはやっていません。
PointSprite ではなく Triangle の頂点を直接動かしているのがポイント。
書き換えたバッファを同期させるために rsgAllocationSyncAll() が必要です。
Graphics 用 RenderScript の中でそのまま座標演算していますが、
これはあまり良い方法ではありません。
Compute 用の RenderScript に分離して rsForEach() を使ったほうが良いでしょう。
OpenCL のように、将来的に GPU 等のハードウエアがサポートされると
考えられるからです。
また頂点の直接更新は GPU リソースの Lock が発生するため同期待が発生し
速度が遅くなる可能性があります。
ダブルバッファ等の対策が必要ですが、RenderScript での効率良いアクセス
はまだ成功していません。
取り敢えずここまで出来れば script だけでひと通り作れそうです。
・flatlib_ap02b.zip ソースコード
次は 3D の Mesh 描画を行います。 「Android 3.x RenderScript (5) 任意の 3D モデルを描画する」
関連エントリ
・Android 3.x RenderScript (3) 独自シェーダーの割り当てとメッシュの描画(2D)
・Android 3.x RenderScript (2) 描画と Allocation
・Android 3.x RenderScript (1)