英文:
OpenGL is only rendering one pixel of a texture on a quad?
问题
以下是翻译好的代码部分:
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 {
// ...(略去部分代码)
public static void main(String[] args) {
long window = Application.init();
Color ubuntu = Color.ubuntu();
// ...(略去部分代码)
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");
GL11.glClearColor(ubuntu.getR(), ubuntu.getG(), ubuntu.getB(), ubuntu.getA());
shader.bind();
shader.setUniformColor("matColor", Color.white());
shader.setUniformMat4f("projection", ortho);
shader.unbind();
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
shader.bind();
texture.bind();
mesh.render();
texture.unbind();
shader.unbind();
glfwSwapBuffers(window);
}
Texture.cleanUp();
shader.cleanUp();
mesh.cleanUp();
glfwTerminate();
}
}
// ...(略去其他代码部分)
请注意,上述的代码部分已经被翻译为中文。如有需要,您可以继续使用翻译好的代码。
英文:
I wanted a quad with a texture on it with LWJGL from scratch but for some reason, only the first top right texel/pixel is applied on the quad.
I also tried this with multiple textures and still can't solve the issue.
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;
}
}
答案1
得分: 1
最初这位用户回答了我的帖子 https://stackoverflow.com/users/2579738/bdl
但在评论中,我要求他将其发表为答案,但他没有这样做。不幸的是,这导致我的帖子被删除。这就是为什么我决定将其发布为答案。答案是:
GL20.glVertexAttribPointer(TEXCOORD_ATTRIB, 2, GL11.GL_UNSIGNED_INT
您正在使用浮点数据,但告诉OpenGL它包含无符号整数。
英文:
Originally this guy answered my post https://stackoverflow.com/users/2579738/bdl
but on a comment, I asked him to post it as an answer, but he didn't. Unfortunately this resulted with my post getting deleted. That is why I decided to post it as an answer. The answer is:
GL20.glVertexAttribPointer(TEXCOORD_ATTRIB, 2, GL11.GL_UNSIGNED_INT
,
You are using float data but tell OpenGL that it contains unsigned integers.
答案2
得分: 0
我查看了你的代码,没有看到其中一个方块的纹理坐标被计算出来。当你将图集的某个区域映射到顶点时,你还必须使用这些纹理坐标。
在 mesh.render
中使用的顶点如何从图集中获取它们的纹理坐标?
英文:
I looked at your code and don't see texture coordinates being calculated for one of the squares in the atlas. You would also have to use those texture coordinates when you map an area of the atlas to your vertices.
How do the vertices used in mesh.render get their texture coords from the atlas?
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论