├── .gitignore
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── hzw
│ │ └── opengles
│ │ └── introduction
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── hzw
│ │ │ └── opengles
│ │ │ └── introduction
│ │ │ ├── GLImageHandler.java
│ │ │ ├── GLShowImageActivity.java
│ │ │ ├── MainActivity.java
│ │ │ └── util
│ │ │ └── OpenGlUtils.java
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable-xxhdpi
│ │ ├── thelittleprince.png
│ │ └── thelittleprince2.png
│ │ ├── drawable
│ │ └── ic_launcher_background.xml
│ │ ├── layout
│ │ ├── activity_01.xml
│ │ └── activity_main.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── values-v21
│ │ └── styles.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── hzw
│ └── opengles
│ └── introduction
│ └── ExampleUnitTest.java
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | # Gradle
2 | .gradle
3 | build
4 | local.properties
5 | reports
6 |
7 | # Maven
8 | target
9 | pom.xml.*
10 | release.properties
11 | gen-external-apklibs
12 |
13 | # Eclipse
14 | .classpath
15 | .project
16 | .settings
17 | eclipsebin
18 |
19 | # IntelliJ IDEA
20 | .idea
21 | *.iml
22 | *.ipl
23 | *.iws
24 | classes/
25 | idea-classes/
26 | coverage-error.log
27 |
28 | # Android
29 | gen
30 | bin
31 | project.properties
32 | out
33 |
34 | # Finder
35 | .DS_Store
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Android OpenGL ES 2.0
2 |
3 | # 从显示一张图片开始学习OpenGL ES
4 |
5 | 文章:[《从显示一张图片开始学习OpenGL ES》](https://blog.csdn.net/u012964944/article/details/82788620)
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 28
5 | defaultConfig {
6 | applicationId "com.hzw.opengles.introduction"
7 | minSdkVersion 15
8 | targetSdkVersion 28
9 | versionCode 1
10 | versionName "1.0"
11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | }
20 |
21 | dependencies {
22 | implementation fileTree(dir: 'libs', include: ['*.jar'])
23 | implementation 'com.android.support:appcompat-v7:28.0.0-rc02'
24 | implementation 'com.android.support.constraint:constraint-layout:1.1.3'
25 | testImplementation 'junit:junit:4.12'
26 | androidTestImplementation 'com.android.support.test:runner:1.0.2'
27 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
28 | }
29 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/hzw/opengles/introduction/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.hzw.opengles.introduction;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.hzw.opengles.introduction", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hzw/opengles/introduction/GLImageHandler.java:
--------------------------------------------------------------------------------
1 | package com.hzw.opengles.introduction;
2 |
3 | import android.opengl.GLES20;
4 |
5 | import com.hzw.opengles.introduction.util.OpenGlUtils;
6 |
7 | import java.nio.FloatBuffer;
8 | import java.util.LinkedList;
9 |
10 | /**
11 | * 负责显示一张图片
12 | */
13 | public class GLImageHandler {
14 | // 数据中有多少个顶点,管线就调用多少次顶点着色器
15 | public static final String NO_FILTER_VERTEX_SHADER = "" +
16 | "attribute vec4 position;\n" + // 顶点着色器的顶点坐标,由外部程序传入
17 | "attribute vec4 inputTextureCoordinate;\n" + // 传入的纹理坐标
18 | " \n" +
19 | "varying vec2 textureCoordinate;\n" +
20 | " \n" +
21 | "void main()\n" +
22 | "{\n" +
23 | " gl_Position = position;\n" +
24 | " textureCoordinate = inputTextureCoordinate.xy;\n" + // 最终顶点位置
25 | "}";
26 |
27 | // 光栅化后产生了多少个片段,就会插值计算出多少个varying变量,同时渲染管线就会调用多少次片段着色器
28 | public static final String NO_FILTER_FRAGMENT_SHADER = "" +
29 | "varying highp vec2 textureCoordinate;\n" + // 最终顶点位置,上面顶点着色器的varying变量会传递到这里
30 | " \n" +
31 | "uniform sampler2D inputImageTexture;\n" + // 外部传入的图片纹理 即代表整张图片的数据
32 | " \n" +
33 | "void main()\n" +
34 | "{\n" +
35 | " gl_FragColor = texture2D(inputImageTexture, textureCoordinate);\n" + // 调用函数 进行纹理贴图
36 | "}";
37 |
38 | private final LinkedList mRunOnDraw;
39 | private final String mVertexShader;
40 | private final String mFragmentShader;
41 | protected int mGLProgId;
42 | protected int mGLAttribPosition;
43 | protected int mGLUniformTexture;
44 | protected int mGLAttribTextureCoordinate;
45 |
46 | public GLImageHandler() {
47 | this(NO_FILTER_VERTEX_SHADER, NO_FILTER_FRAGMENT_SHADER);
48 | }
49 |
50 | public GLImageHandler(final String vertexShader, final String fragmentShader) {
51 | mRunOnDraw = new LinkedList();
52 | mVertexShader = vertexShader;
53 | mFragmentShader = fragmentShader;
54 | }
55 |
56 | public final void init() {
57 | mGLProgId = OpenGlUtils.loadProgram(mVertexShader, mFragmentShader); // 编译链接着色器,创建着色器程序
58 | mGLAttribPosition = GLES20.glGetAttribLocation(mGLProgId, "position"); // 顶点着色器的顶点坐标
59 | mGLUniformTexture = GLES20.glGetUniformLocation(mGLProgId, "inputImageTexture"); // 传入的图片纹理
60 | mGLAttribTextureCoordinate = GLES20.glGetAttribLocation(mGLProgId, "inputTextureCoordinate"); // 顶点着色器的纹理坐标
61 | }
62 |
63 | public void onDraw(final int textureId, final FloatBuffer cubeBuffer,
64 | final FloatBuffer textureBuffer) {
65 | GLES20.glUseProgram(mGLProgId);
66 | // 顶点着色器的顶点坐标
67 | cubeBuffer.position(0);
68 | GLES20.glVertexAttribPointer(mGLAttribPosition, 2, GLES20.GL_FLOAT, false, 0, cubeBuffer);
69 | GLES20.glEnableVertexAttribArray(mGLAttribPosition);
70 | // 顶点着色器的纹理坐标
71 | textureBuffer.position(0);
72 | GLES20.glVertexAttribPointer(mGLAttribTextureCoordinate, 2, GLES20.GL_FLOAT, false, 0, textureBuffer);
73 | GLES20.glEnableVertexAttribArray(mGLAttribTextureCoordinate);
74 | // 传入的图片纹理
75 | if (textureId != OpenGlUtils.NO_TEXTURE) {
76 | GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
77 | GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
78 | GLES20.glUniform1i(mGLUniformTexture, 0);
79 | }
80 |
81 | // 绘制顶点 ,方式有顶点法和索引法
82 | // GLES20.GL_TRIANGLE_STRIP即每相邻三个顶点组成一个三角形,为一系列相接三角形构成
83 | GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); // 顶点法,按照传入渲染管线的顶点顺序及采用的绘制方式将顶点组成图元进行绘制
84 |
85 | GLES20.glDisableVertexAttribArray(mGLAttribPosition);
86 | GLES20.glDisableVertexAttribArray(mGLAttribTextureCoordinate);
87 | GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hzw/opengles/introduction/GLShowImageActivity.java:
--------------------------------------------------------------------------------
1 | package com.hzw.opengles.introduction;
2 |
3 | import android.app.Activity;
4 | import android.graphics.Bitmap;
5 | import android.graphics.BitmapFactory;
6 | import android.opengl.GLES20;
7 | import android.opengl.GLSurfaceView;
8 | import android.os.Bundle;
9 | import android.support.annotation.Nullable;
10 |
11 | import com.hzw.opengles.introduction.util.OpenGlUtils;
12 |
13 | import java.nio.ByteBuffer;
14 | import java.nio.ByteOrder;
15 | import java.nio.FloatBuffer;
16 |
17 | import javax.microedition.khronos.egl.EGLConfig;
18 | import javax.microedition.khronos.opengles.GL10;
19 |
20 | public class GLShowImageActivity extends Activity {
21 | // 绘制图片的原理:定义一组矩形区域的顶点,然后根据纹理坐标把图片作为纹理贴在该矩形区域内。
22 |
23 | // 原始的矩形区域的顶点坐标,因为后面使用了顶点法绘制顶点,所以不用定义绘制顶点的索引。无论窗口的大小为多少,在OpenGL二维坐标系中都是为下面表示的矩形区域
24 | static final float CUBE[] = { // 窗口中心为OpenGL二维坐标系的原点(0,0)
25 | -1.0f, -1.0f, // v1
26 | 1.0f, -1.0f, // v2
27 | -1.0f, 1.0f, // v3
28 | 1.0f, 1.0f, // v4
29 | };
30 | // 纹理也有坐标系,称UV坐标,或者ST坐标。UV坐标定义为左上角(0,0),右下角(1,1),一张图片无论大小为多少,在UV坐标系中都是图片左上角为(0,0),右下角(1,1)
31 | // 纹理坐标,每个坐标的纹理采样对应上面顶点坐标。
32 | public static final float TEXTURE_NO_ROTATION[] = {
33 | 0.0f, 1.0f, // v1
34 | 1.0f, 1.0f, // v2
35 | 0.0f, 0.0f, // v3
36 | 1.0f, 0.0f, // v4
37 | };
38 |
39 | private GLSurfaceView mGLSurfaceView;
40 | private int mGLTextureId = OpenGlUtils.NO_TEXTURE; // 纹理id
41 | private GLImageHandler mGLImageHandler = new GLImageHandler();
42 |
43 | private FloatBuffer mGLCubeBuffer;
44 | private FloatBuffer mGLTextureBuffer;
45 | private int mOutputWidth, mOutputHeight; // 窗口大小
46 | private int mImageWidth, mImageHeight; // bitmap图片实际大小
47 |
48 | @Override
49 | protected void onCreate(@Nullable Bundle savedInstanceState) {
50 | super.onCreate(savedInstanceState);
51 | setContentView(R.layout.activity_01);
52 | mGLSurfaceView = findViewById(R.id.gl_surfaceview);
53 | mGLSurfaceView.setEGLContextClientVersion(2); // 创建OpenGL ES 2.0 的上下文环境
54 |
55 | mGLSurfaceView.setRenderer(new MyRender());
56 | mGLSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); // 手动刷新
57 | }
58 |
59 | private class MyRender implements GLSurfaceView.Renderer {
60 |
61 | @Override
62 | public void onSurfaceCreated(GL10 gl, EGLConfig config) {
63 | GLES20.glClearColor(0, 0, 0, 1);
64 | GLES20.glDisable(GLES20.GL_DEPTH_TEST); // 当我们需要绘制透明图片时,就需要关闭它
65 | mGLImageHandler.init();
66 |
67 | // 需要显示的图片
68 | Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.thelittleprince);
69 | mImageWidth = bitmap.getWidth();
70 | mImageHeight = bitmap.getHeight();
71 | // 把图片数据加载进GPU,生成对应的纹理id
72 | mGLTextureId = OpenGlUtils.loadTexture(bitmap, mGLTextureId, true); // 加载纹理
73 |
74 | // 顶点数组缓冲器
75 | mGLCubeBuffer = ByteBuffer.allocateDirect(CUBE.length * 4)
76 | .order(ByteOrder.nativeOrder())
77 | .asFloatBuffer();
78 | mGLCubeBuffer.put(CUBE).position(0);
79 |
80 | // 纹理数组缓冲器
81 | mGLTextureBuffer = ByteBuffer.allocateDirect(TEXTURE_NO_ROTATION.length * 4)
82 | .order(ByteOrder.nativeOrder())
83 | .asFloatBuffer();
84 | mGLTextureBuffer.put(TEXTURE_NO_ROTATION).position(0);
85 | }
86 |
87 | @Override
88 | public void onSurfaceChanged(GL10 gl, int width, int height) {
89 | mOutputWidth = width;
90 | mOutputHeight = height;
91 | GLES20.glViewport(0, 0, width, height); // 设置窗口大小
92 | adjustImageScaling(); // 调整图片显示大小。如果不调用该方法,则会导致图片整个拉伸到填充窗口显示区域
93 | }
94 |
95 | @Override
96 | public void onDrawFrame(GL10 gl) { // 绘制
97 | GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
98 | // 根据纹理id,顶点和纹理坐标数据绘制图片
99 | mGLImageHandler.onDraw(mGLTextureId, mGLCubeBuffer, mGLTextureBuffer);
100 | }
101 |
102 | // 调整图片显示大小为居中显示
103 | private void adjustImageScaling() {
104 | float outputWidth = mOutputWidth;
105 | float outputHeight = mOutputHeight;
106 |
107 | float ratio1 = outputWidth / mImageWidth;
108 | float ratio2 = outputHeight / mImageHeight;
109 | float ratioMax = Math.min(ratio1, ratio2);
110 | // 居中后图片显示的大小
111 | int imageWidthNew = Math.round(mImageWidth * ratioMax);
112 | int imageHeightNew = Math.round(mImageHeight * ratioMax);
113 |
114 | // 图片被拉伸的比例
115 | float ratioWidth = outputWidth / imageWidthNew;
116 | float ratioHeight = outputHeight / imageHeightNew;
117 | // 根据拉伸比例还原顶点
118 | float[] cube = new float[]{
119 | CUBE[0] / ratioWidth, CUBE[1] / ratioHeight,
120 | CUBE[2] / ratioWidth, CUBE[3] / ratioHeight,
121 | CUBE[4] / ratioWidth, CUBE[5] / ratioHeight,
122 | CUBE[6] / ratioWidth, CUBE[7] / ratioHeight,
123 | };
124 |
125 | mGLCubeBuffer.clear();
126 | mGLCubeBuffer.put(cube).position(0);
127 | }
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hzw/opengles/introduction/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.hzw.opengles.introduction;
2 |
3 | import android.app.ListActivity;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import android.view.View;
7 | import android.widget.ListView;
8 | import android.widget.SimpleAdapter;
9 |
10 | import java.util.ArrayList;
11 | import java.util.HashMap;
12 | import java.util.List;
13 | import java.util.Map;
14 |
15 | public class MainActivity extends ListActivity {
16 |
17 | public static final String TITLE = "title";
18 | public static final String SUBTITLE = "subtitle";
19 |
20 | @Override
21 | protected void onCreate(Bundle savedInstanceState) {
22 | super.onCreate(savedInstanceState);
23 |
24 | setListAdapter(new SimpleAdapter(this, createData(),
25 | android.R.layout.simple_list_item_2, new String[]{TITLE,
26 | SUBTITLE}, new int[]{android.R.id.text1,
27 | android.R.id.text2}));
28 | }
29 |
30 | @Override
31 | protected void onListItemClick(ListView l, View v, int position, long id) {
32 | if (position == 0) {
33 | startActivity(new Intent(getApplicationContext(), GLShowImageActivity.class));
34 |
35 | } else if (position == 1) {
36 |
37 |
38 | }
39 | }
40 |
41 | private List