Last active
November 4, 2025 09:01
-
-
Save sunmeat/e81b39ff4af3783368e3c582308d87ab to your computer and use it in GitHub Desktop.
android opengl minimal code
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| MainActivity.java: | |
| package site.sunmeat.opengles3d; | |
| import android.app.Activity; | |
| import android.opengl.GLSurfaceView; | |
| import android.os.Bundle; | |
| import android.opengl.GLES20; | |
| import android.opengl.Matrix; | |
| import javax.microedition.khronos.egl.EGLConfig; | |
| import javax.microedition.khronos.opengles.GL10; | |
| import java.nio.ByteBuffer; | |
| import java.nio.ByteOrder; | |
| import java.nio.FloatBuffer; | |
| import android.util.Log; | |
| class TriangleRenderer implements GLSurfaceView.Renderer { | |
| private static final String TAG = "TriangleRenderer"; | |
| private FloatBuffer vertexBuffer; | |
| private FloatBuffer colorBuffer; | |
| private int program; | |
| private int positionHandle; | |
| private int colorHandle; | |
| private int mvpMatrixHandle; | |
| private final float[] mvpMatrix = new float[16]; | |
| private final float[] scratch = new float[16]; | |
| private final long startTime = System.currentTimeMillis(); | |
| // координати та кольори | |
| private final float[] triangleCoords = { | |
| 0.0f, 0.933f, 0.0f, | |
| -0.75f, -0.466f, 0.0f, | |
| 0.75f, -0.466f, 0.0f | |
| }; | |
| private final float[] colors = { | |
| 1.0f, 0.0f, 0.0f, 1.0f, | |
| 0.0f, 1.0f, 0.0f, 1.0f, | |
| 0.0f, 0.0f, 1.0f, 1.0f | |
| }; | |
| private float speedX, speedY, speedZ; // випадкові швидкості в град/сек | |
| @Override | |
| public void onSurfaceCreated(GL10 unused, EGLConfig config) { | |
| // встановлення кольору очищення екрану | |
| GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); | |
| // відключення тесту глибини | |
| GLES20.glDisable(GLES20.GL_DEPTH_TEST); | |
| // ініціалізація буферів | |
| ByteBuffer bb = ByteBuffer.allocateDirect(triangleCoords.length * 4); | |
| bb.order(ByteOrder.nativeOrder()); | |
| vertexBuffer = bb.asFloatBuffer(); | |
| vertexBuffer.put(triangleCoords); | |
| vertexBuffer.position(0); | |
| bb = ByteBuffer.allocateDirect(colors.length * 4); | |
| bb.order(ByteOrder.nativeOrder()); | |
| colorBuffer = bb.asFloatBuffer(); | |
| colorBuffer.put(colors); | |
| colorBuffer.position(0); | |
| String vertexShaderCode = "uniform mat4 uMVPMatrix;" + | |
| "attribute vec4 vPosition;" + | |
| "attribute vec4 vColor;" + | |
| "varying vec4 vFragmentColor;" + | |
| "void main() {" + | |
| " gl_Position = uMVPMatrix * vPosition;" + | |
| " vFragmentColor = vColor;" + | |
| "}"; | |
| // компіляція вершинного шейдера | |
| int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode); | |
| if (vertexShader == 0) { | |
| Log.e(TAG, "Vertex shader compilation failed"); | |
| return; | |
| } | |
| String fragmentShaderCode = "precision mediump float;" + | |
| "varying vec4 vFragmentColor;" + | |
| "void main() {" + | |
| " gl_FragColor = vFragmentColor;" + | |
| "}"; | |
| // компіляція фрагментного шейдера | |
| int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode); | |
| if (fragmentShader == 0) { | |
| Log.e(TAG, "Fragment shader compilation failed"); | |
| return; | |
| } | |
| // створення програми шейдерів | |
| program = GLES20.glCreateProgram(); | |
| if (program == 0) { | |
| Log.e(TAG, "Failed to create program"); | |
| return; | |
| } | |
| GLES20.glAttachShader(program, vertexShader); | |
| GLES20.glAttachShader(program, fragmentShader); | |
| GLES20.glLinkProgram(program); | |
| // перевірка лінкування програми | |
| int[] linkStatus = new int[1]; | |
| GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0); | |
| if (linkStatus[0] != GLES20.GL_TRUE) { | |
| Log.e(TAG, "Лінкування програми не вдалося: " + GLES20.glGetProgramInfoLog(program)); | |
| GLES20.glDeleteProgram(program); | |
| program = 0; | |
| return; | |
| } | |
| // отримання хендлів атрибутів | |
| positionHandle = GLES20.glGetAttribLocation(program, "vPosition"); | |
| colorHandle = GLES20.glGetAttribLocation(program, "vColor"); | |
| mvpMatrixHandle = GLES20.glGetUniformLocation(program, "uMVPMatrix"); | |
| // логування помилок хендлів | |
| if (positionHandle == -1) Log.e(TAG, "Не вдалося отримати: vPosition"); | |
| if (colorHandle == -1) Log.e(TAG, "Не вдалося отримати: vColor"); | |
| if (mvpMatrixHandle == -1) Log.e(TAG, "Не вдалося отримати: uMVPMatrix"); | |
| // видалення шейдерів | |
| GLES20.glDeleteShader(vertexShader); | |
| GLES20.glDeleteShader(fragmentShader); | |
| // ініціалізація випадкових швидкостей (від -180 до +180 град/сек) | |
| speedX = (float) (Math.random() * 360 - 180); | |
| speedY = (float) (Math.random() * 360 - 180); | |
| speedZ = (float) (Math.random() * 360 - 180); | |
| } | |
| @Override | |
| public void onDrawFrame(GL10 unused) { | |
| // перевірка валідності програми | |
| if (program == 0) { | |
| Log.e(TAG, "Шось не працює, малювання зупинено"); | |
| return; | |
| } | |
| // очищення буфера кольорів | |
| GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); | |
| // активація програми | |
| GLES20.glUseProgram(program); | |
| // активація атрибутів вершин | |
| GLES20.glEnableVertexAttribArray(positionHandle); | |
| GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 12, vertexBuffer); | |
| GLES20.glEnableVertexAttribArray(colorHandle); | |
| GLES20.glVertexAttribPointer(colorHandle, 4, GLES20.GL_FLOAT, false, 16, colorBuffer); | |
| // матриці проекції та виду (без змін) | |
| float[] projectionMatrix = new float[16]; | |
| float[] viewMatrix = new float[16]; | |
| Matrix.orthoM(projectionMatrix, 0, -1, 1, -1, 1, -5, 5); | |
| Matrix.setLookAtM(viewMatrix, 0, 0, 0, 3, 0f, 0f, 0f, 0f, 1, 0); | |
| // модельна матриця | |
| float[] modelMatrix = new float[16]; | |
| Matrix.setIdentityM(modelMatrix, 0); | |
| // випадкове обертання навколо x, y, z | |
| long currentTime = System.currentTimeMillis(); | |
| // обчислення часу в секундах | |
| float deltaTime = (currentTime - startTime) / 1000.0f; | |
| // обчислення кутів обертання | |
| float angleX = (speedX * deltaTime) % 360.0f; | |
| float angleY = (speedY * deltaTime) % 360.0f; | |
| float angleZ = (speedZ * deltaTime) % 360.0f; | |
| // застосування обертань (порядок: спочатку z, потім y, потім x — стандарт для евлерових кутів) | |
| Matrix.rotateM(modelMatrix, 0, angleX, 1, 0, 0); // обертання навколо x | |
| Matrix.rotateM(modelMatrix, 0, angleY, 0, 1, 0); // обертання навколо y | |
| Matrix.rotateM(modelMatrix, 0, angleZ, 0, 0, 1); // обертання навколо z | |
| // комбінована матриця mvp | |
| Matrix.multiplyMM(scratch, 0, projectionMatrix, 0, viewMatrix, 0); | |
| Matrix.multiplyMM(mvpMatrix, 0, scratch, 0, modelMatrix, 0); | |
| // передача матриці в шейдер | |
| GLES20.glUniformMatrix4fv(mvpMatrixHandle, 1, false, mvpMatrix, 0); | |
| // малювання трикутника | |
| GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3); | |
| // деактивація атрибутів | |
| GLES20.glDisableVertexAttribArray(positionHandle); | |
| GLES20.glDisableVertexAttribArray(colorHandle); | |
| } | |
| @Override | |
| public void onSurfaceChanged(GL10 unused, int width, int height) { | |
| // налаштування viewport | |
| GLES20.glViewport(0, 0, width, height); | |
| } | |
| // завантаження та компіляція шейдера | |
| private int loadShader(int type, String shaderCode) { | |
| int shader = GLES20.glCreateShader(type); | |
| if (shader != 0) { | |
| GLES20.glShaderSource(shader, shaderCode); | |
| GLES20.glCompileShader(shader); | |
| int[] compileStatus = new int[1]; | |
| GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compileStatus, 0); | |
| // перевірка статусу компіляції | |
| if (compileStatus[0] == 0) { | |
| Log.e(TAG, "Компляція шейдерів невдала: " + GLES20.glGetShaderInfoLog(shader)); | |
| GLES20.glDeleteShader(shader); | |
| return 0; | |
| } | |
| } else { | |
| Log.e(TAG, "Не вдалося створити шейдер"); | |
| } | |
| return shader; | |
| } | |
| } | |
| public class MainActivity extends Activity { | |
| private GLSurfaceView glSurfaceView; | |
| @Override | |
| protected void onCreate(Bundle savedInstanceState) { | |
| super.onCreate(savedInstanceState); | |
| setContentView(R.layout.activity_main); | |
| glSurfaceView = findViewById(R.id.gl_surface_view); | |
| // використання OpenGL ES 2.0 | |
| // https://www.khronos.org/opengles/ | |
| glSurfaceView.setEGLContextClientVersion(2); | |
| // встановлення рендерера | |
| glSurfaceView.setRenderer(new TriangleRenderer()); | |
| // безперервний рендеринг для анімації | |
| glSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); | |
| } | |
| @Override | |
| protected void onPause() { | |
| super.onPause(); | |
| // пауза surface view | |
| glSurfaceView.onPause(); | |
| } | |
| @Override | |
| protected void onResume() { | |
| super.onResume(); | |
| // відновлення surface view | |
| glSurfaceView.onResume(); | |
| } | |
| } | |
| ============================================================================================== | |
| activity_main.xml: | |
| <?xml version="1.0" encoding="utf-8"?> | |
| <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | |
| android:layout_width="match_parent" | |
| android:layout_height="match_parent" | |
| android:orientation="vertical"> | |
| <android.opengl.GLSurfaceView | |
| android:id="@+id/gl_surface_view" | |
| android:layout_width="match_parent" | |
| android:layout_height="match_parent" /> | |
| </LinearLayout> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment