How to debug OpenglES shader errors on android? - java

I'm building an opengl es app for android and I have some issues compiling the shaders. Does anyone know how I could get the shaderInfoLog displayed on the screen?
Here's some code for better comprehension:
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
surfaceView=new GLSurfaceView(this);
Renderer renderer = new Renderer();
surfaceView.setEGLContextClientVersion(2);
surfaceView.setRenderer(renderer);
setContentView(surfaceView);
}
}
Renderer class
public class Renderer implements GLSurfaceView.Renderer{
private Object tri;
private int glError = 0;
Renderer(){
}
public void onSurfaceCreated(GL10 gl,EGLConfig config){
float[] positions = //simple quad vertices
{0.5f,0.5f,0.0f,
0.5f,-0.5f,0.0f,
-0.5f,-0.5f,0.0f,
-0.5f,0.5f,0.0f};
int[] index = {1,2,4,2,4,3}; //simple quad indices
ObjectLoader loader = new ObjectLoader();
tri = loader.makeObject(positions, index);
}
public void onSurfaceChanged(GL10 gl, int width, int height){
GLES20.glViewport(0,0,width,height);
}
public void onDrawFrame(GL10 gl){
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); //here I debug opengl errors as different colored backgrounds
GLES20.glClearColor(1.0f,1.0f,1.0f,1.0f);
if(glError == GLES20.GL_NO_ERROR){
GLES20.glClearColor(1.0f,0.0f,0.0f,1.0f);
}else if(glError == GLES20.GL_INVALID_ENUM){
GLES20.glClearColor(0.0f,1.0f,0.0f,1.0f);
}else if(glError == GLES20.GL_INVALID_FRAMEBUFFER_OPERATION){
GLES20.glClearColor(0.0f,0.0f,1.0f,1.0f);
}else if(glError == GLES20.GL_INVALID_OPERATION){
GLES20.glClearColor(1.0f,1.0f,0.0f,1.0f);
}else if(glError == GLES20.GL_INVALID_VALUE){
GLES20.glClearColor(0.0f,1.0f,1.0f,1.0f);
}else if(glError == GLES20.GL_OUT_OF_MEMORY){
GLES20.glClearColor(1.0f,0.0f,1.0f,1.0f);
}
render(tri);
}
private void render(Object obj){
GLES30.glBindVertexArray(obj.vaoID);
GLES30.glEnableVertexAttribArray(0);
obj.shader.start();
GLES30.glDrawElements(GLES20.GL_TRIANGLES, obj.vertcount, GLES20.GL_UNSIGNED_INT, 0);
obj.shader.stop();
GLES30.glDisableVertexAttribArray(0);
GLES30.glBindVertexArray(0);
//get the errors for debug on the next frame
glError = GLES20.glGetError();
}
}
Object class
public class Object {
public int vaoID;
public int vertcount;
public StaticShader shader;
Object(int id, int count, StaticShader s){
vaoID = id;
vertcount = count;
shader = s;
}
}
ObjectLoader class
public class ObjectLoader {
ObjectLoader(){}
public Object makeObject(float[] positions,int[] indices){
int vaoID = CreateVAO();
bindIndicesBuffer(indices);
storeDataInVao(0,positions,vaoID);
unbindVao();
StaticShader shader = new StaticShader();
return new Object(vaoID,indices.length/3, shader);
}
private int CreateVAO(){
int[] vaoID = new int[1];
GLES30.glGenVertexArrays(1,vaoID,0);
GLES30.glBindVertexArray(vaoID[0]);
return vaoID[0];
}
private void storeDataInVao(int index,float[] data, int vaoID){
int[] vboID = new int[1];
GLES20.glGenBuffers(1,vboID,0);
GLES30.glBindBuffer(GLES20.GL_ARRAY_BUFFER,vboID[0]);
FloatBuffer floatbuffer = makeFloatBuffer(data);
GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER,data.length*4,floatbuffer,GLES30.GL_STATIC_DRAW);
GLES30.glVertexAttribPointer(index, 3, GLES30.GL_FLOAT,false,0,0);
GLES30.glBindBuffer(GLES20.GL_ARRAY_BUFFER,0);
}
private void bindIndicesBuffer(int[] indices){
int[] vboID = new int[1];
GLES20.glGenBuffers(1,vboID,0);
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER,vboID[0]);
IntBuffer buffer = makeIntBuffer(indices);
GLES20.glBufferData(GLES20.GL_ELEMENT_ARRAY_BUFFER,indices.length,buffer,GLES20.GL_STATIC_DRAW);
}
private IntBuffer makeIntBuffer(int[] data){
IntBuffer buffer = ByteBuffer.allocateDirect(data.length*4).order(ByteOrder.nativeOrder()).asIntBuffer();
buffer.put(data);
buffer.flip();
return buffer;
}
private FloatBuffer makeFloatBuffer(float[] array){
FloatBuffer floatbuffer = ByteBuffer.allocateDirect(array.length*4).order(ByteOrder.nativeOrder()).asFloatBuffer();
floatbuffer.put(array);
floatbuffer.flip();
return floatbuffer;
}
private void unbindVao(){
GLES30.glBindVertexArray(0);
}
}
StaticShader just calls the constructor of ShaderProgram, so I'll just put ShaderProgram
public abstract class ShaderProgram {
private int programID;
private int vertexShader;
private int fragmentShader;
public String shaderError = "none";
private String vertCode = "void main(){gl_Position = vec4(0.0,0.0,0.0,1.0);}";
private String fragCode = "void main(){gl_FragColor = vec4(1.0,1.0,1.0,1.0);}";
ShaderProgram(){
vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertCode);
fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragCode);
programID = GLES20.glCreateProgram();
GLES20.glAttachShader(programID, fragmentShader);
GLES20.glAttachShader(programID, vertexShader);
GLES20.glLinkProgram(programID);
GLES20.glValidateProgram(programID);
}
abstract void bindAttributes();
private int loadShader(int type, String code){
int shader = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);
if(shader == 0){
System.exit(-1);
}
GLES20.glShaderSource(shader, code);
if(GLES20.glGetError() == GLES20.GL_INVALID_OPERATION){
//System.exit(-1);
}
GLES20.glCompileShader(shader);
final int[] compileStatus = new int[1];
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compileStatus, 0);
if (compileStatus[0] == 0)
{
shaderError = GLES20.glGetShaderInfoLog(shader);
GLES20.glDeleteShader(shader);
shader = 0;
//System.exit(-1);
}
return shader;
}
public void start(){
GLES20.glUseProgram(programID);
}
public void stop(){
GLES20.glUseProgram(0);
}
}
My goal is to display ShaderProgram.shaderError on the screen by any means.
I think I should also say that GLSurfaceView isn't initiated until MainActivity.onCreate() is done, that GLSurfaceView is on a different thread to MainActivity and that GLSurfaceView doesn't have access to MainActivity's context, making it impossible to use Toast.makeText().
For those who don't want to read the entire code, here's the hierachy:
MainActivity - creates a thread for GLSurfaceView that isn't initiated before MainActivity is done--> GLSurfaceView
GLSurfaceView
|
Renderer
|
ObjectLoader
|
StaticShader+Object
|
ShaderProgram

You always create a shader object with the type GLES20.GL_FRAGMENT_SHADER. Use the type argument instead of the constant GLES20.GL_FRAGMENT_SHADER:
int shader = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);
int shader = GLES20.glCreateShader(type);

This is an old post of mine, but I ultimatly found the solution (in Java):
//shader is the shader ID returned by GLES30.glCreateShader();
final int[] compileStatus = new int[1];
GLES30.glGetShaderiv(shader, GLES30.GL_COMPILE_STATUS, compileStatus, 0);
if(compileStatus[0] == 0){
System.err.println("[HERE ->] " + GLES30.glGetShaderInfoLog(shader));
Log.e("Shader Source : ", GLES30.glGetShaderSource(shader));
}
It will print the shader error and the source code afterwards

Related

LWJGL 3 - After the shader is compiled and mesh is made, the triangle still doesn't draw

I am currently trying to make an engine by using LWJGL3, and I am having issues trying to render a triangle.
I first make the window, make the window current context.
Then I make the renderer, which has the meshes.
Meshes have shaders attached to them. By init() the shaders compile and link.
Shader file (I have a parser, which takes fragment/vertex separately. The compilation completes without error, I do have error checking if it fails to compile).
#version 330
layout (location=0) in vec3 aPos;
layout (location=1) in vec4 aColor;
out vec4 fragment;
void main(){
fragment = aColor;
gl_Position = vec4(aPos,1.0);
}
//type fragment
#version 330
in vec4 fColor;
out vec4 color;
void main(){
color = fColor;
}
After this, I try to draw the mesh, it succeeds so, but in the window, I only get a gray color, which is a color I passed as an argument to start the window by that color.
Where could I be going wrong?
Mesh code:
package graphics;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import static org.lwjgl.opengl.GL33.*;
public class Mesh {
int positionSize = 3;
int colorSize = 4;
int floatSizeInBytes = 4;
int vertexSizeInBytes = (positionSize + colorSize) * floatSizeInBytes;
private final Vertex[] vertices;
private final int[] indices;
private int vertexArrayObject;
private int vertexBufferObject;
private int elementArrayObject;
private int vertexCount;
private Shader shader;
public Mesh(Vertex[] vertices, int[] indices, Shader shader) {
this.shader = shader;
this.vertices = vertices;
this.indices = indices;
}
public void init(){
this.shader.compileAndLink();
this.shader.bind();
vertexCount = indices.length;
vertexArrayObject = glGenVertexArrays();
vertexBufferObject = glGenBuffers();
elementArrayObject = glGenBuffers();
glBindVertexArray(vertexArrayObject);
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObject);
glBufferData(GL_ARRAY_BUFFER, flippedBuffer(vertices), GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementArrayObject);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, flippedBuffer(indices), GL_STATIC_DRAW);
glVertexAttribPointer(0, positionSize, GL_FLOAT, false, vertexSizeInBytes, 0);
glVertexAttribPointer(1, colorSize, GL_FLOAT, false, vertexSizeInBytes, (long) positionSize * floatSizeInBytes);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glBindVertexArray(0);
}
public void render() {
bind();
glDrawElements(GL_TRIANGLES, vertexCount, GL_UNSIGNED_INT, 0);
unbind();
System.out.println("Rendered");
}
public void clear() {
glDeleteBuffers(vertexBufferObject);
glDeleteVertexArrays(vertexArrayObject);
shader.unbind();
}
public void bind() {
glBindVertexArray(vertexArrayObject);
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObject);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementArrayObject);
}
public void unbind() {
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
public void stop() {
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glBindVertexArray(0);
shader.unbind();
}
public void setShader(Shader shader) {
try {
stop();
} catch (Exception e) {
System.out.println("Shader could not be stopped or not running");
e.printStackTrace();
}
this.shader = shader;
this.shader.bind();
}
public int getVertexArrayObject() {
return vertexArrayObject;
}
public int getVertexBufferObject() {
return vertexBufferObject;
}
public int getVertexCount() {
return vertexCount;
}
public int getElementArrayObject() {
return elementArrayObject;
}
public FloatBuffer flippedBuffer(Vertex[] vertices) {
FloatBuffer buffer = FloatBuffer.allocate(vertices.length * 7);
for (Vertex vertex : vertices) {
buffer.put(vertex.getPosition().coordinateArray());
buffer.put(vertex.getColor().coordinateArray());
}
buffer.flip();
return buffer;
}
public IntBuffer flippedBuffer(int[] elements) {
IntBuffer buffer = IntBuffer.allocate(elements.length);
for (int element : elements) {
buffer.put(element);
}
buffer.flip();
return buffer;
}
}
Shader code:
package graphics;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import static org.lwjgl.opengl.GL33.*;
public class Shader {
private int shaderProgrammeID;
private String vertexSource;
private String fragmentSource;
private final String filePath;
public Shader(String filePath) {
this.filePath = filePath;
try {
String source = new String(Files.readAllBytes(Paths.get(filePath)));
String[] splitString = source.split("(//type)( )+([a-zA-Z]+)");
int index = source.indexOf("//type") + 6;
int endOfLine = source.indexOf("\r\n", index);
String firstPattern = source.substring(index, endOfLine).trim();
index = source.indexOf("//type", endOfLine) + 6;
endOfLine = source.indexOf("\r\n", index);
String secondPattern = source.substring(index, endOfLine).trim();
if (firstPattern.equals("vertex")) {
vertexSource = splitString[1];
} else if (firstPattern.equals("fragment")) {
fragmentSource = splitString[1];
} else {
throw new IOException("Unexpected shader type " + firstPattern + "'");
}
if (secondPattern.equals("vertex")) {
vertexSource = splitString[2];
} else if (secondPattern.equals("fragment")) {
fragmentSource = splitString[2];
} else {
throw new IOException("Unexpected shader type " + secondPattern + "'");
}
} catch (IOException e) {
e.printStackTrace();
assert false : "Error: Could not Open Shader" + filePath + "'";
}
}
public void compileAndLink() {
int vertexID;
int fragmentID;
vertexID = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexID, vertexSource);
glCompileShader(vertexID);
if (glGetShaderi(vertexID, GL_COMPILE_STATUS) == GL_FALSE) {
System.out.println("ERROR: vertex shader - " + filePath + "could not be be compiled");
System.out.println(glGetShaderInfoLog(vertexID, glGetShaderi(vertexID, GL_INFO_LOG_LENGTH)));
assert false : "";
}
fragmentID = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentID, fragmentSource);
glCompileShader(fragmentID);
if (glGetShaderi(fragmentID, GL_COMPILE_STATUS) == GL_FALSE) {
System.out.println("ERROR: fragment shader - " + filePath + "could not be be compiled");
System.out.println(glGetShaderInfoLog(fragmentID, glGetShaderi(fragmentID, GL_INFO_LOG_LENGTH)));
assert false : "";
}
this.shaderProgrammeID = glCreateProgram();
glAttachShader(this.shaderProgrammeID, vertexID);
glAttachShader(this.shaderProgrammeID, fragmentID);
glLinkProgram(this.shaderProgrammeID);
if (glGetProgrami(this.shaderProgrammeID, GL_LINK_STATUS) == GL_FALSE) {
System.out.println("ERROR: " + filePath + " shaders could not be linked");
System.out.println(glGetProgramInfoLog(this.shaderProgrammeID, glGetProgrami(fragmentID, GL_INFO_LOG_LENGTH)));
assert false : "";
}
System.out.println("Shader " + filePath + " compiled and linked successfully");
}
public void bind() {
glUseProgram(shaderProgrammeID);
}
public void unbind() {
glUseProgram(0);
}
}
Here is the renderer object, I use this to encapsulate Meshes to render them.
package graphics;
import java.util.HashSet;
import static org.lwjgl.opengl.GL33.*;
public class Renderer {
private final HashSet<Mesh> meshes;
public Renderer() {
meshes = new HashSet<>();
}
public void init() {
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
glFrontFace(GL_CW);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
for (Mesh mesh : meshes) {
mesh.init();
}
}
public void render() {
clear();
for (Mesh mesh : meshes) {
mesh.render();
}
}
public void addMesh(Mesh mesh) {
meshes.add(mesh);
}
public void removeMesh(Mesh mesh) {
meshes.remove(mesh);
}
public void clear() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
public void destroy() {
for (Mesh mesh : meshes) {
mesh.unbind();
mesh.clear();
}
}
}
Engine code:
package core;
import graphics.Mesh;
import graphics.Renderer;
import io.Input;
import math.Vector4f;
import utils.Time;
public class Engine implements Runnable {
public static final int TARGET_FPS = 30;
private final Thread thread;
private final MainBehaviour behaviour;
private final Window window;
public Engine(Window window, MainBehaviour mainBehaviour) {
this.thread = new Thread(this, "loop");
this.window = window;
this.behaviour = mainBehaviour;
}
public void init() throws Exception {
Time.init(TARGET_FPS);
window.init();
behaviour.init();
}
public void start() {
thread.start();
}
#Override
public void run() {
try {
init();
loop();
clear();
} catch (Exception exception) {
exception.printStackTrace();
}
}
public void clear() {
behaviour.clear();
window.destroy();
}
private void update() {
behaviour.update();
/*Updating Time */
Time.updateFps();
Time.updateCycle();
/* Input handling */
Input.update();
window.update();
}
private void render() {
behaviour.render(window);
}
public void loop() {
while (window.isRunning()) {
/* Updating delta Time for correct interval Calculation */
Time.updateDeltaTime();
/* Rendering and actually updating Game */
while (Time.checkCycle()) {
update();
}
render();
System.out.println(Time.getFps());
if (!window.isVSync()) {
Time.sync();
}
}
}
}
Window code:
package core;
import graphics.Renderer;
import io.Input;
import math.Vector4f;
import org.lwjgl.glfw.GLFWErrorCallback;
import org.lwjgl.glfw.GLFWVidMode;
import org.lwjgl.opengl.GL;
import static org.lwjgl.glfw.Callbacks.glfwFreeCallbacks;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT;
import static org.lwjgl.opengl.GL11C.*;
import static org.lwjgl.system.MemoryUtil.NULL;
public class Window {
private final Vector4f color;
private int height;
private int width;
private long window;
private long monitor;
private boolean isFullScreen;
private boolean isVSync;
private String name;
private GLFWVidMode mode;
private final Renderer renderer;
public Window(int width, int height, String name, Vector4f color, boolean isVSync, Renderer renderer) {
this.height = height;
this.width = width;
this.name = name;
this.color = color;
this.isVSync = isVSync;
this.renderer = renderer;
}
public Window(int width, int height, String name, String monitor, Vector4f color, Renderer renderer) {
this.height = height;
this.width = width;
this.name = name;
this.color = color;
if (monitor.equals("primary")) {
this.monitor = glfwGetPrimaryMonitor();
mode = glfwGetVideoMode(glfwGetPrimaryMonitor());
}
this.renderer = renderer;
}
public Renderer getRenderer() {
return renderer;
}
public boolean isVSync() {
return isVSync;
}
public void setVSync(boolean vSync) {
this.isVSync = vSync;
}
public boolean isFullScreen() {
return isFullScreen;
}
public void setFullScreen(boolean fullScreen) {
isFullScreen = fullScreen;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void init() {
if (!glfwInit()) {
throw new IllegalStateException("GLFW not initialized or initialization failed");
} else {
GLFWErrorCallback.createPrint(System.err).set();
try {
if (monitor != NULL) {
this.monitor = glfwGetPrimaryMonitor();
mode = glfwGetVideoMode(glfwGetPrimaryMonitor());
window = glfwCreateWindow(width, height, name, glfwGetPrimaryMonitor(), NULL);
isFullScreen = true;
} else {
window = glfwCreateWindow(width, height, name, NULL, NULL);
}
if (window == NULL) {
throw new IllegalStateException("Failed to create the GLFW window");
}
} catch (RuntimeException e) {
System.out.println(e.getMessage());
}
}
GLFWErrorCallback.createPrint(System.err).set();
glfwDefaultWindowHints();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
glfwMakeContextCurrent(this.window);
GL.createCapabilities();
glfwShowWindow(window);
Input.init();
setupCallback();
renderer.init();
}
private void setupCallback() {
glfwSetKeyCallback(window, Input.getKeyboard());
glfwSetMouseButtonCallback(window, Input.getMbtn());
glfwSetCursorPosCallback(window, Input.getMouse());
}
public void destroy() {
renderer.destroy();
glfwFreeCallbacks(window);
glfwDestroyWindow(window);
glfwTerminate();
}
public void setWindowed(int width, int height, int x, int y) {
glfwSetWindowMonitor(window, NULL, x, y, width, height, mode.refreshRate());
}
public void fullScreenWindow() {
glfwSetWindowMonitor(window, NULL, 0, 0, mode.width(), mode.height(), mode.refreshRate());
}
public void fullScreen() {
glfwSetWindowMonitor(window, monitor, 0, 0, mode.width(), mode.height(), 0);
}
public void update() {
if (!isRunning()) {
destroy();
return;
}
renderer.render();
clear();
glfwSwapBuffers(window);
glfwPollEvents();
}
public void clear() {
glClearColor(color.x, color.y, color.z, color.w);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
public boolean isRunning() {
return !glfwWindowShouldClose(window);
}
}
I render in update()
And the main class where I call the whole thing to run:
import core.Engine;
import core.MainBehaviour;
import core.Window;
import graphics.Mesh;
import graphics.Renderer;
import graphics.Shader;
import graphics.Vertex;
import io.Input;
import math.Vector3f;
import math.Vector4f;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_SPACE;
public class Preview {
public static void main(String[] args) {
MainBehaviour game = new MainBehaviour() {
#Override
public void init() throws Exception {
}
#Override
public void update() {
if (Input.isKeyDown(GLFW_KEY_SPACE))
System.out.println("Space button pushed");
}
#Override
public void render(Window window) {
}
#Override
public void clear() {
}
};
Vertex[] vertices = new Vertex[]{
new Vertex(new Vector3f(-1f, -1f, 0f), new Vector4f(1f, 0f, 0f, 1f)),
new Vertex(new Vector3f(0f, 1f, 0f), new Vector4f(0f, 1f, 0f, 1f)),
new Vertex(new Vector3f(1f, -1f, 0f), new Vector4f(0f, 0f, 1f, 1f)),
};
int[] indices = new int[]{
0, 1, 2
};
Shader shader = new Shader("res/shaders/default.glsl");
Mesh mesh = new Mesh(vertices, indices, shader);
Renderer renderer = new Renderer();
renderer.addMesh(mesh);
Window window = new Window(500, 500, "Preview", new Vector4f(0.5f, 0.5f, 0.5f, 1f), false, renderer);
Engine engine = new Engine(window, game);
engine.run();
}
}
The vertex shader color output name is fragment, but the fragment shader input name is fColor. The interface is linked by name. You must use the same name on both sides of the interface. Change the vertex shader:
#version 330
layout (location=0) in vec3 aPos;
layout (location=1) in vec4 aColor;
out vec4 fColor;
void main(){
fColor = aColor;
gl_Position = vec4(aPos,1.0);
}
You seem to be clearing the buffer right after rendering the triangle and before swapping the buffer's contents to screen.
In your window code:
...
renderer.render();
clear();
glfwSwapBuffers(window);
...
You clear the buffer's contents right before swapping it to the window instead of clearing before rendering:
...
clear();
renderer.render();
glfwSwapBuffers(window);
...

What is the right way to create 2D isometric view?

I want to create isometric 2D game. I use modern LWJGL (vertex arrays, shaders etc.) I found true isometric projection with opengl and: I tried to rotate my projection matrix, but it created black triangles on corners of screen. I tried to rotate model matrix, but I found that I can't rotate x and y on ortho matrix (because it's 2D), it does
but I need to rotate x and y axis to get isometric matrix. Then I found this https://www.gamedev.net/blog/33/entry-2250273-isometric-map-in-sfml/ and it says that I only need place tiles on right spots. Who is right? And why in first link they rotate model matrix in old OpenGL even though it's 2D projection matrix and I can't rotate it on modern OpenGL? (Sorry for my bad English)
Edit: How I create model matrix (I use joml for them):
public Matrix4f getModel(GameItem item){
Vector3f rot = item.getRotation();
return new Matrix4f().translate(item.getPosition()).rotateX((float)Math.toRadians(-rot.x)).rotateY((float)Math.toRadians(-rot.y)).rotateZ((float)Math.toRadians(-rot.z)).scale(item.getScale());
}
How I create projection matrix:
public void updateProjectionMatrix(Window window){
projectionMatrix2D.identity().ortho2D(-window.getWidth(), window.getWidth(), -window.getHeight(), window.getHeight());
}
If I rotate matrix before ortho2D() everything is rotated correctly, however there are black triangles on two of screen's corners. If I rotate it after ortho2D() it does exactly like rotating model matrix (shrinks object). In render() I pass matrixes to vertex shader:
#version 330
layout (location=0) in vec3 position;
layout (location=1) in vec2 texCoord;
out vec2 outTexCoord;
uniform mat4 projectionMatrix;
uniform mat4 model;
void main()
{
gl_Position = projectionMatrix* model * vec4(position, 1.0);
outTexCoord = texCoord;
}
Edit 2:
Creating view matrix works:
However it does exactly like rotating projection matrix before ortho2D() - it creates black (because that's my clear color) triangles on two of screen's corners:
and there:
How do I get rid of them?
Edit 3:
I rewrote my code, but nothing has changed.
To run my code, you should add to project LWJGL library, LWJGL-GLFW, LWJGL-STB, LWJGL-OpenGl and JOML library (it's not downloaded with LWJGL). You will also need a random .png image.
Main class:
import Core.Engine;
public class Main {
public static void main(String[] args){
try{
//Is vsync enabled?
boolean vsync = true;
Engine engine = new Engine("GAME", vsync);
//Start program
engine.start();
}
catch(Exception ex){
ex.printStackTrace();
System.exit(-1);
}
}
}
Engine class:
package Core;
public class Engine implements Runnable{
public static final int TARGET_FPS = 60;
public static final int TARGET_UPS = 30;
private final Window window;
//Program thread
private final Thread gameLoopThread;
//Timer for gameLoop
private final Timer timer;
//Creates everything that is on screen
private final Logic logic;
public Engine(String windowTitle, boolean vSync) throws Exception {
gameLoopThread = new Thread(this, "GAME_LOOP_THREAD");
window = new Window(windowTitle, vSync);
logic = new Logic();
timer = new Timer();
}
public void start() {
String osName = System.getProperty("os.name");
if ( osName.contains("Mac") ) {
gameLoopThread.run();
} else {
gameLoopThread.start();
}
}
#Override
public void run() {
try {
init();
gameLoop();
} catch (Exception excp) {
excp.printStackTrace();
}
}
protected void init() throws Exception {
window.init();
timer.init();
logic.init(this.window);
}
protected void gameLoop() {
float elapsedTime;
float accumulator = 0f;
float interval = 1f / TARGET_UPS;
boolean running = true;
while (running && !window.shouldClose()) {
elapsedTime = timer.getElapsedTime();
accumulator += elapsedTime;
input();
while (accumulator >= interval) {
update(interval);
accumulator -= interval;
}
render();
if (!window.isVsync()) {
sync();
}
}
}
private void sync() {
float loopSlot = 1f / TARGET_FPS;
double endTime = timer.getLastLoopTime() + loopSlot;
while (Timer.getTime() < endTime) {
try {
Thread.sleep(1);
} catch (InterruptedException ie) {
}
}
}
protected void input() {
logic.input(window);
}
protected void update(float interval) {
logic.update(interval,window);
}
protected void render() {
logic.render(window);
window.update();
}
}
Timer class:
package Core;
public class Timer {
private double lastLoopTime;
public void init(){
lastLoopTime = getTime();
}
public static double getTime(){
return (double)((double)System.nanoTime() / (double)1000_000_000L);
}
public float getElapsedTime(){
double time = getTime();
float elapsedTime =(float) (time-lastLoopTime);
lastLoopTime = time;
return elapsedTime;
}
public double getLastLoopTime(){
return lastLoopTime;
}
}
Window class:
package Core;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.system.MemoryUtil.*;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.glfw.GLFWErrorCallback;
import org.lwjgl.glfw.GLFWVidMode;
import org.lwjgl.opengl.GL;
public class Window {
private long window;
private int width, height;
private boolean vsync;
private String title;
public Window(String title,boolean vsync){
this.title = title;
this.vsync = vsync;
}
public long getInstance(){
return window;
}
public void init(){
GLFWErrorCallback.createPrint(System.err).set();
if(!glfwInit()){
// Throw an error.
throw new IllegalStateException("GLFW initialization failed!");
}
glfwWindowHint(GLFW_VISIBLE, GL_TRUE); // the window will stay hidden after creation
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); // the window will not be resizable
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
width = vidmode.width();
height = vidmode.height() -30;
System.out.println(width + " "+ height);
window = GLFW.glfwCreateWindow(width,height, title,NULL, NULL);
if(window == NULL){
throw new RuntimeException("ERROR with Window");
}
GLFW.glfwSetWindowPos(window, 0, 29);
glfwMakeContextCurrent(window);
if(vsync)
GLFW.glfwSwapInterval(1);
glfwShowWindow(window);
GL.createCapabilities();
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
public boolean isKeyPressed(int keyCode) {
return GLFW.glfwGetKey(window, keyCode) == GLFW.GLFW_PRESS;
}
public void setClearColor(float r, float g, float b, float alpha) {
glClearColor(r, g, b, alpha);
}
public boolean shouldClose(){
return glfwWindowShouldClose(window);
}
public void update(){
glfwSwapBuffers(window);
glfwPollEvents();
}
public void setVsync(boolean aflag){
this.vsync = aflag;
}
public boolean isVsync(){
return vsync;
}
public int getWidth(){
return width;
}
public int getHeight(){
return height;
}
}
Logic class:
package Core;
import org.joml.Vector3f;
import org.lwjgl.glfw.GLFW;
public class Logic {
private Renderer render;
private GameItem item;
public void init(Window window) throws Exception {
//create object
float[] positions = new float[]{
-600, 600, 0f,//0
-600, -600, 0f,//1
600, -600, 0,//2
600, 600, 0f//3
};
float[] texCoords = new float[]{
0,0,//0
0,1,//1
1,1,//2
1,0 //3
};
int[] indices = new int[]{
0, 1, 3, 3, 1, 2,
};
render = new Renderer();
render.init(window);
Texture texture = new Texture("Textures/image.png");
Mesh mesh = new Mesh(positions,texCoords,indices,texture);
item = new GameItem(mesh);
item.setPosition(-100, 0, 0);
}
public void input(Window window) {
//move
Vector3f s = item.getPosition();
if(window.isKeyPressed(GLFW.GLFW_KEY_W)){
item.setPosition(s.x, s.y+5, s.z);
}
else if(window.isKeyPressed(GLFW.GLFW_KEY_S)){
item.setPosition(s.x, s.y-5, s.z);
}
else if(window.isKeyPressed(GLFW.GLFW_KEY_A)){
item.setPosition(s.x-5, s.y, s.z);
}
else if(window.isKeyPressed(GLFW.GLFW_KEY_D)){
item.setPosition(s.x+5, s.y, s.z);
}
}
public void update(float interval, Window window) {
}
public void render(Window window) {
render.render(window,item);
}
public void cleanup() {
render.cleanup();
}
}
Renderer class:
package Core;
import static org.lwjgl.opengl.GL11.*;
import org.joml.Matrix4f;
import org.joml.Vector3f;
public class Renderer {
private Matrix4f projectionMatrix2D;
private Matrix4f view;
private ShaderProgram shaderProgram;
public Renderer() {
projectionMatrix2D= new Matrix4f();
}
public void init(Window window) throws Exception {
shaderProgram = new ShaderProgram();
shaderProgram.createVertexShader(Utils.load("Shaders/vertex.vs"));
shaderProgram.createFragmentShader(Utils.load("Shaders/fragment.fs"));
shaderProgram.link();
shaderProgram.createUniform("projectionMatrix");
shaderProgram.createUniform("model");
shaderProgram.createUniform("texture_sampler");
shaderProgram.createUniform("view");
view = new Matrix4f().lookAt(new Vector3f(1,1,1), new Vector3f(0), new Vector3f(0,1,0));
updateProjectionMatrix(window);
}
public void clear() {
glClear(GL_COLOR_BUFFER_BIT);
}
public void render(Window window,GameItem item) {
clear();
updateProjectionMatrix(window);
shaderProgram.bind();
shaderProgram.setUniform("projectionMatrix", projectionMatrix2D);
shaderProgram.setUniform("view", view);
shaderProgram.setUniform("texture_sampler", 0);
Vector3f rot = item.getRotation();
Matrix4f model = new Matrix4f().translate(item.getPosition()).rotateX((float)Math.toRadians(-rot.x))
.rotateY((float)Math.toRadians(-rot.y))
.rotateZ((float)Math.toRadians(-rot.z))
.scale(item.getScale());
shaderProgram.setUniform("model", model);
item.getMesh().render();
shaderProgram.unbind();
}
public void updateProjectionMatrix(Window window){
projectionMatrix2D.identity().ortho2D(-window.getWidth(), window.getWidth(), -window.getHeight(), window.getHeight());
//it creates weird triangle on right screen corner
// projectionMatrix2D.identity().ortho2D(-1, 1, -1, 1);
}
public void cleanup() {
if (shaderProgram != null) {
shaderProgram.cleanup();
}
}
}
ShaderProgram class:
package Core;
import static org.lwjgl.opengl.GL20.*;
import java.nio.FloatBuffer;
import java.util.HashMap;
import java.util.Map;
import org.joml.Matrix4f;
import org.joml.Vector2f;
import org.joml.Vector3f;
import org.lwjgl.system.MemoryStack;
public class ShaderProgram {
private final int programId;
private int vertexShaderId;
private int fragmentShaderId;
private final Map<String, Integer> uniforms;
public ShaderProgram() throws Exception {
programId = glCreateProgram();
if (programId == 0) {
throw new Exception("Could not create Shader");
}
uniforms = new HashMap<>();
}
public void createUniform(String uniformName) throws Exception {
int uniformLocation = glGetUniformLocation(programId, uniformName);
if (uniformLocation < 0) {
throw new Exception("Could not find uniform:" + uniformName);
}
uniforms.put(uniformName, uniformLocation);
}
public void setUniform(String uniformName, Matrix4f value) {
try (MemoryStack stack = MemoryStack.stackPush()) {
FloatBuffer fb = stack.mallocFloat(16);
value.get(fb);
glUniformMatrix4fv(uniforms.get(uniformName), false, fb);
}
}
public void setUniform(String uniformName, int value) {
glUniform1i(uniforms.get(uniformName), value);
}
public void setUniform(String uniformName, float value) {
glUniform1f(uniforms.get(uniformName), value);
}
public void setUniform(String uniformName, Vector3f value) {
glUniform3f(uniforms.get(uniformName), value.x, value.y, value.z);
}
public void setUniform(String uniformName, org.joml.Vector4f value) {
glUniform4f(uniforms.get(uniformName), value.x, value.y, value.z, value.w);
}
public void setUniform(String uniformName, Vector2f vector2f) {
glUniform2f(uniforms.get(uniformName),vector2f.x,vector2f.y);
}
public void createVertexShader(String shaderCode) throws Exception {
vertexShaderId = createShader(shaderCode, GL_VERTEX_SHADER);
}
public void createFragmentShader(String shaderCode) throws Exception {
fragmentShaderId = createShader(shaderCode, GL_FRAGMENT_SHADER);
}
protected int createShader(String shaderCode, int shaderType) throws Exception {
int shaderId = glCreateShader(shaderType);
if (shaderId == 0) {
throw new Exception("Error creating shader. Type: " + shaderType);
}
glShaderSource(shaderId, shaderCode);
glCompileShader(shaderId);
if (glGetShaderi(shaderId, GL_COMPILE_STATUS) == 0) {
throw new Exception("Error compiling Shader code: " + glGetShaderInfoLog(shaderId, 1024));
}
glAttachShader(programId, shaderId);
return shaderId;
}
public void link() throws Exception {
glLinkProgram(programId);
if (glGetProgrami(programId, GL_LINK_STATUS) == 0) {
throw new Exception("Error linking Shader code: " + glGetProgramInfoLog(programId, 1024));
}
if (vertexShaderId != 0) {
glDetachShader(programId, vertexShaderId);
}
if (fragmentShaderId != 0) {
glDetachShader(programId, fragmentShaderId);
}
glValidateProgram(programId);
if (glGetProgrami(programId, GL_VALIDATE_STATUS) == 0) {
System.err.println("Warning validating Shader code: " + glGetProgramInfoLog(programId, 1024));
}
}
public void bind() {
glUseProgram(programId);
}
public void unbind() {
glUseProgram(0);
}
public void cleanup() {
unbind();
if (programId != 0) {
glDeleteProgram(programId);
}
}
}
GameItem class:
package Core;
import org.joml.Vector3f;
public class GameItem {
//Mesh for this gameItem
private Mesh mesh;
private final Vector3f position;
private float scale = 1;
private final Vector3f rotation;
public GameItem() {
position = new Vector3f(0, 0, 0);
scale = 1;
rotation = new Vector3f(0, 0, 0);
}
public GameItem(Mesh mesh) {
this();
this.mesh = mesh;
}
public GameItem(GameItem item){
this();
position.set(item.position);
scale =1;
rotation.set(item.rotation);
this.mesh = item.mesh;
}
public Vector3f getPosition() {
return position;
}
public void setPosition(float x, float y, float z) {
this.position.x = x;
this.position.y = y;
this.position.z = z;
}
public float getScale() {
return scale;
}
public void setScale(float scale) {
this.scale = scale;
}
public Vector3f getRotation() {
return rotation;
}
public void setRotation(float x, float y, float z) {
this.rotation.x = x;
this.rotation.y = y;
this.rotation.z = z;
}
public Mesh getMesh() {
return mesh;
}
public void setMesh(Mesh mesh){
this.mesh = mesh;
}
}
Utils class:
package Core;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class Utils {
//Load strings
public static String load(String path){
StringBuilder builder = new StringBuilder();
try (InputStream in = new FileInputStream(path);
BufferedReader reader = new BufferedReader(new InputStreamReader(in))) {
String line;
while ((line = reader.readLine()) != null) {
builder.append(line).append("\n");
}
} catch (IOException ex) {
throw new RuntimeException("Failed to load a shader file!"
+ System.lineSeparator() + ex.getMessage());
}
String source = builder.toString();
return source;
}
}
Mesh class:
package Core;
import java.util.List;
import org.lwjgl.system.MemoryUtil;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import static org.lwjgl.opengl.GL15.*;
import static org.lwjgl.opengl.GL20.*;
import static org.lwjgl.opengl.GL30.*;
public class Mesh{
private final int vaoId;
private final List<Integer> vboIdList;
private final int vertexCount;
private Texture texture;
public Mesh(float[] positions, float[] textCoords, int[] indices,Texture texture) {
this.texture = texture;
FloatBuffer posBuffer = null;
FloatBuffer textCoordsBuffer = null;
IntBuffer indicesBuffer = null;
try {
vertexCount = indices.length;
vboIdList = new ArrayList<>();
vaoId = glGenVertexArrays();
glBindVertexArray(vaoId);
// Position
int vboId = glGenBuffers();
vboIdList.add(vboId);
posBuffer = MemoryUtil.memAllocFloat(positions.length);
posBuffer.put(positions).flip();
glBindBuffer(GL_ARRAY_BUFFER, vboId);
glBufferData(GL_ARRAY_BUFFER, posBuffer, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
// Texture coordinates
vboId = glGenBuffers();
vboIdList.add(vboId);
textCoordsBuffer = MemoryUtil.memAllocFloat(textCoords.length);
textCoordsBuffer.put(textCoords).flip();
glBindBuffer(GL_ARRAY_BUFFER, vboId);
glBufferData(GL_ARRAY_BUFFER, textCoordsBuffer, GL_STATIC_DRAW);
glVertexAttribPointer(1, 2, GL_FLOAT, false, 0, 0);
// Index
vboId = glGenBuffers();
vboIdList.add(vboId);
indicesBuffer = MemoryUtil.memAllocInt(indices.length);
indicesBuffer.put(indices).flip();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboId);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indicesBuffer, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
} finally {
if (posBuffer != null) {
MemoryUtil.memFree(posBuffer);
}
if (textCoordsBuffer != null) {
MemoryUtil.memFree(textCoordsBuffer);
}
if (indicesBuffer != null) {
MemoryUtil.memFree(indicesBuffer);
}
}
}
public Texture getTexture() {
return texture;
}
public void setTexture(Texture texture) {
this.texture = texture;
}
public int getVaoId() {
return vaoId;
}
public int getVertexCount() {
return vertexCount;
}
public void cleanUp() {
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, 0);
for (int vboId : vboIdList) {
glDeleteBuffers(vboId);
}
texture.cleanup();
glBindVertexArray(0);
glDeleteVertexArrays(vaoId);
}
public void render(){
glBindVertexArray(getVaoId());
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glDrawElements(GL_TRIANGLES, getVertexCount(), GL_UNSIGNED_INT, 0);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glBindVertexArray(0);
}
}
Texture class:
package Core;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import org.lwjgl.system.MemoryStack;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL30.glGenerateMipmap;
import static org.lwjgl.stb.STBImage.*;
public class Texture {
private final int id;
public Texture(String fileName) throws Exception {
this(loadTexture(fileName));
}
public Texture(int id) {
this.id = id;
}
public void bind() {
glBindTexture(GL_TEXTURE_2D, id);
}
public int getId() {
return id;
}
private static int loadTexture(String fileName) throws Exception {
int width;
int height;
ByteBuffer buf;
try (MemoryStack stack = MemoryStack.stackPush()) {
IntBuffer w = stack.mallocInt(1);
IntBuffer h = stack.mallocInt(1);
IntBuffer channels = stack.mallocInt(1);
buf = stbi_load(fileName, w, h, channels, 4);
if (buf == null) {
throw new Exception("Image file " + fileName + " not loaded: " + stbi_failure_reason());
}
width = w.get();
height = h.get();
}
int textureId = glGenTextures();
glBindTexture(GL_TEXTURE_2D, textureId);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
GL_RGBA, GL_UNSIGNED_BYTE, buf);
glGenerateMipmap(GL_TEXTURE_2D);
stbi_image_free(buf);
return textureId;
}
public void cleanup() {
glDeleteTextures(id);
}
}
Vertex shader:
#version 330
layout (location=0) in vec3 position;
layout (location=1) in vec2 texCoord;
out vec2 outTexCoord;
uniform mat4 projectionMatrix;
uniform mat4 model;
uniform mat4 view;
void main()
{
gl_Position = projectionMatrix * view * model * vec4(position, 1.0); // if view is after vec4 it creates actually object, but it creates black triangles as well. Without view matrix it is just a normal square
outTexCoord = texCoord;
}
Fragment shader:
#version 330
in vec2 outTexCoord;
out vec4 fragColor;
uniform sampler2D texture_sampler;
void main()
{
fragColor = texture(texture_sampler,outTexCoord);
}

Skybox is fully Black

I am new at openGL
I created this skybox on LWJGL but It's all black
SkyboxRenderer Class :
private static String[] TEXTURE_FILES = {"right","left","bottom","back","front"};
private RawModel cube;
private int texture;
private SkyboxShader shader;
public SkyboxRenderer(Loader loader, Matrix4f projectionMatirx) {
cube = loader.loadToVAO(VERTICES, 3);
texture = loader.loadCubeMap(TEXTURE_FILES);
shader = new SkyboxShader();
shader.start();
shader.loadProjectionMatrix(projectionMatirx);
shader.stop();
}
public void render(Camera camera){
shader.start();
shader.loadViewMatrix(camera);
GL30.glBindVertexArray(cube.getVaoID());
GL20.glEnableVertexAttribArray(0);
GL13.glActiveTexture(GL13.GL_TEXTURE0);
GL11.glBindTexture(GL13.GL_TEXTURE_CUBE_MAP, texture);
GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, cube.getVertexCount());
GL20.glDisableVertexAttribArray(0);
GL30.glBindVertexArray(0);
shader.stop();
}
Loader loadCubeMap function :
public int loadCubeMap(String[] textureFiles){
int texID = GL11.glGenTextures();
GL13.glActiveTexture(GL13.GL_TEXTURE0);
GL11.glBindTexture(GL13.GL_TEXTURE_CUBE_MAP, texID);
for(int i = 0; i < textureFiles.length;i++){
TextureData data = decodeTextureFile("res/" + textureFiles[i] + ".png");
GL11.glTexImage2D(GL13.GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL11.GL_RGBA, data.getWidth(), data.getHeight(), 0, GL11.GL_RGBA,
GL11.GL_UNSIGNED_BYTE, data.getBuffer());
}
GL11.glTexParameteri(GL13.GL_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
GL11.glTexParameteri(GL13.GL_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
GL11.glTexParameteri(GL13.GL_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE);
GL11.glTexParameteri(GL13.GL_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE);
textures.add(texID);
return texID;
}
private TextureData decodeTextureFile(String fileName) {
int width = 0;
int height = 0;
ByteBuffer buffer = null;
try {
FileInputStream in = new FileInputStream(fileName);
PNGDecoder decoder = new PNGDecoder(in);
width = decoder.getWidth();
height = decoder.getHeight();
buffer = ByteBuffer.allocateDirect(4 * width * height);
decoder.decode(buffer, width * 4, Format.RGBA);
buffer.flip();
in.close();
} catch (Exception e) {
e.printStackTrace();
System.err.println("Tried to load texture " + fileName + ", didn't work");
System.exit(-1);
}
return new TextureData(buffer, width, height);
}
Textures exist, but skybox is fullyblack can someone help me!
How can i fix it
I need to add more details for posting because there is too much code...
SkyboxShader :
public class SkyboxShader extends ShaderProgram{
private static final String VERTEX_FILE = "src/com/redcatengine/skybox/skyboxVertexShader.txt";
private static final String FRAGMENT_FILE = "src/com/redcatengine/skybox/skyboxFragmentShader.txt";
private int location_projectionMatrix;
private int location_viewMatrix;
public SkyboxShader() {
super(VERTEX_FILE, FRAGMENT_FILE);
}
public void loadProjectionMatrix(Matrix4f matrix){
super.loadMatrix(location_projectionMatrix, matrix);
}
public void loadViewMatrix(Camera camera){
Matrix4f matrix = Maths.createViewMatrix(camera);
matrix.m30 = 0;
matrix.m31 = 0;
matrix.m32 = 0;
super.loadMatrix(location_viewMatrix, matrix);
}
#Override
protected void getAllUniformLocations() {
location_projectionMatrix = super.getUniformLocation("projectionMatrix");
location_viewMatrix = super.getUniformLocation("viewMatrix");
}
#Override
protected void bindAttributes() {
super.bindAttribute(0, "position");
}
}
public abstract class ShaderProgram {
private int programID;
private int vertexShaderID;
private int fragmentShaderID;
private static FloatBuffer matrixBuffer = BufferUtils.createFloatBuffer(16);
public ShaderProgram(String vertexFile, String fragmentFile) {
vertexShaderID = loadShader(vertexFile, GL20.GL_VERTEX_SHADER);
fragmentShaderID = loadShader(fragmentFile, GL20.GL_FRAGMENT_SHADER);
programID = GL20.glCreateProgram();
GL20.glAttachShader(programID, vertexShaderID);
GL20.glAttachShader(programID, fragmentShaderID);
bindAttributes();
GL20.glLinkProgram(programID);
GL20.glValidateProgram(programID);
getAllUniformLocations();
}
protected abstract void getAllUniformLocations();
protected int getUniformLocation(String uniformName){
return GL20.glGetUniformLocation(programID, uniformName);
}
public void start(){
GL20.glUseProgram(programID);
}
public void stop(){
GL20.glUseProgram(0);
}
public void cleanUp(){
stop();
GL20.glDetachShader(programID, vertexShaderID);
GL20.glDetachShader(programID, fragmentShaderID);
GL20.glDeleteShader(vertexShaderID);
GL20.glDeleteShader(fragmentShaderID);
GL20.glDeleteProgram(programID);
}
protected abstract void bindAttributes();
protected void bindAttribute(int attribute, String variableName){
GL20.glBindAttribLocation(programID, attribute, variableName);
}
protected void loadInt(int location, int value){
GL20.glUniform1i(location, value);
}
protected void loadFloat(int location, float value){
GL20.glUniform1f(location, value);
}
protected void loadVector(int location, Vector3f value){
GL20.glUniform3f(location, value.x, value.y, value.z);
}
protected void load2DVector(int location, Vector2f value){
GL20.glUniform2f(location, value.x, value.y);
}
protected void loadBoolean(int location, boolean value){
float toLoad = 0;
if(value)toLoad = 1;else toLoad = 0;
GL20.glUniform1f(location, toLoad);
}
protected void loadMatrix(int location, Matrix4f matrix){
matrix.store(matrixBuffer);
matrixBuffer.flip();
GL20.glUniformMatrix4(location, false, matrixBuffer);
}
private static int loadShader(String file, int type){
StringBuilder shaderSource = new StringBuilder();
try{
BufferedReader reader = new BufferedReader(new FileReader(file));
String line;
while((line = reader.readLine()) != null){
shaderSource.append(line).append("\n");
}
reader.close();
}catch(IOException e){
System.err.println("Could not read shader file!");
e.printStackTrace();
System.exit(-1);
}
int shaderID = GL20.glCreateShader(type);
GL20.glShaderSource(shaderID, shaderSource);
GL20.glCompileShader(shaderID);
if(GL20.glGetShaderi(shaderID, GL20.GL_COMPILE_STATUS)==GL11.GL_FALSE){
System.out.println(GL20.glGetShaderInfoLog(shaderID, 500));
System.out.println("Could not compile shader.");
System.exit(-1);
}
return shaderID;
}
}
skyboxFragmentShader :
#version 400
in vec3 textureCoords;
out vec4 out_Color;
uniform samplerCube cubeMap;
void main(void){
out_Color = texture(cubeMap, textureCoords);
}
skyboxVertexShader
#version 400
in vec3 position;
out vec3 textureCoords;
uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
void main(void){
gl_Position = projectionMatrix * viewMatrix * vec4(position, 1.0);
textureCoords = position;
}`
Your cube map texture is not cube complete:
Your loader code iterates over all files in the array it is called with:
for(int i = 0; i < textureFiles.length;i++){
// [...]
GL11.glTexImage2D(GL13.GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, [...])
}
However, your input array contains only 5 entires:
String[] TEXTURE_FILES = {"right","left","bottom","back","front"};
You provide only 5 faces for the cube, and forgot the "top" face.
According to the GL spec (quotes are from section 8.17 of the OpenGL 4.5 core profile specification),
A cube map texture is mipmap complete if each of the six texture
images, considered individually, is mipmap complete. Additionally, a
cube map texture is cube complete if the following conditions all hold
true:
The level_base texture images of each of the six cube map faces have identical, positive, and square dimensions.
The levelbase
images were each specified with the same internal format.
It further goes on define texture completeness:
Using the preceding definitions, a texture is complete unless any of the following
conditions hold true:
[...]
The texture is a cube map texture, and is not cube complete.
[...]
So your cube map texture is not complete.
Section 11.1.3.5 states:
If a sampler is used in a shader and the sampler’s associated texture is not
complete, as defined in section 8.17, (0; 0; 0; 1) will be returned for a non-shadow sampler and 0 for a shadow sampler.
So indeed, your cube map should appear completely black.

OpenGL 2.0 glGetUniformLocation: glError 1282 in draw()

I've made a game that draws openGL shapes correctly to screen for most of the game. But for some reason at random intervals in the game I get the 1282 glGetUniformLocation error. Whats strange is that it works for a while but then fails?
This is the stack trace
12-12 12:31:54.781: E/AndroidRuntime(2531): FATAL EXCEPTION: GLThread 86993
12-12 12:31:54.781: E/AndroidRuntime(2531): Process: com.laytonlabs.android.levelup, PID: 2531
12-12 12:31:54.781: E/AndroidRuntime(2531): java.lang.RuntimeException: glGetUniformLocation: glError 1282
12-12 12:31:54.781: E/AndroidRuntime(2531): at com.laytonlabs.android.levelup.MyGLRenderer.checkGlError(MyGLRenderer.java:460)
12-12 12:31:54.781: E/AndroidRuntime(2531): at com.laytonlabs.android.levelup.shapes.Shape.draw(Shape.java:240)
12-12 12:31:54.781: E/AndroidRuntime(2531): at com.laytonlabs.android.levelup.MyGLRenderer.drawShapes(MyGLRenderer.java:350)
12-12 12:31:54.781: E/AndroidRuntime(2531): at com.laytonlabs.android.levelup.MyGLRenderer.drawFixedShapes(MyGLRenderer.java:324)
12-12 12:31:54.781: E/AndroidRuntime(2531): at com.laytonlabs.android.levelup.MyGLRenderer.onDrawFrame(MyGLRenderer.java:205)
12-12 12:31:54.781: E/AndroidRuntime(2531): at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1531)
12-12 12:31:54.781: E/AndroidRuntime(2531): at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1248)
This is the part which is generating the error (I think), inside Shape.java draw() function.
Shape.java
mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
MyGLRenderer.checkGlError("glGetUniformLocation");
The below is the full code for the java classes referenced in the error stack.
MyGLRenderer.java
package com.laytonlabs.android.levelup;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import com.laytonlabs.android.levelup.game.Cell;
import com.laytonlabs.android.levelup.game.CurrentAnswer;
import com.laytonlabs.android.levelup.game.Equation;
import com.laytonlabs.android.levelup.game.Game;
import com.laytonlabs.android.levelup.game.Level;
import com.laytonlabs.android.levelup.game.Score;
import com.laytonlabs.android.levelup.game.Stage;
import com.laytonlabs.android.levelup.game.Time;
import com.laytonlabs.android.levelup.shapes.Color;
import com.laytonlabs.android.levelup.shapes.EquationRectangle;
import com.laytonlabs.android.levelup.shapes.Hexagon;
import com.laytonlabs.android.levelup.shapes.InputSquare;
import com.laytonlabs.android.levelup.shapes.Shape;
import com.laytonlabs.android.levelup.shapes.StatsRectangle;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import android.util.Log;
public class MyGLRenderer implements GLSurfaceView.Renderer {
private static final String TAG = "MyGLRenderer";
private static StatsRectangle levelRectangle;
private static StatsRectangle timeRectangle;
private static StatsRectangle scoreRectangle;
private static EquationRectangle equationRectangle;
private static EquationRectangle answerRectangle;
private ArrayList<Shape> gridShapes;
private ArrayList<Shape> bottomRowShapes;
private static ArrayList<Shape> inputShapes;
// mMVPMatrix is an abbreviation for "Model View Projection Matrix"
private final float[] mMVPMatrix = new float[16];
private final float[] mMVPFixed = new float[16];
private final float[] mProjectionMatrix = new float[16];
private final float[] mViewMatrix = new float[16];
private final float[] mGridModelMatrix = new float[16];
private float[] mFixedModelMatrix = new float[16];
private float[] mTempMatrix = new float[16];
private float mMovementY;
private float mBottomRowScale;
private static String mAnswerText = CurrentAnswer.getLabel();
private boolean isCorrectGuess = false;
private boolean renderCorrectGuess = false;
private boolean isWrongGuess = false;
private boolean renderOutput = false;
//To limit the number of renders per second
private long startTime;
private long endTime;
private long timeElapsed;
private int currentFrame = 0; // active frame
private int outputCurrentFrame = 0; // active frame
private final int FPS = 33; // Frames per second
private final int FPS_ANIMATION_10 = 10;
private final int FPS_ANIMATION_20 = 20;
//Reducer values, these are used for animation scenes
private float mFPSMovementY;
private float mFPSBottomRowScale;
private int gridLevel = 0; //The cell layout level in the grid.
private int rowLevel = 1; //The current row in the grid user is selected.
//Constants for grid presentation
private final float CELL_SCALE = 0.3f;
private final float CELL_OFFSET_Y = 0.7f;
#Override
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
// Set the background frame color
GLES20.glClearColor(Color.DARK_GREY[0], Color.DARK_GREY[1], Color.DARK_GREY[2], Color.DARK_GREY[3]);
startTime = System.currentTimeMillis();
//TODO Add code that sets current answer to whatever the current answer is.
//Initialise fixed shapes
equationRectangle = new EquationRectangle(-0.35f);
answerRectangle = new EquationRectangle(-0.5f);
equationRectangle.setShapes(0.2f, Equation.get());
answerRectangle.setShapes(0.3f, mAnswerText);
//TODO - Change the below calculations to be align_left, align_centre, align_right, etc.
levelRectangle = new StatsRectangle(0 - (Screen.DEFAULT_WIDTH/3), Color.TURQUOISE, Color.TURQUOISE);
timeRectangle = new StatsRectangle(0, Color.PURPLE, Color.PURPLE);
scoreRectangle = new StatsRectangle(0 + (Screen.DEFAULT_WIDTH/3), Color.TURQUOISE, Color.TURQUOISE);
levelRectangle.setShapes(-1f, Level.getLabel());
scoreRectangle.setShapes(-1f, Score.getScoreLabel());
timeRectangle.setShapes(-1f, Time.getTimeRemainingLabel());
setGridShapes();
//Complete the bottom row for inputting guesses
bottomRowShapes = new ArrayList<Shape>();
setBottomRowShapes();
buildInputGrid();
setBottomRowScale(1.0f);
setFPSBottomRowScale(getBottomRowScale() / FPS_ANIMATION_20);
setFPSMovementY((CELL_OFFSET_Y*CELL_SCALE) / FPS_ANIMATION_20);
}
private void buildInputGrid() {
inputShapes = new ArrayList<Shape>();
inputShapes.add(new InputSquare(0.16f, -3.0f, 1.15f, "1")); //1
inputShapes.add(new InputSquare(0.16f, -1.8f, 1.15f, "2")); //2
inputShapes.add(new InputSquare(0.16f, -0.6f, 1.15f, "3")); //3
inputShapes.add(new InputSquare(0.16f, 0.6f, 1.15f, "4")); //4
inputShapes.add(new InputSquare(0.16f, 1.8f, 1.15f, "5")); //5
inputShapes.add(new InputSquare(0.16f, 3.0f, 1.15f, "6")); //6
inputShapes.add(new InputSquare(0.16f, -2.4f, 0, "7")); //7
inputShapes.add(new InputSquare(0.16f, -1.2f, 0, "8")); //8
inputShapes.add(new InputSquare(0.16f, 0, 0, "9")); //9
inputShapes.add(new InputSquare(0.16f, 1.2f, 0, "0")); //0
inputShapes.add(new InputSquare(0.16f, 2.4f, 0, "x")); //X - This is to clear input
}
#Override
public void onDrawFrame(GL10 unused) {
//We dont need continuous rendering, only needed for animation and time switching
endTime = System.currentTimeMillis();
timeElapsed = endTime - startTime;
if (timeElapsed < FPS) {
try {
Log.d(TAG, "Sleeping until "+FPS+" millsecs pass");
Thread.sleep(FPS - timeElapsed);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
startTime = endTime;
//Update the timers by deducting timeRemaining
Time.update();
Matrix.setIdentityM(mGridModelMatrix, 0); // initialize to identity matrix
//Setup the equation display before we start moving the grid around
Matrix.setIdentityM(mFixedModelMatrix, 0); // initialize to identity matrix
// Draw background color
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
// Enable transparency options for colors.
GLES20.glEnable(GLES20.GL_BLEND);
GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
// Set the camera position (View matrix)
Matrix.setLookAtM(mViewMatrix, 0, 0, 0, 3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
// Calculate the projection and view transformation
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
//Clone this for use with fixed and grid MVPs
mTempMatrix = mMVPMatrix.clone();
// Create a rotation for the triangle
// Use the following code to generate constant rotation.
// Leave this code out when using TouchEvents.
// long time = SystemClock.uptimeMillis() % 4000L;
// float angle = 0.090f * ((int) time);
drawGridShapes();
drawFixedShapes();
}
private void drawGridShapes() {
//Start the grid drawing at bottom of screen.
Matrix.translateM(mGridModelMatrix, 0, 0, -0.1f, 0);
//Move the grid down or up the screen depending on touch events.
Matrix.translateM(mGridModelMatrix, 0, 0, mMovementY, 0);
// Combine the rotation matrix with the projection and camera view
// Note that the mMVPMatrix factor *must be first* in order
// for the matrix multiplication product to be correct.
//Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0);
//Add the movement to the matrix
Matrix.multiplyMM(mMVPMatrix, 0, mTempMatrix, 0, mGridModelMatrix, 0);
if (isCorrectGuess()) {
renderCorrectGuess = true; //This is to change the answer text to green inside drawFixedShapes
currentFrame++; // step to next frame
setMovementY(getMovementY() - getFPSMovementY());
setBottomRowScale(getBottomRowScale() - getFPSBottomRowScale());
if (currentFrame >= FPS_ANIMATION_20) { // if end of sequence
currentFrame = 0; // restart sequence
setBottomRowScale(1.0f); // Reset the scale
removeBottomRow();
setCorrectGuess(false); //Mark as false so animation stops and user can make new guess
}
}
//Draw all grid shapes
if (isCorrectGuess()) {
drawAllShapesAndShrinkBottomRow(mMVPMatrix);
} else {
drawAllShapes(getGridShapes(), mMVPMatrix);
}
}
private void drawAllShapes(ArrayList<Shape> shapes, float[] mMVPMatrix) {
for (Shape shape : shapes) {
Log.d(TAG, "Scale Hexagon ("+shape.toString()+") Aft ("+shape.getCentreX()+", "+shape.getCentreY()+")");
drawShapes(shape, mMVPMatrix);
}
}
private void drawAllShapesAndShrinkBottomRow(float[] mMVPMatrix) {
float[] mMVPScaled = mMVPMatrix.clone();
Matrix.scaleM(mMVPScaled, 0, getBottomRowScale(), getBottomRowScale(), 0);
int lastCellIndex = getBottomRowLastCellIndex();
//Apply scaling to the bottom row and just move the other rows.
for (int i = 0; i < getGridShapes().size(); i++) {
if (i <= lastCellIndex) {
drawShapes(getGridShapes().get(i), mMVPScaled);
} else {
drawShapes(getGridShapes().get(i), mMVPMatrix);
}
}
}
private void drawFixedShapes() {
Matrix.multiplyMM(mMVPFixed, 0, mTempMatrix, 0, mFixedModelMatrix, 0);
if (isRenderOutput()) {
//Show the equation using the values from the selected cell.
equationRectangle.setShapes(0.2f, Equation.get());
answerRectangle.setShapes(0.3f, mAnswerText);
setRenderOutput(false);
}
//Update the time if time has changed
if (!timeRectangle.toString().equals(Time.getTimeRemainingLabel())) {
//If the time remaining is almost up then change text to red.
if (Time.isTimeAlmostUp()) {
timeRectangle.setShapes(-1f, Time.getTimeRemainingLabel(), Color.RED);
} else {
timeRectangle.setShapes(-1f, Time.getTimeRemainingLabel());
}
}
//Animation for changing color of the text
if (isWrongGuess()) {
if (outputCurrentFrame == 0) {
answerRectangle.setShapes(0.3f, mAnswerText, Color.RED);
}
outputCurrentFrame++;
if (outputCurrentFrame >= FPS_ANIMATION_10) {
outputCurrentFrame = 0;
setAnswerText("");
answerRectangle.setShapes(0.3f, mAnswerText);
setWrongGuess(false);
}
} else if (renderCorrectGuess) {
if (outputCurrentFrame == 0) {
answerRectangle.setShapes(0.3f, mAnswerText, Color.GREEN);
levelRectangle.setShapes(-1f, Level.getLabel(), Color.YELLOW);
scoreRectangle.setShapes(-1f, Score.getScoreLabel(), Color.YELLOW);
timeRectangle.setShapes(-1f, Time.getTimeRemainingLabel(), Color.YELLOW);
}
outputCurrentFrame++;
if (outputCurrentFrame >= FPS_ANIMATION_20) {
outputCurrentFrame = 0;
answerRectangle.setShapes(0.3f, mAnswerText);
levelRectangle.setShapes(-1f, Level.getLabel());
scoreRectangle.setShapes(-1f, Score.getScoreLabel());
timeRectangle.setShapes(-1f, Time.getTimeRemainingLabel());
setGridShapes();
renderCorrectGuess = false;
}
}
drawShapes(answerRectangle, mMVPFixed);
drawShapes(equationRectangle, mMVPFixed);
drawShapes(levelRectangle, mMVPFixed);
drawShapes(timeRectangle, mMVPFixed);
drawShapes(scoreRectangle, mMVPFixed);
//Draw all input grid shapess
drawAllShapes(inputShapes, mMVPFixed);
}
public static void printStack() {
Log.e(TAG,"Level: " + Level.getLabel());
Log.e(TAG,"Score: " + Score.getScoreLabel());
Log.e(TAG,"Time: " + Time.getTimeRemainingLabel());
Log.e(TAG,"mAnswerText: " + mAnswerText);
Log.e(TAG,"Equation: " + Equation.get());
for (int i = 0; i < inputShapes.size(); i++) {
Log.e(TAG,"inputShapes[" + i + "]: " + inputShapes.get(i).toString());
}
}
private void drawShapes(Shape parentShape, float[] mMVPMatrix) {
parentShape.draw(mMVPMatrix);
if (parentShape.getShapes() == null) {
return;
}
for (Shape nestedShapes : parentShape.getShapes()) {
nestedShapes.draw(mMVPMatrix);
}
}
private ArrayList<Shape> getGridShapes() {
return gridShapes;
}
private int getBottomRowLastCellIndex() {
int lastCellIndex = 0;
Shape prevShape = null;
for (int i = 0; i < getGridShapes().size(); i++) {
if (prevShape == null || getGridShapes().get(i).getCentreY() == prevShape.getCentreY()) {
lastCellIndex = i;
prevShape = getGridShapes().get(i);
} else {
return lastCellIndex;
}
}
return lastCellIndex;
}
private void removeBottomRow() {
for (int i = getBottomRowLastCellIndex(); i >= 0 ; i--) {
getGridShapes().remove(i);
}
//Reset the bottom row shapes
setBottomRowShapes();
}
public ArrayList<Shape> getBottomRowShapes() {
return bottomRowShapes;
}
private void setBottomRowShapes() {
ArrayList<Shape> tempRowShapes = new ArrayList<Shape>();
//Apply scaling to the bottom row and just move the other rows.
for (int i = 0; i <= getBottomRowLastCellIndex(); i++) {
tempRowShapes.add(getGridShapes().get(i));
}
bottomRowShapes = tempRowShapes;
}
#Override
public void onSurfaceChanged(GL10 unused, int width, int height) {
// Adjust the viewport based on geometry changes,
// such as screen rotation
GLES20.glViewport(0, 0, width, height);
//Log.d("MyGLRenderer", "Width: " + width + " Height: " + height);
float ratio = (float) width / height;
Log.d("Screen","Width: "+ width +" - Height: "+ height +" - Ratio: "+ ratio);
// this projection matrix is applied to object coordinates
// in the onDrawFrame() method
if (ratio > 1) {
ratio = Screen.DEFAULT_LANDSCAPE_RATIO;
} else {
ratio = Screen.DEFAULT_PORTRAIT_RATIO;
}
Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
//TODO Store the current answer, the current answer Text and the current Equation.
}
public static int loadShader(int type, String shaderCode){
// create a vertex shader type (GLES20.GL_VERTEX_SHADER)
// or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
int shader = GLES20.glCreateShader(type);
// add the source code to the shader and compile it
GLES20.glShaderSource(shader, shaderCode);
GLES20.glCompileShader(shader);
return shader;
}
public static void checkGlError(String glOperation) {
int error;
while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
Log.e(TAG, glOperation + ": 1glError " + error);
printStack();
//TODO - Print out the fixed shapes values to see if something wierd is being displayed after a while.
throw new RuntimeException(glOperation + ": glError " + error);
}
}
public float getMovementY() {
return mMovementY;
}
public void setMovementY(float movementY) {
mMovementY = movementY;
}
public float[] getProjectionMatrix() {
return mProjectionMatrix;
}
public float[] getGridModelMatrix() {
return mGridModelMatrix;
}
public float[] getFixedModelMatrix() {
return mFixedModelMatrix;
}
public String getAnswerText() {
return mAnswerText;
}
public void setAnswerText(String guessInput) {
if (guessInput == "") {
this.mAnswerText = getInputUnderscores();
return;
}
this.mAnswerText = this.mAnswerText.replaceFirst("_", guessInput);
}
public void resetAnswerText() {
this.mAnswerText = CurrentAnswer.getLabel();
}
private String getInputUnderscores() {
if (Equation.getExpectedAnswer() <= 0) {
return "";
}
return Equation.getExpectedAnswerLabel().replaceAll("[0-9]", "_");
}
public ArrayList<Shape> getInputShapes() {
return inputShapes;
}
Shape.java
package com.laytonlabs.android.levelup.shapes;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
import java.util.ArrayList;
import android.opengl.GLES20;
import com.laytonlabs.android.levelup.MyGLRenderer;
import com.laytonlabs.android.levelup.Vec2;
import com.laytonlabs.android.levelup.game.Cell;
/**
* A two-dimensional square for use as a drawn object in OpenGL ES 2.0.
*/
public abstract class Shape {
private final String TAG = "Shape";
private final String vertexShaderCode =
// This matrix member variable provides a hook to manipulate
// the coordinates of the objects that use this vertex shader
"uniform mat4 uMVPMatrix;" +
"attribute vec4 vPosition;" +
"void main() {" +
// The matrix must be included as a modifier of gl_Position.
// Note that the uMVPMatrix factor *must be first* in order
// for the matrix multiplication product to be correct.
" gl_Position = uMVPMatrix * vPosition;" +
"}";
private final String fragmentShaderCode =
"precision mediump float;" +
"uniform vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}";
private final FloatBuffer vertexBuffer;
private final ShortBuffer drawListBuffer;
private final int mProgram;
private int mPositionHandle;
private int mColorHandle;
private int mMVPMatrixHandle;
protected float[] shapeCoords;
private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
// number of coordinates per vertex in this array
static final int COORDS_PER_VERTEX = 3;
//These will be initiated by the abstract class
private final float[] ORIGINAL_COORDS;
private final short[] DRAW_ORDER; // order to draw vertices
//#RGB: white (255, 255, 255)
private final float[] COLOR;
//Sets the scale of the shape and where the X centre is.
private final float SCALE;
private final float CENTRE_X;
private final float CENTRE_Y;
public abstract float getCentreX();
public abstract float getCentreY();
public float[] getNestedTextColor() {
return null;
}
public void setNestedTextColor(float[] textColor) {}
public Cell getCell() {
return null;
}
public ArrayList<Shape> getShapes() {
return null;
}
public void setShapes(float scale, String nestedText) {}
public void setShapes(float scale, String nestedText, float[] textColor) {}
public boolean intersects(Vec2 touchCoords) {
return false;
}
public float getMinX() {return getMin(getArraySubset(0));}
public float getMaxX() {return getMax(getArraySubset(0));}
public float getMinY() {return getMin(getArraySubset(1));}
public float getMaxY() {return getMax(getArraySubset(1));}
private float getMin(float[] values) {
float minVal = 1000f;
for (float value : values) {
if (value < minVal) {
minVal = value;
}
}
return minVal;
}
private float getMax(float[] values) {
float maxVal = -1000f;
for (float value : values) {
if (value > maxVal) {
maxVal = value;
}
}
return maxVal;
}
private float[] getArraySubset(int offset) {
if (shapeCoords == null || shapeCoords.length == 0) {
return null;
}
float[] subsetArray = new float[shapeCoords.length / COORDS_PER_VERTEX];
int subsetIndex = 0;
for (int i = offset; i < shapeCoords.length; i=(i+COORDS_PER_VERTEX)) {
subsetArray[subsetIndex] = shapeCoords[i];
subsetIndex++;
}
return subsetArray;
}
/**
* Sets up the drawing object data for use in an OpenGL ES context.
*/
public Shape(float[] originalCoords, short[] drawOrder, float[] color,
float scale, float centreX, float centreY) {
this.ORIGINAL_COORDS = originalCoords;
this.DRAW_ORDER = drawOrder;
this.COLOR = color;
this.SCALE = scale;
this.CENTRE_X = centreX;
this.CENTRE_Y = centreY;
this.shapeCoords = ORIGINAL_COORDS.clone();
adjustShape(scale, centreX, centreY);
//Resize based on the scale
//adjustSize(scale);
// initialize vertex byte buffer for shape coordinates
ByteBuffer bb = ByteBuffer.allocateDirect(
// (# of coordinate values * 4 bytes per float)
shapeCoords.length * 4);
bb.order(ByteOrder.nativeOrder());
vertexBuffer = bb.asFloatBuffer();
vertexBuffer.put(shapeCoords);
vertexBuffer.position(0);
// initialize byte buffer for the draw list
ByteBuffer dlb = ByteBuffer.allocateDirect(
// (# of coordinate values * 2 bytes per short)
DRAW_ORDER.length * 2);
dlb.order(ByteOrder.nativeOrder());
drawListBuffer = dlb.asShortBuffer();
drawListBuffer.put(DRAW_ORDER);
drawListBuffer.position(0);
// prepare shaders and OpenGL program
int vertexShader = MyGLRenderer.loadShader(
GLES20.GL_VERTEX_SHADER,
vertexShaderCode);
int fragmentShader = MyGLRenderer.loadShader(
GLES20.GL_FRAGMENT_SHADER,
fragmentShaderCode);
mProgram = GLES20.glCreateProgram(); // create empty OpenGL Program
GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader to program
GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
GLES20.glLinkProgram(mProgram); // create OpenGL program executables
}
//Adjust the original scale of the shape and position
private void adjustShape(float scale, float centreX, float centreY) {
for (int i = 0; i < shapeCoords.length; i++) {
//Apply the scale
shapeCoords[i] = (ORIGINAL_COORDS[i] * scale);
//Apply the x offset
shapeCoords[i] += (i % 3 == 0 ? centreX : 0);
//Apply the y offset
shapeCoords[i] += (i % 3 == 1 ? centreY : 0);
}
}
/**
* Encapsulates the OpenGL ES instructions for drawing this shape.
*
* #param mvpMatrix - The Model View Project matrix in which to draw
* this shape.
*/
public void draw(float[] mvpMatrix) {
// Add program to OpenGL environment
GLES20.glUseProgram(mProgram);
// get handle to vertex shader's vPosition member
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
// Enable a handle to the triangle vertices
GLES20.glEnableVertexAttribArray(mPositionHandle);
// Prepare the triangle coordinate data
GLES20.glVertexAttribPointer(
mPositionHandle, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
vertexStride, vertexBuffer);
// get handle to fragment shader's vColor member
mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
// Set color for drawing the triangle
GLES20.glUniform4fv(mColorHandle, 1, COLOR, 0);
// get handle to shape's transformation matrix
mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
MyGLRenderer.checkGlError("glGetUniformLocation");
// Apply the projection and view transformation
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
MyGLRenderer.checkGlError("glUniformMatrix4fv");
// Draw the square
GLES20.glDrawElements(
GLES20.GL_TRIANGLES, DRAW_ORDER.length,
GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
// Disable vertex array
GLES20.glDisableVertexAttribArray(mPositionHandle);
}
}
There are only two reasons why glGetUniformLocation would emit a GL_INVALID_OPERATION error: because the program isn't a program object or because it wasn't successfully linked.
I see no code where you actually test whether glLinkProgram was successful. You never call glGetProgramiv(program, GL_LINK_STATUS) to test whether the program linked correctly. Clearly, linking failed at some point.
The general code for error handling (in C++) would be:
//Link the program.
glLinkProgram(program);
GLint isLinked = 0;
glGetProgramiv(program, GL_LINK_STATUS, &isLinked);
if(isLinked == GL_FALSE)
{
GLint maxLength = 0;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength);
//The maxLength includes the NULL character
std::vector<GLchar> infoLog(maxLength);
glGetProgramInfoLog(program, maxLength, &maxLength, &infoLog[0]);
//The program is useless now. So delete it.
glDeleteProgram(program);
//Provide the infolog in whatever manner you deem best.
//Exit with failure.
return;
}

LWJGL 3 shaders not rendering

I am rendering a simple quad and have been experimenting with shaders. It worked fine, but I followed a texturing tutorial to add textures and I must have changed something. Because now, even though all the texturing code is commented out, the quad doesn't render when I turn shaders on. I suspect it has something to do with the data binding, as I am still trying to learn it. I can't for the life of me find what's wrong!
Shaders (vertex and fragment):
//vertex
#version 400 core
in vec3 position;
out vec3 colour;
void main (void) {
colour = vec3(position.x + 0.5, 1.0, position.y + 0.5);
}
//fragment
#version 400 core
in vec3 colour;
out vec4 out_Colour;
void main (void) {
out_Colour = vec4(colour, 1.0);
}
Shader program:
public abstract class ShaderProgram {
private int programId;
private int vertexId;
private int fragmentId;
public ShaderProgram (String vertexFile, String fragmentFile) {
vertexId = loadShader(vertexFile, GL20.GL_VERTEX_SHADER);
fragmentId = loadShader(fragmentFile, GL20.GL_FRAGMENT_SHADER);
programId = GL20.glCreateProgram();
GL20.glAttachShader(programId, vertexId);
GL20.glAttachShader(programId, fragmentId);
bindAttributes();
GL20.glLinkProgram(programId);
GL20.glValidateProgram(programId);
}
public void start () {
GL20.glUseProgram(programId);
}
public void stop () {
GL20.glUseProgram(0);
}
public void cleanUp () {
stop();
GL20.glDetachShader(programId, vertexId);
GL20.glDetachShader(programId, fragmentId);
GL20.glDeleteShader(vertexId);
GL20.glDeleteShader(fragmentId);
GL20.glDeleteProgram(programId);
}
protected abstract void bindAttributes ();
protected void bindAttribute (int attribute, String variableName) {
GL20.glBindAttribLocation(programId, attribute, variableName);
}
private static int loadShader (String file, int type) {
StringBuilder shaderSource = new StringBuilder();
try {
BufferedReader reader = new BufferedReader(new FileReader(file));
String line;
while ((line = reader.readLine()) != null) {
shaderSource.append(line).append("\n");
}
reader.close();
} catch (IOException e) {
System.err.println("Could not read shader file!");
e.printStackTrace();
System.exit(-1);
}
int shaderId = GL20.glCreateShader(type);
GL20.glShaderSource(shaderId, shaderSource);
GL20.glCompileShader(shaderId);
if (GL20.glGetShaderi(shaderId, GL20.GL_COMPILE_STATUS) == GL11.GL_FALSE) {
System.err.println(GL20.glGetShaderInfoLog(shaderId));
System.err.println("Could not compile shader!");
System.exit(-1);
}
return shaderId;
}
}
Main shader (extends Shader program):
public class MainShader extends ShaderProgram {
private static final String VERTEX_FILE = "src/shaders/vertexShader.txt";
private static final String FRAGMENT_FILE = "src/shaders/fragmentShader.txt";
public MainShader() {
super(VERTEX_FILE, FRAGMENT_FILE);
}
#Override
protected void bindAttributes() {
super.bindAttribute(ModelLoader.VERTEX_INDICE, "position");
//super.bindAttribute(1, "uv");
}
}
Model loader:
public class ModelLoader {
private static List<Integer> vaos = new ArrayList<Integer>();
private static List<Integer> vbos = new ArrayList<Integer>();
private static List<Integer> textures = new ArrayList<Integer>();
public static final int VERTEX_INDICE = 0;
public static final int UV_INDICE = 1;
public static RawModel loadToVAO (float[] positions, int[] indices) {
int vaoId = createVAO();
bindIndicesBuffer(indices);
storeDataInAttributeList(VERTEX_INDICE, 3, positions);
//storeDataInAttributeList(UV_INDICE, 2, uvCoords);
unbindVAO();
return new RawModel(vaoId, indices.length);
}
/*public static int loadTexture (String file, int textureUnit) {
try {
return TextureLoader.loadTexture("rsrc/" + file + ".png", GL11.GL_TEXTURE_2D);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return 0;
}*/
public static int loadTexture(String file, int textureUnit) {
String filename = "rsrc/" + file + ".png";
ByteBuffer buf = null;
int tWidth = 0;
int tHeight = 0;
try {
// Open the PNG file as an InputStream
InputStream in = new FileInputStream(filename);
// Link the PNG decoder to this stream
PNGDecoder decoder = new PNGDecoder(in);
// Get the width and height of the texture
tWidth = decoder.getWidth();
tHeight = decoder.getHeight();
// Decode the PNG file in a ByteBuffer
buf = ByteBuffer.allocateDirect(
4 * decoder.getWidth() * decoder.getHeight());
decoder.decode(buf, decoder.getWidth() * 4, Format.RGBA);
buf.flip();
in.close();
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
// Create a new texture object in memory and bind it
int texId = GL11.glGenTextures();
GL13.glActiveTexture(textureUnit);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, texId);
// All RGB bytes are aligned to each other and each component is 1 byte
GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);
// Upload the texture data and generate mip maps (for scaling)
GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGB, tWidth, tHeight, 0,
GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buf);
GL30.glGenerateMipmap(GL11.GL_TEXTURE_2D);
// Setup the ST coordinate system
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);
// Setup what to do when the texture has to be scaled
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER,
GL11.GL_NEAREST);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER,
GL11.GL_LINEAR_MIPMAP_LINEAR);
return texId;
}
private static int createVAO () {
int vaoId = GL30.glGenVertexArrays();
vaos.add(vaoId);
GL30.glBindVertexArray(vaoId);
return vaoId;
}
private static void bindIndicesBuffer (int[] indices) {
int vbo = GL15.glGenBuffers();
vbos.add(vbo);
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, vbo);
IntBuffer buffer = storeDataInIntBuffer(indices);
GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, buffer, GL15.GL_STATIC_DRAW);
}
public static void cleanUp () {
for (int vao : vaos) {
GL30.glDeleteVertexArrays(vao);
}
for (int vbo : vbos) {
GL15.glDeleteBuffers(vbo);
}
for (int texture : textures) {
GL11.glDeleteTextures(texture);
}
}
private static void storeDataInAttributeList (int attributeNumber, int size, float[] data) {
int vboId = GL15.glGenBuffers();
vbos.add(vboId);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboId);
FloatBuffer buffer = storeDataInFloatBuffer(data);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, buffer, GL15.GL_STATIC_DRAW);
GL20.glVertexAttribPointer(attributeNumber, size, GL11.GL_FLOAT, false, 0, 0);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
}
private static void unbindVAO () {
GL30.glBindVertexArray(0);
}
private static IntBuffer storeDataInIntBuffer (int[] data) {
IntBuffer buffer = BufferUtils.createIntBuffer(data.length);
buffer.put(data);
buffer.flip();
return buffer;
}
private static FloatBuffer storeDataInFloatBuffer (float[] data) {
FloatBuffer buffer = BufferUtils.createFloatBuffer(data.length);
buffer.put(data);
buffer.flip();
return buffer;
}
}
I had forgotten to assign gl_Position in the vertex shader.

Categories