(This is the second time I am asking this question. Last time I got one answer that didn't solve it (the answer there referred a bit of code that was left accidentally from one of my attempts at fixing this). I also changed the question itself slightly - I changed the order of code to put the part where I believe the mistake higher is and added that I am using macOS, which might be the reason that it doesn't work).
So, I just started learning LWJGL 3 and I used a mixture of a couple of tutorials and example code to make something that should render a rectangle to a magenta screen using VAOs and VBOs. There are no errors but the rectangle doesn't appear on screen (all I can see is a magenta screen). I tried using the old LWJGL pipeline (glBegin() and glEnd()) and it does work so I tried changing random things in the rendering code and the loading to VAOs and VBOs. I also tried to bind the VBO too but it didn't change anything. I also tried debugging and it seems like there is a VAO and a VBO that is created.
Can someone take a look at my code and see if something looks wrong? Here it is (sorry if its a bit messy. Like I said, I don't think the problem is in the Main or Window class but I have no idea about LWJGL so I wanted to put it here anyways. If you have time, please also look at them. Otherwise I would really appreciate it if you could look at the Loader and Renderer classes):
(Btw, I am using macOS, and I do have -XstartOnFirstThread on).
The Raw Model class:
package engine.io;
public class RawModel {
private int vaoID;
private int vertexCount;
public RawModel(int vaoID, int vertexCount) {
this.vaoID = vaoID;
this.vertexCount = vertexCount;
}
public int getVaoID() {
return vaoID;
}
public int getVertexCount() {
return vertexCount;
}
}
The loader class:
package engine.io;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.List;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import org.lwjgl.system.MemoryUtil;
public class Loader {
private List<Integer> vaos = new ArrayList<Integer>();
private List<Integer> vbos = new ArrayList<Integer>();
public RawModel loadToVAO(float[] positions) {
int vaoID = createVAO();
storeDataInAttributeList(0, positions);
unbindVAO();
return new RawModel(vaoID, positions.length/3);
}
private int createVAO() {
int vaoID = GL30.glGenVertexArrays();
vaos.add(vaoID);
GL30.glBindVertexArray(vaoID);
return vaoID;
}
private void storeDataInAttributeList(int attributeNumber, 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, 3, GL11.GL_FLOAT, false, 0, 0);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
}
private void unbindVAO() {
GL30.glBindVertexArray(0);
}
private FloatBuffer storeDataInFloatBuffer(float[] data) {
FloatBuffer buffer = MemoryUtil.memAllocFloat(data.length);
buffer.put(data);
buffer.flip();
return buffer;
}
public void cleanUp() {
for (int vao : vaos) {
GL30.glDeleteVertexArrays(vao);
}
for (int vbo : vbos) {
GL15.glDeleteBuffers(vbo);
}
}
}
The renderer class:
package engine.io;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
public class Renderer {
public void prepare() {
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
GL11.glClearColor(1f, 0f, 1f, 1f);
}
public void render(RawModel model) {
GL30.glBindVertexArray(model.getVaoID());
GL20.glEnableVertexAttribArray(0);
GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, model.getVertexCount());
GL20.glDisableVertexAttribArray(0);
GL30.glBindVertexArray(0);
}
}
Here is the Main class:
import org.lwjgl.*;
import org.lwjgl.glfw.*;
import org.lwjgl.opengl.*;
import org.lwjgl.system.*;
import engine.io.Loader;
import engine.io.RawModel;
import engine.io.Renderer;
import engine.io.Window;
import java.nio.*;
import static org.lwjgl.glfw.Callbacks.*;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.system.MemoryStack.*;
import static org.lwjgl.system.MemoryUtil.*;
public class Main {
private Window window;
Loader loader = new Loader();
Renderer renderer = new Renderer();
float[] vertices = {
// Left bottom triangle
-0.5f, 0.5f, 0f,
-0.5f, -0.5f, 0f,
0.5f, -0.5f, 0f,
// Right top triangle
0.5f, -0.5f, 0f,
0.5f, 0.5f, 0f,
-0.5f, 0.5f, 0f
};
RawModel model;
public void run() {
setup();
loop();
loader.cleanUp();
glfwFreeCallbacks(window.getWindowNum());
glfwDestroyWindow(window.getWindowNum());
glfwTerminate();
glfwSetErrorCallback(null).free();
}
private void loop() {
while ( !glfwWindowShouldClose(window.getWindowNum()) ) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the framebuffer
renderer.render(model);
glfwSwapBuffers(window.getWindowNum()); // swap the color buffers
glfwPollEvents();
}
}
public void setup() {
window = new Window(900, 300, "Flappy Bird");
window.create();
GL.createCapabilities();
GLFW.glfwMakeContextCurrent(window.getWindowNum());
model = loader.loadToVAO(vertices);
renderer.prepare();
GL11.glViewport(0, 0, 900, 300);
}
public static void main(String[] args) {
new Main().run();
}
}
Here is the window class:
package engine.io;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.system.MemoryStack.stackPush;
import static org.lwjgl.system.MemoryUtil.*;
import java.nio.IntBuffer;
//import static org.lwjgl.glfw.GLFW.*;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.glfw.GLFWErrorCallback;
import org.lwjgl.glfw.GLFWVidMode;
import org.lwjgl.system.MemoryStack;
public class Window {
private int width, height;
private String title;
private long window;
public Window(int width, int height, String title) {
this.width = width;
this.height = height;
this.title = title;
}
public void create() {
GLFWErrorCallback.createPrint(System.err).set();
if (!glfwInit())
throw new IllegalStateException("Unable to initialize GLFW");
// Configure GLFW
glfwDefaultWindowHints(); // optional, the current window hints are already the default
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // the window will stay hidden after creation
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // the window will be resizable
// Create the window
window = glfwCreateWindow(width, height, "Flappy Bird", NULL, NULL);
if (window == NULL)
throw new RuntimeException("Failed to create the GLFW window");
// Get the thread stack and push a new frame
try (MemoryStack stack = stackPush()) {
IntBuffer pWidth = stack.mallocInt(1); // int*
IntBuffer pHeight = stack.mallocInt(1); // int*
// Get the window size passed to glfwCreateWindow
glfwGetWindowSize(window, pWidth, pHeight);
// Get the resolution of the primary monitor
GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
// Center the window
glfwSetWindowPos(window, (vidmode.width() - pWidth.get(0)) / 2, (vidmode.height() - pHeight.get(0)) / 2);
} // the stack frame is popped automatically
// Setup a key callback. It will be called every time a key is pressed, repeated
// or released.
glfwSetKeyCallback(window, (window, key, scancode, action, mods) -> {
if (key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE)
glfwSetWindowShouldClose(window, true); // We will detect this in the rendering loop
});
// Make the OpenGL context current
glfwMakeContextCurrent(window);
// Enable v-sync
glfwSwapInterval(1);
// Make the window visible
glfwShowWindow(window);
}
public long getWindowNum() {
return window;
}
}
Since you are on MacOS, I recommend to set up a 3.2 core profile OpenGL Context. See OpenGL Development on OS X:
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, true);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// Create the window
window = glfwCreateWindow(width, height, "Flappy Bird", NULL, NULL);
Related
My code does not work. I think there is a way to render something on the screen without shaders but how? I heard something about modern OpenGL rendering and how OpenGL needs shaders to render. Help me.
This is my code:
import static org.lwjgl.glfw.GLFW.glfwDestroyWindow;
import static org.lwjgl.glfw.GLFW.glfwTerminate;
import java.nio.FloatBuffer;
import org.lwjgl.BufferUtils;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.glfw.GLFWVidMode;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GL30;
import org.lwjgl.system.MemoryUtil;
public class Main {
public static long window;
public static boolean running = true;
public static void createWindow() {
GLFW.glfwInit();
GLFW.glfwDefaultWindowHints();
GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, GL30.GL_FALSE);
GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, GL30.GL_TRUE);
window = GLFW.glfwCreateWindow(600, 600, "RenderQuad", 0, 0);
GLFW.glfwMakeContextCurrent(window);
GLFWVidMode vidmode = GLFW.glfwGetVideoMode(GLFW.glfwGetPrimaryMonitor());
GLFW.glfwSetWindowPos(window, (vidmode.width() - 600) / 2, (vidmode.height() - 600) / 2);
GLFW.glfwShowWindow(window);
GL.createCapabilities();
GL30.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
}
public static int vaoID;
public static int vboID;
public static void render() {
float[] vertices = {
-0.5f, 0.5f, 0f,
-0.5f, -0.5f, 0f,
0.5f, -0.5f, 0f,
0.5f, -0.5f, 0f,
0.5f, 0.5f, 0f,
-0.5f, 0.5f, 0f
};
vaoID = GL30.glGenVertexArrays();
GL30.glBindVertexArray(vaoID);
FloatBuffer buffer = MemoryUtil.memAllocFloat(vertices.length);
buffer.put(vertices);
buffer.flip();
vboID = GL30.glGenBuffers();
GL30.glBindBuffer(GL30.GL_ARRAY_BUFFER, vboID);
GL30.glBufferData(GL30.GL_ARRAY_BUFFER, buffer, GL30.GL_STATIC_DRAW);
MemoryUtil.memFree(buffer);
GL30.glVertexAttribPointer(0, 3, GL30.GL_FLOAT, false, 0, 0);
GL30.glBindBuffer(GL30.GL_ARRAY_BUFFER, 0);
GL30.glBindVertexArray(0);
}
public static void loopCycle() {
GL30.glClear(GL30.GL_COLOR_BUFFER_BIT);
GL30.glEnableVertexAttribArray(0);
GL30.glBindVertexArray(vaoID);
GL30.glDrawArrays(GL30.GL_TRIANGLES, 0, 6);
GL30.glDisableVertexAttribArray(0);
GL30.glBindVertexArray(0);
}
public static void clean() {
GL30.glDisableVertexAttribArray(0);
GL30.glBindBuffer(GL30.GL_ARRAY_BUFFER, 0);
GL30.glBindVertexArray(0);
GL30.glDeleteBuffers(vboID);
GL30.glDeleteVertexArrays(vaoID);
glfwDestroyWindow(window);
glfwTerminate();
}
public static void loop() {
GLFW.glfwSwapBuffers(window);
GLFW.glfwPollEvents();
//GL30.glClear(GL30.GL_COLOR_BUFFER_BIT | GL30.GL_DEPTH_BUFFER_BIT);
}
public static void main(String[] args) {
createWindow();
render();
while(running) {
if(GLFW.glfwWindowShouldClose(window)) {running = false; break;}
loopCycle();
loop();
}
clean();
}
}
You do not have the array for vertex attribute 0 enabled when you draw. The array enable state for each vertex attribute is part of the VAO, and when you call glEnableVertexAttribArray it will affect the currently bound VAO.
I have no idea where this comes from, but a lot of people (and seemingly also tutorials) use a scheme like:
Setup() {
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
// ... [set up some VBOs and maybe EBO]
glVertexAttribPointer(...);
glBindVertexArray(vao);
}
Draw()
{
glBindVertexArray(vao);
glEnableVertexAttribArray(...);
glDraw...(...);
glDisableVertexAttribArray(...);
glBindVertexArray(0);
}
Now this scheme in principle works, you just mistakenly enable the array while VAO 0 is still bound, and then switch to vao for which no array is enabled at all.
But this scheme is utterly inefficient, the VAO does store this information for a reason: so that you not have to re-specify it each time you want to use it. As a result, the glEnableVertexAttribArray() belongs into the Setup function, and should only be called again if the actual set of vertex attributes for that particular VAO changes (which is never in those examples).
I heard something about modern OpenGL rendering and how OpenGL needs shaders to render.
Yes. Note that shaders were introduced to OpenGL with Version 2.0 in 2004, so that's quite a a strecht of the term modern when it comes to GPU development. You really should consider switching to a 3.2 core profile context, where all the legacy deprecated stuff leftover from the 90s is removed.
I have an issue regarding LWJGL and Slick
If I import a texture using Slick while having alpha blending enabled, the display will stop rendering!
Here is the code. Upon start it will simply render a purple square. Once the button T is pressed it will simply import the texture. Although i never actually bind the texture, the screen still turns black!
Here is my code:
import static org.lwjgl.opengl.GL11.GL_BLEND;
import java.io.IOException;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.newdawn.slick.opengl.Texture;
import org.newdawn.slick.opengl.TextureLoader;
import org.newdawn.slick.util.ResourceLoader;
public class Game {
public static void main(String[] args) throws Exception {
Display.setDisplayMode(new DisplayMode(800, 600));
Display.create();
Game game = new Game();
while (!Display.isCloseRequested()) {
game.update();
}
game.close();
}
public Game() {
initGL();
}
Texture boxTexture;
public void update() {
clearGL();
// Use the texture!
GL11.glColor3f(0.4f, 0.2f, 0.9f);
GL11.glBegin(GL11.GL_QUADS);
GL11.glVertex2f(100, 100);
GL11.glVertex2f(140, 100);
GL11.glVertex2f(140, 140);
GL11.glVertex2f(100, 140);
GL11.glEnd();
if(Keyboard.isKeyDown(Keyboard.KEY_T))
{
try {
boxTexture = TextureLoader.getTexture("png",
ResourceLoader.getResourceAsStream("res/ps.png"));
} catch (IOException e) {
e.printStackTrace();
}
}
Display.update();
}
public void close() {
Display.destroy();
System.exit(0);
}
public void initGL() {
// Enable Alpha Blending
GL11.glEnable(GL_BLEND);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA,GL11.GL_ONE_MINUS_SRC_ALPHA);
// Opengl init
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glLoadIdentity();
GL11.glOrtho(0, 500, 0, 500, 1, -1);
GL11.glMatrixMode(GL11.GL_MODELVIEW);
// Enable textures
GL11.glEnable(GL11.GL_TEXTURE_2D);
}
public void clearGL() {
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
GL11.glLoadIdentity();
}
}
TextureLoader.getTexture() binds the texture. If you look at the Slick source code, you will find the following line in the getTexture() method of the InternalTextureLoader class:
GL.glBindTexture(target, textureID);
It pretty much has to bind the texture, since it needs to call functions like glTexImage2D() to load the texture.
So you will actually use texturing after calling this function, since you now have a texture bound, and you enabled texturing in your own code:
GL11.glEnable(GL11.GL_TEXTURE_2D);
To see the texture image, you'll need to perform additional steps, like specify texture coordinates.
I have problem with openGL debugging. I find that a lot of the time, OpenGL will show you it failed by not drawing anything. Every time code looks fine but it is not drawing anything on GL window.
For e.g consider the below code.I write it to draw the cube but it is not drawing anything and i am unable to find the cause.
========================================================
// cube_vertex_array.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include <glut.h>
static GLfloat vertex[]=
{
100.0,100.0,0.0,
400.0,100.0,0.0,
400.0,400.0,0.0,
100.0,400.0,0.0,
100.0,100.0,-300.0,
400.0,100.0,-300.0,
400.0,400.0,-300.0,
100.0,400.0,-300.0
};
static GLfloat color[]=
{
1.0,0.0,0.0,
0.0,1.0,0.0,
0.0,0.0,1.0,
1.0,1.0,0.0,
1.0,0.0,1.0,
0.0,1.0,1.0
};
static GLubyte frontIndices[] = {0,1,2,3};
static GLubyte leftIndices[] = {1,5,6,2};
static GLubyte backIndices[] = {4,7,6,5};
static GLubyte rightIndices[] = {0,3,7,4};
static GLubyte topIndices[] = {3,2,6,7};
static GLubyte bottomIndices[] = {0,4,5,1};
void init(void)
{
glClearColor(0.0,0.0,0.0,0.0); //Set default background color to black.
glClearDepth(2.0); //Set the depth level for clearing depth buffer.
glShadeModel(GL_FLAT); //Set the shading model to FLAT
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //Clear the color and depth buffer.
}
void Display(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //Clear the color and depth buffer.
glColor3f(1.0,0.0,0.0);
//glBegin(GL_LINE_STRIP);
// glVertex3f(0.0,0.0,0.0);
// glVertex3f(200.0,100.0,0.0);
//glEnd();
glEnableClientState(GL_VERTEX_ARRAY); //Enable vertex array.
glEnableClientState(GL_COLOR_ARRAY); //Enable vertex array color.
glColorPointer(3,GL_FLOAT,0,color); //Specify the array for colors.
glVertexPointer(3,GL_FLOAT,0,vertex); //Specify the array for vertex.
glDrawElements(GL_QUADS,4,GL_UNSIGNED_BYTE,frontIndices); //Draw front face.
glDrawElements(GL_QUADS,4,GL_UNSIGNED_BYTE,leftIndices); //Draw left face.
glDrawElements(GL_QUADS,4,GL_UNSIGNED_BYTE,backIndices); //Draw back face.
glDrawElements(GL_QUADS,4,GL_UNSIGNED_BYTE,rightIndices); //Draw right face.
glDrawElements(GL_QUADS,4,GL_UNSIGNED_BYTE,topIndices); //Draw top face.
glDrawElements(GL_QUADS,4,GL_UNSIGNED_BYTE,bottomIndices); //Draw bottom face.
glutSwapBuffers(); //Swap the buffers.
}
void Reshape(int w,int h)
{
glViewport(0.0,(GLsizei)w,0.0,(GLsizei)h); //Set the viewport according to new window size.
glMatrixMode(GL_PROJECTION); //Set matrix mode to projection.
glLoadIdentity(); //Replace the top matrix in the stack to the identity matrix.
gluOrtho2D(0.0,(GLdouble)w,0.0,(GLdouble)h); //Set the orthographic projection.
glMatrixMode(GL_MODELVIEW); //Set matrix mode to modelview.
}
int main(int argc, char **argv)
{
glutInit(&argc,argv); //Initialize the glut.
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB); //Set display mode and also enable double buffering.
glutInitWindowSize(500,500); //Set the initial window size.
glutCreateWindow("Cube"); //Create the window and also assign name to it.
init(); //Initialize the app.
glutDisplayFunc(Display); //Register the Display function.
glutReshapeFunc(Reshape); //Register the Reshape function.
glutMainLoop(); //Start the main loop.
return 0;
}
You have put GL_UNSIGNED_BYTE as the type parameter in glDrawElements(). This will cause openGL to interpret the array of indices you throw in as one byte per index. You should use GL_UNSIGNED_INT here instead.
Here's the working code based on the code your provided (I did port it to java though):
import java.nio.ByteBuffer;
import org.lwjgl.BufferUtils;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import static org.lwjgl.opengl.GL11.*;
public class GLTest {
public static void main(String[] args) {
try {
Display.create();
Display.setDisplayMode(new DisplayMode(500, 500));
Display.setResizable(true);
//the same arrays as the ones you specified.
float[] vertices = new float[]{100.0f,100.0f,0.0f,
400.0f,100.0f,0.0f,
400.0f,400.0f,0.0f,
100.0f,400.0f,0.0f,
100.0f,100.0f,-300.0f,
400.0f,100.0f,-300.0f,
400.0f,400.0f,-300.0f,
100.0f,400.0f,-300.0f};
float[] color = new float[]{1,0,0,
0,1,0,
0,0,1,
1,1,0,
1,0,1,
0,1,1};
int[] frontIndices = new int[]{0, 1, 2, 3};
//JWJGL bookkeeping..
ByteBuffer vertexBuffer = BufferUtils.createByteBuffer(vertices.length * 4);
ByteBuffer colourBuffer = BufferUtils.createByteBuffer(color.length * 4);
for(int i = 0; i < vertices.length; i++) {
vertexBuffer.putFloat(vertices[i]);
}
vertexBuffer.rewind();
for(int i = 0; i < color.length; i++) {
colourBuffer.putFloat(color[i]);
}
colourBuffer.rewind();
ByteBuffer indexBuffer = BufferUtils.createByteBuffer(4 * frontIndices.length);
for(int i = 0; i < frontIndices.length; i++) {
indexBuffer.putInt(frontIndices[i]);
}
indexBuffer.rewind();
//back you your code
glClearColor(1,1,1,1);
glShadeModel(GL_SMOOTH);
while(!Display.isCloseRequested()) {
glViewport(0, 0, Display.getWidth(), Display.getHeight());
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0,Display.getWidth(), 0, Display.getHeight(), -1, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glColorPointer(3, GL_FLOAT, 0, colourBuffer);
glVertexPointer(3, GL_FLOAT, 0, vertexBuffer);
glDrawElements(GL_QUADS, 4, GL_UNSIGNED_INT, indexBuffer);
Display.update();
Display.sync(60);
}
} catch (LWJGLException e) {
e.printStackTrace();
}
}
}
Which results in:
use tools like glTrace / glIntercept (to look at OpenGL call trace), gDebugger (to visualize textures, shaders, OGL state etc.)
There is a list of OpenGL debugging tools here : https://www.opengl.org/wiki/Debugging_Tools
Also your code is using the old fixed pipeline which is considered deprecated since OpenGL 3.3, so i would recommend either not putting the tag "opengl-3" on your questions, or using opengl 3.3 core context and learning the "modern" OpenGL (which is more powerful and more difficult to learn but makes you understand how the GPU works).
how do i add textures for a height map? I followed TheCodingUniverse's tutorial #36 (i think thats the number) for creating height maps, he covers using shaders to set X colour for X height, However i would like to set XX texture for X height instead of a colour for each height. How do i do this?
I have tried binding the texture as i am drawing the triangle strip (like i would do on a 3D square of GL_QUADS), but then the heightmap fails to render. All other things in the world continue to render, however.
StackOverflow is complaining about how i have given too little info, but i am unsure what more i have got to tell you, apart from code samples.
Current Heightmap Code:
package Terrain;
import org.lwjgl.BufferUtils;
import org.lwjgl.LWJGLException;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL20;
import org.lwjgl.util.glu.GLU;
import org.lwjgl.util.glu.Sphere;
import com.bulletphysics.collision.broadphase.BroadphaseInterface;
import com.bulletphysics.collision.broadphase.DbvtBroadphase;
import com.bulletphysics.collision.dispatch.CollisionConfiguration;
import com.bulletphysics.collision.dispatch.CollisionDispatcher;
import com.bulletphysics.collision.dispatch.CollisionObject;
import com.bulletphysics.collision.dispatch.DefaultCollisionConfiguration;
import com.bulletphysics.collision.shapes.CollisionShape;
import com.bulletphysics.collision.shapes.SphereShape;
import com.bulletphysics.collision.shapes.StaticPlaneShape;
import com.bulletphysics.dynamics.DiscreteDynamicsWorld;
import com.bulletphysics.dynamics.RigidBody;
import com.bulletphysics.dynamics.RigidBodyConstructionInfo;
import com.bulletphysics.dynamics.constraintsolver.ConstraintSolver;
import com.bulletphysics.dynamics.constraintsolver.SequentialImpulseConstraintSolver;
import com.bulletphysics.linearmath.DefaultMotionState;
import com.bulletphysics.linearmath.MotionState;
import com.bulletphysics.linearmath.Transform;
import utility.EulerCamera;
import utility.ShaderLoader;
import javax.imageio.ImageIO;
import javax.vecmath.Matrix4f;
import javax.vecmath.Quat4f;
import javax.vecmath.Vector3f;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL13.*;
import static org.lwjgl.opengl.GL20.*;
/**
* A 3D terrain loaded from a height-map and a lookup texture. Press 'L' to reload the shader and texture files. Press
* 'P' to switch between normal, point, and wire-frame mode. Press 'F' to flatten the terrain. Click here for an image:
* https://twitter.com/i/#!/CodingUniverse/media/slideshow?url=pic.twitter.com%2FDgMdZ5jm.
*
* #author Oskar Veerhoek
*/
public class TexturedTerrain {
private static final String WINDOW_TITLE = "Terrain!";
private static final int[] WINDOW_DIMENSIONS = {1200, 650};
// private static final EulerCamera camera = new EulerCamera.Builder().setPosition(-5.4f, 19.2f,
// 33.2f).setRotation(30, 61, 0).setAspectRatio(ASPECT_RATIO).setFieldOfView(60).build();
private static EulerCamera camera = new EulerCamera.Builder()
.setFieldOfView(60)
.setNearClippingPane(0.3f)
.setFarClippingPane(500)
.setPosition(0, 25, 15)
.build();
/**
* The shader program that will use the lookup texture and the height-map's vertex data to draw the terrain.
*/
public static int shaderProgram;
/**
* The texture that will be used to find out which colours correspond to which heights.
*/
public static int lookupTexture;
public static int textureTerrain;
/**
* The display list that will contain the height-map's vertex data.
*/
public static int heightmapDisplayList;
/**
* The points of the height. The first dimension represents the z-coordinate. The second dimension represents the
* x-coordinate. The float value represents the height.
*/
public static float[][] data;
/**
* Whether the terrain should vary in height or be displayed on a grid.
*/
private static boolean flatten = false;
public static void render() {
// Clear the pixels on the screen and clear the contents of the depth buffer (3D contents of the scene)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Reset any translations the camera made last frame update
glLoadIdentity();
// Apply the camera position and orientation to the scene
camera.applyTranslations();
if (flatten) {
glScalef(1, 0, 1);
}
// Render the heightmap using the shaders that are being used
glCallList(heightmapDisplayList);
}
private static void input() {
while (Keyboard.next()) {
if (Keyboard.getEventKeyState()) {
if (Keyboard.isKeyDown(Keyboard.KEY_F)) {
flatten = !flatten;
}
if (Keyboard.getEventKey() == Keyboard.KEY_ESCAPE){
Display.destroy();
System.exit(0);
}
if (Keyboard.getEventKey() == Keyboard.KEY_L) {
// Reload the shaders and the heightmap data.
glUseProgram(0);
glDeleteProgram(shaderProgram);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, 0);
glDeleteTextures(lookupTexture);
glDeleteTextures(textureTerrain);
setUpShaders();
setUpHeightmap();
}
if (Keyboard.getEventKey() == Keyboard.KEY_P) {
// Switch between normal mode, point mode, and wire-frame mode.
int polygonMode = glGetInteger(GL_POLYGON_MODE);
if (polygonMode == GL_LINE) {
glPolygonMode(GL_FRONT, GL_FILL);
} else if (polygonMode == GL_FILL) {
glPolygonMode(GL_FRONT, GL_POINT);
} else if (polygonMode == GL_POINT) {
glPolygonMode(GL_FRONT, GL_LINE);
}
}
}
}
if (Mouse.isButtonDown(0)) {
Mouse.setGrabbed(true);
} else if (Mouse.isButtonDown(1)) {
Mouse.setGrabbed(false);
}
if (Mouse.isGrabbed()) {
camera.processMouse(1, 80, -80);
}
camera.processKeyboard(16, 1);
}
private static int loadTexture(String path) throws IOException {
// Create an input stream for the 'lookup texture', a texture that will used by the fragment shader to
// determine which colour matches which height on the heightmap
FileInputStream inputStream = new FileInputStream(path);
// Create a class that will give us information about the image file (width and height) and give us the
// texture data in an OpenGL-friendly manner
PNGDecoder decoder = new PNGDecoder(inputStream);
// Create a ByteBuffer in which to store the contents of the texture. Its size is the width multiplied by
// the height and 4, which stands for the amount of bytes a float is in Java.
ByteBuffer buffer = BufferUtils.createByteBuffer(4 * decoder.getWidth() * decoder.getHeight());
// 'Decode' the texture and store its data in the buffer we just created
decoder.decode(buffer, decoder.getWidth() * 4, PNGDecoder.Format.RGBA);
// Make the contents of the ByteBuffer readable to OpenGL (and unreadable to us)
buffer.flip();
// Close the input stream for the heightmap 'lookup texture'
inputStream.close();
// Generate a texture handle for the 'lookup texture'
int texture = glGenTextures();
glBindTexture(GL_TEXTURE_2D, texture);
// Hand the texture data to OpenGL
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, decoder.getWidth(), decoder.getHeight(), 0, GL_RGBA,
GL_UNSIGNED_BYTE, buffer);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glBindTexture(GL_TEXTURE_2D, 0);
return texture;
}
public static BufferedImage image(){
BufferedImage heightmapImage = null;
try{
heightmapImage = ImageIO.read(new File("res/heightmap.bmp"));
}catch (IOException e){
e.printStackTrace();
}
if (heightmapImage == null){
return null;
}
return heightmapImage;
}
public static void setUpHeightmap() {
try {
// Load the heightmap-image from its resource file
BufferedImage heightmapImage = ImageIO.read(new File("res/heightmap.bmp"));
// Initialise the data array, which holds the heights of the heightmap-vertices, with the correct dimensions
data = new float[heightmapImage.getWidth()][heightmapImage.getHeight()];
// Lazily initialise the convenience class for extracting the separate red, green, blue, or alpha channels
// an int in the default RGB color model and default sRGB colourspace.
Color colour;
// Iterate over the pixels in the image on the x-axis
for (int z = 0; z < data.length; z++) {
// Iterate over the pixels in the image on the y-axis
for (int x = 0; x < data[z].length; x++) {
// Retrieve the colour at the current x-location and y-location in the image
colour = new Color(heightmapImage.getRGB(z, x));
// Store the value of the red channel as the height of a heightmap-vertex in 'data'. The choice for
// the red channel is arbitrary, since the heightmap-image itself only has white, gray, and black.
int y = colour.getRed();;
data[z][x] = y;
//xheight.put(x, y);
//zheight.put(z, y);
}
}
lookupTexture = loadTexture("res/heightmap_lookup.png");
//textureTerrain = loadTexture("res/heightmap.png");
} catch (IOException e) {
e.printStackTrace();
}
glBindTexture(GL_TEXTURE_2D, lookupTexture);
// Generate a display list handle for the display list that will store the heightmap vertex data
heightmapDisplayList = glGenLists(1);
// TODO: Add alternative VBO rendering for pseudo-compatibility with version 3 and higher.
glNewList(heightmapDisplayList, GL_COMPILE);
// Scale back the display list so that its proportions are acceptable.
//glScalef(0.4f, 0.4f, 0.4f);
// Iterate over the 'strips' of heightmap data.
for (int z = 0; z < data.length - 1; z++) {
// Render a triangle strip for each 'strip'.
glBegin(GL_TRIANGLE_STRIP);
for (int x = 0; x < data[z].length; x++) {
// Take a vertex from the current strip
//glTexCoord2f(0, 0);
//GL20.glUseProgram(0);
glVertex3f(x, data[z][x], z);
// Take a vertex from the next strip
//glTexCoord2f(1, 1);
glVertex3f(x, data[z + 1][x], z + 1);
/*
glColor3f(0.9f, 0.9f, 0.9f);
glBegin(GL_QUADS);
glTexCoord2f(0, 1); // put before every vertex
glVertex3f(-50,0 ,-50);
glTexCoord2f(1, 1);
glVertex3f(50, 0 ,-50);
glTexCoord2f(1, 0);
glVertex3f(50, 0 ,50);
glTexCoord2f(0, 0);
glVertex3f(-50, 0 ,50);
glEnd();
*/
}
glEnd();
}
glEndList();
}
public static void setUpShaders() {
shaderProgram = ShaderLoader.loadShaderPair("res/landscape.vs", "res/landscape.fs");
glUseProgram(shaderProgram);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, lookupTexture);
glUniform1i(glGetUniformLocation(shaderProgram, "lookup"), 0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, textureTerrain);
glUniform1i(glGetUniformLocation(shaderProgram, "terrain"), 1);
}
private static void cleanUp(boolean asCrash) {
glUseProgram(0);
glDeleteProgram(shaderProgram);
glDeleteLists(heightmapDisplayList, 1);
glBindTexture(GL_TEXTURE_2D, 0);
glDeleteTextures(lookupTexture);
System.err.println(GLU.gluErrorString(glGetError()));
Display.destroy();
System.exit(asCrash ? 1 : 0);
}
private static void setUpMatrices() {
camera.applyPerspectiveMatrix();
}
private static void setUpStates() {
camera.applyOptimalStates();
glPointSize(1);
// Enable the sorting of shapes from far to near
glEnable(GL_DEPTH_TEST);
// Set the background to a blue sky colour
glClearColor(0, 0.75f, 1, 1);
// Remove the back (bottom) faces of shapes for performance
glEnable(GL_CULL_FACE);
}
private static void update() {
Display.update();
Display.sync(60);
}
private static void enterGameLoop() {
while (!Display.isCloseRequested()) {
render();
input();
update();
//TODO
//TODO
}
}
private static void setUpDisplay() {
try {
Display.setDisplayMode(new DisplayMode(WINDOW_DIMENSIONS[0], WINDOW_DIMENSIONS[1]));
Display.setVSyncEnabled(true);
Display.setTitle(WINDOW_TITLE);
Display.create();
} catch (LWJGLException e) {
e.printStackTrace();
cleanUp(true);
}
}
public static void main(String[] args) {
setUpDisplay();
setUpStates();
setUpHeightmap();
setUpShaders();
//setUpPhysics();
setUpMatrices();
enterGameLoop();
cleanUp(false);
}
}
Just trying to get a triangle to draw to the screen, following a c++ tutorial. Tried to run the program and I get a NullPointerException on all Opengl calls. Also, I'm following a tutorial for opengl 3 although most of my calls are for earlier versions, is this just how lwjgl is set up, with the functions residing in the version where they originated from?
package examples;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.*;
import java.nio.*;
public class Triangle
{
// An array of 3 vectors which represents 3 vertices
static final float vertexData[] = {
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
};
// This will identify our vertex buffer
int vertexBufferID;
public static void main(String[] args)
{
new Triangle();
}
public Triangle()
{
// Allocate floatBuffer to hold vertex data
FloatBuffer vertexBuffer = FloatBuffer.allocate(9);
// Put float data into buffer and position ready to read
vertexBuffer.put(vertexData).position(0);
// Generate 1 buffer, put the resulting identifier in vertexbuffer
IntBuffer buffers = IntBuffer.allocate(1); // allocate
GL15.glGenBuffers(buffers);
vertexBufferID = buffers.get(0);
// Binds a buffer to the ARRAY_BUFFER(target) (1 at a time) (breaks other bonds)
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vertexBufferID);
// Give our vertices to OpenGL. (creates store for data bound to target(above))
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, vertexBuffer,GL15.GL_STATIC_DRAW);
try {
Display.setDisplayMode(new DisplayMode(800,600));
Display.create();
} catch (LWJGLException e) {
e.printStackTrace();
System.exit(0);
}
while(!Display.isCloseRequested())
{
// Render
// 1st attribute buffer : vertices
GL20.glEnableVertexAttribArray(0); // enable vertex attribute index: 0
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vertexBufferID);
// Specify location of vertex data for index 0
GL33.glVertexAttribP1ui(0, GL11.GL_FLOAT, false, 0);
// Draw the triangle!
GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, 3); // Starting from vertex 0; 3 vertices total -> 1 triangle
GL20.glDisableVertexAttribArray(0);
}
}
}
I think that the problem is you have to create the Display before any openGl call.
Try to move
try {
Display.setDisplayMode(new DisplayMode(800,600));
Display.create();
} catch (LWJGLException e) {
e.printStackTrace();
System.exit(0);
}
at the top of the Triangle constructor.