Cadrul Android Efecte media permite dezvoltatorilor să aplice cu ușurință o mulțime de efecte vizuale impresionante fotografiilor și clipurilor video. Pe măsură ce cadrul utilizează GPU-ul pentru a efectua toate operațiile de procesare a imaginilor, acesta poate accepta texturile OpenGL ca intrări. În acest tutorial, veți învăța cum să utilizați OpenGL ES 2.0 pentru a converti o resursă trasabilă într-o textură și apoi să utilizați cadrul pentru a aplica diferite efecte.
Pentru a urma acest tutorial, trebuie să aveți:
GLSurfaceView
Pentru a afișa grafica OpenGL în aplicația dvs., trebuie să utilizați a GLSurfaceView
obiect. Ca oricare alta Vedere
, îl puteți adăuga la un Activitate
sau Fragment
definind-o într-un fișier XML cu aspect sau creând o instanță a acestuia în cod.
În acest tutorial, veți avea un a GLSurfaceView
obiect ca fiind singurul Vedere
în tine Activitate
. Prin urmare, crearea acestuia în cod este mai simplă. Odată creat, trimiteți-l la setContentView
astfel încât să umple întregul ecran. Ta Activitate
„s onCreate
metoda ar trebui să arate astfel:
protejat void onCreate (Bundle savedInstanceState) super.onCreate (savedInstanceState); Vizualizare GLSurfaceView = GLSurfaceView nou (acest lucru); setContentView (vizualizare);
Deoarece cadrul Media Efecte acceptă numai OpenGL ES 2.0 sau o versiune ulterioară, treceți valoarea 2
la setEGLContextClientVersion
metodă.
view.setEGLContextClientVersion (2);
Pentru a vă asigura că GLSurfaceView
redă conținutul său numai atunci când este necesar, trece valoarea RENDERMODE_WHEN_DIRTY
la setRenderMode
metodă.
view.setRenderMode (GLSurfaceView.RENDERMODE_WHEN_DIRTY);
A GLSurfaceView.Renderer
este responsabil pentru desenarea conținutului GLSurfaceView
.
Creați o clasă nouă care implementează GLSurfaceView.Renderer
interfață. O să chem această clasă EffectsRenderer
. După adăugarea unui constructor și suprimarea tuturor metodelor interfeței, clasa ar trebui să arate astfel:
clasa publică EffectsRenderer implementează GLSurfaceView.Renderer public EffectsRenderer (Context context) super (); @Override public void onSurfaceCreated (GL10 gl, EGLCconfig) @Overide public void onSurfaceChanged GL10 gl, int width, int height @Override public void peDrawFrame (GL10 gl)
Întoarce-te la tine Activitate
și apelați setRenderer
astfel încât GLSurfaceView
utilizează funcția de randare personalizată.
view.setRenderer (noul EffectsRenderer (acest lucru));
Dacă intenționați să publicați aplicația pe Google Play, adăugați următoarele AndroidManifest.xml:
Acest lucru vă asigură că aplicația dvs. poate fi instalată numai pe dispozitivele care acceptă OpenGL ES 2.0. Mediul OpenGL este acum gata.
GLSurfaceView
nu poate afișa direct o fotografie. Fotografia trebuie transformată într-o textură și aplicată mai întâi într-o formă OpenGL. În acest tutorial, vom crea un plan 2D care are patru noduri. Din motive de simplitate, hai să o facem pătrat. Creați o nouă clasă, Pătrat
, pentru a reprezenta pătratul.
clasa publica publica
Sistemul implicit de coordonate OpenGL își are originea în centrul său. Ca rezultat, coordonatele celor patru colțuri ale pieței noastre, ale căror laturi sunt două unități lung, va fi:
Toate obiectele pe care le desenăm folosind OpenGL ar trebui să fie alcătuite din triunghiuri. Pentru a desena pătratul, avem nevoie de două triunghiuri cu o margine comună. Aceasta înseamnă că coordonatele triunghiurilor vor fi:
triunghiul 1: (-1, -1), (1, -1) și (-1, 1)
triunghiul 2: (1, -1), (-1, 1) și (1, 1)
Creeaza o pluti
pentru a reprezenta aceste vârfuri.
vertexurile plutitoare private [] = -1f, -1f, 1f, -1f, -1f, 1f, 1f, 1f,;
Pentru a plasa textura pe pătrat, trebuie să specificați coordonatele vârfurilor texturii. Texturile urmează un sistem de coordonate în care valoarea coordonatei y crește pe măsură ce mergeți mai sus. Creați o altă matrice pentru a reprezenta vârful texturii.
textura plutitoare privatăVerturi [] = 0f, 1f, 1f, 1f, 0f, 0f, 1f, 0f;
Rețelele de coordonate trebuie să fie transformate în buffere de octeți înainte ca OpenGL să le poată folosi. Să declare aceste tampoane mai întâi.
privat FloatBuffer verticesBuffer; privat FloatBuffer textureBuffer;
Scrie codul pentru a inițializa aceste tampoane într-o metodă nouă numită initializeBuffers
. Folosește ByteBuffer.allocateDirect
metoda de creare a tamponului. Pentru că a pluti
utilizări 4 octeți, trebuie să multiplicați dimensiunea matricelor cu valoarea 4.
Apoi, utilizați ByteBuffer.nativeOrder
pentru a determina ordinea byte a platformei native care sta la baza, și setați ordinea tampoanelor la acea valoare. Folosește asFloatBuffer
- metoda de conversie ByteBuffer
exemplu în FloatBuffer
. După FloatBuffer
este creat, utilizați a pune
metoda de încărcare a matricei în tampon. În cele din urmă, utilizați poziţie
pentru a vă asigura că memoria tampon este citită de la început.
Conținutul mesajului initializeBuffers
metoda ar trebui să arate astfel:
private void initializeBuffers () ByteBuffer buff = ByteBuffer.allocateDirect (vertices.length * 4); buff.order (ByteOrder.nativeOrder ()); verticesBuffer = buff.asFloatBuffer (); verticesBuffer.put (nodurile); verticesBuffer.position (0); Buff = ByteBuffer.allocateDirect (textureVertices.length * 4); buff.order (ByteOrder.nativeOrder ()); textureBuffer = buff.asFloatBuffer (); textureBuffer.put (textureVertices); textureBuffer.position (0);
Este timpul să vă scrieți propriile shadere. Shaderele nu sunt decât programe simple C care sunt gestionate de GPU pentru a procesa fiecare vertex individual. Pentru acest tutorial, trebuie să creați două shadere, un shader de vârf și un fragment de shader.
Codul C pentru shaderul de vertex este:
atribut vec4 aPosition; atribut vec2 aTexPosition; variabile vec2 vTexPosition; void principală () gl_Position = aPosition; vTexPosition = aTexPosition; ;
Codul C pentru fragmentul shader este:
precizie, cu capăt mediu; proba uniformă de eșantionare2D; variabile vec2 vTexPosition; void principal () gl_FragColor = textură2D (uTexture, vTexPosition); ;
Dacă știți deja OpenGL, acest cod trebuie să vă fie cunoscut deoarece este comun pe toate platformele. Dacă nu, pentru a înțelege aceste programe, trebuie să vă referiți la documentația OpenGL. Iată o scurtă explicație pentru a începe:
o pozitie
este o variabilă care va fi legată de FloatBuffer
care conține coordonatele nodurilor. asemănător, aTexPosition
este o variabilă care va fi legată de FloatBuffer
care conține coordonatele texturii. gl_Position
este o variabilă OpenGL încorporată și reprezintă poziția fiecărui vârf. vTexPosition
este a variabil
variabilă, a cărei valoare este pur și simplu transferată către shader-ul fragmentului.texture2D
și le atribuie fragmentului utilizând o variabilă încorporată numită gl_FragColor
.Codul shader trebuie reprezentat ca Şir
obiecte din clasă.
privat final String vertexShaderCode = "atv vec4 aPosition;" + "atribut vec2 aTexPosition;" + "variază vec2 vTexPosition;" + "void principală () " + "gl_Position = aPoziție;" + "vTexPosition = aTexPosition;" + ""; fragmentul final al fragmentului StringShaderCode = "floare de precizie mediump"; + "eșantion unic de eșantionare2D"; + "variază vec2 vTexPosition;" + "void main () " + "gl_FragColor = textură2D (uTexture, vTexPosition);" + "";
Creați o nouă metodă numită initializeProgram
pentru a crea un program OpenGL după compilarea și conectarea shaderelor.
Utilizare glCreateShader
pentru a crea un obiect shader și a trimite o referință la acesta sub forma unui int
. Pentru a crea un shader de vertex, treceți valoarea GL_VERTEX_SHADER
la el. În mod similar, pentru a crea un shader de fragment, treceți valoarea GL_FRAGMENT_SHADER
la el. Următoarea utilizare glShaderSource
pentru a asocia codul shader corespunzător cu shader. Utilizare glCompileShader
pentru a compila codul shader.
După ce ați compilat ambele shadere, creați un nou program folosind glCreateProgram
. Ca și cum glCreateShader
, acest lucru returnează și o int
ca referință la program. Apel glAttachShader
pentru a atașa shaderele la program. În cele din urmă, sună glLinkProgram
pentru a conecta programul.
Metoda și variabilele asociate ar trebui să arate astfel:
private int vertexShader; private int fragmentShader; programul privat int; privat 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); program = GLES20.glCreateProgram (); GLES20.glAttachShader (program, vertexShader); GLES20.glAttachShader (program, fragmentShader); GLES20.glLinkProgram (program);
S-ar putea să fi observat că metodele OpenGL (metodele prefixate cu gl
) aparțin clasei GLES20
. Acest lucru se datorează faptului că folosim OpenGL ES 2.0. Dacă doriți să utilizați o versiune superioară, atunci va trebui să utilizați clasele GLES30
sau GLES31
.
Creați o nouă metodă numită a desena
pentru a desena efectiv pătratul folosind vârfurile și shaderele pe care le-am definit mai devreme.
Iată ce trebuie să faceți în această metodă:
glBindFramebuffer
pentru a crea un obiect tampon numit cadru (adesea numit FBO).glUseProgram
pentru a începe să folosiți programul pe care tocmai l-am conectat.GL_BLEND
la glDisable
pentru a dezactiva amestecarea culorilor în timpul redării.glGetAttribLocation
pentru a obține un mâner variabilelor o pozitie
și aTexPosition
menționate în codul shader de vârfuri.glGetUniformLocation
pentru a obține un mâner la constanta uTexture
menționate în codul shader de fragment.glVertexAttribPointer
să asociați o pozitie
și aTexPosition
se mânerează cu verticesBuffer
si textureBuffer
respectiv.glBindTexture
pentru a lega textură (trecut ca argument la a desena
metoda) la shader-ul fragmentului.GLSurfaceView
utilizând glClear
.glDrawArrays
metoda de a trage efectiv cele două triunghiuri (și astfel pătratul).Codul pentru a desena
metoda ar trebui să arate astfel:
public void draw (textura int) GLES20.glBindFramebuffer (GLES20.GL_FRAMEBUFFER, 0); GLES20.glUseProgram (program); GLES20.glDisable (GLES20.GL_BLEND); int positionHandle = GLES20.glGetAttribLocație (program, "aPoziție"); int textureHandle = GLES20.glGetUniformLocation (program, "uTexture"); int texturePositionHandle = GLES20.glGetAttribLocation (program, "aTexPosition"); GLES20.glVertexAttribPointer (texturăPositionHandle, 2, GLES20.GL_FLOAT, falsă, 0, texturaBuffer); GLES20.glEnableVertexAttribArray (texturePositionHandle); GLES20.glActiveTexture (GLES20.GL_TEXTURE0); GLES20.glBindTexture (GLES20.GL_TEXTURE_2D, textura); GLES20.glUniform1i (texturăHandle, 0); GLES20.glVertexAttribPointer (pozițiaHandle, 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);
Adăugați un constructor la clasă pentru a inițializa bufferele și programul în momentul creării obiectului.
public Square () initializeBuffers (); initializeProgram ();
În prezent, editorul nostru nu face nimic. Trebuie să schimbăm acest lucru astfel încât acesta să poată face planul pe care l-am creat în pașii anteriori.
Dar, mai întâi, să creăm a Bitmap
. Adăugați o fotografie la proiectul dvs. res / drawable pliant. Se numește fișierul pe care îl folosesc forest.jpg. Folosește BitmapFactory
pentru a converti fotografia într-o Bitmap
obiect. De asemenea, stocați dimensiunile Bitmap
obiect în variabile separate.
Schimbați constructorul EffectsRenderer
astfel încât să aibă următorul conținut:
fotografie bitmap privată; private photoWidth, photoHeight; public EffectsRenderer (Context context) super (); photo = BitmapFactory.decodeResource (context.getResources (), R.drawable.forest); photoWidth = photo.getWidth (); photoHeight = photo.getHeight ();
Creați o nouă metodă numită generateSquare
pentru a converti bitmap-ul într-o textura și a inițializa a Pătrat
obiect. De asemenea, veți avea nevoie de o serie de numere întregi pentru a menține referințe la texturile OpenGL. Utilizare glGenTextures
pentru a inițializa matricea și glBindTexture
pentru a activa textura la index 0
.
Apoi, utilizați glTexParameteri
pentru a stabili diferite proprietăți care să decidă cum este redată textura:
GL_TEXTURE_MIN_FILTER
(funcția de minificare) și GL_TEXTURE_MAG_FILTER
(funcția de mărire) la GL_LINEAR
pentru a vă asigura că textura pare netedă, chiar și atunci când este întinsă sau strânsă.GL_TEXTURE_WRAP_S
și GL_TEXTURE_WRAP_T
la GL_CLAMP_TO_EDGE
astfel încât textura să nu se repete niciodată.În cele din urmă, utilizați texImage2D
metoda de hartă a Bitmap
la textura. Punerea în aplicare a directivei generateSquare
metoda ar trebui să arate astfel:
int int textures [] = int int [2]; pătrat privat; void privat generateSquare () GLES20.glGenTexturi (2, texturi, 0); GLES20.glBindTexture (GLES20.GL_TEXTURE_2D, texturi [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, fotografie, 0); pătrat = pătrat nou ();
Ori de câte ori dimensiunile GLSurfaceView
schimba onSurfaceChanged
metodă a Renderer
se numește. Aici trebuie să sunați glViewPort
pentru a specifica noile dimensiuni ale ferestrei de vizualizare. De asemenea, sunați glClearColor
pentru a picta GLSurfaceView
negru. Apoi, sună generateSquare
pentru a reinitializa texturile și avionul.
@Overide public void onSurfaceChanged (GL10 gl, lățime int, int înălțime) GLES20.glViewport (0,0, lățime, înălțime); GLES20.glClearColor (0,0,0,1); generateSquare ();
În cele din urmă, apelați Pătrat
obiecte a desena
în interiorul metodei onDrawFrame
metodă a Renderer
.
@Override public void peDrawFrame (GL10 gl) square.draw (texturi [0]);
Acum puteți să rulați aplicația dvs. și să vedeți fotografia pe care ați ales-o ca fiind redată ca textură OpenGL într-un avion.
Codul complex pe care l-am scris până acum era doar o condiție prealabilă pentru utilizarea cadrului Media Efecte. Acum este momentul să începeți să utilizați cadrul propriu-zis. Adăugați următoarele câmpuri la dvs. Renderer
clasă.
efecte privateContextContextContext; efect efectiv privat;
Inițializați effectContext
folosind câmpul EffectContext.createWithCurrentGlContext
. Este responsabil pentru gestionarea informațiilor despre efectele vizuale din interiorul unui context OpenGL. Pentru a optimiza performanța, aceasta trebuie apelată o singură dată. Adăugați următorul cod la începutul paginii onDrawFrame
metodă.
dacă (effectContext == null) effectContext = EfectContext.createWithCurrentGlContext ();
Crearea unui efect este foarte simplă. Folosește effectContext
pentru a crea un EffectFactory
și utilizați EffectFactory
pentru a crea un Efect
obiect. O dată Efect
obiect este disponibil, puteți apela aplica
și să transmită o referire la textura originală, în cazul nostru texturi [0]
, împreună cu o referință la un obiect textura gol, în cazul nostru este texturi [1]
. După aplica
se numește metoda, texturi [1]
va conține rezultatul Efect
.
De exemplu, pentru a crea și a aplica alb-negru efect, iată codul pe care trebuie să-l scrieți:
void privat griScaleEffect () EffectFactory factory = efectContext.getFactory (); efect = factory.createEffect (EffectFactory.EFFECT_GRAYSCALE); efect.apply (texturi [0], photoWidth, photoHeight, texturi [1]);
Apelați această metodă în onDrawFrame
și treci texturi [1]
la Pătrat
obiecte a desena
metodă. Ta onDrawFrame
metoda ar trebui să aibă următorul cod:
@Override public void peDrawFrame (GL10 gl) if (effectContext == null) efectContext = EfectContext.createWithCurrentGlContext (); dacă (efect! = null) effect.release (); griScaleEffect (); square.draw (texturi [1]);
eliberare
metoda este folosită pentru a elibera toate resursele deținute de un Efect
. Când rulați aplicația, ar trebui să vedeți următorul rezultat:
Puteți utiliza același cod pentru a aplica alte efecte. De exemplu, iată codul care trebuie aplicat film documentar efect:
document void privat privatEffect () EffectFactory factory = effectContext.getFactory (); efect = factory.createEffect (EffectFactory.EFFECT_DOCUMENTARY); efect.apply (texturi [0], photoWidth, photoHeight, texturi [1]);
Rezultatul arată astfel:
Unele efecte iau parametri. De exemplu, efectul de ajustare a luminozității are a strălucire
parametru care ia un parametru pluti
valoare. Poți să folosești setParameter
pentru a modifica valoarea oricărui parametru. Următorul cod vă arată cum să îl utilizați:
luminozitatea privată voidEffect () EffectFactory factory = effectContext.getFactory (); efect = factory.createEffect (EffectFactory.EFFECT_BRIGHTNESS); efect.setParametru ("luminozitate", 2f); efect.apply (texturi [0], photoWidth, photoHeight, texturi [1]);
Efectul va face ca aplicația dvs. să producă următorul rezultat:
În acest tutorial, ați învățat cum să utilizați Media Effects Framework pentru a aplica diferite efecte fotografiilor dvs. În timp ce ați făcut acest lucru, ați învățat cum să desenați un plan folosind OpenGL ES 2.0 și să aplicați diverse texturi.
Cadrul poate fi aplicat atât fotografiilor, cât și videoclipurilor. În cazul videoclipurilor, pur și simplu trebuie să aplicați efectul pentru cadrele individuale ale videoclipului în onDrawFrame
metodă.
Ați văzut deja trei efecte în acest tutorial și cadrul are mai multe zeci pentru a vă experimenta. Pentru a afla mai multe despre ele, consultați site-ul Web al dezvoltatorului Android.