RenderScript är ett skriptspråk på Android som låter dig skriva högpresterande grafisk rendering och rå beräkningskod. Läs mer om RenderScript och skriv din första grafikapp som använder RenderScript i den här handledningen.
RenderScript-API: erna infördes formellt för Android SDK i API-nivå 11 (även Android 3.0, Honeycomb). RenderScript ger ett sätt att skriva prestanda kritisk kod som systemet senare kompilerar till inbyggd kod för processorn den kan köras på. Det här kan vara enhets-CPU, en multi-core CPU eller till och med GPU. Vilket det i slutändan går på beror på många faktorer som inte är lättillgängliga för utvecklaren, men beror också på vilken arkitektur den interna plattformen kompilatorn stöder.
Denna handledning kommer att komma igång med ett enkelt renderingskript vi kallar "Falling Snow". Det är ett partikelsystem där varje snöflinga representeras av en punkt som faller och accelererar ner på skärmen. Vind och annan slumpmässighet ger en mild virvlande effekt.
RenderScript är baserat på C-programmeringsspråket. Om du inte känner till C rekommenderar vi att du förstår det först innan du försöker använda RenderScript. Även om RenderScript inte är OpenGL, behöver det inte heller att du använder det för grafisk rendering, men begreppen för att använda den liknar OpenGL-koncept. Därför hjälper bekantskap med OpenGL och 3D grafikterminologi.
Den öppna källkoden för denna handledning finns tillgänglig för nedladdning. Vi rekommenderar att du använder den för att följa med. Kodlistan i den här handledningen innehåller inte hela innehållet i varje fil.
Låt oss börja med det mest detaljerade steget och arbeta för att använda manuset från en typisk Android-aktivitet.
Skapa en ny projektfil kallad snow.rs i ditt src-träd, under paketet du ska arbeta i. Uppifrån definierar du vilken version av RenderScript du arbetar med:
#pragma-versionen (1)
Ange sedan det Java-paket som det här skriptet tillhör, till exempel:
#pragma rs java_package_name (com.mamlambo.fallingsnow)
Vi använder vissa funktioner från RenderScript-grafik-API: n, så inkludera den rubriken:
#include "rs_graphics.rsh"
Definiera nu två funktioner, root () och init ():
int root () // TBD void init () // TBD
Funktionen root () kommer att vara startpunkten för det här skriptet. Returvärdet definierar om skriptet körs en gång (retur 0) eller med N-millisekunder (retur N). Om hårdvaran inte kan följa den önskade frekvensen körs rot () så ofta som möjligt.
Funktionen init () kallas en gång när skriptet laddas och är ett bra ställe att initiera variabler och andra tillståndsparametrar.
Skapa en maskervariabel som kommer att initieras på Android-sidan och skapa en enkel struktur för att hålla information om varje snöflinga. Medan du är på det, skapa ett par variabler för att hålla vind- och tyngdkraftsvärden.
rs_mesh snowMesh; typedef struct __attribute __ ((packed, aligned (4))) Snö float2 hastighet; float2 position; uchar4 färg; Snow_t; Snow_t * snow; float2 vind; float2 grav;
Initialisera vinden och tyngdkraften i init () -funktionen:
grav.x = 0; grav.y = 18; wind.x = rsRand (50) +20; wind.y = rsRand (4) - 2;
Initiera snön i sin egen funktion:
void initSnow () const float w = rsgGetWidth (); const float h = rsgGetHeight (); int snowCount = rsAllocationGetDimX (rsGetAllocation (snö)); Snow_t * pSnow = snow; för (int i = 0; i < snowCount; i++) pSnow->position.x = rsRand (w); pSnow-> position.y = rsRand (h); pSnow-> hastighet.y = rsRand (60); pSnow-> hastighet.x = rsRand (100); pSnow-> hastighet.x - = 50; uchar4c = rsPackColorTo8888 (255, 255, 255); pSnow-> color = c; pSnow ++;
Innan vi går vidare, låt oss prata om initSnow () -funktionen. För att starta, hämtas bredden och höjden på ritningsområdet. Då måste man veta hur många snöflingor som vi skapar. Det gör detta genom att få dimensionerna av allokeringen, som refereras av snöpekaren. Men var är pekaren initialiserad och vad är en fördelning? Pekaren initieras från Android-koden. En fördelning är ett sätt att minnet hanteras av Android-koden, men används av manuset. De interna detaljerna är inte viktiga just nu. För vårt ändamål kan vi tänka på det som en uppsättning Snow_t struct-objekt.
Slingan iterates över varje struktur och ställer in några slumpmässiga värden så att startscenen ser naturlig ut.
Låt oss nu genomföra en enkel root () -funktion som drar scenen utan animering. Vi använder detta för att få resten av systemet på plats:
int root () rsgClearColor (0,0f, 0,0f, 0,0f, 0,0f); rsgDrawMesh (snowMesh); returnera 0;
När du sparar skriptprojektfilen i Eclipse, skapar byggarna automatiskt en fil som heter snow.bc i / res / raw-katalogen. Den här automatiskt genererade filen borde inte kontrolleras i källkontrollen, och det bör inte ändras. Dessutom skapas vissa Java-filer i / gen-mappen. Det här är gränssnittsfilerna som används för att ringa in i manuset från Android.
Nu när manuset skapas måste vi initiera det för användning från din Android-klass. För att göra detta har vi skapat en hjälpresyklass som heter SnowRS. I det fördelar vi minnet för snöflingorna, initierar manuset och binder nät- och snöflingan till det. Den här klassen använder också ett RenderScriptGL-objekt. Det här objektet skapas i nästa steg som en del av den visningsklass vi ska göra.
offentlig klass SnowRS offentlig statisk slutlig int SNOW_FLAKES = 4000; privat ScriptC_snow mScript; skyddad int mWidth; skyddad int mHeight; skyddad boolean mPreview; skyddade resurser mResources; skyddad RenderScriptGL mRS; offentliga SnowRS (int bredd, int höjd) mWidth = width; mHeight = höjd; public void stop () mRS.bindRootScript (null); public void start () mRS.bindRootScript (mScript); public void init (RenderScriptGL rs, Resurser res, boolean isPreview) mRS = rs; mResources = res; mPreview = isPreview; mScript = (ScriptC_snow) createScript (); offentliga RenderScriptGL getRS () return mRS; offentliga resurser getResources () return mResources; offentliga ScriptC createScript () ScriptField_Snow snow = nya ScriptField_Snow (mRS, SNOW_FLAKES); Mesh.AllocationBuilder smb = ny Mesh.AllocationBuilder (mRS); smb.addVertexAllocation (snow.getAllocation ()); smb.addIndexSetType (Mesh.Primitive.POINT); Mesh sm = smb.create (); ScriptC_snow-skript; script = nytt ScriptC_snow (mRS, getResources (), R.raw.snow); script.set_snowMesh (sm); script.bind_snow (snö); script.invoke_initSnow (); returnera skript;
Låt oss särskilt titta på metoden createScript (). Den första sektionen skapar strukturuppsättningen med 4000 poster (SNOW_FLAKES = 4000). Det använder sedan detta för att skapa ett nätobjekt som används för återgivning. Renderingskonstruktionen är inställd på POINT, så varje snöflinga kommer att dyka upp som en pixel på skärmen.
Därefter initierar vi själva skriptet, med hjälp av den råa resursangivelsen som skapades av Eclipse-byggaren. Sedan tilldelar vi nätverket och arrayallokeringen i skriptet via samtalen set_snowMesh () respektive bind_snow (). Slutligen initierar vi snön med ett samtal till funktionen initSnow () som vi skapade tidigare genom att ringa invoke_initSnow ().
Skriptet startar inte vid denna tidpunkt, men init () -funktionen har kallats. För att få skriptet att springa, ring bindRootScript () på skriptobjektet, vilket ses i startmetoden ().
Android SDK ger bara det objekt vi behöver för utdata från RenderScript: RSSurfaceView-klassen. Implementera en klass som heter FallingSnowView som utökar denna klass, som så:
offentlig klass FallingSnowView utökar RSSurfaceView privat RenderScriptGL mRSGL; privat SnowRS mRender; offentlig FallingSnowView (kontext sammanhang) super (context); @Override public void surfaceChanged (SurfaceHolder hållare, int format, int w, int h) super.surfaceChanged (hållare, format, w, h); om (mRSGL == null) RenderScriptGL.SurfaceConfig sc = nytt RenderScriptGL.SurfaceConfig (); mRSGL = createRenderScriptGL (sc); mRSGL.setSurface (hållare, w, h); mRender = ny SnowRS (w, h); mRender.init (mRSGL, getResources (), false); mRender.start (); @Override protected void onDetachedFromWindow () if (mRSGL! = Null) mRSGL = null; destroyRenderScriptGL ();
Metoden surfaceChanged () skapar ett RenderScriptGL-objekt från SurfaceHolder som passerat, efter behov. Då skapas vårt SnowRS-objekt och återgivning startas.
Allt är på plats för att använda FallingSnowView-klassen inom en Aktivitetsklass. Metoden onCreate () i din aktivitetsklass kan vara så enkel som denna:
Offentligt ogiltigt onCreate (Bundle savedInstanceState) super.onCreate (savedInstanceState); snowView = nytt FallingSnowView (detta); setContentView (Snowview);
Men vänta! Det är bara en statisk bild. Inte särskilt intressant, är det? Låt oss återvända till snow.rs-filen och redigera root () -funktionen. För en viss enkel simulering av pseudo-fysik-stilen, vill du iterera över varje snöflinga och tillämpa sin nuvarande hastighet och vind till sin position. Justera sedan hastigheten baserat på gravitationens acceleration. Slutligen kontrollera om någon snö har fallit ner längst ner på skärmen. Komplexitetsnivån och effektiviteten av kodning här kommer att påverka vilken sorts ramhastighet du äntligen kommer att få. Vi har försökt att behålla det helt enkelt för denna handledning.
Här är vad vi har gjort:
int root () // Rensa till bakgrundsfärgen rsgClearColor (0.0f, 0.0f, 0.0f, 0.0f); // tid sedan senaste uppdateringen float dt = min (rsGetDt (), 0.1f); // dimensflöde w = rsgGetWidth (); float h = rsgGetHeight (); int snowCount = rsAllocationGetDimX (rsGetAllocation (snö)); Snow_t * pSnow = snow; för (int i = 0; i < snowCount; i++) pSnow->position.x + = ((pSnow-> hastighet.x + wind.x) * dt); pSnow-> position.y + = ((pSnow-> hastighet.y + wind.y) * dt); om (pSnow-> position.y> h) pSnow-> position.y = 0; pSnow-> position.x = rsRand (w); pSnow-> hastighet.y = rsRand (60); pSnow-> hastighet.x + = (grav.x) * dt; pSnow-> hastighet.y + = (grav.y) * dt; pSnow ++; rsgDrawMesh (snowMesh); om (rsRand (32) == 1) wind.x = 0-wind.x; returnera 30;
Och här är det på gång:
Denna handledning har bara repat ytan på vad RenderScript kan göra (Haha, få det? Surface?). Du kan använda ett RenderScript-beräkningsskript för att tillämpa grafiska effekter på bitmappar. Du kan lägga till shaders för att utnyttja enhetsgrafikhårdvara för att dra scenen annorlunda. Du kan ställa in transformationer för att rita i ett 3D-utrymme. Du kan konfigurera texturer för att rita. Det finns mycket mer du kan göra med RenderScript.
Medan RenderScript är mer begränsande än att använda OpenGL ES i 3D-reningsområdet, lägger tillägget av RenderScript-beräkningar bara några välkomstmöjligheter. Att rita en snabb 3D-scen med RenderScript kan vara effektivare, kodningsvis än att använda OpenGL. Att använda RenderScript för kraftig beräkning eller bildmanipulation kan vara snabbare att utvecklas och fungera bättre än liknande NDK-lösningar (på grund av automatisk distribution över hårdvarukärnor). Till skillnad från när du utvecklar med Android NDK behöver du inte oroa dig för den underliggande hårdvaruarkitekturen.
Den största nackdelen med RenderScript är bristen på portabilitet av befintlig kod. Om du redan har OpenGL-kod eller beräkningsfunktioner i C som du vill utnyttja i dina Android-appar kan du bara hålla fast vid Android NDK.
Denna handledning har gett dig en smak av att använda RenderScript med dina Android-applikationer. Du lärde dig grunderna i att skriva och initiera ett skript och återge till skärmen. Allt detta gjordes i samband med ett enkelt partikelsystem som simulerade snöflingor med pixelstorlek.
Låt oss veta vilka coola RenderScript-appar som du bygger i kommentarerna!
Mobila utvecklare Lauren Darcey och Shane Conder har medverkat flera böcker om Android-utveckling: en fördjupad programmeringsbok med titeln Android Wireless Application Development och Sams Lär dig själv Android Application Development inom 24 timmar. När de inte skriver, spenderar de sin tid på att utveckla mobil mjukvara hos sina företag och tillhandahålla konsulttjänster. De kan nås via e-post till [email protected], via deras blogg på androidbook.blogspot.com, och på Twitter @ androidwireless.