Met het Media Effects-framework van Android kunnen ontwikkelaars eenvoudig tal van indrukwekkende visuele effecten toepassen op foto's en video's. Aangezien het framework de GPU gebruikt om al zijn bewerkingen voor beeldverwerking uit te voeren, kan het alleen OpenGL-texturen als invoer accepteren. In deze zelfstudie leer je hoe je OpenGL ES 2.0 gebruikt om een aantrekbare bron om te zetten in een structuur en vervolgens het kader te gebruiken om verschillende effecten toe te passen.
Als u deze zelfstudie wilt volgen, moet u beschikken over:
GLSurfaceView
Als u OpenGL-afbeeldingen in uw app wilt weergeven, moet u a. Gebruiken GLSurfaceView
voorwerp. Zoals elk ander Uitzicht
, je kunt het aan een toevoegen Activiteit
of Fragment
door het te definiëren in een lay-out XML-bestand of door er een exemplaar van te maken in code.
In deze tutorial zul je een GLSurfaceView
object als de enige Uitzicht
in uw Activiteit
. Daarom is het eenvoudiger om het in code te maken. Eenmaal gemaakt, geeft u deze door aan de setContentView
methode zodat deze het hele scherm vult. Jouw Activiteit
's onCreate
methode zou er als volgt uit moeten zien:
protected void onCreate (Bundle savedInstanceState) super.onCreate (savedInstanceState); GLSurfaceView-weergave = nieuwe GLSurfaceView (this); setContentView (view);
Omdat het kader Media Effects alleen OpenGL ES 2.0 of hoger ondersteunt, geeft u de waarde door 2
naar de setEGLContextClientVersion
methode.
view.setEGLContextClientVersion (2);
Om ervoor te zorgen dat de GLSurfaceView
maakt de inhoud alleen wanneer nodig, geef de waarde door RENDERMODE_WHEN_DIRTY
naar de setRenderMode
methode.
view.setRenderMode (GLSurfaceView.RENDERMODE_WHEN_DIRTY);
EEN GLSurfaceView.Renderer
is verantwoordelijk voor het tekenen van de inhoud van de GLSurfaceView
.
Maak een nieuwe klasse die de. Implementeert GLSurfaceView.Renderer
interface. Ik ga deze klas bellen EffectsRenderer
. Na het toevoegen van een constructor en het overschrijven van alle methoden van de interface, zou de klasse er als volgt uit moeten zien:
public class EffectsRenderer implementeert GLSurfaceView.Renderer public EffectsRenderer (context context) super (); @Override public void onSurfaceCreated (GL10 gl, EGLConfig config) @Override public void onSurfaceChanged (GL10 gl, int width, int height) @Override public void onDrawFrame (GL10 gl)
Ga terug naar jouw Activiteit
en bel de setRenderer
methode zodat de GLSurfaceView
maakt gebruik van de aangepaste renderer.
view.setRenderer (nieuwe EffectsRenderer (this));
Als u van plan bent uw app te publiceren op Google Play, voegt u het volgende toe aan AndroidManifest.xml:
Dit zorgt ervoor dat uw app alleen kan worden geïnstalleerd op apparaten die OpenGL ES 2.0 ondersteunen. De OpenGL-omgeving is nu klaar.
De GLSurfaceView
kan een foto niet rechtstreeks weergeven. De foto moet eerst in een structuur worden omgezet en eerst op een OpenGL-vorm worden toegepast. In deze zelfstudie maken we een 2D-vlak met vier hoekpunten. Laten we het omwille van de eenvoud een vierkant maken. Maak een nieuwe klas, Plein
, om het vierkant te vertegenwoordigen.
openbare klas Square
Het standaard OpenGL-coördinatensysteem heeft zijn oorsprong in het midden. Dientengevolge, de coördinaten van de vier hoeken van ons vierkant, waarvan de zijkanten zijn twee eenheden lang, zal zijn:
Alle objecten die we met OpenGL tekenen, moeten uit driehoeken bestaan. Om het vierkant te tekenen, hebben we twee driehoeken met een gemeenschappelijke rand nodig. Dit betekent dat de coördinaten van de driehoeken zijn:
driehoek 1: (-1, -1), (1, -1) en (-1, 1)
driehoek 2: (1, -1), (-1, 1) en (1, 1)
Maak een vlotter
array om deze hoekpunten te vertegenwoordigen.
private float-hoekpunten [] = -1f, -1f, 1f, -1f, -1f, 1f, 1f, 1f,;
Als u de textuur op het vierkant wilt plaatsen, moet u de coördinaten van de hoekpunten van de textuur opgeven. Texturen volgen een coördinatensysteem waarin de waarde van de y-coördinaat toeneemt naarmate u hoger gaat. Maak een andere array om de hoekpunten van de textuur te vertegenwoordigen.
privé float textureVertices [] = 0f, 1f, 1f, 1f, 0f, 0f, 1f, 0f;
De arrays van coördinaten moeten worden geconverteerd naar bytebuffers voordat OpenGL deze kan gebruiken. Laten we deze buffers eerst verklaren.
private FloatBuffer verticesBuffer; privé FloatBuffer-textuurBuffer;
Schrijf de code om deze buffers te initialiseren in een nieuwe methode genaamd initializeBuffers
. Gebruik de ByteBuffer.allocateDirect
methode om de buffer te maken. Omdat een vlotter
toepassingen 4 bytes, moet u de grootte van de arrays vermenigvuldigen met de waarde 4.
Gebruik vervolgens ByteBuffer.nativeOrder
om de bytevolgorde van het onderliggende native platform te bepalen en de volgorde van de buffers op die waarde in te stellen. Gebruik de asFloatBuffer
methode om het te converteren ByteBuffer
bijvoorbeeld in een FloatBuffer
. Na de FloatBuffer
is gemaakt, gebruik de leggen
methode om de array in de buffer te laden. Gebruik tenslotte de positie
methode om ervoor te zorgen dat de buffer vanaf het begin wordt gelezen.
De inhoud van de initializeBuffers
methode zou er als volgt uit moeten zien:
private void initializeBuffers () ByteBuffer buff = ByteBuffer.allocateDirect (vertices.length * 4); buff.order (ByteOrder.nativeOrder ()); verticesBuffer = buff.asFloatBuffer (); verticesBuffer.put (vertices); verticesBuffer.position (0); buff = ByteBuffer.allocateDirect (textureVertices.length * 4); buff.order (ByteOrder.nativeOrder ()); textureBuffer = buff.asFloatBuffer (); textureBuffer.put (textureVertices); textureBuffer.position (0);
Het is tijd om je eigen shaders te schrijven. Shaders zijn niets anders dan eenvoudige C-programma's die door de GPU worden uitgevoerd om elk individueel hoekpunt te verwerken. Voor deze zelfstudie moet je twee shaders maken, een hoekpuntshader en een fragmentshader.
De C-code voor de hoekpuntshader is:
attribuut vec4 aPosition; kenmerk vec2 aTexPosition; variërende vec2 vTexPosition; void main () gl_Position = aPosition; vTexPosition = aTexPosition; ;
De C-code voor de fragmentshader is:
precisie mediump float; uniforme sampler2D uTextuur; variërende vec2 vTexPosition; void main () gl_FragColor = texture2D (uTexture, vTexPosition); ;
Als u OpenGL al kent, moet deze code u bekend voorkomen, omdat deze op alle platforms gebruikelijk is. Als u dit niet doet, moet u om deze programma's te begrijpen naar de OpenGL-documentatie verwijzen. Hier is een korte uitleg om aan de slag te gaan:
een positie
is een variabele die zal worden gebonden aan de FloatBuffer
die de coördinaten van de hoekpunten bevat. evenzo, aTexPosition
is een variabele die gebonden zal zijn aan de FloatBuffer
die de coördinaten van de textuur bevat. gl_Position
is een ingebouwde OpenGL-variabele en vertegenwoordigt de positie van elke vertex. De vTexPosition
is een wisselende
variabele, waarvan de waarde eenvoudigweg wordt doorgegeven aan de fragmentshader.texture2D
methode en wijst ze toe aan het fragment met behulp van een ingebouwde variabele met de naam gl_FragColor
.De arceringscode moet worden weergegeven als Draad
objecten in de klas.
private final String vertexShaderCode = "attribute vec4 aPosition;" + "attribute vec2 aTexPosition;" + "variërende vec2 vTexPosition;" + "void main () " + "gl_Position = aPosition;" + "vTexPosition = aTexPosition;" + ""; private final String fragmentShaderCode = "precision mediump float;" + "uniforme sampler2D uTexture;" + "variërende vec2 vTexPosition;" + "void main () " + "gl_FragColor = texture2D (uTexture, vTexPosition);" + "";
Maak een nieuwe methode genaamd initializeProgram
om een OpenGL-programma te maken na het compileren en koppelen van de shaders.
Gebruik glCreateShader
om een arceringobject te maken en er een verwijzing naar terug te sturen in de vorm van een int
. Als u een hoekpuntshader wilt maken, geeft u de waarde door GL_VERTEX_SHADER
ernaar toe. Op dezelfde manier geeft u de waarde door om een fragmentshader te maken GL_FRAGMENT_SHADER
ernaar toe. Volgend gebruik glShaderSource
om de juiste shadercode aan de arcering te koppelen. Gebruik glCompileShader
om de shadercode te compileren.
Na het compileren van beide shaders, maakt u een nieuw programma met behulp van glCreateProgram
. Net als glCreateShader
, ook dit geeft een int
als een verwijzing naar het programma. telefoontje glAttachShader
om de shaders aan het programma te hechten. Tot slot, bel glLinkProgram
om het programma te linken.
Uw methode en de bijbehorende variabelen moeten er als volgt uitzien:
private int vertexShader; privé int fragmentShader; privé int-programma; private void initializeProgram () 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); programma = GLES20.glCreateProgram (); GLES20.glAttachShader (programma, vertexShader); GLES20.glAttachShader (programma, fragmentShader); GLES20.glLinkProgram (programma);
Je hebt misschien gemerkt dat de OpenGL-methoden (de methoden voorafgegaan door gl
) behoren tot de klas GLES20
. Dit komt omdat we OpenGL ES 2.0 gebruiken. Als u een hogere versie wilt gebruiken, moet u de klassen gebruiken GLES30
of GLES31
.
Maak een nieuwe methode genaamd trek
om het vierkant daadwerkelijk te tekenen met de hoekpunten en shaders die we eerder hebben gedefinieerd.
Dit is wat u moet doen in deze methode:
glBindFramebuffer
een benoemd framebufferobject maken (vaak FBO genoemd).glUseProgram
om het programma te gaan gebruiken dat we zojuist hebben gekoppeld.GL_BLEND
naar glDisable
om het mengen van kleuren tijdens het renderen uit te schakelen.glGetAttribLocation
om grip te krijgen op de variabelen een positie
en aTexPosition
genoemd in de vertex shader-code.glGetUniformLocation
om grip te krijgen op de constante uTexture
genoemd in de fragment-arceringscode.glVertexAttribPointer
om het te associëren een positie
en aTexPosition
handgrepen met de verticesBuffer
en de textureBuffer
respectievelijk.glBindTexture
om de textuur te binden (doorgegeven als een argument aan de trek
methode) naar de fragmentshader.GLSurfaceView
gebruik makend van glClear
.glDrawArrays
methode om de twee driehoeken (en dus het vierkant) daadwerkelijk te tekenen.De code voor de trek
methode zou er als volgt uit moeten zien:
openbare ongeldige trekking (int texture) GLES20.glBindFramebuffer (GLES20.GL_FRAMEBUFFER, 0); GLES20.glUseProgram (programma); GLES20.glDisable (GLES20.GL_BLEND); int positionHandle = GLES20.glGetAttribLocation (programma, "aPosition"); int textureHandle = GLES20.glGetUniformLocation (programma, "uTexture"); int texturePositionHandle = GLES20.glGetAttribLocation (programma, "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, textuur); 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);
Voeg een constructor toe aan de klasse om de buffers en het programma te initialiseren op het moment dat het object wordt gemaakt.
public Square () initializeBuffers (); initializeProgram ();
Momenteel doet onze renderer niets. We moeten dat veranderen zodat het het vliegtuig kan maken dat we in de vorige stappen hebben gemaakt.
Maar laten we eerst een maken Bitmap
. Voeg een foto toe aan uw project res / betekenbare map. Het bestand dat ik gebruik wordt gebeld forest.jpg. Gebruik de BitmapFactory
om de foto in een te converteren Bitmap
voorwerp. Bewaar ook de afmetingen van de Bitmap
object in afzonderlijke variabelen.
Wijzig de constructor van de EffectsRenderer
klasse, zodat deze de volgende inhoud heeft:
privé bitmapfoto; private int photoWidth, photoHeight; public EffectsRenderer (context context) super (); photo = BitmapFactory.decodeResource (context.getResources (), R.drawable.forest); photoWidth = photo.getWidth (); photoHeight = photo.getHeight ();
Maak een nieuwe methode genaamd generateSquare
om de bitmap in een structuur om te zetten en a te initialiseren Plein
voorwerp. U zult ook een array van gehele getallen nodig hebben om verwijzingen naar de OpenGL-texturen te behouden. Gebruik glGenTextures
om de array te initialiseren en glBindTexture
om de textuur bij index te activeren 0
.
Gebruik vervolgens glTexParameteri
om verschillende eigenschappen in te stellen die bepalen hoe de textuur wordt weergegeven:
GL_TEXTURE_MIN_FILTER
(de verkleinerfunctie) en de GL_TEXTURE_MAG_FILTER
(de vergrotingsfunctie) naar GL_LINEAR
om ervoor te zorgen dat de textuur er glad uitziet, zelfs wanneer deze uitgerekt of gekrompen is.GL_TEXTURE_WRAP_S
en GL_TEXTURE_WRAP_T
naar GL_CLAMP_TO_EDGE
zodat de textuur nooit wordt herhaald.Gebruik tenslotte de texImage2D
methode om de kaart in kaart te brengen Bitmap
naar de textuur. De implementatie van de generateSquare
methode zou er als volgt uit moeten zien:
private int textures [] = nieuwe int [2]; privé Vierkant plein; private void generateSquare () GLES20.glGenTextures (2, textures, 0); GLES20.glBindTexture (GLES20.GL_TEXTURE_2D, texturen [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 ();
Wanneer de afmetingen van de GLSurfaceView
verander de onSurfaceChanged
methode van de renderer
wordt genoemd. Hier moet je bellen glViewport
om de nieuwe dimensies van de viewport op te geven. Bel ook glClearColor
om de te schilderen GLSurfaceView
zwart. Bel vervolgens generateSquare
om de texturen en het vlak opnieuw te initialiseren.
@Override public void onSurfaceChanged (GL10 gl, int width, int height) GLES20.glViewport (0,0, width, height); GLES20.glClearColor (0,0,0,1); generateSquare ();
Tot slot belt u de Plein
voorwerpen trek
methode binnen de onDrawFrame
methode van de renderer
.
@Override public void onDrawFrame (GL10 gl) square.draw (textures [0]);
U kunt nu uw app uitvoeren en zien dat de door u gekozen foto wordt weergegeven als een OpenGL-structuur in een vlak.
De complexe code die we tot nu toe schreven, was slechts een vereiste om het kader voor media-effecten te gebruiken. Het is nu tijd om het framework zelf te gaan gebruiken. Voeg de volgende velden toe aan uw renderer
klasse.
privé EffectContext effectContext; privé effect effect;
Initialiseer de effectContext
veld met behulp van de EffectContext.createWithCurrentGlContext
. Het is verantwoordelijk voor het beheer van de informatie over de visuele effecten in een OpenGL-context. Om de prestaties te optimaliseren, moet deze maar één keer worden aangeroepen. Voeg de volgende code toe aan het begin van uw onDrawFrame
methode.
if (effectContext == null) effectContext = EffectContext.createWithCurrentGlContext ();
Het creëren van een effect is heel eenvoudig. Gebruik de effectContext
om een te maken EffectFactory
en gebruik de EffectFactory
om een te maken Effect
voorwerp. Eens een Effect
object is beschikbaar, u kunt bellen van toepassing zijn
en geef een verwijzing naar de originele textuur door, in ons geval is dat zo texturen [0]
, samen met een verwijzing naar een leeg textuurobject, in ons geval is dat het geval textures [1]
. Na de van toepassing zijn
methode wordt genoemd, textures [1]
bevat het resultaat van de Effect
.
Bijvoorbeeld om het te maken en toe te passen grijstinten effect, hier is de code die je moet schrijven:
private void grayScaleEffect () EffectFactory factory = effectContext.getFactory (); effect = factory.createEffect (EffectFactory.EFFECT_GRAYSCALE); effect.apply (texturen [0], photoWidth, photoHeight, textures [1]);
Noem deze methode in onDrawFrame
en ga voorbij textures [1]
naar de Plein
voorwerpen trek
methode. Jouw onDrawFrame
methode moet de volgende code hebben:
@Override public void onDrawFrame (GL10 gl) if (effectContext == null) effectContext = EffectContext.createWithCurrentGlContext (); if (effect! = null) effect.release (); grayScaleEffect (); square.draw (textures [1]);
De vrijlating
methode wordt gebruikt om alle bronnen vrij te maken die in het bezit zijn van een Effect
. Wanneer u de app uitvoert, ziet u het volgende resultaat:
U kunt dezelfde code gebruiken om andere effecten toe te passen. Hier is bijvoorbeeld de code om de documentaire effect:
private void documentaryEffect () EffectFactory factory = effectContext.getFactory (); effect = factory.createEffect (EffectFactory.EFFECT_DOCUMENTARY); effect.apply (texturen [0], photoWidth, photoHeight, textures [1]);
Het resultaat ziet er als volgt uit:
Sommige effecten nemen parameters. Het helderheidsaanpassingseffect heeft bijvoorbeeld een helderheid
parameter die een vlotter
waarde. Je kunt gebruiken setParameter
om de waarde van elke parameter te wijzigen. De volgende code laat zien hoe je het gebruikt:
private void brightnessEffect () EffectFactory factory = effectContext.getFactory (); effect = factory.createEffect (EffectFactory.EFFECT_BRIGHTNESS); effect.setParameter ("brightness", 2f); effect.apply (texturen [0], photoWidth, photoHeight, textures [1]);
Door het effect wordt uw app het volgende resultaat weergegeven:
In deze zelfstudie hebt u geleerd hoe u met het Media Effects Framework verschillende effecten op uw foto's kunt toepassen. Terwijl je dit deed, heb je ook geleerd hoe je een vliegtuig tekent met OpenGL ES 2.0 en verschillende texturen erop toepast.
Het raamwerk kan worden toegepast op zowel foto's als video's. In het geval van video's, moet je het effect gewoon toepassen op de individuele frames van de video in de onDrawFrame
methode.
Je hebt al drie effecten in deze zelfstudie gezien en het raamwerk heeft nog tientallen extra effecten om mee te experimenteren. Raadpleeg de website van de Android-ontwikkelaar voor meer informatie over deze sites.