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);
}
Related
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);
...
I'm learning LWJGL and OpenGL by following this tutorial i found, i tried my best to change the code to be compatible with the never versions and hadnt a problem with it so far. But now my Tilerenderer wont render more than one type of tile/one texture for the VBO's and i tried to fix it now since 3 days (in the beginning it didn't render anything at all) but couldn't find anything that fixes this problem.
I'm using Java 9.0.4 and LWJGL 3.2.1 build 12 with JOML 1.9.13, GLFW, OpenGL and stb.
So far i tried changing the entire code involved in this problem and changing different variables for shaders but nothing seemed to work so far.
Here are all classes i figured might have something to do with the problem.
The Main class
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL11.*;
import org.joml.Matrix4f;
import org.joml.Vector3f;
import org.lwjgl.opengl.GL;
public class Main {
public static void main(String[] args) {
int speed = 5;
Window.setCallbacks();
if (!glfwInit()) {
throw new IllegalStateException("Failed to init GLFW");
}
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
Window window = new Window();
window.setSize(640, 480);
window.setFullscreen(false);
window.createWindow("GAME");
GL.createCapabilities();
Camera camera = new Camera(window.getWidth(), window.getHeight());
glEnable(GL_TEXTURE_2D);
TileRenderer tiles = new TileRenderer();
Shader shader = new Shader("shader");
World world = new World();
world.setTile(Tile.test_tile, 0, 0);
double frameCap = 1.0 / 60.0;
double frameTime = 0;
double time = Timer.getTime();
double unprocessed = 0;
int frames = 0;
while(!window.shouldClose()) {
boolean canRender = false;
double time2 = Timer.getTime();
double passed = time2 - time;
unprocessed+=passed;
frameTime += passed;
time = time2;
while (unprocessed >= frameCap) {
canRender = true;
unprocessed-=frameCap;
if(window.getInput().isMouseButtonDown(0)) {
glfwSetWindowShouldClose(window.getWindow(), true);
}
if (window.getInput().isKeyPressed(GLFW_KEY_ESCAPE)) {
glfwSetWindowShouldClose(window.getWindow(), true);
}
if(window.getInput().isKeyDown(GLFW_KEY_W)) {
camera.addPosition(new Vector3f(0, -speed, 0));
}
if(window.getInput().isKeyDown(GLFW_KEY_A)) {
camera.addPosition(new Vector3f(speed, 0, 0));
}
if(window.getInput().isKeyDown(GLFW_KEY_S)) {
camera.addPosition(new Vector3f(0, speed, 0));
}
if(window.getInput().isKeyDown(GLFW_KEY_D)) {
camera.addPosition(new Vector3f(-speed, 0, 0));
}
if(window.getInput().isKeyDown(GLFW_KEY_O)) {
speed = 5;
}
if(window.getInput().isKeyDown(GLFW_KEY_P)) {
speed = 25;
}
window.update();
if (frameTime >= 1.0) {
frameTime = 0;
System.out.println("FPS:" + frames);
frames = 0;
}
}
if (canRender) {
glClear(GL_COLOR_BUFFER_BIT);
world.render(tiles, shader, camera);
window.swapBuffers();
frames++;
}
}
glfwTerminate();
}
}
The World class
import org.joml.Matrix4f;
import org.joml.Vector3f;
public class World {
private byte[] tiles;
private int width;
private int height;
private Matrix4f world;
public World () {
width = 16;
height = 16;
tiles = new byte [width * height];
world = new Matrix4f().setTranslation(new Vector3f(0));
world.scale(32);
}
public void render(TileRenderer renderer, Shader shader, Camera camera) {
for (int x = 0; x < height; x++) {
for (int y = 0; y < width; y++) {
renderer.renderTile(tiles[x + y * width], y, -x, shader, world, camera);
}
}
}
public void setTile (Tile tile, int x, int y) {
System.err.println(tile.getId());
tiles[x + y * width] = tile.getId();
}
}
The Tilerenderer class
import java.util.HashMap;
import org.joml.Matrix4f;
import org.joml.Vector3f;
public class TileRenderer {
private HashMap<String, Texture> tileTextures;
private Model tileModel;
public TileRenderer() {
tileTextures = new HashMap<>();
float[] vertices = new float[]{
-1f, 1f, 0, // TOP LEFT 0
1f, 1f, 0, // TOP RIGHT 1
1f, -1f, 0, // BOTTOM RIGHT 2
-1f, -1f, 0,// BOTTOM LEFT 3
};
float[] texture = new float[]{0, 0, 1, 0, 1, 1, 0, 1,};
int[] indices = new int[]{0, 1, 2, 2, 3, 0};
tileModel = new Model(vertices, texture, indices);
for (int i = 0; i < Tile.tiles.length; i++) {
if (Tile.tiles[i] != null) {
if (!tileTextures.containsKey(Tile.tiles[i].getTexture())) {
String tex = Tile.tiles[i].getTexture();
tileTextures.put(tex, new Texture(tex + ".png"));
}
}
}
}
public void renderTile (byte id, int x, int y, Shader shader, Matrix4f world, Camera camera) {
shader.bind();
if (tileTextures.containsKey(Tile.tiles[id].getTexture())) {
tileTextures.get(Tile.tiles[id].getTexture()).bind(0);
}
Matrix4f tilePos = new Matrix4f().translate(new Vector3f(x*2, y*2, 0));
Matrix4f target = new Matrix4f();
camera.getProjection().mul(world, target);
target.mul(tilePos);
shader.setUniform("sampler", 0);
shader.setUniform("projection", target);
tileModel.render();
}
}
The Tile class
public class Tile {
public static Tile tiles[] = new Tile[16];
public static final Tile testTile = new Tile((byte)0, "Test");
public static final Tile testTile2 = new Tile((byte)1, "Test2");
private byte id;
private String texture;
public Tile(byte id, String texture) {
this.id = id;
this.texture = texture;
if (tiles[id] != null) {
throw new IllegalStateException("Tiles at: [" + id + "] is already being used!");
}
tiles[id] = this;
}
public byte getId () {return id;}
public String getTexture () {return texture;}
}
The Model class
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL15.*;
import static org.lwjgl.opengl.GL20.*;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import org.lwjgl.BufferUtils;
public class Model {
private int draw_count;
private int v_id;
private int t_id;
private int i_id;
public Model (float[] vertices, float[] tex_coords, int[] indices) {
draw_count = indices.length;
IntBuffer buffer = BufferUtils.createIntBuffer(indices.length);
buffer.put(indices);
buffer.flip();
v_id = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, v_id);
glBufferData(GL_ARRAY_BUFFER, createBuffer(vertices), GL_STATIC_DRAW);
t_id = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, t_id);
glBufferData(GL_ARRAY_BUFFER, createBuffer(tex_coords), GL_STATIC_DRAW);
i_id = glGenBuffers();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, i_id);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, buffer, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
public void render() {
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, v_id);
glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, t_id);
glVertexAttribPointer(1, 2, GL_FLOAT, false, 0, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, i_id);
glDrawElements(GL_TRIANGLES, draw_count, GL_UNSIGNED_INT, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
}
private FloatBuffer createBuffer(float[] data) {
FloatBuffer buffer = BufferUtils.createFloatBuffer(data.length);
buffer.put(data);
buffer.flip();
return buffer;
}
}
The Texture class
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import static org.lwjgl.stb.STBImage.*;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL13.*;
import org.lwjgl.BufferUtils;
public class Texture {
private int id;
private int width;
private int heigth;
public Texture (String filename) {
IntBuffer width = BufferUtils.createIntBuffer(1);
IntBuffer heigth = BufferUtils.createIntBuffer(1);
IntBuffer comp = BufferUtils.createIntBuffer(1);
ByteBuffer data = stbi_load("./res/" + filename, width, heigth, comp, 4);
this.width = width.get();
this.heigth = heigth.get();
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, this.width, this.heigth, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
stbi_image_free(data);
}
public void bind (int sampler) {
if (sampler >= 0 && sampler <= 31) {
glActiveTexture(GL_TEXTURE0 + sampler);
glBindTexture(GL_TEXTURE_2D, sampler);
}
}
}
The Shader class
import static org.lwjgl.opengl.GL20.*;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.nio.FloatBuffer;
import org.joml.Matrix4f;
import org.lwjgl.BufferUtils;
public class Shader {
private int program;
private int vs;
private int fs;
public Shader (String filename) {
program = glCreateProgram();
vs = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vs, readFile(filename + ".vs"));
glCompileShader(vs);
if (glGetShaderi(vs, GL_COMPILE_STATUS) != 1) {
System.err.println(glGetShaderInfoLog(vs));
System.exit(1);
}
fs = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fs, readFile(filename + ".fs"));
glCompileShader(fs);
if (glGetShaderi(fs, GL_COMPILE_STATUS) != 1) {
System.err.println(glGetShaderInfoLog(fs));
System.exit(1);
}
glAttachShader(program, vs);
glAttachShader(program, fs);
glBindAttribLocation(program, 0, "vertices");
glBindAttribLocation(program, 1, "textures");
glLinkProgram(program);
if (glGetProgrami(program, GL_LINK_STATUS) != 1) {
System.err.println(glGetProgramInfoLog(program));
System.exit(1);
}
glValidateProgram(program);
if (glGetProgrami(program, GL_VALIDATE_STATUS) != 1) {
System.err.println(glGetProgramInfoLog(program));
System.exit(1);
}
}
public void bind () {
glUseProgram(program);
}
private String readFile (String filename) {
StringBuilder string = new StringBuilder();
BufferedReader br;
try {
br = new BufferedReader(new FileReader(new File("./shaders/" + filename)));
String line;
while((line = br.readLine()) != null) {
string.append(line);
string.append("\n");
}
} catch (IOException e ) {e.printStackTrace();}
return string.toString();
}
public void setUniform (String name, int value) {
int location = glGetUniformLocation(program, name);
if (location != -1) {
glUniform1i(location, value);
}
}
public void setUniform (String name, Matrix4f value) {
int location = glGetUniformLocation(program, name);
FloatBuffer buffer = BufferUtils.createFloatBuffer(16);
value.get(buffer);
if (location != -1) {
glUniformMatrix4fv(location, false, buffer);
}
}
}
The Fragment Shader
#version 120
uniform sampler2D sampler;
varying vec2 tex_coords;
void main () {
gl_FragColor = texture2D(sampler, tex_coords);
}
The Vertex Shader
#version 120
attribute vec3 vertices;
attribute vec2 textures;
varying vec2 tex_coords;
uniform mat4 projection;
void main() {
tex_coords = textures;
gl_Position = projection*vec4(vertices, 1);
}
So far I'm creating 16x16 tiles all with the same texture but it is supposed to change the tile at 0, 0 (top left corner) to have a different texture
There is a basic misunderstanding, about how texturing works in OpenGL.
You have to create a separate texture object for each texture by glGenTextures. (See also Java Code Examples for org.lwjgl.opengl.GL11.glTexImage2D()).
int textureObject = glGenTextures();
This texture object has to be bound before loading the texture and before rendering the mesh. The texture is bound to the active texture unit, which is set by glActiveTexture.
int textureUnitIndex = 0; // e.g
glActiveTexture(GL_TEXTURE0 + textureUnitIndex);
glBindTexture(GL_TEXTURE_2D, textureObject);
The texture unit is a binding point for the shader program. The texture sampler in the shader program has to be associated to the same binding point. This can be done by glUniform1i:
GLSL:
uniform sampler2D sampler;
Java:
int location = glGetUniformLocation(program, "sampler");
glUniform1i(location, textureUnitIndex);
Sidenote: since GLSL version 4.2 this can be done in the fragment shader by specifying binding points - See OpenGL Shading Language 4.20 Specification - 4.4.4 Opaque-Uniform Layout Qualifiers; page 60:
#version 420
layout (binding = 0) uniform sampler2D sampler;
In your code never a texture object is generated. So the default texture object 0 is used for all textures. This causes that you code doesn't work.
Change the class Texture, somehow as follows, to solve the issue:
public class Texture {
private int textureObject;
private int width;
private int heigth;
public Texture (String filename) {
IntBuffer width = BufferUtils.createIntBuffer(1);
IntBuffer heigth = BufferUtils.createIntBuffer(1);
IntBuffer comp = BufferUtils.createIntBuffer(1);
ByteBuffer data = stbi_load("./res/" + filename, width, heigth, comp, 4);
this.width = width.get();
this.heigth = heigth.get();
textureObject = glGenTextures(); // generate texture name
glBindTexture(GL_TEXTURE_2D, textureObject);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, this.width, this.heigth, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
stbi_image_free(data);
}
public void bind (int sampler) {
if (sampler >= 0 && sampler <= 31) {
glActiveTexture(GL_TEXTURE0 + sampler);
glBindTexture(GL_TEXTURE_2D, textureObject); // bind texture object
}
}
}
When i try to enable projectionMatrix the rendered image disappear, i see only black screen. The console don't return any error.
Where is the problem?
Renderer
package ms.renderer;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL13.*;
import static org.lwjgl.opengl.GL20.*;
import static org.lwjgl.opengl.GL30.*;
import org.joml.Matrix4f;
import ms.Display;
import ms.renderer.VertexArrayObject.Vertex;
import ms.shaders.ShaderProgram;
import ms.utils.FileLoader;
import ms.utils.TextureLoader;
public class Renderer {
VertexArrayObject vertices;
ShaderProgram shaders;
Display display;
Matrix4f projectionMatrix;
private float FOV = (float) Math.toRadians(60.0f);
private float Z_NEAR = 0.01f;
private float Z_FAR = 1000.0f;
public Renderer() {
display = new Display();
vertices = new VertexArrayObject();
}
public void init() throws Exception {
shaders = new ShaderProgram();
shaders.createVertexShader(FileLoader.loadResources("/ms/resources/shaders/vertexShader.vs"));
shaders.createFragmentShader(FileLoader.loadResources("/ms/resources/shaders/fragmentShader.fs"));
shaders.link();
float aspectRation = (float) display.getWidth() / display.getHeight();
projectionMatrix = new Matrix4f().perspective(FOV, aspectRation, Z_NEAR, Z_FAR);
shaders.createUniforms("projectionMatrix");
shaders.createUniforms("textureSampler");
}
public void render(Vertex vertex, TextureLoader texture) {
clear();
shaders.bindPorgram();
shaders.setUniform("projectionMatrix", projectionMatrix);
shaders.setUniform("textureSampler", 0);
glBindVertexArray(vertex.getVaoID());
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture.getId());
glDrawElements(GL_TRIANGLES, vertex.getVertexCount(), GL_UNSIGNED_INT, 0);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glBindVertexArray(0);
shaders.unbindProgram();
}
public void clear() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
public void clenaUp() {
vertices.cleanVAO();
shaders.cleanUp();
}
}
VertexShader
#version 400 core
in vec3 position;
in vec2 textureCoords;
out vec2 outTextureCoords;
uniform mat4 projectionMatrix;
void main()
{
gl_Position = projectionMatrix * vec4(position, 1.0);
outTextureCoords = textureCoords;
}
MainClass - gameLoop() method
public void gameLoop() throws Exception {
isRunning = true;
display.libVersion();
renderer.init();
float[] vertices = {
-0.5f, 0.5f, -5.0f,
-0.5f, -0.5f, -5.0f,
0.5f, -0.5f, -5.0f,
0.5f, 0.5f, -5.0f
};
int[] indices = {
0,1,3,
3,1,2
};
float[] textCoords = {
0, 0,
0, 1,
1, 1,
1, 0
};
Vertex model = loader.loadVAO(vertices, textCoords, indices);
TextureLoader texture = new TextureLoader("image");
while (isRunning && !display.windowShouldClose()) {
update();
renderer.render(model, texture);
if(display.windowShouldClose()) {
renderer.clenaUp();
isRunning = false;
}
}
}
If i remove projectionMatrix the image render normally. The projectionMatrix uniform loading by convert the value in Floatbuffer.
ShaderProgram - setUniform() method
public void setUniform(String uniformName, Matrix4f value) {
FloatBuffer buffers = BufferUtils.createFloatBuffer(16);
value.get(buffers);
glUniformMatrix4fv(uniforms.get(uniformName), false, buffers);
}
I found the error:
Renderer
public void createProjectionMatrix() {
Matrix4f projectionMatrix = transformation.getProjectionMatrix(
FOV,
display.getWidth(), //Solve by indicate an int value
display.getHeight(), //Solve by indicate an int value
Z_NEAR,
Z_FAR);
shaders.setUniform("projectionMatrix", projectionMatrix);
}
but if i resize the window, the image move to the right corner, obviusly.
How can I resolve the getWidth() and getHeight() method?
Display
import org.lwjgl.*;
import org.lwjgl.glfw.*;
import org.lwjgl.opengl.*;
import ms.input.KeyboardInput;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.system.MemoryUtil.*;
public class Display {
GLFWKeyCallback keyCallback;
GLFWWindowSizeCallback windowSizeCallback;
private int width;
private int height;
private String title;
public long window;
public Display() {}
public Display(String title, int width, int height) {
this.title = title;
this.width = width;
this.height = height;
}
public void init() {
GLFWErrorCallback.createPrint(System.err).set();
if ( !glfwInit() )
throw new IllegalStateException("Unable to initialize GLFW");
glfwDefaultWindowHints();
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
window = glfwCreateWindow(width, height, title, NULL, NULL);
if ( window == NULL )
throw new RuntimeException("Failed to create the GLFW window");
glfwSetWindowSizeCallback(window, windowSizeCallback = new GLFWWindowSizeCallback() {
#Override
public void invoke(long window, int width, int height) {
Display.this.width = width;
Display.this.height = height;
}
});
glfwSetKeyCallback(window, keyCallback = new KeyboardInput());
GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
glfwSetWindowPos(
window,
(vidmode.width() - width) / 2,
(vidmode.height() - height) / 2
);
glfwMakeContextCurrent(window);
glfwSwapInterval(1);
glfwShowWindow(window);
GL.createCapabilities();
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
}
public void libVersion() {
System.out.println("LWJGL Version: " + Version.getVersion() + "!");
System.out.println("GLFW Version: " + glfwGetVersionString() + "!");
System.out.println("OpenGL Version: " + glGetString(GL_VERSION));
}
public boolean windowShouldClose() {
return glfwWindowShouldClose(window);
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
I have been spending the last few days trying to scale a texture to a Quad (OpenGL 3)...
But for some reason I am getting only the top left pixel of the image rendered on to the quad, I am using Slick-Utils to load and bind the texture....
Here is the code:
MainGame.java
package test.game;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.ContextAttribs;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.PixelFormat;
import org.newdawn.slick.opengl.Texture;
import test.engine.Loader;
import test.engine.rendersystem.Model;
import test.engine.rendersystem.ShaderProgram;
import static org.lwjgl.opengl.GL11.*;
import java.io.IOException;
import java.util.HashMap;
public class MainGame {
// Constants
public static final String WINDOW_TITLE = "Neon Indev";
public static final int WIDTH = 800;
public static final int HEIGHT = 480;
public static final ContextAttribs OPENGL_CONTEXTS_ATTRIBS = new ContextAttribs(3, 2).withForwardCompatible(true).withProfileCore(true);
public static final int SYNC_FPS = 60;
static float[] vertexData =
{
-0.5f, 0.5f,
0.5f, 0.5f,
0.5f, -0.5f,
-0.5f, -0.5f
};
static float[] colorData = { 0f, 1f, 1f, 1f,
0f, 1f, 1f, 1f,
0f, 1f, 1f, 1f,
0f, 1f, 1f, 1f,
0f, 1f, 1f, 1f,
0f, 1f, 1f, 1f};
static int[] indexData = {
0, 1, 3,
3, 1, 2
};
static float[] texData = {
0,0,
1,0,
1,1,
0,1
};
static Model model;
static ShaderProgram shader;
static Texture testTexture;
public static void main(String[] args) {
init();
render();
}
private static void init() {
// Display Initialization
try {
Display.setTitle(WINDOW_TITLE);
Display.setDisplayMode(new DisplayMode(WIDTH, HEIGHT));
Display.create(new PixelFormat(), OPENGL_CONTEXTS_ATTRIBS);
} catch (LWJGLException e) {
e.printStackTrace();
System.exit(1);
}
// OpenGL Initialization
glViewport(0, 0, WIDTH, HEIGHT);
glClearColor(0f, 0f, 0f, 1f);
model = new Model(vertexData, colorData, indexData);
try {
testTexture = Loader.loadTexture("res/images/png/image02 (1).png", "PNG");
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
model.setTexture(testTexture, texData);
// Shader Initialization
HashMap<String, Integer> vboDataToBind = new HashMap<String, Integer>();
vboDataToBind.put("in_Position", Model.ATTRIB_VERTEX);
vboDataToBind.put("in_Color", Model.ATTRIB_COLOR);
vboDataToBind.put("in_TextureCoord", Model.ATTRIB_TEXTURE);
shader = new ShaderProgram("res/shaders/frag/color.frag", "res/shaders/vert/color.vert", true, true, vboDataToBind);
}
private static void render() {
// Resize Check
if (Display.wasResized()) {
resize();
}
// Render loop
while (!Display.isCloseRequested()) {
glClear(GL_COLOR_BUFFER_BIT);
shader.bind();
model.render();
shader.unbind();
Display.update();
Display.sync(SYNC_FPS);
}
// Dispose if out of the loop
dispose();
}
private static void resize() {
// Resize code goes here
}
private static void dispose() {
model.dispose();
shader.dispose();
Display.destroy();
}
}
ShaderProgram.java
package test.game.engine.rendersystem;
import java.io.IOException;
import java.util.HashMap;
import java.util.Set;
import test.engine.Loader;
import static org.lwjgl.opengl.GL20.*;
public class ShaderProgram {
String fragmentShader;
String vertexShader;
int programID, vertexShaderID, fragmentShaderID;
boolean initialized;
public ShaderProgram(String fragmentShader, String vertexShader, boolean url, boolean init, HashMap<String, Integer> vboDataToBind) {
if (url) {
try {
this.fragmentShader = Loader.getStringFromTextFile(fragmentShader);
this.vertexShader = Loader.getStringFromTextFile(vertexShader);
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
} else {
this.fragmentShader = fragmentShader;
this.vertexShader = vertexShader;
}
if (init) {
init(vboDataToBind);
this.initialized = true;
} else {
this.initialized = false;
}
}
public void init(HashMap<String, Integer> vboDataToBind) {
if (!initialized) {
// Initialize the shaders
vertexShaderID = glCreateShader(GL_VERTEX_SHADER);
fragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
// Setting up the shader values
glShaderSource(vertexShaderID, vertexShader);
glShaderSource(fragmentShaderID, fragmentShader);
// Compile the shaders
glCompileShader(vertexShaderID);
glCompileShader(fragmentShaderID);
// Create the shader program and attach them
programID = glCreateProgram();
glAttachShader(programID, vertexShaderID);
glAttachShader(programID, fragmentShaderID);
// Bind any needed data
if (!vboDataToBind.isEmpty()) {
Set<String> keys = vboDataToBind.keySet();
for (String key : keys) {
bindVBOAttrib(vboDataToBind.get(key), key);
}
}
// Link and validate the program
glLinkProgram(programID);
glValidateProgram(programID);
}
}
public void bind() {
glUseProgram(programID);
}
public void unbind() {
glUseProgram(0);
}
public void dispose() {
glDeleteProgram(programID);
glDeleteShader(vertexShaderID);
glDeleteShader(fragmentShaderID);
}
public void bindVBOAttrib(int attribNumber, String variableName) {
glBindAttribLocation(programID, attribNumber, variableName);
}
public String getFragmentShader() {
return fragmentShader;
}
public String getVertexShader() {
return vertexShader;
}
public int getProgramID() {
return programID;
}
public int getVertexShaderID() {
return vertexShaderID;
}
public int getFragmentShaderID() {
return fragmentShaderID;
}
}
Vertex Shader:
#version 150 core
in vec4 in_Position;
in vec4 in_Color;
in vec2 in_TextureCoord;
out vec4 pass_Color;
out vec2 pass_TextureCoord;
void main(void) {
gl_Position = in_Position;
pass_Color = in_Color;
pass_TextureCoord = in_TextureCoord;
}
Fragment Shader:
#version 150 core
uniform sampler2D texture_diffuse;
in vec4 pass_Color;
in vec2 pass_TextureCoord;
out vec4 out_Color;
void main(void) {
out_Color = pass_Color;
// Override out_Color with our texture pixel
out_Color = texture(texture_diffuse, pass_TextureCoord);
}
Loader.java
package test.game.engine;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import org.newdawn.slick.opengl.Texture;
import org.newdawn.slick.opengl.TextureLoader;
import org.newdawn.slick.util.ResourceLoader;
public class Loader {
public static String getStringFromTextFile(String url) throws IOException {
BufferedReader reader = new BufferedReader(new FileReader(new File(url)));
String line;
StringBuilder response = new StringBuilder();
while ((line = reader.readLine()) != null) {
response.append(line).append("\n");
}
reader.close();
return response.toString();
}
public static Texture loadTexture(String url, String format) throws IOException {
return TextureLoader.getTexture(format, ResourceLoader.getResourceAsStream(url));
}
}
Model.java
package test.game.engine.rendersystem;
import static org.lwjgl.opengl.GL30.*;
import static org.lwjgl.opengl.GL13.*;
import static org.lwjgl.opengl.GL11.*;
import org.newdawn.slick.opengl.Texture;
public class Model {
// Attribute Numbers
public static final int ATTRIB_VERTEX = 0;
public static final int ATTRIB_INDEX = 1;
public static final int ATTRIB_COLOR = 2;
public static final int ATTRIB_TEXTURE = 3;
// Dimensions
public static final int DIMEN_VERTEX = 2;
public static final int DIMEN_INDEX = 1;
public static final int DIMEN_COLOR = 4;
public static final int DIMEN_TEXTURE = 2;
int vaoID;
int vertexCount;
VBO vertexData;
VBO colorData;
VBO textureData;
IntVBO indexData;
boolean textured;
Texture texture;
public Model(float[] vertexData, float[] colorData, int[] indexData) {
this.vertexCount = vertexData.length;
this.vaoID = glGenVertexArrays();
this.textured = false;
bindVertexArray();
this.vertexData = new VBO(ATTRIB_VERTEX, vaoID, vertexData, DIMEN_VERTEX, true);
this.colorData = new VBO(ATTRIB_COLOR, vaoID, colorData, DIMEN_COLOR, true);
this.indexData = new IntVBO(ATTRIB_INDEX, vaoID, indexData, DIMEN_INDEX, true);
unbindVertexArray();
}
public boolean isTextured() {
return textured;
}
public void setTexture(Texture texture, float[] texCoords) {
this.textured = true;
this.textureData = new VBO(ATTRIB_TEXTURE, vaoID, texCoords, DIMEN_TEXTURE, true);
this.texture = texture;
}
public void unbindVertexArray() {
glBindVertexArray(0);
}
public void bindVertexArray() {
glBindVertexArray(vaoID);
}
public void dispose() {
vertexData.dispose();
colorData.dispose();
indexData.dispose();
glDeleteVertexArrays(vaoID);
}
public void render() {
bindVertexArray();
colorData.bind();
vertexData.bind();
if (textured) textureData.bind();
indexData.bind();
vertexData.enable();
if (textured) textureData.enable();
colorData.enable();
indexData.enable();
// Loading in the texture data
if (textured) {
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture.getTextureID());
}
glDrawElements(GL_TRIANGLES, this.vertexCount, GL_UNSIGNED_INT, 0);
colorData.disable();
vertexData.disable();
indexData.disable();
if (textured) textureData.disable();
vertexData.unbind();
colorData.unbind();
indexData.unbind();
if (textured) textureData.disable();
unbindVertexArray();
}
}
VBO.java
package test.game.engine.rendersystem;
import static org.lwjgl.opengl.GL20.*;
import static org.lwjgl.opengl.GL11.*;
import java.nio.FloatBuffer;
import org.lwjgl.BufferUtils;
import static org.lwjgl.opengl.GL15.*;
public class VBO {
public int vboID;
public int attributeNumber;
public int vaoID;
public int dataDimensions;
public float[] data;
public VBO(int attributeNumber, int vaoID, float[] data, int dimensions, boolean loadToVBO) {
this.attributeNumber = attributeNumber;
this.vaoID = vaoID;
this.data = data;
this.dataDimensions = dimensions;
if (loadToVBO) {
loadToVBO();
}
}
public void loadToVBO() {
this.vboID = glGenBuffers();
bind();
glBufferData(GL_ARRAY_BUFFER, storeDataInFloatBuffer(), GL_STATIC_DRAW);
glVertexAttribPointer(attributeNumber, dataDimensions, GL_FLOAT, false, 0, 0);
unbind();
}
public void bind() {
glBindBuffer(GL_ARRAY_BUFFER, vboID);
}
public void unbind() {
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
public void enable() {
glEnableVertexAttribArray(attributeNumber);
}
public void disable() {
glDisableVertexAttribArray(attributeNumber);
}
public void dispose() {
glDeleteBuffers(vboID);
}
private FloatBuffer storeDataInFloatBuffer() {
FloatBuffer buffer = BufferUtils.createFloatBuffer(data.length);
buffer.put(data);
buffer.flip();
return buffer;
}
}
Here is the image I wanted to render (It is a test image) (It is in PNG)
A cow:
Here is the output I got. A cow on the quad:
P.S I am a new to OpenGL and low level graphics APIs.... So forgive me if I did any newb/noob mistakes.
I believe your problem lies here:
public void setTexture(Texture texture, float[] texCoords) {
this.textured = true;
this.textureData = new VBO(ATTRIB_TEXTURE, vaoID, texCoords, DIMEN_TEXTURE, true);
this.texture = texture;
}
When you created the initial vbos you bound the VAO then unbound it which allowed the drawing to take place, but when you add the texture coordinates no VAO is bound leading to your vbo being bound to nothing. When render is then called and the texture coordinates called upon nothing is found and so the default 0;0 coordinates, aka the single corner pixel, are used.
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.