首先要用到glm库。在进行PVM变换时可以帮助我们方便地运算。
下载:/g-truc/glm/tags
首先是处理顶点数据,由于是一个正方体,有六个面,每个面两个三角形,一共36个点。输入顶点有顶点坐标和顶点的颜色值。这里的坐标是处在模型空间的坐标。
const float vertices[] = {//立方体数组-0.5f, -0.5f, -0.5f, 1.0f,0.0f,0.0f, //六个面,每个面两个三角形。0.5f, -0.5f, -0.5f, 1.0f,0.0f,0.0f, //每一行前三个为顶点的坐标,0.5f, 0.5f, -0.5f, 1.0f,0.0f,0.0f, //后三个值为顶点颜色值0.5f, 0.5f, -0.5f, 1.0f,0.0f,0.0f,-0.5f, 0.5f, -0.5f, 1.0f,0.0f,0.0f,-0.5f, -0.5f, -0.5f, 1.0f,0.0f,0.0f,-0.5f, -0.5f, 0.5f, 0.0f,1.0f,0.0f,0.5f, -0.5f, 0.5f, 0.0f,1.0f,0.0f,0.5f, 0.5f, 0.5f, 0.0f,1.0f,0.0f,0.5f, 0.5f, 0.5f, 0.0f,1.0f,0.0f,-0.5f, 0.5f, 0.5f, 0.0f,1.0f,0.0f,-0.5f, -0.5f, 0.5f, 0.0f,1.0f,0.0f,-0.5f, 0.5f, 0.5f, 0.0f,0.0f,1.0f,-0.5f, 0.5f, -0.5f, 0.0f,0.0f,1.0f,-0.5f, -0.5f, -0.5f, 0.0f,0.0f,1.0f,-0.5f, -0.5f, -0.5f, 0.0f,0.0f,1.0f,-0.5f, -0.5f, 0.5f, 0.0f,0.0f,1.0f,-0.5f, 0.5f, 0.5f, 0.0f,0.0f,1.0f,0.5f, 0.5f, 0.5f, 0.5f,0.0f,0.0f,0.5f, 0.5f, -0.5f, 0.5f,0.0f,0.0f,0.5f, -0.5f, -0.5f, 0.5f,0.0f,0.0f,0.5f, -0.5f, -0.5f, 0.5f,0.0f,0.0f,0.5f, -0.5f, 0.5f, 0.5f,0.0f,0.0f,0.5f, 0.5f, 0.5f, 0.5f,0.0f,0.0f,-0.5f, -0.5f, -0.5f, 0.0f,0.5f,0.0f,0.5f, -0.5f, -0.5f, 0.0f,0.5f,0.0f,0.5f, -0.5f, 0.5f, 0.0f,0.5f,0.0f,0.5f, -0.5f, 0.5f, 0.0f,0.5f,0.0f,-0.5f, -0.5f, 0.5f, 0.0f,0.5f,0.0f,-0.5f, -0.5f, -0.5f, 0.0f,0.5f,0.0f,-0.5f, 0.5f, -0.5f, 0.0f,0.0f,0.5f,0.5f, 0.5f, -0.5f, 0.0f,0.0f,0.5f,0.5f, 0.5f, 0.5f, 0.0f,0.0f,0.5f,0.5f, 0.5f, 0.5f, 0.0f,0.0f,0.5f,-0.5f, 0.5f, 0.5f, 0.0f,0.0f,0.5f,-0.5f, 0.5f, -0.5f, 0.0f,0.0f,0.5f};
很繁杂,但是一般模型都是导入的,不用自己去一个个顶点的写。
接下来根据坐标转换流程来写:
PVM变换矩阵就对应于这个过程。
model矩阵对应模型变换过程,将局部坐标转换到世界坐标。view矩阵对应
接下来是PVM变换矩阵。实际的变换顺序是“MVP”。Model矩阵用于将模型坐标转化为世界坐标。view矩阵用于从世界坐标转移到观察坐标,要设置观察摄像机的位置,观察方向和头顶方向。projection对应从观察空间到剪裁空间的变换,指定了坐标的范围。
在进入主循环后,得到pvm三个矩阵:
// Transform坐标变换矩阵glm::mat4 model(1);//model矩阵,局部坐标变换至世界坐标model = glm::translate(model, glm::vec3(0.0, 0.0, 0.0));model = glm::rotate(model, (float)glfwGetTime(), glm::vec3(0.5f, 1.0f, 0.0f));model = glm::scale(model, glm::vec3(1.0f, 1.0f, 1.0f));glm::mat4 view(1);//view矩阵,世界坐标变换至观察坐标系view = glm::lookAt(camera_position, camera_position + camera_front, camera_up);glm::mat4 projection(1);//projection矩阵,投影矩阵projection = glm::perspective(glm::radians(fov), (float)screen_width / screen_height, 0.1f, 100.0f);
这里之前要设置view矩阵中摄像机的各参数:
glm::vec3 camera_position = glm::vec3(0.0f, 0.0f, 3.0f); // 摄像机位置glm::vec3 camera_front = glm::vec3(0.0f, 0.0f, -1.0f);// 摄像机方向glm::vec3 camera_up = glm::vec3(0.0f, 1.0f, 0.0f); // 摄像机上向量
以及projection矩阵中的视角:
float fov=45.0f;
首先计算的是model矩阵
首先要创建一个model矩阵,glm::mat4 model(1)创建一个4*4的单位矩阵。glm::translate()函数用于进行平移变换,将物体平移(0,0,0),也就是不变。glm::rotate()函数用于旋转。第二个参数为旋转角度,第三个参数为旋转轴。这里的glfwGetTime是获取glfw初始化到当前状态所经过的秒数。如果是一个常数的话,就不会旋转了。那是因为每次进入循环之后矩阵在初始状态,都旋转同一个角度,那么肯定就看不到旋转。每次循环旋转不同角度,且这个角度的大小随时间变换,才能看得到旋转效果。glm::scale()函数用于缩放,vec3指定x,y,z三个方向上的缩放比例。
glm::mat4 model(1);//model矩阵,局部坐标变换至世界坐标model = glm::translate(model, glm::vec3(0.0,0.0,0.0)); model = glm::rotate(model, (float)glfwGetTime(), glm::vec3(0.5f, 1.0f, 0.0f)); model = glm::scale(model, glm::vec3(1.0f,1.0f,1.0f));
接着是view矩阵
首先还是要创建一个4*4的单位矩阵通过lookAt函数计算。第一个参数是相机位置,第二个参数是相机所正对的目标的坐标,这里用camera_position+camera_front(相机的方向)进行向量加法后可以得到相机正对的坐标。第三个参数是相机的上向量。
glm::mat4 view(1);//view矩阵,世界坐标变换至观察坐标系view = glm::lookAt(camera_position, camera_position + camera_front, camera_up);
最后是projection矩阵
这是一个投影透视矩阵第一个参数fov是角度值,定义了视野。对一个真实的观察效果,通常设置为45,如果要看到更多东西需要设置一个更大的值.第二个参数设置宽高比,也就是屏幕宽度比上屏幕高度。第三个和第四个参数设置了平截头体(也就是一个四棱台)的近和远平面。通常设置最近距离为0.1,最远距离为100.0。所有在近平面和远平面的顶点且处于平截头体内的顶点都会被渲染。
glm::mat4 projection(1);//projection矩阵,投影矩阵projection = glm::perspective(glm::radians(fov), (float)screen_width / screen_height, 0.1f, 100.0f);
以上对model,view,projection矩阵进行初始化后,还要将这三个矩阵传入着色器内。
glGetUniformLocation函数可以获得某个着色器中参数的位置,第一个参数为着色器id,第二个参数为该参数的名字
int model_location = glGetUniformLocation(shader.ID, "model");
glUniformMatrix4fv函数用于向指定位置传入一个4*4矩阵值。
glUniformMatrx4fv(model_location,1,GL_FALSE,glm::value_ptr(model));
接下来看顶点着色器的内容:
#version 330 corelayout (location = 0) in vec3 aPos;layout (location = 1) in vec3 PosColor;out vec3 positionColor;uniform mat4 model;uniform mat4 view;uniform mat4 projection;void main(){gl_Position = projection * view * model * vec4(aPos, 1.0);positionColor=PosColor;}
可以看到这里的参数和我们在程序内设置的是对应的。
注意顺序。由于opengl的向量是列向量,与行向量算的方式相反。