本节我们将绘制一个3维物体,立方体。

如果要渲染3D物体,我们需要了解MVP(Model View Projection),它表示三个转换矩阵。实际上这个名字不够明确,更加确切的释义如下:

  • Model - Model to World  模型空间到世界空间
  • View - World to View      世界空间到视图空间
  • Projection - View to Projection   视图空间到投影空间

要实现这三个转换矩阵,我们需要借助glm数学库提供的一些方便的结构体和函数。

重构

我们先对程序结构进行修改,对工程右键>Add > New Filter, 创建一个Primitives 文件夹,在其中创建两个文件,一个Vertex.h,一个ShapeData.h

Vertex.h中定义了一个Vertex结构体,它包含两个glm::vec3成员,分别表示位置和颜色。

 #pragma once
#include <glm\glm.hpp> struct Vertex
{
glm::vec3 position;
glm::vec3 color;
};

ShapeData.h中定义了一个ShapeData结构体,包含四个成员变量,分别是

  • Vertex* 类型:顶点数组指针
  • Gluint类型:顶点数量
  • GLushort* 类型:索引数组指针
  • GLuint 类型:索引数组长度

另外还提供了构造函数,清理函数

 #pragma once
#include <GL\glew.h>
#include "Vertex.h" struct ShapeData
{
ShapeData() :
vertices(), numVertices(), indices(), numIndices() {} Vertex* vertices;
GLuint numVertices;
GLushort* indices;
GLuint numIndices; GLsizeiptr vertexBufferSize() const
{
return numVertices * sizeof(Vertex);
}
GLsizeiptr indexBufferSize() const
{
return numIndices * sizeof(GLushort);
} void cleanUp()
{
delete[] vertices;
delete[] indices;
numVertices = numIndices = ;
}
};

此外还加入了一个新的类,ShapeGenerator

ShapeGenerator.h

 #pragma once
#include <ShapeData.h> class ShapeGenerator
{
public:
static ShapeData makeCube();
};

ShapeGenerator.cpp

 #include "ShapeGenerator.h"
#include "Vertex.h" #define NUM_ARRAY_ELEMENTS(a) sizeof(a) / sizeof(*a) ShapeData ShapeGenerator::makeCube()
{
ShapeData ret;
Vertex stackVerts[]=
{
glm::vec3(-1.0f, +1.0f, +1.0f), //
glm::vec3(+1.0f, 0.0f, 0.0f), //Color
glm::vec3(+1.0f, +1.0f, +1.0f), //
glm::vec3(0.0f, +1.0f, 0.0f), //Color
glm::vec3(+1.0f, +1.0f, -1.0f), //
glm::vec3(0.0f, 0.0f, +1.0f), //Color
glm::vec3(-1.0f, +1.0f, -1.0f), //
glm::vec3(+1.0f, +1.0f, +1.0f), //Color glm::vec3(-1.0f, +1.0f, -1.0f), //
glm::vec3(+1.0f, 0.0f, +1.0f), //Color
glm::vec3(+1.0f, +1.0f, -1.0f), //
glm::vec3(0.0f, 0.5f, 0.2f), //Color
glm::vec3(+1.0f, -1.0f, -1.0f), //
glm::vec3(0.8f, 0.6f, 0.4f), //Color
glm::vec3(-1.0f, -1.0f, -1.0f), //
glm::vec3(0.3f, +1.0f, +0.5f), //Color glm::vec3(+1.0f, +1.0f, -1.0f), //
glm::vec3(0.2f, 0.5f, 0.2f), //Color
glm::vec3(+1.0f, +1.0f, +1.0f), //
glm::vec3(0.9f, 0.3f, 0.7f), //Color
glm::vec3(+1.0f, -1.0f, +1.0f), //
glm::vec3(0.3f, 0.7f, 0.5f), //Color
glm::vec3(+1.0f, -1.0f, -1.0f), //
glm::vec3(0.5f, 0.7f, 0.5f), //Color glm::vec3(-1.0f, +1.0f, +1.0f), //
glm::vec3(0.7f, 0.8f, 0.2f), //Color
glm::vec3(-1.0f, +1.0f, -1.0f), //
glm::vec3(0.5f, 0.7f, 0.3f), //Color
glm::vec3(-1.0f, -1.0f, -1.0f), //
glm::vec3(0.8f, 0.6f, 0.4f), //Color
glm::vec3(-1.0f, -1.0f, +1.0f), //
glm::vec3(0.3f, +1.0f, +0.5f), //Color glm::vec3(+1.0f, +1.0f, +1.0f), //
glm::vec3(0.7f, 0.8f, 0.2f), //Color
glm::vec3(-1.0f, +1.0f, +1.0f), //
glm::vec3(0.5f, 0.7f, 0.3f), //Color
glm::vec3(-1.0f, -1.0f, +1.0f), //
glm::vec3(0.8f, 0.6f, 0.4f), //Color
glm::vec3(+1.0f, -1.0f, +1.0f), //
glm::vec3(0.3f, +1.0f, +0.5f), //Color glm::vec3(+1.0f, -1.0f, -1.0f), //
glm::vec3(0.7f, 0.8f, 0.2f), //Color
glm::vec3(-1.0f, -1.0f, -1.0f), //
glm::vec3(0.5f, 0.7f, 0.3f), //Color
glm::vec3(-1.0f, -1.0f, +1.0f), //
glm::vec3(0.8f, 0.6f, 0.4f), //Color
glm::vec3(+1.0f, -1.0f, +1.0f), //
glm::vec3(0.3f, +1.0f, +0.5f), //Color
}; ret.numVertices = NUM_ARRAY_ELEMENTS(stackVerts);
ret.vertices = new Vertex[ret.numVertices];
memcpy(ret.vertices, stackVerts, sizeof(stackVerts)); unsigned short stackIndices[] =
{
,,,,,,
,,,,,,
,,,,,,
,,,,,,
,,,,,,
,,,,,,
}; ret.numIndices = NUM_ARRAY_ELEMENTS(stackIndices);
ret.indices = new GLushort[ret.numIndices];
memcpy(ret.indices, stackIndices, sizeof(stackIndices));
return ret;
}

主要作用是提供了一个静态方法 makeCube,返回一个立方体的数据。

修改MyGlWindow类

 #include <gl\glew.h>
#include "MyGlWindow.h"
#include <iostream>
#include <fstream>
#include <glm\gtc\matrix_transform.hpp>
#include <ShapeGenerator.h> GLuint programID;
GLuint numIndices; void MyGlWindow::sendDataToOpenGL()
{ ShapeData shape = ShapeGenerator::makeCube(); GLuint vertexBufferID;
glGenBuffers(, &vertexBufferID);
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferID);
glBufferData(GL_ARRAY_BUFFER, shape.vertexBufferSize(), shape.vertices, GL_STATIC_DRAW); GLuint indexBufferID;
glGenBuffers(, &indexBufferID);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferID);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, shape.indexBufferSize(), shape.indices, GL_STATIC_DRAW); glEnableVertexAttribArray();
glVertexAttribPointer(, , GL_FLOAT, GL_FALSE, sizeof(GLfloat) * , ); glEnableVertexAttribArray();
glVertexAttribPointer(, , GL_FLOAT, GL_FALSE, sizeof(GLfloat) * , (char*)(sizeof(GLfloat) * )); numIndices = shape.numIndices;
shape.cleanUp(); } void MyGlWindow::installShaders()
{
GLuint vertexShaderID = glCreateShader(GL_VERTEX_SHADER);
GLuint fragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER); std::string tmp = ReadShaderCode("VertexShaderCode2.glsl");
const char* vertexShaderCode = tmp.c_str();
glShaderSource(vertexShaderID, , &vertexShaderCode, ); tmp = ReadShaderCode("FragmentShaderCode2.glsl");
const char* fragmentShaderCode = tmp.c_str();
glShaderSource(fragmentShaderID, , &fragmentShaderCode, ); glCompileShader(vertexShaderID);
glCompileShader(fragmentShaderID); programID = glCreateProgram();
glAttachShader(programID, vertexShaderID);
glAttachShader(programID, fragmentShaderID); glLinkProgram(programID); glUseProgram(programID);
} void MyGlWindow::initializeGL()
{
glewInit();
glEnable(GL_DEPTH_TEST);
sendDataToOpenGL();
installShaders();
} void MyGlWindow::paintGL()
{
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
glViewport(, , width(), height()); //更新:最新版本的glm中,glm::mat4()生成的是不是单位矩阵,而是零矩阵,这里要使用glm::mat4(1.0f)才可以
glm::mat4 modelTransformMatrix = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f,-3.0f));
glm::mat4 projectionMatrix = glm::perspective(30.0f, ((float)width()) / height(), 0.1f, 10.0f); GLint modelTransformUniformLocation = glGetUniformLocation(programID, "modelMatrix");
GLint projectionMatrixUniformLocation = glGetUniformLocation(programID, "projectionMatrix"); glUniformMatrix4fv(modelTransformUniformLocation, , GL_FALSE, &modelTransformMatrix[][]);
glUniformMatrix4fv(projectionMatrixUniformLocation, , GL_FALSE, &projectionMatrix[][]); glDrawElements(GL_TRIANGLES, numIndices, GL_UNSIGNED_SHORT, ); } std::string MyGlWindow::ReadShaderCode(const char* fileName)
{
std::ifstream myInput(fileName);
if (!myInput.good())
{
std::cout << "File failed to load..." << fileName;
exit();
}
return std::string(
std::istreambuf_iterator<char>(myInput),
std::istreambuf_iterator<char>());
}

Vertex Shader :

 #version                            

 in layout(location=) vec3 position;
in layout(location=) vec3 vertexColor; uniform mat4 modelMatrix;
uniform mat4 projectionMatrix; out vec3 passingColor; void main()
{
vec4 v = vec4(position,1.0);
vec4 newPosition = modelMatrix * v;
gl_Position = projectionMatrix * newPosition;
passingColor= vertexColor;
}

Fragment Shader:

 #version                                          

 in vec3 passingColor;
out vec4 finalColor; void main()
{
finalColor = vec4(passingColor,1.0);
}

注意MyGlWindow的78-85行,是使用Uniform 变量的通用方法,使用的是Vertex Shader中第6-7行的两个uniform。

使用Uniform变量的步骤总结:

  1. 使用glGetUniformLocation获取Uniform变量的ID,并储存在一个GLint 变量中
  2. 使用glUnifomxxxx()类的函数和刚才得到的ID给Uniform赋值。

另外要注意85-86行,函数的最后一参数需要一个const GLfloat * 类型的变量,所以我们使用[0][0]获取矩阵的第一个元素,它是个GLfloat类型的,再对他使用取地址符&得到它的地址。

编译运行以后得到一个平面(实际上是立方体的一个面):

我们在最开始提到了3个矩阵,但是这里只用到了两个,实际上少了第二个矩阵,World to View矩阵,这也正是为什么我们现在无法移动观察视角的原因,我们的相机被假设在世界原点,朝向-z的方向看去,这是默认的设置。后面我们会学习world to view的转换矩阵。

3D Computer Grapihcs Using OpenGL - 11 Model View Projection Matrices的更多相关文章

  1. 3D Computer Grapihcs Using OpenGL - 17 添加相机(旋转)

    在11节我们说过,MVP矩阵中目前只应用了两个矩阵,World to View 矩阵被省略了,这就导致我们的画面没有办法转换视角. 本节我们将添加这一环节,让相机可以旋转. 为了实现这一目的,我们添加 ...

  2. 3D Computer Grapihcs Using OpenGL - 15 Draw Element Instanced

    友情提示:继续本节之前,需要保存此前的代码,本节为了试验,会对代码做一些修改,但后续的修改需要我们把代码返回之前的进度. OpenGL内置支持Instancing,有专门的函数来处理这件事情. 为了方 ...

  3. 3D Computer Grapihcs Using OpenGL - 09 Enable Depth Test

    启用Depth Test OpenGL是个3D绘图API,也就是说不只有xy坐标轴,还有第三个坐标轴z,z轴的方向是垂直于屏幕,指向屏幕内. 靠近人眼的方向是负方向,标准化设备坐标的最小值是-1, 最 ...

  4. 3D Computer Grapihcs Using OpenGL - 06 Vertex and Fragment Shaders

    从这里就接触到了可编程图形渲染管线. 下面介绍使用Vertex Shader (顶点着色器)和 Fragment Shader(像素着色器)的方法. 我们的目标是使用这两个着色器给三角形填充绿色. 添 ...

  5. 3D Computer Grapihcs Using OpenGL - 19 Vertex Array Object(顶点数组对象)

    大部分OpenGL教程都会在一开始就讲解VAO,但是该教程的作者认为这是很不合理的,因为要理解它的作用需要建立在我们此前学过的知识基础上.因此直到教程已经进行了一大半,作者才引入VAO这个概念.在我看 ...

  6. 3D Computer Grapihcs Using OpenGL - 16 使用DrawElementsInstanced绘制立方体

    我们使用15节学到的知识来绘制14节的立方体. 在第14节我们使用了两次glDrawElements实现了OpenGL实例化,发现这样仍然不太方便,如果需要绘制成千上万的立方体,就需要手写成千上万次的 ...

  7. 3D Computer Grapihcs Using OpenGL - 14 OpenGL Instancing

    如果我们需要绘制两个(或者多个)一样的立方体(或者物体),只是位置.缩放.旋转不一样,那么我们可以不需要多次将这个物体的顶点信息.颜色信息等发送到显卡,而是发送一次,绘制多次,仅仅是每次绘制之前应用不 ...

  8. 3D Computer Grapihcs Using OpenGL - 12 Rotation Matrix

    为了证明我们上节渲染出来的是一个立方体而不是一个平面,我们决定将它旋转一定角度,这样我们就需要一个旋转矩阵(也属于ModelTransformMatrix的一部分) 上一节我们的ModelTransf ...

  9. 3D Computer Grapihcs Using OpenGL - 10 Color Buffer

    本节我们将尝试利用三角形制作一个“走马灯”效果. 一个三角形如图示方式,从左向右依次移动. 先看一下代码: MyGlWindow.cpp #include <gl\glew.h> #inc ...

随机推荐

  1. CentOS7 内核模块管理

    1.查看所有模块:lsmod 2.查看指定模块的详细信息:modinfo 模块名 3.动态加载模块:modprobe 模块名 4.动态卸载模块:modprobe -r 模块名 5.开机自动加载模块:假 ...

  2. 极*Java速成教程 - (4)

    Java语言基础 多态 多态是面向对象的一大重要特性,如果说封装是隐藏一个类怎么做,继承是确定一系列的类做什么,那多态就是通过手段去分离做什么和怎么做. 向上转型与收窄 在开发者将一类事物封装成类以后 ...

  3. MyBatis二级缓存的笔记及记录

    一.什么是二级缓存: 由于一级缓存是一次性的.临时的:每个会话都会创建一个新的:多个会话之间是不能共享的: 二级缓存用于解决一级缓存的不足:每一个“namespace”都会对应一个二级缓存:执行查询的 ...

  4. 【暑假培训1】test1

    T1: 30pts:直接暴力三层循环枚举 就就就先不写代码了qwq: 70pts: 因为X+Y+Z==0 所以我们可以考虑枚举X和Y,然后利用↑式子求出Z=-X-Y: 然后touli xcg的70pt ...

  5. openmvg中cmd模块解析

    ---恢复内容开始--- 在openmvg库中,定义了一个CmdLine类来定义例程的输入参数格式.源文件为.\openMVG\src\third_party\cmdLine\cmdLine.h. 先 ...

  6. [51Nod1623] 完美消除

    link $solution:$ 首先我们可以发现一个结论,对于一个数 $x$ ,它的最低修改次数为它每位与前去中是否都比此位上的数大,有则答案 $-1$ .因为若有小数则没有办法将其答案贡献变低. ...

  7. 小程序中页面兼容h5标签的解析

    有时候当小程序向后台拿数据是一篇html标签的文章时,把它放进小程序会发现很多标签就不兼容,如果要一个个改又很麻烦,有没有方法可以很快地兼容html标签呢? 有个工具可以做到:wxParse 下载了它 ...

  8. OC(构造函数,分类等知识总结)

    文章来源:http://my.oschina.net/luoguankun/blog/219532 一.成员变量的作用域 ·        @public ·        在任何地方都能直接访问对象 ...

  9. 第七讲 自定义Realm实现授权

    1.仅仅通过配置文件来指定权限不够灵活,并且不方便,在实际的应用中大多数情况下都是将用户信息,角色信息,权限信息保存到了数据库中.所以需要从数据库中去获取相关的数据信息.可以使用shiro提供的Jdb ...

  10. 09java进阶——IO

    1.File类 1.1目录及路径分隔符 package cn.jxufe.java.chapter09.demo01; import java.io.File; public class Test01 ...