Добавление ProjectionMatrix к сущности приводит к странному поведению при рендеринге - PullRequest
0 голосов
/ 17 октября 2018

Введение в проблему:

Я работаю над игровым движком с использованием библиотеки 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 .

...