OpenGL 只在四边形上渲染纹理的一个像素?
OpenGL is only rendering one pixel of a texture on a quad?
我想要一个带有纹理的四边形,从头开始使用 LWJGL,但由于某种原因,四边形上只应用了右上角的第一个 texel/pixel。
我也尝试过使用多个纹理,但仍然无法解决问题。
这是纹理:
这是我得到的:
package com.codebitcookie.engine;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL11.*;
import com.codebitcookie.graphics.Color;
import com.codebitcookie.graphics.Mesh;
import com.codebitcookie.graphics.Texture;
import com.codebitcookie.maths.Matrix4f;
import com.codebitcookie.shaders.Shader;
import static org.lwjgl.glfw.GLFW.*;
public class Core {
private static float[] verts = {
// Quad
// -0.5f, 0.5f, 0.5f,
// 0.5f, 0.5f, 0.5f,
// 0.5f, -0.5f, 0.5f,
//
// -0.5f, 0.5f, 0.5f,
// 0.5f, -0.5f, 0.5f,
// -0.5f, -0.5f, 0.5f
// Triangle
// 0.0f, 0.5f, 0.5f,
// 0.5f, -0.5f, 0.5f,
// -0.5f, -0.5f, 0.5f
// You might have to flip the Y-values because of the way OpenGL's coordinates work
// (i.e. (0,0) is normally bottom-left instead of top-left)
// Quad
0, 1,
1, 1,
1, 0,
1, 0,
0, 0,
0, 1
// Triangle
// 0, 1,
// 0.5f, 0,
// 1, 1
};
// int[] indices = {
// 0, 1, 3,
// 3, 1, 2
// };
private static float[] uvs = {
// 0, 0,
// 0, 1,
// 1, 1,
// 1, 0
0, 1,
1, 1,
1, 0,
1, 0,
0, 0,
0, 1
};
public static void main(String[] args) {
long window = Application.init();
Color ubuntu = Color.ubuntu();
//1920 x 1080 is a 16:9 aspect ratio thats why we take the aspect ratio of 1080p and apply it on a 10x10x10 projection
// Matrix4f ortho = Matrix4f.orthographic(-10.0f, 10.0f, -10.0f * 9.0f / 16.0f, 10.0f * 9.0f / 16.0f, 1.0f, -1.0f);
Matrix4f ortho = Matrix4f.orthographic(0.0f, Application.getWidth(), Application.getHeight(), 0.0f, 1.0f, -1.0f); //TODO: CHECK IF Z WORKS
Mesh mesh = new Mesh(verts, uvs);
Shader shader = new Shader("res/shaders/VertexShader.vs", "res/shaders/FragmentShader.fs");
Texture texture = new Texture("minecraftTextureAtlas", "res/textures/minecraftTextureAtlas.png");
// Texture texture = new Texture("minecraftPlanks", "res/textures/minecraftPlanks.jpeg");
GL11.glClearColor(ubuntu.getR(), ubuntu.getG(), ubuntu.getB(), ubuntu.getA()); //set a color for when you call glClear to clear the screen
shader.bind();
shader.setUniformColor("matColor", Color.white());
shader.setUniformMat4f("projection", ortho);
shader.unbind();
while(!glfwWindowShouldClose(window)) {
glfwPollEvents(); //processes events that are in the event queue and then returns immediately.
//this clears the color buffer and sets it to what we set it to when we call glClearColor()
//GL_COLOR_BUFFER_BIT: contains the RGB info for every pixel.
//GL_DEPTH_BUFFER_BIT: contains the distance between pixels from screen or for making layers in our case for e.g some pixel is in front of others
//GL_STENCIL_BUFFER_BIT: A custom buffer for your use for per pixel info
//And many others
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
shader.bind();
texture.bind();
mesh.render();
texture.unbind();
shader.unbind();
glfwSwapBuffers(window); //Swap Buffers
}
Texture.cleanUp();
shader.cleanUp();
mesh.cleanUp();
glfwTerminate(); // Destroys all windows and cursors, frees and restores resources. you must call glfwInit after.
}
}
******************** Application.java ********************
package com.codebitcookie.engine;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.system.MemoryUtil.NULL;
import org.lwjgl.glfw.GLFWVidMode;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GL11;
import com.codebitcookie.graphics.Color;
public class Application {
private static final String TITLE = "2D Game Engine /w GUI";
private static int width = 1280;
private static int height = 720;
private static long window;
public static long init() {
//check if GLFW is initialized if not we throw an exception
if(!glfwInit()) {
throw new IllegalStateException("GLFW failed to be initialized");
}
// GLFW.glfwDefaultWindowHints(); //equals to the two lines below
glfwWindowHint(GLFW_VISIBLE, GL11.GL_FALSE); //makes the window visible
glfwWindowHint(GLFW_RESIZABLE, GL11.GL_TRUE); //makes the window resizable
//sets OpenGL to 3.3; major is 3.*** and minor is ***.3 = 3.3
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
//for more info on these, check out:
//https://community.khronos.org/t/forward-compatible-vs-core-profile/65039
//https://www.khronos.org/opengl/wiki/OpenGL_Context
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL11.GL_TRUE); //makes mac run nicely but restores all deprecated functionality
window = glfwCreateWindow(width, height, TITLE, NULL, NULL);
//if window could not be created
if(window == NULL) {
glfwTerminate(); // Destroys all windows and cursors, frees and restores resources. you must call glfwInit after.
throw new RuntimeException("Unable to create window " + window);
}
GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
glfwSetWindowPos(window, (vidmode.width() - width) / 2, (vidmode.height() - height) / 2);
glfwMakeContextCurrent(window);
glfwShowWindow(window); //show window which we hid in the window hints
GL.createCapabilities();
//I don't have screen tearing issues so I am commenting this out
// glfwSwapInterval(1);
GL11.glEnable(GL11.GL_DEPTH_TEST);
return window;
}
public static String getTitle() {
return TITLE;
}
public static int getWidth() {
return width;
}
******************** Mesh.java ********************
package com.codebitcookie.graphics;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import com.codebitcookie.utils.BufferUtils;
public class Mesh {
public final static int VERTEX_ATTRIB = 0;
public final static int TEXCOORD_ATTRIB = 1;
private int vao, vbo, tcbo; //Vertex Array Obj, Vertex Buffer Obj, Texture Coords Buffer Obj
private float[] vertices, textureCoordinates;
public Mesh(float[] vertices, float[] textureCoordinates) {
this.vertices = vertices;
this.textureCoordinates = textureCoordinates;
//Create and bind VAO
vao = GL30.glGenVertexArrays();
GL30.glBindVertexArray(vao);
//Create and bind VBO, creates a buffer data store and stores that in VAO
vbo = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, BufferUtils.createFloatBuffer(vertices), GL15.GL_STATIC_DRAW);
GL20.glVertexAttribPointer(VERTEX_ATTRIB, 2, GL11.GL_FLOAT, false, 0, 0);
//Create and bind TCBO, creates a buffer data store and stores that in VAO
tcbo = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, tcbo);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, BufferUtils.createFloatBuffer(textureCoordinates), GL15.GL_STATIC_DRAW);
GL20.glVertexAttribPointer(TEXCOORD_ATTRIB, 2, GL11.GL_UNSIGNED_INT, false, 0, 0);
GL30.glBindVertexArray(0); //unbind latest bounded VAO
}
public void render() {
GL30.glBindVertexArray(vao);
GL20.glEnableVertexAttribArray(1);
GL20.glEnableVertexAttribArray(0); //enabling the 0th attribute list index (ORDER OF ENABLING / DISABLING DOES'NT MATTER)
GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, vertices.length);
GL20.glDisableVertexAttribArray(0); //disabling the 0th attribute list index
GL20.glDisableVertexAttribArray(1);
GL30.glBindVertexArray(0);
}
//delete the VAOs and the VBOs
public void cleanUp() {
GL30.glDeleteVertexArrays(vao);
GL15.glDeleteBuffers(vbo);
GL15.glDeleteBuffers(tcbo);
}
}
******************** Shader.java ********************
package com.codebitcookie.shaders;
import java.util.HashMap;
import java.util.Map;
import javax.swing.plaf.PanelUI;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import org.lwjgl.system.CallbackI.P;
import com.codebitcookie.graphics.Color;
import com.codebitcookie.maths.Matrix4f;
import com.codebitcookie.maths.Vector3f;
import com.codebitcookie.utils.BufferUtils;
import com.codebitcookie.utils.FileUtils;
public class Shader {
private Map<String, Integer> locationCache = new HashMap<>();
private static int programID;
private int vertexShaderID;
private int fragmentShaderID;
private boolean enabled = false;
public Shader(String vertexShaderPath, String fragmentShaderPath) {
//Create Shaders
programID = GL20.glCreateProgram();
vertexShaderID = GL20.glCreateShader(GL20.GL_VERTEX_SHADER);
fragmentShaderID = GL20.glCreateShader(GL20.GL_FRAGMENT_SHADER);
//After the shader is created we assign the actual shader file to the shader
GL20.glShaderSource(vertexShaderID, FileUtils.loadAsString(vertexShaderPath));
GL20.glShaderSource(fragmentShaderID, FileUtils.loadAsString(fragmentShaderPath));
//Compile the Shaders, and check for any compilation errors
GL20.glCompileShader(vertexShaderID);
if(GL20.glGetShaderi(vertexShaderID, GL20.GL_COMPILE_STATUS) == GL11.GL_FALSE) {
System.err.println("[VERTEX] Compile Error: " + GL20.glGetShaderInfoLog(vertexShaderID, 2048));
System.exit(-1);
}
GL20.glCompileShader(fragmentShaderID);
if(GL20.glGetShaderi(fragmentShaderID, GL20.GL_COMPILE_STATUS) == GL11.GL_FALSE) {
System.err.println("[FRAGMENT] Compile Error: " + GL20.glGetShaderInfoLog(fragmentShaderID, 2048));
System.exit(-1);
}
//Attach shaders to program
GL20.glAttachShader(programID, vertexShaderID);
GL20.glAttachShader(programID, fragmentShaderID);
//set layout location explicitly in java
GL20.glBindAttribLocation(programID, 0, "vertices");
GL20.glBindAttribLocation(programID, 1, "uv");
//Links together and creates executables for all shaders that were attached to the program, and check for any linking errors
GL20.glLinkProgram(programID);
if(GL20.glGetProgrami(programID, GL20.GL_LINK_STATUS) == GL11.GL_FALSE) {
System.err.println("[PROGRAM] Link Error: " + GL20.glGetProgramInfoLog(programID, 2048));
System.exit(-1);
}
//Checks if the executables created are valid and can execute given the current OpenGL state, and check for any validation errors
GL20.glValidateProgram(programID);
if(GL20.glGetProgrami(programID, GL20.GL_VALIDATE_STATUS) == GL11.GL_FALSE) {
System.err.println("[PROGRAM] Validation Error: " + GL20.glGetProgramInfoLog(programID, 2048));
System.exit(-1);
}
}
public void bind() {
GL20.glUseProgram(programID);
enabled = true;
}
public void unbind() {
GL20.glUseProgram(0);
}
public void cleanUp() {
unbind();
locationCache.clear();
GL20.glDetachShader(programID, vertexShaderID);
GL20.glDetachShader(programID, fragmentShaderID);
GL20.glDeleteShader(vertexShaderID);
GL20.glDeleteShader(fragmentShaderID);
GL20.glDeleteProgram(programID);
}
public int getUniformLocation(String name) {
if(locationCache.containsKey(name)) {
return locationCache.get(name);
}
int location = GL20.glGetUniformLocation(programID, name);
if(location == -1) {
System.err.println("[Shader] Error: Couldn't get the location uniform variable " + name);
} else {
locationCache.put(name, location);
}
return location;
}
public void setUniformColor(String name, Color c) {
if(!enabled) bind();
GL20.glUniform4f(getUniformLocation(name), c.getR(), c.getG(), c.getB(), c.getA());
}
public void setUniform1i(String name, int value) {
if(!enabled) bind();
GL20.glUniform1i(getUniformLocation(name), value);
}
public void setUniform1b(String name, boolean value) {
if(!enabled) bind();
int toLoad = value ? 1 : 0;
GL20.glUniform1f(getUniformLocation(name), toLoad);
}
public void setUniform1f(String name, float value) {
if(!enabled) bind();
GL20.glUniform1f(getUniformLocation(name), value);
}
public void setUniform2f(String name, float x, float y) {
if(!enabled) bind();
GL20.glUniform2f(getUniformLocation(name), x, y);
}
public void setUniform3f(String name, Vector3f value) {
if(!enabled) bind();
GL20.glUniform3f(getUniformLocation(name), value.getX(), value.getY(), value.getZ());
}
public void setUniformMat4f(String name, Matrix4f matrix) {
if(!enabled) bind();
GL20.glUniformMatrix4fv(getUniformLocation(name), false, BufferUtils.createFloatBuffer(matrix.getMatrix())); //false is for transpose, if we didn't setup our matrices in column major we could say true which would to this for us automatically but extra work for the memory, I think?
}
}
******************** VertexShader.vs ********************
//It doesn't matter what extension or name you give these Shader files as we only read the text
//Vertex files are used for positioning vertices and that's it.
//Will be run per vertex, so 3 times
//Uniform is the type of variable which can communicate with java code / not glsl
//Uses version 3.3 with the core profile
#version 330 core
in vec2 position;
in vec2 textureCoords;
out vec4 color;
out vec2 uvCoords;
uniform vec4 matColor;
uniform mat4 projection;
void main(){
//our triangle, or whatever is drawn is a single unit, and because of our special projection matrix is rendered as one single pixel
//to fix this problem we create a world position (see notes) which scales the unit up (position * 100)
//and moves it by 100 pixels from the top and 100 pixels from the left ( + vec2(100, 100) )
vec2 worldPosition = (position * 100);// + vec2(100, 100);
gl_Position = projection * vec4(worldPosition, 0.0f, 1.0f);
color = matColor;
uvCoords = textureCoords;
}
********************* FragmentShader.fs *********************
#version 330 core
uniform sampler2D sampler;
in vec4 color;
in vec2 uvCoords;
out vec4 out_color;
void main() {
out_color = color * texture(sampler, uvCoords);
}
******************** Texture.java ********************
package com.codebitcookie.graphics;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.LinkedList;
import java.util.List;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.stb.STBImage;
public class Texture {
private String name = "";
private String filepath = "";
private int id;
private int width, height;
private static List<Texture> textureInstances = new LinkedList<>();
private static Texture tmp = null;
private static int i = 0;
public Texture(String name, String filepath) {
this.name = name;
this.filepath = filepath;
IntBuffer width = BufferUtils.createIntBuffer(1);
IntBuffer height = BufferUtils.createIntBuffer(1);
IntBuffer channels = BufferUtils.createIntBuffer(1);
//get pixel data in RGBA format with STBImage, if we used BufferedImage we would get ARGB and we would have to change it to RGBA
ByteBuffer data = STBImage.stbi_load(filepath, width, height, channels, 4);
id = GL11.glGenTextures();
this.width = width.get();
this.height = height.get();
GL11.glBindTexture(GL11.GL_TEXTURE_2D, id);
//see notes for this
//We do GL_NEAREST because in our game we are using pixel art and it won't look sharp if we use bi linear filtering
GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, this.width, this.height, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, data);
STBImage.stbi_image_free(data);
textureInstances.add(this);
}
public static Texture find(String texturename) {
for(i = 0; i < textureInstances.size(); i++) {
tmp = getTextureInstances().get(i);
if(tmp.name.startsWith(texturename))
return tmp;
}
return null;
}
public void bind() {
GL13.glActiveTexture(GL13.GL_TEXTURE0);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, id);
}
public void unbind() {
GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
}
public static void cleanUp() {
for(i = 0; i < textureInstances.size(); i++) {
GL13.glDeleteTextures(textureInstances.get(i).getId());
}
}
public String getName() {
return name;
}
public int getId() {
return id;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public static List<Texture> getTextureInstances() {
return textureInstances;
}
}
原来这个人回答了我的 post https://whosebug.com/users/2579738/bdl
但是在评论中,我要求他 post 作为答案,但他没有。不幸的是,这导致我的 post 被删除。这就是为什么我决定 post 它作为答案。答案是:
GL20.glVertexAttribPointer(TEXCOORD_ATTRIB, 2, GL11.GL_UNSIGNED_INT
,
您正在使用浮点数据,但告诉 OpenGL 它包含无符号整数。
我查看了您的代码,没有看到正在为图集中的一个正方形计算纹理坐标。当您将地图集的一个区域映射到您的顶点时,您还必须使用这些纹理坐标。
mesh.render中使用的顶点如何从图集中获取它们的纹理坐标?
我想要一个带有纹理的四边形,从头开始使用 LWJGL,但由于某种原因,四边形上只应用了右上角的第一个 texel/pixel。
我也尝试过使用多个纹理,但仍然无法解决问题。
这是纹理:
这是我得到的:
package com.codebitcookie.engine;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL11.*;
import com.codebitcookie.graphics.Color;
import com.codebitcookie.graphics.Mesh;
import com.codebitcookie.graphics.Texture;
import com.codebitcookie.maths.Matrix4f;
import com.codebitcookie.shaders.Shader;
import static org.lwjgl.glfw.GLFW.*;
public class Core {
private static float[] verts = {
// Quad
// -0.5f, 0.5f, 0.5f,
// 0.5f, 0.5f, 0.5f,
// 0.5f, -0.5f, 0.5f,
//
// -0.5f, 0.5f, 0.5f,
// 0.5f, -0.5f, 0.5f,
// -0.5f, -0.5f, 0.5f
// Triangle
// 0.0f, 0.5f, 0.5f,
// 0.5f, -0.5f, 0.5f,
// -0.5f, -0.5f, 0.5f
// You might have to flip the Y-values because of the way OpenGL's coordinates work
// (i.e. (0,0) is normally bottom-left instead of top-left)
// Quad
0, 1,
1, 1,
1, 0,
1, 0,
0, 0,
0, 1
// Triangle
// 0, 1,
// 0.5f, 0,
// 1, 1
};
// int[] indices = {
// 0, 1, 3,
// 3, 1, 2
// };
private static float[] uvs = {
// 0, 0,
// 0, 1,
// 1, 1,
// 1, 0
0, 1,
1, 1,
1, 0,
1, 0,
0, 0,
0, 1
};
public static void main(String[] args) {
long window = Application.init();
Color ubuntu = Color.ubuntu();
//1920 x 1080 is a 16:9 aspect ratio thats why we take the aspect ratio of 1080p and apply it on a 10x10x10 projection
// Matrix4f ortho = Matrix4f.orthographic(-10.0f, 10.0f, -10.0f * 9.0f / 16.0f, 10.0f * 9.0f / 16.0f, 1.0f, -1.0f);
Matrix4f ortho = Matrix4f.orthographic(0.0f, Application.getWidth(), Application.getHeight(), 0.0f, 1.0f, -1.0f); //TODO: CHECK IF Z WORKS
Mesh mesh = new Mesh(verts, uvs);
Shader shader = new Shader("res/shaders/VertexShader.vs", "res/shaders/FragmentShader.fs");
Texture texture = new Texture("minecraftTextureAtlas", "res/textures/minecraftTextureAtlas.png");
// Texture texture = new Texture("minecraftPlanks", "res/textures/minecraftPlanks.jpeg");
GL11.glClearColor(ubuntu.getR(), ubuntu.getG(), ubuntu.getB(), ubuntu.getA()); //set a color for when you call glClear to clear the screen
shader.bind();
shader.setUniformColor("matColor", Color.white());
shader.setUniformMat4f("projection", ortho);
shader.unbind();
while(!glfwWindowShouldClose(window)) {
glfwPollEvents(); //processes events that are in the event queue and then returns immediately.
//this clears the color buffer and sets it to what we set it to when we call glClearColor()
//GL_COLOR_BUFFER_BIT: contains the RGB info for every pixel.
//GL_DEPTH_BUFFER_BIT: contains the distance between pixels from screen or for making layers in our case for e.g some pixel is in front of others
//GL_STENCIL_BUFFER_BIT: A custom buffer for your use for per pixel info
//And many others
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
shader.bind();
texture.bind();
mesh.render();
texture.unbind();
shader.unbind();
glfwSwapBuffers(window); //Swap Buffers
}
Texture.cleanUp();
shader.cleanUp();
mesh.cleanUp();
glfwTerminate(); // Destroys all windows and cursors, frees and restores resources. you must call glfwInit after.
}
}
******************** Application.java ********************
package com.codebitcookie.engine;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.system.MemoryUtil.NULL;
import org.lwjgl.glfw.GLFWVidMode;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GL11;
import com.codebitcookie.graphics.Color;
public class Application {
private static final String TITLE = "2D Game Engine /w GUI";
private static int width = 1280;
private static int height = 720;
private static long window;
public static long init() {
//check if GLFW is initialized if not we throw an exception
if(!glfwInit()) {
throw new IllegalStateException("GLFW failed to be initialized");
}
// GLFW.glfwDefaultWindowHints(); //equals to the two lines below
glfwWindowHint(GLFW_VISIBLE, GL11.GL_FALSE); //makes the window visible
glfwWindowHint(GLFW_RESIZABLE, GL11.GL_TRUE); //makes the window resizable
//sets OpenGL to 3.3; major is 3.*** and minor is ***.3 = 3.3
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
//for more info on these, check out:
//https://community.khronos.org/t/forward-compatible-vs-core-profile/65039
//https://www.khronos.org/opengl/wiki/OpenGL_Context
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL11.GL_TRUE); //makes mac run nicely but restores all deprecated functionality
window = glfwCreateWindow(width, height, TITLE, NULL, NULL);
//if window could not be created
if(window == NULL) {
glfwTerminate(); // Destroys all windows and cursors, frees and restores resources. you must call glfwInit after.
throw new RuntimeException("Unable to create window " + window);
}
GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
glfwSetWindowPos(window, (vidmode.width() - width) / 2, (vidmode.height() - height) / 2);
glfwMakeContextCurrent(window);
glfwShowWindow(window); //show window which we hid in the window hints
GL.createCapabilities();
//I don't have screen tearing issues so I am commenting this out
// glfwSwapInterval(1);
GL11.glEnable(GL11.GL_DEPTH_TEST);
return window;
}
public static String getTitle() {
return TITLE;
}
public static int getWidth() {
return width;
}
******************** Mesh.java ********************
package com.codebitcookie.graphics;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import com.codebitcookie.utils.BufferUtils;
public class Mesh {
public final static int VERTEX_ATTRIB = 0;
public final static int TEXCOORD_ATTRIB = 1;
private int vao, vbo, tcbo; //Vertex Array Obj, Vertex Buffer Obj, Texture Coords Buffer Obj
private float[] vertices, textureCoordinates;
public Mesh(float[] vertices, float[] textureCoordinates) {
this.vertices = vertices;
this.textureCoordinates = textureCoordinates;
//Create and bind VAO
vao = GL30.glGenVertexArrays();
GL30.glBindVertexArray(vao);
//Create and bind VBO, creates a buffer data store and stores that in VAO
vbo = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, BufferUtils.createFloatBuffer(vertices), GL15.GL_STATIC_DRAW);
GL20.glVertexAttribPointer(VERTEX_ATTRIB, 2, GL11.GL_FLOAT, false, 0, 0);
//Create and bind TCBO, creates a buffer data store and stores that in VAO
tcbo = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, tcbo);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, BufferUtils.createFloatBuffer(textureCoordinates), GL15.GL_STATIC_DRAW);
GL20.glVertexAttribPointer(TEXCOORD_ATTRIB, 2, GL11.GL_UNSIGNED_INT, false, 0, 0);
GL30.glBindVertexArray(0); //unbind latest bounded VAO
}
public void render() {
GL30.glBindVertexArray(vao);
GL20.glEnableVertexAttribArray(1);
GL20.glEnableVertexAttribArray(0); //enabling the 0th attribute list index (ORDER OF ENABLING / DISABLING DOES'NT MATTER)
GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, vertices.length);
GL20.glDisableVertexAttribArray(0); //disabling the 0th attribute list index
GL20.glDisableVertexAttribArray(1);
GL30.glBindVertexArray(0);
}
//delete the VAOs and the VBOs
public void cleanUp() {
GL30.glDeleteVertexArrays(vao);
GL15.glDeleteBuffers(vbo);
GL15.glDeleteBuffers(tcbo);
}
}
******************** Shader.java ********************
package com.codebitcookie.shaders;
import java.util.HashMap;
import java.util.Map;
import javax.swing.plaf.PanelUI;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import org.lwjgl.system.CallbackI.P;
import com.codebitcookie.graphics.Color;
import com.codebitcookie.maths.Matrix4f;
import com.codebitcookie.maths.Vector3f;
import com.codebitcookie.utils.BufferUtils;
import com.codebitcookie.utils.FileUtils;
public class Shader {
private Map<String, Integer> locationCache = new HashMap<>();
private static int programID;
private int vertexShaderID;
private int fragmentShaderID;
private boolean enabled = false;
public Shader(String vertexShaderPath, String fragmentShaderPath) {
//Create Shaders
programID = GL20.glCreateProgram();
vertexShaderID = GL20.glCreateShader(GL20.GL_VERTEX_SHADER);
fragmentShaderID = GL20.glCreateShader(GL20.GL_FRAGMENT_SHADER);
//After the shader is created we assign the actual shader file to the shader
GL20.glShaderSource(vertexShaderID, FileUtils.loadAsString(vertexShaderPath));
GL20.glShaderSource(fragmentShaderID, FileUtils.loadAsString(fragmentShaderPath));
//Compile the Shaders, and check for any compilation errors
GL20.glCompileShader(vertexShaderID);
if(GL20.glGetShaderi(vertexShaderID, GL20.GL_COMPILE_STATUS) == GL11.GL_FALSE) {
System.err.println("[VERTEX] Compile Error: " + GL20.glGetShaderInfoLog(vertexShaderID, 2048));
System.exit(-1);
}
GL20.glCompileShader(fragmentShaderID);
if(GL20.glGetShaderi(fragmentShaderID, GL20.GL_COMPILE_STATUS) == GL11.GL_FALSE) {
System.err.println("[FRAGMENT] Compile Error: " + GL20.glGetShaderInfoLog(fragmentShaderID, 2048));
System.exit(-1);
}
//Attach shaders to program
GL20.glAttachShader(programID, vertexShaderID);
GL20.glAttachShader(programID, fragmentShaderID);
//set layout location explicitly in java
GL20.glBindAttribLocation(programID, 0, "vertices");
GL20.glBindAttribLocation(programID, 1, "uv");
//Links together and creates executables for all shaders that were attached to the program, and check for any linking errors
GL20.glLinkProgram(programID);
if(GL20.glGetProgrami(programID, GL20.GL_LINK_STATUS) == GL11.GL_FALSE) {
System.err.println("[PROGRAM] Link Error: " + GL20.glGetProgramInfoLog(programID, 2048));
System.exit(-1);
}
//Checks if the executables created are valid and can execute given the current OpenGL state, and check for any validation errors
GL20.glValidateProgram(programID);
if(GL20.glGetProgrami(programID, GL20.GL_VALIDATE_STATUS) == GL11.GL_FALSE) {
System.err.println("[PROGRAM] Validation Error: " + GL20.glGetProgramInfoLog(programID, 2048));
System.exit(-1);
}
}
public void bind() {
GL20.glUseProgram(programID);
enabled = true;
}
public void unbind() {
GL20.glUseProgram(0);
}
public void cleanUp() {
unbind();
locationCache.clear();
GL20.glDetachShader(programID, vertexShaderID);
GL20.glDetachShader(programID, fragmentShaderID);
GL20.glDeleteShader(vertexShaderID);
GL20.glDeleteShader(fragmentShaderID);
GL20.glDeleteProgram(programID);
}
public int getUniformLocation(String name) {
if(locationCache.containsKey(name)) {
return locationCache.get(name);
}
int location = GL20.glGetUniformLocation(programID, name);
if(location == -1) {
System.err.println("[Shader] Error: Couldn't get the location uniform variable " + name);
} else {
locationCache.put(name, location);
}
return location;
}
public void setUniformColor(String name, Color c) {
if(!enabled) bind();
GL20.glUniform4f(getUniformLocation(name), c.getR(), c.getG(), c.getB(), c.getA());
}
public void setUniform1i(String name, int value) {
if(!enabled) bind();
GL20.glUniform1i(getUniformLocation(name), value);
}
public void setUniform1b(String name, boolean value) {
if(!enabled) bind();
int toLoad = value ? 1 : 0;
GL20.glUniform1f(getUniformLocation(name), toLoad);
}
public void setUniform1f(String name, float value) {
if(!enabled) bind();
GL20.glUniform1f(getUniformLocation(name), value);
}
public void setUniform2f(String name, float x, float y) {
if(!enabled) bind();
GL20.glUniform2f(getUniformLocation(name), x, y);
}
public void setUniform3f(String name, Vector3f value) {
if(!enabled) bind();
GL20.glUniform3f(getUniformLocation(name), value.getX(), value.getY(), value.getZ());
}
public void setUniformMat4f(String name, Matrix4f matrix) {
if(!enabled) bind();
GL20.glUniformMatrix4fv(getUniformLocation(name), false, BufferUtils.createFloatBuffer(matrix.getMatrix())); //false is for transpose, if we didn't setup our matrices in column major we could say true which would to this for us automatically but extra work for the memory, I think?
}
}
******************** VertexShader.vs ********************
//It doesn't matter what extension or name you give these Shader files as we only read the text
//Vertex files are used for positioning vertices and that's it.
//Will be run per vertex, so 3 times
//Uniform is the type of variable which can communicate with java code / not glsl
//Uses version 3.3 with the core profile
#version 330 core
in vec2 position;
in vec2 textureCoords;
out vec4 color;
out vec2 uvCoords;
uniform vec4 matColor;
uniform mat4 projection;
void main(){
//our triangle, or whatever is drawn is a single unit, and because of our special projection matrix is rendered as one single pixel
//to fix this problem we create a world position (see notes) which scales the unit up (position * 100)
//and moves it by 100 pixels from the top and 100 pixels from the left ( + vec2(100, 100) )
vec2 worldPosition = (position * 100);// + vec2(100, 100);
gl_Position = projection * vec4(worldPosition, 0.0f, 1.0f);
color = matColor;
uvCoords = textureCoords;
}
********************* FragmentShader.fs *********************
#version 330 core
uniform sampler2D sampler;
in vec4 color;
in vec2 uvCoords;
out vec4 out_color;
void main() {
out_color = color * texture(sampler, uvCoords);
}
******************** Texture.java ********************
package com.codebitcookie.graphics;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.LinkedList;
import java.util.List;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.stb.STBImage;
public class Texture {
private String name = "";
private String filepath = "";
private int id;
private int width, height;
private static List<Texture> textureInstances = new LinkedList<>();
private static Texture tmp = null;
private static int i = 0;
public Texture(String name, String filepath) {
this.name = name;
this.filepath = filepath;
IntBuffer width = BufferUtils.createIntBuffer(1);
IntBuffer height = BufferUtils.createIntBuffer(1);
IntBuffer channels = BufferUtils.createIntBuffer(1);
//get pixel data in RGBA format with STBImage, if we used BufferedImage we would get ARGB and we would have to change it to RGBA
ByteBuffer data = STBImage.stbi_load(filepath, width, height, channels, 4);
id = GL11.glGenTextures();
this.width = width.get();
this.height = height.get();
GL11.glBindTexture(GL11.GL_TEXTURE_2D, id);
//see notes for this
//We do GL_NEAREST because in our game we are using pixel art and it won't look sharp if we use bi linear filtering
GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, this.width, this.height, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, data);
STBImage.stbi_image_free(data);
textureInstances.add(this);
}
public static Texture find(String texturename) {
for(i = 0; i < textureInstances.size(); i++) {
tmp = getTextureInstances().get(i);
if(tmp.name.startsWith(texturename))
return tmp;
}
return null;
}
public void bind() {
GL13.glActiveTexture(GL13.GL_TEXTURE0);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, id);
}
public void unbind() {
GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
}
public static void cleanUp() {
for(i = 0; i < textureInstances.size(); i++) {
GL13.glDeleteTextures(textureInstances.get(i).getId());
}
}
public String getName() {
return name;
}
public int getId() {
return id;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public static List<Texture> getTextureInstances() {
return textureInstances;
}
}
原来这个人回答了我的 post https://whosebug.com/users/2579738/bdl 但是在评论中,我要求他 post 作为答案,但他没有。不幸的是,这导致我的 post 被删除。这就是为什么我决定 post 它作为答案。答案是:
GL20.glVertexAttribPointer(TEXCOORD_ATTRIB, 2, GL11.GL_UNSIGNED_INT
,
您正在使用浮点数据,但告诉 OpenGL 它包含无符号整数。
我查看了您的代码,没有看到正在为图集中的一个正方形计算纹理坐标。当您将地图集的一个区域映射到您的顶点时,您还必须使用这些纹理坐标。
mesh.render中使用的顶点如何从图集中获取它们的纹理坐标?