El marco de efectos de medios de Android permite a los desarrolladores aplicar fácilmente muchos efectos visuales impresionantes a fotos y videos. Como el marco utiliza la GPU para realizar todas sus operaciones de procesamiento de imágenes, solo puede aceptar texturas OpenGL como entrada. En este tutorial, aprenderá cómo usar OpenGL ES 2.0 para convertir un recurso dibujable en una textura y luego usar el marco para aplicarle varios efectos..
Para seguir este tutorial, necesitas tener:
GLSurfaceView
Para mostrar los gráficos OpenGL en su aplicación, tiene que usar un GLSurfaceView
objeto. Como cualquier otro Ver
, puedes agregarlo a un Actividad
o Fragmento
definiéndolo en un archivo XML de diseño o creando una instancia del mismo en código.
En este tutorial, vas a tener un GLSurfaceView
objeto como el único Ver
en tus Actividad
. Por lo tanto, crearlo en código es más simple. Una vez creado, pásalo al setContentView
Método para que llene toda la pantalla. Tu Actividad
es onCreate
El método debería verse así:
void protegido onCreate (Bundle savedInstanceState) super.onCreate (savedInstanceState); GLSurfaceView view = new GLSurfaceView (esto); setContentView (ver);
Debido a que el marco de Media Effects solo es compatible con OpenGL ES 2.0 o superior, pase el valor 2
al setEGLContextClientVersion
método.
view.setEGLContextClientVersion (2);
Para asegurarse de que el GLSurfaceView
renderiza su contenido solo cuando es necesario, pasa el valor RENDERMODE_WHEN_DIRTY
al setRenderMode
método.
view.setRenderMode (GLSurfaceView.RENDERMODE_WHEN_DIRTY);
UNA GLSurfaceView.Renderer
Es responsable de dibujar los contenidos de la GLSurfaceView
.
Crear una nueva clase que implemente el GLSurfaceView.Renderer
interfaz. Voy a llamar a esta clase EffectsRenderer
. Después de agregar un constructor y anular todos los métodos de la interfaz, la clase debería tener este aspecto:
La clase pública EffectsRenderer implementa GLSurfaceView.Renderer public EffectsRenderer (contexto contextual) 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)
Vuelve a tu Actividad
y llamar al setRenderer
método para que el GLSurfaceView
utiliza el renderizador personalizado.
view.setRenderer (nuevo EffectsRenderer (this));
Si planea publicar su aplicación en Google Play, agregue lo siguiente a AndroidManifest.xml:
Esto asegura que su aplicación solo pueda instalarse en dispositivos que sean compatibles con OpenGL ES 2.0. El entorno OpenGL ya está listo.
los GLSurfaceView
no se puede mostrar una foto directamente. La foto debe convertirse en una textura y aplicarse primero a una forma OpenGL. En este tutorial, crearemos un plano 2D que tiene cuatro vértices. Para simplificar, hagámoslo un cuadrado. Crear una nueva clase, Cuadrado
, para representar el cuadrado.
clase publica cuadrada
El sistema de coordenadas predeterminado de OpenGL tiene su origen en su centro. Como resultado, las coordenadas de las cuatro esquinas de nuestra plaza, cuyos lados son dos unidades largo, será:
Todos los objetos que dibujamos utilizando OpenGL deben estar formados por triángulos. Para dibujar el cuadrado, necesitamos dos triángulos con un borde común. Esto significa que las coordenadas de los triángulos serán:
triángulo 1: (-1, -1), (1, -1) y (-1, 1)
triángulo 2: (1, -1), (-1, 1) y (1, 1)
Crear un flotador
matriz para representar estos vértices.
vértices flotantes privados [] = -1f, -1f, 1f, -1f, -1f, 1f, 1f, 1f,;
Para asignar la textura en el cuadrado, debe especificar las coordenadas de los vértices de la textura. Las texturas siguen un sistema de coordenadas en el que el valor de la coordenada y aumenta a medida que aumenta. Crea otra matriz para representar los vértices de la textura..
float privado textureVertices [] = 0f, 1f, 1f, 1f, 0f, 0f, 1f, 0f;
Las matrices de coordenadas deben convertirse en búferes de bytes antes de que OpenGL pueda usarlos. Vamos a declarar estos buffers primero.
FloatBuffer privado verticesBuffer; FloatBuffer de textura privadaBuffer;
Escriba el código para inicializar estos buffers en un nuevo método llamado inicializarBuffers
. Utilizar el ByteBuffer.allocateDirect
Método para crear el búfer. Porque un flotador
usos 4 bytes, debe multiplicar el tamaño de los arreglos con el valor 4.
A continuación, utilice ByteBuffer.nativeOrder
para determinar el orden de bytes de la plataforma nativa subyacente y establecer el orden de los buffers en ese valor. Utilizar el comoFloatBuffer
método para convertir el ByteBuffer
instancia en un FloatBuffer
. Después de la FloatBuffer
es creado, usa el poner
Método para cargar la matriz en el búfer. Finalmente, use el posición
Método para asegurarse de que el búfer se lee desde el principio.
Los contenidos de la inicializarBuffers
El método debería verse así:
private void initializeBuffers () ByteBuffer buff = ByteBuffer.allocateDirect (vertices.length * 4); buff.order (ByteOrder.nativeOrder ()); verticesBuffer = buff.asFloatBuffer (); verticesBuffer.put (vértices); verticesBuffer.position (0); buff = ByteBuffer.allocateDirect (textureVertices.length * 4); buff.order (ByteOrder.nativeOrder ()); textureBuffer = buff.asFloatBuffer (); textureBuffer.put (textureVertices); texturaBuffer.position (0);
Es hora de escribir tus propios shaders. Los Shaders no son más que simples programas en C que se ejecutan por la GPU para procesar cada vértice individual. Para este tutorial, tienes que crear dos sombreadores, un sombreado de vértice y un sombreador de fragmentos..
El código C para el sombreador de vértices es:
atributo vec4 aPosition; atributo vec2 aTexPosition; variable vec2 vTexPosition; void main () gl_Position = aPosition; vTexPosition = aTexPosition; ;
El código C para el sombreador de fragmentos es:
flotador mediump de precisión; muestreador uniforme2D uTextura; variable vec2 vTexPosition; void main () gl_FragColor = texture2D (uTexture, vTexPosition); ;
Si ya conoce OpenGL, este código le debe ser familiar porque es común en todas las plataformas. Si no lo hace, para comprender estos programas debe consultar la documentación de OpenGL. Aquí hay una breve explicación para comenzar:
una posición
Es una variable que estará ligada a la FloatBuffer
que contiene las coordenadas de los vértices. similar, aTexPosition
Es una variable que estará ligada a la FloatBuffer
que contiene las coordenadas de la textura. gl_Posición
es una variable integrada de OpenGL y representa la posición de cada vértice. los vTexPosition
es un variar
variable, cuyo valor simplemente se pasa al fragmento shader.textura2D
Método y los asigna al fragmento utilizando una variable incorporada llamada gl_FragColor
.El código de sombreado debe representarse como Cuerda
objetos en la clase.
Private final String vertexShaderCode = "attribute vec4 aPosition;" + "atributo vec2 aTexPosition;" + "variable vec2 vTexPosition"; + "void main () " + "gl_Position = aPosition;" + "vTexPosition = aTexPosition;" + ""; Private final String fragmentShaderCode = "precision mediump float;" + "muestreador uniforme2D uTextura;" + "variable vec2 vTexPosition"; + "void main () " + "gl_FragColor = texture2D (uTexture, vTexPosition);" + "";
Crea un nuevo método llamado inicializar programa
para crear un programa OpenGL después de compilar y vincular los shaders.
Utilizar glCreateShader
para crear un objeto de sombreado y devolverle una referencia en forma de En t
. Para crear un sombreador de vértice, pase el valor GL_VERTEX_SHADER
lo. Del mismo modo, para crear un sombreador de fragmentos, pase el valor GL_FRAGMENT_SHADER
lo. Siguiente uso glShaderSource
para asociar el código de sombreado apropiado con el sombreador. Utilizar glCompileShader
para compilar el código de sombreado.
Después de compilar ambos shaders, crea un nuevo programa usando glCreateProgram
. Al igual que glCreateShader
, esto también devuelve un En t
Como referencia al programa. Llamada glAttachShader
para adjuntar los shaders al programa. Por fin llamar glLinkProgram
para vincular el programa.
Su método y las variables asociadas deben tener este aspecto:
privado int vertexShader; privado int fragmentShader; programa int privado; 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); programa = GLES20.glCreateProgram (); GLES20.glAttachShader (programa, vertexShader); GLES20.glAttachShader (programa, fragmentShader); GLES20.glLinkProgram (programa);
Es posible que haya notado que los métodos OpenGL (los métodos prefijados con gl
) pertenecen a la clase GLES20
. Esto es porque estamos utilizando OpenGL ES 2.0. Si desea utilizar una versión superior, deberá utilizar las clases Glos30
o GLES31
.
Crea un nuevo método llamado dibujar
para dibujar el cuadrado usando los vértices y sombreadores que definimos anteriormente.
Esto es lo que necesitas hacer en este método:
glBindFramebuffer
para crear un objeto de búfer de cuadros con nombre (a menudo llamado FBO).glUseProgram
Para empezar a usar el programa que acabamos de enlazar..GL_BLEND
a glDisable
para deshabilitar la mezcla de colores mientras se renderiza.glGetAttribLocation
para obtener un manejo de las variables una posición
y aTexPosition
mencionado en el código de sombreado de vértice.glGetUniformLocation
para obtener una manija a la constante uTextura
mencionado en el código de sombreado de fragmentos.glVertexAttribPointer
asociar el una posición
y aTexPosition
maneja con el verticesBuffer
y el texturaBuffer
respectivamente.glBindTexture
para unir la textura (pasada como un argumento a la dibujar
Método) al fragmento shader.GLSurfaceView
utilizando glClear
.glDrawArrays
Método para dibujar realmente los dos triángulos (y por lo tanto el cuadrado).El código para el dibujar
El método debería verse así:
sorteo público vacío (textura int) GLES20.glBindFramebuffer (GLES20.GL_FRAMEBUFFER, 0); GLES20.glUseProgram (programa); GLES20.glDisable (GLES20.GL_BLEND); int positionHandle = GLES20.glGetAttribLocation (program, "aPosition"); int textureHandle = GLES20.glGetUniformLocation (programa, "uTexture"); int texturePositionHandle = GLES20.glGetAttribLocation (programa, "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, textura); GLES20.glUniforme1i (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);
Agregue un constructor a la clase para inicializar los buffers y el programa en el momento de la creación del objeto..
plaza pública () initializeBuffers (); initializeProgram ();
Actualmente, nuestro renderizador no hace nada. Necesitamos cambiar eso para que pueda renderizar el plano que creamos en los pasos anteriores.
Pero primero, vamos a crear una Mapa de bits
. Agrega cualquier foto a tu proyecto res / drawable carpeta. El archivo que estoy usando se llama bosque.jpg. Utilizar el BitmapFactory
para convertir la foto en una Mapa de bits
objeto. Además, almacenar las dimensiones de la Mapa de bits
objeto en variables separadas.
Cambiar el constructor de la EffectsRenderer
clase para que tenga los siguientes contenidos:
Foto de mapa de bits privada; Privado int photoWidth, photoHeight; public EffectsRenderer (contexto de contexto) super (); photo = BitmapFactory.decodeResource (context.getResources (), R.drawable.forest); photoWidth = photo.getWidth (); photoHeight = photo.getHeight ();
Crea un nuevo método llamado generarSquare
para convertir el mapa de bits en una textura e inicializar una Cuadrado
objeto. También necesitarás una matriz de enteros para contener referencias a las texturas de OpenGL. Utilizar glGenTextures
para inicializar la matriz y glBindTexture
para activar la textura en el índice 0
.
A continuación, utilice glTexParameteri
para establecer varias propiedades que decidan cómo se representa la textura:
GL_TEXTURE_MIN_FILTER
(la función minificadora) y la GL_TEXTURE_MAG_FILTER
(la función de aumento) para GL_LINEAR
para asegurarse de que la textura se vea suave, incluso cuando se estira o se encoge.GL_TEXTURE_WRAP_S
y GL_TEXTURE_WRAP_T
a GL_CLAMP_TO_EDGE
Para que la textura nunca se repita..Finalmente, use el texImage2D
método para mapear el Mapa de bits
a la textura. La implementación de la generarSquare
El método debería verse así:
Texturas int privadas [] = int nuevo [2]; Plaza privada; private void generateSquare () GLES20.glGenTextures (2, texturas, 0); GLES20.glBindTexture (GLES20.GL_TEXTURE_2D, texturas [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 ();
Siempre que las dimensiones de la GLSurfaceView
cambiar el onSurfaceChanged
método de la Renderizador
se llama. Aquí es donde tienes que llamar glViewPort
para especificar las nuevas dimensiones de la ventana gráfica. Además, llame glClearColor
para pintar el GLSurfaceView
negro. A continuación, llame generarSquare
Reinicializar las texturas y el plano..
@Override public void onSurfaceChanged (GL10 gl, int width, int height) GLES20.glViewport (0,0, width, height); GLES20.glClearColor (0,0,0,1); generateSquare ();
Finalmente, llame al Cuadrado
objetos dibujar
metodo dentro del onDrawFrame
método de la Renderizador
.
@Override public void onDrawFrame (GL10 gl) square.draw (texturas [0]);
Ahora puede ejecutar su aplicación y ver que la foto que eligió se renderiza como una textura OpenGL en un plano.
El código complejo que escribimos hasta ahora era solo un requisito previo para utilizar el marco de Media Effects. Ahora es el momento de comenzar a utilizar el marco en sí. Agregue los siguientes campos a su Renderizador
clase.
Efecto de contexto privado Efecto de contexto; Efecto de efecto privado;
Inicializar el effectContext
campo utilizando el EffectContext.createWithCurrentGlContext
. Es responsable de administrar la información sobre los efectos visuales dentro de un contexto OpenGL. Para optimizar el rendimiento, esto debe llamarse solo una vez. Agregue el siguiente código al comienzo de su onDrawFrame
método.
if (effectContext == null) effectContext = EffectContext.createWithCurrentGlContext ();
Crear un efecto es muy simple. Utilizar el effectContext
para crear un EffectFactory
y usar el EffectFactory
para crear un Efecto
objeto. Una vez que un Efecto
El objeto está disponible, puedes llamar aplicar
y pasarle una referencia a la textura original, en nuestro caso es texturas [0]
, junto con una referencia a un objeto de textura en blanco, en nuestro caso es texturas [1]
. Después de la aplicar
método se llama, texturas [1]
contendrá el resultado de la Efecto
.
Por ejemplo, para crear y aplicar el escala de grises Efecto, aquí tienes el código que debes escribir:
private void grayScaleEffect () EffectFactory factory = effectContext.getFactory (); effect = factory.createEffect (EffectFactory.EFFECT_GRAYSCALE); effect.apply (texturas [0], photoWidth, photoHeight, textures [1]);
Llame a este método en onDrawFrame
y pasar texturas [1]
al Cuadrado
objetos dibujar
método. Tu onDrawFrame
El método debe tener el siguiente código:
@Override public void onDrawFrame (GL10 gl) if (effectContext == null) effectContext = EffectContext.createWithCurrentGlContext (); if (effect! = null) effect.release (); grayScaleEffect (); square.draw (texturas [1]);
los lanzamiento
Este método se utiliza para liberar todos los recursos en poder de un Efecto
. Cuando ejecute la aplicación, debería ver el siguiente resultado:
Puedes usar el mismo código para aplicar otros efectos. Por ejemplo, aquí está el código para aplicar el documental efecto:
documentary void privadoEffect () EffectFactory factory = effectContext.getFactory (); effect = factory.createEffect (EffectFactory.EFFECT_DOCUMENTARY); effect.apply (texturas [0], photoWidth, photoHeight, textures [1]);
El resultado se ve así:
Algunos efectos toman parámetros. Por ejemplo, el efecto de ajuste de brillo tiene un brillo
parámetro que toma un flotador
valor. Puedes usar setParameter
Para cambiar el valor de cualquier parámetro. El siguiente código le muestra cómo usarlo:
private void brightnessEffect () EffectFactory factory = effectContext.getFactory (); effect = factory.createEffect (EffectFactory.EFFECT_BRIGHTNESS); effect.setParameter ("brillo", 2f); effect.apply (texturas [0], photoWidth, photoHeight, textures [1]);
El efecto hará que tu aplicación genere el siguiente resultado:
En este tutorial, ha aprendido cómo utilizar el Marco de efectos de medios para aplicar varios efectos a sus fotos. Mientras lo hacía, también aprendió a dibujar un plano con OpenGL ES 2.0 y a aplicarle varias texturas..
El marco puede aplicarse tanto a fotos como a videos. En el caso de los videos, simplemente tiene que aplicar el efecto a los cuadros individuales del video en el onDrawFrame
método.
Ya has visto tres efectos en este tutorial y el marco tiene docenas más para experimentar. Para saber más sobre ellos, consulte el sitio web del desarrollador de Android.