Android OpenGL ES 学习(十) – GLSurfaceView 源码解析GL线程以及自定义 EGL

这篇具有很好参考价值的文章主要介绍了Android OpenGL ES 学习(十) – GLSurfaceView 源码解析GL线程以及自定义 EGL。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

OpenGL 学习教程
Android OpenGL ES 学习(一) – 基本概念
Android OpenGL ES 学习(二) – 图形渲染管线和GLSL
Android OpenGL ES 学习(三) – 绘制平面图形
Android OpenGL ES 学习(四) – 正交投影
Android OpenGL ES 学习(五) – 渐变色
Android OpenGL ES 学习(六) – 使用 VBO、VAO 和 EBO/IBO 优化程序
Android OpenGL ES 学习(七) – 纹理
Android OpenGL ES 学习(八) –矩阵变换
Android OpenGL ES 学习(九) – 坐标系统和。实现3D效果
Android OpenGL ES 学习(十) – GLSurfaceView 源码解析GL线程以及自定义 EGL
代码工程地址: https://github.com/LillteZheng/OpenGLDemo.git

前面我们已经学习了 GL 的基本实现效果,我们一般的操作顺序就是

  • onSurfaceCreated: 进行着色器的加载编译
  • onSurfaceChanged:设置 gl 的大小,或者设置正交投影
  • onDrawFrame:绘制

那你会不会好奇,为啥我们按照这样的顺序,弄完就可以正常显示了呢?你是否对OpenGL 绘制的 “完整流程” 感兴趣呢?你是否也遇到下面这些疑问呢?

  1. GL线程和普通线程有什么区别?
  2. texture所占用的空间是跟GL线程绑定的吗?
  3. 为什么通常一个GL线程的texture等数据,在另一个GL线程没法用?
  4. 为什么通常GL线程销毁后,为什么texture也跟着销毁了?
  5. 不同线程如何共享OpenGL数据

这一章,我们带着这些疑问,来探究 OpenGL 渲染的完整流程,今天要完成的效果,不使用GLSurfaceView,编写 EGL + SurfaceVIew 实现 3D 效果:
Android OpenGL ES 学习(十) – GLSurfaceView 源码解析GL线程以及自定义 EGL

一. 渲染完整流程

为什么叫完整流程?前面也说道,Android 系统把复杂的过程都封装好了,我们只需要调用 GlSurfaceView 就可以很轻松的拿到OpenGL 的渲染环境,然后编写着色器代码和逻辑即可。
但实际上,OpenGL 的核心流程应该是这样的:

Android OpenGL ES 学习(十) – GLSurfaceView 源码解析GL线程以及自定义 EGL
为了得到真相,我们通过查看GLSurfaceView 的源码去看。

二. GLSurfaceView 源码

从入口出发,我们都是调用 glSurface.setRenderer() 去设置的,进到源码后可以发现,它主要做了以下几件事情:

    // EGLConfig 对象.用于指定OpenGL颜色、深度、模版等设置
    if (mEGLConfigChooser == null) {
        mEGLConfigChooser = new SimpleEGLConfigChooser(true);
    }

    // EGLContext 对象.用于提供EGLContext创建和销毁的处理
    if (mEGLContextFactory == null) {
        mEGLContextFactory = new DefaultContextFactory();
    }
    
    // EGLSurface窗口对象. 用于提供EGLSurface创建和销毁的处理
    if (mEGLWindowSurfaceFactory == null) {
        mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory();
    }
     
    //开启 GLThread线程 
    mRenderer = renderer;
    mGLThread = new GLThread(mThisWeakRef);
    mGLThread.start();

额,看完这个注释,你可能会一头雾水,EGL 是啥,EGLSurface 又是啥,看来我们需要先了解 EGL .

2.1 EGL

EGL 是 OpenGL 与本地窗口系统 Native Window System)之间的通信接口,它的主要作用是:

  • 与设备的原生窗口系统通信
  • 查询绘图表面的可用类型和配置
  • OpenGL 和其他图形渲染 API 渲染

不同平台上EGL配置是不一样的,而OpenGL的调用方式是一致的,也就是说OpenGL的跨平台特性依赖于EGL接口。

EGL 通过创建 eglSurface,和上下文 eglContext,就可以通过相关 api 访问本地窗口系统,实现绘制。如下图,体现了 EGL ,display 和 context 三者之间的关系。

Android OpenGL ES 学习(十) – GLSurfaceView 源码解析GL线程以及自定义 EGL

  • Display(EglDisplay):实际显示设备的抽象,你可以理解成显示模块
  • Surface(EglSurface):用来存储图像的内存区域 FrameBuffer 的抽象,包括 Color Buffer (颜色缓冲区),Stencil Buffer(模板缓冲区),Depth Buffer(深度缓冲区)
  • Context(EglContext):存储 OpenGL ES 绘图的一些状态信息,比如存储 texture

在开发时,GLSurfaceView 已经帮我们对 Display,Surface,Context 进行封装管理,我们可以很方便的调用 GLSurfaceView.Render ,实现渲染绘制。
使用 EGL 渲染的一般步骤:

  1. 获取 EGLDisplay 对象,建立与本地窗口系统的连接:调用 eglGetDisplay 方法得到 EGLDisplay。
  2. 初始化 EGL 方法:打开连接之后,调用 eglInitialize 方法初始化。
  3. 获取 EGLConfig 对象,确定渲染表面的配置信息:调用 eglChooseConfig 方法得到 EGLConfig。
  4. 创建渲染表面 EGLSurface:通过 EGLDisplay 和 EGLConfig ,调用 eglCreateWindowSurface 或 eglCreatePbufferSurface 方法创建渲染表面,得到 EGLSurface,其中 eglCreateWindowSurface 用于创建屏幕上渲染区域,eglCreatePbufferSurface 用于创建屏幕外渲染区域。
  5. 创建渲染上下文 EGLContext :通过 EGLDisplay 和 EGLConfig ,调用 eglCreateContext 方法创建渲染上下文,得到 EGLContext。
  6. 绑定上下文:通过 eglMakeCurrent 方法将 EGLSurface、EGLContext、EGLDisplay 三者绑定,绑定成功之后 OpenGLES 环境就创建好了,接下来便可以进行渲染。
  7. 交换缓冲:OpenGLES 绘制结束后,使用 eglSwapBuffers 方法交换前后缓冲,将绘制内容显示到屏幕上,而屏幕外的渲染不需要调用此方法。
  8. 释放 EGL 环境:绘制结束后,不再需要使用 EGL 时,需要取消 eglMakeCurrent 的绑定,销毁 EGLDisplay、EGLSurface、EGLContext 三个对象。

原来如此,原来 GLSurfaceView 里面,已经帮我们把 EGL 初始化好了,回到 glSurface.setRenderer() 的方法:

    // EGLConfig 对象.用于指定OpenGL颜色、深度、模版等设置
    if (mEGLConfigChooser == null) {
        mEGLConfigChooser = new SimpleEGLConfigChooser(true);
    }

    // EGLContext 对象.用于提供EGLContext创建和销毁的处理
    if (mEGLContextFactory == null) {
        mEGLContextFactory = new DefaultContextFactory();
    }
    
    // EGLSurface窗口对象. 用于提供EGLSurface创建和销毁的处理
    if (mEGLWindowSurfaceFactory == null) {
        mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory();
    }
     
    //开启 GLThread线程 
    mRenderer = renderer;
    mGLThread = new GLThread(mThisWeakRef);
    mGLThread.start();

我们就知道,原来注释是这个意思,管理关键还得看一下 GL 线程做了什么。

2.2 GL 线程

我们去看看它和其他线程有什么区别:
Android OpenGL ES 学习(十) – GLSurfaceView 源码解析GL线程以及自定义 EGL
实际上,它跟普通线程没有什么区别,也是继承Thread,关键就是 guradedRun() 方法,它实际上把 EGL 给初始化好,然后回调相关方法,让你去绘制,在一个 while 循环中,它做了哪些操作呢?

while(true){
	//初始化 egl,配置等信息
	mEglHelper.start()
	//回去 Surface,并配置到当前线程
	mElgHeper.createSurface()
	回调 onSurfaceCreated()
	回调 onSurfaceChanged()
	回调 onDrawFrame()
	//egl交换缓冲区,呈现画面
	mEglHelper.swap()
}

从这里看,guradedRun() 跟我们之前说的流程基本是一样的,还有非常熟悉的 onSurfaceCreated(), onSurfaceChanged() 和 onDrawFrame() 也是在 EGL 配置之后,再回调的。
这里我们也可以得出一个结论,就是 GL线程跟普通线程没什么区别,就是一个普通线程,只是按照 OpenGL 完整的绘图方式走了一遍,因此我们也可以自己自定义,也可以实现OpenGL 的绘制,从 glGenTextures 等绘图api 都是 native 方法也可以证明这一点。

2.3 为什么通常一个GL线程的texture等数据,在另一个GL线程没法用

回答这个问题之前,先看看纹理对象是怎么生成的,查看 GLES30.glGenTextures 方法:

// C function void glGenTextures ( GLsizei n, GLuint *textures )

public static native void glGenTextures(
    int n,
    int[] textures,
    int offset
);

看来跟 EGL 没关系,那它是怎么知道是 GL 线程去调,还是普通线程去调呢?它又是怎么把 glGenTextures 和 glDeleteTextures 对应到正确的线程上的呢?看源码:

Android OpenGL ES 学习(十) – GLSurfaceView 源码解析GL线程以及自定义 EGL

Android OpenGL ES 学习(十) – GLSurfaceView 源码解析GL线程以及自定义 EGL

实际上,它会先通过 getGlThreadSpecific() 这个方法去拿到一个 context,这个 context 实际上就是 EGL Context ,它是比较特殊的。什么意思呢,就是不同线程去拿,得到的 EGL Context 可能都不一样,这取决于给这个 EGL Context 是什么,我的理解是相当于线程类自己的局部变量,每个线程存储的 context 都不一样。

那EGL Context 是什么时候被设置进去的呢?
还记得 前面说到的 eglMakeCurrent() 这个方法吗?

egl.eglMakeCurrent(display, eglSurface, eglSurface, eglContext)

实际上,第四个参数eglContext 最终会设置给 setGlThreadSpecific() 中的变量存储起来,给其 API 使用。
然后我们再看一下 eglMakeCurrent 做了什么:
Android OpenGL ES 学习(十) – GLSurfaceView 源码解析GL线程以及自定义 EGL

可以得到以下总结:

  1. 获取当前线程 EGL context ,给底层使用
  2. 判断当前的 context 是否为 IS_CURRENT 状态,否则返回-1
  3. 如果 gl 是 IS_CURRENT 状态,但是不是当前线程,也return
  4. 如果 gl 不是 IS_CURRENT 状态,将 current 设置成非 IS_CURRENT 状态
  5. 将gl置为IS_CURRENT状态并将gl设置为当前线程的Thread Local的EGL Context

结论:

  1. 如果一个 EGL context 被一个线程使用 glMakeCurrent ,则它不能被另外一个线程 glMakeCurrent 了
  2. glMakeCurrent 之后, EGL Context 会跟当前的 EGL context 脱离关系

关于 texture 与 egl context 的关系,可以参考这篇文章 https://cloud.tencent.com/developer/article/1035505

因此,我们可以回答一下上面的问题:

  1. GL线程和普通线程有什么区别?: 没有区别,只是 GL线程 按照 OpenGL 的流程跑了一遍。
  2. texture所占用的空间是跟GL线程绑定的吗?:不是,跟 EGL context 绑定,跟 线程没关系。
  3. 为什么通常一个GL线程的texture等数据,在另一个GL线程没法用?:因为调用 OpenGL 接口,会先去获取 EGL context,而不同线程获取的状态是不一样的,而 texture 又放在 EGL context 中,因此无法在另一个 GL 线程使用。
  4. 为什么通常GL线程销毁后,为什么texture也跟着销毁了?:因为 GLSurfaceView 在销毁时,调用了 eglDestroyContext() 方法,销毁了 EGL context,从而 texture 也销毁了,跟 GL线程没关系
  5. 不同线程如何共享OpenGL数据:共享 EGL context,线程调用 eglCreateContext 时,传入另一个线程的 EGL Context。

三. 自定义 EGL 现成,使用SurfaceView+EGL 实现渲染

前面说到,GLSurfaceView 帮我们把 egl 的配置都弄好了,优点是使用简单,缺点是当我们想共享同个 EGL context,实现同个场景,不同 surface 的渲染时,GLSurfaceView 就使用了。
所以,我们自定义自己的 GLSurfaceView。

首先,创建一个类,让它也继承 SurfaceView,并创建一个线程,用来加载 EGL :

inner class EglSurfaceView(context: Context) : SurfaceView(context), SurfaceHolder.Callback {

        init {
            holder.addCallback(this)
        }l
        override fun surfaceCreated(holder: SurfaceHolder) {
      
        }

        override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {

        }

        override fun surfaceDestroyed(holder: SurfaceHolder) {

        }
        inner class EglThread(private val surface: Surface) : Thread() {
				 override fun run() {
				 	....
				 }
		}
}

接着,就需要创建 EGL 了,这里我们通过模仿 GLSurfaceView 的 EGLHelper 方法,得到以下类:

inner class EglHelper {
        private var egl: EGL10? = null
        private var eglDisplay: EGLDisplay? = null
        private var eglSurface: EGLSurface? = null
        private var eglContext: EGLContext? = null
        fun initEgl(surface: Surface) {
            //1、得到Egl实例:
            val egl = EGLContext.getEGL() as EGL10
            //2、得到默认的显示设备(就是窗口)
            val display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY)
            if (display === EGL10.EGL_NO_DISPLAY) {
                throw RuntimeException("eglGetDisplay failed")
            }

            //3、初始化默认显示设备
            val displayVersions = IntArray(2)
            if (!egl.eglInitialize(display, displayVersions)) {
                throw RuntimeException("eglInitialize failed")
            }
            //4、设置显示设备的属性
            val attr = intArrayOf(
                EGL14.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
            )
            //5、从系统中获取对应属性的配置
            val num_config = IntArray(1)
            if(!egl.eglChooseConfig(display, attr, null, 1, num_config)) {
                throw RuntimeException("eglChooseConfig failed")
            }

            val numConfigs = num_config[0]

            if (numConfigs <= 0) {
                throw RuntimeException("No configs match configSpec")
            }

            val configs = arrayOfNulls<EGLConfig>(numConfigs)
            if (!egl.eglChooseConfig(display, attr, configs, numConfigs, num_config)) {
                throw RuntimeException("eglChooseConfig#2 failed")
            }

            //6、创建EglContext
            val  attrib_list = intArrayOf(
                EGL14.EGL_CONTEXT_CLIENT_VERSION, 3,
                EGL10.EGL_NONE
            )
            val eglContext = egl.eglCreateContext(display,
                configs[0],
                EGL10.EGL_NO_CONTEXT, attrib_list)

            //7、创建渲染的Surface
            val eglSurface = egl.eglCreateWindowSurface(display, configs[0], surface, null)

            //8、绑定EglContext和Surface到显示设备中
            if (!egl.eglMakeCurrent(display, eglSurface, eglSurface, eglContext)) {
                throw RuntimeException("eglMakeCurrent fail")
            }
            this.egl = egl
            this.eglDisplay = display
            this.eglSurface = eglSurface
            this.eglContext = eglContext

        }

        fun swapBuffers() {
            egl?.eglSwapBuffers(eglDisplay, eglSurface)
        }

        fun destroy() {
            egl?.apply {
                eglMakeCurrent(
                    eglDisplay,
                    EGL10.EGL_NO_SURFACE,
                    EGL10.EGL_NO_SURFACE,
                    EGL10.EGL_NO_CONTEXT
                )
                eglDestroySurface(eglDisplay, eglSurface)
                eglSurface = null
                eglDestroyContext(eglDisplay, eglContext)
                eglContext = null
                eglTerminate(eglDisplay)
                eglDisplay = null
                egl = null
            }
        }
    }

步骤比较简单,就是:

  1. 拿到Egl实例 :EGLContext.getEGL()
  2. 得到默认的显示设备(就是窗口):egl.eglGetDisplay
  3. 初始化默认显示设备:egl.eglInitialize
  4. 设置显示设备的属性和配置到 egl 中:egl.eglChooseConfig
  5. 创建EglContext:egl.eglCreateContext
  6. 创建渲染的Surface:egl.eglCreateWindowSurface
  7. 绑定EglContext和Surface到显示设备中:egl.eglMakeCurrent
  8. 交换缓冲区,渲染:egl.eglSwapBuffers

然后我们把 EhlHeper 跟线程结合起来,完整的代码如下:

/**
* 自定义一个 SurfaceView,里面的线程模拟 EGL 环境
*/
inner class EglSurfaceView(context: Context) : SurfaceView(context), SurfaceHolder.Callback {

   init {
       holder.addCallback(this)
   }


   private var eglThread: EglThread? = null

   override fun surfaceCreated(holder: SurfaceHolder) {
       eglThread = EglThread(holder.surface)
       eglThread?.start()
   }

   override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
       eglThread?.changeSize(width, height)
   }

   override fun surfaceDestroyed(holder: SurfaceHolder) {
       eglThread?.release()
   }

   /**
    * gl线程,里面包含 EGL 创建流程
    */
   inner class EglThread(private val surface: Surface) : Thread() {
       private var isExit = false
       private var isFirst = true
       private var isSizeChange = false
       private var eglHelper: EglHelper? = null
       private var width = 0
       private var height = 0
       override fun run() {
           super.run()
           eglHelper = EglHelper().apply {
               initEgl(surface)
           }
           while (true) {
               if (isExit) {
                   release()
                   break
               }

               if (isFirst) {
                   isFirst = false
                   //回调给主类
                   this@L8_ShapeRender.onSurfaceCreated(null, null)
               }
               if (isSizeChange) {
                   isSizeChange = false
                   this@L8_ShapeRender.onSurfaceChanged(null, width, height)
               }
               eglHelper?.let {
                   this@L8_ShapeRender.onDrawFrame(null)
                   it.swapBuffers()
               }
               try {
                   sleep(16)
               } catch (e: Exception) {
               }
           }
       }

       fun release() {
           isExit = true
           eglHelper?.destroy()
           surface.release()
       }

       fun changeSize(width: Int, height: Int) {
           this.width = width
           this.height = height
           isSizeChange = true
       }
   }


}

都比较好理解,可以看到,我们也通过 onSurfaceCreated,onSurfaceChanged 和 onDrawFrame 回调给外部,这样,着色器相关的,就不用改变啦。
看一下效果,可以正常运行:
Android OpenGL ES 学习(十) – GLSurfaceView 源码解析GL线程以及自定义 EGL

参考:
https://cloud.tencent.com/developer/article/1035505
https://cloud.tencent.com/developer/article/1899820文章来源地址https://www.toymoban.com/news/detail-447666.html

到了这里,关于Android OpenGL ES 学习(十) – GLSurfaceView 源码解析GL线程以及自定义 EGL的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Android OpenGL ES 学习(八) –矩阵变换

    OpenGL 学习教程 Android OpenGL ES 学习(一) – 基本概念 Android OpenGL ES 学习(二) – 图形渲染管线和GLSL Android OpenGL ES 学习(三) – 绘制平面图形 Android OpenGL ES 学习(四) – 正交投影 Android OpenGL ES 学习(五) – 渐变色 Android OpenGL ES 学习(六) – 使用 VBO、VAO 和 EBO/IBO 优化程序 Android OpenG

    2023年04月08日
    浏览(38)
  • 【Android App】三维投影OpenGL ES的讲解及着色器实现(附源码和演示 超详细)

    需要源码请点赞关注收藏后评论区留言私信~~~ 虽然OpenGL的三维制图功能非常强大,但是它主要为计算机设计的,对于嵌入式设备和移动端设备来说显得比较臃肿,所以业界又设计了专供嵌入式设备的OpenGL ES 它相当于OpenGL的精简版,因为嵌入式设备追求性价比,所以能不做的

    2024年02月04日
    浏览(49)
  • Android OpenGL ES 学习(九) – 坐标系统和实现3D效果

    OpenGL 学习教程 Android OpenGL ES 学习(一) – 基本概念 Android OpenGL ES 学习(二) – 图形渲染管线和GLSL Android OpenGL ES 学习(三) – 绘制平面图形 Android OpenGL ES 学习(四) – 正交投影 Android OpenGL ES 学习(五) – 渐变色 Android OpenGL ES 学习(六) – 使用 VBO、VAO 和 EBO/IBO 优化程序 Android OpenG

    2024年01月25日
    浏览(44)
  • Android OpenGL ES 学习(十三) -离屏渲染FBO(截图)RBO, OES转 FBO

    Android OpenGL ES 学习(一) – 基本概念 Android OpenGL ES 学习(二) – 图形渲染管线和GLSL Android OpenGL ES 学习(三) – 绘制平面图形 Android OpenGL ES 学习(四) – 正交投屏 Android OpenGL ES 学习(五) – 渐变色 Android OpenGL ES 学习(六) – 使用 VBO、VAO 和 EBO/IBO 优化程序 Android OpenGL ES 学习(七) –

    2024年02月12日
    浏览(40)
  • 【Android OpenGL开发】OpenGL ES与EGL介绍

    OpenGL(Open Graphics Library)是一个跨编程语言、跨平台的编程图形程序接口,主要用于图像的渲染。 Android提供了简化版的OpenGL接口,即OpenGL ES。 早先定义 OpenGL ES 是 OpenGL 的嵌入式设备版本,用于移动端平台(Android、iOS),但由于嵌入式设备要求的是高性能,所以一些其它纯

    2024年02月04日
    浏览(86)
  • IJKPLAYER源码分析-OpenGL ES渲染

    IJKPLAYER在视频render之时,并非简单使用SDL渲染API,而是用了OpenGL ES,再分别在Android和iOS平台做视频的显示; 一言以蔽之,OpenGL ES并不能做到直接在窗口上render并显示,而是需要一个中间媒介。这个中间媒介,将OpenGL ES的render结果,最终输出到窗口上显示; 在Android端,这个中

    2024年04月11日
    浏览(34)
  • (线程池)多线程使用场景--es数据批量导入、数据汇总、异步调用;如何控制某个方法允许并发访问线程的数量;对ThreadLocal的理解及实现原理、源码解析、ThreadLocal的内存泄露问题

    CountDownLatch(闭锁/倒计时锁) 用来进行线程同步协作,等待所有线程完成倒计时(一个或者多个线程,等待其他多个线程完成某件事情之后才能执行) 其中构造参数用来初始化等待计数值 await() 用来等待计数归零 countDown() 用来让计数 减一 多线程使用场景一:( es数据批量导

    2024年04月25日
    浏览(65)
  • Android OpenGL ES实现简单绿幕抠图

    目录 正文 OES Filter BlendShader Filter 最后的效果 缺陷 实现绿幕抠图,其实想法很简单。 这里简单粗暴的使用着色器替换。 OES Filter 直接实现在相机预览上的Shader ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #extension GL_OES_EGL_image_external : require precision mediump float ;        

    2024年02月13日
    浏览(29)
  • OpenGL ES FBO 离屏渲染详解(附项目源码)

    该原创文章首发于微信公众号:字节流动 FBO(Frame Buffer Object)即帧缓冲区对象,实际上是一个可添加缓冲区的容器,可以为其添加纹理或渲染缓冲区对象(RBO)。 FBO 本身不能用于渲染,只有添加了纹理或者渲染缓冲区之后才能作为渲染目标,它仅且提供了 3 种附着(Attac

    2024年02月12日
    浏览(45)
  • OpenGL-ES 学习(4)---- OpenGL-ES 坐标体系

    坐标体系 我们知道 OpenGL -ES 坐标系中每个顶点的 x,y,z 坐标都应该在 -1.0 到 1.0 之间,超出这个坐标范围的顶点都将不可见。 将一个物体(图像)渲染到屏幕上,通常经过将物体坐标转换为标准化设备坐标,然后再将标准化设备坐标转化为屏幕坐标的过程。 ( 将物体坐标转

    2024年02月19日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包