Android-rammene for Media Effects gjør at utviklere enkelt kan bruke mange imponerende visuelle effekter på bilder og videoer. Siden rammen bruker GPU til å utføre alle bildebehandlingsoperasjoner, kan den bare akseptere OpenGL-teksturer som inngang. I denne opplæringen skal du lære hvordan du bruker OpenGL ES 2.0 til å konvertere en trekkbar ressurs til en tekstur og deretter bruke rammen til å bruke ulike effekter på det.
For å følge denne opplæringen må du ha:
GLSurfaceView
Hvis du vil vise OpenGL-grafikk i appen din, må du bruke en GLSurfaceView
gjenstand. Som alle andre Utsikt
, du kan legge den til en Aktivitet
eller Fragment
ved å definere det i en layout XML-fil eller ved å lage en forekomst av det i kode.
I denne opplæringen skal du ha en GLSurfaceView
objekt som den eneste Utsikt
i din Aktivitet
. Derfor er det enklere å lage det i kode. Når du er opprettet, send det til setContentView
metode slik at den fyller hele skjermen. Din Aktivitet
's onCreate
metoden skal se slik ut:
beskyttet ugyldig onCreate (Bundle savedInstanceState) super.onCreate (savedInstanceState); GLSurfaceView view = ny GLSurfaceView (dette); setContentView (syn);
Fordi Media Effects-rammen bare støtter OpenGL ES 2.0 eller høyere, må du sende verdien 2
til setEGLContextClientVersion
metode.
view.setEGLContextClientVersion (2);
For å være sikker på at GLSurfaceView
gjør innholdet bare når det er nødvendig, passere verdien RENDERMODE_WHEN_DIRTY
til setRenderMode
metode.
view.setRenderMode (GLSurfaceView.RENDERMODE_WHEN_DIRTY);
EN GLSurfaceView.Renderer
er ansvarlig for å tegne innholdet i GLSurfaceView
.
Lag en ny klasse som implementerer GLSurfaceView.Renderer
grensesnitt. Jeg skal ringe denne klassen EffectsRenderer
. Etter å ha lagt til en konstruktør og overstyrer alle grensesnittets metoder, bør klassen se slik ut:
offentlig klasse EffectsRenderer implementerer GLSurfaceView.Renderer public EffectsRenderer (Kontekst kontekst) super (); @Override public void onSurfaceCreated (GL10 gl, EGLConfig config) @Override public void onSurfaceChanged (GL10 gl, int bredde, int høyde) @Override public void onDrawFrame (GL10 gl)
Gå tilbake til din Aktivitet
og ring til setRenderer
metode slik at GLSurfaceView
bruker den tilpassede rendereren.
view.setRenderer (new EffectsRenderer (dette));
Hvis du planlegger å publisere appen din på Google Play, legger du til følgende i AndroidManifest.xml:
Dette sørger for at appen din kun kan installeres på enheter som støtter OpenGL ES 2.0. OpenGL-miljøet er nå klart.
De GLSurfaceView
kan ikke vise et bilde direkte. Bildet må konverteres til en tekstur og brukes først til en OpenGL-form. I denne opplæringen skal vi lage et 2D-plan som har fire hjørner. For enkelhets skyld, la oss gjøre det til et torg. Lag en ny klasse, Torget
, å representere torget.
offentlig klasse Square
Standard OpenGL koordinatsystemet har sin opprinnelse i sentrum. Som et resultat, koordinatene til de fire hjørnene på torget vårt, hvis sider er to enheter lenge, vil være:
Alle objektene vi tegner ved hjelp av OpenGL, skal bestå av trekanter. For å tegne plassen trenger vi to trekanter med en felles kant. Dette betyr at koordinatene til trianglene vil være:
trekant 1: (-1, -1), (1, -1) og (-1, 1)
trekant 2: (1, -1), (-1, 1) og (1, 1)
Lage en flyte
array for å representere disse punktene.
private float vertices [] = -1f, -1f, 1f, -1f, -1f, 1f, 1f, 1f,;
For å kartlegge tekstur på torget, må du angi koordinatene til tekstene i teksturen. Teksturer følger et koordinatsystem der verdien av y-koordinaten øker etter hvert som du går høyere. Opprett en annen matrise for å representere stikkene på tekstur.
private float textureVertices [] = 0f, 1f, 1f, 1f, 0f, 0f, 1f, 0f;
Arrays av koordinater må konverteres til bytebuffere før OpenGL kan bruke dem. La oss erklære disse buffere først.
private FloatBuffer verticesBuffer; privat FloatBuffer textureBuffer;
Skriv koden for å initialisere disse buffere i en ny metode som kalles initializeBuffers
. Bruke ByteBuffer.allocateDirect
metode for å lage buffer. Fordi a flyte
bruker 4 byte, må du formere størrelsen på arrayene med verdien 4.
Deretter bruker du ByteBuffer.nativeOrder
for å bestemme byteordren til den underliggende innfødte plattformen, og angi rekkefølgen til bufferne til den verdien. Bruke asFloatBuffer
metode for å konvertere ByteBuffer
forekommer i a FloatBuffer
. Etter FloatBuffer
er opprettet, bruk sette
Metode for å laste opp arrayet i bufferen. Til slutt bruker du stilling
metode for å sikre at bufferen er lest fra begynnelsen.
Innholdet i initializeBuffers
metoden skal se slik ut:
Private void initializeBuffers () ByteBuffer buff = ByteBuffer.allocateDirect (vertices.length * 4); buff.order (ByteOrder.nativeOrder ()); verticesBuffer = buff.asFloatBuffer (); verticesBuffer.put (hjørner); verticesBuffer.position (0); buff = ByteBuffer.allocateDirect (textureVertices.length * 4); buff.order (ByteOrder.nativeOrder ()); textureBuffer = buff.asFloatBuffer (); textureBuffer.put (textureVertices); textureBuffer.position (0);
Det er på tide å skrive dine egne shaders. Shaders er bare enkle C-programmer som drives av GPUen for å behandle hvert enkelt toppunkt. For denne opplæringen må du opprette to shaders, en vertex shader og en fragment shader.
C-koden for vertex shader er:
attributt vec4 aPosition; attributt vec2 aTexPosition; varierende vec2 vTexPosition; void main () gl_Position = aPosition; vTexPosition = aTexPosition; ;
C-koden for fragmentskader er:
presisjon mediump float; uniform sampler2D uTexture; varierende vec2 vTexPosition; void main () gl_FragColor = texture2D (uTexture, vTexPosition); ;
Hvis du allerede kjenner OpenGL, bør denne koden være kjent for deg, fordi den er vanlig på alle plattformer. Hvis du ikke gjør det, for å forstå disse programmene må du referere til OpenGL-dokumentasjonen. Her er en kort forklaring for å komme i gang:
en posisjon
er en variabel som vil bli bundet til FloatBuffer
som inneholder koordinatene til punktene. på samme måte, aTexPosition
er en variabel som vil bli bundet til FloatBuffer
som inneholder koordinatene til tekstur. gl_Position
er en innebygd OpenGL-variabel og representerer posisjonen til hvert vertex. De vTexPosition
er en varierende
variabel, hvis verdi bare overføres til fragmentskader.texture2D
metode og tilordner dem til fragmentet ved hjelp av en innebygd variabel som heter gl_FragColor
.Skyggekoden må være representert som string
objekter i klassen.
privat endelig streng vertexShaderCode = "attributt vec4 aPosition;" + "attributt vec2 aTexPosition;" + "varierende vec2 vTexPosition;" + "void main () " + "gl_Position = aPosition;" + "vTexPosition = aTexPosition;" + ""; privat endelig String fragmentShaderCode = "presisjon mediump float;" + "uniform sampler2D uTexture;" + "varierende vec2 vTexPosition;" + "void main () " + "gl_FragColor = texture2D (uTexture, vTexPosition);" + "";
Opprett en ny metode som heter initializeProgram
å lage et OpenGL-program etter å ha kompilert og koblet shaders.
Bruk glCreateShader
å skape et shaderobjekt og returnere en referanse til det i form av en int
. For å opprette en vertex shader, pass verdien GL_VERTEX_SHADER
til det. På samme måte, for å opprette en fragment shader, passere verdien GL_FRAGMENT_SHADER
til det. Neste bruk glShaderSource
å knytte den tilhørende skyggekoden med skyggeren. Bruk glCompileShader
å kompilere skyggekoden.
Etter å ha samlet begge shaders, opprett et nytt program ved hjelp av glCreateProgram
. Akkurat som glCreateShader
, dette returnerer også en int
som referanse til programmet. Anrop glAttachShader
å feste shaders til programmet. Til slutt, ring glLinkProgram
å koble programmet.
Metoden din og tilhørende variabler skal se slik ut:
privat int vertexShader; privat int fragmentShader; privat int program; privat tomrom initialiseringsprogram () vertexShader = GLES20.glCreateShader (GLES20.GL_VERTEX_SHADER); GLES20.glShaderSource (vertexShader, vertexShaderCode); GLES20.glCompileShader (vertexShader); fragmentShader = GLES20.glCreateShader (GLES20.GL_FRAGMENT_SHADER); GLES20.glShaderSource (fragmentShader, fragmentShaderCode); GLES20.glCompileShader (fragmentShader); program = GLES20.glCreateProgram (); GLES20.glAttachShader (program, vertexShader); GLES20.glAttachShader (program, fragmentShader); GLES20.glLinkProgram (program);
Du har kanskje lagt merke til at OpenGL-metodene (metodene prefikset med gl
) tilhører klassen GLES20
. Dette skyldes at vi bruker OpenGL ES 2.0. Hvis du ønsker å bruke en høyere versjon, må du bruke klassene GLES30
eller GLES31
.
Opprett en ny metode som heter tegne
å faktisk tegne torget ved hjelp av vinkler og shaders vi definerte tidligere.
Her er hva du trenger å gjøre i denne metoden:
glBindFramebuffer
å opprette et navngitt rammebufferobjekt (ofte kalt FBO).glUseProgram
å begynne å bruke programmet vi nettopp har koblet til.GL_BLEND
til glDisable
for å deaktivere blanding av farger under gjenoppretting.glGetAttribLocation
å få et håndtak til variablene en posisjon
og aTexPosition
nevnt i vertex shader-koden.glGetUniformLocation
å få et håndtak til konstanten uTexture
nevnt i fragment shader-koden.glVertexAttribPointer
å knytte en posisjon
og aTexPosition
håndterer med verticesBuffer
og textureBuffer
henholdsvis.glBindTexture
å binde tekstur (passert som et argument til tegne
metode) til fragment shader.GLSurfaceView
ved hjelp av glClear
.glDrawArrays
metode for faktisk å tegne de to trekanter (og dermed kvadratet).Koden for tegne
metoden skal se slik ut:
offentlig tomromstegning (int tekstur) GLES20.glBindFramebuffer (GLES20.GL_FRAMEBUFFER, 0); GLES20.glUseProgram (program); GLES20.glDisable (GLES20.GL_BLEND); int positionHandle = GLES20.glGetAttribLocation (program, "aPosition"); int textureHandle = GLES20.glGetUniformLocation (program, "uTexture"); int texturePositionHandle = GLES20.glGetAttribLocation (program, "aTexPosition"); GLES20.glVertexAttribPointer (texturePositionHandle, 2, GLES20.GL_FLOAT, false, 0, textureBuffer); GLES20.glEnableVertexAttribArray (texturePositionHandle); GLES20.glActiveTexture (GLES20.GL_TEXTURE0); GLES20.glBindTexture (GLES20.GL_TEXTURE_2D, tekstur); GLES20.glUniform1i (textureHandle, 0); GLES20.glVertexAttribPointer (positionHandle, 2, GLES20.GL_FLOAT, false, 0, verticesBuffer); GLES20.glEnableVertexAttribArray (positionHandle); GLES20.glClear (GLES20.GL_COLOR_BUFFER_BIT); GLES20.glDrawArrays (GLES20.GL_TRIANGLE_STRIP, 0, 4);
Legg til en konstruktør i klassen for å initialisere buffere og programmet når objektet ble opprettet.
Public Square () initializeBuffers (); initializeProgram ();
For tiden gjør vår gjengivelse ingenting. Vi må endre det slik at det kan gjøre planet vi opprettet i de foregående trinnene.
Men først, la oss lage en bitmap
. Legg til et bilde på prosjektet ditt res / teikne mappe. Filen jeg bruker kalles forest.jpg. Bruke BitmapFactory
å konvertere bildet til en bitmap
gjenstand. Lagre også dimensjonene til bitmap
objekt i separate variabler.
Endre konstruktøren til EffectsRenderer
klassen slik at den har følgende innhold:
privat Bitmap-bilde; privat int photoWidth, photoHeight; offentlig EffectsRenderer (kontekst kontekst) super (); photo = BitmapFactory.decodeResource (context.getResources (), R.drawable.forest); photoWidth = photo.getWidth (); photoHeight = photo.getHeight ();
Opprett en ny metode som heter generateSquare
å konvertere bitmapet til en tekstur og initialisere a Torget
gjenstand. Du vil også trenge en rekke heltal for å holde referanser til OpenGL-teksturer. Bruk glGenTextures
å initialisere arrayen og glBindTexture
for å aktivere tekstur på indeksen 0
.
Deretter bruker du glTexParameteri
å angi forskjellige egenskaper som bestemmer hvordan teksten gjengis:
GL_TEXTURE_MIN_FILTER
(minifunksjonsfunksjonen) og GL_TEXTURE_MAG_FILTER
(forstørrelsesfunksjonen) til GL_LINEAR
for å sørge for at tekstur ser jevnt ut, selv når den strekkes eller krympes.GL_TEXTURE_WRAP_S
og GL_TEXTURE_WRAP_T
til GL_CLAMP_TO_EDGE
slik at tekstur aldri blir gjentatt.Til slutt bruker du texImage2D
metode for å kartlegge bitmap
til tekstur. Gjennomføringen av generateSquare
metoden skal se slik ut:
private int teksturer [] = new int [2]; privat torget; privat void generateSquare () GLES20.glGenTextures (2, teksturer, 0); GLES20.glBindTexture (GLES20.GL_TEXTURE_2D, teksturer [0]); GLES20.glTexParameteri (GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); GLES20.glTexParameteri (GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); GLES20.glTexParameteri (GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); GLES20.glTexParameteri (GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); GLUtils.texImage2D (GLES20.GL_TEXTURE_2D, 0, foto, 0); square = new Square ();
Når dimensjonene av GLSurfaceView
endre, onSurfaceChanged
metode av renderer
er kalt. Her er hvor du må ringe glViewPort
å angi de nye dimensjonene til visningsporten. Også, ring glClearColor
å male GLSurfaceView
svart. Neste, ring generateSquare
å reinitialisere teksturer og flyet.
@Override offentlig tomrum påSurfaceChanged (GL10 gl, int bredde, int høyde) GLES20.glViewport (0,0, bredde, høyde); GLES20.glClearColor (0,0,0,1); generateSquare ();
Til slutt, ring til Torget
objektets tegne
metode inne i onDrawFrame
metode av renderer
.
@Override public void onDrawFrame (GL10 gl) square.draw (teksturer [0]);
Du kan nå kjøre appen din og se bildet du valgte å bli gjengitt som en OpenGL-tekstur på et fly.
Den komplekse koden vi skrev til nå var bare en forutsetning for å bruke Media Effects-rammeverket. Nå er det dags å begynne å bruke rammen selv. Legg til følgende felt i din renderer
klasse.
privat EffectContext effectContext; privat effekt effekt;
Initialiser effectContext
feltet ved å bruke EffectContext.createWithCurrentGlContext
. Det er ansvarlig for å administrere informasjonen om de visuelle effektene i en OpenGL-kontekst. For å optimalisere ytelsen, bør dette bare kalles én gang. Legg til følgende kode i begynnelsen av din onDrawFrame
metode.
hvis (effectContext == null) effectContext = EffectContext.createWithCurrentGlContext ();
Å skape en effekt er veldig enkel. Bruke effectContext
å opprette en EffectFactory
og bruk EffectFactory
å opprette en Effekt
gjenstand. En gang en Effekt
Objektet er tilgjengelig, du kan ringe søke om
og send en referanse til den opprinnelige teksten til den, i vårt tilfelle er det teksturer [0]
, sammen med en referanse til et tomt teksturobjekt, i vårt tilfelle er det teksturer [1]
. Etter søke om
Metoden kalles, teksturer [1]
vil inneholde resultatet av Effekt
.
For eksempel, for å opprette og bruke gråtoner effekt, her er koden du må skrive:
privat void grayScaleEffect () EffectFactory fabrikk = effectContext.getFactory (); effect = factory.createEffect (EffectFactory.EFFECT_GRAYSCALE); effect.apply (teksturer [0], photoWidth, photoHeight, teksturer [1]);
Ring denne metoden i onDrawFrame
og passere teksturer [1]
til Torget
objektets tegne
metode. Din onDrawFrame
metoden skal ha følgende kode:
@Override public void onDrawFrame (GL10 gl) if (effectContext == null) effectContext = EffectContext.createWithCurrentGlContext (); hvis (effekt! = null) effect.release (); grayScaleEffect (); square.draw (strukturer [1]);
De utgivelse
Metoden brukes til å frigjøre alle ressurser som en Effekt
. Når du kjører appen, bør du se følgende resultat:
Du kan bruke samme kode for å bruke andre effekter. For eksempel, her er koden for å bruke dokumentar effekt:
private void documentaryEffect () EffectFactory fabrikk = effectContext.getFactory (); effect = factory.createEffect (EffectFactory.EFFECT_DOCUMENTARY); effect.apply (teksturer [0], photoWidth, photoHeight, teksturer [1]);
Resultatet ser slik ut:
Noen effekter tar parametere. For eksempel har lysstyrkejusteringseffekten a lysstyrke
parameter som tar a flyte
verdi. Du kan bruke setParameter
for å endre verdien av en hvilken som helst parameter. Følgende kode viser deg hvordan du bruker den:
privat tomt lysstyrkeEffect () EffectFactory fabrikk = effectContext.getFactory (); effect = factory.createEffect (EffectFactory.EFFECT_BRIGHTNESS); effect.setParameter ("lysstyrke", 2f); effect.apply (teksturer [0], photoWidth, photoHeight, teksturer [1]);
Effekten vil gjøre at appen gir følgende resultat:
I denne opplæringen har du lært hvordan du bruker Media Effects Framework til å bruke ulike effekter på bildene dine. Mens du gjør det, lærte du også å tegne et fly ved hjelp av OpenGL ES 2.0 og bruke ulike teksturer på den.
Rammen kan brukes på både bilder og videoer. I tilfelle av videoer, må du bare bruke effekten til de enkelte rammene av videoen i onDrawFrame
metode.
Du har allerede sett tre effekter i denne opplæringen og rammen har dusinvis mer for deg å eksperimentere med. Hvis du vil vite mer om dem, kan du se på Android Developer's nettsted.