[CG从零开始] 5. 搞清 MVP 矩阵理论 + 实践( 二 )


from math import cos, sinimport mathimport numpy as npclass Camera:def __init__(self) -> None:self._fov = 60self._near = 0.3self._far = 1000self._aspect = 5 / 4# -- 都是常规的 get set,这里略去# ......# 完全参照投影矩阵的公式定义def getProjectionMatrix(self):r = math.radians(self._fov / 2)cotangent = cos(r) / sin(r)deltaZ = self._near - self._farprojection = np.zeros((4,4))projection[0,0] = cotangent / self._aspectprojection[1,1] = cotangentprojection[2,2] = (self._near + self._far) / deltaZprojection[2,3] = 2 * self._near * self._far / deltaZprojection[3,2] = -1return projection4. 构建 MVP 矩阵完成了上述的步骤后,我们就可以构建 MVP 矩阵了 。
...# 定义物体的 transformtrans = transform.Transform()trans.localPosition = [0,0,0]trans.localScale = [0.005,0.005,0.005]trans.localEulerAngle = [0,10,0]# 获取 model 矩阵model = trans.localMatrix()# 定义相机的 transformviewTrans = transform.Transform()viewTrans.localPosition = [0,2,2]viewTrans.localEulerAngle = [-40,0,0]# 获取 view 矩阵view = viewTrans.get_to_Local()# 定义相机并获得 projection 矩阵cam = Camera()proj = cam.getProjectionMatrix()# 构建 MVP 矩阵mvp = np.transpose(proj @ view @ model)# 作为 uniform 传入 shader 中,然后 shader 中将顶点位置乘上mvp矩阵 。mshader.set_mat4("u_mvp", mvp)...然后加载模型,构建一下顶点数组和索引数组,我给每个顶点额外添加了随机的颜色
importer = ModelImporter()meshes = importer.load_mesh("box.fbx")vert = []for i in range(len(meshes[0].vertices)):if i % 3 == 0:vert.extend([meshes[0].vertices[i],meshes[0].vertices[i + 1],meshes[0].vertices[i + 2]])vert.extend([meshes[0].normals[i],meshes[0].normals[i + 1],meshes[0].normals[i + 2]])vert.extend([random.random(),random.random(),random.random()])inde = meshes[0].subMeshes[0].indices# 开一下深度测试gl.glEnable(gl.GL_DEPTH_TEST)我们可以看一下最终效果 。
【[CG从零开始] 5. 搞清 MVP 矩阵理论 + 实践】

[CG从零开始] 5. 搞清 MVP 矩阵理论 + 实践

文章插图
总结:
  1. 通过 Transform 我们可以获得 model 矩阵和 view 矩阵;
  2. 通过相机的参数,我们可以获得 projection 矩阵;
  3. 按照 p * v * m * pos 的顺序,即可将顶点位置进行投影;
  4. 本文代码没有考虑层级关系,为了简洁,原理都是一样的;
  5. 为了简洁旋转采用的欧拉角进行存储 , 没有用四元数 。希望本文的例子,可以帮助理解 MVP 矩阵,以及学习一下如何加载、渲染模型的 API 等 。

推荐阅读