LearnOpenGL - Android OpenGL ES 3.0 绘制三角形

这篇具有很好参考价值的文章主要介绍了LearnOpenGL - Android OpenGL ES 3.0 绘制三角形。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

系列文章目录

  • LearnOpenGL 笔记 - 入门 01 OpenGL
  • LearnOpenGL 笔记 - 入门 02 创建窗口
  • LearnOpenGL 笔记 - 入门 03 你好,窗口
  • LearnOpenGL 笔记 - 入门 04 你好,三角形
  • OpenGL - 如何理解 VAO 与 VBO 之间的关系


1.前言

经过一段时间 OpenGL 的学习,我们已经掌握了如何使用 glwf 在桌面端绘制简单图形。现在让我们把目光投向移动端,看看如何在 Android 上使用 OpenGL 绘制图形。本文假设你对 Android 基础有所了解,并使用 Kotlin 编写示例 demo,项目的代码你可以在 Android_OpenGLES3_Demo 中找到。

本文参考了以下资料

  • Android OpenGL开发——图像绘制详解
  • android平台下OpenGL ES 3.0实例详解顶点缓冲区对象(VBO)和顶点数组对象(VAO)

2. OpenGL ES 3.0

OpenGL ES(OpenGl for Embedded System)是 Khronos 公司为移动端提供的 OpenGL 子集,用于在嵌入式设备和移动端设备中渲染 2D 和 3D 图形,它是为了适应低功耗设备需求而设计的。OpenGL ES 去掉了一些冗余的 API,保留了最核心有用的部分。

Android 支持多版 OpenGL ES API,从 Android 1.0 开始支持 OpenGL ES 1.0 和 1.1,从 Android 2.2 开始支持 OpenGL ES 2.0,从 Android 4.3 开始支持 OpenGL ES 3.0,从 Android 5.0 开始支持 OpenGL ES 3.11。Android 对应 OpenGL ES 的版本支持如下表所示

Android 版本 OpenGL ES 版本
Android 1.0 OpenGL ES 1.0/1.1
Android 2.2 OpenGL ES 2.0
Android 4.3 OpenGL ES 3.0
Android 5.0 OpenGL ES 3.1

由于历史原因,和一些兼容性的问题,很多项目中都使用了 OpenGL ES 2.0 版本而不是 3.0。对比两个版本,它们有着一下区别

  • 性能方面,OpenGL ES 3.0 相比于 OpenGL ES 2.0 增加了一些新功能和优化,例如更大的缓冲区、更多的格式、更多的统一变量、实例渲染、像素缓冲对象和遮挡查询等。这些功能可以提高图形渲染的效率和质量,但也可能增加开发的复杂度和资源消耗。

  • 兼容性方面,OpenGL ES 3.0 是向后兼容 OpenGL ES 2.0 的,也就是说使用 2.0 编写的应用程序是可以在 3.0 中继续使用的。但是,并不是所有的设备都支持 OpenGL ES 3.0 ,所以如果要考虑跨平台的兼容性,可能还需要使用 OpenGL ES 2.0 或者做一些适配工作。

  • 易用性方面,OpenGL ES 3.0 和 OpenGL ES 2.0 都是可编程图形管线,开发者可以自己编写图形管线中的顶点着色器和片段着色器两个阶段的代码。这样可以提供更多的灵活性和创造力,但也需要更多的编程技巧和知识。相比于 OpenGL ES 1.x 系列的固定功能管线,OpenGL ES 2.0/3.0 不支持 OpenGL ES 1.x 支持的固定功能定点单元。

那么为什么选择 OpenGL ES 3.0 呢?很直白的一个理由:ES 3.0 中使用的 OpenGL Shader Language(GLSL)版本是 300,而 LearnOpenGL 教程中使用的是 GLSL 330,两者几乎没有差异,只需最小的学习成本就能够掌握。但如果使用 ES 2.0,那么你还得花一些时间来学习和适应不同版本 GLSL。

3 GLSurfaceView 和 GLSurfaceView.Render

在 Android 中,GLSurfaceView 是一个基础自 SurfaceView 的类,它是专门用于显示 OpenGL 渲染的内容。 而 GLSurfaceView.Render 则负责具体地渲染画面。简单来说,GLSurfaceView 类似于 glwf 中窗口的概念,它负责画面的显示、OpenGL Context 的管理、渲染线程的控制等等,而 GLSurfaceView.Render 则是 OpenGL API 调用的逻辑的抽象,你需要创建自己的 Render 来绘制图形。

还是上代码进行说明

class MyOpenGLSurfaceView(context: Context?, attrs :AttributeSet?) : GLSurfaceView(context, attrs) {
    constructor(context: Context?) : this(context, null)

    var render = MyOpenGLSurfaceRender()

    init {
        setEGLContextClientVersion(3)
        setRenderer(render)
    }
}
  • MyOpenGLSurfaceView 继承自 GLSurfaceView,注意要实现 GLSurfaceView(Context context, AttributeSet attrs) 构造函数,我们才能在 layout.xml 布局文件中直接使用 MyOpenGLSurfaceView,否则会出现 Binary XML file line # : Error inflating class 错误
  • init 中,setEGLContextClientVersion(3) 表示我们使用 OpenGL ES 3.0 版本
  • init 中,setRenderer 设置 Render
class MyOpenGLSurfaceRender : GLSurfaceView.Renderer {
   	// ...
    override fun onSurfaceCreated(p0: GL10?, p1: EGLConfig?) {
        //设置背景颜色
        GLES30.glClearColor(0.0f, 0.5f, 0.5f, 0.5f)

        //...
    }

    override fun onSurfaceChanged(p0: GL10?, width: Int, height: Int) {
        GLES30.glViewport(0, 0, width, height)
    }

    override fun onDrawFrame(p0: GL10?) {
        GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)
        // ...
    }
}
  • MyOpenGLSurfaceRender 继承自 GLSurfaceView.Renderer,需要实现 onSurfaceCreatedonSurfaceChangedonDrawFrame
  • onSurfaceCreatedonSurfaceChangedonDrawFrame 一定会在同一个渲染线程中执行,你可以通过 Log 来确定这一点,例如 Log.d("xxx", "onSurfaceCreated thread " + Thread.currentThread())
  • onSurfaceCreated 当 surface 被创建或者重新创建时调用,在这个函数中放置一些创建资源的逻辑,例如设置 vbo 和 vao,编译 shader 等等。
  • onSurfaceChanged 当 surface 大小发生变化时调用,我们可以在这里设置 viewport
  • onDrawFrame 负责渲染当前帧。类比的话,可以将之前 LearnOpenGL 代码中 while 循环中的代码放置在这里

4. 绘制三角形

在 Android 中调用 OpenGL ES API 的方式与 OpenGL API 几乎无异,仿照 LearnOpenGL 中的代码,给出在 Kotlin 代码,只是有一些细节的地方需要注意,接下来讨论代码。

首先是工具类 Shader

class Shader(private val vertexShaderSource: String, private val fragmentShaderSource: String) {
    private val TAG = "Shader"
    var id : Int = 0

    fun prepareShaders(): Int{

        // compile vertex shader
        val vertexShader = createAndCompileShader(GLES30.GL_VERTEX_SHADER, vertexShaderSource)
        if(vertexShader == -1){
            return -1
        }

        // compile fragment shader
        val fragmentShader = createAndCompileShader(GLES30.GL_FRAGMENT_SHADER, fragmentShaderSource)
        if(fragmentShader == -1){
            return -1
        }

        id = createShaderProgram(vertexShader, fragmentShader)
        if(id == -1){
            return -1
        }

        return 0
    }

    private fun createAndCompileShader(type: Int, source: String): Int{
        val success: IntArray = intArrayOf(0)

        val shader = GLES30.glCreateShader(type)
        GLES30.glShaderSource(shader, source)
        GLES30.glCompileShader(shader)
        GLES30.glGetShaderiv(shader, GLES30.GL_COMPILE_STATUS, success, 0)
        if (success[0] == 0) {
            val log = GLES30.glGetShaderInfoLog(shader)
            Log.e(TAG, "compile vertex source failed.$log")
            GLES30.glDeleteShader(shader)
            return -1
        }
        return shader
    }

    private fun createShaderProgram(vertexShader: Int, fragmentShader: Int): Int{
        val success: IntArray = intArrayOf(0)

        val program = GLES30.glCreateProgram()
        GLES30.glAttachShader(program, vertexShader)
        GLES30.glAttachShader(program, fragmentShader)
        GLES30.glLinkProgram(program)

        GLES30.glGetProgramiv(program, GLES30.GL_LINK_STATUS, success, 0)
        if (success[0] == 0) {
            val log = GLES30.glGetShaderInfoLog(program)
            Log.e(TAG, "link shader program failed. $log")
            GLES30.glDeleteProgram(program)
            return -1
        }

        GLES30.glDeleteShader(vertexShader)
        GLES30.glDeleteShader(fragmentShader)

        return program
    }
}
  • 该类参考 我们自己的着色器类 - LearnOpenGL,输入 vertex 和 fragment shader 源码,负责 shader 的编译和链接工作
  • 调用 prepareShaders 来编译和链接 shader,如果失败将返回 -1;如果调用成功,使用 id 来引用 当前的 shader program

Triangle 类:

class Triangle {
    private val vertices = floatArrayOf(
        -0.5f, -0.5f, 0.0f, // left
        0.5f, -0.5f, 0.0f, // right
        0.0f, 0.5f, 0.0f  // top
    )

    val vaos: IntArray = intArrayOf(0)
    val vbos: IntArray = intArrayOf(0)

    fun prepareData(){
        // prepare vbo data
        val vertexBuffer = ByteBuffer.allocateDirect(vertices.size * Float.SIZE_BYTES).order(ByteOrder.nativeOrder()).asFloatBuffer()
        vertexBuffer.put(vertices)
        vertexBuffer.position(0)

        // generate vao and vbo
        GLES30.glGenVertexArrays(1, vaos, 0)
        GLES30.glGenBuffers(1, vbos, 0)

        GLES30.glBindVertexArray(vaos[0])

        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vbos[0])
        GLES30.glBufferData(
            GLES30.GL_ARRAY_BUFFER,
            Float.SIZE_BYTES * vertices.size,
            vertexBuffer,
            GLES30.GL_STATIC_DRAW
        )

        GLES30.glVertexAttribPointer(
            0, 3, GLES30.GL_FLOAT, false,
            3 * Float.SIZE_BYTES, 0
        )
        GLES30.glEnableVertexAttribArray(0)

        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0)
        GLES30.glBindVertexArray(0)
    }
}
  • 注意 vertexBuffer,由于 Kotlin 数据使用大端(BigEnd)存放,而 OpenGL 的数据为小端,因此在 Android 下使用 OpenGL 必须做大小端的转换。
  • 其他部分则是按部就班,创建 vbo 和 vao,设置 vbo 数据,设置 vao 属性等等,不再赘述
class MyOpenGLSurfaceRender : GLSurfaceView.Renderer {
    private val tri = Triangle()
    private val shader: Shader = Shader(
        VertexShaderSource.vertexShaderSource,
        FragmentShaderSource.fragmentShaderSource
    )

    override fun onSurfaceCreated(p0: GL10?, p1: EGLConfig?) {
        GLES30.glClearColor(0.0f, 0.5f, 0.5f, 0.5f)

        tri.prepareData()
        shader.prepareShaders()
    }

    override fun onSurfaceChanged(p0: GL10?, width: Int, height: Int) {
        GLES30.glViewport(0, 0, width, height)
    }

    override fun onDrawFrame(p0: GL10?) {
        GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)

        GLES30.glUseProgram(shader.id)
        GLES30.glBindVertexArray(tri.vaos[0])

        GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, 3)
    }
}
  • onSurfaceCreated 函数中,创建三角形需要的数据和 shader
  • onDrawFrame 函数中,glUseProgram 使用编译好的 shader id;glBindVertexArray 使用设置好的 vao 进行渲染
  • 注意一点,所有 GLES30. 的函数调用都要在渲染线程中操作,也就是说这些函数必须在 onSurfaceCreatedonSurfaceChangedonDrawFrame 中调用,否则渲染失败。

最后,你可以在布局文件中,直接引用 MyOpenGLSurfaceView 作为控件,就像 Button 一样方便,例如

    <com.xinging.opengltest.MyOpenGLSurfaceView
        android:layout_width="match_parent"
        android:layout_height="500dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        />

android opengl绘图,opengl,android,kotlin,opengl

5. 总结

本文介绍了如何在 Android 下使用 OpenGL ES 3.0 绘制三角形。通过自定义 GLSurfaceView 和 GLSurfaceView.Render ,在 Android 下调用 OpenGL API 来实现绘图;本文所有代码在 Android_OpenGLES3_Demo文章来源地址https://www.toymoban.com/news/detail-660230.html


参考

  • Android_OpenGLES3_Demo
  • Android OpenGL开发——图像绘制详解
  • android平台下OpenGL ES 3.0实例详解顶点缓冲区对象(VBO)和顶点数组对象(VAO)
  • NDK OpenGL ES渲染系列 之 OpenGL ES各版本使用GLSL差异

到了这里,关于LearnOpenGL - Android OpenGL ES 3.0 绘制三角形的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • OpenGL入门教程之 三角形练习

     本文章被包含在OpenGL学习专栏中。  本练习基于OpenGL绘制三角形中绘制三角形的代码程序。  1.绘制两个彼此相连的三角形。  2.创建相同的两个三角形,对它们的数据使用不同的VAO和VBO。  3.创建两个着色器程序。使用两个着色器程序分别绘制出一个绿色三角形和黄色三

    2023年04月21日
    浏览(26)
  • 一起学 WebGL:绘制三角形

    大家好,我是前端西瓜哥。画了好几节课的点,这次我们来画三角形了。 三角形可太重要了,再复杂的三维模型都是由一个个小三角形组合而成,越多越精细越真实。 这次绘制三角形,要绘制的点就有三个了,不再是一个。为此我们需要用到缓存区对象(buffer object)。 通过

    2023年04月18日
    浏览(23)
  • 前端开发必备技能!用简单CSS代码绘制三角形,提升用户体验

        🎬 江城开朗的豌豆 :个人主页  🔥 个人专栏  :《 VUE 》 《 javaScript 》  📝  个人网站  :《 江城开朗的豌豆🫛 》  ⛺️ 生活的理想,就是为了理想的生活 ! 目录  ⭐  专栏简介  📘  文章引言 一、前言 二、实现过程 三、原理分析 ⭐  写在最后         欢

    2024年02月07日
    浏览(33)
  • css实现圆角三角形,圆角三角形的实现

    今天给大家带来一个如何实现圆角三角形的方案,这个方案虽然可以实现,但是也是借助拼凑等方式来实现的,假如想一个div来实现圆角三角形,还是比较困难的。之前文章讲了如何实现对话框,里面介绍了三角形的实现方式。今天讲讲如何实现圆角三角形。 想要生成一个带

    2024年02月09日
    浏览(41)
  • 用python写九九乘法表(左上三角、左下三角、右上三角、右下三角、正三角形、倒三角形格式)

    1.左上三角格式:   2.左下三角格式:   3.右上三角格式:     4.右下角格式:     5.倒三角格式:      

    2024年02月11日
    浏览(46)
  • C语言程序设计:输入一个三角形的三条边长,求出三角形的面积。

    已知三角形的三边长a,b,c,则该三角形的面积公式为:           area=  其中s = (a+b+c)/2

    2024年02月06日
    浏览(44)
  • OpenCV项目开发实战-- 将一个三角形变形为另一个三角形 ( C++ / Python )代码实现

     文末附基于Python和C++两种方式实现的测试代码下载链接 图 1:左图中蓝色三角形内的所有像素都已转换为右图中的蓝色三角形。 在本教程中,我们将看到如何将图像中的单个三角形变形为不同图像中的另一个三角形。 在计算机图形学中,人们一直在处理扭曲三角形,因为任

    2024年02月09日
    浏览(64)
  • CSS 画三角形

    1、transform: rotate + overflow: hidden 就是利用BFC的特性,在封闭的盒子里面,以图形的左下角(left bottom)作为旋转中心,进行旋转,把超出部分隐藏、 2、clip-path 剪切 clip-path 可以将一个容器裁剪成任何我们想要的样子 3、border + transparent 设置一个宽高为0的盒子,用边框大小来控

    2024年01月25日
    浏览(40)
  • 【数字三角形】

    题目描述 上图给出了一个数字三角形。从三角形的顶部到底部有很多条不同的路径。对于每条路径,把路径上面的数加起来可以得到一个和,你的任务就是找到最大的和。 路径上的每一步只能从一个数走到下一层和它最近的左边的那个数或者右 边的那个数。此外,向左下走

    2024年02月05日
    浏览(41)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包