Что я делаю неправильно, когда пытаюсь переместить объект с помощью шейдеров в LWJGL? - PullRequest
0 голосов
/ 05 мая 2018

Я пытаюсь правильно перевести и спроецировать точки сетки на поверхность окна с помощью шейдеров (интерес представляет вершинный шейдер, который отвечает за вывод конечных позиций вершин).

Я более подробно остановлюсь на этом вопросе примерно через 6 часов, так как сейчас мне пора спать. Если у кого-то есть желание прочитать это (оно еще не написано правильно и написано так, как могло бы быть), я был бы признателен за это еще больше.

Модель (квад) представлена ​​следующими вершинами: [-0,5f, 0,5f, 0f] (V0), [-0,5f, -0,5f, 0f] (V1), [0,5f, -0,5 f, 0f] (V2), [0,5f, 0,5f, 0].

У меня есть следующая пара методов, чтобы создать матрицу перспективной проекции :

public static Matrix4f getProjectionMatrix(float fovy, int width, int height, float zNear, float zFar) {
    float aspectRatio = (float) width / height;
    projectionMatrix.perspective(fovy, aspectRatio, zNear, zFar);
    return projectionMatrix;
}

Этот метод находится в классе Преобразования создает и возвращает матрицу перспективной проекции.

  • fovy = Вертикальное поле зрения
  • ширина = ширина окна
  • высота = высота окна
  • zNear = Ближняя плоскость отсечения
  • zFar = Дальняя плоскость отсечения

    public Matrix4f perspective(float fovy, float aspectRatio, float zNear, float zFar) {
        float scale = (float) (Math.tan(fovy * 0.5) * zNear);
        float top = scale;
        float right = top * aspectRatio;
        float bottom = -top;
        float left = bottom * aspectRatio;
    
        this.m00 = 2*zNear / (right - left);
        this.m03 = (right + left) / (right - left);
        this.m11 = 2*zNear / (top - bottom);
        this.m12 = (top + bottom) / (top - bottom);
        this.m22 = -(zFar + zNear) / (zFar - zNear);
        this.m23 = -2*zFar*zNear / (zFar - zNear);
        this.m32 = -1;
    
        return this;
    

    } Этот метод относится к классу Matrix4f .

Вот начало класса Matrix4f с переменными экземпляра Matrix4f и конструктором:

public class Matrix4f {
    float m00, m01, m02, m03;
    float m10, m11, m12, m13;
    float m20, m21, m22, m23;
    float m30, m31, m32, m33;

public Matrix4f() {
    m00 = 1.0f; m01 = 0.0f; m02 = 0.0f; m03 = 0.0f;
    m10 = 0.0f; m11 = 1.0f; m12 = 0.0f; m13 = 0.0f;
    m20 = 0.0f; m21 = 0.0f; m22 = 1.0f; m23 = 0.0f;
    m30 = 0.0f; m31 = 0.0f; m32 = 0.0f; m33 = 1.0f;
}

После вызова конструктора тождественная матрица уже создана. Осталось только знать, как инициализировать матрицу перспективной проекции. Это делается при вызове конструктора класса Преобразования :

public class Transformations {
    private Matrix4f translationMatrix;
    private static Matrix4f projectionMatrix;

    public Transformations() {
        translationMatrix = new Matrix4f();
        projectionMatrix = new Matrix4f();
    }

Теперь для матрицы перевода . Функциональность его создания предоставляется в классе Transformations с помощью метода getTranslationMatrix(float x, float y, float z), который создает и возвращает матрицу перевода, используя параметры:

public Matrix4f getTranslationMatrix(float x, float y, float z) {
    translationMatrix.m03 = x;
    translationMatrix.m13 = y;
    translationMatrix.m23 = z;
    return translationMatrix;
}

Чтобы действительно иметь что-то, представляющее игровую модель, я создал класс с именем GameEntity . Представляет сетку и положение модели:

public class GameEntity {

    private final Mesh mesh;
    private Vector3f position;

    public GameEntity(Mesh mesh) {
        this.mesh = mesh;
        position = new Vector3f(0, 0, 0);
    }

    public Vector3f getPosition() {
        return position;
    }    

    public void updatePosition(float x, float y, float z) {
        position.x += x;
        position.y += y;
        position.z += z;
    }

    public Mesh getMesh() {
        return mesh;
    }
}

Метод updatePosition должен перемещать объект вокруг окна в целом. Я не буду включать здесь объяснение кода для класса Mesh . Все, что вам нужно знать, - это то, что он содержит данные сетки (вершины) о GameEntity , например, позиции вершин, цвета вершин, индексы и т. Д., Которые все хранятся в массиве вершин. объект, который в свою очередь используется для рендеринга этого экземпляра Mesh в окно. В моем случае два треугольника, которые образуют четырехугольник, представлены как экземпляр GameEntity .

Перемещение модели : Метод updatePosition вызывается при каждом нажатии любой из клавиш W, A, S, D, пробела или сдвига влево. Он обновляет положение экземпляра GameEntity (который хранится в массиве GameEntity[] entities с индексом 0) на определенную величину:

    private void processInput() {
        glfwPollEvents();

        if (window.keys[GLFW_KEY_W]) {
            entities[0].updatePosition(0, 0, -1.0f);
        } else if (window.keys[GLFW_KEY_S]){
            entities[0].updatePosition(0, 0, 1.0f);
        } else if (window.keys[GLFW_KEY_A]) {
            entities[0].updatePosition(1.0f, 0, 0);
        } else if (window.keys[GLFW_KEY_D]) {
            entities[0].updatePosition(-1.0f, 0, 0);
        } else if (window.keys[GLFW_KEY_SPACE]) {
            entities[0].updatePosition(0, 1.0f, 0);
        } else if (window.keys[GLFW_KEY_LEFT_SHIFT]) {
            entities[0].updatePosition(0, -1.0f, 0);
        } 
     }

Этот метод вызывается внутри основного игрового цикла.

Затем внутри класса Renderer матрица перевода для модели строится на основе ее положения, а матрица проекции - на основе свойств объекта окна:

private ShaderProgram shaderProgram;
private Window window = new Window();

private final Transformations transformation;

private Matrix4f translationMatrix = new Matrix4f();
private Matrix4f projectionMatrix = new Matrix4f();


private static double angleOfView = 60.0;
private static final float FOVY = (float) Math.toRadians(angleOfView);
private static final float zNear = 0.01f;
private static final float zFar = 1000.0f;

shaderProgram.createUniform("translationMatrix");
shaderProgram.createUniform("projectionMatrix");

public void render(Window window, GameEntity[] entities) {
    i++;

    clear();

    if (window.isResized()) {
        glViewport(0, 0, window.getWidth(), window.getHeight());
        window.setResized(false);
    }

    //make the shaders active
    shaderProgram.bind();

    //update the projection matrix
    Matrix4f projectionMatrix = transformation.getProjectionMatrix(FOVY, window.getWidth(), window.getHeight(), zNear, zFar);

    shaderProgram.setUniformMatrix("projectionMatrix", projectionMatrix);

    //render each game item
    for(GameEntity entity : entities) {
        Matrix4f translationMat = transformation.getTranslationMatrix(entity.getPosition());
        shaderProgram.setUniformMatrix("translationMatrix", translationMat);
        entity.getMesh().render();
    }

    shaderProgram.unbind();
}

Во-первых, все унифицированные местоположения определены (выше метода render()).

Метод clear() очищает буфер рендеринга - он подготавливает его для рендеринга нового изображения. В следующем предложении if обрабатываются операции изменения размера окна. Если размер окна изменяется, предложение if с соответствующими методами обновит width и height окна в соответствии с измененным размером окна.

projectionMatrix состоит из переменных, которые определены как переменные экземпляра класса Renderer (FOVY, zNear, zFar) и двух переменных, которые получают текущую ширину и высоту объекта окна ( window.getWidth(), window.getHeight()).

Затем матрица проекции «отправляется» в вершинный шейдер путем вызова shaderProgram.setUniformMatrix("projectionMatrix", projectionMatrix);:

private final Map<String, Integer> uniforms;

public void createUniform(String uniformName) throws Exception {
    int uniformLocation = glGetUniformLocation(programID, uniformName);

    if (uniformLocation < 0) {
        throw new Exception("[ShaderProgram.createUniform]: Couldn't find uniform: " + uniformName);
    }
    uniforms.put(uniformName, uniformLocation);
}

Этот метод находится в классе ShaderProgram , который содержит ссылки на активную шейдерную программу и связанные с ней унифицированные переменные, которые хранятся в uniforms Hashmap ..

Затем в цикле for отображаются два четырехугольника. Сначала строится матрица перевода на основе значения позиции GameInstance , которое представляется в виде вектора из трех кортежей (x, y, z). Затем эта созданная матрица «отправляется» в вершинный шейдер.

Теперь, отправив и перспективную проекцию (projectionMatrix), и матрицу перевода (translationMatri x) в вершинный шейдер, пришло время вызвать метод рендеринга в нашей Mesh экземпляр для его рендеринга. Код для метода render() (в контексте entity.getMesh().render()):

public void render() {
    glBindVertexArray(getVaoID());
    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, getIndicesVboID());

    glDrawElements(GL_TRIANGLES, getVertexCount(), GL_UNSIGNED_INT, 0);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    glDisableVertexAttribArray(0);
    glBindVertexArray(0);
}

Однако результаты не являются удовлетворительными. Когда нажимаются W или S, квад (два треугольника) уходит дальше или ближе к «нам», что правильно. Однако при нажатии A или D (что должно перевести модель на ось x), квад (два треугольника) не переводится. Он просто вращается вокруг своего центра . Как решить проблему? Спасибо за помощь и за время, потраченное на чтение этого длинного вопроса.

1 Ответ

0 голосов
/ 05 мая 2018

Кажется, что-то не так с номенклатурой строк / столбцов.

Как я понимаю, ваша ячейка кода mij в матрице - это col = i, row = j

OpenGL ожидает массив из 16 значений, представляющий собой перевод 13,14,15 позиций. Обычно это называется «основной порядок столбцов», перевод представлен в 4-м столбце.

Так попробуйте это:

public Matrix4f getTranslationMatrix(float x, float y, float z) {
    translationMatrix.m30 = x;
    translationMatrix.m31 = y;
    translationMatrix.m32 = z;
    return translationMatrix;
}

Кроме того, просмотрите ваш perspective. Я думаю, что у вас есть некоторые ошибки в индексах ij. ii клетки выглядят хорошо.

...