当前位置: 首页 > 知识库问答 >
问题:

当我尝试在LWJGL中使用着色器移动对象时,我做错了什么?

孟昆
2023-03-14

我正在尝试使用着色器(感兴趣的点是负责输出最终顶点位置的顶点着色器)正确地平移网格点并将其投影到窗口表面。

模型(四边形)由以下顶点表示:[-0.5f,0.5f,0f](V0),[-0.5f,-0.5f,0f](V1),[0.5f,-0.5f,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;
}

这个方法可以在Transformations类中找到,它创建并返回透视投影矩阵。

>

  • 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实例。

    移动模型:每次按下W、A、S、D、space或left shift键时,都会调用updatePosition方法。它会将GameEntity实例(存储在索引0处的GameEntity[]entities数组中)的位置更新一个设置的数量:

        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();
    }
    

    首先,定义所有的统一位置(在渲染()方法上面)。

    clear()方法清除渲染缓冲区——它为渲染新图像做好准备。在下面的if子句中,处理窗口大小调整操作。如果调整了窗口的大小,则带有相应方法的If子句将更新窗口的宽度高度,以匹配调整大小的窗口。

    projectionMatrix由定义为渲染器类实例变量的变量(FOVYzNearzFar)和两个获取窗口对象当前宽度和高度的变量(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类中,该类保存对活动着色器porgram的引用以及与之关联的统一变量,这些变量存储在Hashmap中。

    然后,在for循环中,渲染两个四边形。首先,基于GameInstance的位置值构建一个转换矩阵,该位置值表示为三元组向量(x,y,z)。然后,创建的矩阵被“发送”到顶点着色器。

    现在,将透视投影(投影矩阵)和平移矩阵(平移矩阵x)发送到顶点着色器后,是时候调用我们的Mesh实例上的渲染方法来渲染它了。渲染()方法的代码(在entity.getMesh()的上下文中)。渲染())是:

    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个答案

    陈宜修
    2023-03-14
    匿名用户

    似乎行/列命名法有问题。

    据我所知,矩阵中你的代码单元格mij是coll=i,row=j

    OpenGL需要16个值的数组,即13、14、15个位置的平移。通常这被称为“列主顺序”,翻译在第四列。

    所以试试这个:

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

    另外,回顾一下你的视角。我想你在ij索引中有一些错误<代码>ii单元格看起来不错。

     类似资料:
    • 我正在尝试使用ajax php和jquery上传图像,我在这里做错了什么。应在不刷新页面的情况下上载图像。我试过上面的代码。如果你看到任何问题,请帮忙!! 问题是我想将上传的文件移动到上传文件夹并将图像名称作为BLOB插入数据库。

    • 我是MVVMCROSS6.0和Xamarin的新手。 null ConfigurationChanges=ConfigChanges.Screensize ConfigChanges.Orientation)]公共类MainActivity:MvxFormsAppCompatActivity{protected override void OnCreate(Bundle Bundle){TabLa

    • 我正在做一个项目,我的意图是运行一个玉米作业,并发送邮件给我的朋友,祝他们生日,我能够从MySQL DB获取电子邮件,并将其与当前日期进行比较,但当涉及到发送电子邮件时,我得到NullPointerException。 我确信应用程序属性没有问题,我在其他项目中也使用了它们,它们的功能正常 这是我得到以下信息的错误

    • 我正试图将即时消息添加到现有的应用程序中。但是我不确定应该如何配置socket.io模块。我已经尝试了以下方法: 因为打开一个页面时没有记录connected,所以我假设丢失了一些东西。我的错误在哪里?

    • 所以我只想使用谷歌云视觉应用编程接口从图像中检测文本或标签。但是当我运行这段代码时,我总是得到: 但我不知道为什么。。。下面是我得到的完整json输出: 我的测试代码如下: 所以问题是。。此代码有什么问题?

    • 它返回几个错误,其中大多数显示:<代码> 我对编码很陌生,我只是想用推特API制作一个有趣的机器人,但有很多错误,我不知道该怎么办。 出现的主要错误如下: (从集合导入namedtuple,映射导入错误:无法从“集合”导入名称“映射”) 谁能帮帮我吗? 出现的错误:回溯(最近一次调用):文件“C:\Users\wgama\PycharmProjects\botesquilo\botesquilo.