0
votes

I successfully followed this guide to draw a triangle with OpenGL, and now I'm trying to modify the code to draw a circle. The tutorial didn't instruct me how to do this so I tried using the code from here, halfway down the page under "For your toolbox." The approach I'm taking is to define a square in OpenGL, then like what was suggested here, use the fragment shader to draw a circle within the square. Here's the code I have for the Circle class:

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)
        }
    }
}

However, running the app only yields a white triangle in the center of the screen. Its vertices are in the top left, bottom left, and lower right, so it seems like OpenGL is drawing only one of the triangles required for the square.

I noticed that the drawOrder and drawListBuffer variables from this section were never used in the next part of the tutorial; they only focused on drawing the triangle and not the square. Maybe that has something to do with it?

1

1 Answers

0
votes

I don't want to rewrite your program but you are missing some steps. You need to create a VAO and two VBO. The first VBO can be the vertices and the second VBO can be the texture coordinates (I did not include code for this, check some tutorials). Bind the buffers and put in the data then call glVertexAttribPointer to each buffer. Be sure to load your texture:

GLES30.glGenTextures(1, textureHandle);

and bind it when rendering.

GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureID);

In the vert shader you can do:

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

//main method
tc = texCoord;

In frag shader you can do:

uniform sampler2D boundTexture;
in vec2 tc;

//main method
vec4 textColor = texture(boundTexture,tc);
color = textColor;

On a side before note before gldrawarrays check vertexCount it needs to be 6 for two triangles, I have a feeling its currently 3. Hope this helps.