Qt OpenGL(05)标准化设备坐标(NDC)

2023-11-09 16:59

本文主要是介绍Qt OpenGL(05)标准化设备坐标(NDC),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • OpenGL中的坐标简介
  • 标准化设备坐标
    • 标准化设备坐标绘制 x y z 三个轴线
    • 完整代码
      • 顶点着色器
      • 片段着色器
      • Widget.h
      • Widget.cpp
  • 总结

OpenGL中的坐标简介

OpenGL 基于绘制流水线模型,而且绘制流水线的第一个步骤是对顶点进行一 系列的操作, 其中大部分属于几何操作。这些操作可以用一系列坐标变换来表示。

对于基于固定功能绘制流水线和立即绘制模式的 OpenGL 版本,绘制流水线中存在 6 个标架。 其中有些会用于我们的 OpenGL 应用程序代码中,有些则会用于我们编写的着色器中。并不是所有的这 6 个标架对应用程序都是可见的。同一个顶点在不同的标架下会有不同的表示。

这 6 个坐标在绘制流水线中通常按照下面的先后顺序出现:

  1. 对象坐标系或者建模坐标系
  2. 世界坐标系
  3. 眼坐标系或者照相机坐标系
  4. 裁减坐标系
  5. 标准化的设备坐标系
  6. 窗口坐标系或者屏幕坐标系

标准化设备坐标

先了解一下 标准化设备坐标 :( Normalized Device Coordinates, NDC )

标准化设备坐标是一个 x、y 和 z 值在 -1.0 到 1.0 的一小段空间。任何落在范围外的坐标都会被丢弃/裁剪,不会显示在你的屏幕上。下面你会看到我们定义的在标准化设备坐标中的三角形(忽略z轴):

在这里插入图片描述

最后通过使用由 glViewport 函数提供的数据,进行视口变换(Viewport Transform),标准化设备坐标会变换为屏幕空间坐标(Screen-space Coordinates)。

引用:[你好,三角形 - LearnOpenGL CN (learnopengl-cn.github.io)](https://learnopengl-cn.github.io/01 Getting started/04 Hello Triangle/)

标准化设备坐标绘制 x y z 三个轴线

在这里插入图片描述

上面的讲述忽略了 z 轴,下面绘制出 z 轴出来,对 NDC 坐标有个几何上的直观印象。

  1. 准备三个轴线的端点的坐标

    float lines[] = {// x-1.0f, 0.0f, 0.0f,1.0f, 0.0f, 0.0f,// y0.0f, -1.0f, 0.0f,0.0f,  1.0f, 0.0f,// z0.0f,  0.0f, -1.0f,0.0f,  0.0f,  1.0f,
    };
    

    利用 glDrawArrays (GL_LINES,0,2) 绘制

  2. 三个轴的正方形的端点的索引

    因为数据已经存在线段的坐标中,所以可以使用 索引对象 EBO ,保存 x, y, z 轴的正方向端点的索引

    unsigned int end[] = {1,3,5
    };
    

    利用 glDrawArrays (GL_LINES,0,2) 绘制

  3. 绑定显卡缓存数据VBO

        glGenBuffers (1,&VBO);glBindBuffer (GL_ARRAY_BUFFER,VBO);glBufferData (GL_ARRAY_BUFFER,sizeof(lines) + sizeof(end) ,NULL,GL_STATIC_DRAW);glBufferSubData (GL_ARRAY_BUFFER,0,sizeof(lines),lines );
    
  4. 指定顶点数组对象:Vertex Array Object,VAO

    这个 A A A 我更愿意理解成 a t t r i b u t e attribute attribute 或者 a s s i s t a n t assistant assistant,为 GPU 缓存中的数据分类并记录哪些是位置数据,哪些是颜色数据等。

        glGenVertexArrays (1,&VAO);glBindVertexArray(VAO);glVertexAttribPointer(0,    // vao 索引,对应顶点着色器中的变量的位置 : “layout (location = 0) in vec3 aPos;”3,    // 变量中元素的个数, 比如 aPos变量 有 3 个数据【x,y,z】组成GL_FLOAT, // 类型GL_FALSE, // 标准化,是否在 [-1,1] 之间3 * sizeof(float),  // 步长,表示下个元组的首元素 和 该元组首元素之间的大小,因为顶点数据中 可能还夹杂颜色、法向量等数据(void*)0 );   // 变量的偏移量,在多个变量混合时指定变量的偏移glEnableVertexAttribArray(0); // 使能 location = 0 位置
    
  5. 指定元素缓冲对象( EBO 或者叫 IBO )

    EBO 理解成 VAO 的成员,让VAO中已经记录成功的变量从新组合或者复用

        glGenBuffers(1, &EBO);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(end), end, GL_STATIC_DRAW);
    
  6. 设置着色器程序

        QString filename = ":/shader";shaderProgram.addShaderFromSourceFile (QOpenGLShader::Vertex,   filename+".vert");shaderProgram.addShaderFromSourceFile (QOpenGLShader::Fragment, filename+".frag");shaderProgram.link ();
    
  7. 渲染过程

        // 绘制 x y zglLineWidth (20.0f);    // 线宽shaderProgram.setUniformValue ("u_color",1.0f, 0.0f, 0.0f, 1.0f); // 红色glDrawArrays (GL_LINES,0,2); // 画 x 轴shaderProgram.setUniformValue ("u_color",0.0f, 1.0f, 0.0f, 1.0f); // 绿色glDrawArrays (GL_LINES,2,2); // 画 y 轴shaderProgram.setUniformValue ("u_color",0.0f, 0.0f, 1.0f, 1.0f); // 蓝色glDrawArrays (GL_LINES,4,2); // 画 z 轴// 绘制 endglPointSize (30.0f);    // 点大小shaderProgram.setUniformValue ("u_color",1.0f, 0.0f, 0.0f, 1.0f);   // 红色glDrawElements (GL_POINTS,1,GL_UNSIGNED_INT,0); // x 端点shaderProgram.setUniformValue ("u_color",0.0f, 1.0f, 0.0f, 1.0f);   // 绿色glDrawElements (GL_POINTS,1,GL_UNSIGNED_INT, (void *)(sizeof(unsigned int)*1)); // y 端点shaderProgram.setUniformValue ("u_color",0.0f, 0.0f, 1.0f, 1.0f);   // 蓝色glDrawElements (GL_POINTS,1,GL_UNSIGNED_INT, (void *)(sizeof(unsigned int)*2)); // z 端点
    

完整代码

顶点着色器

因为 z 轴正对屏幕,如果不做旋转,显示不出 z 轴,所以需要一个模型矩阵 model 变量

#version 330 core
layout (location = 0) in vec3 aPos;
uniform mat4 model;void main()
{gl_Position = model * vec4(aPos.x, aPos.y, aPos.z, 1.0);
};

片段着色器

通过 u_color 设置不同的颜色

#version 330 core
uniform vec4 u_color;
void main()
{gl_FragColor = vec4( u_color );
}

Widget.h

#ifndef WIDGET_H
#define WIDGET_H#include <QOpenGLWidget>
#include <QOpenGLFunctions_3_3_Core>
#include <QOpenGLShaderProgram>class Widget : public QOpenGLWidget,QOpenGLFunctions_3_3_Core
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();// QOpenGLWidget interface
protected:virtual void initializeGL() override;virtual void paintGL() override;virtual void resizeGL(int w, int h) override;private:unsigned int VAO, VBO, EBO;QOpenGLShaderProgram shaderProgram;int min,max;double ratio;
};
#endif // WIDGET_H

Widget.cpp

#include "Widget.h"
#include <QApplication>
#include <QEvent>
#include <QThread>#define qRandom   QRandomGenerator::global ()
#define qout if( 1 ) qDebug() << __FILE__ << __LINE__ << ": "float lines[] = {// x-1.0f, 0.0f, 0.0f,1.0f, 0.0f, 0.0f,// y0.0f, -1.0f, 0.0f,0.0f,  1.0f, 0.0f,// z0.0f,  0.0f, -1.0f,0.0f,  0.0f,  1.0f,
};unsigned int end[] = {1,3,5
};
//float end[] = {
//    // x
//    1.0f,  0.0f, 0.0f,
//    // y
//    0.0f,  1.0f, 0.0f,
//    // z
//    0.0f,  0.0f, 1.0f,
//};Widget::Widget(QWidget *parent): QOpenGLWidget(parent)
{setWindowTitle ("07_xyz");resize (200,200);ratio = qApp->devicePixelRatio ();}Widget::~Widget()
{makeCurrent ();glDeleteBuffers (1,&VBO);glDeleteVertexArrays (1,&VAO);doneCurrent ();
}void Widget::initializeGL()
{initializeOpenGLFunctions ();const char *version =(const char *) glGetString (GL_VERSION);qout << QString(version);// ---------------------------------glGenBuffers (1,&VBO);glBindBuffer (GL_ARRAY_BUFFER,VBO);glBufferData (GL_ARRAY_BUFFER,sizeof(lines) ,NULL,GL_STATIC_DRAW);glBufferSubData (GL_ARRAY_BUFFER,0,sizeof(lines),lines );QString filename = ":/shader";shaderProgram.addShaderFromSourceFile (QOpenGLShader::Vertex,   filename+".vert");shaderProgram.addShaderFromSourceFile (QOpenGLShader::Fragment, filename+".frag");shaderProgram.link ();glGenVertexArrays (1,&VAO);glBindVertexArray(VAO);glVertexAttribPointer(0,    // vao 索引,对应顶点着色器中的变量的位置 : “layout (location = 0) in vec3 aPos;”3,    // 变量中元素的个数, 比如 aPos变量 有 3 个数据【x,y,z】组成GL_FLOAT, // 类型GL_FALSE, // 标准化,是否在 [-1,1] 之间3 * sizeof(float),  // 步长,表示下个元组的首元素 和 该元组首元素之间的大小,因为顶点数据中 可能还夹杂颜色、法向量等数据(void*)0 );   // 变量的偏移量,在多个变量混合时指定变量的偏移glEnableVertexAttribArray(0); // 使用 location = 0 的索引glGenBuffers(1, &EBO);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(end), end, GL_STATIC_DRAW);glBindBuffer (GL_ARRAY_BUFFER,0);//    glPolygonMode (GL_FRONT_AND_BACK,GL_LINE);// 深度测试glEnable(GL_DEPTH_TEST);
}int rotateAngle = 0;
void Widget::paintGL()
{glClearColor(0.2f, 0.3f, 0.3f, 1.0f);   // 设置背景色glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glViewport ((max-min)/2 ,0,min,min);shaderProgram.bind ();glBindVertexArray(VAO);shaderProgram.setUniformValue ("u_color",1.0f, 0.5f, 0.2f, 1.0f);QMatrix4x4 model;model.rotate ( -15, 1.0f, 0.0f, 0.0f);model.rotate ( rotateAngle+15, 0.0f, 1.0f, 0.0f);shaderProgram.setUniformValue ("model",model);// 绘制 x y zglLineWidth (20.0f);    // 线宽glPointSize (30.0f);    // 点大小shaderProgram.setUniformValue ("u_color",1.0f, 0.0f, 0.0f, 1.0f); // 红色glDrawArrays (GL_LINES,0,2); // 画 x 轴glDrawElements (GL_POINTS,1,GL_UNSIGNED_INT,0); // x 端点shaderProgram.setUniformValue ("u_color",0.0f, 1.0f, 0.0f, 1.0f); // 绿色glDrawArrays (GL_LINES,2,2); // 画 y 轴glDrawElements (GL_POINTS,1,GL_UNSIGNED_INT, (void *)(sizeof(unsigned int)*1)); // y 端点shaderProgram.setUniformValue ("u_color",0.0f, 0.0f, 1.0f, 1.0f); // 蓝色glDrawArrays (GL_LINES,4,2); // 画 z 轴glDrawElements (GL_POINTS,1,GL_UNSIGNED_INT, (void *)(sizeof(unsigned int)*2)); // z 端点//    qout << this->windowState ();
//    if( this->windowState () != Qt::WindowMinimized && 1 ){if( 1 ){QThread::currentThread ()->msleep (50);rotateAngle += 1 ;update ();}
}void Widget::resizeGL(int w, int h)
{qout << "resizeGL";min = std::min (w,h) * ratio;max = std::max (w,h) * ratio;
}

总结

在这里插入图片描述

如图所示,在 NDC 坐标中 Z Z Z 的正向是朝屏幕内部的,这结果还真是让人意外。

这篇关于Qt OpenGL(05)标准化设备坐标(NDC)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/377411

相关文章

Qt实现发送HTTP请求的示例详解

《Qt实现发送HTTP请求的示例详解》这篇文章主要为大家详细介绍了如何通过Qt实现发送HTTP请求,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1、添加network模块2、包含改头文件3、创建网络访问管理器4、创建接口5、创建网络请求对象6、创建一个回复对

Qt 中集成mqtt协议的使用方法

《Qt中集成mqtt协议的使用方法》文章介绍了如何在工程中引入qmqtt库,并通过声明一个单例类来暴露订阅到的主题数据,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友一起看看吧... 目录一,引入qmqtt 库二,使用一,引入qmqtt 库我是将整个头文件/源文件都添加到了工程中进行编译,这样 跨平台

如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别详解

《如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别详解》:本文主要介绍如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别的相关资料,描述了如何使用海康威视设备网络SD... 目录前言开发流程问题和解决方案dll库加载不到的问题老旧版本sdk不兼容的问题关键实现流程总结前言作为

基于Qt Qml实现时间轴组件

《基于QtQml实现时间轴组件》时间轴组件是现代用户界面中常见的元素,用于按时间顺序展示事件,本文主要为大家详细介绍了如何使用Qml实现一个简单的时间轴组件,需要的可以参考下... 目录写在前面效果图组件概述实现细节1. 组件结构2. 属性定义3. 数据模型4. 事件项的添加和排序5. 事件项的渲染如何使用

基于Qt开发一个简单的OFD阅读器

《基于Qt开发一个简单的OFD阅读器》这篇文章主要为大家详细介绍了如何使用Qt框架开发一个功能强大且性能优异的OFD阅读器,文中的示例代码讲解详细,有需要的小伙伴可以参考一下... 目录摘要引言一、OFD文件格式解析二、文档结构解析三、页面渲染四、用户交互五、性能优化六、示例代码七、未来发展方向八、结论摘要

python与QT联合的详细步骤记录

《python与QT联合的详细步骤记录》:本文主要介绍python与QT联合的详细步骤,文章还展示了如何在Python中调用QT的.ui文件来实现GUI界面,并介绍了多窗口的应用,文中通过代码介绍... 目录一、文章简介二、安装pyqt5三、GUI页面设计四、python的使用python文件创建pytho

QT实现TCP客户端自动连接

《QT实现TCP客户端自动连接》这篇文章主要为大家详细介绍了QT中一个TCP客户端自动连接的测试模型,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录版本 1:没有取消按钮 测试效果测试代码版本 2:有取消按钮测试效果测试代码版本 1:没有取消按钮 测试效果缺陷:无法手动停

基于Qt实现系统主题感知功能

《基于Qt实现系统主题感知功能》在现代桌面应用程序开发中,系统主题感知是一项重要的功能,它使得应用程序能够根据用户的系统主题设置(如深色模式或浅色模式)自动调整其外观,Qt作为一个跨平台的C++图形用... 目录【正文开始】一、使用效果二、系统主题感知助手类(SystemThemeHelper)三、实现细节

Qt实现文件的压缩和解压缩操作

《Qt实现文件的压缩和解压缩操作》这篇文章主要为大家详细介绍了如何使用Qt库中的QZipReader和QZipWriter实现文件的压缩和解压缩功能,文中的示例代码简洁易懂,需要的可以参考一下... 目录一、实现方式二、具体步骤1、在.pro文件中添加模块gui-private2、通过QObject方式创建

Qt QWidget实现图片旋转动画

《QtQWidget实现图片旋转动画》这篇文章主要为大家详细介绍了如何使用了Qt和QWidget实现图片旋转动画效果,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 一、效果展示二、源码分享本例程通过QGraphicsView实现svg格式图片旋转。.hpjavascript