返回

点亮Android OpenGL ES

Android

OpenGL ES是一个跨平台的图形库,它提供了对图形硬件的访问,允许开发人员创建交互式的、硬件加速的2D和3D图形。Android从2.0开始支持OpenGL ES,并提供了许多API来帮助开发人员使用OpenGL ES。

在本教程中,我们将创建一个使用OpenGL ES绘制三角形的应用程序。我们将使用NDK来构建这个应用程序,因为NDK允许我们使用C/C++来编写Android应用程序。

首先,我们需要创建一个新的Android项目。可以在Android Studio中创建一个新的项目,并选择“Empty Activity”作为模板。

接下来,我们需要添加NDK支持。可以在项目的build.gradle文件中添加以下代码:

android {
    ...
    externalNativeBuild {
        ndkBuild {
            path "src/main/jni/Android.mk"
        }
    }
}

然后,我们需要创建一个新的JNI模块。可以在项目的src/main目录下创建一个名为jni的目录,并在其中创建一个名为Android.mk的文件。Android.mk文件的内容如下:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := native-lib
LOCAL_SRC_FILES := native-lib.cpp
LOCAL_LDLIBS := -llog

include $(BUILD_SHARED_LIBRARY)

接下来,我们需要创建一个名为native-lib.cpp的文件,并将其放在src/main/jni目录下。native-lib.cpp文件的内容如下:

#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <jni.h>

// Vertex shader code
const char *vertexShaderCode =
    "attribute vec4 vPosition;"
    "attribute vec2 vTexCoord;"
    "varying vec2 texCoord;"
    "void main() {"
    "  gl_Position = vPosition;"
    "  texCoord = vTexCoord;"
    "}";

// Fragment shader code
const char *fragmentShaderCode =
    "precision mediump float;"
    "uniform sampler2D tex;"
    "varying vec2 texCoord;"
    "void main() {"
    "  gl_FragColor = texture2D(tex, texCoord);"
    "}";

// Create a shader program
GLuint createShaderProgram() {
  GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
  glShaderSource(vertexShader, 1, &vertexShaderCode, NULL);
  glCompileShader(vertexShader);
  GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
  glShaderSource(fragmentShader, 1, &fragmentShaderCode, NULL);
  glCompileShader(fragmentShader);

  GLuint program = glCreateProgram();
  glAttachShader(program, vertexShader);
  glAttachShader(program, fragmentShader);
  glLinkProgram(program);

  GLint linkStatus;
  glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
  if (linkStatus != GL_TRUE) {
    GLchar infoLog[512];
    glGetProgramInfoLog(program, sizeof(infoLog), NULL, infoLog);
    printf("Error linking program: %s\n", infoLog);
    return 0;
  }

  return program;
}

// Create a texture from a raw image file
GLuint createTextureFromRawImage(const char *filename) {
  GLuint texture;
  glGenTextures(1, &texture);
  glBindTexture(GL_TEXTURE_2D, texture);

  FILE *file = fopen(filename, "rb");
  if (file == NULL) {
    printf("Error opening file: %s\n", filename);
    return 0;
  }

  fseek(file, 0, SEEK_END);
  long fileSize = ftell(file);
  fseek(file, 0, SEEK_SET);

  unsigned char *buffer = (unsigned char *)malloc(fileSize);
  fread(buffer, 1, fileSize, file);
  fclose(file);

  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 512, 512, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
  glGenerateMipmap(GL_TEXTURE_2D);

  free(buffer);

  return texture;
}

// Create a vertex buffer object
GLuint createVertexBufferObject(const GLfloat *vertices, GLsizeiptr size) {
  GLuint vertexBufferObject;
  glGenBuffers(1, &vertexBufferObject);
  glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObject);
  glBufferData(GL_ARRAY_BUFFER, size, vertices, GL_STATIC_DRAW);

  return vertexBufferObject;
}

// Create a vertex array object
GLuint createVertexArrayObject(GLuint vertexBufferObject) {
  GLuint vertexArrayObject;
  glGenVertexArrays(1, &vertexArrayObject);
  glBindVertexArray(vertexArrayObject);

  glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObject);

  // Position attribute
  glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid *)0);
  glEnableVertexAttribArray(0);

  // TexCoord attribute
  glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid *)(3 * sizeof(GLfloat)));
  glEnableVertexAttribArray(1);

  glBindVertexArray(0);

  return vertexArrayObject;
}

// Initialize the OpenGL ES context
void initOpenGL() {
  // Create a shader program
  GLuint program = createShaderProgram();
  glUseProgram(program);

  // Create a texture from a raw image file
  GLuint texture = createTextureFromRawImage("res/raw/image.raw");
  glActiveTexture(GL_TEXTURE0);
  glBindTexture(GL_TEXTURE_2D, texture);

  // Create a vertex buffer object
  GLfloat vertices[] = {
      // Position           // TexCoord
      -0.5f, -0.5f, 0.0f, 0.0f, 0.0f,  // Bottom left
      0.5f, -0.5f, 0.0f, 1.0f, 0.0f,  // Bottom right
      0.0f, 0.5f, 0.0f, 0.5f, 1.0f,  // Top
  };
  GLuint vertexBufferObject = createVertexBufferObject(vertices, sizeof(vertices));

  // Create a vertex array object
  GLuint vertexArrayObject = createVertexArrayObject(vertexBufferObject);

  // Set the clear color
  glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
}

// Render a frame
void renderFrame() {
  // Clear the color buffer
  glClear(GL_COLOR_BUFFER_BIT);

  // Bind the vertex array object
  glBindVertexArray(vertexArrayObject);

  // Draw the triangle
  glDrawArrays(GL_TRIANGLES, 0, 3);

  // Unbind the vertex array object
  glBindVertexArray(0);
}

// JNI function to initialize the OpenGL ES context
JNIEXPORT void JNICALL Java_com_example_opengles_MainActivity_initOpenGL(JNIEnv *env, jobject obj) {
  initOpenGL();
}

// JNI function to render a frame
JNIEXPORT void JNICALL Java_com_example_opengles_MainActivity_renderFrame(JNIEnv *env, jobject obj) {
  renderFrame();
}

接下来,我们需要在MainActivity.java文件中添加以下代码:

package com.example.opengles;

import android.app.Activity;
import android.graphics.SurfaceTexture;
import android.opengl.GLSurfaceView;
import android.os.Bundle;

public class MainActivity extends Activity implements SurfaceTexture.OnFrameAvailableListener {
    private GLSurfaceView glSurfaceView;
    private MyRenderer renderer;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        glSurfaceView = new GLSurfaceView(this);
        renderer = new MyRenderer();
        glSurfaceView.setEGLContextClientVersion(2);
        glSurfaceView.setRenderer(renderer);
        glSurfaceView.setOnFrameAvailableListener(this);

        setContentView(glSurfaceView);
    }

    @Override
    protected void onPause() {
        super.onPause();
        glSurfaceView.onPause();
    }

    @Override
    protected void onResume() {
        super.onResume();
        glSurfaceView.onResume