所以,我当前的 OpenGL 渲染器似乎出于某种原因用纹理 A 绘制了纹理 B。我检查了 RenderDoc 中的插槽,它们似乎与 glBindTexture() 和 glActiveTexture() 正确绑定。我不太确定发生了什么事。我观察到,当完全不移动相机时,问题似乎就消失了。
这是主课:
package demo;
import org.joml.Matrix4f;
import org.lwjgl.opengl.GL46;
import rebel.graphics.*;
public class Test3D {
public static void main(String[] args) {
Window window = new Window(1000, 600, "");
Renderer3D renderer3D = new Renderer3D(window.getWidth(), window.getHeight(), true);
Texture2D a = new Texture2D("project/logo.png");
Texture2D b = new Texture2D("project/texture.png");
float rotX = 0f, rotY = 0f, rotZ = 0f;
while(!window.shouldClose()){
renderer3D.clear(Color.WHITE);
renderer3D.setOrigin(0, 0);
renderer3D.drawTexture(0.5f / -2f, 0.5f / -2f, 0.5f, 0.5f, a, Color.RED);
renderer3D.drawTexture((0.5f / -2f) + 0.5f, 0.5f / -2f, 1f, 1f, b, Color.BLUE);
renderer3D.resetTransform();
renderer3D.getCamera().getViewMatrix().rotate((float) Math.toRadians(rotX), 1, 0, 0);
renderer3D.getCamera().getViewMatrix().rotate((float) Math.toRadians(rotY), 0, 1, 0);
renderer3D.getCamera().getViewMatrix().translate(0, 0, 1f);
rotX = 360 * -(window.getMouseY() / window.getHeight());
rotY = 360 * (window.getMouseX() / window.getWidth());
renderer3D.updateCamera2D();
renderer3D.render();
renderer3D.getCamera().getViewMatrix().set(new Matrix4f().identity());
System.out.println(GL46.glGetError());
window.update();
}
window.close();
}
}
还有 3D 渲染器:
package rebel.graphics;
import org.joml.*;
import org.lwjgl.BufferUtils;
import rebel.FileReader;
import java.lang.Math;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.List;
import static org.lwjgl.opengl.GL46.*;
public class Renderer3D {
private int width;
private int height;
private Matrix4f proj;
private Camera camera;
private Matrix4f translation;
private VertexBuffer vertexBuffer;
private float[] vertexData;
private int maxTextureSlots;
private ShaderProgram defaultShaderProgram, currentShaderProgram;
private ArrayList<String> renderCallNames = new ArrayList<>(50);
private boolean debug = false;
private FastTextureLookup textureLookup;
private static final float FOV = (float) Math.toRadians(60.0f);
private static final float Z_NEAR = 0.01f;
private static final float Z_FAR = 1000.f;
public Renderer3D(int width, int height, boolean msaa) {
this.width = width;
this.height = height;
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_NOTEQUAL);
float aspectRatio = (float) getWidth() / getHeight();
proj = new Matrix4f().perspective(FOV, aspectRatio,
Z_NEAR, Z_FAR);
camera = new Camera();
translation = new Matrix4f().identity();
IntBuffer d = BufferUtils.createIntBuffer(1);
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, d);
maxTextureSlots = d.get();
textureLookup = new FastTextureLookup(maxTextureSlots);
defaultShaderProgram = new ShaderProgram(
FileReader.readFile(Renderer2D.class.getClassLoader().getResourceAsStream("3DBatchVertexShader.glsl")),
FileReader.readFile(Renderer2D.class.getClassLoader().getResourceAsStream("3DBatchFragmentShader.glsl"))
);
defaultShaderProgram.prepare();
currentShaderProgram = defaultShaderProgram;
currentShaderProgram.bind();
updateCamera2D();
VertexArray vertexArray = new VertexArray();
vertexArray.bind();
vertexArray.setVertexAttributes(
new VertexAttribute(0, 3, false, "v_pos"),
new VertexAttribute(1, 2, false, "v_uv"),
new VertexAttribute(2, 1, false, "v_texindex"),
new VertexAttribute(3, 4, false, "v_color"),
new VertexAttribute(4, 1, false, "v_thickness")
);
vertexBuffer = new VertexBuffer(1000, vertexArray.getStride());
vertexArray.build();
vertexData = new float[vertexBuffer.getNumOfVertices() * vertexBuffer.getVertexDataSize()];
}
/***
* Sets the current shader. This shader must be compiled before calling this method!
* @param shaderProgram
*/
public void setShader(ShaderProgram shaderProgram){
if(currentShaderProgram != shaderProgram) {
currentShaderProgram = shaderProgram;
currentShaderProgram.bind();
updateCamera2D();
}
}
public void updateCamera2D(){
currentShaderProgram.setMatrix4f("v_model", getTranslation());
currentShaderProgram.setMatrix4f("v_view", getView());
currentShaderProgram.setMatrix4f("v_projection", getProj());
currentShaderProgram.setIntArray("u_textures", createTextureSlots());
}
private int[] createTextureSlots() {
int[] slots = new int[maxTextureSlots];
for (int i = 0; i < maxTextureSlots; i++) {
slots[i] = i;
}
return slots;
}
public VertexBuffer getVertexBuffer() {
return vertexBuffer;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public Matrix4f getProj() {
return proj;
}
public Camera getCamera() {
return camera;
}
public Matrix4f getView() {
return camera.getViewMatrix();
}
public Matrix4f getTranslation() {
return translation;
}
private int quadIndex;
private int nextTextureSlot;
private Matrix4f transform = new Matrix4f().identity();
private float originX, originY;
public Matrix4f getTransform() {
return transform;
}
public void setTransform(Matrix4f transform) {
this.transform = transform;
}
public void resetTransform(){
setTransform(new Matrix4f().identity());
}
public void drawTexture(float x, float y, float w, float h, Texture2D texture){
drawTexture(x, y, w, h, texture, Color.WHITE);
}
public void setOrigin(float x, float y){
this.originX = x;
this.originY = y;
}
public void drawTexture(float x, float y, float w, float h, Texture2D texture, Color color){
drawTexture(x, y, w, h, texture, color, new Rect2D(0, 0, 1, 1), false, false);
}
public void drawTexture(float x, float y, float w, float h, Texture2D texture, Color color, Rect2D rect2D, boolean xFlip, boolean yFlip) {
int slot = nextTextureSlot;
boolean isUniqueTexture = false;
//Existing texture
if (textureLookup.hasTexture(texture)) {
slot = textureLookup.getTexture(texture);
}
//Unique Texture
else {
glActiveTexture(GL_TEXTURE0 + slot);
texture.bind();
texture.setSlot(slot);
textureLookup.registerTexture(texture, slot);
isUniqueTexture = true;
}
drawQuadGL(x, y, w, h, slot, color, originX, originY, rect2D, -1, xFlip, yFlip);
if(isUniqueTexture) nextTextureSlot++;
if(nextTextureSlot == maxTextureSlots)
render("Next Batch Render [No more rebel.engine.graphics.Texture slots out of " + maxTextureSlots + "]");
}
public void drawQuadGL(float x, float y, float w, float h, int slot, Color color, float originX, float originY, Rect2D region, float thickness, boolean xFlip, boolean yFlip){
//Translate back by origin (for rotation math)
//This usually takes everything near (0, 0)
Rect2D copy = new Rect2D(region.x, region.y, region.w, region.h);
if(xFlip){
float temp = copy.x;
copy.x = copy.w;
copy.w = temp;
}
if(yFlip){
float temp = copy.y;
copy.y = copy.h;
copy.h = temp;
}
float z = -0.25f;
Vector4f topLeft = new Vector4f(x - originX, y - originY, z, 1);
Vector4f topRight = new Vector4f(x + w - originX, y - originY, z, 1);
Vector4f bottomLeft = new Vector4f(x - originX, y + h - originY, z, 1);
Vector4f bottomRight = new Vector4f(x + w - originX, y + h - originY, z, 1);
topLeft.mul(transform);
topRight.mul(transform);
bottomLeft.mul(transform);
bottomRight.mul(transform);
//Translate forward by origin back to the current position
topLeft.x += originX;
topRight.x += originX;
bottomLeft.x += originX;
bottomRight.x += originX;
topLeft.y += originY;
topRight.y += originY;
bottomLeft.y += originY;
bottomRight.y += originY;
{
vertexData[(quadIndex * vertexBuffer.getVertexDataSize()) + 0] = topLeft.x;
vertexData[(quadIndex * vertexBuffer.getVertexDataSize()) + 1] = topLeft.y;
vertexData[(quadIndex * vertexBuffer.getVertexDataSize()) + 2] = topLeft.z;
vertexData[(quadIndex * vertexBuffer.getVertexDataSize()) + 3] = copy.x;
vertexData[(quadIndex * vertexBuffer.getVertexDataSize()) + 4] = copy.y;
vertexData[(quadIndex * vertexBuffer.getVertexDataSize()) + 5] = slot;
vertexData[(quadIndex * vertexBuffer.getVertexDataSize()) + 6] = color.r;
vertexData[(quadIndex * vertexBuffer.getVertexDataSize()) + 7] = color.g;
vertexData[(quadIndex * vertexBuffer.getVertexDataSize()) + 8] = color.b;
vertexData[(quadIndex * vertexBuffer.getVertexDataSize()) + 9] = color.a;
vertexData[(quadIndex * vertexBuffer.getVertexDataSize()) + 10] = thickness;
vertexData[(quadIndex * vertexBuffer.getVertexDataSize()) + 11] = bottomLeft.x;
vertexData[(quadIndex * vertexBuffer.getVertexDataSize()) + 12] = bottomLeft.y;
vertexData[(quadIndex * vertexBuffer.getVertexDataSize()) + 13] = bottomLeft.z;
vertexData[(quadIndex * vertexBuffer.getVertexDataSize()) + 14] = copy.x;
vertexData[(quadIndex * vertexBuffer.getVertexDataSize()) + 15] = copy.h;
vertexData[(quadIndex * vertexBuffer.getVertexDataSize()) + 16] = slot;
vertexData[(quadIndex * vertexBuffer.getVertexDataSize()) + 17] = color.r;
vertexData[(quadIndex * vertexBuffer.getVertexDataSize()) + 18] = color.g;
vertexData[(quadIndex * vertexBuffer.getVertexDataSize()) + 19] = color.b;
vertexData[(quadIndex * vertexBuffer.getVertexDataSize()) + 20] = color.a;
vertexData[(quadIndex * vertexBuffer.getVertexDataSize()) + 21] = thickness;
vertexData[(quadIndex * vertexBuffer.getVertexDataSize()) + 22] = bottomRight.x;
vertexData[(quadIndex * vertexBuffer.getVertexDataSize()) + 23] = bottomRight.y;
vertexData[(quadIndex * vertexBuffer.getVertexDataSize()) + 24] = bottomRight.z;
vertexData[(quadIndex * vertexBuffer.getVertexDataSize()) + 25] = copy.w;
vertexData[(quadIndex * vertexBuffer.getVertexDataSize()) + 26] = copy.h;
vertexData[(quadIndex * vertexBuffer.getVertexDataSize()) + 27] = slot;
vertexData[(quadIndex * vertexBuffer.getVertexDataSize()) + 28] = color.r;
vertexData[(quadIndex * vertexBuffer.getVertexDataSize()) + 29] = color.g;
vertexData[(quadIndex * vertexBuffer.getVertexDataSize()) + 30] = color.b;
vertexData[(quadIndex * vertexBuffer.getVertexDataSize()) + 31] = color.a;
vertexData[(quadIndex * vertexBuffer.getVertexDataSize()) + 32] = thickness;
vertexData[(quadIndex * vertexBuffer.getVertexDataSize()) + 33] = topRight.x;
vertexData[(quadIndex * vertexBuffer.getVertexDataSize()) + 34] = topRight.y;
vertexData[(quadIndex * vertexBuffer.getVertexDataSize()) + 35] = topRight.z;
vertexData[(quadIndex * vertexBuffer.getVertexDataSize()) + 36] = copy.w;
vertexData[(quadIndex * vertexBuffer.getVertexDataSize()) + 37] = copy.y;
vertexData[(quadIndex * vertexBuffer.getVertexDataSize()) + 38] = slot;
vertexData[(quadIndex * vertexBuffer.getVertexDataSize()) + 39] = color.r;
vertexData[(quadIndex * vertexBuffer.getVertexDataSize()) + 40] = color.g;
vertexData[(quadIndex * vertexBuffer.getVertexDataSize()) + 41] = color.b;
vertexData[(quadIndex * vertexBuffer.getVertexDataSize()) + 42] = color.a;
vertexData[(quadIndex * vertexBuffer.getVertexDataSize()) + 43] = thickness;
}
quadIndex++;
if(quadIndex == vertexBuffer.maxQuads()) render("Next Batch Render");
}
public void render(){
render("Final Draw Call [rebel.engine.graphics.Renderer2D.render()]");
if(debug){
System.out.println("Renderer2D (" + this + ") - Debug");
for(String call : getRenderCalls()){
System.out.print("\t" + call + "\n");
}
System.out.println("\n");
}
renderCallNames.clear();
}
public void render(String renderName) {
renderCallNames.add(renderName);
glBindBuffer(GL_ARRAY_BUFFER, getVertexBuffer().myVbo);
glBufferSubData(GL_ARRAY_BUFFER, 0, vertexData);
int numOfIndices = quadIndex * 6;
int[] indices = new int[numOfIndices];
int offset = 0;
for (int j = 0; j < numOfIndices; j += 6) {
indices[j] = offset;
indices[j + 1] = 1 + offset;
indices[j + 2] = 2 + offset;
indices[j + 3] = 2 + offset;
indices[j + 4] = 3 + offset;
indices[j + 5] = offset;
offset += 4;
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, getVertexBuffer().myEbo);
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, indices);
glDrawElements(GL_TRIANGLES, indices.length, GL_UNSIGNED_INT, 0);
vertexData = new float[vertexBuffer.getNumOfVertices() * vertexBuffer.getVertexDataSize()];
quadIndex = 0;
nextTextureSlot = 0;
textureLookup.clear();
}
public List<String> getRenderCalls(){
return new ArrayList<>(renderCallNames);
}
public void clear(Color color) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glClearColor(color.r, color.g, color.b, color.a);
}
}
这是我的顶点着色器:
#version 330 core
layout (location = 0) in vec3 v_pos;
layout (location = 1) in vec2 v_uv;
layout (location = 2) in float v_texindex;
layout (location = 3) in vec4 v_color;
layout (location = 4) in float v_thickness;
out vec2 f_uv;
out float f_texindex;
out vec4 f_color;
out vec2 f_origin;
out vec2 f_size;
out float f_thickness;
uniform mat4 v_model;
uniform mat4 v_view;
uniform mat4 v_projection;
void main() {
f_texindex = v_texindex;
f_uv = v_uv;
f_color = v_color;
f_thickness = v_thickness;
gl_Position = v_projection * v_view * v_model * vec4(v_pos, 1.0);
}
还有我的片段着色器
#version 330 core
out vec4 FragColor;
in vec2 f_uv;
uniform sampler2D u_textures[32];
in float f_texindex;
in vec4 f_color;
in float f_thickness;
layout(origin_upper_left) in vec4 gl_FragCoord;
void main()
{
int index = int(f_texindex);
if(index == -1){
FragColor = f_color;
}
else if(index == -2){
vec2 uv = f_uv * 2.0 - 1.0;
float distance = length(uv);
if(distance <= 1.0 && distance >= (1.0 - f_thickness))
FragColor = f_color;
else
discard;
}
else {
if(index == 0) FragColor += texture(u_textures[0], f_uv) * f_color;
if(index == 1) FragColor += texture(u_textures[1], f_uv) * f_color;
if(index == 2) FragColor += texture(u_textures[2], f_uv) * f_color;
if(index == 3) FragColor += texture(u_textures[3], f_uv) * f_color;
if(index == 4) FragColor += texture(u_textures[4], f_uv) * f_color;
if(index == 5) FragColor += texture(u_textures[5], f_uv) * f_color;
if(index == 6) FragColor += texture(u_textures[6], f_uv) * f_color;
if(index == 7) FragColor += texture(u_textures[7], f_uv) * f_color;
if(index == 8) FragColor += texture(u_textures[8], f_uv) * f_color;
if(index == 9) FragColor += texture(u_textures[9], f_uv) * f_color;
if(index == 10) FragColor += texture(u_textures[10], f_uv) * f_color;
if(index == 11) FragColor += texture(u_textures[11], f_uv) * f_color;
if(index == 12) FragColor += texture(u_textures[12], f_uv) * f_color;
if(index == 13) FragColor += texture(u_textures[13], f_uv) * f_color;
if(index == 14) FragColor += texture(u_textures[14], f_uv) * f_color;
if(index == 15) FragColor += texture(u_textures[15], f_uv) * f_color;
if(index == 16) FragColor += texture(u_textures[16], f_uv) * f_color;
if(index == 17) FragColor += texture(u_textures[17], f_uv) * f_color;
if(index == 18) FragColor += texture(u_textures[18], f_uv) * f_color;
if(index == 19) FragColor += texture(u_textures[19], f_uv) * f_color;
if(index == 20) FragColor += texture(u_textures[20], f_uv) * f_color;
if(index == 21) FragColor += texture(u_textures[21], f_uv) * f_color;
if(index == 22) FragColor += texture(u_textures[22], f_uv) * f_color;
if(index == 23) FragColor += texture(u_textures[23], f_uv) * f_color;
if(index == 24) FragColor += texture(u_textures[24], f_uv) * f_color;
if(index == 25) FragColor += texture(u_textures[25], f_uv) * f_color;
if(index == 26) FragColor += texture(u_textures[26], f_uv) * f_color;
if(index == 27) FragColor += texture(u_textures[27], f_uv) * f_color;
if(index == 28) FragColor += texture(u_textures[28], f_uv) * f_color;
if(index == 29) FragColor += texture(u_textures[29], f_uv) * f_color;
if(index == 30) FragColor += texture(u_textures[30], f_uv) * f_color;
if(index == 31) FragColor += texture(u_textures[31], f_uv) * f_color;
}
}
我期望它渲染纹理时不会出现任何瑕疵,主要是因为我从 2D 渲染器中继承了纹理批处理逻辑。我确信我错过了一些简单的事情。我使用的是 NVIDIA GeForce MX450 PCIe/SSE2。
glGetError() 没有报告任何错误,并且 LWJGL OpenGL 回调始终保持沉默。我已经使用 RenderDoc 验证了纹理已绑定到正确的插槽。
虽然问题可能出在您的纹理加载中,但我认为它更有可能出现在您的片段着色器中。我认为将 FragColor 声明为
FragColor += texture(u_textures[_], f_uv) * f_color
会给你的程序带来许多不同的问题。我知道统一数组必须在 GLSL 中初始化。批处理时,还有另一种解决方案。着色器存储缓冲区对象 (SSBO)。
我建议阅读this