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.