代码之家  ›  专栏  ›  技术社区  ›  James Ko

在Android上用OpenGL绘制圆

  •  0
  • James Ko  · 技术社区  · 6 年前

    我成功地跟踪了 this guide 用OpenGL画一个三角形,现在我试图修改代码来画一个圆。教程没有教我怎么做,所以我试着使用 here ,在页面的“For your toolbox”部分,我采用的方法是在OpenGL中定义一个正方形,然后按照建议 here ,使用片段着色器在正方形内绘制圆。这是我的圆类代码:

    package com.example.onwards
    
    import android.content.res.Resources
    import android.opengl.GLES30
    import java.nio.ByteBuffer
    import java.nio.ByteOrder
    import java.nio.FloatBuffer
    
    class Circle {
        private val vertexCoords = floatArrayOf(
            -0.5f, 0.5f, 0.0f, // top left
            -0.5f, -0.5f, 0.0f, // bottom left
            0.5f, -0.5f, 0.0f, // bottom right
            0.5f, 0.5f, 0.0f // top right
        )
    
        private val resolution = floatArrayOf(
            getScreenWidth().toFloat(),
            getScreenHeight().toFloat()
        )
    
        private val vertexShaderCode =
            """ |attribute vec4 vPosition;
                |void main() {
                |    gl_Position = vPosition;
                |}
                |""".trimMargin()
    
        private val fragmentShaderCode =
            """ |precision mediump float;
                |
                |uniform vec2 vResolution;
                |
                |float circle(in vec2 st, in float r) {
                |   vec2 dist = st - vec2(0.5);
                |   return 1. - step(r * r, dot(dist, dist));
                |}
                |
                |void main() {
                |   vec2 st = gl_FragCoord.xy / vResolution.xy;
                |   vec3 color = vec3(circle(st, 0.5));
                |   // TODO: Set alpha to 0 outside of circle
                |   gl_FragColor = vec4(color, 1.);
                |}
                |""".trimMargin()
    
        private val vertexBuffer: FloatBuffer =
            ByteBuffer.allocateDirect(vertexCoords.size * 4).run {
                order(ByteOrder.nativeOrder())
                asFloatBuffer().apply {
                    put(vertexCoords)
                    position(0)
                }
            }
    
        private val vertexCount: Int = vertexCoords.size / COORDS_PER_VERTEX
        private val vertexStride: Int = COORDS_PER_VERTEX * 4
    
        private var program: Int
        private var positionHandle: Int = 0
    
        init {
            val vertexShader: Int = loadShader(GLES30.GL_VERTEX_SHADER, vertexShaderCode)
            val fragmentShader: Int = loadShader(GLES30.GL_FRAGMENT_SHADER, fragmentShaderCode)
    
            program = GLES30.glCreateProgram().also {
                GLES30.glAttachShader(it, vertexShader)
                GLES30.glAttachShader(it, fragmentShader)
                GLES30.glLinkProgram(it)
            }
        }
    
        private fun loadShader(type: Int, shaderCode: String): Int {
            return GLES30.glCreateShader(type).also { shader ->
                GLES30.glShaderSource(shader, shaderCode)
                GLES30.glCompileShader(shader)
            }
        }
    
        private fun getScreenWidth(): Int {
            return Resources.getSystem().displayMetrics.widthPixels
        }
    
        private fun getScreenHeight(): Int {
            return Resources.getSystem().displayMetrics.heightPixels
        }
    
        fun draw() {
            GLES30.glUseProgram(program)
    
            positionHandle = GLES30.glGetAttribLocation(program, "vPosition").also {
                GLES30.glEnableVertexAttribArray(it)
                GLES30.glVertexAttribPointer(
                    it,
                    COORDS_PER_VERTEX,
                    GLES30.GL_FLOAT,
                    false,
                    vertexStride,
                    vertexBuffer
                )
    
                GLES30.glGetUniformLocation(program, "vResolution").also { resHandle ->
                    GLES30.glUniform2fv(resHandle, 1, resolution, 0)
                }
    
                GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, vertexCount)
                GLES30.glDisableVertexAttribArray(it)
            }
        }
    }
    

    但是,运行这个应用程序只会在屏幕中央产生一个白色三角形。它的顶点在左上角、左下角和右下角,所以OpenGL似乎只绘制了正方形所需的一个三角形。

    我注意到 drawOrder drawListBuffer 变量来自 this section 在本教程的下一部分中从未使用过;它们只专注于绘制三角形而不是正方形。也许这和它有关?

    0 回复  |  直到 6 年前
        1
  •  0
  •   vasmos    6 年前

    我不想重写你的程序,但你遗漏了一些步骤。你需要创建一个VAO和两个VBO。第一个VBO可以是顶点,第二个VBO可以是纹理坐标(我没有为此包含代码,请查看一些教程)。绑定缓冲区并放入数据,然后调用每个缓冲区的glVertexAttribPointer。确保加载纹理:

    GLES30.glGenTextures(1, textureHandle);
    

    渲染时绑定。

    GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureID);
    

    在“顶点”明暗器中,可以执行以下操作:

    layout(location=0) in vec3 position;
    layout(location=1) in vec2 texCoord;
    out vec2 tc;
    
    //main method
    tc = texCoord;
    

    在frag着色器中,可以执行以下操作:

    uniform sampler2D boundTexture;
    in vec2 tc;
    
    //main method
    vec4 textColor = texture(boundTexture,tc);
    color = textColor;
    

    在gldrawarrays检查vertexCount之前的一个边上,两个三角形需要6,我有一种感觉,它当前是3。希望这有帮助。