Advertisement
  1. Code
  2. Android SDK

Cara menggunakan Android Media Effects dengan OpenGL ES

Scroll to top
Read Time: 12 min

Indonesian (Bahasa Indonesia) translation by Sandi Muamar (you can also view the original English article)

Android Media Effects framework memungkinkan developer untuk dengan mudah menerapkan banyak impresif untuk foto dan video. Rangka menggunakan GPU untuk melakukan semua gambar pengolahan operasi, itu hanya dapat menerima tekstur OpenGL sebagai input. Dalam tutorial ini, Anda akan belajar bagaimana menggunakan OpenGL ES 2.0 untuk mengubah resource drawable menjadi sebuah tekstur dan kemudian menggunakan framework untuk menerapkan berbagai efek untuk itu.

Prasyarat

Untuk mengikuti tutorial ini, Anda harus mempunyai:

  • IDE yang mendukung pengembangan aplikasi Android. Jika Anda tidak memiliki satu, mendapatkan versi terbaru dari Android Studio dari Android pengembang situs.
  • sebuah perangkat yang menjalankan Android 4.0 + dan GPU yang mendukung OpenGL ES 2.0.
  • pemahaman dasar tentang OpenGL.

1. menyiapkan lingkungan OpenGL ES

Langkah 1: Buat GLSurfaceView

Untuk menampilkan grafis OpenGL dalam aplikasi Anda, Anda harus menggunakan GLSurfaceView objek. Seperti View lain, Anda dapat menambahkannya ke Activity atau Fragment oleh mendefinisikan dalam tata letak file XML atau membuat contoh kode.

Dalam tutorial ini, Anda akan memiliki sebuah objek GLSurfaceView sebagai satu-satunya View dalam Activity Anda. Oleh karena itu, membuat kode lebih sederhana. Setelah dibuat, passing ke metode setContentView sehingga ini mengisi seluruh layar. Metode onCreate Activity's Anda akan terlihat seperti ini:

1
protected void onCreate(Bundle savedInstanceState) {
2
    super.onCreate(savedInstanceState);
3
4
    GLSurfaceView view = new GLSurfaceView(this);
5
    setContentView(view);
6
}

Karena kMedia Effects framework hanya mendukung OpenGL ES 2.0 atau lebih tinggi, melewati nilai 2 metode setEGLContextClientVersion.

1
view.setEGLContextClientVersion(2);

Untuk memastikan bahwa GLSurfaceView menuliskan isinya hanya bila diperlukan, melewati nilai RENDERMODE_WHEN_DIRTY setRenderMode metode.

1
view.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);

Langkah 2: Buat sebuah Renderer

GLSurfaceView.Renderer bertanggung jawab untuk drawing isi GLSurfaceView.

Membuat sebuah class baru yang mengimplementasikan interface GLSurfaceView.Renderer. Aku akan memanggil kelas ini EffectsRenderer. Setelah menambahkan konstruktor dan mengganti semua metode antarmuka, kelas akan terlihat seperti ini:

1
public class EffectsRenderer implements GLSurfaceView.Renderer {
2
3
    public EffectsRenderer(Context context){
4
  	super();
5
	}
6
7
    @Override
8
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
9
    }
10
11
    @Override
12
    public void onSurfaceChanged(GL10 gl, int width, int height) {
13
	}
14
15
    @Override
16
    public void onDrawFrame(GL10 gl) {
17
	}
18
}

Kembali ke Activity Anda dan memanggil metode setRenderer sehingga GLSurfaceView menggunakan renderer kustom.

1
view.setRenderer(new EffectsRenderer(this));

Langkah 3: Mengedit Manifest

Jika Anda berencana untuk menerbitkan aplikasi Anda di Google Play, tambahkan baris berikut ke AndroidManifest.xml:

1
<uses-feature android:glEsVersion="0x00020000" android:required="true" />

Hal ini membuat yakin bahwa aplikasi Anda hanya dapat diinstal pada perangkat yang mendukung OpenGL ES 2.0. Lingkungan OpenGL kini siap.

2. menciptakan sebuah OpenGL Pesawat

Langkah 1: Menentukan simpul

GLSurfaceView tidak dapat menampilkan foto langsung. Foto telah dikonversi menjadi sebuah tekstur dan diterapkan ke bentuk OpenGL yang pertama. Dalam tutorial ini, kita akan menciptakan sebuah pesawat 2D yang memiliki empat simpul. Demi kesederhanaan, mari kita membuat sebuah persegi. Membuat baru kelas, Square, untuk mewakili alun-alun.

1
public class Square {
2
3
}

Sistem koordinat OpenGL default berawal di pusat. Sebagai akibatnya, koordinat empat sudut alun-alun kami, yang sisi-sisinya adalah dua unit panjang, akan:

  • sudut kiri bawah di (-1, -1)
  • sudut kanan di bawah (1, -1)
  • pojok kanan atas di (1, 1)
  • sudut kiri atas pada (-1, 1)

Semua objek yang kita buat menggunakan OpenGL harus terdiri dari segitiga. Menggambar square, kita membutuhkan dua segitiga dengan tepi yang umum. Ini berarti bahwa koordinat segitiga akan:

segitiga 1: (-1, -1), (1, -1) dan (-1, 1)
segitiga 2: (1, -1), (-1, 1), dan (1, 1)

Membuat float array yang mewakili simpul tersebut.

1
private float vertices[] = {
2
        -1f, -1f,
3
        1f, -1f,
4
        -1f, 1f,
5
        1f, 1f,
6
};

Untuk peta tekstur ke alun-alun, Anda perlu menentukan koordinat Vertex tekstur. Tekstur mengikuti sistem koordinat di mana nilai yang meningkat koordinat y sebagai Anda pergi lebih tinggi. Membuat lain array yang mewakili Vertex tekstur.

1
private float textureVertices[] = {
2
        0f,1f,
3
        1f,1f,
4
        0f,0f,
5
        1f,0f
6
};

Langkah 2: Buat objek Buffer

Array koordinat perlu dikonversi menjadi byte buffer sebelum OpenGL dapat menggunakannya. Mari kita menyatakan buffer ini pertama.

1
private FloatBuffer verticesBuffer;
2
private FloatBuffer textureBuffer;

Menulis kode untuk menginisialisasi buffer ini dalam metode baru yang disebut initializeBuffers. Menggunakan ByteBuffer.allocateDirect metode untuk menciptakan buffer. Karena float menggunakan 4 byte, Anda perlu mengalikan ukuran array dengan nilai 4.

Selanjutnya, gunakan ByteBuffer.nativeOrder untuk menentukan urutan byte mendasarinya platform native, dan mengatur urutan buffer untuk nilai. Menggunakan metode asFloatBuffer untuk mengkonversi contoh ByteBuffer ke FloatBuffer. Setelah FloatBuffer dibuat, menggunakan put method untuk memuat array ke buffer. Akhirnya, menggunakan metode position untuk memastikan bahwa buffer membaca dari awal.

Isi dari metode initializeBuffers yang akan terlihat seperti ini:

1
private void initializeBuffers(){
2
    ByteBuffer buff = ByteBuffer.allocateDirect(vertices.length * 4);
3
    buff.order(ByteOrder.nativeOrder());
4
    verticesBuffer = buff.asFloatBuffer();
5
    verticesBuffer.put(vertices);
6
    verticesBuffer.position(0);
7
8
    buff = ByteBuffer.allocateDirect(textureVertices.length * 4);
9
    buff.order(ByteOrder.nativeOrder());
10
    textureBuffer = buff.asFloatBuffer();
11
    textureBuffer.put(textureVertices);
12
    textureBuffer.position(0);
13
}

Step 3: Membuat Shaders

Saatnya untuk menulis shaders Anda sendiri. Shaders hanyalah program C sederhana yang dijalankan oleh GPU untuk memproses vertex setiap individu. Untuk tutorial ini, Anda harus membuat dua shaders, vertex shader dan shader fragmen.

C kode untuk vertex shader adalah:

1
attribute vec4 aPosition; 
2
attribute vec2 aTexPosition; 
3
varying vec2 vTexPosition; 
4
void main() { 
5
  gl_Position = aPosition; 
6
  vTexPosition = aTexPosition; 
7
};

C kode untuk shader fragment adalah:

1
precision mediump float; 
2
uniform sampler2D uTexture; 
3
varying vec2 vTexPosition; 
4
void main() { 
5
  gl_FragColor = texture2D(uTexture, vTexPosition); 
6
};

Jika Anda sudah tahu OpenGL, kode ini harus akrab bagi Anda karena itu umum di semua platform. Jika Anda tidak, untuk memahami program ini Anda harus merujuk ke dokumentasi OpenGL. Berikut adalah penjelasan singkat untuk Anda mulai:

  • Vertex shader bertanggung jawab untuk menarik Vertex individu. aPosition adalah variabel yang akan terikat untuk FloatBuffer yang berisi koordinat Vertex. Demikian pula, aTexPosition adalah variabel yang akan terikat untuk FloatBuffer yang berisi koordinat tekstur. gl_Position adalah variabel OpenGL built-in dan mewakili posisi vertex masing-masing. vTexPosition adalah sebuah variabel yang varying, yang nilainya hanya diteruskan ke shader fragment.
  • Dalam tutorial ini, shader fragment bertanggung jawab untuk mewarnai square. Ini mengambil warna dari tekstur menggunakan metode texture2D dan menetapkan mereka fragment menggunakan variabel built-in yang bernama gl_FragColor.

Kode shader perlu diwakili sebagai objek String di kelas.

1
private final String vertexShaderCode =
2
        "attribute vec4 aPosition;" +
3
        "attribute vec2 aTexPosition;" +
4
        "varying vec2 vTexPosition;" +
5
        "void main() {" +
6
        "  gl_Position = aPosition;" +
7
        "  vTexPosition = aTexPosition;" +
8
        "}";
9
10
private final String fragmentShaderCode =
11
        "precision mediump float;" +
12
        "uniform sampler2D uTexture;" +
13
        "varying vec2 vTexPosition;" +
14
        "void main() {" +
15
        "  gl_FragColor = texture2D(uTexture, vTexPosition);" +
16
        "}";

Step 4: Membuat Program

Menciptakan metode baru yang disebut initializeProgram untuk membuat OpenGL program setelah kompilasi dan menghubungkan shaders.

Gunakan glCreateShader untuk membuat objek shader dan mengembalikan referensi ke itu dalam form int. Untuk membuat vertex shader, melewati nilai GL_VERTEX_SHADER untuk itu. Demikian pula, untuk membuat shader fragment, melewati nilai GL_FRAGMENT_SHADER untuk itu. Selanjutnya menggunakan glShaderSource untuk mengasosiasikan kode shader sesuai dengan shader. Gunakan glCompileShader untuk mengkompilasi kode shader.

Setelah kompilasi shaders kedua, membuat sebuah program baru yang menggunakan glCreateProgram. Seperti glCreateShader, ini juga mengembalikan int sebagai referensi untuk program. Memanggil glAttachShader untuk melampirkan shaders ke program. Akhirnya, hubungi glLinkProgram untuk menghubungkan program.

Metode Anda dan variabel yang terkait akan terlihat seperti ini:

1
private int vertexShader;
2
private int fragmentShader;
3
private int program;
4
5
private void initializeProgram(){
6
    vertexShader = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
7
    GLES20.glShaderSource(vertexShader, vertexShaderCode);
8
    GLES20.glCompileShader(vertexShader);
9
10
    fragmentShader = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);
11
    GLES20.glShaderSource(fragmentShader, fragmentShaderCode);
12
    GLES20.glCompileShader(fragmentShader);
13
14
    program = GLES20.glCreateProgram();
15
    GLES20.glAttachShader(program, vertexShader);
16
    GLES20.glAttachShader(program, fragmentShader);
17
18
    GLES20.glLinkProgram(program);
19
}

Anda mungkin telah memperhatikan bahwa OpenGL metode (metode diawali dengan gl) milik kelas GLES20. Hal ini karena kami menggunakan OpenGL ES 2.0. Jika Anda ingin menggunakan versi yang lebih tinggi, maka Anda akan harus menggunakan kelas-kelas GLES30 atau GLES31.

Langkah 5: Menggambar persegi

Buat metode baru yang disebut draw untuk benar-benar menarik alun-alun yang menggunakan dan Vertex shaders kita didefinisikan sebelumnya.

Berikut adalah apa yang perlu Anda lakukan dalam metode ini:

  1. Gunakan glBindFramebuffer untuk membuat objek penyangga bernama bingkai (sering disebut FBO).
  2. Gunakan glUseProgram untuk mulai menggunakan program kami hanya dihubungkan.
  3. Melewati nilai GL_BLEND glDisable untuk menonaktifkan pencampuran warna sementara render.
  4. Gunakan glGetAttribLocation untuk mendapatkan pegangan ke variabel aPosition dan aTexPosition yang disebutkan dalam kode shader vertex.
  5. Gunakan glGetUniformLocation untuk mendapatkan pegangan untuk uTexture konstan yang disebutkan dalam kode shader fragmen.
  6. Gunakan glVertexAttribPointer untuk mengasosiasikan pegangan aPosition dan aTexPosition dengan verticesBuffer dan textureBuffer masing-masing.
  7. Gunakan glBindTexture untuk mengikat tekstur (melewati sebagai argumen untuk metode draw) shader fragment.
  8. Hapus Isi GLSurfaceView yang menggunakan glClear.
  9. Akhirnya, menggunakan metode glDrawArrays untuk benar-benar menggambar dua segitiga (dan dengan demikian alun-alun).

Kode untuk metode draw akan terlihat seperti ini:

1
public void draw(int texture){
2
    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
3
    GLES20.glUseProgram(program);
4
    GLES20.glDisable(GLES20.GL_BLEND);
5
6
    int positionHandle = GLES20.glGetAttribLocation(program, "aPosition");
7
    int textureHandle = GLES20.glGetUniformLocation(program, "uTexture");
8
    int texturePositionHandle = GLES20.glGetAttribLocation(program, "aTexPosition");
9
10
    GLES20.glVertexAttribPointer(texturePositionHandle, 2, GLES20.GL_FLOAT, false, 0, textureBuffer);
11
    GLES20.glEnableVertexAttribArray(texturePositionHandle);
12
13
    GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
14
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture);
15
    GLES20.glUniform1i(textureHandle, 0);
16
17
    GLES20.glVertexAttribPointer(positionHandle, 2, GLES20.GL_FLOAT, false, 0, verticesBuffer);
18
    GLES20.glEnableVertexAttribArray(positionHandle);
19
20
    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
21
    GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
22
}

Tambahkan konstruktor kelas untuk menginisialisasi buffer dan program pada saat pembuatan objek.

1
public Square(){
2
    initializeBuffers();
3
    initializeProgram();
4
}

3. render OpenGL pesawat dan tekstur

Saat ini, kami renderer tidak apa-apa. Kita perlu mengubah hal itu sehingga ia dapat membuat pesawat yang kita buat di langkah sebelumnya.

Tapi pertama-tama, mari kita membuat Bitmap. Tambahkan foto ke folder res/drawable proyek Anda. Saya menggunakan file yang disebut forest.jpg. Gunakan BitmapFactory untuk mengkonversi foto ke sebuah objek Bitmap. Juga, menyimpan dimensi objek Bitmap variabel terpisah.

Mengubah konstruktor kelas EffectsRenderer sehingga memiliki isi sebagai berikut:

1
private Bitmap photo;
2
private int photoWidth, photoHeight;
3
public EffectsRenderer(Context context){
4
    super();
5
    photo = BitmapFactory.decodeResource(context.getResources(), R.drawable.forest);
6
    photoWidth = photo.getWidth();
7
    photoHeight = photo.getHeight();
8
}

Buar metode baru yang disebut generateSquare untuk mengkonversi bitmap menjadi sebuah tekstur dan menginisialisasi objek Square. Anda juga akan memerlukan serangkaian bilangan bulat untuk menahan referensi untuk tekstur OpenGL. Gunakan glGenTextures untuk menginisialisasi array dan glBindTexture untuk mengaktifkan tekstur di indeks 0.

Selanjutnya, gunakan glTexParameteri untuk mengatur berbagai properti yang memutuskan bagaimana tekstur dituliskan:

  • Set GL_TEXTURE_MIN_FILTER (fungsi minifying) dan GL_TEXTURE_MAG_FILTER (pembesar fungsi) untuk GL_LINEAR untuk memastikan bahwa tekstur tampak halus, bahkan ketika itu telah menggeliat atau menyusut.
  • Mengatur GL_TEXTURE_WRAP_S dan GL_TEXTURE_WRAP_T untuk GL_CLAMP_TO_EDGE sehingga tekstur tidak pernah terulang lagi.

Akhirnya, menggunakan texImage2D metode untuk memetakan Bitmap untuk tekstur. Pelaksanaan metode generateSquare yang akan terlihat seperti ini:

1
private int textures[] = new int[2];
2
private Square square;
3
4
private void generateSquare(){
5
    GLES20.glGenTextures(2, textures, 0);
6
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]);
7
8
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
9
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
10
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
11
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
12
13
    GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, photo, 0);
14
    square = new Square();
15
}

Setiap kali perubahan dimensi GLSurfaceView, metode onSurfaceChanged Renderer dipanggil. Berikut ini adalah dimana Anda harus memanggil glViewPort untuk menentukan dimensi baru viewport. Juga, panggil glClearColor untuk mewarnai menjadi hitam GLSurfaceView. Selanjutnya, hubungi generateSquare untuk reinitialize tekstur dan pesawat.

1
@Override
2
public void onSurfaceChanged(GL10 gl, int width, int height) {
3
    GLES20.glViewport(0,0,width, height);
4
    GLES20.glClearColor(0,0,0,1);
5
    generateSquare();
6
}

Akhirnya, memanggil metode draw objek Square di dalam metode onDrawFrame Renderer.

1
@Override
2
public void onDrawFrame(GL10 gl) {
3
    square.draw(textures[0]);
4
}

Anda sekarang dapat menjalankan aplikasi Anda dan lihat foto Anda telah memilih yang diterjemahkan sebagai tekstur OpenGL di pesawat.

4. menggunakan Media efek Framework

Kode yang kompleks yang kami menulis sampai sekarang adalah hanya sebuah prasyarat untuk menggunakan Media Effects framework.. Sekarang saatnya untuk mulai menggunakan framework itu sendiri. Menambahkan kolom berikut kelas Renderer Anda.

1
private EffectContext effectContext;
2
private Effect effect;

Menginisialisasi bidang effectContext dengan menggunakan EffectContext.createWithCurrentGlContext. Hal ini bertanggung jawab untuk mengelola informasi tentang efek visual dalam konteks OpenGL. Untuk mengoptimalkan kinerja, ini harus disebut hanya sekali. Tambahkan kode berikut pada permulaan metode onDrawFrame Anda.

1
if(effectContext==null) {
2
    effectContext = EffectContext.createWithCurrentGlContext();
3
}

Membuat efek sangat sederhana. Gunakan effectContext untuk membuat EffectFactory dan menggunakan EffectFactory untuk membuat objek Effect. Setelah Effect tersedia, Anda dapat memanggil apply dan pass referensi untuk tekstur aslinya, dalam kasus kami ini adalah textures[0], bersama dengan referensi ke objek kosong tekstur, dalam kasus kami ini adalah textures[1]. Setelah apply metode ini disebut, textures[1] akan berisi hasil dari Effect.

Misalnya, untuk membuat dan menerapkan efek grayscale, di sini 's kode Anda harus menulis:

1
private void grayScaleEffect(){
2
    EffectFactory factory = effectContext.getFactory();
3
    effect = factory.createEffect(EffectFactory.EFFECT_GRAYSCALE);
4
    effect.apply(textures[0], photoWidth, photoHeight, textures[1]);
5
}

Memanggil metode onDrawFrame dan pass textures[1] metode draw objek Square. Metode onDrawFrame Anda harus memiliki kode berikut:

1
@Override
2
public void onDrawFrame(GL10 gl) {
3
    if(effectContext==null) {
4
        effectContext = EffectContext.createWithCurrentGlContext();
5
    }
6
    if(effect!=null){
7
        effect.release();
8
    }
9
    grayScaleEffect();
10
    square.draw(textures[1]);
11
}

release metode digunakan untuk membebaskan semua resource yang diselenggarakan oleh Effect. Ketika Anda menjalankan aplikasi, Anda harus melihat hasil sebagai berikut:

Anda dapat menggunakan kode yang sama untuk menerapkan efek lainnya. Sebagai contoh, berikut adalah kode untuk menerapkan efek dokumenter:

1
private void documentaryEffect(){
2
    EffectFactory factory = effectContext.getFactory();
3
    effect = factory.createEffect(EffectFactory.EFFECT_DOCUMENTARY);
4
    effect.apply(textures[0], photoWidth, photoHeight, textures[1]);
5
}

Hasil terlihat seperti ini:

Beberapa efek mengambil parameter. Sebagai contoh, efek penyesuaian kecerahan memiliki parameter brightness yang mengambil nilai float. Anda dapat menggunakan setParameter untuk mengubah nilai parameter apapun. Kode berikut memperlihatkan bagaimana menggunakannya:

1
private void brightnessEffect(){
2
    EffectFactory factory = effectContext.getFactory();
3
    effect = factory.createEffect(EffectFactory.EFFECT_BRIGHTNESS);
4
    effect.setParameter("brightness", 2f);
5
    effect.apply(textures[0], photoWidth, photoHeight, textures[1]);
6
}

Efek akan membuat aplikasi Anda membuat hasil sebagai berikut:

Kesimpulan

Dalam tutorial ini, Anda telah belajar bagaimana untuk menggunakan Media Effects Framework  untuk menerapkan berbagai efek ke foto Anda. Meskipun demikian, Anda juga belajar cara menggambar pesawat menggunakan OpenGL ES 2.0 dan berlaku berbagai tekstur untuk itu.

Kerangka dapat diterapkan untuk foto dan video. Dalam video, Anda hanya perlu menerapkan efek ke frame individu video dalam metode onDrawFrame.

Anda telah melihat tiga efek dalam tutorial ini dan framework memiliki belasan lagi bagi Anda untuk bereksperimen dengan. Untuk mengetahui lebih banyak tentang mereka, merujuk pada website pengembang Android.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.