OpenGL Android 安卓 入门 GLES20 初学者 初级 官方


前言:
本篇仅为个人学习记录,是我看了一遍官方文档的总结,并非专业教程,它的第一作用是我在以后运用OpenGL的过程中能快捷地查看基础用法,即好记性不如翻文章,第二作用是能帮助完全不了解OpenGL的人快速入门,即用一个小时时间达到我看了一周官方文档的OpenGL的了解程度,之后进阶再看其他文章就更容易上手。
提示:
看代码是最好连同注释一起看了,方便理解,官方文档原版注释,我怕我翻译变味就不翻了。

参考:官方文档

一.在Manifest.xml中加入OpenGL清单声明

首先新建一个项目,在Manifest.xml中加入这句,用啥版本的就写啥版本的。


	<!-- Tell the system this app requires OpenGL ES 2.0. -->
    <uses-feature android:glEsVersion="0x00020000" android:required="true" />
<!--        <uses-feature android:glEsVersion="0x00030000" android:required="true" />-->
<!--        <uses-feature android:glEsVersion="0x00030001" android:required="true" />-->
    

版本适配

  • 2.0 ——> Android 2.2(API 级别 8)开始可用
  • 3.0 ——> Android 4.3(API 级别 18)开始可用
  • 3.1 ——> Android 5.0(API 级别 21)开始可用

二.继承并实例化 GLSurfaceViewGLSurfaceView.Renderer

自定义MyGLSurfaceView继承GLSurfaceView


import android.content.Context;
import android.opengl.GLSurfaceView;

public class MyGLSurfaceView extends GLSurfaceView {

    private final MyGLRenderer renderer;

    public MyGLSurfaceView(Context context){
        super(context);

        // Create an OpenGL ES 2.0 context
        setEGLContextClientVersion(2);

        renderer = new MyGLRenderer();

        // Set the Renderer for drawing on the GLSurfaceView
        setRenderer(renderer);
        
		// Render the view only when there is a change in the drawing data
		//setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
    }
}

  • setEGLContextClientVersion(version):通知默认的EGLContextFactory和默认的EGLConfigChooser,选择哪个EGLContext客户端版本。注意:1)必须用在 setRenderer() 之前;2)应该设置Manifest.xml里标明的版本。
  • setRenderMode(renderMode):默认模式是‘持续渲染’
    RENDERMODE_CONTINUOUSLY顾名思义就不停的绘制;‘指示渲染’ RENDERMODE_WHEN_DIRTY是只在surface被创建时或者调用 requestRender() 时才会绘制,可以省电,提高系统性能,能让gpu和cpu休息,好处多多,注意该方法要在 setRenderer() 之后调用。

自定义MyGLRenderer实现GLSurfaceView.Renderer


import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.opengl.GLSurfaceView;

public class MyGLRenderer implements GLSurfaceView.Renderer {

    public void onSurfaceCreated(GL10 unused, EGLConfig config) {}

    public void onDrawFrame(GL10 unused) {}

    public void onSurfaceChanged(GL10 unused, int width, int height) {}
}

这里要明白这三个重写的方法的意义:

  • onSurfaceCreated(GL10 unused, EGLConfig config):在SurfaceView创建时候调用一次,一般只调用一次,在这里面,我们一般用来做一些初始化操作,比如实例图形类;
  • onDrawFrame(GL10 unused):这个简单,绘制,相当于 Viewdraw()
  • onSurfaceChanged(GL10 unused, int width, int height):当 GLSurfaceView 的大小发生改变时会调用(例如屏幕翻转、代码改变View的大小),在最开始创建时会调用一次。

MainActivity.class 中实例MyGLSurfaceView


public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        MyGLSurfaceView mglsv = new MyGLSurfaceView(this);

        ConstraintLayout.LayoutParams lp = new ConstraintLayout.LayoutParams(ConstraintLayout.LayoutParams.MATCH_PARENT, ConstraintLayout.LayoutParams.MATCH_PARENT);
        addContentView(mglsv,lp);
    }
}

这时,你的界面就是这样的:
空SurfaceView

三.(屏幕 - 视图)比例适配

首先来说说为什么要比例适配,一句话:因为OpenGL 假定屏幕采用均匀的方形坐标系,即它认为你的屏幕是正方形的,看图(左边未适配,右边已适配):
比例适配对比图
在MyGLRenderer创建投影和相机视图矩阵:

    private final float[] vPMatrix= new float[16];
    private final float[] projMatrix= new float[16];
    private final float[] vMatrix= new float[16];
    
	public void onSurfaceCreated(GL10 unused, EGLConfig config) {}
	
    public void onSurfaceChanged(GL10 unused, int width, int height) {
        GLES20.glViewport(0, 0, width, height);

        float ratio = (float) width / height;

        // create a projection matrix from device screen geometry
        Matrix.frustumM(projMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
    }

	public void onDrawFrame(GL10 unused) {
        ...
        // Create a camera view matrix
        Matrix.setLookAtM(vMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
        // Combine the projection and camera view matrices
        Matrix.multiplyMM(vPMatrix, 0, projMatrix, 0, vMatrix, 0);

        // Draw objects
        ...
	}
  • glViewport(x, y, width, height) :设置画布的位置和宽高。x、y 以像素为单位,指定了画布的左下角位置。width、height 表示这个画布矩形的宽度和高度。画布

  • Matrix.multiplyMM(result, resultOffset, lhs, lhsOffset, rhs, rhsOffset):将两个矩阵合并(即相乘) 。result为目标矩阵(就是装合并之后的容器矩阵)、lhs和rhs是用来结合的两个矩阵。

  • glFrustumM(m, offset, left, Right, bottom, top, near, far):根据参数创建一个透视投影矩阵。除了near、far(视景体剪裁距离)其他的我也没懂怎么用,大家基本是固定的-ratio,ratio,-1,1。
    视景体

  • Matrix.setLookAtM(rm, rmOffset, eyeX, eyeY, eyeZ, centerX, centerY,centerZ, upX, upY, upZ):设置相机位置,各个参数顾名思义就行了,最后三个参数表示(0,0,0)→(upX,upY,upZ)的方向为相机正上方的方向。

其他的就不解释了,注释写的很清楚了。

现在,你的GLSurfaceView就能以正确的比例显示了,不过你的界面还是这样:
空SurfaceView

四.使用 OpenGL ES 显示图形

1.绘制背景

自定义类实现 GLSurfaceview.Renderer


      public void onSurfaceCreated(GL10 unused, EGLConfig config) {
          // Set the background frame color
          GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
          ···
      }

      public void onDrawFrame(GL10 unused) {
          // Redraw background color
          GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
          ···
      }
      
  • glClearColor(red,green,blue,alpha)
    glClear(mask):改变背景颜色。前者设置好清除颜色,后者利用前一个函数设置好的当前清除颜色设置窗口颜色。

2.创建图形类

三角形:


public class Triangle {

        private FloatBuffer vertexBuffer;

        // number of coordinates per vertex in this array
        static final int COORDS_PER_VERTEX = 3;
        static float triangleCoords[] = {   // in counterclockwise order:
                 0.0f,  0.622008459f, 0.0f, // top
                -0.5f, -0.311004243f, 0.0f, // bottom left
                 0.5f, -0.311004243f, 0.0f  // bottom right
        };

        // Set color with red, green, blue and alpha (opacity) values
        float color[] = { 1.0f, 0f, 0f, 1.0f };

        public Triangle() {
            // initialize vertex byte buffer for shape coordinates
            ByteBuffer bb = ByteBuffer.allocateDirect(
                    // (number of coordinate values * 4 bytes per float)
                    triangleCoords.length * 4);
            // use the device hardware's native byte order
            bb.order(ByteOrder.nativeOrder());

            // create a floating point buffer from the ByteBuffer
            vertexBuffer = bb.asFloatBuffer();
            // add the coordinates to the FloatBuffer
            vertexBuffer.put(triangleCoords);
            // set the buffer to read the first coordinate
            vertexBuffer.position(0);
        }
    }
    

要绘制三角形,您必须先定义其坐标。在 OpenGL 中,执行此操作的典型方式是为坐标定义浮点数的顶点数组。为了最大限度地提高工作效率,您可以将这些坐标写入 ByteBuffer 中,它会传递到 OpenGL ES 图形管道进行处理。(官方文档的原话,下面是其他文章找来的解释)

  • OpenGL并不是对堆里面的数据进行操作,而是在直接内存中(Direct Memory),即操作的数据需要保存到NIO里面的Buffer对象中。而我们上面声明的float[]对象保存在堆中,因此,需要我们将float[]对象转为java.nio.Buffer对象
  • OpenGL在底层的实现是C语言,与Java默认的数据存储字节顺序可能不同,即大端小端问题。因此,为了保险起见,在将数据传递给OpenGL之前,我们需要指明使用本机的存储顺序,即bb.order(ByteOrder.nativeOrder())

注意:形状的坐标是按照逆时针顺序定义的。绘制顺序非常重要,因为它定义了哪一边是形状的正面(您通常想要绘制的那一面),哪一边是背面(您可以使用 OpenGL ES 面剔除功能选择不绘制的那一面)。

正方形:


public class Square {

        private FloatBuffer vertexBuffer;
        private ShortBuffer drawListBuffer;

        // number of coordinates per vertex in this array
        static final int COORDS_PER_VERTEX = 3;
        static float[] squareCoords = {
                -0.5f,  0.5f, 0.0f,   // top left
                -0.5f, -0.5f, 0.0f,   // bottom left
                 0.5f, -0.5f, 0.0f,   // bottom right
                 0.5f,  0.5f, 0.0f }; // top right

		//绘制顺序:0,1,2形成一个三角形,0,2,3形成一个三角形
        private short[] drawOrder = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices

        public Square() {
            // initialize vertex byte buffer for shape coordinates
            ByteBuffer bb = ByteBuffer.allocateDirect(
            // (# of coordinate values * 4 bytes per float)
                    squareCoords.length * 4);
            bb.order(ByteOrder.nativeOrder());
            vertexBuffer = bb.asFloatBuffer();
            vertexBuffer.put(squareCoords);
            vertexBuffer.position(0);

            // initialize byte buffer for the draw list
            ByteBuffer dlb = ByteBuffer.allocateDirect(
            // (# of coordinate values * 2 bytes per short)
                    drawOrder.length * 2);
            dlb.order(ByteOrder.nativeOrder());
            drawListBuffer = dlb.asShortBuffer();
            drawListBuffer.put(drawOrder);
            drawListBuffer.position(0);
        }
    }
    

在 OpenGL 中定义三角形非常简单,如果希望定义复杂一点的图形比如方形,有多种方式可以执行此操作,但在 OpenGL ES 中绘制此类形状的典型方式是使用两个绘制在一起的三角形:

同样,对于表示该形状的两个三角形,您应按逆时针顺序定义顶点,并将这些值放入 ByteBuffer 中。为了避免两次定义这两个三角形共用的两个坐标,请使用绘制列表告知 OpenGL ES 图形管道如何绘制这些顶点。

完善三角形图形类:

public class Triangle {

	···
    private final String vertexShaderCode =
            // This matrix member variable provides a hook to manipulate
            // the coordinates of the objects that use this vertex shader
            "uniform mat4 vPMatrix;" +
            "attribute vec4 vPosition;" +
            "void main() {" +
            // the matrix must be included as a modifier of gl_Position
            // Note that the uMVPMatrix factor *must be first* in order
            // for the matrix multiplication product to be correct.
            "  gl_Position = uMVPMatrix * vPosition;" +
            "}";

   	private final String fragmentShaderCode =
           "precision mediump float;" +
           "uniform vec4 vColor;" +
           "void main() {" +
           "  gl_FragColor = vColor;" +
           "}";

	private final int mProgram;
	private int positionHandle;
    private int colorHandle;

    private final int vertexCount = triangleCoords.length // COORDS_PER_VERTEX;
    private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
    
	public Triangle() {
        ···

	    int vertexShader = MyGLRenderer.loadShader(GLES20.GL_VERTEX_SHADER,
	                                    vertexShaderCode);
	    int fragmentShader = MyGLRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER,
	                                    fragmentShaderCode);
	
	    // create empty OpenGL ES Program
	    mProgram = GLES20.glCreateProgram();
	
	    // add the vertex shader to program
	    GLES20.glAttachShader(mProgram, vertexShader);
	
	    // add the fragment shader to program
	    GLES20.glAttachShader(mProgram, fragmentShader);
	
	    // creates OpenGL ES program executables
	    GLES20.glLinkProgram(mProgram);
	}
	
	public void draw(float[] vPMatrix) {
        // Add program to OpenGL ES environment
        GLES20.glUseProgram(mProgram);

        // get handle to vertex shader's vPosition member
        positionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");

        // Enable a handle to the triangle vertices
        GLES20.glEnableVertexAttribArray(positionHandle);

        // Prepare the triangle coordinate data
        GLES20.glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX,
                                     GLES20.GL_FLOAT, false,
                                     vertexStride, vertexBuffer);

        // get handle to fragment shader's vColor member
        colorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");

        // Set color for drawing the triangle
        GLES20.glUniform4fv(colorHandle, 1, color, 0);

		// get handle to fragment shader's vPMatrix menber
        mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");

        // Apply the combined projection and camera view transformations
        GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);

        // Draw the triangle
        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);

        // Disable vertex array
        GLES20.glDisableVertexAttribArray(positionHandle);
    }
}

上面,在自定义 Triangle 的构造方法中,完成了实例化program和shader并将它们关联。在 draw() 中为program各个组件设置数据并绘制。program和shader的解释如下:

顶点着色程序:用于渲染形状的顶点的 OpenGL ES 图形代码。
片段着色程序:用于使用颜色或纹理渲染形状面的 OpenGL ES 代码。
程序:包含您希望用于绘制一个或多个形状的着色程序的 OpenGL ES 对象。

您至少需要一个顶点着色程序绘制形状,以及一个 Fragment 着色程序为该形状着色。您还必须对这些着色程序进行编译,然后将其添加到之后用于绘制形状的 OpenGL ES 程序中(这些都是官方文档的原话)。

  • GLES20.glGetUniformLocation(program, name):获取着色器程序中,指定为uniform类型变量的id。
  • GLES20.glGetAttribLocation(program,name):获取着色器程序中,指定为attribute类型变量的id。
  • GLES20.glDrawArrays(mode, first, count)GLES20.glDrawElements(mode,count,type,indices) :绘制。后者可以传入绘制顺序buffer,便于重复利用顶点。

如果你要绘制上面的方形,就要这样写

	GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length, GLES20.GL_UNSIGNED_SHORT, dlb);

MyGLRenderer 类中添加 loadShader() 方法,顾名思义就是加载着色器的静态方法:


public class MyGLRenderer implements GLSurfaceView.Renderer {

	···
	public static int loadShader(int type, String shaderCode){

      	// create a vertex shader type (GLES20.GL_VERTEX_SHADER)
       // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
       int shader = GLES20.glCreateShader(type);

       // add the source code to the shader and compile it
       GLES20.glShaderSource(shader, shaderCode);
       GLES20.glCompileShader(shader);

       return shader;
   }
}

其他方法看注释就很清楚了。完善了Triangle后,直接在RendereronDrawFrame() 中调用triangle.draw() 就能显示图形了:


	Triangle triangle;
	
	@Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
    	···
		triangle = new Triangle();
	}
	
	public void onDrawFrame(GL10 unused) {
        ...
        // Draw objects
        triangle.draw(vPMatrix);
	}
	

三角形

五.添加动画、触摸反馈

1.添加动画

修改MyGLRenderer类:


	private float[] rotationMatrix = new float[16];
	
    @Override
    public void onDrawFrame(GL10 gl) {
        float[] scratch = new float[16];

        ...

        // Create a rotation transformation for the triangle
        long time = SystemClock.uptimeMillis() % 4000L;
        float angle = 0.090f * ((int) time);
        Matrix.setRotateM(rotationMatrix, 0, angle, 0, 0, -1.0f);

        // Combine the rotation matrix with the projection and camera view
        // Note that the vPMatrix factor *must be first* in order
        // for the matrix multiplication product to be correct.
        Matrix.multiplyMM(scratch, 0, vPMatrix, 0, rotationMatrix, 0);

        // Draw triangle
        mTriangle.draw(scratch);
    }
    

上面代码看注释也很清楚了,在把‘投影×机视图(矩阵)’传递给三角形的渲染程序之前,先‘×旋转(矩阵)’。

Matrix.setRotateM() 等操作矩阵的方法请移步其他专业教程,看不看也行,顾名思义用起来就懂了。

现在你的程序是这样的:
在这里插入图片描述

2.响应触摸事件

MyGLSurfaceView 设置监听事件并设置渲染模式为‘指示渲染’以提高性能、添加触摸事件:

public class OneGlSurfaceView extends GLSurfaceView {

	···
	private final float TOUCH_SCALE_FACTOR = .5f;
    private float previousX;
    private float previousY;
    ···

    public MyGLSurfaceView(Context context) {
        ...
        // Render the view only when there is a change in the drawing data
        setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
    }
    
    @Override
    public boolean onTouchEvent(MotionEvent e) {
        // MotionEvent reports input details from the touch screen
        // and other input controls. In this case, you are only
        // interested in events where the touch position changed.

        float x = e.getX();
        float y = e.getY();

        switch (e.getAction()) {
            case MotionEvent.ACTION_MOVE:

                float dx = x - previousX;
                float dy = y - previousY;

                // reverse direction of rotation above the mid-line
                if (y > getHeight() / 2) {
                  dx = dx * -1 ;
                }

                // reverse direction of rotation to left of the mid-line
                if (x < getWidth() / 2) {
                  dy = dy * -1 ;
                }

                renderer.setAngle(
                        renderer.getAngle() +
                        ((dx + dy) * TOUCH_SCALE_FACTOR));
                requestRender();
        }

        previousX = x;
        previousY = y;
        return true;
    }
    

MyGLRenderer 类中修改相应的方法:


public class MyGLRenderer implements GLSurfaceView.Renderer {
     
     ...
     public volatile float mAngle;

     public float getAngle() {
         return mAngle;
     }

     public void setAngle(float angle) {
         mAngle = angle;
     }
     
	public void onDrawFrame(GL10 gl) {
        ...
        float[] scratch = new float[16];

        // Create a rotation for the triangle
        // long time = SystemClock.uptimeMillis() % 4000L;
        // float angle = 0.090f * ((int) time);
        Matrix.setRotateM(rotationMatrix, 0, mAngle, 0, 0, -1.0f);

        // Combine the rotation matrix with the projection and camera view
        // Note that the vPMatrix factor *must be first* in order
        // for the matrix multiplication product to be correct.
        Matrix.multiplyMM(scratch, 0, vPMatrix, 0, rotationMatrix, 0);

        // Draw triangle
        mTriangle.draw(scratch);
    }
}

不多说直接上效果图:
滑动三角形旋转
好了,以上就是官方文档中的全部内容了。

六.展示2D图片

完善Square

public class Square {

    private static String vertexShaderCode = "uniform mat4 uMVPMatrix;" +
            "attribute vec4 vPosition;" +
            "attribute vec2 a_texCoord;" +
            "varying vec2 v_texCoord;" +
            "void main() {" +
            "  gl_Position = uMVPMatrix * vPosition;" +
            "  v_texCoord = a_texCoord;" +
            "}";

    private static String fragmentShaderCode =  "precision mediump float;" +
            "varying vec2 v_texCoord;" +
            "uniform sampler2D s_texture;" +
            "void main() {" +
            "  gl_FragColor = texture2D(s_texture, v_texCoord);" +
            "}";
	
	//各种buffer
    private FloatBuffer vertexBuffer;
    private FloatBuffer textureBuffer;
    private ShortBuffer drawListBuffer;
	
	//表示数组中每两个数组成一个坐标
	private static int COORDS_PER_TEXTURE = 2;
	//纹理坐标,x和y都∈[0,1]
    static float textureCoords[] = {
    		//画个纹理坐标系:
            0f, 0f,//左上	(0,0)O·—— —— —— ——x(1,0)
            0f, 1f,//左下		   \	纹理
            1f, 1f,//右下			\	 图片				
            1f, 0f //右上		(1,1)\y			 ·(1,1)
    };
    
    Bitmap bitmap;

    int mProgram, mPositionHandle, mTexCoordHandle, mMatrixHandle,mTexSamplerHandle;

    public Square(Context context) {
		···
		//将float[]转换成FloatBuffer,同顶点坐标一样
        textureBuffer = ByteBuffer.allocateDirect(textureCoords.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
        textureBuffer.put(textureCoords);
        textureBuffer.position(0);
		
		//获取图片
        bitmap = BitmapFactory.decodeResource(context.getResources(), R.raw.beauty);

        //数据转换
        int vertexShader = OneGlRenderer.loadShader(GLES20.GL_VERTEX_SHADER,vertexShaderCode);
        int fragmentShader = OneGlRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);

        // 创建空的OpenGL ES程序
        mProgram = GLES20.glCreateProgram();

        // 添加顶点着色器到程序中
        GLES20.glAttachShader(mProgram, vertexShader);

        // 添加片段着色器到程序中
        GLES20.glAttachShader(mProgram, fragmentShader);

        // 创建OpenGL ES程序可执行文件
        GLES20.glLinkProgram(mProgram);

		//用起来
        GLES20.glUseProgram(mProgram);

		//获取渲染程序中各个属性的hooker
        mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
        mTexCoordHandle = GLES20.glGetAttribLocation(mProgram, "a_texCoord");
        mMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
        mTexSamplerHandle = GLES20.glGetUniformLocation(mProgram, "s_texture");

		//顶点坐标数据传入到渲染程序对应的位置
        GLES20.glEnableVertexAttribArray(mPositionHandle);
        GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, 12, vertexBuffer);

		//纹理坐标数据传入到渲染程序对应的位置
        GLES20.glEnableVertexAttribArray(mTexCoordHandle);
        GLES20.glVertexAttribPointer(mTexCoordHandle, COORDS_PER_TEXTURE, GLES20.GL_FLOAT, false, 8, textureBuffer);

		//创建并绑定纹理
        int[] textures = new int[1];
        GLES20.glGenTextures(textures.length, textures, 0);
        int textureId = textures[0];
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
        GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
        bitmap.recycle();
    }

    public void draw(float[] mvpMatrix) {

        GLES20.glUniformMatrix4fv(mMatrixHandle, 1, false, mvpMatrix, 0);

        GLES20.glUniform1i(mTexSamplerHandle, 0);

        GLES20.glDrawElements(GLES20.GL_TRIANGLES,drawOrder.length,GLES20.GL_UNSIGNED_SHORT,drawListBuffer);
    }
}

修改MyGLRenderer类:


	Context context;
	Square square;
	
	public OneGlRenderer(Context context) {
        this.context = context;
    }
    
    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {	
    	···
		square= new Square(context);
	}
	
	public void onDrawFrame(GL10 unused) {
        ...
        // Draw objects
//        triangle.draw(vPMatrix);
		square.draw(vPMatrix);
	}
	

现在你的应用就是这样的(图片是百度上下的,正方形):
2D纹理展示

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值