【LWJGL2 WIKI】【现代OpenGL篇】用投影、视图、模型矩阵画方形

原文:http://wiki.lwjgl.org/wiki/The_Quad_with_Projection,_View_and_Model_matrices

Introduction 介绍

在OpenGL里,位移、旋转、缩放摄像机和模型用的矩阵必须我们自己计算。传统的OpenGL里有一堆帮忙计算矩阵的函数,但是OpenGL作为一个图形类库职责是画东西而不是算东西,所以现在这些工作最好是由我们自己来完成。本教程我们将用shader根据预先定义好的变换矩阵来计算每个顶点的位置。

So… what’s a matrix? 矩阵是什么

如果你想问这个问题,那你最好先学学数学。本教程不解释矩阵是什么,只教你怎样解决移动物体的问题。关于矩阵,你可以看看这个:[1]

3 Matrices 三个矩阵

为了移动模型和操作摄像机,我们要根据计算过程分为三个矩阵:

  • 投影矩阵(Projection matrix)
  • 视图矩阵(View matrix)
  • 模型矩阵(Model matrix)

Projection matrix 投影矩阵

投影矩阵用来定义摄像机在3D世界中的可视区域,它也被称为截头锥体,你可以想象成它是一个顶被削平的金字塔,而你将要站在顶部向底部看去。因此通常宽高和我们要绘制的矩阵(即视口,viewport)有相同的横纵比。我们可以定义金字塔的深度,所有塔内的部分都会被画在屏幕上,塔外部分则削掉以节省OpenGL性能开销。金字塔底是塔顶的缩放版(仍然应有相同的横纵比),缩放程度由我们的“视野”决定。
把投影矩阵想象成摄像机镜头的数学抽象,用此矩阵我们可以定义许多东西,比如最终图象的横纵比、放大系数、可视矩离。用图表示大概是:
这里写图片描述

还记得OpenGL默认的每个轴的坐标范围是[-1,1]吗,使用投影矩阵可以改变这个范围。比如,当我们使用16:10的宽屏分辨率时,我们的高度范围仍然是[-1,1],但是宽度范围扩充为[-1.6,1.6]更合理。如果我们在矩阵里没用正确的横纵比,最终图片将看起来很变形。
矩阵应该是这样的:
这里写图片描述

代码:

// Setup projection matrix
projectionMatrix = new Matrix4f();
float fieldOfView = 60f;
float aspectRatio = (float)WIDTH / (float)HEIGHT;
float near_plane = 0.1f;
float far_plane = 100f;

float y_scale = this.coTangent(this.degreesToRadians(fieldOfView / 2f));
float x_scale = y_scale / aspectRatio;
float frustum_length = far_plane - near_plane;

projectionMatrix.m00 = x_scale;
projectionMatrix.m11 = y_scale;
projectionMatrix.m22 = -((far_plane + near_plane) / frustum_length);
projectionMatrix.m23 = -1;
projectionMatrix.m32 = -((2 * near_plane * far_plane) / frustum_length);
projectionMatrix.m33 = 0;

两个额外的函数cotangent和degressToRadians是简单的帮助函数,cotangent其实就相当于1/Math.tan(角度),因为视野角是角度格式,必须转换为弧度才可用(Math.tan方法参数要求是弧度),把角度转换为弧度只需要乘以PI/180。

View matrix 视图矩阵

投影矩阵是摄像机的镜头,视图矩阵则是摄像机本身,我们可以随意旋转和移动摄像机,用视图矩阵乘以投影矩阵才会得到一个完整的摄像机的数学定义。

Model matrix 模型矩阵

对每个模型来说,最终的矩阵应该各不相同,这就是模型矩阵。就是说我们可以像旋转、移动、缩放摄像机一样同样对待模型。

Manipulating matrices 操作矩阵

LWJGL提供了许多可以操作矩阵的静态方法,本例中我们使用一个4x4的浮点数矩阵,应该用到类Matrix4f。使用此类可以实现旋转、缩放和平移:

// Translate camera
Matrix4f.translate(cameraPos, viewMatrix, viewMatrix);

以上代码中,我们按某一向量平移视图矩阵(cameraPos是Vector3类型的变量),结果矩阵将覆盖我们当前矩阵(译注:因为两个参数里传的都viewMatrix,应该是一个是源矩阵,一个是结果矩阵),旋转和缩放的用法也是类似。

Moving, rotating and scaling our object 平移、旋转、缩放物体

操作矩阵需要Vector3对象,我们为每种操作单独定义一个Vector3变量:

private Vector3f modelPos = null;
private Vector3f modelAngle = null;
private Vector3f modelScale = null;

当定义方形时,我们也给这些向量一个标准初始值:

// Set the default quad rotation, scale and position values
modelPos = new Vector3f(0, 0, 0);
modelAngle = new Vector3f(0, 0, 0);
modelScale = new Vector3f(1, 1, 1);
cameraPos = new Vector3f(0, 0, -1);

在我们的程序循环逻辑部分,要创建完整的模型矩阵,这些变换必须按顺序进行:缩放、平移、旋转

// Scale, translate and rotate model
Matrix4f.scale(modelScale, modelMatrix, modelMatrix);
Matrix4f.translate(modelPos, modelMatrix, modelMatrix);
Matrix4f.rotate(this.degreesToRadians(modelAngle.z), new Vector3f(0, 0, 1),
        modelMatrix, modelMatrix);
Matrix4f.rotate(this.degreesToRadians(modelAngle.y), new Vector3f(0, 1, 0),
        modelMatrix, modelMatrix);
Matrix4f.rotate(this.degreesToRadians(modelAngle.x), new Vector3f(1, 0, 0),
        modelMatrix, modelMatrix);

Uniform shader variables shader中的uniform变量

你得根据三个矩阵计算每个顶点的正确位置,因此我们的shader必须事先就得到这些信息,用uniform变量来实现。uniform变量是一种会在渲染管线里保持不变的变量,这正是我们所需要的,渲染前我们得上传新的矩阵数据。按下面这样定义uniform变量:

#version 150 core

uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelMatrix;

in vec4 in_Position;

在main方法里,我们要算新的顶点坐标,in_Position是原来的顶点坐标:

gl_Position = projectionMatrix * viewMatrix * modelMatrix * in_Position;

片段shader不需要知道发生了什么。in变量与属性列表相连,uniform变量并不需要。我们把数据放进这些变量中,因此我们需要知道这些变量存在于我们shader(内存)的何处。可以这样获取:

// Get matrices uniform locations
projectionMatrixLocation = GL20.glGetUniformLocation(pId, "projectionMatrix");
viewMatrixLocation = GL20.glGetUniformLocation(pId, "viewMatrix");
modelMatrixLocation = GL20.glGetUniformLocation(pId, "modelMatrix");

得到的这些location是整数,一旦shader创建好,它们将不变。所以它们是全局的,当我们上传数据时正需要它们。

Uploading data to uniform variables 上传数据到uniform变量

我们已经有了投影、视图、模型矩阵,最后只需要把它们上传到uniform变量中去,在此之前有些事要做:

  • 储存Matrix4f数据在一个FloatBuffer中,此FloatBuffer必须保存16个值。
  • Flip这个FloatBuffer
  • 把FloatBuffer发送到uniform location

保存矩阵很容易,定义一个全局FloatBuffer保存16个值,我们的矩阵有一个叫store的函数,接收一个FloatBuffer然后把矩阵信息存在其中。Flip一个FloatBuffer之前也常做,应该驾轻就熟了。uniform location就是之前提到的当创建shader时就有的数据值。我们可以用glUniformMatrix4上传FloatBuffer,保存、flip、上传三个矩阵,别忘了还要绑定shader:

// Upload matrices to the uniform variables
GL20.glUseProgram(pId);

projectionMatrix.store(matrix44Buffer); matrix44Buffer.flip();
GL20.glUniformMatrix4(projectionMatrixLocation, false, matrix44Buffer);
viewMatrix.store(matrix44Buffer); matrix44Buffer.flip();
GL20.glUniformMatrix4(viewMatrixLocation, false, matrix44Buffer);
modelMatrix.store(matrix44Buffer); matrix44Buffer.flip();
GL20.glUniformMatrix4(modelMatrixLocation, false, matrix44Buffer);

GL20.glUseProgram(0);

The result 结果

这里写图片描述
现在即使视口不是正方形,方形仍然被无拉伸地正确渲染。这是理所当然的,因为我们用了投影矩阵,它调整数值以适应我们使用的横纵比。

Complete source code 完整代码

Vertex shader

#version 150 core

uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelMatrix;

in vec4 in_Position;
in vec4 in_Color;
in vec2 in_TextureCoord;

out vec4 pass_Color;
out vec2 pass_TextureCoord;

void main(void) {
    gl_Position = in_Position;
    // Override gl_Position with our new calculated position
    gl_Position = projectionMatrix * viewMatrix * modelMatrix * in_Position;

    pass_Color = in_Color;
    pass_TextureCoord = in_TextureCoord;
}

Fragment shader

#version 150 core

uniform sampler2D texture_diffuse;

in vec4 pass_Color;
in vec2 pass_TextureCoord;

out vec4 out_Color;

void main(void) {
    out_Color = pass_Color;
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值