macOS 上的 LWJGL:尝试使用着色器但未呈现任何内容

LWJGL on macOS : trying to have shader but nothing is rendered

我正在按照教程学习 LWJGL,但是 window 上什么也没有出现。 我是 Java 编程新手,我使用的是 macOS

这是我的主要内容 class:

package main;

import org.lwjgl.glfw.GLFW;

import engine.graphics.Mesh;
import engine.graphics.Renderer;
import engine.graphics.Shader;
import engine.graphics.Vertex;
import engine.io.Input;
import engine.io.Window;
import engine.maths.Vector3f;

public class Main implements Runnable{
    
    public Window window;
    public Renderer renderer;
    public Shader shader;
    
    public final int WIDHT = 1280, HEIGHT = 760;
    public final String TITLE = "Window";
    
    public Mesh mesh = new Mesh(new Vertex[] {
        new Vertex(new Vector3f(-0.5f,  0.5f, 0.0f)),
        new Vertex(new Vector3f(-0.5f, -0.5f, 0.0f)),
        new Vertex(new Vector3f( 0.5f, -0.5f, 0.0f)),
        new Vertex(new Vector3f( 0.5f,  0.5f, 0.0f))
    }, new int[] {
        0, 1, 2,
        0, 3, 2
    });
    
    public void init() {
        window = new Window(WIDHT, HEIGHT, TITLE);
        shader = new Shader("/shaders/mainVertex.glsl", "/shaders/mainFragment.glsl");
        renderer = new Renderer(shader);
        window.setBackgroundColor(1.0f, 0.0f, 0.0f);
        
        window.create();
        mesh.create();
        shader.create();
    }
    
    private void update() {
        window.update();
        if(Input.isButtonDown(GLFW.GLFW_MOUSE_BUTTON_LEFT)) {
            System.out.println("x:" + Input.getMouseX() + " /y:" + Input.getMouseY());
        }
    }
    
    private void render() {
        renderer.renderMesh(mesh);
        window.swapBuffers();
    }
    
    public void run() {
        init();
        while(!window.shouldClose() && !Input.isKeyDown(GLFW.GLFW_KEY_ESCAPE)) {
            update();
            render();
        }
        window.destroy();
    }

    public static void main(String[] args) {
        new Main().run();
    }

}

我的Windowclass:

package engine.io;

import org.lwjgl.glfw.GLFW;
import org.lwjgl.glfw.GLFWVidMode;
import org.lwjgl.glfw.GLFWWindowSizeCallback;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GL11;
import engine.maths.Vector3f;

public class Window {
    
    private int width, height;
    private String title;
    private long window;
    private int[] windowPosX = new int[1], windowPosY = new int[1];
    
    private int frames;
    private long time;
    
    private Input input;
    
    private Vector3f background = new Vector3f(0, 0, 0);
    
    private GLFWWindowSizeCallback sizeCallback;
    private boolean isResized;


    public Window(int width, int height, String title) {
        this.width = width;
        this.height = height;
        this.title = title;
    }
    
    public void create() {
        if(!GLFW.glfwInit()) {
            System.err.println("ERROR: GLFW wasn't initialize");
            return;
        }
        
        GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MAJOR, 3);           
        GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MINOR, 3);
        GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_FORWARD_COMPAT, GL11.GL_TRUE);
        GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_PROFILE, GLFW.GLFW_OPENGL_CORE_PROFILE);
        
        input = new Input();
        window = GLFW.glfwCreateWindow(width, height, title, 0, 0);
        
        if(window == 0) {
            System.err.println("ERROR: window wasn't created");
            return;
        }
        
        GLFWVidMode videoMode = GLFW.glfwGetVideoMode(GLFW.glfwGetPrimaryMonitor());
        windowPosX[0] = (videoMode.width() - width) / 2;
        windowPosY[0] = 0;
        GLFW.glfwSetWindowPos(window, windowPosX[0], windowPosY[0]);
        GLFW.glfwMakeContextCurrent(window);
        GL.createCapabilities();
        GL11.glEnable(GL11.GL_DEPTH_TEST);
        
        createCallback();
        
        GLFW.glfwShowWindow(window);
        
        GLFW.glfwSwapInterval(1);
        time = System.currentTimeMillis();
    }
    
    private void createCallback() {
        sizeCallback = new GLFWWindowSizeCallback() {
            public void invoke(long window, int w, int h) {
                width = w;
                height = h;
                isResized = true;
            }
        };
        
        GLFW.glfwSetKeyCallback(window, input.getKeyboardCallBack());
        GLFW.glfwSetCursorPosCallback(window, input.getMouseMoveCallBack());
        GLFW.glfwSetMouseButtonCallback(window, input.getMouseButtonsCallBack());
        GLFW.glfwSetScrollCallback(window, input.getMouseScrollCallBack());
        GLFW.glfwSetWindowSizeCallback(window, sizeCallback);
    }
    
    public void update() {
        if(isResized){
            GL11.glViewport(0, 0, width, height);
            isResized = false;
        }
        GL11.glClearColor(background.getX(), background.getY(), background.getZ(), 1.0f);
        GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
        GLFW.glfwPollEvents();
        
        frames ++;
        if(System.currentTimeMillis() > time + 1000) {
            GLFW.glfwSetWindowTitle(window, title + "   | FPS:" + frames);
            time = System.currentTimeMillis();
            frames = 0;
        }
    }
    
    public void swapBuffers() {
        GLFW.glfwSwapBuffers(window);
    }
    
    public void setBackgroundColor(float r, float g, float b) {
        background.setX(r);
        background.setY(g);;
        background.setZ(b);;
    }

    public int getWidth() {
        return width;
    }

    public int getHeight() {
        return height;
    }

    public String getTitle() {
        return title;
    }

    public long getWindow() {
        return window;
    }

    public boolean shouldClose() {
        return GLFW.glfwWindowShouldClose(window);
    }
    
    public void destroy() {
        input.destroy();
        sizeCallback.free();
        GLFW.glfwWindowShouldClose(window);
        GLFW.glfwDestroyWindow(window);
        GLFW.glfwTerminate();
    }
}

我的着色器和渲染器class:

package engine.graphics;

import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL20;

import engine.utils.FileUtils;

public class Shader {
    private String vertexFile, fragmentFile;
    private int vertexID, fragmentID, programID;
    
    public Shader(String vertexPath, String fragmentPath) {
        vertexFile = FileUtils.loadAsString(vertexPath);
        fragmentFile = FileUtils.loadAsString(fragmentPath);
    }
    
    public void create() {
        programID = GL20.glCreateProgram();
        
        vertexID = GL20.glCreateShader(GL20.GL_VERTEX_SHADER);
        
        GL20.glShaderSource(vertexID, vertexFile);
        GL20.glCompileShader(vertexID);
        if(GL20.glGetShaderi(vertexID, GL20.GL_COMPILE_STATUS) == GL11.GL_FALSE) {
            System.err.println("Vertex Shader: " + GL20.glGetShaderInfoLog(vertexID));
            return;
        }
        
        fragmentID = GL20.glCreateShader(GL20.GL_FRAGMENT_SHADER);
        
        GL20.glShaderSource(fragmentID, fragmentFile);
        GL20.glCompileShader(fragmentID);
        if(GL20.glGetShaderi(fragmentID, GL20.GL_COMPILE_STATUS) == GL11.GL_FALSE) {
            System.err.println("Fragment Shader: " + GL20.glGetShaderInfoLog(fragmentID));
            return;
        }
        
        GL20.glAttachShader(programID, vertexID);
        GL20.glAttachShader(programID, fragmentID);
        
        GL20.glLinkProgram(programID);
        if(GL20.glGetProgrami(programID, GL20.GL_LINK_STATUS) == GL11.GL_FALSE) {
            System.err.println("Program Linking: " + GL20.glGetProgramInfoLog(programID));
            return;
        }
        GL20.glValidateProgram(programID);
        if(GL20.glGetProgrami(programID, GL20.GL_VALIDATE_STATUS) == GL11.GL_FALSE) {
            System.err.println("Program Validation: " + GL20.glGetProgramInfoLog(programID));
            return;
        }
        
        GL20.glDeleteShader(vertexID);
        GL20.glDeleteShader(fragmentID);
    }
    
    public void bind() {
        GL20.glUseProgram(programID);
    }
    
    public void unbind() {
        GL20.glUseProgram(0);
    }
    
    public void destroy() {
        GL20.glDeleteProgram(programID);
    }
}
package engine.graphics;

import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL30;

public class Renderer {
    private Shader shader;
    
    public Renderer(Shader shader) {
        this.shader = shader;
    }
    
    public void renderMesh(Mesh mesh) {
        GL30.glBindVertexArray(mesh.getVAO());
        GL30.glEnableVertexAttribArray(0);
        GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, mesh.getIBO());
        shader.bind();
        GL11.glDrawElements(GL11.GL_TRIANGLES, mesh.getIndices().length, GL11.GL_FLOAT, 0);
        shader.unbind();
        GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
        GL30.glDisableVertexAttribArray(0);
        GL30.glBindVertexArray(0);
    }
}

顶点、网格、输入、Vetor3f 和 FileUtils class:

package engine.graphics;

import engine.maths.Vector3f;

public class Vertex {
    private Vector3f position;
    
    public Vertex(Vector3f position) {
        this.position = position;
    }

    public Vector3f getPosition() {
        return position;
    }
}
package engine.graphics;

import java.nio.FloatBuffer;
import java.nio.IntBuffer;

import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import org.lwjgl.system.MemoryUtil;

public class Mesh {
    private Vertex[] vertices;
    private int[] indices;
    private int vao, pbo, ibo;
    
    public Mesh(Vertex[] vertices, int[] indices) {
        this.vertices = vertices;
        this.indices = indices;
    }
    
    public void create() {
        vao = GL30.glGenVertexArrays();
        GL30.glBindVertexArray(vao);
        
        FloatBuffer positionBuffer = MemoryUtil.memAllocFloat(vertices.length * 3);
        float[] positionData = new float[vertices.length * 3];
        for (int i = 0; i < vertices.length; i++) {
            positionData[i * 3] = vertices[i].getPosition().getX();
            positionData[i * 3 + 1] = vertices[i].getPosition().getY();
            positionData[i * 3 + 2] = vertices[i].getPosition().getZ();
        }
        positionBuffer.put(positionData).flip();
        
        pbo = GL15.glGenBuffers();
        GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, pbo);
        GL15.glBufferData(GL15.GL_ARRAY_BUFFER, positionBuffer, GL15.GL_STATIC_DRAW);
        GL20.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, ibo, 0);
        GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
        
        IntBuffer indicesBuffer = MemoryUtil.memAllocInt(indices.length);
        indicesBuffer.put(indices).flip();
        
        ibo = GL15.glGenBuffers();
        GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, ibo);
        GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, indicesBuffer, GL15.GL_STATIC_DRAW);
        GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
    }

    public Vertex[] getVertices() {
        return vertices;
    }

    public int[] getIndices() {
        return indices;
    }

    public int getVAO() {
        return vao;
    }

    public int getPBO() {
        return pbo;
    }

    public int getIBO() {
        return ibo;
    }
}
package engine.io;

import org.lwjgl.glfw.GLFW;
import org.lwjgl.glfw.GLFWCursorPosCallback;
import org.lwjgl.glfw.GLFWKeyCallback;
import org.lwjgl.glfw.GLFWMouseButtonCallback;
import org.lwjgl.glfw.GLFWScrollCallback;

public class Input {
    private static boolean[] keys = new boolean[GLFW.GLFW_KEY_LAST];
    private static boolean[] buttons = new boolean[GLFW.GLFW_MOUSE_BUTTON_LAST];
    private static double mouseX, mouseY;
    private static double scrollX, scrollY;

    
    private GLFWKeyCallback keyboard;
    private GLFWCursorPosCallback mouseMove;
    private GLFWMouseButtonCallback mouseButtons;
    private GLFWScrollCallback mouseScroll;

    public Input() {
        keyboard = new GLFWKeyCallback() {
            public void invoke(long window, int key, int scancode, int action, int mods) {
                if(key == -1) {
                    return;
                }
                keys[key] = (action != GLFW.GLFW_RELEASE);
            }
        };
        mouseMove = new GLFWCursorPosCallback() {
            public void invoke(long window, double xpos, double ypos) {
                mouseX = xpos;
                mouseY = ypos;
            }
        };
        mouseButtons = new GLFWMouseButtonCallback() {
            public void invoke(long window, int button, int action, int mods) {
                buttons[button] = (action != GLFW.GLFW_RELEASE);
            }
        };
        
        mouseScroll = new GLFWScrollCallback() {
            public void invoke(long window, double offsetx, double offsety) {
                scrollX += offsetx;
                scrollY += offsety;
            }
        };
    }
    
    public static boolean isKeyDown(int key) {
        return keys[key];
    }
    
    public static boolean isButtonDown(int button) {
        return buttons[button];
    }
    
    public void destroy() {
        keyboard.free();
        mouseMove.free();
        mouseButtons.free();
        mouseScroll.free();
    }

    public static double getMouseX() {
        return mouseX;
    }
    public static double getMouseY() {
        return mouseY;
    }
    
    public static double getScrollX() {
        return scrollX;
    }
    public static double getScrollY() {
        return scrollY;
    }

    public GLFWKeyCallback getKeyboardCallBack() {
        return keyboard;
    }

    public GLFWCursorPosCallback getMouseMoveCallBack() {
        return mouseMove;
    }

    public GLFWMouseButtonCallback getMouseButtonsCallBack() {
        return mouseButtons;
    }
    
    public GLFWScrollCallback getMouseScrollCallBack() {
        return mouseScroll;
    }
}
package engine.maths;

public class Vector3f {
    private float x, y, z;
    
    public Vector3f(float x, float y, float z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }
    
    public void set(float x, float y, float z) {
        this.x = x;
        this.y = y;
        this.z = z;
        
    }

    public float getX() {
        return x;
    }

    public void setX(float x) {
        this.x = x;
    }

    public float getY() {
        return y;
    }

    public void setY(float y) {
        this.y = y;
    }

    public float getZ() {
        return z;
    }

    public void setZ(float z) {
        this.z = z;
    }
}
package engine.utils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class FileUtils {
    public static String loadAsString(String path) {
        StringBuilder result = new StringBuilder();
        
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(FileUtils.class.getResourceAsStream(path)))) {
            String line = "";
            while ((line = reader.readLine()) != null) {
                result.append(line).append("\n");
            }
        } catch (IOException e) {
            System.err.println("Couldn't find the file at " + path);
        }
        
        return result.toString();
    }
}

以及 mainVertex.glsl 和 mainFragment.glsl 文件:

#version 330 core

layout(location = 0) in vec3 position;

out vec3 color;

void main() {
    gl_Position = vec4(position, 1.0);
    color = position;
}
#version 330 core

in vec3 color;

out vec4 outColor;

void main() {
    outColor = vec4(color, 1.0);
}

代码很多,不知道看懂了有什么用

在您调用的 Renderer.renderMesh(Mesh) 方法中

GL11.glDrawElements(GL11.GL_TRIANGLES, mesh.getIndices().length, GL11.GL_FLOAT, 0);

GL_FLOAT 是该方法的 type 参数的无效值,因为索引不能是浮点数,而你的不是。你使用整数。

因此,将 GL_FLOAT 更改为 GL_UNSIGNED_INT