RenderScript er et skriptspråk på Android som gjør at du kan skrive høy ytelse grafisk gjengivelse og rå beregningskode. Lær mer om RenderScript og skriv din første grafikkapp som bruker RenderScript i denne opplæringen.
RenderScript-APIene ble formelt introdusert til Android SDK i API-nivå 11 (også Android 3.0, Honeycomb). RenderScript gir et middel til å skrive ytelseskritisk kode som systemet senere kompilerer til innbyggerkoden for prosessoren den kan kjøre på. Dette kan være enhetens CPU, en multi-core CPU, eller til og med GPU. Som det til slutt kjører, avhenger av mange faktorer som ikke er lett tilgjengelige for utvikleren, men avhenger også av hvilken arkitektur den interne plattformskompilatoren støtter.
Denne opplæringen vil komme i gang med et enkelt renderingskript vi kaller "Falling Snow". Det er et partikkelsystem hvor hver snøfnugg er representert av et punkt som faller, akselererer nedover skjermen. Vind og annen tilfeldighet skaper en mild virvlende effekt.
RenderScript er basert på C programmeringsspråket. Hvis du ikke er kjent med C, anbefaler vi at du blir kjent med det først før du prøver å bruke RenderScript. Selv om RenderScript ikke er OpenGL, krever det heller ikke at du bruker det til grafisk gjengivelse, konseptene for bruk av det ligner på OpenGL-konsepter. Derfor vil kjennskap til OpenGL og 3D grafikk terminologi hjelpe.
Åpne kildekoden for denne opplæringen er tilgjengelig for nedlasting. Vi anbefaler at du bruker den til å følge med. Kodelistene i denne opplæringen inkluderer ikke hele innholdet i hver fil.
La oss starte med det mest detaljerte trinnet og arbeide for å bruke skriptet fra en typisk Android-aktivitet.
Opprett en ny prosjektfil som heter snow.rs i ditt src-tre, under pakken du skal jobbe med. På toppen, definer du versjonen av RenderScript du jobber med:
#pragma versjon (1)
Sett deretter inn Java-pakken som dette skriptet tilhører, for eksempel:
#pragma rs java_package_name (com.mamlambo.fallingsnow)
Vi bruker noen funksjoner fra RenderScript-grafikk-API, så ta med det overskriften:
#include "rs_graphics.rsh"
Definer nå to funksjoner, root () og init ():
int root () // TBD void init () // TBD
Roten () -funksjonen blir inngangspunktet for dette skriptet. Returverdien definerer om skriptet kjøres en gang (retur 0) eller med N-millisekund intervaller (retur N). Hvis maskinvaren ikke kan følge med den forespurte frekvensen, kjører roten () så ofte som mulig.
Init () -funksjonen kalles en gang når skriptet laster og er et godt sted å initialisere variabler og andre tilstandsparametere.
Opprett en nettverksvariabel som vil bli initialisert på Android-siden, og lag en enkel struktur for å holde informasjon om hver snøfnugg. Mens du er i det, opprett et par variabler for å holde vind- og tyngdekraftsverdiene.
rs_mesh snowMesh; typedef struct __attribute __ ((pakket, justert (4))) Snø float2 hastighet; float2 posisjon; uchar4 farge; Snow_t; Snow_t * snø; float2 vind; float2 grav;
Initialiser vinden og tyngdekraften i init () -funksjonen:
grav.x = 0; grav.y = 18; wind.x = rsRand (50) +20; wind.y = rsRand (4) - 2;
Initialiser snøen i sin egen funksjon:
void initSnow () const float w = rsgGetWidth (); const float h = rsgGetHeight (); int snowCount = rsAllocationGetDimX (rsGetAllocation (snø)); Snow_t * pSnow = snø; for (int i = 0; i < snowCount; i++) pSnow->posisjon.x = rsRand (w); pSnow-> position.y = rsRand (h); pSnow-> hastighet.y = rsRand (60); pSnow-> hastighet.x = rsRand (100); pSnow-> hastighet.x - = 50; uchar4 c = rsPackColorTo8888 (255, 255, 255); pSnow-> farge = c; pSnow ++;
Før vi går videre, la oss snakke om initSnow () -funksjonen. For å starte, hentes bredden og høyden på tegneområdet. Skriptet trenger derfor å vite hvor mange snøfnuggstrukturer vi skal skape. Det gjør dette ved å få dimensjonene til tildelingen, som refereres av snøpekeren. Men hvor er pekeren initiert og hva er en allokering? Pekeren er initialisert fra Android-koden. En allokering er en av måtene som minne administreres av Android-koden, men brukes av skriptet. De interne detaljene er ikke viktige for øyeblikket. For vårt formål kan vi tenke på det som en rekke Snow_t struct objekter.
Sløyfen lukter over hver struktur og angir noen tilfeldige verdier, slik at startbildet ser naturlig ut.
La oss nå implementere en enkel root () -funksjon som trekker scenen uten animasjon. Vi bruker dette for å få resten av systemet på plass:
int root () rsgClearColor (0,0f, 0,0f, 0,0f, 0,0f); rsgDrawMesh (snowMesh); returner 0;
Når du lagrer skriptprosjektfilen i Eclipse, vil bygningsmennene automatisk opprette en fil som heter snow.bc i / res / raw-katalogen. Denne automatisk genererte filen bør ikke kontrolleres i kildekontrollen, og det bør heller ikke endres. I tillegg er det opprettet noen Java-filer i / gen-mappen. Dette er grensesnittfilene som brukes til å ringe inn i skriptet fra Android.
Nå som skriptet er opprettet, må vi initialisere det for bruk fra Android-klassene dine. For å gjøre dette har vi opprettet en hjelper Java-klasse kalt SnowRS. I det tildeler vi minnet til snøfnuggene, initierer skriptet, og binder nettverket og snøflakallokalet til det. Denne klassen bruker også en RenderScriptGL-objekt. Dette objektet er opprettet i neste trinn som en del av View-klassen vi skal lage.
offentlig klasse SnowRS offentlig statisk endelig int SNOW_FLAKES = 4000; privat ScriptC_snow mScript; beskyttet int mWidth; beskyttet int mHeight; beskyttet boolean mPreview; beskyttede ressurser mResources; beskyttet RenderScriptGL mRS; offentlig SnowRS (int bredde, int høyde) mWidth = width; mHeight = høyde; offentlig tomgangstopp () mRS.bindRootScript (null); offentlig tomgangstart () mRS.bindRootScript (mScript); public void init (RenderScriptGL rs, Resources res, boolean isPreview) mRS = rs; mResources = res; mPreview = erPreview; mScript = (ScriptC_snow) createScript (); offentlig RenderScriptGL getRS () return mRS; offentlige ressurser getResources () return mResources; offentlig ScriptC createScript () ScriptField_Snow snø = nytt 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 script; script = nytt ScriptC_snow (mRS, getResources (), R.raw.snow); script.set_snowMesh (sm); script.bind_snow (sne); script.invoke_initSnow (); returnere skript;
Spesielt, la oss se på createScript () -metoden. Den første delen oppretter strukturen med 4000 oppføringer (SNOW_FLAKES = 4000). Det bruker da dette til å lage et maskobjekt, som brukes til gjenoppretting. Renderingskonstruksjonen er satt til POINT, så hver snøfnugg vil dukke opp som en piksel på skjermen.
Deretter initialiserer vi selve skriptet, ved hjelp av rå ressursoppføring opprettet av Eclipse-byggeren. Deretter tildeler vi nettverket og arrayallokeringen i skriptet via anropene set_snowMesh () og bind_snow (), henholdsvis. Til slutt begynner vi snøen med et anrop til initSnow () -funksjonen vi opprettet tidligere ved å ringe invoke_initSnow ().
Skriptet begynner ikke å kjøre på dette punktet, men init () -funksjonen er blitt kalt. For å få skriptet til å kjøre, ring bindRootScript () på skriptobjektet, sett i startmetoden ().
Android SDK gir bare objektet vi trenger for utdata fra RenderScript: RSSurfaceView-klassen. Implementere en klasse som heter FallingSnowView som utvider denne klassen, slik som:
offentlig klasse FallingSnowView utvider RSSurfaceView private RenderScriptGL mRSGL; privat SnowRS mRender; offentlig FallingSnowView (kontekst kontekst) super (kontekst); @Override public void surfaceChanged (SurfaceHolder holder, int format, int w, int h) super.surfaceChanged (holder, format, w, h); hvis (mRSGL == null) RenderScriptGL.SurfaceConfig sc = nytt RenderScriptGL.SurfaceConfig (); mRSGL = createRenderScriptGL (sc); mRSGL.setSurface (holder, w, h); mRender = ny SnowRS (w, h); mRender.init (mRSGL, getResources (), false); mRender.start (); @Override protected void onDetachedFromWindow () hvis (mRSGL! = Null) mRSGL = null; destroyRenderScriptGL ();
Metoden surfaceChanged () lager et RenderScriptGL-objekt fra SurfaceHolder sendt inn, etter behov. Da er vårt SnowRS-objekt opprettet, og rendering er startet.
Alt er på plass for å bruke FallingSnowView-klassen i en aktivitetsklasse. Metoden onCreate () i aktivitetsklassen kan være så enkel som dette:
Offentlig tomgang onCreate (Bundle savedInstanceState) super.onCreate (savedInstanceState); snowView = ny FallingSnowView (dette); setContentView (Snow);
Men vent! Det er bare et statisk bilde. Ikke veldig interessant, er det? La oss gå tilbake til snow.rs-filen og redigere rot () -funksjonen. For noen enkle simuleringer av pseudo-fysikk-stil, vil du gjerne iterere over hver snøflake og bruke sin nåværende hastighet og vind til sin posisjon. Deretter justerer hastigheten basert på tyngdekraften akselerasjon. Endelig sjekk for å se om det er snøfall nederst på skjermen. Kompleksitetsnivået og effektiviteten av kodingen her vil påvirke hvilken slags rammefrekvens du til slutt vil få. Vi har forsøkt å holde det veldig enkelt for denne opplæringen.
Her er det vi har gjort:
int root () // Klar til bakgrunnsfargen rsgClearColor (0.0f, 0.0f, 0.0f, 0.0f); // tid siden sist oppdatert float dt = min (rsGetDt (), 0.1f); // dimens float w = rsgGetWidth (); flyte h = rsgGetHeight (); int snowCount = rsAllocationGetDimX (rsGetAllocation (snø)); Snow_t * pSnow = snø; for (int i = 0; i < snowCount; i++) pSnow->posisjon.x + = ((pSnow-> hastighet.x + wind.x) * dt); pSnow-> position.y + = ((pSnow-> hastighet.y + wind.y) * dt); hvis (pSnow-> position.y> h) pSnow-> position.y = 0; pSnow-> posisjon.x = rsRand (w); pSnow-> hastighet.y = rsRand (60); pSnow-> hastighet.x + = (grav.x) * dt; pSnow-> hastighet.y + = (grav.y) * dt; pSnow ++; rsgDrawMesh (snowMesh); hvis (rsRand (32) == 1) wind.x = 0-wind.x; returnere 30;
Og her er det i bevegelse:
Denne opplæringen har nettopp ridd på overflaten av hva RenderScript kan gjøre (Haha, få den? Surface?). Du kan bruke et RenderScript-beregningsskript for å bruke grafiske effekter til bitmaps. Du kan legge til shaders for å utnytte enhetsgrafikk maskinvare for å tegne scenen annerledes. Du kan sette opp transformasjoner for å tegne i et 3D-rom. Du kan konfigurere teksturer for å tegne. Det er mye mer du kan gjøre med RenderScript.
Selv om RenderScript er mer begrensende enn å bruke OpenGL ES i 3D-gjengivelsesområdet, legger tillegget av RenderScript-kunnskap til noen velkomstegenskaper. Tegning av en rask 3D-scene ved hjelp av RenderScript kan være mer effektiv, kodende, enn å bruke OpenGL. Bruke RenderScript for tung beregning eller bildehåndtering kan være raskere å utvikle, og bedre enn tilsvarende NDK-løsninger (på grunn av automatisk distribusjon på tvers av maskinvarekjerner). I motsetning til når du utvikler med Android NDK, trenger du ikke å bekymre deg for den underliggende maskinvarearkitekturen.
Den største ulempen av RenderScript er mangelen på bærbarhet av eksisterende kode. Hvis du allerede har OpenGL-kode eller beregningsfunksjoner i C som du vil utnytte i Android-appene dine, vil du kanskje bare holde fast i Android NDK.
Denne opplæringen har gitt deg en smak av å bruke RenderScript med Android-applikasjonene dine. Du lærte grunnleggende om å skrive og initialisere et skript, og gjengivelse til skjermen. Alt dette ble gjort i sammenheng med et enkelt partikkelsystem som simulerte pikselstørrelser av snøflak.
Gi oss beskjed om hvilke kule RenderScript-apper du bygger i kommentarene!
Mobilutviklere Lauren Darcey og Shane Conder har medforfatter flere bøker om Android-utvikling: en grundig programmeringsbok med tittel Android Wireless Application Development og Sams Lær deg selv Android Application Development i 24 timer. Når de ikke skriver, bruker de sin tid på å utvikle mobil programvare hos deres firma og tilby konsulenttjenester. De kan nås via e-post til [email protected], via bloggen deres på androidbook.blogspot.com, og på Twitter @androidwireless.