Android OpenGL ES2.0基础(二、画个三角形)
2017-03-10 10:29
267 查看
一、概要
上节中了解了OpenGLES最简单的使用OpenGLES2.0基础,这节在深入一个层次了解怎么去画一个三角形。对于OpenGL绘图,最主要的就是Renderer的处理。因为GLSurfaceView相当于一个View,只是显示图像,而Renderer就是GLSurfaceView要显示的图像。在Renderer中主要有三个函数onSurfaceCreated(),onSurfaceCreated()和onDrawFrame()。其中前两个函数主要用于初始化逻辑,绘图主要是在onDrawFrame()方法里面。二、相关要点
1.绘制方式
OpenGLES图元的绘制方式有GL_POINTS 、GL_LINES、 GL_LINE_LOOP 、GL_LINE_STRIP、GL_TRIANGLES 、GL_TRIANGLE_STRIP 、GL_TRIANGLE_FAN 这几种,每个都有自己独特的作用。在使用时根据自己的需求选择不同的绘制方式进行绘制。绘制方式 | 解释 |
---|---|
GL_POINTS | 点 |
GL_LINES | 线段 |
GL_LINE_STRIP | 多段线 |
GL_LINE_LOOP | 线圈 |
GL_TRIANGLES | 三角形 |
GL_TRIANGLE_STRIP | 三角形条带 |
GL_TRIANGLE_FAN | 三角形扇 |
2. 索引法 :GLES20.glDrawElements(GLES20.GL_POINTS, iCount, GLES20.GL_UNSIGNED_BYTE, mIndexBuffer);
(1).GL_LINES(绘制直线)
以(v0,v1),(v2,v3),(v4,v5)的形式绘制直线。(当绘制的点只有两个时,GL_LINES与GL_LINE_STRIP绘线方式没有差异)GLES20.glDrawArrays(GLES20.GL_LINES, 0, count);
(2).GL_LINE_STRIP(绘制直线)
以(v0,v1),(v1,v2),(v2,v3),(v3,v4),(v4,v5)的形式绘制直线。GL_LINE_LOOP以(v0,v1),(v1,v2),(v2,v3),(v3,v4),(v4,v5),(v5,v0)的形式绘制直线,比GL_LINE_STRIP多一条(v5,v0)的直线。GLES20.glDrawArrays(GLES20.GL_LINE_STRIP, 0, count);
(3).GL_POINTS(绘制点)
GLES20.glDrawArrays(GLES20.GL_POINTS, 0, count);
(4).GL_TRIANGLES(绘制三角形)
每三个顶之间绘制三角形,之间不连接。GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, count);
(5).GL_TRIANGLE_STRIP(绘制三角形)
它是由最少3个点构成(正好3个点就是一个GL_TRIANGLES)每增加1个点,新增的点会和之前已有的两个点构成新的三角形,依次类推。GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, count);
(6).GL_TRIANGLE_FAN(绘制三角形)
以(v0,v1,v2),(v0,v2,v3),(v0,v3,v4)的形式绘制三角形。GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, count);
2.OpenGL矩阵变换
通过模拟人眼观察物体, 可能引起屏幕图像改变的几种行为和分别对应的几种OpenGL变换,每种变换都可以由相应的Matrix静态方法生成相应的变换矩阵。 坐标系原点在屏幕中央,z轴正方向垂直屏幕向外。行为 | 变换 | 记录变换方式的放法 | 记录变换方式的[4*4]矩阵 |
---|---|---|---|
眼睛相对物体的位置改变 | 视图变换 | Matrix.setLookAtM(mVMatrix,0(偏移量),cx(相机位置x), cy, cz,tx(观察点位置x), ty, tz,upx(顶部朝向x), upy, upz) | mVMatrix[16] |
物体平移 | 模型变换 | Matrix.translateM(mMMatrix,0(偏移量),x(平移量x), y, z) | mMMatrix[16] |
物体按坐标缩放比例缩放 | 模型变换 | Matrix.scaleM(mMMatrix,sx(缩放因子),sy, sz) | mMMatrix[16] |
物体旋转 | 模型变换 | Matrix.rotateM(mMMatrix,0(偏移量),angle(旋转角度),x(需要旋转的轴), y, z) | mMMatrix[16] |
凸透镜眼睛 | 投影变换 | Matrix.frustumM(mPMatrix,0(偏移量),left,right,bottom,top,near,far//near>0) | mPMatrix[16] |
平面透镜眼睛 | 投影变换 | Matrix.orthoM(mPMatrix,0(偏移量),left,right,bottom,top,near,far//near>0) | mPMatrix[16] |
2. 变换顺序: 矩阵相乘是讲究顺序的, 比如先平移再缩放 和 先缩放再平移的效果是不同的。
三、绘制流程
定义物体顶点信息编辑着色器代码
编译连接着色器程序
物体顶点信息传入着色器
GLSurfaceView加载Renderer
Activity加载GLSurfaceView
渲染物体输出屏幕
1.定义物体顶点信息
物体的顶点信息一般包括:(1)三维坐标信息; (2)三维法向量信息;(3)颜色信息或二维贴图坐标信息;复杂物体的坐标通常通过特定的软件绘制生成顶点信息文件,然后进行解读;2.编辑着色器代码
着色器语言编写的代码, 通常后缀名.sh, 语法和C相似95%以上, 是OpenGLES2.0的可编程渲染通道, 增加了编程难度, 但是能实现更绚丽多彩的效果。一般编写好的着色器代码放在android工程目录下的assets文件, assets常用轻量资源的读写。3.编译连接着色器
从assets获取着色器脚本, 并返回脚本字符串的过程
// 加载顶点着色器的脚本内容 mVertexShader = ShaderUtil.loadFromAssetsFile("graph_color_and_texture_vertex.shl",mContext.getResources()); // 加载片元着色器的脚本内容 mFragmentShader = ShaderUtil.loadFromAssetsFile("graph_color_and_texture_frag.shl",mContext.getResources());
加载片元着色器和顶点着色器
// 加载顶点着色器 int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource); if (vertexShader == 0) { return 0; } // 加载片元着色器 int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource); if (pixelShader == 0) { return 0; } // 创建程序 int program = GLES20.glCreateProgram(); // 若程序创建成功则向程序中加入顶点着色器与片元着色器 if (program != 0) { // 向程序中加入顶点着色器 GLES20.glAttachShader(program, vertexShader); checkGlError("glAttachShader"); // 向程序中加入片元着色器 GLES20.glAttachShader(program, pixelShader); checkGlError("glAttachShader"); // 链接程序 GLES20.glLinkProgram(program); // 存放链接成功program数量的数组 int[] linkStatus = new int[1]; // 获取program的链接情况 GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0); // 若链接失败则报错并删除程序 if (linkStatus[0] != GLES20.GL_TRUE) { Log.e("ES20_ERROR", "Could not link program: "); Log.e("ES20_ERROR", GLES20.glGetProgramInfoLog(program)); GLES20.glDeleteProgram(program); program = 0; } }
4. 物体顶点信息传入着色器
生成顶点信息, 用FloatBuffer类存储
ByteBuffer bbv = ByteBuffer.allocateDirect(triangleCoords.length * 4);//一个float 4个字节 bbv.order(ByteOrder.nativeOrder()); mVertexBuffer = bbv.asFloatBuffer(); mVertexBuffer.put(triangleCoords);//存入信息 mVertexBuffer.position(0);//设置起始位置为0;
获取着色器参数ID
maPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
传入数据
GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false, 3 * 4, mVertexBuffer);
使用数据
GLES20.glEnableVertexAttribArray(maPositionHandle);
5. GLSurfaceView加载Renderer
初始化GLSurfaceView
//设置版本 setEGLContextClientVersion(2); //创建渲染器实例 mRenderer = new MyRenderer(); // 设置渲染器 setRenderer(mRenderer); //设置渲染模式 setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
初始化Renderer
/** * 仅调用一次,用于设置view的OpenGL ES环境 */ @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { //设置背景色 GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); triangle = new Triangle(); } /** * 当view的几何形状发生变化时调用,比如设备从竖屏变为横屏 */ @Override public void onSurfaceChanged(GL10 gl, int width, int height) { GLES20.glViewport(0, 0, width, height); float ratio = (float) width / height; Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7); } /** * 每次重绘view时调用 */ @Override public void onDrawFrame(GL10 gl) { float[] scratch = new float[16]; // 绘制背景 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f); Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0); Matrix.setRotateM(mRotationMatrix, 0, mAngle, 0, 0, 1.0f); Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0); // 重绘背景色 triangle.draw(scratch); }
7. 渲染物体输出屏幕
//编译OpenGL ES shader和链接program ProgramFactory.initFactory(getApplicationContext()); mySurfaceView = new MySurfaceView(this); setContentView(mySurfaceView);
四、运行效果
五、全部代码
1.在Manifests中添加
<uses-feature android:glEsVersion="0x00020000" android:required="true" /> <supports-gl-texture android:name="GL_OES_compressed_ETC1_RGB8_texture" /> <supports-gl-texture android:name="GL_OES_compressed_paletted_texture" />
2.MyGLSurfaceView继承自SurfaceView
import android.content.Context; import android.opengl.GLSurfaceView; import android.view.MotionEvent; /** * Created by Administrator on 2017/3/8. * description : */ public class MyGLSurfaceView extends GLSurfaceView { MyRenderer mRenderer; public MyGLSurfaceView(Context context) { super(context); init(); } private void init(){ // 创建一个OpenGL ES 2.0 context setEGLContextClientVersion(2); //创建渲染器实例 mRenderer = new MyRenderer(); // 设置渲染器 setRenderer(mRenderer); //设置渲染模式 setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); } private final float TOUCH_SCALE_FACTOR = 180.0f / 320; private float mPreviousX; private float mPreviousY; @Override public boolean onTouchEvent(MotionEvent e) { float x = e.getX(); float y = e.getY(); switch (e.getAction()) { case MotionEvent.ACTION_MOVE: float dx = x - mPreviousX; float dy = y - mPreviousY; if (y > getHeight() / 2) { dx = dx * -1 ; } if (x < getWidth() / 2) { dy = dy * -1 ; } mRenderer.setAngle(mRenderer.getAngle() +((dx + dy) * TOUCH_SCALE_FACTOR)); requestRender(); } mPreviousX = x; mPreviousY = y; return true; } }
3.MyRenderer实现了GLSurfaceView.Renderer接口
import android.opengl.GLES20; import android.opengl.GLSurfaceView; import android.opengl.Matrix; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; /** * Created by Administrator on 2017/3/8. * description : */ public class MyRenderer implements GLSurfaceView.Renderer { Triangle triangle; private final float[] mMVPMatrix = new float[16]; private final float[] mProjectionMatrix = new float[16]; private final float[] mViewMatrix = new float[16]; private final float[] mRotationMatrix = new float[16]; private float mAngle; /** * 仅调用一次,用于设置view的OpenGL ES环境 */ @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { //设置背景色 GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); triangle = new Triangle(); } /** * 当view的几何形状发生变化时调用,比如设备从竖屏变为横屏 */ @Override public void onSurfaceChanged(GL10 gl, int width, int height) { GLES20.glViewport(0, 0, width, height); float ratio = (float) width / height; Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7); } /** * 每次重绘view时调用 */ @Override public void onDrawFrame(GL10 gl) { float[] scratch = new float[16]; // 绘制背景 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f); Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0); Matrix.setRotateM(mRotationMatrix, 0, mAngle, 0, 0, 1.0f); Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0); // 重绘背景色 triangle.draw(scratch); } public void setAngle(float angle) { this.mAngle = angle; } public float getAngle() { return mAngle; } }
3.Triangle主要处理画三角形逻辑
import android.opengl.GLES20; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; /** * Created by Administrator on 2017/3/8. * description : */ public class Triangle { // GL 绘图 private FloatBuffer mVertexBuffer; // 顶点坐标数据缓冲 private int mProgram; // 自定义渲染管线程序id private int mMVPMatrixHandle;// 总变换矩阵引用id private int maPositionHandle; // 顶点位置属性引用id private int maColorHandle; // 顶点颜色属性引用id // 设置每个顶点的坐标数 static final int COORDS_PER_VERTEX = 3; // 设置三角形顶点数组 static float triangleCoords[] = { // 默认按逆时针方向顺序绘制 0.0f, 0.622008459f, 0.0f, // 顶 -0.5f, -0.311004243f, 0.0f, // 左底 0.5f, -0.311004243f, 0.0f // 右底 }; private final int vertexCount = triangleCoords.length / COORDS_PER_VERTEX; private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex // 设置图形的RGB值和透明度 static float color[] = {0.63671875f, 0.76953125f, 0.22265625f, 1.0f}; public Triangle() { initVertexData(); initShader(); } // 初始化顶点坐标与着色数据的方法 public void initVertexData() { // 如果数据只有一个的时候会出错,mVertexBuffer和mColorBuffer容错10 ByteBuffer bbv = ByteBuffer.allocateDirect(triangleCoords.length * 4);//一个float 4个字节 bbv.order(ByteOrder.nativeOrder());///前期设置 mVertexBuffer = bbv.asFloatBuffer();//作为中介开辟FloatBuffer mVertexBuffer.put(triangleCoords); mVertexBuffer.position(0); } // 初始化着色器 public void initShader() { // 加载顶点着色器的脚本内容 mProgram = ProgramFactory.shared().getProgram(); // 获取程序中顶点位置属性引用id maPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition"); // 获取程序中顶点颜色属性引用id maColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor"); // 获取程序中总变换矩阵引用id mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix"); } public void draw(float[] mvpMatrix) { // 制定使用某套shader程序 GLES20.glUseProgram(mProgram); // 允许顶点位置数据数组 GLES20.glEnableVertexAttribArray(maPositionHandle); // 为画笔指定顶点位置数据 GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false, vertexStride, mVertexBuffer); // GLES20.glUniform4fv(maColorHandle, 1, color, 0); // mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix"); // GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0); // GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount); // GLES20.glDisableVertexAttribArray(maPositionHandle); } }
4.在MainActivity中使用
import android.os.Bundle; import android.support.v7.app.AppCompatActivity; /** * Created by Administrator on 2017/3/8. * description : */ public class MainActivity extends AppCompatActivity { MyGLSurfaceView mySurfaceView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //编译OpenGL ES shader和链接program ProgramFactory.initFactory(getApplicationContext()); mySurfaceView = new MyGLSurfaceView(this); setContentView(mySurfaceView); } @Override protected void onPause() { super.onPause(); mySurfaceView.onPause(); } @Override protected void onResume() { super.onResume(); mySurfaceView.onResume(); } }
5.以及在assets目录下的两个着色器脚本
graph_color_and_texture_frag.shl
precision mediump float; uniform vec4 vColor; void main() { gl_FragColor = vColor; }
graph_color_and_texture_vertex.shl
uniform mat4 uMVPMatrix; attribute vec4 vPosition; void main() { gl_Position = uMVPMatrix * vPosition; }
6.下面贴上两个OpenGL工具类
ProgramFactory
import android.content.Context; /** * Created by Administrator on 2017/3/8. * description : */ public class ProgramFactory { private static Context mContext = null; String mVertexShader; String mFragmentShader; int mMixProgram; public static void initFactory(Context context){ mContext = context; } private ProgramFactory() { // 加载顶点着色器的脚本内容 mVertexShader = ShaderUtil.loadFromAssetsFile("graph_color_and_texture_vertex.shl", mContext.getResources()); // 加载片元着色器的脚本内容 mFragmentShader = ShaderUtil.loadFromAssetsFile("graph_color_and_texture_frag.shl", mContext.getResources()); // 基于顶点着色器与片元着色器创建程序 mMixProgram = ShaderUtil.createProgram(mVertexShader, mFragmentShader); } private static ProgramFactory mInstance; public static ProgramFactory shared() { if(mContext == null){ throw new IllegalStateException("ProgramFactory.initFactory() method is not transfer : mContext = null"); } if (mInstance == null) { mInstance = new ProgramFactory(); } return mInstance; } public static void recycle() { mInstance = null; } public int getProgram() { return mMixProgram; } }
ShaderUtil
import android.content.res.Resources; import android.opengl.GLES20; import android.util.Log; import java.io.ByteArrayOutputStream; import java.io.InputStream; /** * Created by Administrator on 2017/3/8. * description :加载顶点Shader与片元Shader的工具类 */ public class ShaderUtil { /** * 加载制定shader的方法 * * @param shaderType * shader的类型 GLES20.GL_VERTEX_SHADER GLES20.GL_FRAGMENT_SHADER * @param source * shader的脚本字符串 * @return */ public static int loadShader(int shaderType, String source) { // 创建一个新shader int shader = GLES20.glCreateShader(shaderType); // 若创建成功则加载shader if (shader != 0) { if (source != null) { // 加载shader的源代码 GLES20.glShaderSource(shader, source); } // 编译shader GLES20.glCompileShader(shader); // 存放编译成功shader数量的数组 int[] compiled = new int[1]; // 获取Shader的编译情况 GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0); // 若编译失败则显示错误日志并删除此shader if (compiled[0] == 0) { // Log.e("ES20_ERROR", "Could not compile shader " + shaderType + ":"); // Log.e("ES20_ERROR", GLES20.glGetShaderInfoLog(shader)); GLES20.glDeleteShader(shader); shader = 0; } } return shader; } // 创建shader程序的方法 public static int createProgram(String vertexSource, String fragmentSource) { // 加载顶点着色器 int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource); if (vertexShader == 0) { return 0; } // 加载片元着色器 int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource); if (pixelShader == 0) { return 0; } // 创建程序 int program = GLES20.glCreateProgram(); // 若程序创建成功则向程序中加入顶点着色器与片元着色器 if (program != 0) { // 向程序中加入顶点着色器 GLES20.glAttachShader(program, vertexShader); checkGlError("glAttachShader"); // 向程序中加入片元着色器 GLES20.glAttachShader(program, pixelShader); checkGlError("glAttachShader"); // 链接程序 GLES20.glLinkProgram(program); // 存放链接成功program数量的数组 int[] linkStatus = new int[1]; // 获取program的链接情况 GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0); // 若链接失败则报错并删除程序 if (linkStatus[0] != GLES20.GL_TRUE) { Log.e("ES20_ERROR", "Could not link program: "); Log.e("ES20_ERROR", GLES20.glGetProgramInfoLog(program)); GLES20.glDeleteProgram(program); program = 0; } } return program; } // 检查每一步操作是否有错误的方法 public static void checkGlError(String op) { int error; while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) { // Log.e("ES20_ERROR", op + ": glError " + error); throw new RuntimeException(op + ": glError " + error); } } // 从sh脚本中加载shader内容的方法 public static String loadFromAssetsFile(String fname, Resources r) { String result = null; try { InputStream in = r.getAssets().open(fname); int ch = 0; ByteArrayOutputStream baos = new ByteArrayOutputStream(); while ((ch = in.read()) != -1) { baos.write(ch); } byte[] buff = baos.toByteArray(); baos.close(); in.close(); result = new String(buff, "UTF-8"); result = result.replaceAll("\\r\\n", "\n"); } catch (Exception e) { e.printStackTrace(); } return result; } }
以上即为使用OpenGLES20画三角形的全部代码
相关链接:
源码下载
Shader相关介绍
matrix详解
相关文章推荐
- Android中使用opengl es2.0基础(2)- 三角形的绘制
- Android OpenGL ES 2.0绘制简单三角形
- Android OpenGL ES2.0基础(一、最简单的使用)
- Android openGL ES 2.0入门--画三角形、正方形
- [OpenGL]从零开始写一个Android平台下的全景视频播放器——1.1 OpenGL ES 2.0基础知识
- android 使用OPENGL ES实现三角形纹理贴图效果-纹理映射基础
- Android中使用opengl es2.0基础(1)
- 【Android 应用开发】OpenGL ES 2.0 -- 制作 3D 彩色旋转三角形 - 顶点着色器 片元着色器 使用详解
- [OpenGL]从零开始写一个Android平台下的全景视频播放器——1.2 用OpenGL ES 2.0画一个三角形
- Android中使用opengl es2.0基础(3)-正方体绘制
- Android OpenGL ES 2.0 (一) 画三角形
- Android OpenGL ES2.0基础(三、Shader相关介绍)
- OpenGL ES2.0入门之Android篇(一)——绘制三角形
- 【Android 应用开发】OpenGL ES 2.0 -- 制作 3D 彩色旋转三角形 - 顶点着色器 片元着色器 使用详解
- Android OpenGL ES 基础:绘制三角形
- OpenGL ES2.0入门之Android篇(一)——绘制三角形
- 在android 的openGL ES 2.0中怎么向shader中传struct
- Android OpenGL ES 2.0 (五) 添加材质
- android opengl es实例画三角形-native C实现
- android opengl es 2.0里surfaceview背景透明