Issue
I am attempting to write a very simple Android application that uses OpenGL to display a green background and render a single cyan triangle over it, to verify that my code properly displays some geometry. I am running into an issue where the call to glClearColor/glClear
to set the background is visible and makes the view green as intended, but I see no visible change from calling glDrawArrays
after pointing the 0th attribute array to a FloatBuffer
containing vertex coords. All my vertex shader does is pass the position as a vec4
to the fragment shader, which always just sets the output color to cyan, so I would expect to see the view green with one cyan triangle, but instead I see only the green background, and I am unsure why.
Main activity file (imports omitted):
setupDefaultProgram()
}
override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
GLES30.glViewport(0, 0, width, height)
}
override fun onDrawFrame(gl: GL10?) {
glRenderer.renderFunc()
}
}
Renderer code:
class GlRenderer {
companion object {
// Pass through position and UV values
val vertexSource = """
#version 300 es
in vec2 position;
void main() {
gl_Position = vec4(position, -0.5, 1.0);
}
""".trimIndent()
// Eventually get the texture value, for now, just make it cyan so I can see it
val fragmentSource = """
#version 300 es
precision mediump float;
out vec4 fragColor;
void main() {
fragColor = vec4(0.0, 1.0, 1.0, 1.0);
}
""".trimIndent()
}
private var vertexBuffer: FloatBuffer
private var defaultProgram: Int = -1
private var vertexLocation: Int = -1
private fun checkGlError(msg: String) {
val errCodeEgl = EGL14.eglGetError()
val errCodeGl = GLES30.glGetError()
if (errCodeEgl != EGL14.EGL_SUCCESS || errCodeGl != GLES30.GL_NO_ERROR) {
throw RuntimeException(
"$msg - $errCodeEgl(${GLU.gluErrorString(errCodeEgl)}) : $errCodeGl(${
GLU.gluErrorString(
errCodeGl
)
})"
)
}
}
init {
// Flat square
// Am I allocating and writing to these correctly?
val vertices = floatArrayOf(-1f, -1f, 1f, -1f, -1f, 1f, 1f, 1f)
vertexBuffer = ByteBuffer.allocateDirect(vertices.size * 4).asFloatBuffer().also {
it.put(vertices)
it.position(0)
}
}
fun setupDefaultProgram() {
defaultProgram = makeProgram(
mapOf(
GLES30.GL_VERTEX_SHADER to vertexSource,
GLES30.GL_FRAGMENT_SHADER to fragmentSource
)
)
vertexLocation = GLES30.glGetAttribLocation(defaultProgram, "position")
checkGlError("Getting uniform")
}
private fun compileShader(source: String, shaderType: Int): Int {
val shaderId = GLES30.glCreateShader(shaderType)
checkGlError("Create shader")
if (shaderId == 0) {
Log.e("Native", "Could not create shader for some reason")
checkGlError("Could not create shader")
}
GLES30.glShaderSource(shaderId, source)
checkGlError("Setting shader source")
GLES30.glCompileShader(shaderId)
val statusBuffer = IntArray(1)
GLES30.glGetShaderiv(shaderId, GLES30.GL_COMPILE_STATUS, statusBuffer, 0)
val shaderLog = GLES30.glGetShaderInfoLog(shaderId)
Log.d("Native", "Compiling shader #$shaderId : $shaderLog")
if (statusBuffer[0] == 0) {
GLES30.glDeleteShader(shaderId)
checkGlError("Failed to compile shader $shaderId")
}
return shaderId
}
private fun makeProgram(sources: Map<Int, String>): Int {
val program = GLES30.glCreateProgram()
checkGlError("Create program")
sources.forEach {
val shader = compileShader(it.value, it.key)
GLES30.glAttachShader(program, shader)
}
val linkBuffer = IntArray(1)
GLES30.glLinkProgram(program)
GLES30.glGetProgramiv(program, GLES30.GL_LINK_STATUS, linkBuffer, 0)
if (linkBuffer[0] == 0) {
GLES30.glDeleteProgram(program)
checkGlError("Failed to link program $program")
}
return program
}
// Called to actually draw to the surface. When fully implemented it should draw whatever is
// on the associated texture, but for now, to debug, I just want to verify I can draw vertices,
// but it seems I cannot?
fun renderFunc() {
GLES30.glClearColor(0f, 1f, 0.5f, 1f)
GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)
checkGlError("Clearing")
GLES30.glUseProgram(defaultProgram)
checkGlError("Use program")
GLES30.glEnableVertexAttribArray(vertexLocation)
vertexBuffer.position(0)
FloatArray(2 * 4).apply {
vertexBuffer.get(this)
vertexBuffer.position(0)
Log.d("Native", "Vertex buffer ${contentToString()}")
}
GLES30.glVertexAttribPointer(vertexLocation, 2, GLES30.GL_FLOAT, false, 0, vertexBuffer)
checkGlError("Attribute 0")
// Just render a triangle
GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, 3)
GLES30.glFinish()
checkGlError("Finished GL")
}
}
My debug output logs what I would expect:
D/Native: Compiling shader #2 :
D/Native: Compiling shader #3 :
D/Native: Vertex buffer [-1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0]
I am including <uses-feature android:glEsVersion="0x00020000" android:required="true" />
in my manifest. Where am I going wrong that the geometry of the triangle is either not rendered to the screen or is not visible?
Solution
When you create Buffer
for GL, you need to specify the native byte-order
.
val vertices = floatArrayOf(-1f, -1f, 1f, -1f, -1f, 1f, 1f, 1f)
vertexBuffer = ByteBuffer.allocateDirect(vertices.size * 4).order(ByteOrder.nativeOrder()).asFloatBuffer().also {
it.put(vertices)
it.position(0)
}
Answered By - ardget
Answer Checked By - Cary Denson (JavaFixing Admin)