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)