Hello Guest

[Newb Question] Rendering textured quads just renders them white?

  • 4 Replies
  • 3757 Views
I'm extremely new to LWJGL, and more importantly, to graphics rendering in general (though I am by no means new to programming). As such, I've built a lot of my project's data backbone and avoided rendering, but now I need to start learning how to render graphics.

I followed the tutorial "The Quad Textured" on the LWJGL wiki, modified to fit my project, and I've run into a persistent problem: although I'm successfully loading a texture in from a PNG and binding it, etc. (i.e. I'm getting a positive texture ID and no errors), the quads are being drawn completely white rather than taking on the texture of the image.

Rather than copy/paste my entire project here, I'll post the code I believe is relevant; feel free to ask if there's something more you need.

QuadRenderer.java (the quad rendering class):
Code: [Select]
package com.IceMetalPunk.learnLWJGL.render;

import java.nio.ByteBuffer;
import java.nio.FloatBuffer;

import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;

public class QuadRenderer {
public Vertex[] vertices;
public FloatBuffer vertexBuffer;
int vaoID, vboID, indexVBOID, indexCount;

/*
* Constructors
*
* Coordinates are for vertices clockwise from bottom-left.
*/
public QuadRenderer(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3,
float x4, float y4, float z4) {
this.vertices = new Vertex[] { new Vertex(), new Vertex(), new Vertex(), new Vertex() };
this.vertices[0].setXYZ(x1, y1, z1);
this.vertices[0].setUV(0f, 0f);
this.vertices[1].setXYZ(x2, y2, z2);
this.vertices[1].setUV(0f, 1f);
this.vertices[2].setXYZ(x3, y3, z3);
this.vertices[2].setUV(1f, 1f);
this.vertices[3].setXYZ(x4, y4, z4);
this.vertices[3].setUV(1f, 0f);

// Get vertex data into a single buffer of floats in the proper order
this.vertexBuffer = BufferUtils.createFloatBuffer(this.vertices.length * Vertex.numElements);
for (int i = 0; i < this.vertices.length; ++i) {
this.vertexBuffer.put(this.vertices[i].get());
}

// OpenGL uses CC order; vertexes above specified in clockwise order; so
// flip
this.vertexBuffer.flip();

// Specify OpenGL triangle-list drawing order
byte[] drawOrder = new byte[] { 0, 1, 2, 2, 3, 0 };
this.indexCount = drawOrder.length;
ByteBuffer drawOrderBuffer = BufferUtils.createByteBuffer(this.indexCount);
drawOrderBuffer.put(drawOrder);
drawOrderBuffer.flip();

// Create a Vertex Array Object (VAO) and bind/select it
this.vaoID = GL30.glGenVertexArrays();
GL30.glBindVertexArray(this.vaoID);

// Create a Vertex Buffer Array (VBO) and bind/select it
this.vboID = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboID);

// Put the vertex data into the drawing buffer for the selected VBO
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, this.vertexBuffer, GL15.GL_STATIC_DRAW);

// Put the vertex data into the buffers for the selected VAO.
// This specifies which byte offsets in the VBO correspond to position,
// color, and UV of the vertex.
// The "false" parameter here specifies whether the values should be
// normalized; no.
GL20.glVertexAttribPointer(0, Vertex.positionElements, GL11.GL_FLOAT, false, Vertex.size, Vertex.positionOffset);
GL20.glVertexAttribPointer(1, Vertex.colorElements, GL11.GL_FLOAT, false, Vertex.size, Vertex.colorOffset);
GL20.glVertexAttribPointer(2, Vertex.uvElements, GL11.GL_FLOAT, false, Vertex.size, Vertex.uvOffset);

// Deselect (unbind) the VBO and VAO
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
GL30.glBindVertexArray(0);

// Create a new VBO for the draw order array, bind it, put the index
// data in, then unbind it
this.indexVBOID = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, this.indexVBOID);
GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, drawOrderBuffer, GL15.GL_STATIC_DRAW);
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
}

/* "Destructors" */
public void destroy() {
// Disable the VAO attribute lists, just in case they're enabled
GL30.glBindVertexArray(this.vaoID);
GL20.glDisableVertexAttribArray(0);
GL20.glDisableVertexAttribArray(1);
GL20.glDisableVertexAttribArray(2);

// Unbind (deselect) VBO and delete it
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
GL15.glDeleteBuffers(this.vboID);

// Unbind (deselect) the draw order VBO and delete it
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
GL15.glDeleteBuffers(this.indexVBOID);

// Unbind (deselect) and delete the VAO
GL30.glBindVertexArray(0);
GL30.glDeleteVertexArrays(this.vaoID);
}

// Just in case!
public void finalize() {
this.destroy();
}

/* Rendering! */
public void render(int textureID) {

// Use texture unit 0 and bind/select the given texture
GL13.glActiveTexture(GL13.GL_TEXTURE0);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureID);

// Bind the vertex attribute array and enable the position, color, and
// UV attributes
GL30.glBindVertexArray(this.vaoID);
GL20.glEnableVertexAttribArray(0); // Position
GL20.glEnableVertexAttribArray(1); // Color
GL20.glEnableVertexAttribArray(2); // UV

// Bind our index VBO to the element array buffer
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, this.indexVBOID);

// Draw the vertices! Last parameter is the offset in the buffer to
// start at.
GL11.glDrawElements(GL11.GL_TRIANGLES, this.indexCount, GL11.GL_UNSIGNED_BYTE, 0);

// Cleanup: Deselect (unbind) everything and disable attribute lists
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
GL20.glDisableVertexAttribArray(0);
GL20.glDisableVertexAttribArray(1);
GL20.glDisableVertexAttribArray(2);
GL30.glBindVertexArray(0);
}
}

Vertex.java:
Code: [Select]
package com.IceMetalPunk.learnLWJGL.render;

public class Vertex {
private float[] xyzw = new float[] { 0f, 0f, 0f, 1f };
private float[] rgba = new float[] { 1f, 1f, 1f, 1f };
private float[] uv = new float[] { 0f, 0f };

/* Some memory-management constants used when rendering from this data */
public static final int positionElements = 4;
public static final int colorElements = 4;
public static final int uvElements = 2;
public static final int numElements = positionElements + colorElements + uvElements;

// There are 4 bytes per float in Java, on any platform
public static final int bytesPerElement = 4;

public static final int positionOffset = 0;
public static final int colorOffset = positionElements * bytesPerElement;
public static final int uvOffset = colorOffset + colorElements * bytesPerElement;

public static final int size = bytesPerElement * (positionElements + colorElements + uvElements);

/* Setters */
public void setXYZ(float x, float y, float z) {
this.setXYZW(x, y, z, 1f);
}

public void setXYZW(float x, float y, float z, float w) {
this.xyzw = new float[] { x, y, z, w };
}

public void setUV(float u, float v) {
this.uv = new float[] { u, v };
}

public void setRGB(float r, float g, float b) {
this.setRGBA(r, g, b, this.rgba[3]);
}

public void setRGBA(float r, float g, float b, float a) {
this.rgba = new float[] { r, g, b, a };
}

/* Getters */
public float[] getRGBA() {
return new float[] { this.rgba[0], this.rgba[1], this.rgba[2], this.rgba[3] };
}

public float[] getXYZW() {
return new float[] { this.xyzw[0], this.xyzw[1], this.xyzw[2], this.xyzw[3] };
}

public float[] getUV() {
return new float[] { this.uv[0], this.uv[1] };
}

// Gets *everything* in one array
public float[] get() {
float[] ret = new float[numElements];
int pos, col, tex;

for (pos = 0; pos < this.xyzw.length; ++pos) {
ret[pos] = this.xyzw[pos];
}
for (col = 0; col < this.rgba.length; ++col) {
ret[pos + col] = this.rgba[col];
}
for (tex = 0; tex < this.uv.length; ++tex) {
ret[pos + col + tex] = this.uv[tex];
}
return ret;
}
}

Renderer#textureFromPNG() method (creates a texture given a PNG image's filename):
Code: [Select]
// Load textures from PNG files and return their handle IDs
public int textureFromPNG(String filename) {
GL.createCapabilities();
FileInputStream input = null;
PNGDecoder png = null;
ByteBuffer output = null;
try {
// Load PNG file into buffer
input = new FileInputStream(filename);
}
catch (FileNotFoundException e1) {
System.err.println("Texture not found: " + filename);
return -1;
}
try {
png = new PNGDecoder(input);
// Setup output buffer of RGBA data
output = ByteBuffer.allocateDirect(4 * png.getHeight() * png.getWidth());
// Decode PNG to raw RGBA data
png.decode(output, 4 * png.getWidth(), PNGDecoder.Format.RGBA);
// Flip because PNGs are stored flipped
output.flip();
// Cleanup
input.close();
}
catch (IOException e) {
System.err.println("Texture could not be decoded as a PNG: " + filename);
return -2;
}

// Allocate the next available texture handle ID
int texID = GL11.glGenTextures();
// Set texture unit 0 for our work?
GL13.glActiveTexture(GL13.GL_TEXTURE0);
// Bind the soon-to-be texture to our ID
GL11.glBindTexture(GL11.GL_TEXTURE_2D, texID);
// Indicate 1 byte per color component
GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);
// Create a texture from the RGBA data
GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, png.getWidth(), png.getHeight(), 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, output);
// Generate mipmap for scaling the texture
GL30.glGenerateMipmap(GL11.GL_TEXTURE_2D);
// (S,T) is like (U,V) coords, and this sets up repeat wrapping on edges
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);
// Setup algorithms for magnifying and minimizing images
// Nearest-neighbor sampling for +, mipmap for -
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR_MIPMAP_LINEAR);
this.textureList.add(texID); // Add to a list for tracking and cleanup later; irrelevant, I think
return texID;
}

As I said, I mostly just followed a couple of tutorials and modified it for my project. So that's the relevant code; when I call QuadRenderer#render(), passing in a textureID generated from Renderer#textureFromPNG(), it renders a solid white square instead of a textured one. I tried playing around with the vertex RGBA and found it makes no difference; it's ignored and a solid white square is all I ever get rendered. (If it matters, the texture I'm using is a 32x32 brown dirt PNG.)

Where have I gone wrong?
« Last Edit: January 10, 2016, 03:14:58 by IceMetalPunk »

*

Kai

Re: [Newb Question] Rendering textured quads just renders them white?
« Reply #1 on: January 10, 2016, 11:37:17 »
That tutorial you mentioned is using a shader, so please show that "setupShaders()" method of yours and your vertex and fragment shader.
If you are not using a shader but the fixed-function pipeline, then you may not simply use glVertexAttribPointer, since OpenGL has no idea that index 2 means UV-coordinates. You would then use GL11.glTexCoordPointer for that. It is just a concidence that you actually even see a quad, because attribute index 0 is very frequently implicitly used for the vertex position attribute. Actually, you should have used GL11.glVertexPointer for that.
Then, when using a texture with the fixed-function pipeline, you must also enable texturing via: GL11.glEnable(GL_TEXTURE_2D)

Re: [Newb Question] Rendering textured quads just renders them white?
« Reply #2 on: January 11, 2016, 00:27:57 »
Ah, okay. I was under the (clearly wrong) assumption that the shaders and the textured quad rendering were independent pieces of that tutorial; I didn't realize they were so dependently linked. I'll look into finding tutorials on how to work with the fixed-function pipeline instead. Thank you!

*

Offline abcdef

  • ****
  • 335
Re: [Newb Question] Rendering textured quads just renders them white?
« Reply #3 on: January 11, 2016, 08:58:35 »
if you are starting out you should find tutorials on modern opengl as the fixed function pipe line is now deprecated.

This guy has a good set of tutorials but there are lots out there

http://antongerdelan.net/opengl/


Re: [Newb Question] Rendering textured quads just renders them white?
« Reply #4 on: February 16, 2016, 00:47:38 »
https://www.youtube.com/user/ThinMatrix

ThinMatrix has a few dozen modern opengl game programming stuff in 3d that you can adapt to 2d.