java - 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”崩溃。有人可以帮我吗?如果问题令人困惑,请告诉我,以便我可以尝试更好地解释它。
解决方案
所以,在处理了大学和工作的事情之后,我终于回到了这段代码,我终于可以纠正我的错误了。感谢 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 术语的掌握不好。
推荐阅读
- javascript - 我想将html表单值传递给按钮上的href标签
- c# - 取消选择 Xamarin 表单列表视图中的选定行
- c++ - 在同一个互斥锁上的锁定和解锁顺序是否一致?
- c - 在 Mac/Windows 上编译 C 代码以在 Linux 上运行
- react-native - React Native 中的重新渲染问题
- vue.js - 渲染错误:“错误:在条件渲染中找不到模块'./432.jpg'”
- amazon-web-services - Delphi 10.4 Amazon aws S3 连接错误
- swift - iOS - Xcode 控制台中的约束警告
- javascript - 使用画布绘制未获得预期的画布输出
- xero-api - 如何将 Xero api 调用次数减少到每天 5000 次以下限制?