OpenGL 坐标系统
1.简介
OpenGL希望在每次顶点着色器运行后,我们可见的所有顶点都为标准化设备坐标。也就是说,每个顶点的x,y,z坐标都应该在-1.0到1.0之间,超出这个坐标范围的顶点都将不可见。将坐标变换为标准化设备坐标,接着再转化为屏幕坐标的过程通常是分步进行的,也就是类似于流水线那样子。在流水线中,物体的顶点在最终转化为屏幕坐标之前还会被变换到多个坐标系统,总共有5个不同的坐标系统:
- 局部空间(Local Space,或者称为物体空间(Object Space))
- 世界空间(World Space)
- 观察空间(View Space,或者称为视觉空间(Eye Space))
- 裁剪空间(Clip Space)
- 屏幕空间(Screen Space)
为了将坐标从一个坐标系变换到另一个坐标系,我们需要用到几个变换矩阵,最重要的几个分别是模型(Model)、观察(View)、投影(Projection)三个矩阵。
- 局部坐标:是对象相对于局部原点的坐标,也是物体起始的坐标。
- 世界空间坐标:世界空间坐标是处于一个更大的空间范围的,这些坐标相对于世界的全局原点,它们会和其它物体一起相对于世界的原点进行摆放。
- 观察空间坐标:每个坐标都是从摄像机或者说观察者的角度进行观察的。
- 裁剪坐标:裁剪坐标会被处理至-1.0到1.0的范围内,并判断哪些顶点将会出现在屏幕上。
- 屏幕坐标:我们将使用一个叫做视口变换(Viewport Transform)的过程。视口变换将位于-1.0到1.0范围的坐标变换到由glViewport函数所定义的坐标范围内。最后变换出来的坐标将会送到光栅器,将其转化为片段。
2.局部空间
局部空间是指物体所在的坐标空间,即对象最开始所在的地方。前面三角形顶点是被设定在-0.5到0.5的坐标范围中,(0, 0)是它的原点。这些都是局部坐标。
3.世界空间
是指顶点相对于(游戏)世界的坐标,如果你希望将物体分散在世界上摆放(特别是非常真实的那样),这就是你希望物体变换到的空间。物体的坐标将会从局部变换到世界空间;该变换是由模型矩阵(Model Matrix)实现的。模型矩阵是一种变换矩阵,它能通过对物体进行位移、缩放、旋转来将它置于它本应该在的位置或朝向。
4.观察空间
观察空间经常被人们称之OpenGL的摄像机(Camera),观察空间是将世界空间坐标转化为用户视野前方的坐标而产生的结果。因此观察空间就是从摄像机的视角所观察到的空间。而这通常是由一系列的位移和旋转的组合来完成,平移/旋转场景从而使得特定的对象被变换到摄像机的前方。这些组合在一起的变换通常存储在一个观察矩阵(View Matrix)里。
5.裁剪空间
在一个顶点着色器运行的最后,OpenGL期望所有的坐标都能落在一个特定的范围内,且任何在这个范围之外的点都应该被裁剪掉(Clipped)。被裁剪掉的坐标就会被忽略,所以剩下的坐标就将变为屏幕上可见的片段。
为了将顶点坐标从观察变换到裁剪空间,我们需要定义一个投影矩阵(Projection Matrix),它指定了一个范围的坐标,比如在每个维度上的-1000到1000。投影矩阵接着会将在这个指定的范围内的坐标变换为标准化设备坐标的范围(-1.0, 1.0)。所有在范围外的坐标不会被映射到在-1.0到1.0的范围之间,所以会被裁剪掉。在上面这个投影矩阵所指定的范围内,坐标(1250, 500, 750)将是不可见的,这是由于它的x坐标超出了范围,它被转化为一个大于1.0的标准化设备坐标,所以被裁剪掉了。
6.正射投影
正射投影矩阵定义了一个类似立方体的平截头箱,它定义了一个裁剪空间,在这空间之外的顶点都会被裁剪掉。
上面的平截头体定义了可见的坐标,它由由宽、高、近(Near)平面和远(Far)平面所指定。任何出现在近平面之前或远平面之后的坐标都会被裁剪掉。
要创建一个正射投影矩阵,我们可以使用GLM的内置函数glm::ortho
:
glm::ortho(0.0f, 800.0f, 0.0f, 600.0f, 0.1f, 100.0f);
- 前两个参数指定了平截头体的左右坐标
- 第三和第四参数指定了平截头体的底部和顶部。通过这四个参数我们定义了近平面和远平面的大小,
- 然后第五和第六个参数则定义了近平面和远平面的距离。这个投影矩阵会将处于这些x,y,z值范围内的坐标变换为标准化设备坐标。
7.透视投影
如果你曾经体验过实际生活给你带来的景象,你就会注意到离你越远的东西看起来更小。这个奇怪的效果称之为透视(Perspective)。
正如你看到的那样,由于透视,这两条线在很远的地方看起来会相交。这正是透视投影想要模仿的效果,它是使用透视投影矩阵来完成的。
在GLM中可以这样创建一个透视投影矩阵:
glm::mat4 proj = glm::perspective(glm::radians(45.0f), (float)width/(float)height, 0.1f, 100.0f);
- 第一个参数定义了fov的值,它表示的是视野(Field of View),并且设置了观察空间的大小。如果想要一个真实的观察效果,它的值通常设置为45.0f,但想要一个末日风格的结果你可以将其设置一个更大的值。
- 第二个参数设置了宽高比,由视口的宽除以高所得。
- 第三和第四个参数设置了平截头体的近和远平面。我们通常设置近距离为0.1f,而远距离设为100.0f。所有在近平面和远平面内且处于平截头体内的顶点都会被渲染。
我们为上述的每一个步骤都创建了一个变换矩阵:模型矩阵、观察矩阵和投影矩阵。一个顶点坐标将会根据以下过程被变换到裁剪坐标:
注意矩阵运算的顺序是相反的(记住我们需要从右往左阅读矩阵的乘法)。最后的顶点应该被赋值到顶点着色器中的gl_Position,OpenGL将会自动进行透视除法和裁剪。
8.示例
3D效果
#ifndef MYOPENGLWIDGET_H
#define MYOPENGLWIDGET_H
#include <QOpenGLWidget>
#include <QOpenGLFunctions_3_3_Core>
#include <QOpenGLTexture>
#include <QImage>
#include <QOpenGLShaderProgram>class MyOpenGLWidget : public QOpenGLWidget,public QOpenGLFunctions_3_3_Core
{
public:MyOpenGLWidget(QWidget *parent = nullptr);protected:virtual void initializeGL();virtual void paintGL();virtual void resizeGL(int w, int h);private:QOpenGLTexture *m_wall;QOpenGLTexture *m_face;QOpenGLShaderProgram *m_program;
};#endif // MYOPENGLWIDGET_H#include "myopenglwidget.h"
#include <QMatrix4x4>
#include <QTime>
#include <QTimer>
#include <math.h>
#include <QVector3D>
#include <QVector>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
};GLuint indices[] = {0, 1, 3,1, 2, 3
};//顶点着色器语言
const GLchar* vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 position;\n"
"layout (location = 1) in vec2 texCoord;\n"
"out vec2 outTexCoord;\n"
"uniform mat4 model;\n"
"uniform mat4 view;\n"
"uniform mat4 projection;\n"
"void main()\n"
"{\n"
"gl_Position = projection * view * model * vec4(position,1.0);\n"
"outTexCoord = texCoord;\n"
"}\n\0";//片段着色器语言
//texture函数会使用之前设置的纹理参数对相应的颜色值进行采样
//mix按一定的比例,混合两个纹理颜色
const GLchar* fragmentShaderSource = "#version 330 core\n"
"out vec4 color;\n"
"uniform sampler2D ourTexture1;\n"
"uniform sampler2D ourTexture2;\n"
"in vec2 outTexCoord;\n"
"void main()\n"
"{\n"
"color = mix(texture(ourTexture1, outTexCoord),texture(ourTexture2, vec2(outTexCoord.x, outTexCoord.y)),0.45);\n"
"}\n\0";GLuint VBO, VAO,EBO;
GLuint shaderProgram;QVector<QVector3D> cubePositions = {QVector3D( 0.0f, 0.0f, 0.0f),QVector3D( 2.0f, 5.0f, -15.0f),QVector3D(-1.5f, -2.2f, -2.5f),QVector3D(-3.8f, -2.0f, -12.3f),QVector3D( 2.4f, -0.4f, -3.5f),QVector3D(-1.7f, 3.0f, -7.5f),QVector3D( 1.3f, -2.0f, -2.5f),QVector3D( 1.5f, 2.0f, -2.5f),QVector3D( 1.5f, 0.2f, -1.5f),QVector3D(-1.3f, 1.0f, -1.5f)
};MyOpenGLWidget::MyOpenGLWidget(QWidget *parent): QOpenGLWidget(parent)
{
}void MyOpenGLWidget::initializeGL()
{initializeOpenGLFunctions();m_program = new QOpenGLShaderProgram();m_program->addShaderFromSourceCode(QOpenGLShader::Vertex,vertexShaderSource);m_program->addShaderFromSourceCode(QOpenGLShader::Fragment,fragmentShaderSource);m_program->link();glGenVertexArrays(1, &VAO);glGenBuffers(1, &VBO);glBindVertexArray(VAO);//绑定VAOglBindBuffer(GL_ARRAY_BUFFER, VBO);//顶点缓冲对象的缓冲类型是GL_ARRAY_BUFFERglBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);//把顶点数据复制到缓冲的内存中GL_STATIC_DRAW :数据不会或几乎不会改变。glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);glEnableVertexAttribArray(0);glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));glEnableVertexAttribArray(1);glBindBuffer(GL_ARRAY_BUFFER, 0);glGenBuffers(1, &EBO);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);glBindVertexArray(0);//解绑VAOm_wall = new QOpenGLTexture(QImage("./container.jpg").mirrored());m_face = new QOpenGLTexture(QImage("./awesomeface.png").mirrored());m_program->bind();m_program->setUniformValue("ourTexture1",0);m_program->setUniformValue("ourTexture2",1);//设置透视投影矩阵QMatrix4x4 projection;projection.perspective(45,(float)(width())/(height()),0.1,100);m_program->setUniformValue("projection",projection);}void MyOpenGLWidget::paintGL()
{glClearColor(0.2f,0.3f,0.3f,1.0f);glEnable(GL_DEPTH_TEST);glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);QMatrix4x4 model;QMatrix4x4 view;view.translate(0,0,-3);m_program->bind();glBindVertexArray(VAO);//绑定VAOm_wall->bind(0);m_face->bind(1);//设置观察矩阵m_program->setUniformValue("view",view);foreach(auto pos , cubePositions){//设置为单位矩阵model.setToIdentity();//平移model.translate(pos);//设置模型矩阵m_program->setUniformValue("model",model);glDrawArrays(GL_TRIANGLES,0,36);}}void MyOpenGLWidget::resizeGL(int w, int h)
{}
OpenGL 坐标系统相关推荐
- QT之OpenGL坐标系统
QT之OpenGL坐标系统 0. 写在前面的话 1. 概述 2. 各个空间简要 2.1 局部空间 2.2 世界空间 2.3 观察空间 2.4 裁剪空间 2.4.1 正射投影 2.4.2 透视投影 3. ...
- OpenGL 坐标系统(Perspective)
一.坐标系统概述 本文类容见LearnOpenGL CN.直接copy过来留个存档. OpenGL希望每次顶点着色后,我们的可见顶点都为标准化设备坐标(Normalized Device Coordi ...
- 初识OpenGL (-)坐标系统(Coordinate System)
1. 5个不同的坐标系统 局部空间(Local Space,或者称为物体空间(Object Space)) 世界空间(World Space) 观察空间(View Space,或者称为视觉空间(Eye ...
- 十一、OpenGL的坐标系统
第一部分概念 OpenGL 坐标系统:OpenGL 坐标系中每个顶点的 x,y,z 坐标都应该在 -1.0 到 1.0 之间,超出这个坐标范围的顶点都将不可见.将一个物体(图像)渲染到屏幕上,通常经过 ...
- NDK OpenGL ES 3.0 开发(八):坐标系统
该原创文章首发于微信公众号:字节流动 OpenGL 坐标系统 我们知道 OpenGL 坐标系中每个顶点的 x,y,z 坐标都应该在 -1.0 到 1.0 之间,超出这个坐标范围的顶点都将不可见. 将一 ...
- Android Sensors (3) 传感器坐标系统
传感器坐标系统 通常,sensor framework使用一个标准的三轴坐标系统来表达数值. 对于大多数传感器来说,坐标系统是相对于设备屏幕来说的. 当一个设备被放在其默认的方向上时,X轴是水平指向右 ...
- OpenGL核心模式详细讲解[结合LearnOpenGL]
OpenGL立即渲染模式&核心模式 OpenGL (for"Open Graphics Library") is an API (Application Programmi ...
- OpenGL 鼠标拾取模型
1.简介 在我们的场景中,使用鼠标光标点击或"挑选"一个3d对象是很有用的.一种方法是从鼠标投射3d光线,通过相机,进入场景,然后检查光线是否与任何物体相交.这通常被称为光线投射. ...
- 【OpenGL学习笔记⑥】——3D变换【旋转的正方体 实现地月系统 旋转+平移+缩放】
✈️ 文章目录 零. 成果预览图 一.3D立方体的顶点数组 二.纹理旋转 三.纹理缩放 四.画n个3D图形 五.轨道的数学公式 六.深度缓冲(Z 缓冲) 七.完整代码 八.参考附录: 神器的正方体 ☁ ...
最新文章
- R可视化使用ggplot2创建样本数据热力图(heatmap)
- tensorflow的mnist改写成pytorch
- python的爬虫库_python做爬虫常用库
- Yii2.0 ActiveForm Input Fields
- PostgreSQL数据目录深度揭秘
- sqlite字段类型说明
- Python运行时报错 ModuleNotFoundError: No module named ‘exceptions‘
- 关于win10的path系统变量
- 简单的学生管理系统详解(附源码)
- 【系统分析师之路】2020年系统分析师综合知识历年真题
- PC是夕阳行业?你们图样图森破
- you-get遇到的坑
- idea启动报符号缺失,无法启动与构建
- 苹果CMS完全开发文档 - 苹果CMS手册 - 苹果CMS教程 - 苹果CMS帮助 - 苹果cmsV10
- 计算机培训日志小学,小学信息技术研修日志
- Windows 10下安装Elementary OS双系统
- matlab如何拼接矩阵
- 机器学习导论:什么是机器学习?
- 使用UltraISO制作U盘CentOS启动盘
- spaceclaim简单建模2
热门文章
- 光环国际PMP:项目经理常见的工作误区
- 带你快速实现购物车功能
- 怎么制作GIF表情图
- 电脑无法登录TIM / QQ的一种解决方案
- Google使用全攻略(二)
- 计算机在音乐教学的应用论文,【音乐教学论文】信息技术在音乐教学的应用(共2464字)...
- 10大堪称神器的Chrome插件,工作效率倍速提升
- 鱼c工作室python-鱼C工作室Python作业
- oracle物化视图trunc,转载:oracle物化视图介绍
- 红帽linux9安装,红帽linux9.0安装教程