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

news2025/1/14 14:49:08

文章目录

  • 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,
        // y
         0.0f, -1.0f, 0.0f,
         0.0f,  1.0f, 0.0f,
        // z
         0.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 z
        glLineWidth (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 轴
    
        // 绘制 end
        glPointSize (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_OBJECT

public:
    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,
    // y
     0.0f, -1.0f, 0.0f,
     0.0f,  1.0f, 0.0f,
    // z
     0.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 z
    glLineWidth (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 的正向是朝屏幕内部的,这结果还真是让人意外。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/149026.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

3-2内存管理-虚拟内存

文章目录一.虚拟内存的基本概念二.请求分页管理方式&#xff08;一&#xff09;页表机制&#xff08;二&#xff09;缺页中断机构&#xff08;三&#xff09;地址变换机构三.页面置换算法&#xff08;一&#xff09;最佳置换算法OPT&#xff08;二&#xff09;先进先出页面置换…

flowable 简介

flowable 简介目录概述需求&#xff1a;设计思路实现思路分析1.管理2.二、初识Flowable五大引擎3.2.通过编写程序的方式来构造ProcessEngineConfiguration对象4.流程引擎API架构图5.flowable 表结构说明参考资料和推荐阅读Survive by day and develop by night. talk for impor…

Leetcode_单周赛_327

6283. 正整数和负整数的最大计数 代码 直接遍历统计即可 class Solution {public int maximumCount(int[] nums) {int a 0, b 0;for (int i 0; i < nums.length; i) {if (nums[i] > 0) a;else if (nums[i] < 0) b;}return Math.max(a, b);} }6285. 执行 K 次操作…

Python代码实现:坐标轮换法求解多维最优化问题

文章目录多维最优化问题坐标轮换法原理代码实现坐标轮换法坐标轮换法优缺点多维最优化问题 此前介绍的黄金分割法和切线法都是针对一维最优化问题的解决方案。本文开始&#xff0c;我们将最优化问题从一维扩展到多维&#xff0c;暂时仍考虑无约束的优化场景。 坐标轮换法原理…

ArrayList | 简单的洗牌算法

一个洗牌程序需要包含&#xff1a; 创建一副扑克牌&#xff08;除去大小王剩下52张&#xff0c;每种花色13张&#xff09;。洗牌&#xff0c;打乱牌的顺序。揭牌&#xff0c;每位玩家轮流揭牌&#xff0c;从洗完后的牌组中获得自己的牌。因此&#xff0c;我们可以依照以下思路来…

R 语言 4.2.2安装 WGCNA

文章目录1 WGCNA库介绍2 安装踩坑还得是官方文档这样安装我出现的问题参考AppendixA. 安装RB. 配置环境C. 修改镜像1 WGCNA库介绍 WGCNA是用于加权相关网络分析的R包&#xff0c; 相关网络越来越多地用于生物信息学应用 加权基因共表达网络分析是一种系统生物学方法&#xff0…

按键控制电源通断,实现各种设备/电脑开关机低功耗IC

前言 今天记录一下一些硬件开关电的低功耗控制ic&#xff0c;代替物理机械开关&#xff0c;后续有新的更好用的芯片会继续更新此博。 环境 every machine 参考文档 正文 一版我们选择ic&#xff0c;除了功能之外还要看一些性能&#xff0c;这里我暂时录入的功能就是一个按…

SpringCloud从入门到精通(九)

bus bus-概述 • Spring Cloud Bus 是用轻量的消息中间件将分布式的节点连接起来&#xff0c;可以用于广播配置文件的更改或 者服务的监控管理。关键的思想就是&#xff0c;消息总线可以为微服务做监控&#xff0c;也可以实现应用程序之间相通信。 • Spring Cloud Bus 可选的…

【MySQL】为什么使用B+树做索引

【MySQL】为什么使用B树做索引? 索引这个词&#xff0c;相信大多数人已经相当熟悉了&#xff0c;很多人都知道MySQL的索引主要以B树为主&#xff0c;但是要问到为什么用B树&#xff0c;恐怕很少有人能把前因后果讲述的很完整。本文就来从头到尾介绍下数据库的索引。 索引是一…

linux系统中QT控件的操作的基本方法

大家好&#xff0c;今天主要和大家聊一聊&#xff0c;如何学习QT中的控件使用方法。 目录 第一&#xff1a;QT控件基本简介 第二&#xff1a;QPushButton使用方法 第三&#xff1a;QTableWidget简介 第四&#xff1a;最终运行效果 第一&#xff1a;QT控件基本简介 老子曾说…

Rad Studio 11.2 安装 OrangeUI 组件教程

官方文档&#xff1a;http://www.orangeui.cn/components/install 本文参考官方文档进行 11 版本的安装 开始 打开 Rad Studio 11&#xff0c;点击 FIle–Open Project… 找到解压的目录下的 .groupproj 文件 出现移动端提示弹窗&#xff0c;关掉 即可 右键 点击右侧第一个程序…

大数取余公式

ab)modP[(amodP)(bmodP)]modP (ab)modP[(amodP)(bmodP)]modP欢迎使用Markdown编辑器 你好&#xff01; 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章&#xff0c;了解一下Markdown的基本语法知识。 新的改变 …

数据结构-随机化快速排序

一、概念及其介绍 快速排序由 C. A. R. Hoare 在 1960 年提出。 随机化快速排序基本思想&#xff1a;通过一趟排序将要排序的数据分割成独立的两部分&#xff0c;其中一部分的所有数据都比另外一部分的所有数据都要小&#xff0c;然后再按此方法对这两部分数据分别进行快速排…

Jenkins凭证/凭据管理详解

文章目录一、Jenkins中的凭证凭证类型凭证范围系统全局用户凭证域凭证提供者系统凭证提供者 &#xff08;Jenkins 凭证提供者&#xff0c;常用&#xff09;用户凭证提供者文件夹凭证提供者BlueOcean 凭证插件凭证存储二、管理凭证选择凭证提供者选择凭证类型通过提供者指定凭证…

JavaScript---DOM---DOM重点核心---1.8

关于DOM操作&#xff0c;我们主要针对于元素的操作。主要有创建、增、删、改、查、属性操作、事件操作。 创建 document.writeinnerHTMLcreateElement 增 appendChildinsertBefore 删 removeChild 改 主要修改dom的元素属性&#xff0c;dom元素的内容、属性、表单的值等 …

websocket的实现

websocket的实现 本文的websocket实现基于单线程Reactor网络模型的代码实现 初步了解websocket&#xff08;必读&#xff09;:参考连接 websocekt的实现基于http&#xff0c;数据传输与处理过程也很类似&#xff1a;基于reactor的http服务器 websocket握手 websocket基于T…

Python-123练习-02数值运算

文章目录1. 整数四则运算2. 除法运算3. 计算矩形面积4. 计算矩形面积结果保留两位小数5. 计算存款利息6. 计算多个垫片面积的和7. 换披萨8. 表达式求值9. 三角函数计算10. 三角形周长及面积1. 整数四则运算 描述 编写程序&#xff0c;计算2个正整数的和、差、积、商并输出。题…

2023春节祝福系列第一弹(放飞孔明灯,祝福大家身体健康)

2023年春节祝福第一弹 放飞孔明灯&#xff0c;祝福大家身体健康&#xff01; 目录 一、前言 二、一片星光闪烁的旋转星空 &#xff08;1&#xff09;、效果展示&#xff1a; &#xff08;2&#xff09;、相关源代码 &#xff08;3&#xff09;、语法解释 &#xff08;3.…

[ 数据结构 ] 堆排序--------思路、图解、代码

0 基本介绍 堆定义:首先是完全二叉树,分为大顶堆和小顶堆大顶堆:顾名思义,如果将父子节点看成一个堆(三个节点的组合),那么顶的值需要大于其两个子节点的值,即顶大;小顶堆即顶小升序排序使用大顶堆,降序使用小顶堆回顾顺序存储二叉树中,父子节点的关系为:下标为n的节点,它的左…

Java开发 - Spring框架初体验

目录 前言 了解框架的概念 Spring框架 关于Spring 在Maven中使用Spring Spring怎么管理对象 spring怎么创建对象 通过Bean注解创建对象 通过组件扫描创建对象 关于ComponentScan("xxxxxx") Spring Bean的作用域 自动装配技术 什么是自动装配 补充 Io…