Android Opengl es学习笔记

1. 一般的opengl绘制流程

1. 初始化EGL

EGL是opengl的渲染环境(状态机模型环境),在使用opengl之前需要先初始化EGL环境

package com.example.gldemo;

import android.view.Surface;

import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.egl.EGLSurface;
import javax.microedition.khronos.opengles.GL;

public class EGLHelper {
    //仿照源码 里面的EglHelper 写  具体看 GLSurfaceView 里面 从start()方法 开始
    private EGL10 mEgl;
    private EGLDisplay mEglDisplay;//默认的显示设备
    private EGLContext mEglContext;
    private EGLSurface mEglSurface;

    public void initEgl(Surface surface, EGLContext eglContext) {
//1.得到Egl实例
        mEgl = (EGL10) EGLContext.getEGL();

//2.得到默认的显示设备(就是窗口)
        mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);


        if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
            throw new RuntimeException("eglGetDisplay failed");
        }

//3.初始化默认显示设备
        int[] version = new int[2];
        if (!mEgl.eglInitialize(mEglDisplay, version)) {
            throw new RuntimeException("eglInitialize failed");
        }
//4.配置属性自己写  设置显示设备的属性
        int[] attrbutes = new int[]{
                EGL10.EGL_RED_SIZE, 8,
                EGL10.EGL_GREEN_SIZE, 8,
                EGL10.EGL_BLUE_SIZE, 8,
                EGL10.EGL_ALPHA_SIZE, 8,
                EGL10.EGL_DEPTH_SIZE, 8,
                EGL10.EGL_STENCIL_SIZE, 8,
                EGL10.EGL_RENDERABLE_TYPE, 4,
                EGL10.EGL_NONE
        };
        //EGL10.EGL_NONE结尾
		//        EGL_DEPTH_SIZE, 24, //请求深度缓冲区
		//        EGL_STENCIL_SIZE, 8,//请求模版缓冲区
        int[] num_config = new int[1];
        if (!mEgl.eglChooseConfig(mEglDisplay, attrbutes, null, 1, num_config)) {
            throw new IllegalArgumentException("eglChooseConfig failed");
        }
        
//5.从系统中获取对应属性的配置
        int numConfigs = num_config[0];
        EGLConfig[] configs = new EGLConfig[numConfigs];
        if (!mEgl.eglChooseConfig(mEglDisplay, attrbutes, configs, numConfigs,
                num_config)) {
            throw new IllegalArgumentException("eglChooseConfig#2 failed");
        }

//!!!属性必须要设置, 是指定EGL上下文版本和OpenglES的版本。 如果不指定可能会报错误:
// "glDrawArrays is called with VERTEX_ARRAY client state disabled!!!"
int arrtibuteList[] = new int[]{EGL_CONTEXT_CLIENT_VERSION, 2/*OpenglES Version*/,
                EGL10.EGL_NONE};
//6.创建EglContext
        if (eglContext != null) {
            mEglContext = mEgl.eglCreateContext(mEglDisplay, configs[0], eglContext, null);
        } else {//如果没有就创建
            mEglContext = mEgl.eglCreateContext(mEglDisplay, configs[0], EGL10.EGL_NO_CONTEXT, null);
        }
        
//7.创建渲染的Surface
		//使用Android的surface创建egl surface, surface就是渲染的目标窗口。
        mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, configs[0], surface, null);

//8.绑定EglContext和Surface到显示设备中
        if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
            throw new RuntimeException("eglMakeCurrent fail");
        }
    }

//9.刷新数据,显示渲染场景
    public boolean swapBuffers() {
        if (mEgl != null) {
            return mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
        } else {
            throw new RuntimeException("egl is null ");
        }
    }

//10. 回收数据
    public void destoryEgl() {
        if (mEgl != null) {
            mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
                    EGL10.EGL_NO_SURFACE,
                    EGL10.EGL_NO_CONTEXT);
            mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
            mEglSurface = null;

            mEgl.eglDestroyContext(mEglDisplay, mEglContext);
            mEglContext = null;

            mEgl.eglTerminate(mEglDisplay);
            mEglDisplay = null;
            mEgl = null;
        }
    }
}

2. 使用OpenGL API绘制数据

@@: OpenGL是GPU绘制接口,使用opengl绘制之后,数据会被放到显卡的缓存(GPU显存)中,需要调用 EGL的 eglSwapBuffers 将数据转换到目标 eglWindowSurface 中才会显示在屏幕上。

1. 绘制三角形(图元)流程

package com.example.gldemo;

import android.opengl.GLES20;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

public class MainActivity extends AppCompatActivity {

    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }

    private SurfaceView mSV;

    private final String vertexShaderCode =
            "attribute vec4 vPosition;" +
                    "void main() {" +
                    "  gl_Position = vPosition;" +
                    "}";

    private final String fragmentShaderCode =
            "precision mediump float;" +
                    "uniform vec4 vColor;" +
                    "void main() {" +
                    "  gl_FragCol= vColor;" +
                    "}";
                   
	    private int loadShader(int type, String shaderCode) {
        //根据type创建顶点着色器或者片元着色器
        int shader = GLES20.glCreateShader(type);
        //将资源加入到着色器中,并编译
        GLES20.glShaderSource(shader, shaderCode);
        GLES20.glCompileShader(shader);

        int st[] = new int[1];
        GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, st, 0);
        if (st[0] == GLES20.GL_FALSE) {
            GLES20.glDeleteShader(shader);
            Log.e("Main", "loadShader: shader compile failed");
            return GLES20.GL_FALSE;
        }

        return shader;
    }

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

        // Example of a call to a native method
        TextView tv = findViewById(R.id.sample_text);
        tv.setText(stringFromJNI());

        mSV = (SurfaceView) findViewById(R.id.sv);
        mSV.getHolder().addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder holder) {

            }

            @Override
            public void surfaceChanged(final SurfaceHolder holder,
            int format, final int width, final int height) {
            new Thread() {
                    @Override
                    public void run() {
                        super.run();
                    ///1. 初始化EGL环境/
                        EGLHelper eglHelper = new EGLHelper();
                        eglHelper.initEgl(holder.getSurface(), null);


                        float r = 0.0f;
                        float g = 1.0f;
                        float b = 1.0f;


                        float triangleCoords[] = {
                                0.5f,  0.5f, 0.0f, // top
                                -0.5f, -0.5f, 0.0f, // bottom left
                                0.5f, -0.5f, 0.0f  // bottom right
                        };

                        float color[] = { 1.0f, 1.0f, 1.0f, 1.0f }; //白色

						while (true) {
						///2. 绘制背景设置/
							//设置视角
							GLES20.glViewport(0, 0, width, height);
							//清除缓存
                            GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
                            //清除屏幕颜色
                            GLES20.glClearColor(r, g, b, 1.0f);
                            
                            //将顶点坐标保存到浮点数组
	                        FloatBuffer vertextBuffer = ByteBuffer.allocateDirect(triangleCoords.length * 4)
	                                .order(ByteOrder.nativeOrder())
	                                .asFloatBuffer()
	                                .put(triangleCoords);
	                        vertextBuffer.position(0);//重置读取位置到0.
	
					///3. 着色器导入,编译/
							//导入顶点着色器和片元着色器
							int vertextShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
	                        int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
				
					///4. Program创建,链接,并绑定到OpenGL 2.0环境/
							//创建一个空的OpenGLES程序(运行在GPU的程序), 代表了对Shader的操作
	                        int program = GLES20.glCreateProgram();
	                        //将顶点着色器加入到程序
	                        GLES20.glAttachShader(program, vertextShader);
	                        //将片元着色器加入到程序中
	                        GLES20.glAttachShader(program, fragmentShader);
	                        //连接到着色器程序
	                        GLES20.glLinkProgram(program);
	                        int[] ls = new int[1];
	                        GLES20.glGetProgramiv(program,GLES20.GL_LINK_STATUS, ls, 0);
	                        if (ls[0] == GLES20.GL_FALSE) {
	                            Log.e("Main", "run: glLinkProgram failed");
	                        }
                            //将程序加入到OpenGLES2.0环境
                            GLES20.glUseProgram(program);
                            
					///5. 设置顶点数据/
                            //获取顶点着色器的vPosition成员句柄
                            int vPosition = GLES20.glGetAttribLocation(program, "vPosition");
                            //启用三角形顶点的句柄(必须先启动顶点属性数组才能设置)
                            GLES20.glEnableVertexAttribArray(vPosition);
                            //准备三角形坐标数据
                            GLES20.glVertexAttribPointer(vPosition, 3, GLES20.GL_FLOAT, false, 3*4,
                            vertextBuffer);
                            //禁止定点数据句柄,这个可以放在glVertexAttribPointer之前或者之后, 如果
                            //之前不行就放在之后试一下。。。
                            //glDisableVertexAttribArray(0);
					
					///6. 设置片元数据/
                            //获取片元着色器的vColor成员的句柄
                            int vColor = GLES20.glGetUniformLocation(program, "vColor");
                            //设置绘制三角形的颜色
                      		GLES20.glUniform4fv(vColor, 1, color, 0);

					///7. 绘制前面设置的数据/
                            //绘制三角形
                            GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, triangleCoords.length / 3);

                            //绘制完成后再禁止定点数据句柄
                            GLES20.glDisableVertexAttribArray(vPosition);

					///8. 显示显示绘制结果/
							//[必须要] 绘制完成的数据在显存中, 必须要使用egl的eglSwapBuffer才能将数据显示在surface中,
							//显示在屏幕上
                            eglHelper.swapBuffers();
                            try {
                                Thread.sleep(16);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                      }
                }.start();
		}
		@Override
        public void surfaceDestroyed(SurfaceHolder holder) {

        }
        });
    }

1.5 混合数组(顶点坐标和颜色值放在一起)给shader传参

public void surfaceChanged(final SurfaceHolder holder, int format, final int width, final int height) {
new Thread() {
@Override
public void run() {
    super.run();
    EGLHelper eglHelper = new EGLHelper();
    eglHelper.initEgl(holder.getSurface(), null);


    float r = 0.5f;
    float g = 0.5f;
    float b = 0.5f;


    float triangleCoords[] = {
            //x, y
            //r, g, b, a
            0.5f, 0.5f, // top
            1.0f, 1.0f, 1.0f, 1.0f,

            -0.5f, -0.5f, // bottom left
            1.0f, 1.0f, 1.0f, 1.0f,

            0.5f, -0.5f, // bottom right
            1.0f, 1.0f, 1.0f, 1.0f,
    };
    glViewport(0, 0, width, height);

    int vertextShader = loadShader(GL_VERTEX_SHADER, vertexShaderCode);
    int fragmentShader = loadShader(GL_FRAGMENT_SHADER, fragmentShaderCode);

    int program = buildProgram(vertextShader, fragmentShader);

    //将程序加入到OpenGLES2.0环境
    glUseProgram(program);

    FloatBuffer vertextBuffer = ByteBuffer.allocateDirect(triangleCoords.length * 4)
            .order(ByteOrder.nativeOrder())
            .asFloatBuffer()
            .put(triangleCoords);
    vertextBuffer.position(0);
	while (true) {
        glClearColor(r, g, b, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);


        {

            /* vPosition */
            vertextBuffer.position(0);
            //获取顶点着色器的vPosition成员句柄
            int vPosition = glGetAttribLocation(program, "vPosition");
            //准备三角形坐标数据
            glVertexAttribPointer(vPosition, 2, GL_FLOAT, false, 6 * 4, vertextBuffer);
            //启用三角形顶点的句柄
            glEnableVertexAttribArray(vPosition);

			/* aColor */
            vertextBuffer.position(2);
            int aColor = glGetAttribLocation(program, "aColor");
            //为shader属性赋值,一次调用是为所有的顶点赋予属性值
            glVertexAttribPointer(aColor, //指定要设置的属性为aColor(颜色属性)
						            4, //一个顶点读取四个元素
						            GL_FLOAT, //元素类型是浮点型float
						            false,
						            6 * 4, //颜色数值的步长为
						            	  //((2(定点2个) + 4(颜色4个)) * 4(一个元素四个字节))个字节
						            vertextBuffer// 要赋值的buffer的其实偏移地址(颜色越过一个定点
						            			//(两个坐标)从第三个偏移位置上开始)
						            );
            glEnableVertexAttribArray(aColor);//启用定点属性(默认是关闭,
            								//关闭的话opengl拿不到定点的数据, 绘制失败)

            //绘制三角形
            glDrawArrays(GL_TRIANGLE_FAN, 0, 3/*从上面的设置中取三组(顶点和颜色)数据*/);

            //禁止定点数据句柄
            //禁止必须要在绘制之后, 否则绘制失败
            glDisableVertexAttribArray(0);
            glDisableVertexAttribArray(1);
        }

        eglHelper.swapBuffers();
        
		try {
            Thread.sleep(16);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}
}.start();
}
                        

2. 绘制纹理

private EGL10 mEgl;
private EGLDisplay mEglDisplay;
private EGLConfig mEglConfig;
private EGLContext mEglContext;
private EGLSurface mEglSurface;
        
private static final String sSimpleVS =
                "attribute vec4 position;\n" +
                "attribute vec2 texCoords;\n" +
                "varying vec2 outTexCoords;\n" +
                "uniform mat4 projection;\n" +
                "\nvoid main(void) {\n" +
                "    outTexCoords = texCoords;\n" +
                "    gl_Position = projection * position;\n" +
                "}\n\n";
private static final String sSimpleFS =
                "precision mediump float;\n\n" +
                "varying vec2 outTexCoords;\n" +
                "uniform sampler2D texture;\n" +
                "\nvoid main(void) {\n" +
                "    gl_FragColor = texture2D(texture, outTexCoords);\n" +
                "}\n\n"; 

private static final int FLOAT_SIZE_BYTES = 4;
private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES;
private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0;
private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3;

private boolean drawWallpaperWithOpenGL(SurfaceHolder sh, int w, int h, int left, int top) {
 			//初始化EGL环境
            if (!initGL(sh)) return false;

            final float right = left + mBackground.getWidth() * mScale;
            final float bottom = top + mBackground.getHeight() * mScale;

            final Rect frame = sh.getSurfaceFrame();
            final Matrix4f ortho = new Matrix4f();
            ortho.loadOrtho(0.0f, frame.width(), frame.height(), 0.0f, -1.0f, 1.0f);

			//创建顶点数据缓存结构
            final FloatBuffer triangleVertices = createMesh(left, top, right, bottom);
			
			//导入Texure(Bitmap位图数据)
            final int texture = loadTexture(mBackground);
            //创建Program,链接顶点和片元着色器
            final int program = buildProgram(sSimpleVS, sSimpleFS);

            final int attribPosition = glGetAttribLocation(program, "position");
            final int attribTexCoords = glGetAttribLocation(program, "texCoords");
            final int uniformTexture = glGetUniformLocation(program, "texture");
            final int uniformProjection = glGetUniformLocation(program, "projection");
			
			checkGlError();

            glViewport(0, 0, frame.width(), frame.height());
            //绑定纹理,绑定之后,所有的操作都针对当前的纹理进行操作
            glBindTexture(GL_TEXTURE_2D, texture);

            glUseProgram(program);//绑定program到当前的opengl版本环境
            glEnableVertexAttribArray(attribPosition);
            glEnableVertexAttribArray(attribTexCoords);
            glUniform1i(uniformTexture, 0);
            glUniformMatrix4fv(uniformProjection, 1, false, ortho.getArray(), 0);
            
            checkGlError();
			if (w > 0 || h > 0) {
                glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
                glClear(GL_COLOR_BUFFER_BIT);
            }

            // drawQuad
            triangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
            glVertexAttribPointer(attribPosition, 3, GL_FLOAT, false,
                    TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);

            triangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
            glVertexAttribPointer(attribTexCoords, 3, GL_FLOAT, false,
                    TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);

			//绘制
            glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

			//显示绘制内容
            boolean status = mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
            checkEglError();

            finishGL(texture, program);

            return status;
  }

//初始化EGL环境
private boolean initGL(SurfaceHolder surfaceHolder) {
            mEgl = (EGL10) EGLContext.getEGL();

            mEglDisplay = mEgl.eglGetDisplay(EGL_DEFAULT_DISPLAY);
            if (mEglDisplay == EGL_NO_DISPLAY) {
                throw new RuntimeException("eglGetDisplay failed " +
                        GLUtils.getEGLErrorString(mEgl.eglGetError()));
            }

            int[] version = new int[2];
            if (!mEgl.eglInitialize(mEglDisplay, version)) {
                throw new RuntimeException("eglInitialize failed " +
                        GLUtils.getEGLErrorString(mEgl.eglGetError()));
            }

            mEglConfig = chooseEglConfig();
            if (mEglConfig == null) {
                throw new RuntimeException("eglConfig not initialized");
            }
            
            mEglContext = createContext(mEgl, mEglDisplay, mEglConfig);
            if (mEglContext == EGL_NO_CONTEXT) {
                throw new RuntimeException("createContext failed " +
                        GLUtils.getEGLErrorString(mEgl.eglGetError()));
            }

            int attribs[] = {
                EGL_WIDTH, 1,
                EGL_HEIGHT, 1,
                EGL_NONE
            };
            EGLSurface tmpSurface = mEgl.eglCreatePbufferSurface(mEglDisplay, mEglConfig, attribs);
            mEgl.eglMakeCurrent(mEglDisplay, tmpSurface, tmpSurface, mEglContext);

            int[] maxSize = new int[1];
            Rect frame = surfaceHolder.getSurfaceFrame();
            glGetIntegerv(GL_MAX_TEXTURE_SIZE, maxSize, 0);

            mEgl.eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
            mEgl.eglDestroySurface(mEglDisplay, tmpSurface);

            if(frame.width() > maxSize[0] || frame.height() > maxSize[
            mEgl.eglDestroyContext(mEglDisplay, mEglContext);
                mEgl.eglTerminate(mEglDisplay);
                Log.e(GL_LOG_TAG, "requested  texture size " +
                    frame.width() + "x" + frame.height() + " exceeds the support maximum of " +
                    maxSize[0] + "x" + maxSize[0]);
                return false;
            }
            mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, surfaceHolder, null);
            if (mEglSurface == null || mEglSurface == EGL_NO_SURFACE) {
                int error = mEgl.eglGetError();
                if (error == EGL_BAD_NATIVE_WINDOW || error == EGL_BAD_ALLOC) {
                    Log.e(GL_LOG_TAG, "createWindowSurface returned " +
                                         GLUtils.getEGLErrorString(error) + ".");
                    return false;
                }
                throw new RuntimeException("createWindowSurface failed " +
                        GLUtils.getEGLErrorString(error));
            }

            if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
                throw new RuntimeException("eglMakeCurrent failed " +
                        GLUtils.getEGLErrorString(mEgl.eglGetError()));
            }

            return true;
    }

	private EGLConfig chooseEglConfig() {
            int[] configsCount = new int[1];
            EGLConfig[] configs = new EGLConfig[1];
            int[] configSpec = getConfig();
            if (!mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1, configsCount)) {
                throw new IllegalArgumentException("eglChooseConfig failed " +
                        GLUtils.getEGLErrorString(mEgl.eglGetError()));
            } else if (configsCount[0] > 0) {
                return configs[0];
            }
            return null;
        }
        
 private int[] getConfig() {
     return new int[] {
             EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
             EGL_RED_SIZE, 8,
             EGL_GREEN_SIZE, 8,
             EGL_BLUE_SIZE, 8,
             EGL_ALPHA_SIZE, 0,
             EGL_DEPTH_SIZE, 0,
             EGL_STENCIL_SIZE, 0,
             EGL_CONFIG_CAVEAT, EGL_NONE,
             EGL_NONE
     };
 }
 
EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
        int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
        return egl.eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, attrib_list);
}
  
private int loadTexture(Bitmap bitmap) {
         int[] textures = new int[1];

         glActiveTexture(GL_TEXTURE0);
         glGenTextures(1, textures, 0);
         checkGlError();

         int texture = textures[0];
         glBindTexture(GL_TEXTURE_2D, texture);
         checkGlError();

         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
			
		//设置要绘制的内容为2D,并设置纹理类型和格式等参数,和纹理图片数据(bitmap)
         GLUtils.texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmap, GL_UNSIGNED_BYTE, 0);
         checkGlError();

         return texture;
  }
  
  private int buildProgram(String vertex, String fragment) {
        int vertexShader = buildShader(vertex, GL_VERTEX_SHADER);
        if (vertexShader == 0) return 0;

        int fragmentShader = buildShader(fragment, GL_FRAGMENT_SHADER);
        if (fragmentShader == 0) return 0;

        int program = glCreateProgram();
        glAttachShader(program, vertexShader);
        glAttachShader(program, fragmentShader);
        glLinkProgram(program);
        checkGlError();

        glDeleteShader(vertexShader);
        glDeleteShader(fragmentShader);

        int[] status = new int[1];
        glGetProgramiv(program, GL_LINK_STATUS, status, 0);
        if (status[0] != GL_TRUE) {
            String error = glGetProgramInfoLog(program);
            Log.d(GL_LOG_TAG, "Error while linking program:\n" + error);
            glDeleteProgram(program);
            return 0;
}

private FloatBuffer createMesh(int left, int top, float right, float bottom) {
          final float[] verticesData = {
                  // X, Y, Z, U, V
                   left,  bottom, 0.0f, 0.0f, 1.0f,
                   right, bottom, 0.0f, 1.0f, 1.0f,
                   left,  top,    0.0f, 0.0f, 0.0f,
                   right, top,    0.0f, 1.0f, 0.0f,
          };

          final int bytes = verticesData.length * FLOAT_SIZE_BYTES;
          final FloatBuffer triangleVertices = ByteBuffer.allocateDirect(bytes).order(
                  ByteOrder.nativeOrder()).asFloatBuffer();
          triangleVertices.put(verticesData).position(0);
          return triangleVertices;
  }
  
  private int buildShader(String source, int type) {
          int shader = glCreateShader(type);

          glShaderSource(shader, source);
          checkGlError();

          glCompileShader(shader);
          checkGlError();

          int[] status = new int[1];
          glGetShaderiv(shader, GL_COMPILE_STATUS, status, 0);
          if (status[0] != GL_TRUE) {
              String error = glGetShaderInfoLog(shader);
              Log.d(GL_LOG_TAG, "Error while compiling shader:\n" + error);
              glDeleteShader(shader);
              return 0;
          }

          return shader;
      }
      
private void finishGL(int texture, int program) {
          int[] textures = new int[1];
          textures[0] = texture;
          glDeleteTextures(1, textures, 0);
          glDeleteProgram(program);
          mEgl.eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
          mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
          mEgl.eglDestroyContext(mEglDisplay, mEglContext);
          mEgl.eglTerminate(mEglDisplay);
}


3. 离屏渲染 FrameBuffer

步骤:

  1. 创建framebuffer
  2. 绑定framebuffer
  3. 切换opengl 的上下文绑定的framebuffer
  4. 设置RenderBuffer
  5. 设置attach(附着纹理/附着深度/附着模板)
  6. 绘制(opengl的绘制是直接将图形渲染到当前绑定的framebuffer)
  7. 从当前framebuffer拂去/拷贝出数据;
 	GLuint texId; //创建一张用于附着到FB的问题
    glActiveTexture(GL_TEXTURE_2D);
    glGenTextures(1, &texId);

    glBindTexture(GL_TEXTURE_2D, texId); //绑定使用该纹理
	//设置纹理参数
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

	//创建framebuffer和renderbuffer
    GLuint fb, renderBuffer;
    glGenFramebuffers(1, &fb);
    glBindFramebuffer(GL_FRAMEBUFFER, fb);

    glGenRenderbuffers(1, &renderBuffer);
    glBindRenderbuffer(GL_RENDERBUFFER, renderBuffer);//绑定renderbuffer
    //设置renderbuffer的缓冲大小
    glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8,
                          min(esContext->width, GL_MAX_RENDERBUFFER_SIZE),
                          min(esContext->height, GL_MAX_RENDERBUFFER_SIZE));


	//附着纹理到framebuffer
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texId, 0);
    //绑定renderbuffer到FB
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderBuffer);

    GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
    if (status != GL_FRAMEBUFFER_COMPLETE) {
        esLogMessage("!GL_FRAMEBUFFER_COMPLETE(%x)\n", status);

        //glBindFramebuffer(GL_FRAMEBUFFER, 0);
        return;
    }

	//各种draw()...
    const size_t size = (const size_t) (esContext->width * esContext->height * 4);
    void *const ptr = malloc(size);
    //glReadPixels 是从当前使用的FB中读取图形缓存数据,通过这个可以将渲染的数据拿出来(离屏渲染).
    glReadPixels(0, 0, esContext->width, esContext->height, GL_RGB565, GL_UNSIGNED_BYTE, ptr);

	//纹理拷贝
	//创建一张空纹理, 用于拷贝当前FB中的数据,拷贝前需要绑定.
	GLuint tex2[1];
    glGenTextures(1, tex2);
    glBindTexture(GL_TEXTURE_2D ,tex2[0]);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

	//glCopyTexImage2D 可以从当前绑定的FB中将其附着的纹理中的数据拷贝到当前正在绑定使用的一张纹理上.
    glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 0, 0,esContext->width , esContext->height,0);

	//使用完后,要将framebuffer切换回默认的framebuffer(屏幕使用的FB)
    glBindFramebuffer(GL_FRAMEBUFFER, 0)

4. 混合 glBlend

混合是根据一定的设置, 将多张纹理/绘制目标进行一定的混合计算,最后渲染到屏幕上.
混合的目的是对目标纹理和源纹理进行透明度或者颜色交叉计算.
一半在由透明渲染需求的时候, 会用到混合.

步骤:

  1. 启动混合;
  2. 设置混合颜色;
  3. 设置混合方式;
  4. 绘制(绘制会将目标纹理(后渲染的纹理)混合到源纹理(前面渲染的纹理)上)
  5. 关闭混合

	//绘制源纹理
	glDrawArrays(GL_TRIANGLES, 0, 3);
	glDrawArrays(GL_TRIANGLES, 0, 3);
	glDrawArrays(GL_TRIANGLES, 0, 3);
	...
	//在启用混合之前,都属于源纹理
	//启用混合之后,绘制的都属于目标纹理
	glEnable(GL_BLEND); //启用混合
    glBlendColor(1, 1, 1, 0.5f); //设置混合颜色
    //使用glBlendColor设置的混合颜色,进行混合.
    //这里表示源和目标都使用Alpha来进行混合(只混合透明度)
    glBlendFunc(GL_CONSTANT_ALPHA, GL_CONSTANT_ALPHA);
    //绘制目标纹理
    glDrawArrays(GL_TRIANGLES, 0, 3);
    glDrawArrays(GL_TRIANGLES, 0, 3);
    ...

	//关闭混合
    glDisable(GL_BLEND);
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值