Android OpenGL ES 离屏幕渲染2——获取渲染结果并显示到ImageView控件中,使用最简模型展示

news2024/10/6 6:57:57

简介:

        紧接上文,本文将用一个不包含顶点shader和片元shader的最小模型讲述如何把通过EGL创建的OpenGL ES环境渲染后的结果进行提取,单纯输出一片铺满视口的红色的像素。

EGL环境创建逻辑:

        先看完整代码:

package com.cjztest.glOffscreenProcess.demo0

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

/**以指定的大小产生EGLContext、EGLDisplay对象,并把OpenGL ES渲染管线的内容输出到EGLDisplay中*/
class EGLMaker : Object {
    protected var mEGLDisplay: EGLDisplay? = null
    protected var mEGLConfig: EGLConfig? = null
    protected var mEGLContext: EGLContext? = null
    protected var mEGLSurface: EGLSurface? = null
    protected var mEglStatus: EglStatus = EglStatus.INVALID
    protected var mContext: Context? = null
    private var mRenderer: IRenderer? = null
    protected var mWidth: Int = 0
    protected var mHeight: Int = 0
    var mIsCreated = false

    // EGLConfig参数
    private val mEGLConfigAttrs = intArrayOf(
            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
    )

    enum class EglStatus {
        INVALID, INITIALIZED, CREATED, CHANGED, DRAW;
        val isValid: Boolean
            get() = this != INVALID
    }

    // 渲染器接口
    interface IRenderer {
        fun onSurfaceCreated()
        fun onSurfaceChanged(width: Int, height: Int)
        fun onDrawFrame()
    }

    fun setRenderer(renderer: IRenderer) {
        mRenderer = renderer
    }

    // EGLContext参数
    private val mEGLContextAttrs = intArrayOf(EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, EGL14.EGL_NONE)

    /**以当前window宽高创建EGLDisplay**/
    constructor(context: Context) {
        mContext = context
        val mWindowManager = mContext!!.getSystemService(Context.WINDOW_SERVICE) as WindowManager
        val displayMetrics = DisplayMetrics()
        mWindowManager.defaultDisplay.getRealMetrics(displayMetrics)
        mWidth = displayMetrics.widthPixels
        mHeight = displayMetrics.heightPixels
        createEGLEnv()
    }

   
    /** 以指定宽高创建EGLDisplay**/
    constructor(context: Context, width: Int, height: Int) {
        mContext = context
        mWidth = width
        mHeight = height
        createEGLEnv()
    }

    // 创建EGL环境
    fun createEGLEnv() : Boolean {
        if (mIsCreated) {
            return true
        }
        //1.创建EGLDisplay
        mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY)
        val versions = IntArray(2)
        EGL14.eglInitialize(mEGLDisplay, versions, 0, versions, 1)
        // 2.创建EGLConfig
        val configs: Array<EGLConfig?> = arrayOfNulls(1)
        val configNum = IntArray(1)
        EGL14.eglChooseConfig(mEGLDisplay, mEGLConfigAttrs, 0, configs, 0, 1, configNum, 0) //获取1个EGL配置,因为这个例子只需要一个
        if (configNum[0] > 0) {
            mEGLConfig = configs[0]
        } else {
            return false
        }
        // 3.创建EGLContext
        if (mEGLConfig != null) {
            mEGLContext = EGL14.eglCreateContext(mEGLDisplay, mEGLConfig, EGL14.EGL_NO_CONTEXT, mEGLContextAttrs, 0)
        }

        // 4.创建EGLSurface
        if (mEGLContext != null && mEGLContext != EGL14.EGL_NO_CONTEXT) {
            val eglSurfaceAttrs = intArrayOf(EGL14.EGL_WIDTH, mWidth, EGL14.EGL_HEIGHT, mHeight, EGL14.EGL_NONE) //以传入的宽高作为eglSurface
            mEGLSurface = EGL14.eglCreatePbufferSurface(mEGLDisplay, mEGLConfig, eglSurfaceAttrs, 0)
        }
        // 5.绑定EGLSurface和EGLContext到显示设备(EGLDisplay),但这个EGLDisplay的内容没有和View绑定,所以并不会直接显示
        if (mEGLSurface != null && mEGLSurface != EGL14.EGL_NO_SURFACE) {
            EGL14.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext)
            mEglStatus = EglStatus.INITIALIZED
        }
        mIsCreated = true
        return true
    }

    // 销毁EGL环境
    fun destroyEGLEnv() {
        if (!mIsCreated) {
            return
        }
        // 与显示设备解绑
        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
        mIsCreated = false
    }

    override fun finalize() {
        super.finalize()
        destroyEGLEnv()
    }

    // 请求渲染
    fun requestRender() {
        if (!mEglStatus.isValid) {
            return
        }
        if (mEglStatus == EglStatus.INITIALIZED) {
            mRenderer?.onSurfaceCreated()
            mEglStatus = EglStatus.CREATED
        }
        if (mEglStatus == EglStatus.CREATED) {
            mRenderer?.onSurfaceChanged(mWidth, mHeight)
            mEglStatus = EglStatus.CHANGED
        }
        if (mEglStatus == EglStatus.CHANGED || mEglStatus == EglStatus.DRAW) {
            mRenderer?.onDrawFrame()
            mEglStatus = EglStatus.DRAW
        }
    }

    fun getWidth() : Int {
        return mWidth
    }

    fun getHeight() : Int {
        return mHeight
    }
    
}

        类中核心逻辑在createEGLEnv方法中,负责根据制定的宽高、配置创建EGL环境。

        在当前线程把EGL环境创建后之后,就可以在当前线程中调用OpenGLES API进行渲染了。为了方便灵活地自定义GL接口的调用逻辑,和EGL环境的创建逻辑进行解耦,在这里写了一个回调接口IRenderer:

    // 渲染器接口
    interface IRenderer {
        fun onSurfaceCreated()
        fun onSurfaceChanged(width: Int, height: Int)
        fun onDrawFrame()
    }

        类中的requestRender方法其实就是在判断EGL环境创建就绪后,回调里面的自定义的onDrawFrame实现调用用户自定义的GL绘制逻辑。但实际上,在本线程中,只要你确认EGL已经创建成功,你可以在任意地方调用GL API,只是这样写可以用EGLMaker顺便帮自定义逻辑做一次EGL环境是否就绪的判断。

最小绘制测试逻辑:

        这里仅仅使用GLES30的API,做一次红色的清屏操作,然后使用glReadPixels获取里面的像素,然后显示到ImageView中,然后显示的是红色画面即代表EGL创建成功,而且OpenGL API能被成功调用。

测试逻辑搭建:

        测试渲染逻辑:

        由于只是渲染一个红色画面所以需要调用glClearColor当前渲染上下文状态颜色红色调用glClear并使用GL_COLOR_BUFFER_BIT参数FrameBuffer颜色组件进行即可,足够简单,甚至不需要启用GL_DEPTH_BUFFER_BIT

    override fun onDrawFrame() {
        GLES30.glClearColor(1f, 0f, 0f, 1f) //设定清理颜色为红色
        // 将颜色缓存区设置为预设的颜色
        GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)
        //试试读出里面的像素,如果有颜色就代表这一阶段成功了
        if (mWidth != null && mHeight != null) {
            val byteBuffer = ByteBuffer.allocate(mWidth!! * mHeight!! * 4)
            GLES30.glReadPixels(0, 0, mWidth!!, mHeight!!, GLES30.GL_RGBA, GLES30.GL_UNSIGNED_BYTE, byteBuffer)
            if (mContentBmp == null || mContentBmp?.width != mWidth!! || mContentBmp?.height != mWidth!!) {
                mContentBmp = Bitmap.createBitmap(mWidth!!, mHeight!!, Bitmap.Config.ARGB_8888)
            }
            mContentBmp?.copyPixelsFromBuffer(byteBuffer)
        }
    }

        测试Activity

package com.cjztest.glOffscreenProcess.demo0

import android.app.Activity
import android.graphics.Color
import android.os.Bundle
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.LinearLayout

class OffScreenGLDemoActivity : Activity() {

    private lateinit var mEGL: EGLMaker

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val imageView = ImageView(this)

        val renderer = Renderer()
        mEGL = EGLMaker(this, 300, 300)
        mEGL.setRenderer(renderer)
        mEGL.requestRender()

        imageView.setImageBitmap(renderer.getBitmap())

        setContentView(imageView)
    }
}

        最终结果:

                原本没有颜色ImageView正确显示红色

结尾:

        GL上下文生效后,自然不止绘制这么简单画面,这个只是最简单的实例。实际可以按照以往正常OpenGL代码方式写好顶点shader片元shader编译后运行对应的shader program,以及执行纹理贴图逻辑顶点读取塞入缓冲逻辑实现复杂画面操作本文只是为了简化阅读难度,故意不引入更多复杂的操作,所以仅仅只是一次红色而已

github地址:

learnopengl/app/src/main/java/com/cjztest/glOffscreenProcess/demo0 at main · cjzjolly/learnopengl · GitHub

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1905192.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

异常组成、作用、处理方式(3种)、异常方法、自定义异常

目录 异常的组成&#xff1a;运行异常与编译异常 两者区别&#xff1a;编译异常用来提醒程序员&#xff0c;运行异常大部分是由于参数传递错误导致 异常作用&#xff1a; 作用1&#xff1a;就是平时的报错&#xff0c;方便我们找到报错的来源 作用2&#xff1a;在方法内部…

华为机试HJ51输出单向链表中倒数第k个结点

华为机试HJ51输出单向链表中倒数第k个结点 题目&#xff1a; 想法&#xff1a; 因为要用链表&#xff0c;且要找到倒数第k个结点&#xff0c;针对输入序列倒叙进行构建链表并找到对应的元素输出。注意因为有多个输入&#xff0c;要能接受多次调用 class Node(object):def __…

[Godot3.3.3] – 人物死亡动画 part-2

前言 在上一个 part 中已经完成了大部分的逻辑&#xff0c;现在进行一些新的修改。 增加重力 首先将 PlayerDeath 中的 AnimationPlayer 设置为自动播放。 返回 PlayerDeath.gd 并增加一个重力 300&#xff0c;防止玩家的尸体腾空运动。 var gravity 1000 _process 函数中…

某大会的影响力正在扩大,吞噬了整个数据库世界!

1.规模空前 你是否曾被那句“上有天堂&#xff0c;下有苏杭”所打动&#xff0c;对杭州的湖光山色心驰神往&#xff1f;7月&#xff0c;正是夏意正浓的时节&#xff0c;也是游览杭州的最佳时期。这座古典与现代交融的城市将迎来了第13届PostgreSQL中国技术大会。作为全球数据库…

基于深度学习LightWeight的人体姿态之行为识别系统源码

一. LightWeight概述 light weight openpose是openpose的简化版本&#xff0c;使用了openpose的大体流程。 Light weight openpose和openpose的区别是&#xff1a; a 前者使用的是Mobilenet V1&#xff08;到conv5_5&#xff09;&#xff0c;后者使用的是Vgg19&#xff08;前10…

二、Spring

二、Spring 1、Spring简介 1.1、Spring概述 官网地址&#xff1a;https://spring.io/ Spring 是最受欢迎的企业级 Java 应用程序开发框架&#xff0c;数以百万的来自世界各地的开发人员使用 Spring 框架来创建性能好、易于测试、可重用的代码。 Spring 框架是一个开源的 Jav…

Nginx七层(应用层)反向代理:HTTP反向代理proxy_pass篇

Nginx七层&#xff08;应用层&#xff09;反向代理 HTTP反向代理proxy_pass篇 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress of thi…

MySQL Binlog详解:提升数据库可靠性的核心技术

文章目录 1. 引言1.1 什么是MySQL Bin Log&#xff1f;1.2 Bin Log的作用和应用场景 2. Bin Log的基本概念2.1 Bin Log的工作原理2.2 Bin Log的三种格式 3. 配置与管理Bin Log3.1 启用Bin Log3.2 配置Bin Log参数3.3 管理Bin Log文件3.4 查看Bin Log内容3.5 使用mysqlbinlog工具…

论文研读|AI生成图像检测发展历程及研究现状

前言&#xff1a;本篇博客系统性梳理AI生成图像检测的研究工作。 「人工智能生成图像检测」研究及发展现状介绍 参考资料 https://fdmas.github.io/AIGCDetect/针对AIGC检测的鲁棒性测试——常见攻击手段汇总论文研读&#xff5c;以真实图像为参考依据的AIGC检测论文研读&…

C++ 函数高级——函数重载——注意事项

1.引用作为重载条件 2.函数重载碰到函数默认参数 示例&#xff1a; 运行结果&#xff1a;

HTTP长连接

长连接优点 HTTP为什么要开启长连接呢? 主要是为了节省建立的时间,请求可以复用同一条TCP链路,不用重复进行三握+四挥 如果没有长连接,每次请求都做三握+四挥 如果有长链接,在一个 TCP 连接中可以持续发送多份数据而不会断开连接,即请求可以复用TCP链路 长连接缺点 …

国内免费Ai最新体验+评分

我们主要从画图、数据分析、语言三个方面进行简单测评 1.智谱AI - ChatGLM 智谱产品&#xff1a; 智谱清言 CodeGeeX–智能编程助手——放在这里给大家推荐一手 AMiner 学术论文检索平台 测试1-画图测试 ⭐⭐⭐⭐ 画一头猪 测试2-数据分析 ⭐⭐⭐⭐ 画图和线性回归模型…

通信协议_C#实现自定义ModbusRTU主站

背景知识&#xff1a;modbus协议介绍 相关工具 mbslave:充当从站。虚拟串口工具:虚拟出一对串口。VS2022。 实现过程以及Demo 打开虚拟串口工具: 打开mbslave: 此处从站连接COM1口。 Demo实现 创建DLL库&#xff0c;创建ModbusRTU类,进行实现&#xff1a; using Syste…

53-1 内网代理3 - Netsh端口转发(推荐)

靶场还是用上一篇文章搭建的靶场 :52-5 内网代理2 - LCX端口转发(不推荐使用LCX)-CSDN博客 一、Netsh 实现端口转发 Netsh是Windows自带的命令行脚本工具,可用于配置端口转发。在一个典型的场景中,如果我们位于公网无法直接访问内网的Web服务器,可以利用中间的跳板机通过…

防火墙基础及登录(华为)

目录 防火墙概述防火墙发展进程包过滤防火墙代理防火墙状态检测防火墙UTM下一代防火墙&#xff08;NGFW&#xff09; 防火墙分类按物理特性划分软件防火墙硬件防火墙 按性能划分百兆级别和千兆级别 按防火墙结构划分单一主机防火墙路由集成式防火墙分布式防火墙 华为防火墙利用…

SpringBoot运维篇

工程打包与运行 windows系统 直接使用maven对项目进行打包 jar支持命令行启动需要依赖maven插件支持&#xff0c;打包时须确认是否具有SpringBoot对应的maven插件 <build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><ar…

Mysql在Windows系统下安装以及配置

目录 一、下载Mysql 二、安装Mysql及环境配置 一、下载Mysql 1. 下载地址 官网:https://www.mysql.com&#xff0c;这里我选用的是Mysql8.0.37版本&#xff08;版本无所谓&#xff0c;随便下8.0.几都行&#xff09; 2.点击DOWNLOADS 然后&#xff0c;点击 MySQL Community…

React中的useMemo和memo

引言 React是一个声明式的JavaScript库&#xff0c;用于构建用户界面。在开发过程中&#xff0c;性能优化是一个重要的方面。useMemo和memo是React提供的工具&#xff0c;用于帮助开发者避免不必要的渲染和计算&#xff0c;从而提升应用性能。 问题背景 在React应用中&#…

计算样本之间的相似度

文章目录 前言一、距离度量1.1 欧几里得距离&#xff08;Euclidean Distance&#xff09;1.2 曼哈顿距离&#xff08;Manhattan Distance&#xff09;1.3 切比雪夫距离&#xff08;Chebyshev Distance&#xff09;1.4 闵可夫斯基距离&#xff08;Minkowski Distance&#xff09…

2024世界人工智能大会,神仙打架

B站&#xff1a;啥都会一点的研究生公众号&#xff1a;啥都会一点的研究生 AI圈最近又发生了啥新鲜事&#xff1f; 该栏目以周更频率总结国内外前沿AI动态&#xff0c;感兴趣的可以点击订阅合集以及时收到最新推送 B站首秀世界人工智能大会&#xff0c;展示自研AI技术与AIGC…