This is my first time working with Android and OpenGL ES, but I do have a fair bit of experience with straight OpenGL. I'm simply trying to draw a red triangle. No uniforms, only one vertex attribute.
Vertex shader:
#version 300 es
in vec3 in_loc;
void main() {
gl_Position.xyz = in_loc;
}
Fragment shader:
#version 300 es
precision mediump float;
out vec4 outColor;
void main() {
outColor = vec4(1.0f, 0.0f, 0.0f, 1.0f);
}
Result:
I am checking for and getting no errors during shader creation, compilation, attachment, and linking. I've tried changing precision mediump float;
to precision highp float;
and precision lowp float;
as well.
Usually I'd expect this straight black output to indicate a broken fragment shader, but I'm getting no errors, and as far as I can tell it's very simple code that's identical to various example code found online.
Shader creation code:
public class Shader {
public enum Type { VERTEX, FRAGMENT }
static private int createShader(String src, Type type) {
int shaderHandle = 0;
switch (type) {
case VERTEX:
shaderHandle = GLES30.glCreateShader(GLES30.GL_VERTEX_SHADER);
break;
case FRAGMENT:
shaderHandle = GLES30.glCreateShader(GLES30.GL_FRAGMENT_SHADER);
break;
}
if (shaderHandle == 0) {
Log.e("Shader", "Failed to create shader");
return 0;
}
// Compile shader
GLES30.glShaderSource(shaderHandle, src);
GLES30.glCompileShader(shaderHandle);
// Check for compile errors
int status[] = { 0 };
GLES30.glGetShaderiv(shaderHandle, GLES30.GL_COMPILE_STATUS, status, 0);
if (status[0] == 0) {
Log.e("Shader", "Failed to compile shader");
Log.e("Shader", GLES30.glGetShaderInfoLog(shaderHandle));
GLES30.glDeleteShader(shaderHandle);
return 0;
}
// Check for OpenGL errors
if (Util.isGLError()) {
GLES30.glDeleteShader(shaderHandle);
return 0;
}
return shaderHandle;
}
static private int createProgram(int vertShaderHandle, int fragShaderHandle) {
int programHandle = GLES30.glCreateProgram();
if (programHandle == 0) {
Log.e("Shader", "Failed to create program");
return 0;
}
// Attach and link shaders to form program
GLES30.glAttachShader(programHandle, vertShaderHandle);
GLES30.glAttachShader(programHandle, fragShaderHandle);
GLES30.glLinkProgram(programHandle);
// Check for linking errors
int status[] = { 0 };
GLES30.glGetProgramiv(programHandle, GLES30.GL_LINK_STATUS, status, 0);
if (status[0] == 0) {
Log.e("Shader", "Failed to link program");
Log.e("Shader", GLES30.glGetProgramInfoLog(programHandle));
GLES30.glDeleteProgram(programHandle);
return 0;
}
// Check for OpenGL errors
if (Util.isGLError()) {
GLES30.glDeleteProgram(programHandle);
return 0;
}
return programHandle;
}
private String name;
private int programHandle;
public Shader(String name) {
this.name = name;
this.programHandle = 0;
}
public boolean initFromSrc(String vertSrc, String fragSrc) {
// Create shaders
int vertShaderHandle = createShader(vertSrc, Type.VERTEX);
if (vertShaderHandle == 0) {
Log.e("Shader", "Failed to create vertex shader");
return false;
}
int fragShaderHandle = createShader(fragSrc, Type.FRAGMENT);
if (fragShaderHandle == 0) {
Log.e("Shader", "Failed to create fragment shader");
GLES30.glDeleteShader(vertShaderHandle);
return false;
}
// Create shader program
programHandle = createProgram(vertShaderHandle, fragShaderHandle);
if (programHandle == 0) {
Log.e("Shader", "Failed to create program");
GLES30.glDeleteShader(vertShaderHandle);
GLES30.glDeleteShader(fragShaderHandle);
return false;
}
// Shaders can now be deleted
GLES30.glDeleteShader(vertShaderHandle);
GLES30.glDeleteShader(fragShaderHandle);
return true;
}
public boolean initFromFile(String vertFile, String fragFile) {
String vertSrc = Util.readTextFile(vertFile);
if (vertSrc == null) {
Log.e("Shader", "Failed to read shader file: " + vertFile);
return false;
}
String fragSrc = Util.readTextFile(fragFile);
if (fragSrc == null) {
Log.e("Shader", "Failed to read shader file: " + fragFile);
return false;
}
return initFromSrc(vertSrc, fragSrc);
}
public int getProgramHandle() { return programHandle; }
}
Everything else:
@Override
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
GLES30.glClearColor(0.0f, 0.5f, 1.0f, 1.0f);
shader = new Shader("basic");
if (!shader.initFromFile("shaders/basic.vert", "shaders/basic.frag")) {
Log.e("MainRenderer", "Failed to initialize basic shader");
}
float[] locs = {
0.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f,
1.0f, 1.0f, 0.0f
};
ByteBuffer locsBB = ByteBuffer.allocateDirect(locs.length * 4);
locsBB.order(ByteOrder.nativeOrder());
FloatBuffer locsFB = locsBB.asFloatBuffer();
locsFB.put(locs);
int[] vboArr = { 0 };
GLES30.glGenBuffers(1, vboArr, 0);
int vboHandle = vboArr[0];
GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vboHandle);
GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER, locs.length * 4, locsBB, GLES30.GL_STATIC_DRAW);
GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0);
int[] indices = {
0, 1, 2
};
ByteBuffer indicesBB = ByteBuffer.allocateDirect(indices.length * 4);
indicesBB.order(ByteOrder.nativeOrder());
IntBuffer indicesIB = indicesBB.asIntBuffer();
indicesIB.put(indices);
int[] iboArr = { 0 };
GLES30.glGenBuffers(1, iboArr, 0);
int iboHandle = iboArr[0];
GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, iboHandle);
GLES30.glBufferData(GLES30.GL_ELEMENT_ARRAY_BUFFER, indices.length * 4, indicesBB, GLES30.GL_STATIC_DRAW);
GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, 0);
int[] vaoArr = { 0 };
GLES30.glGenVertexArrays(1, vaoArr, 0);
vaoHandle = vaoArr[0];
if (vaoHandle == 0) {
Log.e("MainRenderer", "Failed to create vao");
}
GLES30.glBindVertexArray(vaoHandle);
int locationAI = GLES30.glGetAttribLocation(shader.getProgramHandle(), "in_loc");
GLES30.glEnableVertexAttribArray(locationAI);
GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vboHandle);
GLES30.glVertexAttribPointer(locationAI, 3, GLES30.GL_FLOAT, false, 0, 0);
GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, iboHandle);
GLES30.glBindVertexArray(0);
GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0);
GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, 0);
}
@Override
public void onDrawFrame(GL10 unused) {
// Redraw background color
GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);
// Draw triangle
GLES30.glBindVertexArray(vaoHandle);
GLES30.glDrawElements(GLES30.GL_TRIANGLES, 3, GLES30.GL_UNSIGNED_INT, 0);
GLES30.glBindVertexArray(0);
}