Введение в проблему:
Я работаю над игровым движком с использованием библиотеки LWJGL, следуя этому руководству .Однако я пытаюсь сделать так, чтобы между основным движком и самой игрой было реальное разделение.Поэтому я сильно усложнил проект, и я думаю, что это вызывает некоторые проблемы, так как ProjectionMatrix не работает, как описано в видео.
Что я делаю:
Создание ProjectionMatrix:
Чтобы создать ProjectionMatrix, я создал метод, который создает его для меня:
public static Matrix4f createProjectionMatrix(float aspectRatio, float fov, float nearPlane, float farPlane) {
float y_scale = (float) ((1f / Math.tan(Math.toRadians(fov / 2f))) * aspectRatio);
float x_scale = y_scale / aspectRatio;
float frustum_length = nearPlane - farPlane;
Matrix4f projectionMatrix = new Matrix4f();
projectionMatrix.m00 = x_scale;
projectionMatrix.m11 = y_scale;
projectionMatrix.m22 = -((farPlane + nearPlane) / frustum_length);
projectionMatrix.m23 = -1;
projectionMatrix.m32 = -((2 * nearPlane * farPlane) / frustum_length);
projectionMatrix.m33 = 0;
return projectionMatrix;
}
Я создаю ProjectionMatrix со следующими значениями:
aspectRatio = width/height = 640/480 = 1.33333
fov = 100
nearPlane = -0.5
farPlane = 100
Это приводит к следующим значениям для моего ProjectionMatrix:
0.83909965 0.0 0.0 0.0
0.0 0.83909965 0.0 0.0
0.0 0.0 0.9990005 -0.9995003
0.0 0.0 -1.0 0.0
Использование ProjectionMatrix:
Для использования ProjectionMatrix Iсоздали следующие шейдеры:
vertex.vs:
#version 150
in vec3 position;
in vec2 textureCoordinates;
out vec2 passTextureCoordinates;
uniform mat4 transformationMatrix;
uniform mat4 projectionMatrix;
uniform int useProjectionMatrix;
void main(void){
if (useProjectionMatrix == 1) {
gl_Position = projectionMatrix * transformationMatrix * vec4(position,1.0);
} else {
gl_Position = transformationMatrix * vec4(position,1.0);
}
passTextureCoordinates = textureCoordinates;
}
фрагмент.fs:
#version 150
in vec2 passTextureCoordinates;
out vec4 out_Color;
uniform sampler2D textureSampler;
void main(void){
out_Color = texture(textureSampler,passTextureCoordinates);
}
Наконецдля рендеринга сущности я создал следующий класс рендерера:
public class TexturedEntityRenderer extends AbstractEntityRenderer{
private float aspectRatio;
private float fov;
private float nearPlane;
private float farPlane;
public void prepare() {
GL11.glClearColor(0,0,0,1);
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
}
public void render (AbstractEntity entity, AbstractShader shader) {
if(shader instanceof TexturedEntityShader) {
if(entity.getModel() instanceof TexturedModel) {
TexturedModel model = (TexturedModel)entity.getModel();
GL30.glBindVertexArray(model.getVaoID());
GL20.glEnableVertexAttribArray(0);
GL20.glEnableVertexAttribArray(1);
Matrix4f transformationMatrix = MatrixMaths.createTransformationMatrix(entity.getPosition(), entity.getRx(), entity.getRy(), entity.getRz(), entity.getScale());
((TexturedEntityShader)shader).loadTransformationMatrix(transformationMatrix);
GL13.glActiveTexture(GL13.GL_TEXTURE0);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, ((TexturedModel)entity.getModel()).getTexture().getTextureID());
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, model.getVaoID());
GL11.glDrawElements(GL11.GL_TRIANGLES, model.getVertexCount(), GL11.GL_UNSIGNED_INT, 0);
GL20.glDisableVertexAttribArray(0);
GL20.glDisableVertexAttribArray(1);
GL30.glBindVertexArray(0);
} else {
ExceptionThrower.throwException(new ModelInvalidException());
}
} else {
ExceptionThrower.throwException(new ShaderIncompatableException(shader.toString()));
}
}
public void setup(AbstractShader shader) {
nearPlane = Float.parseFloat(OptionHandler.getProperty(GraphicOptions.WINDOWNEARPLANE_KEY, OptionHandler.GRAPHIC_OPTION_ID));
farPlane = Float.parseFloat(OptionHandler.getProperty(GraphicOptions.WINDOWFARPLANE_KEY, OptionHandler.GRAPHIC_OPTION_ID));
aspectRatio = DisplayManager.getWidth() / DisplayManager.getHeight();
fov = Float.parseFloat(OptionHandler.getProperty(GraphicOptions.WINDOWFOV_KEY, OptionHandler.GRAPHIC_OPTION_ID));
((TexturedEntityShader)shader).loadProjectionMatrix(MatrixMaths.createProjectionMatrix(aspectRatio, fov, nearPlane, farPlane));
((TexturedEntityShader)shader).loadUseProjectionMatrix();
}
}
Функция Optionhandler.getProperty()
в setup()
возвращает свойство для данного ключа (например, значение fov или nearPlane) изтекстовый файл.(Я проверил, что это работает, печатая все загруженные опции.) Кроме того, функции DisplayManager.getWidth()
и DisplayManager.getHeight()
, очевидно, получают ширину и высоту для вычисления переменной aspectRatio.
Обновлениесущность:
И последнее, но не менее важное: я обновляю сущность с помощью класса EntityModifier, который выглядит следующим образом:
public class EntityModifier {
private Vector3f dposition;
private float drx;
private float dry;
private float drz;
private float dscale;
public BasicEntityModifier(Vector3f dposition, float drx, float dry, float drz, float dscale) {
this.dposition = dposition;
this.drx = drx;
this.dry = dry;
this.drz = drx;
this.dscale = dscale;
}
public Vector3f getDposition() {
return dposition;
}
public float getDrx() {
return drx;
}
public float getDry() {
return dry;
}
public float getDrz() {
return drz;
}
public float getDscale() {
return dscale;
}
@Override
public String toString() {
return "BasicEntityModifier [dposition=" + dposition + ", drx=" + drx + ", dry=" + dry + ", drz=" + drz + ", dscale=" + dscale + "]";
}
}
Каждая создаваемая мной сущность имеет одну из этихклассы и я вызываем метод обновления, который добавляет значения к преобразованию сущности:
public void update() {
increasePosition(modifier.getDposition().getX(),modifier.getDposition().getY(),modifier.getDposition().getZ());
increaseRotation(modifier.getDrx(), modifier.getDry(), modifier.getDrz());
increaseScale(modifier.getDscale());
}
private void increasePosition(float dx, float dy, float dz) {
position.x += dx;
position.y += dy;
position.z += dz;
}
private void increaseRotation(float drx, float dry, float drz) {
rx += drx;
ry += dry;
rz += drz;
}
private void increaseScale(float dscale) {
scale += dscale;
}
Проблема:
Я могу изменить положение x
и y
значений объекта обычно, но всякий раз, когда я меняю положение z
, используя EntityModifier, объект загружается, но затем исчезает с экрана.Он загружается примерно на 60 кадров, прежде чем исчезнуть и изменить значение dz
, похоже, никак не влияет на скорость, с которой он исчезает (см. РЕДАКТИРОВАТЬ 2).Также у сущности нет эффекта масштаба, как показано в учебнике здесь (та же ссылка, но с отметкой времени).
Изменение значения dz
на 0 останавливает исчезновение объекта.
Что здесь происходит?Как я могу это исправить?
РЕДАКТИРОВАТЬ:
В комментариях было указано, что значение nearPlane должно быть положительным, поэтому я изменил его на 0,5, но все равно получаю те же результаты.Я также изменил: float frustum_length = nearPlane - farPlane;
на float frustum_length = farPlane - nearPlane;
, что также было предложено там (это также не решило проблему).
РЕДАКТИРОВАТЬ 2:
После еще одного исследования я нашел несколько интересныхвещи:
1. Изменение скорости, с которой изменяется значение z, влияет на время, необходимое для исчезновения объекта.Узнав об этом, я попытался рассчитать несколько разных значений dz (при этом dz - это изменение на кадр z), и я получил это:
`for dz = -0.002 -> frames before dissapear: 515 frames.`
`for dz = -0.001 -> frames before dissapear: 1024 frames.`
`for dz = 0.02 -> frames before dissapear: 63 frames.`
Если мы примем во внимание время реакции (я сделал вывод программыобщее количество визуализированных кадров при закрытии и просто закрытие его как можно быстрее, когда сущность исчезла) мы можем вычислить значения для z, при которых сущность исчезает.
-0.002 * 515 ≈ -1
-0.001 * 1024 ≈ -1
0.02 * 63 ≈ 1
Это, вероятно, связано с тем, как система координат работает в OpenGL, но все еще не объясняет, почему объект не становится меньше, как в учебнике, упомянутом выше.
2. Удаление кода, который добавляет ProjectionMatrix к классу рендерера, не меняет поведение.Это означает, что ошибка в другом месте.
Новая проблема:
Я думаю, что нет проблемы с ProjectionMatrix (или, по крайней мере, не является проблемой, вызывающей такое поведение), но проблема в том, что позиция сущности превышает 1 или -1 на осях z.Однако это все еще не объясняет, почему не существует «эффекта масштабирования».Поэтому я не думаю, что ограничение перемещения z между -1 и 1 решит эту проблему, на самом деле, я думаю, что это сработает против нас, так как сущность не должна быть визуализирована в любом случае, если она полностью «уменьшена» или уменьшена.
Что может вызвать эту проблему, если это не ProjectionManager?
РЕДАКТИРОВАТЬ 3:
Кто-то на Reddit отметил, что следующие классы также могут быть интересны для решения проблемы:
AbstractShader : содержит базовые функциональные возможности шейдеров, общие для всех классов шейдеров.
TexturedEntityShader : используется для визуализации текстурированного объекта (показано выше)
DisplayManager : класс, который обрабатывает рендеринг.
EDIT 4:
После дальнейшего обсуждения reddit об этой проблеме мы столкнулись с проблемой иудалось исправить: значение для useProjectionMatrix
не было загружено, потому что шейдер был остановлен, когда я пытался загрузить его.Изменение метода loadUseProjectionMatrix()
на:
public void loadUseProjectionMatrix() {
super.start();
super.loadBoolean(useProjectionMatrixLocation, useProjectionMatrix);
System.out.println("loaded useProjectionMatrix: " + useProjectionMatrix + "\n\n");
super.stop();
}
, кажется, частично решает проблему, поскольку projectionMatrix теперь можно использовать внутри шейдера (до того, как он не будет использоваться из-за того, что значение useProjectionMatrix
всегда будет 0
, поскольку мы не загружаем значение для него.).
Однако это не решило проблему в целом, так как я думаю, что проблема с projectionMatrix все еще существует.Сущность вообще не хочет отображаться при использовании projectionMatrix, но прекрасно отрисовывается, когда ее не использует.Я попытался жестко кодировать значения projectionMatrix с помощью следующего шейдера:
#version 150
in vec3 position;
in vec2 textureCoordinates;
out vec2 passTextureCoordinates;
uniform mat4 transformationMatrix;
uniform mat4 projectionMatrix;
uniform int useProjectionMatrix;
mat4 testMat;
void main(void){
testMat[0] = vec4(0.83909965, 0, 0, 0);
testMat[1] = vec4(0, 0.83909965, 0, 0);
testMat[2] = vec4(0, 0, 0.9990005, -0.9995003);
testMat[3] = vec4(0, 0, -1, 0);
if (true) {
gl_Position = testMat * transformationMatrix * vec4(position,1.0);
} else {
gl_Position = transformationMatrix * vec4(position,1.0);
}
passTextureCoordinates = textureCoordinates;
}
Однако это не похоже на работу.Эти значения в порядке?
Fow, кто хочет увидеть это здесь, 2 сообщения, которые я создал на reddit об этой проблеме: post 1 , post 2 .