所以,我正在使用LWJGL和OpenGL渲染一些四边形和纹理。我的四边形一直很好,直到添加了一些纹理。具体来说,当我添加了向屏幕呈现文本的功能时,所有四边形颜色都变为黑色。我一直在寻找解决方案,但没有运气,最常见的答案是仅在绘制纹理时使用glEnable(GL_TEXTURE_2D);
,否则在绘制四边形时禁用它。这对我来说似乎没有用,我的四边形仍然是黑色的。我已经在下面包含了我的渲染器,字体和纹理类,因为我很确定问题就在那里。
编辑:所以我做了一些测试,我甚至没有尝试绘制普通纹理(来自.png文件)但是,现在我已经尝试过这样做了好吧,我发现它们也是完全黑色的。到目前为止,唯一正确渲染的是我的字体/文本,即使我创建了一个纹理来渲染它们
Renderer.java
public class Renderer {
private VertexArrayObject vao;
private VertexBufferObject vbo;
private ShaderProgram shaderProgram;
private FloatBuffer vertices;
private int numVertices;
private boolean drawing;
private Font fontSketchalot;
public void drawQuad(float x, float y, float width, float height, Colors c) {
glDisable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
begin();
if (vertices.remaining() < 5 * 7) flush();
// Calculate Vertex positions
float x1 = x;
float y1 = y;
float x2 = x + width;
float y2 = y - height;
// Calculate color
float r = c.getR();
float g = c.getG();
float b = c.getB();
// Put data into buffer
vertices.put(x1).put(y1).put(r).put(g).put(b).put(0f).put(1f);
vertices.put(x1).put(y2).put(r).put(g).put(b).put(0f).put(0f);
vertices.put(x2).put(y2).put(r).put(g).put(b).put(1f).put(0f);
vertices.put(x2).put(y1).put(r).put(g).put(b).put(1f).put(1f);
// We drew X vertices
numVertices += 4;
end();
}
public void drawTextureFromTexture(Texture texture, float x, float y, float width, float height) {
drawTexture(texture, x, y, x + width, y - height, 0, 0, 1, 1, Colors.white);
}
public void drawTextureFromFont(Texture texture, float x, float y, float width, float height, float s1, float t1, float sWidth, float sHeight, Colors c) {
drawTexture(texture, x, y, x + width, y - height, s1, t1, s1 + sWidth, t1 + sHeight, c);
}
public void drawTexture(Texture texture, float x1, float y1, float x2, float y2, float s1, float t1, float s2, float t2, Colors c) {
glEnable(GL_TEXTURE_2D);
texture.prepare();
begin();
if (vertices.remaining() < 5 * 7) flush();
// Calculate color
float r = c.getR();
float g = c.getG();
float b = c.getB();
// Put data into buffer
vertices.put(x1).put(y1).put(r).put(g).put(b).put(s1).put(t2);
vertices.put(x1).put(y2).put(r).put(g).put(b).put(s1).put(t1);
vertices.put(x2).put(y2).put(r).put(g).put(b).put(s2).put(t1);
vertices.put(x2).put(y1).put(r).put(g).put(b).put(s2).put(t2);
// We drew X vertices
numVertices += 4;
end();
texture.unbind();
}
public void drawText(String text, float x, float y, float scale, Colors c) {
fontSketchalot.drawText(this, text, x, y, scale, c);
}
// Initialize renderer
public void init(){
// Create font
fontSketchalot = new Font(Fonts.SKETCHALOT);
// Set up shader programs
setupShaderProgram();
// Set wrapping and filtering values
setParameter(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
setParameter(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
setParameter(GL_TEXTURE_MIN_FILTER, GL_NEAREST);
setParameter(GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// Enable blending (?????)
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
// Set parameter of texture
private void setParameter(int name, int value) {
glTexParameteri(GL_TEXTURE_2D, name, value);
}
// Clears drawing area
public void clear() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
// Begin rendering
public void begin() {
if (drawing) throw new IllegalStateException("Renderer is already drawing.");
drawing = true;
numVertices = 0;
}
// End rendering
public void end() {
if (!drawing) throw new IllegalStateException("Renderer is not drawing.");
drawing = false;
flush();
}
// Flushes data to GPU to get rendered
public void flush() {
if (numVertices > 0) {
vertices.flip();
if (vao != null) vao.bind();
else vbo.bind(GL_ARRAY_BUFFER);
specifyVertexAttributes();
}
shaderProgram.use();
// Upload the new vertex data
vbo.bind(GL_ARRAY_BUFFER);
vbo.uploadSubData(GL_ARRAY_BUFFER, 0, vertices);
// Draw batch
glDrawArrays(GL_QUADS, 0, numVertices);
// Clear vertex data for next batch
vertices.clear();
numVertices = 0;
}
private void setupShaderProgram() {
// Generate VertexArrayObject
if (Game.is32Supported()) {
vao = new VertexArrayObject();
vao.bind();
} else {
throw new RuntimeException("OpenGL 3.2 not supported.");
}
// Generate VertexBufferObject
vbo = new VertexBufferObject();
vbo.bind(GL_ARRAY_BUFFER);
// Create FloatBuffer
vertices = MemoryUtil.memAllocFloat(4096);
// Upload null data to allocate storage for the VBO
long size = vertices.capacity() * Float.BYTES;
vbo.uploadData(GL_ARRAY_BUFFER, size, GL_DYNAMIC_DRAW);
// Initialize variables
numVertices = 0;
drawing = false;
// Load Shaders:
Shader vertexShader, fragmentShader;
if (Game.is32Supported()) {
vertexShader = Shader.loadShader(GL_VERTEX_SHADER, "res/shaders/default.vert");
fragmentShader = Shader.loadShader(GL_FRAGMENT_SHADER, "res/shaders/default.frag");
} else {
throw new RuntimeException("OpenGL 3.2 not supported.");
}
// Create ShaderProgram
shaderProgram = new ShaderProgram();
shaderProgram.attachShader(vertexShader);
shaderProgram.attachShader(fragmentShader);
if (Game.is32Supported()) {
shaderProgram.bindFragmentDataLocation(0, "fragColor");
}
shaderProgram.link();
shaderProgram.use();
// Delete linked shaders
vertexShader.delete();
fragmentShader.delete();
// Get width & height of framebuffer
long window = GLFW.glfwGetCurrentContext();
int width, height;
try (MemoryStack stack = MemoryStack.stackPush()) {
IntBuffer widthBuffer = stack.mallocInt(1);
IntBuffer heightBuffer = stack.mallocInt(1);
GLFW.glfwGetFramebufferSize(window, widthBuffer, heightBuffer);
width = widthBuffer.get();
height = heightBuffer.get();
}
// Specify vertex pointers
specifyVertexAttributes();
// Set Model Matrix to identity matrix
Matrix4f model = new Matrix4f();
int uniModel = shaderProgram.getUniformLocation("model");
shaderProgram.setUniform(uniModel, model);
// Set View Matrix to identity matrix
Matrix4f view = new Matrix4f();
int uniView = shaderProgram.getUniformLocation("view");
shaderProgram.setUniform(uniView, view);
// Set Projection Matrix to an orthographic projection
Matrix4f projection = Matrix4f.orthographic(0f, width, 0f, height, -1f, 1f);
int uniProjection = shaderProgram.getUniformLocation("projection");
shaderProgram.setUniform(uniProjection, projection);
}
// Specifies the vertex shader pointers (attributes)
private void specifyVertexAttributes() {
int posAttrib = shaderProgram.getAttributeLocation("position");
shaderProgram.enableVertexAttribute(posAttrib);
shaderProgram.pointVertexAttribute(posAttrib, 2, 7 * Float.BYTES, 0);
int colAttrib = shaderProgram.getAttributeLocation("color");
shaderProgram.enableVertexAttribute(colAttrib);
shaderProgram.pointVertexAttribute(colAttrib, 3, 7 * Float.BYTES, 2 * Float.BYTES);
int texAttrib = shaderProgram.getAttributeLocation("texcoord");
shaderProgram.enableVertexAttribute(texAttrib);
shaderProgram.pointVertexAttribute(texAttrib, 2, 7 * Float.BYTES, 5 * Float.BYTES);
int uniTex = shaderProgram.getUniformLocation("texImage");
shaderProgram.setUniform(uniTex, 0);
}
}
Font.java
public class Font {
private String fontPath;
private java.awt.Font font;
private Map<Character, Glyph> glyphs;
private float fontHeight;
private Texture texture;
public Font(String fontPath) {
System.out.println("Creating font...");
this.fontPath = fontPath;
loadFont();
glyphs = new HashMap<>();
texture = createFontTexture();
System.out.println("Font created.");
}
public Font() {
System.out.println("Creating font...");
this.fontPath = "DEFAULT";
font = new java.awt.Font(java.awt.Font.MONOSPACED, java.awt.Font.PLAIN, 30);
glyphs = new HashMap<>();
texture = createFontTexture();
System.out.println("Font created.");
}
private void loadFont() {
try {
System.out.println("Loading font...");
font = java.awt.Font.createFont(java.awt.Font.TRUETYPE_FONT, new FileInputStream(fontPath)).deriveFont(java.awt.Font.PLAIN, 30);
System.out.println("Font loaded.");
} catch (Exception e) {
throw new RuntimeException("Could not load font.");
}
}
private Texture createFontTexture() {
int imageWidth = 0;
int imageHeight = 0;
// Add up total width and height
for (int i = 32; i < 256; i++) {
if (i == 127) { // DEL control code
continue;
}
char c = (char) i;
BufferedImage ch = createCharImage(c);
if (ch == null) {
System.out.println("Could not load [CHAR: \"" + c + "\"] from font: " + fontPath);
continue;
}
imageWidth += ch.getWidth();
imageHeight = Math.max(imageHeight, ch.getHeight());
}
fontHeight = Converter.glfwCoordToOpenGLCoord(imageHeight);
/* Image for the texture */
BufferedImage image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = image.createGraphics();
// Set up glyphs (Map<CHAR, GLYPH>)
int xOffsetI = 0;
float xOffsetF = 0;
for (int i = 32; i < 256; i++) {
if (i == 127) { // DEL control code
continue;
}
char c = (char) i;
BufferedImage charImage = createCharImage(c);
if (charImage == null) {
System.out.println("Could not load [CHAR: \"" + c + "\"] from font: " + fontPath);
continue;
}
int charWidth = charImage.getWidth();
int charHeight = charImage.getHeight();
// Create glyph
Glyph ch = new Glyph(Converter.glfwCoordToOpenGLCoord(charWidth), Converter.glfwCoordToOpenGLCoord(charHeight), ((float)charWidth / (float)imageWidth), (float)charHeight / (float)imageHeight, xOffsetF, 0.0f);
// Draw char on image
g.drawImage(charImage, xOffsetI, 0, null);
xOffsetI += charWidth;
xOffsetF += ch.sWidth;
// Put in map
glyphs.put(c, ch);
}
// Flip image Horizontal to get the origin to bottom left
AffineTransform transform = AffineTransform.getScaleInstance(1f, -1f);
transform.translate(0, -image.getHeight());
AffineTransformOp operation = new AffineTransformOp(transform, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
image = operation.filter(image, null);
// Get char width & char height of image
int width = image.getWidth();
int height = image.getHeight();
// Put pixel data into int[] pixels
int[] pixels = new int[width * height];
image.getRGB(0, 0, width, height, pixels, 0, width);
// Put pixel data into byte buffer
ByteBuffer buffer = MemoryUtil.memAlloc(width * height * 4);
for (int i = 0; i < height; i++){
for (int j = 0; j < width; j++) {
// Pixel format : OxAARRGGBB
int pixel = pixels[i * width + j];
buffer.put((byte) ((pixel >> 16) & 0xFF)); // 0x000000RR
buffer.put((byte) ((pixel >> 8) & 0xFF)); // 0x000000GG
buffer.put((byte) ((pixel) & 0xFF)); // 0x000000BB
buffer.put((byte) ((pixel >> 24) & 0xFF)); // 0x000000AA
//buffer.put((byte)(0xFF)); // Test
}
}
buffer.flip(); // Set index back to 0
Texture fontTexture = Texture.createTexture(width, height, buffer); // Create texture
MemoryUtil.memFree(buffer); // Free buffer
return fontTexture;
}
private BufferedImage createCharImage(char c) {
// Begin by calculating proper width and height
BufferedImage image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = image.createGraphics();
if (Options.ANTIALIAS) {
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
}
g.setFont(font);
FontMetrics fontMetrics = g.getFontMetrics();
g.dispose();
int charWidth = fontMetrics.charWidth(c);
int charHeight = fontMetrics.getHeight();
if (charWidth == 0) return null;
// Now set up the image
image = new BufferedImage(charWidth, charHeight, BufferedImage.TYPE_INT_ARGB);
g = image.createGraphics();
if (Options.ANTIALIAS) {
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
}
g.setFont(font);
g.setPaint(Color.WHITE); // Use white so we can set the color while rendering
g.drawString(String.valueOf(c), 0, fontMetrics.getAscent()); // Paint char onto image
g.dispose();
// Finally return the final image
return image;
}
public void drawText(Renderer renderer, CharSequence text, float x, float y, float scale, Colors c) {
float xOffset = 0;
float yOffset = 0;
texture.prepare();
//renderer.begin();
for (int i = 0; i < text.length(); i++) {
char ch = text.charAt(i);
if (ch == '\n') {
// If we start a new line, change the yOffset to reflect (then continue)
yOffset -= fontHeight;
continue;
}
if (ch == '\r') {
// Skip carriage return
continue;
}
Glyph g = glyphs.get(ch);
renderer.drawTextureFromFont(texture, x + xOffset, y + yOffset, g.width * scale, g.height * scale, g.s, g.t, g.sWidth, g.sHeight, c);
xOffset += g.width * scale;
}
//renderer.end();
texture.unbind();
}
}
Texture.java
public class Texture {
// Store handle
private final int id;
private int width;
private int height;
private ByteBuffer image;
// Draw texture using given renderer
public void draw(Renderer renderer, float x, float y, float scale) {
// Scale texture
float w = ((float)width * scale * 2) / (Game.WIDTH);
float h = ((float)height * scale * 2) / (Game.HEIGHT);
renderer.drawTextureFromTexture(this, x, y, w, h);
}
// Create new texture
public Texture() {
id = glGenTextures();
}
// Prepare texture to be drawn
public void prepare() {
bind();
uploadData(width, height, image);
}
// Bind texture
public void bind() {
glBindTexture(GL_TEXTURE_2D, id);
}
// Set parameter of texture
private void setParameter(int name, int value) {
glTexParameteri(GL_TEXTURE_2D, name, value);
}
// Upload image data with specified width and height
public void uploadData(int width, int height, ByteBuffer data) {
uploadData(GL_RGBA8, width, height, GL_RGBA, data);
}
// Upload image data with specified internal format, width, height, and image format
public void uploadData(int internalFormat, int width, int height, int format, ByteBuffer data) {
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, GL_UNSIGNED_BYTE, data);
}
// Delete texture
public void delete() {
glDeleteTextures(id);
}
// Get width
public int getWidth() {
return width;
}
// Set width
public void setWidth(int width) {
if (width > 0) {
this.width = width;
}
}
// Get height
public int getHeight() {
return height;
}
// Set height
public void setHeight(int height) {
if (height > 0) {
this.height = height;
}
}
// Set image
public void setImage(ByteBuffer image) {
this.image = image;
}
public static Texture createTexture(int width, int height, ByteBuffer image) {
Texture texture = new Texture();
texture.setWidth(width);
texture.setHeight(height);
texture.setImage(image);
texture.prepare();
return texture;
}
public static Texture loadTexture(String path) {
ByteBuffer image;
int width, height;
try (MemoryStack stack = MemoryStack.stackPush()) {
IntBuffer w = stack.mallocInt(1);
IntBuffer h = stack.mallocInt(1);
IntBuffer comp = stack.mallocInt(1);
// Load image
stbi_set_flip_vertically_on_load(true);
image = stbi_load(path, w, h, comp, 4);
if (image == null) throw new RuntimeException("Could not load texture.");
width = w.get();
height = h.get();
}
return createTexture(width, height, image);
}
public void unbind() {
glBindTexture(GL_TEXTURE_2D, 0);
}
}