OpenGL 坐标系统

news/2024/12/4 22:37:14/

1.简介

OpenGL希望在每次顶点着色器运行后,我们可见的所有顶点都为标准化设备坐标。也就是说,每个顶点的xyz坐标都应该在-1.01.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)
{}


http://www.ppmy.cn/news/298359.html

相关文章

android 如何分析应用的内存(五)

android 如何分析应用的内存&#xff08;五&#xff09; 接上文 lldb的工具篇的GUI部分。分成两部分&#xff1a; vscode 的LLDBas的LLDB 接下来是as的LLDB as的LLDB 为了进行LLDB的调试&#xff0c;需要对as进行配置&#xff0c;事实上&#xff0c;每一个在AS中编辑的应…

低温泵变频matlab,低温地热间接供热换热参数优化与MATLAB实现

1优化设计方法及其数学描述图1为本文所研究的低温地热间接供热系统流程图。低温地热间接供热系统由地热生产(回灌)井、间接板式换热器、辅助板式换热器及供热终端(辐射地板或风机盘管)组成。平常低温地热水满足建筑的基本负荷的需要;负荷高峰时,辅助热源(调峰锅炉)启动运行。换…

聊空调冷热源的选择逻辑与原则

近年来&#xff0c;建筑项目空调系统初投资与运行费用均较高&#xff0c;作为空调系统设计过程中的一个重要的决策环节&#xff0c;冷热源方案的选择非常重要。冷热源形式不同&#xff0c;系统的初投资和能耗差别会很大。如何决策出合理的空调方式&#xff0c;需要对众多影响因…

2022施工员-设备方向-通用基础(施工员)操作证考试题及模拟考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2022汽车修理工&#xff08;初级&#xff09;考试100题是汽车修理工&#xff08;初级&#xff09;模拟考试题库全真模拟题&#xff01;2022年汽车修理工&#xff08;初级&#xff09;考试题模拟考试题库及在线模拟考试…

建筑“光储直柔”配用电系统关键技术分析

低碳发展背景下的建筑“光储直柔”配用电系统关键技术分析&#xff08;2021&#xff09; 摘 要 在低碳发展的背景下&#xff0c;为适应高比例的可再生能源结构&#xff0c;建筑电气化已经成为未来的发展趋势。建筑电气化不仅要提高建筑电气化率&#xff0c;还要发展新型建筑配…

2022年R1快开门式压力容器操作判断题模拟考试平台操作

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2022年R1快开门式压力容器操作复习题为R1快开门式压力容器操作考试题目考前押题&#xff01;2022年R1快开门式压力容器操作判断题模拟考试平台操作依据R1快开门式压力容器操作考前押题。R1快开门式压力容器操作考试资…

几度风雨路 工地铸辉煌 ——访济南四建集团总经理赵吉刚

赵吉刚&#xff0c;1965年6月25日出生&#xff0c;泰安肥城人&#xff0c;暖通专业本科毕业。现任济南四建集团有限责任公司副总经理&#xff0c;济南四建集团有限责任公司建筑安装公司经理。 88 年毕业于山东建筑工程学院。 88 —89年济南四建集团有限责任公司任技术员。 89 —…

工程项目管理流程(转)

A 签订项目管理委托合同B 项目管理部进场C 工程图设计C/01 方案设计C/02 初步设计C/03 初步设计报批C/04 施工图设计C/04.01 施工图设计C/04.02 施工图审核C/04.03 交付图纸D 办理项目规划手续D/01 申请规划设计方案审查通知书D/02 专项审批&#xff08;人防、消防、交通、园林…