1. 引言

本文基于C++语言,描述OpenGL的坐标系统

前置知识可参考:

笔者这里不过多描述每个名词、函数和细节,更详细的文档可以参考:

2. 概述

OpenGL中坐标变换的流程如下图:

有图可知:

  • 创建一个物体到屏幕绘制需要三个矩阵变换:模型(Model)、观察(View)、投影(Projection)(即,MVP)

  • 裁剪坐标:\(V_{clip} = M_{projrction} \cdot M_{view} \cdot M_{model} \cdot V_{local}\)

投影时主要有两者投影方式:

  • 正交投影:平行视角

  • 透视投影:近大远小

3. 编码

编码实现只需设置MVP矩阵即可

设置Model矩阵:

glm::mat4 model = glm::mat4(1.0f);
model = glm::rotate(model, glm::radians(-55.0f), glm::vec3(1.0f, 0.0f, 0.0f));

设置View矩阵:

glm::mat4 view = glm::mat4(1.0f);
// 注意,我们将矩阵向我们要进行移动场景的反方向移动。
view = glm::translate(view, glm::vec3(0.0f, 0.0f, -3.0f));

设置投影矩阵:

glm::mat4 projection = glm::mat4(1.0f);
projection = glm::perspective(glm::radians(45.0f), screenWidth / screenHeight, 0.1f, 100.0f);

在顶点着色器中设置MVP变换:

#version 330 core
layout (location = 0) in vec3 aPos;
...
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection; void main()
{
// 注意乘法要从右向左读
gl_Position = projection * view * model * vec4(aPos, 1.0);
...
}

将变换矩阵传输到GPU:

// 模型矩阵
int modelLoc = glGetUniformLocation(ourShader.ID, "model"));
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
// 观察矩阵和投影矩阵与之类似
int viewLoc = glGetUniformLocation(ourShader_ID, "view");
glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
int projectionLoc = glGetUniformLocation(ourShader_ID, "projection");
glUniformMatrix4fv(projectionLoc, 1, GL_FALSE, glm::value_ptr(projection));

至此就完成了一次简单的MVP变换,结果图如下:

4. 立体化

构建一个立体的箱子:

设置立方体的六个面(12个三角形,36个点):

float vertices[] = {
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, -0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, -0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 1.0f, 1.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f, -0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f
};

开启深度测试:

glEnable(GL_DEPTH_TEST);

清除深度缓冲:

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

可选项,让箱子旋转:

model = glm::rotate(model, (float)glfwGetTime() * glm::radians(50.0f), glm::vec3(0.5f, 1.0f, 0.0f));

如果顺利的话,结果如下:

6. 多个立方体

这里的多个立方体实质就是指定(同一个立方体)平移到多个位置

设置多个位置:

glm::vec3 cubePositions[] = {
glm::vec3( 0.0f, 0.0f, 0.0f),
glm::vec3( 2.0f, 5.0f, -15.0f),
glm::vec3(-1.5f, -2.2f, -2.5f),
glm::vec3(-3.8f, -2.0f, -12.3f),
glm::vec3( 2.4f, -0.4f, -3.5f),
glm::vec3(-1.7f, 3.0f, -7.5f),
glm::vec3( 1.3f, -2.0f, -2.5f),
glm::vec3( 1.5f, 2.0f, -2.5f),
glm::vec3( 1.5f, 0.2f, -1.5f),
glm::vec3(-1.3f, 1.0f, -1.5f)
};

绘制多个Model:

glBindVertexArray(VAO);
for (unsigned int i = 0; i < 10; i++)
{
glm::mat4 model = glm::mat4(1.0f);
model = glm::translate(model, cubePositions[i]);
model = glm::rotate(model, (float)glfwGetTime() * glm::radians(50.0f), glm::vec3(1.0f, 0.3f, 0.5f));
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model)); glDrawArrays(GL_TRIANGLES, 0, 36);
}

实现效果:

7. 完整代码

主要文件test.cpp

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <math.h>
#include "Shader.hpp"
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#include <glm/glm.hpp>
#include <glm/ext/matrix_transform.hpp> // glm::translate, glm::rotate, glm::scale
#include <glm/ext/matrix_clip_space.hpp> // glm::perspective
#include <glm/gtc/type_ptr.hpp> void framebuffer_size_callback(GLFWwindow *window, int width, int height);
void process_input(GLFWwindow *window);
unsigned int *renderInit();
void render(unsigned int shaderProgram, unsigned int VAO, unsigned int texture1, unsigned int texture2);
bool checkCompile(unsigned int shader);
bool checkProgram(unsigned int shaderProgram); int main()
{
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
GLFWwindow *window = glfwCreateWindow(800, 600, "CoordinateSystem", nullptr, nullptr); if (window == nullptr)
{
std::cout << "Faild to create window" << std::endl;
glfwTerminate();
}
glfwMakeContextCurrent(window); if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Faild to initialize glad" << std::endl;
return -1;
}
glad_glViewport(0, 0, 800, 600);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); unsigned int *arr = renderInit(); while (!glfwWindowShouldClose(window))
{
process_input(window); // render
std::cout << arr[0] << " " << arr[1] << " " << arr[2] << " " << arr[3] << " " << arr[4] << std::endl;
render(arr[0], arr[1], arr[3], arr[4]); glfwSwapBuffers(window);
glfwPollEvents();
} glDeleteProgram(arr[0]);
glDeleteVertexArrays(1, &arr[1]);
glDeleteBuffers(1, &arr[2]); glfwTerminate();
return 0;
} void framebuffer_size_callback(GLFWwindow *window, int width, int height)
{
glViewport(0, 0, width, height);
} void process_input(GLFWwindow *window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
{
glfwSetWindowShouldClose(window, true);
}
} unsigned int *renderInit()
{
//配置项
glEnable(GL_DEPTH_TEST); unsigned int VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO); unsigned int texture1;
glGenTextures(1, &texture1);
glBindTexture(GL_TEXTURE_2D, texture1);
// 为当前绑定的纹理对象设置环绕、过滤方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// 加载并生成纹理
int width, height, nrChannels;
unsigned char *data = stbi_load("../container.jpg", &width, &height, &nrChannels, 0);
if (data)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
std::cout << "Failed to load texture" << std::endl;
}
stbi_image_free(data); unsigned int texture2;
glGenTextures(1, &texture2);
glBindTexture(GL_TEXTURE_2D, texture2);
// 为当前绑定的纹理对象设置环绕、过滤方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// // 加载并生成纹理
int width2, height2, nrChannels2;
stbi_set_flip_vertically_on_load(true);
unsigned char *data2 = stbi_load("../awesomeface.png", &width2, &height2, &nrChannels2, 0);
if (data2)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width2, height2, 0, GL_RGBA, GL_UNSIGNED_BYTE, data2);
glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
std::cout << "Failed to load texture" << std::endl;
}
stbi_image_free(data2); float vertices[] = {
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, -0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, -0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 1.0f, 1.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f, -0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f}; unsigned int VBO;
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *)(3 * sizeof(float)));
glEnableVertexAttribArray(1); Shader shaderProgram = Shader("../test.vs.glsl", "../test.fs.glsl");
shaderProgram.use(); glUniform1i(glGetUniformLocation(shaderProgram.ID, "texture1"), 0);
glUniform1i(glGetUniformLocation(shaderProgram.ID, "texture2"), 1);
return new unsigned int[5]{shaderProgram.ID, VAO, VBO, texture1, texture2};
} void render(unsigned int shaderProgram, unsigned int VAO, unsigned int texture1, unsigned int texture2)
{
glClearColor(0.2, 0.3, 0.3, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture1);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture2);
glUseProgram(shaderProgram); glm::vec3 cubePositions[] = {
glm::vec3(0.0f, 0.0f, 0.0f),
glm::vec3(2.0f, 5.0f, -15.0f),
glm::vec3(-1.5f, -2.2f, -2.5f),
glm::vec3(-3.8f, -2.0f, -12.3f),
glm::vec3(2.4f, -0.4f, -3.5f),
glm::vec3(-1.7f, 3.0f, -7.5f),
glm::vec3(1.3f, -2.0f, -2.5f),
glm::vec3(1.5f, 2.0f, -2.5f),
glm::vec3(1.5f, 0.2f, -1.5f),
glm::vec3(-1.3f, 1.0f, -1.5f)}; glm::mat4 view = glm::mat4(1.0f);
// 注意,我们将矩阵向我们要进行移动场景的反方向移动。
view = glm::translate(view, glm::vec3(0.0f, 0.0f, -3.0f));
glm::mat4 projection = glm::mat4(1.0f);
projection = glm::perspective(glm::radians(45.0f), 800.0f / 600.0f, 0.1f, 100.0f);
// 模型矩阵
int modelLoc = glGetUniformLocation(shaderProgram, "model");
// 观察矩阵
int viewLoc = glGetUniformLocation(shaderProgram, "view");
glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
// 投影矩阵
int projectionLoc = glGetUniformLocation(shaderProgram, "projection");
glUniformMatrix4fv(projectionLoc, 1, GL_FALSE, glm::value_ptr(projection)); glBindVertexArray(VAO);
for (unsigned int i = 0; i < 10; i++)
{
glm::mat4 model = glm::mat4(1.0f);
model = glm::translate(model, cubePositions[i]);
float angle = 20.0f * (i + 1);
model = glm::rotate(model, (float)glfwGetTime() * glm::radians(50.0f), glm::vec3(1.0f, 0.3f, 0.5f));
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model)); glDrawArrays(GL_TRIANGLES, 0, 36);
}
}

顶点着色器test.vs.glsl

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoord; out vec2 TexCoord; uniform mat4 model;
uniform mat4 view;
uniform mat4 projection; void main()
{
// 注意乘法要从右向左读
gl_Position = projection * view * model * vec4(aPos, 1.0);
TexCoord = aTexCoord;
}

片段着色器test.fs.glsl

#version 330 core
out vec4 FragColor; in vec2 TexCoord; uniform sampler2D texture1;
uniform sampler2D texture2; void main()
{
FragColor = mix(texture(texture1, TexCoord), texture(texture2, TexCoord), 0.2);
}

8. 参考资料

[1]坐标系统 - LearnOpenGL CN (learnopengl-cn.github.io)

[2]OpenGL学习笔记(七)坐标系统 - 知乎 (zhihu.com)

[3]g-truc/glm: OpenGL Mathematics (GLM) (github.com)

基于C++的OpenGL 05 之坐标系统的更多相关文章

  1. 基于Cocos2d-x学习OpenGL ES 2.0之多纹理

    没想到原文出了那么多错别字,实在对不起观众了.介绍opengl es 2.0的不多.相信介绍基于Cocos2d-x学习OpenGL ES 2.0之多纹理的,我是独此一家吧.~~ 子龙山人出了一个系列: ...

  2. 基于Cocos2d-x学习OpenGL ES 2.0系列——纹理贴图(6)

    在上一篇文章中,我们介绍了如何绘制一个立方体,里面涉及的知识点有VBO(Vertex Buffer Object).IBO(Index Buffer Object)和MVP(Modile-View-P ...

  3. 基于Cocos2d-x学习OpenGL ES 2.0系列——使用VBO索引(4)

    在上一篇文章中,我们介绍了uniform和模型-视图-投影变换,相信大家对于OpenGL ES 2.0应该有一点感觉了.在这篇文章中,我们不再画三角形了,改为画四边形.下篇教程,我们就可以画立方体了, ...

  4. 1、基于MFC的OpenGL程序

    首先,使用的库是GLUT以及GLAUX,先下载两者,添加查找路径以及链接   一.单文本文件   工程openGLMFC 1.创建单文本文件   2.添加路径.链接 方法如之前篇章所示, 链接库为op ...

  5. Linux OpenGL 实践篇-4 坐标系统

    OpenGL中顶点经过顶点着色器后会变为标准设备坐标系.标准设备坐标系的各坐标的取值范围是[-1,1],超过这个范围的点将会被剔除.而这个变换的过程可描述为顶点在几个坐标系统的变换,这几个坐标系统为: ...

  6. 【游戏开发】基于VS2017的OpenGL开发环境搭建

    一.简介 最近,马三买了两本有关于“计算机图形学”的书籍,准备在工作之余鼓捣鼓捣图形学和OpenGL编程,提升自己的价值(奔着学完能涨一波工资去的).俗话说得好,“工欲善其事,必先利其器”.想学习图形 ...

  7. 基于MFC的OpenGL程序<转>

    原贴地址:https://www.cnblogs.com/pinking/p/6180225.html 首先,使用的库是GLUT以及GLAUX,先下载两者,添加查找路径以及链接   一.单文本文件   ...

  8. 基于Cocos2d-x学习OpenGL ES 2.0系列——你的第一个立方体(5)

    在上篇文章中,我们介绍了VBO索引的使用,使用VBO索引可以有效地减少顶点个数,优化内存,提高程序效率. 本教程将带领大家一起走进3D--绘制一个立方体.其实画立方体本质上和画三角形没什么区别,所有的 ...

  9. 基于对话框的Opengl框架

    转自:http://blog.csdn.net/longxiaoshi/article/details/8238933 12-11-29 14:55 1198人阅读 评论(6) 收藏 举报  分类: ...

  10. OpenGL中的坐标系统详细概括:包括Z缓冲

    一: 首先就是关于几个坐标系统的概括: 局部坐标是对象相对于局部原点的坐标,也是物体起始的坐标. 下一步是将局部坐标变换为世界空间坐标,世界空间坐标是处于一个更大的空间范围的.这些坐标相对于世界的全局 ...

随机推荐

  1. NGINX的配置和基本使用

    Linux NGINX NGINX:engine X ,2002年开发,分为社区版和商业版(nginx plus ) 社区版:分为主线版(开发版,奇数),稳定版(偶数) Nginx官网:http:// ...

  2. MySQL5.7兼容5.6

    MySQL5.7兼容5.6配置----MySQL5.7以上版本数据库兼容MySQL5.5-5.6版本数据库 手动安装MySQL 8.0/5.7 需要修改配置兼容 ,修改后需要重启mysql服务 (建议 ...

  3. List排序(降序)

    一.添加一个比较器 点击查看代码 import java.util.Comparator; /** * @Classname ComparatorResultType * @Description 排 ...

  4. ATM购物车

    ATM项目实现思路: ATM架构设计 三层架构 core目录下的src.py(浏览器) (展示层) interface目录下的多个py文件(框架) (核心逻辑层) db目录下db_handler.py ...

  5. PowerDotNet平台化软件架构设计与实现系列(15):支付平台

    PowerDotNet个人项目中功能全面而强大的一个系统是支付平台.我对PowerDotNet的自信很大程度上来自于经过PowerDotNet重写后的支付.财务.结算.CRM等业务型公共服务系统的稳定 ...

  6. SSM基础学习笔记

    SSM框架: 1.界面层---servlet类---SpringMVC 2.业务逻辑层---service类---Spring 3.数据访问层(持久层)---dao类---MyBatis 一.MyBa ...

  7. 有关WCH的CH42x以及CH45x选型,常见问题处理方法

    南京沁恒微电子的CH45x系列为数码管.按键驱动芯片. CH42x系列为IO扩展芯片.CH422和CH423除了支持的OC数量有一些区别,在单片机的驱动上,并没有任何区别,驱动CH423的代码是可以套 ...

  8. MongoDB从入门到实战之.NET Core使用MongoDB开发ToDoList系统(1)-后端项目框架搭建

    前言: 前面的四个章节我们主要讲解了MongoDB的相关基础知识,接下来我们就开始进入使用.NET7操作MongoDB开发一个ToDoList系统实战教程. MongoDB从入门到实战的相关教程 Mo ...

  9. [图像处理] YUV图像处理入门5

    12 yuv420转换为rgb(opencv mat) yuv格式具有亮度信息和色彩信息分离的特点,但大多数图像处理操作都是基于RGB格式,而且自己造轮子工作量太大.因此通常都会将yuv转换为rgb, ...

  10. python之路43 JavaScript语法BOM与DOM jQuery对比 标签绑定事件

    前戏 到目前为止,我们已经学过了JavaScript的一些简单的语法.但是这些简单的语法,并没有和浏览器有任何交互. 也就是我们还不能制作一些我们经常看到的网页的一些交互,我们需要继续学习BOM和DO ...