首页 > 解决方案 > Java/LWJGL 3 优化和渲染错误

问题描述

我正在关注关于 OPENGL/LWJGL 3 3D 游戏教程系列的 ThinMatrix 教程(此处),尽管我的代码写得与他的有点不同,但进展顺利。

我可以使用基本的 LWJGL 3 渲染/着色方法来渲染如下图所示的对象。基本上,在Render类中使用这个方法:

 //Old method
    
    public void createModel(EntityLoader entity, StaticShader shader) {
        
        ModelLoader model = entity.getModel();
        MeshLoader mesh = model.createModel();
        glBindVertexArray(mesh.getVaoId());
        glEnableVertexAttribArray(0);
        glEnableVertexAttribArray(1);
        glEnableVertexAttribArray(2);
        shader.setUniformFloat("shineDamper", model.getShineDamper());
        shader.setUniformFloat("reflectivity", model.getReflectivity());
        
        Matrix4f transformationMatrix = Maths.createTransfMatrix(entity.getPosition(), entity.getRotX(), entity.getRotY(), 
                entity.getRotZ(), entity.getScale());
        shader.setUniformMatrix("transformationMatrix", transformationMatrix);
        
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, model.getTexture());
        glUniform1i(glGetUniformLocation(shader.getID(), "texture"), 0); 

        glDrawElements(GL_TRIANGLES, mesh.getVertexCount(), GL_UNSIGNED_INT, 0);
        
        mesh.cleanUp();
    }

并像这样在类中调用它:

public static void main(String[] args){
        
        Render render = new Render();
        window = render.init();
        glfwMakeContextCurrent(window);
        glfwSwapInterval(1);
        
        glfwShowWindow(window);
        
        createCapabilities(); 
        Light light = new Light(new Vector3f(0,0,20), new Vector3f(1,1,1));
        
        ObjLoader obj = new ObjLoader();
        ModelLoader squareModel = obj.loadObj("game_engine/src/Objetos/Ico.obj");
        EntityLoader squareEntity = new EntityLoader(squareModel, new Vector3f(0,0,-5),0,0,0,1);
        
        squareModel.setShineDamper(10);
        squareModel.setReflectivity(1);
        squareModel.loadTexture("game_engine/src/Texturas/awesomeface.png");
        
        Camera camera = new Camera();
        
        StaticShader shader = new StaticShader();
        shader.start();
        shader.use();
        
        while(!glfwWindowShouldClose(window))
        {
            render.prepare();
            processInput(window);
            
            glfwSetFramebufferSizeCallback(window, resizeWindow);
            GLFW.glfwSetScrollCallback(window, new GLFWScrollCallback() {
                @Override public void invoke (long win, double dx, double dy) {
                    XScroll = (float) dx;
                    YScroll = (float) dy;
                }
            });
            
            camera.move(window, YScroll);
            YScroll = 0;
            
            shader.setUniformVector("lightPosition", light.getPosition());
            shader.setUniformVector("lightColour", light.getColour());
        
            Matrix4f viewMatrix = Maths.createViewMatrix(camera);
            shader.setUniformMatrix("viewMatrix", viewMatrix);
        
            shader.setUniformMatrix("projectionMatrix", render.getProjectionMatrix());
            
            render.createModel(squareEntity, shader);
            
            glfwSwapBuffers(window);
            glfwPollEvents();    
        }
        shader.cleanup();
        glfwTerminate();

    }

在此处输入图像描述

直到第 13 个视频 Optimizing,他教了如何以更优化的方式执行相同的过程,使用 HashMaps 和 ArrayLists 来存储实体,并且每个实体列表只调用一次 VAO 元素。所以,我做了这个MasterRender类:

public class Master {
    
    private StaticShader shader = new StaticShader();
    private Render renderer = new Render(shader);
    
    private Map<ModelLoader, List<EntityLoader>> entities = new HashMap<ModelLoader, List<EntityLoader>>();
    
    public void render(Light sun, Camera camera){
        renderer.prepare();
        shader.start();
        shader.use();
        
        shader.setUniformVector("lightPosition", sun.getPosition());
        shader.setUniformVector("lightColour", sun.getColour());
        
        Matrix4f viewMatrix = Maths.createViewMatrix(camera);
        shader.setUniformMatrix("viewMatrix", viewMatrix);
        
        shader.setUniformMatrix("projectionMatrix", renderer.getProjectionMatrix());
    }
    
    public void processEntity(EntityLoader entity) {
        ModelLoader entityModel = entity.getModel();
        List<EntityLoader> batch = entities.get(entityModel);
        if(batch!=null) {
            batch.add(entity);
        }else {
            List<EntityLoader> newBatch = new ArrayList<EntityLoader>();
            newBatch.add(entity);
            entities.put(entityModel, newBatch);
        }
    }
    
    public void renderEntity() {
        
        renderer.render(entities);
        
        shader.stop();
        entities.clear();
    }
    
    public long loadWindow() {
        return renderer.init();
    }
    
    public void cleanup() {
        shader.cleanup();
    }

}

并在 Render 类中使用了新方法:

//Optimized methods
    
    public void render(Map<ModelLoader, List<EntityLoader>> entities) {
        for(ModelLoader model:entities.keySet()) {
            prepareModel(model);
            List<EntityLoader> batch = entities.get(model);
            for(EntityLoader entity : batch) {
                prepareEntity(entity);
                glDrawElements(GL_TRIANGLES, model.createModel().getVertexCount(), GL_UNSIGNED_INT, 0);
            }
            unbindModel();
        }
        
    }
    
    private void prepareModel(ModelLoader model) {
        MeshLoader mesh = model.createModel();
        glBindVertexArray(mesh.getVaoId());
        glEnableVertexAttribArray(0);
        glEnableVertexAttribArray(1);
        glEnableVertexAttribArray(2);
        shader.setUniformFloat("shineDamper", model.getShineDamper());
        shader.setUniformFloat("reflectivity", model.getReflectivity());
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, model.getTexture());
        glUniform1i(glGetUniformLocation(shader.getID(), "texture"), 0); 
        
    }
    
    private void unbindModel() {
        glDisableVertexAttribArray(0);
        glDisableVertexAttribArray(1);
        glDisableVertexAttribArray(2);
        glBindVertexArray(0);

        glBindTexture(GL_TEXTURE_2D, 0);

    }
    
    private void prepareEntity(EntityLoader entity) {
        Matrix4f transformationMatrix = Maths.createTransfMatrix(entity.getPosition(), entity.getRotX(), entity.getRotY(), 
                entity.getRotZ(), entity.getScale());
        shader.setUniformMatrix("transformationMatrix", transformationMatrix);
        
    }

最后,我调整了类,就像他在视频中所做的那样

public static void main(String[] args){
        
        Master render = new Master();
        window = render.loadWindow();
...
List<EntityLoader> allIcos = new ArrayList<EntityLoader>();
        allIcos.add(new EntityLoader(squareModel, new Vector3f(0,0,-5),0,0,0,1));
        
        while(!glfwWindowShouldClose(window))
        {
            processInput(window);
            
            glfwSetFramebufferSizeCallback(window, resizeWindow);
            GLFW.glfwSetScrollCallback(window, new GLFWScrollCallback() {
                @Override public void invoke (long win, double dx, double dy) {
                    XScroll = (float) dx;
                    YScroll = (float) dy;
                }
            });
            
            camera.move(window, YScroll);
            YScroll = 0;

            render.render(light, camera);
            for(EntityLoader Ico : allIcos) {
                render.processEntity(Ico);
                Ico.changeRot(0, 3, 0);
            }
            render.renderEntity();
            
            glfwSwapBuffers(window);
            glfwPollEvents();    
        }

它不仅没有工作,而且还买了一个日志错误:

#
# A fatal error has been detected by the Java Runtime Environment:
#
#  EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x00007ff8b8cd07b0, pid=11800, tid=7260
#
# JRE version: OpenJDK Runtime Environment (16.0.2+7) (build 16.0.2+7-67)
# Java VM: OpenJDK 64-Bit Server VM (16.0.2+7-67, mixed mode, tiered, compressed oops, compressed class ptrs, g1 gc, windows-amd64)
# Problematic frame:
# C  [nvoglv64.dll+0x8507b0]
#
# No core dump will be written. Minidumps are not enabled by default on client versions of Windows
#
# If you would like to submit a bug report, please visit:
#   https://bugreport.java.com/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#

---------------  S U M M A R Y ------------

Command Line: -Dfile.encoding=Cp1252 -XX:+ShowCodeDetailsInExceptionMessages br.com.renanlima.git.main

---------------  T H R E A D  ---------------

Current thread (0x000001ae5e0ce780):  JavaThread "main" [_thread_in_native, id=7260, stack(0x0000007d29000000,0x0000007d29100000)]

Stack: [0x0000007d29000000,0x0000007d29100000],  sp=0x0000007d290ff350,  free space=1020k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
C  [nvoglv64.dll+0x8507b0]

Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
j  org.lwjgl.opengl.GL11C.nglDrawElements(IIIJ)V+0
j  org.lwjgl.opengl.GL11C.glDrawElements(IIIJ)V+4
j  org.lwjgl.opengl.GL11.glDrawElements(IIIJ)V+4
j  br.com.renanlima.render.Render.render(Ljava/util/Map;)V+84
j  br.com.renanlima.render.Master.renderEntity()V+8
j  br.com.renanlima.git.main.main([Ljava/lang/String;)V+254
v  ~StubRoutines::call_stub

siginfo: EXCEPTION_ACCESS_VIOLATION (0xc0000005), reading address 0x0000000000000000
...

我的问题是:我在这种新的渲染方法中做错了什么?除了 ThinMatrix 的教程之外,我还查找了其他教程,他们看到都以类似的方式进行操作。

编辑 1:所以,使用 Eclipse 的调试器我发现了 2 件事: 1 - 出于某种原因,“transformationMatrix”加载不同,即使这两种方法相同并且它们获得的参数相同。

是使用“createModel()”方法的矩阵变量,这是一个有效的方法

是使用优化方法的矩阵变量,这是行不通的

这似乎是发生这种情况的唯一矩阵/顶点,但我可能会忽略一些东西

2 - 我想我已经确定了崩溃发生的具体部分。运行“glDrawElements”方法时。

在此处输入图像描述

更具体地说,在方法的这一部分:

在此处输入图像描述

但是在调用该方法之前,似乎没有一个变量是错误的。同样,我可能忽略了一些东西,但是在“findNative()”部分有很多变量,所以我会更好地研究它。

编辑 1.2:我解决了 transformationMatrix 错误。这是因为“changeRot(0, 3, 0);” 被提前打电话。但我仍然不知道为什么会发生“glDrawElements”崩溃。有人可以帮我吗?如果问题令人困惑,请告诉我,以便我可以尝试更好地解释它。

标签: javaopengloptimizationrenderlwjgl

解决方案


所以,在处理了大学和工作的事情之后,我终于回到了这段代码,我终于可以纠正我的错误了。感谢 Nick Clark 对与 VAO 相关的缓冲区的评论。

问题似乎是因为这行代码

 glDrawElements(GL_TRIANGLES, model.createModel().getVertexCount(), GL_UNSIGNED_INT, 0);

特别是在“model.createModel()”部分。它创建了另一个干净的网格,没有与它的 VAO 关联的缓冲区。旧方法有效,因为我直接从网格对象调用“顶点计数”(实际上是索引计数,我将其命名错误),如下所示:

MeshLoader mesh = model.createModel();
...
glDrawElements(GL_TRIANGLES, mesh.getVertexCount(), GL_UNSIGNED_INT, 0);

因此,为了修复错误,我将这两种方法合并为一个新方法:

//Optimized method

public void render(Map<ModelLoader, List<EntityLoader>> entities) {
    for(ModelLoader model:entities.keySet()) {
        MeshLoader mesh = model.createModel();
        glBindVertexArray(mesh.getVaoId());
        glEnableVertexAttribArray(0);
        glEnableVertexAttribArray(1);
        glEnableVertexAttribArray(2);
        shader.setUniformFloat("shineDamper", model.getShineDamper());
        shader.setUniformFloat("reflectivity", model.getReflectivity());
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, model.getTexture());
        glUniform1i(glGetUniformLocation(shader.getID(), "texture"), 0); 
        List<EntityLoader> batch = entities.get(model);
        for(EntityLoader entity : batch) {
            prepareEntity(entity);
            glDrawElements(GL_TRIANGLES, mesh.getVertexCount(), GL_UNSIGNED_INT, 0);
        }
        mesh.cleanUp();
    }
    
}

现在我可以以理论上优化的方式渲染对象

//main class
List<EntityLoader> allIcos = new ArrayList<EntityLoader>();
    allIcos.add(new EntityLoader(squareModel, new Vector3f(0,0,-5),0,0,0,1));
    allIcos.add(new EntityLoader(squareModel, new Vector3f(0,5,-5),0,0,0,1));
    allIcos.add(new EntityLoader(squareModel, new Vector3f(0,-5,-5),0,0,0,1));
...
render.render(light, camera);
        for(EntityLoader Ico : allIcos) {
            render.processEntity(Ico);
        }
        render.renderEntity();

在此处输入图像描述

我将尝试优化和清理这段代码,但现在这就是它的工作方式。请随时纠正此答案中的任何命名/术语错误,这主要是因为我对英语和 Java 术语的掌握不好。


推荐阅读