将对象从3D坐标系转换为2D显示空间(立方体)

将对象从3D坐标系转换为2D显示空间(立方体)

现在,我们将研究如何将对象的顶点从其本地3D坐标系转换为2D屏幕显示空间。多个对象位于3D世界坐标空间中。单个顶点具有X,Y,Z坐标。这些必须从3D世界空间转换为正在看它们的相机的3D坐标空间。从这里,将顶点从相机空间(也称为视图空间)转换为3D屏幕空间,然后最终在2D屏幕显示空间中显示。形成三角形的三个顶点的组在此过程的早期阶段保持“连接”,直到将它们双线插值进行双线插值,以产生代表三角形以显示在屏幕上的三角形的单个片段的集合。每个片段都有一个3D屏幕X,Y,Z坐标,可用于隐藏表面拆卸等物品。要从3D对象空间移动到3D屏幕空间,使用了模型视图投影矩阵。这些阶段中的每个阶段将在以下小节中更详细地探讨。

我们将从一个由两个三角形组成的对象开始,然后是一个涉及立方体的示例,然后是一组立方体。

两个三角形对象

// ***************************************************
  /* THE DATA
   */
  // anticlockwise/counterclockwise ordering
  private float[] vertices = {      
    // position (x,y,z), colour (r,g,b),    tex coords (s,t)
    -0.5f,  0.5f, 0.0f,  1.0f, 0.0f, 0.0f,  0.0f, 1.0f,  // top left
    -0.5f, -0.5f, 0.0f,  0.0f, 1.0f, 0.0f,  0.0f, 0.0f,  // bottom left
     0.5f, -0.5f, 0.0f,  0.0f, 0.0f, 1.0f,  1.0f, 0.0f,  // bottom right
     0.5f,  0.5f, 0.0f,  1.0f, 1.0f, 1.0f,  1.0f, 1.0f   // top right
  };
  
  private int vertexStride = 8;        // 8 floats per vertex
  private int vertexXYZFloats = 3;     // 3 floats for the position
  private int vertexColourFloats = 3;  // 3 flopats for the colour
  private int vertexTexFloats = 2;     // 2 floats for the texture coordinates
  
  private int[] indices = {            // Note that we start from 0
      0, 1, 2,                         // Triangle 1 is made up of vertices 0, 1 and 2
                                       // in anticlockwise ordering
      0, 2, 3                          // Triangle 2 is made up of vertices 0, 2 and 3 
                                       // in anticlockwise ordering
  };

上述代码给出数据结构和下一个代码给出了渲染方法。绘制了两个三角形,并用瓦特(Watt)书的前盖绘制纹理。这两个三角形已从观看者旋转,因此从观看者中看到。

public void render(GL3 gl) {
    gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
  
    double elapsedTime = getSeconds()-startTime;
    
    Mat4 projectionMatrix = Mat4Transform.perspective(45, aspect);
    
    float zposition = 2f;
    //float zposition = 2f+(float)(Math.sin(Math.toRadians(elapsedTime*50)));
    Vec3 position = new Vec3(0,0,zposition);
    Mat4 viewMatrix = Mat4Transform.lookAt(position, new Vec3(0,0,0), new Vec3(0,1,0));
    
    float angle = -55f;
    //float angle = (float)(-115*Math.sin(Math.toRadians(elapsedTime*50)));
    Mat4 modelMatrix = Mat4Transform.rotateAroundX(angle);
    
    Mat4 mvpMatrix = Mat4.multiply(viewMatrix, modelMatrix);
    mvpMatrix = Mat4.multiply(projectionMatrix, mvpMatrix);
    
    shader.use(gl);
    shader.setFloatArray(gl, "model", modelMatrix.toFloatArrayForGLSL());
    shader.setFloatArray(gl, "view", viewMatrix.toFloatArrayForGLSL());
    shader.setFloatArray(gl, "projection", projectionMatrix.toFloatArrayForGLSL());
    shader.setFloatArray(gl, "mvpMatrix", mvpMatrix.toFloatArrayForGLSL());
    
    gl.glActiveTexture(GL.GL_TEXTURE0);
    gl.glBindTexture(GL.GL_TEXTURE_2D, textureId1[0]);
  
    gl.glBindVertexArray(vertexArrayId[0]);
    gl.glDrawElements(GL.GL_TRIANGLES, indices.length, GL.GL_UNSIGNED_INT, 0);
    gl.glBindVertexArray(0);
  }

使用静态方法mat4transform.perspective()创建投影矩阵。然后使用静态方法mat4transform.lookat(从,到,向上)创建视图矩阵。这需要三个参数:相机位置,相机正在看的目标以及世界的名义向量;在此示例中,摄像机处于位置(0,0,2),着眼于世界原产地,最初的向量(0,1,0)。革兰氏– SCHMIDT过程(如讲座中所述)用于为视图创建坐标框架(即相机)。

选择–55度的角度将两个三角形旋转X轴旋转,该轴将旋转远离观看器的顶部。静态方法mat4transform.rotatearoundx()用于设置所需的模型转换矩阵。

在下一步中,计算模型视图投影矩阵。重要的是:请记住,矩阵在它们正在转换的顶点之前。因此,它们是按照投影,视图,模型的,因此模型矩阵是第一个应用于顶点的。

然后,使用单独的统一制度将ModelMatrix,ViewMatrix,IdjotectionMatrix和MVPMATRIX的每一个都传递到顶点着色器。为此,必须首先将4x4矩阵转换为浮子数组。方法Mat4.tofloatarrayforglsl()用于执行此操作。它将行柱有序的矩阵转换为存储在浮子数组中的列有序矩阵,这是顶点着色器中使用的数据类型中所需的内容。还将一种新方法添加到着色器类中,以在顶点着色器中设置相关的统一。

在此示例中,我已经将所有4个矩阵传递给着色器,以证明我们可以(i)将模型,视图和投影矩阵分别传递到着色器,或者(ii)将它们组合到CPU上的模型视频预测矩阵中。然后将其传递给着色器,或(iii)同时使用。我们还可以将单个矩阵传递,并将其组合成GPU上的模型观察矩阵 - 这就是Joey在他的教程中所做的。在顶点着色器中编写模型视图预测矩阵的问题是,然后重复对象中每个顶点的相同计算。但是,这对于某些程序可能是必要的,具体取决于所需的内容。在将来的程序中,我们将仅通过模型矩阵和模型视图预测矩阵。原因将在稍后进行解释。

接下来要看的是顶点着色器,该阴影器在下一个代码中给出。大多数应该对您熟悉。新的制服被称为MAT4型,例如统一MAT4 MVPMATRIX,其中包含从主程序发送的模型视图投影矩阵。这用于乘以顶点的位置,以将其转换为3D屏幕空间(或已知的剪辑空间)。评论的输出线显示,可以在顶点着色器中计算模型观看预测矩阵。

#version 330 core

layout (location = 0) in vec3 position;
layout (location = 1) in vec3 color;
layout (location = 2) in vec2 texCoord;

out vec3 aColor;
out vec2 aTexCoord;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
uniform mat4 mvpMatrix;

void main() {
  //mat4 mvpMatrix2 = projection * view * model;
  //gl_Position = mvpMatrix2 * vec4(position, 1.0);
  
  gl_Position = mvpMatrix * vec4(position, 1.0);
  
  aColor = color;
  aTexCoord = texCoord;
}

顶点着色器的每个实例在不同的顶点上执行相同的操作。 所有顶点都发送到管道的下一个阶段,在那里它们被重新连接在一起以制作三角形,然后将其栅格隔开以产生一组由片段着色器处理的片段。 片段着色器不需要从我们以前的程序中更改。 它只是使用相关纹理坐标来算出片段的颜色。 我们在三角数据结构中使用的颜色属性被忽略(因此可以从示例中删除)。

我们现在有一个工作的3D世界。

(1) 一个立方体对象
立方体的每个面都有纹理坐标在0.0,0.0至1.0,1.0的范围内

// ***************************************************
  /* THE DATA
   */
  // anticlockwise/counterclockwise ordering
  
   private float[] vertices = new float[] {  // x,y,z, colour, s,t
      -0.5f, -0.5f, -0.5f,  1.0f, 0.0f, 0.0f,  0.0f, 0.0f,  // 0
      -0.5f, -0.5f,  0.5f,  1.0f, 0.0f, 0.0f,  1.0f, 0.0f,  // 1
      -0.5f,  0.5f, -0.5f,  1.0f, 0.0f, 0.0f,  0.0f, 1.0f,  // 2
      -0.5f,  0.5f,  0.5f,  1.0f, 0.0f, 0.0f,  1.0f, 1.0f,  // 3
       0.5f, -0.5f, -0.5f,  1.0f, 0.0f, 0.0f,  1.0f, 0.0f,  // 4
       0.5f, -0.5f,  0.5f,  1.0f, 0.0f, 0.0f,  0.0f, 0.0f,  // 5
       0.5f,  0.5f, -0.5f,  1.0f, 0.0f, 0.0f,  1.0f, 1.0f,  // 6
       0.5f,  0.5f,  0.5f,  1.0f, 0.0f, 0.0f,  0.0f, 1.0f,  // 7

      -0.5f, -0.5f, -0.5f,  0.0f, 1.0f, 0.0f,  1.0f, 0.0f,  // 8
      -0.5f, -0.5f,  0.5f,  0.0f, 1.0f, 0.0f,  0.0f, 0.0f,  // 9
      -0.5f,  0.5f, -0.5f,  0.0f, 1.0f, 0.0f,  1.0f, 1.0f,  // 10
      -0.5f,  0.5f,  0.5f,  0.0f, 1.0f, 0.0f,  0.0f, 1.0f,  // 11
       0.5f, -0.5f, -0.5f,  0.0f, 1.0f, 0.0f,  0.0f, 0.0f,  // 12
       0.5f, -0.5f,  0.5f,  0.0f, 1.0f, 0.0f,  1.0f, 0.0f,  // 13
       0.5f,  0.5f, -0.5f,  0.0f, 1.0f, 0.0f,  0.0f, 1.0f,  // 14
       0.5f,  0.5f,  0.5f,  0.0f, 1.0f, 0.0f,  1.0f, 1.0f,  // 15

      -0.5f, -0.5f, -0.5f,  0.0f, 0.0f, 1.0f,  0.0f, 0.0f,  // 16
      -0.5f, -0.5f,  0.5f,  0.0f, 0.0f, 1.0f,  0.0f, 1.0f,  // 17
      -0.5f,  0.5f, -0.5f,  0.0f, 0.0f, 1.0f,  0.0f, 1.0f,  // 18
      -0.5f,  0.5f,  0.5f,  0.0f, 0.0f, 1.0f,  0.0f, 0.0f,  // 19
       0.5f, -0.5f, -0.5f,  0.0f, 0.0f, 1.0f,  1.0f, 0.0f,  // 20
       0.5f, -0.5f,  0.5f,  0.0f, 0.0f, 1.0f,  1.0f, 1.0f,  // 21
       0.5f,  0.5f, -0.5f,  0.0f, 0.0f, 1.0f,  1.0f, 1.0f,  // 22
       0.5f,  0.5f,  0.5f,  0.0f, 0.0f, 1.0f,  1.0f, 0.0f   // 23
     };
    
     private int[] indices =  new int[] {
      0,1,3, // x -ve 
      3,2,0, // x -ve
      4,6,7, // x +ve
      7,5,4, // x +ve
      9,13,15, // z +ve
      15,11,9, // z +ve
      8,10,14, // z -ve
      14,12,8, // z -ve
      16,20,21, // y -ve
      21,17,16, // y -ve
      23,22,18, // y +ve
      18,19,23  // y +ve
    };
    
  private int vertexStride = 8;
  private int vertexXYZFloats = 3;
  private int vertexColourFloats = 3;
  private int vertexTexFloats = 2;

给出渲染方法和随附的方法,用于防止渲染方法变得混乱。 getModelMatrix()返回模型变换矩阵。 在这里,它只是返回身份矩阵。 GetViewMatrix()将相机设置为位置(2,3,4),查看原点(0,0,0),名义上向量为(0,1,0)。

 public void render(GL3 gl) {
    gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);

    Mat4 projectionMatrix = Mat4Transform.perspective(45, aspect);
    Mat4 viewMatrix = getViewMatrix();
    Mat4 modelMatrix = getModelMatrix();
    Mat4 mvpMatrix = Mat4.multiply(projectionMatrix, Mat4.multiply(viewMatrix, modelMatrix));
    
    shader.use(gl);
    shader.setFloatArray(gl, "model", modelMatrix.toFloatArrayForGLSL());
    shader.setFloatArray(gl, "view", viewMatrix.toFloatArrayForGLSL());
    shader.setFloatArray(gl, "projection", projectionMatrix.toFloatArrayForGLSL());
    shader.setFloatArray(gl, "mvpMatrix", mvpMatrix.toFloatArrayForGLSL());
    
    gl.glActiveTexture(GL.GL_TEXTURE0);
    gl.glBindTexture(GL.GL_TEXTURE_2D, textureId1[0]);
  
    gl.glBindVertexArray(vertexArrayId[0]);
    gl.glDrawElements(GL.GL_TRIANGLES, indices.length, GL.GL_UNSIGNED_INT, 0);
    gl.glBindVertexArray(0);
  }
  
  private Mat4 getModelMatrix() {
    double elapsedTime = getSeconds()-startTime;
    //float angle = -55;
    //float angle = (float)(-115*Math.sin(Math.toRadians(elapsedTime*50)));
    Mat4 modelMatrix = new Mat4(1);
    //modelMatrix = Mat4.multiply(Mat4Transform.rotateAroundY(angle), modelMatrix);
    //modelMatrix = Mat4.multiply(Mat4Transform.rotateAroundX(angle), modelMatrix);
    return modelMatrix;
  }
  
  private Mat4 getViewMatrix() {
    double elapsedTime = getSeconds()-startTime;
    float xposition = 2;
    float yposition = 3;
    float zposition = 4;
    //float xposition = 3.0f*(float)(Math.sin(Math.toRadians(elapsedTime*50)));
    //float zposition = 3.0f*(float)(Math.cos(Math.toRadians(elapsedTime*50)));
    Mat4 viewMatrix = Mat4Transform.lookAt(new Vec3(xposition,yposition,zposition), 
                                           new Vec3(0,0,0), new Vec3(0,1,0));
    return viewMatrix;
  }

练习
首先尝试GetViewMatrix()中的评论线。 这些将使围绕世界轴的相机位置旋转,以便相机一直在看世界的起源。 只有一个物体以屏幕上的世界来源为中心,它可以(令人困惑)看起来像对象正在旋转,而不是摄像机旋转。
在更改GetModelMatrix()之前,将视图位置设置回2,3,4的固定值。 现在,在GetModelMatrix()中评论一些行。 在运行程序之前尝试猜测效果。

(2)一群立方体
多维数据集数据结构中使用的纹理坐标已更改,以便每个面使用纹理的一部分。 您应该在v03_gleventlistener.java中检查此数据结构。

Rendering a 5x5 grid of cubes

public void render(GL3 gl) {
    gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);

    Mat4 projectionMatrix = Mat4Transform.perspective(45, aspect);
    Mat4 viewMatrix = getViewMatrix();
    
    shader.use(gl);
    shader.setFloatArray(gl, "view", viewMatrix.toFloatArrayForGLSL());
    shader.setFloatArray(gl, "projection", projectionMatrix.toFloatArrayForGLSL());
    
    gl.glActiveTexture(GL.GL_TEXTURE0);
    gl.glBindTexture(GL.GL_TEXTURE_2D, textureId1[0]);
  
    for (int i=-2; i<3; ++i) {
      for (int j=-2; j<3; ++j) {
        Mat4 modelMatrix = getModelMatrix(2f*i, 2f*j);
        Mat4 mvpMatrix = Mat4.multiply(projectionMatrix, Mat4.multiply(viewMatrix, modelMatrix));

        shader.setFloatArray(gl, "model", modelMatrix.toFloatArrayForGLSL());
        shader.setFloatArray(gl, "mvpMatrix", mvpMatrix.toFloatArrayForGLSL());

        gl.glBindVertexArray(vertexArrayId[0]);
        gl.glDrawElements(GL.GL_TRIANGLES, indices.length, GL.GL_UNSIGNED_INT, 0);
        gl.glBindVertexArray(0);
      }
    }
  }
    
  private Mat4 getModelMatrix(float i, float j) {
    double elapsedTime = getSeconds()-startTime;
    float angle = (float)(elapsedTime*50);
    Mat4 modelMatrix = new Mat4(1);    
    //modelMatrix = Mat4.multiply(modelMatrix, Mat4Transform.rotateAroundY(angle));
    modelMatrix = Mat4.multiply(modelMatrix, Mat4Transform.translate(i, 0, j));
    //modelMatrix = Mat4.multiply(modelMatrix, Mat4Transform.rotateAroundX(angle));
    modelMatrix = Mat4.multiply(modelMatrix, Mat4Transform.rotateAroundY(angle));
    return modelMatrix;
  }
  
  private Mat4 getViewMatrix() {
    double elapsedTime = getSeconds()-startTime;
    Vec3 pos = new Vec3(4,6,10);
    Mat4 viewMatrix = Mat4Transform.lookAt(pos, new Vec3(0,0,0), new Vec3(0,1,0));
    return viewMatrix;
  }

(3)更多的立方体
此示例显示了100个随机定位的立方体。该程序类似于v03.java,其中一个主要区别。 立方体位于随机位置,而不是正常网格的一部分。 每次渲染场景时,都必须生成相同的随机位置。 这是使用程序运行后一次创建的随机数的全局数组来完成的。

 public void render(GL3 gl) {
    gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);

    Mat4 projectionMatrix = Mat4Transform.perspective(45, aspect);
    Mat4 viewMatrix = getViewMatrix();
    
    shader.use(gl);
    shader.setFloatArray(gl, "view", viewMatrix.toFloatArrayForGLSL());
    shader.setFloatArray(gl, "projection", projectionMatrix.toFloatArrayForGLSL());
    
    gl.glActiveTexture(GL.GL_TEXTURE0);
    gl.glBindTexture(GL.GL_TEXTURE_2D, textureId1[0]);
  
    for (int i=0; i<100; ++i) {
      Mat4 modelMatrix = getModelMatrix(i);
      Mat4 mvpMatrix = Mat4.multiply(projectionMatrix, Mat4.multiply(viewMatrix, modelMatrix));
        
      shader.setFloatArray(gl, "model", modelMatrix.toFloatArrayForGLSL());
      shader.setFloatArray(gl, "mvpMatrix", mvpMatrix.toFloatArrayForGLSL());
      
      gl.glBindVertexArray(vertexArrayId[0]);
      gl.glDrawElements(GL.GL_TRIANGLES, indices.length, GL.GL_UNSIGNED_INT, 0);
      gl.glBindVertexArray(0);
    }
  }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值