FBO离屏渲染 中使用 GLSurfaceView 来驱动 Renderer 渲染图片,为了隐藏 GLSurfaceView,将其设置为透明的,并且宽高都设置为1。本文将使用 EGL 代替 GLSurfaceView 生成 OpenGL ES 的渲染环境,实现离屏渲染,将渲染后的图片显示在 ImageView 上。
EGL 为 OpenGL ES 提供了绘制表面(或渲染画布),是 OpenGL ES 与显示设备的桥梁,让 OpenGL ES 绘制的内容能够在呈现当前设备上。
EGL 环境创建分为以下5步:
1)创建EGLDisplay
EGLDisplay mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
int[] versions = new int[2];
EGL14.eglInitialize(mEGLDisplay, versions,0, versions, 1);
2)创建EGLConfig
int[] mEGLConfigAttrs = {
EGL14.EGL_RED_SIZE, 8,
EGL14.EGL_GREEN_SIZE, 8,
EGL14.EGL_BLUE_SIZE, 8,
EGL14.EGL_ALPHA_SIZE, 8,
EGL14.EGL_DEPTH_SIZE, 8,
EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
EGL14.EGL_NONE
};
EGLConfig[] configs = new EGLConfig[1];
int[] configNum = new int[1];
EGL14.eglChooseConfig(mEGLDisplay, mEGLConfigAttrs, 0, configs, 0,1, configNum, 0);
EGLConfig mEGLConfig = configs[0];
3)创建EGLContext
int[] mEGLContextAttrs = {EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, EGL14.EGL_NONE};
EGLContext mEGLContext = EGL14.eglCreateContext(mEGLDisplay, mEGLConfig, EGL14.EGL_NO_CONTEXT, mEGLContextAttrs, 0);
4)创建EGLSurface
int[] eglSurfaceAttrs = {EGL14.EGL_WIDTH, mWidth, EGL14.EGL_HEIGHT, mHeight, EGL14.EGL_NONE};
EGLSurface mEGLSurface = EGL14.eglCreatePbufferSurface(mEGLDisplay, mEGLConfig, eglSurfaceAttrs, 0);
5)绑定EGLSurface和EGLContext到显示设备(EGLDisplay)
EGL14.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext);
读者如果对 OpenGL ES 不太熟悉,请回顾以下内容:
绘制三角形绘制立方体MVP矩阵变换透视变换原理
纹理贴图正方形图片贴到圆形上凸镜贴图
FBO离屏渲染
本文完整代码资源见→EGL+FBO离屏渲染
项目目录如下:
2 案例本案例实现了将彩色图片转换为灰色,并且使用 ImageView 显示转换后的图片。
MainActivity.java
package com.zhyan8.egl.activity;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.widget.ImageView;
import androidx.appcompat.app.AppCompatActivity;
import com.zhyan8.egl.R;
import com.zhyan8.egl.model.Model;
import com.zhyan8.egl.opengl.MyEGLSurface;
import com.zhyan8.egl.opengl.MyRender;
public class MainActivity extends AppCompatActivity implements Model.Callback {
private ImageView mImageView;
private MyEGLSurface mEGlSurface;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mImageView = findViewById(R.id.imageView);
initEGLSurface();
mEGlSurface.requestRender();
}
private void initEGLSurface() {
mEGlSurface = new MyEGLSurface(this);
MyRender render = new MyRender(getResources());
render.setCallback(this);
mEGlSurface.init(render);
}
@Override
public void onCall(final Bitmap bitmap) {
runOnUiThread(() -> {
mImageView.setImageBitmap(bitmap);
});
}
}
activity_main.xml
BaseEGLSurface.java
package com.zhyan8.egl.opengl;
import android.content.Context;
import android.opengl.EGL14;
import android.opengl.EGLConfig;
import android.opengl.EGLContext;
import android.opengl.EGLDisplay;
import android.opengl.EGLSurface;
import android.util.DisplayMetrics;
import android.view.WindowManager;
public class BaseEGLSurface {
protected EGLDisplay mEGLDisplay;
protected EGLConfig mEGLConfig;
protected EGLContext mEGLContext;
protected EGLSurface mEGLSurface;
protected Context mContext;
protected Renderer mRenderer;
protected EglStatus mEglStatus = EglStatus.INVALID;
protected int mWidth;
protected int mHeight;
public BaseEGLSurface(Context context) {
mContext = context;
WindowManager mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics displayMetrics = new DisplayMetrics();
mWindowManager.getDefaultDisplay().getRealMetrics(displayMetrics);
mWidth = displayMetrics.widthPixels;
mHeight = displayMetrics.heightPixels;
}
public BaseEGLSurface(Context context, int width, int height) {
mContext = context;
mWidth = width;
mHeight = height;
}
// 设置渲染器
public void setRenderer(Renderer renderer) {
mRenderer = renderer;
}
// EGLDisplay宽高发生变化
public void onSurfaceChanged(int width, int height) {
mWidth = width;
mHeight = height;
EGL14.eglDestroySurface(mEGLDisplay, mEGLSurface);
createSurface();
mEglStatus = EglStatus.CREATED;
}
// 请求渲染
public void requestRender() {
if (mEglStatus == mEglStatus.INVALID) {
return;
}
if (mEglStatus == EglStatus.INITIALIZED) {
mRenderer.onSurfaceCreated();
mRenderer.onSurfaceChanged(mWidth, mHeight);
mEglStatus = EglStatus.CREATED;
}
if (mEglStatus == EglStatus.CREATED || mEglStatus == EglStatus.DRAW) {
mRenderer.onDrawFrame();
mEglStatus = EglStatus.DRAW;
}
}
// 创建EGL环境
public void createEGLEnv() {
createDisplay();
createConfig();
createContext();
createSurface();
makeCurrent();
}
// 销毁EGL环境
public void destroyEGLEnv() {
// 与显示设备解绑
EGL14.eglMakeCurrent(mEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT);
// 销毁 EGLSurface
EGL14.eglDestroySurface(mEGLDisplay, mEGLSurface);
// 销毁EGLContext
EGL14.eglDestroyContext(mEGLDisplay, mEGLContext);
// 销毁EGLDisplay(显示设备)
EGL14.eglTerminate(mEGLDisplay);
mEGLContext = null;
mEGLSurface = null;
mEGLDisplay = null;
}
// 1.创建EGLDisplay
private void createDisplay() {
mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
int[] versions = new int[2];
EGL14.eglInitialize(mEGLDisplay, versions,0, versions, 1);
}
// 2.创建EGLConfig
private void createConfig() {
EGLConfig[] configs = new EGLConfig[1];
int[] configNum = new int[1];
EGL14.eglChooseConfig(mEGLDisplay, mEGLConfigAttrs, 0, configs, 0,1, configNum, 0);
if (configNum[0] > 0) {
mEGLConfig = configs[0];
}
}
// 3.创建EGLContext
private void createContext() {
if (mEGLConfig != null) {
mEGLContext = EGL14.eglCreateContext(mEGLDisplay, mEGLConfig, EGL14.EGL_NO_CONTEXT, mEGLContextAttrs, 0);
}
}
// 4.创建EGLSurface
private void createSurface() {
if (mEGLContext != null && mEGLContext != EGL14.EGL_NO_CONTEXT) {
int[] eglSurfaceAttrs = {EGL14.EGL_WIDTH, mWidth, EGL14.EGL_HEIGHT, mHeight, EGL14.EGL_NONE};
mEGLSurface = EGL14.eglCreatePbufferSurface(mEGLDisplay, mEGLConfig, eglSurfaceAttrs, 0);
}
}
// 5.绑定EGLSurface和EGLContext到显示设备(EGLDisplay)
private void makeCurrent() {
if (mEGLSurface != null && mEGLSurface != EGL14.EGL_NO_SURFACE) {
EGL14.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext);
mEglStatus = EglStatus.INITIALIZED;
}
}
// EGLConfig参数
private int[] mEGLConfigAttrs = {
EGL14.EGL_RED_SIZE, 8,
EGL14.EGL_GREEN_SIZE, 8,
EGL14.EGL_BLUE_SIZE, 8,
EGL14.EGL_ALPHA_SIZE, 8,
EGL14.EGL_DEPTH_SIZE, 8,
EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
EGL14.EGL_NONE
};
// EGLContext参数
private int[] mEGLContextAttrs = {EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, EGL14.EGL_NONE};
// EGL状态
enum EglStatus {
INVALID, INITIALIZED, CREATED, DRAW
}
// 渲染器接口
interface Renderer {
void onSurfaceCreated();
void onSurfaceChanged(int width, int height);
void onDrawFrame();
}
}
MyEGLSurface.java
package com.zhyan8.egl.opengl;
import android.content.Context;
public class MyEGLSurface extends BaseEGLSurface {
public MyEGLSurface(Context context) {
super(context);
}
public MyEGLSurface(Context context, int width, int height) {
super(context, width, height);
}
public void init(Renderer renderer) {
setRenderer(renderer);
createEGLEnv();
}
}
MyRender.java
package com.zhyan8.egl.opengl;
import android.content.res.Resources;
import android.opengl.GLES30;
import com.zhyan8.egl.model.Model;
public class MyRender implements BaseEGLSurface.Renderer {
private Model mModel;
public MyRender(Resources resources) {
mModel = new Model(resources);
}
@Override
public void onSurfaceCreated() {
//设置背景颜色
GLES30.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
//启动深度测试
GLES30.glEnable(GLES30.GL_DEPTH_TEST);
//创建程序id
mModel.onModelCreate();
}
@Override
public void onSurfaceChanged(int width, int height) {
mModel.onModelChange(width, height);
}
@Override
public void onDrawFrame() {
GLES30.glClearColor(0.5f, 0.7f, 0.3f, 1.0f);
// 将颜色缓存区设置为预设的颜色
GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT | GLES30.GL_DEPTH_BUFFER_BIT);
// 启用顶点的数组句柄
GLES30.glEnableVertexAttribArray(0);
GLES30.glEnableVertexAttribArray(1);
// 绘制模型
mModel.onModelDraw();
// 禁止顶点数组句柄
GLES30.glDisableVertexAttribArray(0);
GLES30.glDisableVertexAttribArray(1);
}
public void setCallback(Model.Callback callback) {
mModel.setCallback(callback);
}
}
Model.java
package com.zhyan8.egl.model;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.opengl.GLES30;
import com.zhyan8.egl.R;
import com.zhyan8.egl.utils.ArraysUtils;
import com.zhyan8.egl.utils.ShaderUtils;
import com.zhyan8.egl.utils.TextureUtils;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
public class Model {
private static final int TEXTURE_DIMENSION = 2; // 纹理坐标维度
private static final int VERTEX_DIMENSION = 3; // 顶点坐标维度
private Callback mCallback;
private Resources mResources;
private float mVertex[] = {-1.0f, 1.0f, 0.0f, -1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, -1.0f, 0.0f};
private float[] mFboTexture = {0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f};
protected FloatBuffer mVertexBuffer;
protected FloatBuffer mFboTextureBuffer;
// 帧缓冲对象 - 颜色、深度、模板附着点,纹理对象可以连接到帧缓冲区对象的颜色附着点
private int[] mFrameBufferId = new int[1];
private int[] mTextureId = new int[2];
private int mProgramId;
private Point mBitmapSize = new Point();
public Model(Resources resources) {
mResources = resources;
mVertexBuffer = ArraysUtils.getFloatBuffer(mVertex);
mFboTextureBuffer = ArraysUtils.getFloatBuffer(mFboTexture);
}
// 模型创建
public void onModelCreate() {
mProgramId = ShaderUtils.createProgram(mResources, R.raw.vertex_shader, R.raw.fragment_shader);
TextureUtils.loadTexture(mResources, R.raw.xxx, mBitmapSize, mTextureId, mFrameBufferId);
}
// 模型参数变化
public void onModelChange(int width, int height) {
GLES30.glViewport(0, 0, mBitmapSize.x, mBitmapSize.y);
}
// 模型绘制
public void onModelDraw() {
GLES30.glUseProgram(mProgramId);
// 准备顶点坐标和纹理坐标
GLES30.glVertexAttribPointer(0, VERTEX_DIMENSION, GLES30.GL_FLOAT, false, 0, mVertexBuffer);
GLES30.glVertexAttribPointer(1, TEXTURE_DIMENSION, GLES30.GL_FLOAT, false, 0, mFboTextureBuffer);
// 激活纹理
GLES30.glActiveTexture(GLES30.GL_TEXTURE);
// 绑定纹理
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mTextureId[0]);
// 绑定缓存
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFrameBufferId[0]);
// 绘制贴图
GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 4);
showBitmap();
}
private void showBitmap() {
// 分配字节缓区大小, 一个像素4个字节
ByteBuffer byteBuffer = ByteBuffer.allocate(mBitmapSize.x * mBitmapSize.y * Integer.BYTES);
GLES30.glReadPixels(0, 0, mBitmapSize.x, mBitmapSize.y, GLES30.GL_RGBA, GLES30.GL_UNSIGNED_BYTE, byteBuffer);
Bitmap bitmap = Bitmap.createBitmap(mBitmapSize.x, mBitmapSize.y, Bitmap.Config.ARGB_8888);
// 从缓存区读二进制缓冲数据
bitmap.copyPixelsFromBuffer(byteBuffer);
// 回调
mCallback.onCall(bitmap);
}
public void setCallback(Callback callback) {
mCallback = callback;
}
public interface Callback{
void onCall(Bitmap bitmap);
}
}
ShaderUtils.java
package com.zhyan8.egl.utils;
import android.content.res.Resources;
import android.opengl.GLES30;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
public class ShaderUtils {
//创建程序id
public static int createProgram(Resources resources, int vertexShaderResId, int fragmentShaderResId) {
final int vertexShaderId = compileShader(resources, GLES30.GL_VERTEX_SHADER, vertexShaderResId);
final int fragmentShaderId = compileShader(resources, GLES30.GL_FRAGMENT_SHADER, fragmentShaderResId);
return linkProgram(vertexShaderId, fragmentShaderId);
}
//通过外部资源编译着色器
private static int compileShader(Resources resources, int type, int shaderId){
String shaderCode = readShaderFromResource(resources, shaderId);
return compileShader(type, shaderCode);
}
//通过代码片段编译着色器
private static int compileShader(int type, String shaderCode){
int shader = GLES30.glCreateShader(type);
GLES30.glShaderSource(shader, shaderCode);
GLES30.glCompileShader(shader);
return shader;
}
//链接到着色器
private static int linkProgram(int vertexShaderId, int fragmentShaderId) {
final int programId = GLES30.glCreateProgram();
//将顶点着色器加入到程序
GLES30.glAttachShader(programId, vertexShaderId);
//将片元着色器加入到程序
GLES30.glAttachShader(programId, fragmentShaderId);
//链接着色器程序
GLES30.glLinkProgram(programId);
return programId;
}
//从shader文件读出字符串
private static String readShaderFromResource(Resources resources, int shaderId) {
InputStream is = resources.openRawResource(shaderId);
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String line;
StringBuilder sb = new StringBuilder();
try {
while ((line = br.readLine()) != null) {
sb.append(line);
sb.append("\n");
}
br.close();
} catch (Exception e) {
e.printStackTrace();
}
return sb.toString();
}
}
TextureUtils.java
package com.zhyan8.egl.utils;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;
import android.opengl.GLES30;
import android.opengl.GLUtils;
public class TextureUtils {
// 加载纹理贴图
public static void loadTexture(Resources resources, int resourceId, Point bitmapSize, int[] textureId, int[] frameBufferId) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;
Bitmap bitmap = BitmapFactory.decodeResource(resources, resourceId, options);
bitmapSize.set(bitmap.getWidth(), bitmap.getHeight());
// 生成纹理id
GLES30.glGenTextures(2, textureId, 0);
for (int i = 0; i < 2; i++) {
// 绑定纹理到OpenGL
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureId[i]);
GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_NEAREST);
GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR);
GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_S, GLES30.GL_CLAMP_TO_EDGE);
GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_T, GLES30.GL_CLAMP_TO_EDGE);
if (i == 0) {
// 第一个纹理对象给渲染管线(加载bitmap到纹理中)
GLUtils.texImage2D(GLES30.GL_TEXTURE_2D, 0, GLES30.GL_RGBA, bitmap, 0);
} else {
// 第二个纹理对象给帧缓冲区
GLES30.glTexImage2D(GLES30.GL_TEXTURE_2D, 0, GLES30.GL_RGBA, bitmap.getWidth(), bitmap.getHeight(),
0, GLES30.GL_RGBA, GLES30.GL_UNSIGNED_BYTE, null);
}
// 取消绑定纹理
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, GLES30.GL_NONE);
}
// 创建帧缓存id
GLES30.glGenFramebuffers(1, frameBufferId, 0);
// 绑定帧缓存
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, frameBufferId[0]);
// 将第二个纹理附着在帧缓存的颜色附着点上
GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D, textureId[1], 0);
// 取消绑定帧缓存
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, GLES30.GL_NONE);
}
}
ArraysUtils.java
package com.zhyan8.egl.utils;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
public class ArraysUtils {
public static FloatBuffer getFloatBuffer(float[] floatArr) {
FloatBuffer fb = ByteBuffer.allocateDirect(floatArr.length * Float.BYTES)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
fb.put(floatArr);
fb.position(0);
return fb;
}
}
vertex_shader.glsl
#version 300 es
layout (location = 0) in vec4 vPosition;
layout (location = 1) in vec2 aTextureCoord;
out vec2 vTexCoord;
void main() {
gl_Position = vPosition;
vTexCoord = aTextureCoord;
}
fragment_shader.glsl
#version 300 es
precision mediump float;
uniform sampler2D uTextureUnit;
in vec2 vTexCoord;
out vec4 fragColor;
void main() {
vec4 color = texture(uTextureUnit, vTexCoord);
float rgb = color.g;
vec4 c = vec4(rgb, rgb, rgb, color.a);
fragColor = c;
}
3 运行效果
原图:
处理后:
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)