12.4 OpenGL顶点后处理:图元裁剪

图元裁剪 Primitive Clipping

Primitive Clipping(图元裁剪)是图形渲染管线中的一个重要步骤,发生在顶点处理之后、光栅化之前。这个阶段主要目的是去除位于视体(View Volume)之外或者被用户自定义裁剪平面(Clip Planes)裁剪掉的图元(如点、线段和三角形),以减少不必要的渲染计算。

在OpenGL中,原始图元裁剪过程首先会根据视口坐标系(Viewport)、近裁剪面(Near Clip Plane)、远裁剪面(Far Clip Plane)以及可能存在的额外用户定义裁剪平面来确定有效视体。接着,对每个传入的图元进行裁剪操作:

  1. 如果整个图元都在视体内,则保留该图元。
  2. 如果部分图元在视体内,则通过线性插值算法生成新的顶点,并根据这些顶点重新构造出一个位于视体内的新图元。
  3. 如果整个图元都在视体外或被用户定义裁剪平面裁剪,则丢弃该图元。

此外,可以通过查询对象追踪经过原始图元裁剪阶段处理的图元数量(CLIPPING_INPUT_PRIMITIVES)以及成功通过裁剪并进一步由光栅化阶段处理的图元数量(CLIPPING_OUTPUT_PRIMITIVES)。同时,启用RASTERIZER_DISCARD状态时,实现可能会直接在裁剪阶段后丢弃图元,这可能导致裁剪输入和输出计数器不增加。

控制裁剪空间坐标的原点和深度范围

void glClipControl( enum origin, enum depth );

允许开发者控制裁剪空间坐标的原点和深度范围。这对于精确的投影和深度测试至关重要,尤其是在需要与某些渲染API进行兼容性调整时。

  • origin 参数指定裁剪空间坐标系的原点位置:

    • GL_LOWER_LEFT: 原点位于左下角,这是默认值,符合传统OpenGL坐标系统。
    • GL_UPPER_LEFT: 原点位于左上角,这更符合Direct3D等其他API的习惯。
  • depth 参数定义裁剪空间深度范围:

    • GL_NEGATIVE_ONE_TO_ONE: 深度范围从-1到1,这是OpenGL的传统深度缓冲范围。
    • GL_ZERO_TO_ONE: 深度范围从0到1,这与某些其他图形API如Vulkan和Direct3D中的深度范围一致。

通过调用这个函数,开发者可以根据自己的需求来匹配不同API的深度缓冲区设置,以及根据不同的坐标系统原点来确保跨平台应用的一致性。

裁剪和剔除过程

几何体(primitives)在渲染过程中需要经过裁剪和剔除阶段以确保只有位于视口可见区域内的部分被绘制。以下是该过程的详细说明:

  1. Culling Against Cull Volume

    • 用户可以通过gl_CullDistance数组定义多个自定义剔除半空间,并且系统允许的最大数量由MAX_CULL_DISTANCES决定。
    • 在顶点着色器阶段结束后,每个几何体根据其顶点对应的cull距离与用户定义的cull半空间进行比较。如果一个几何体的所有顶点相对于任何启用的cull半空间的距离均为负值,则该几何体会被剔除,不会进入后续的渲染流程。
  2. Clipping to Clip Volume

    • 视口视锥体(view volume)是按照特定坐标范围定义的(例如:-wc ≤ xc ≤ wc, -wc ≤ yc ≤ wc, zmin ≤ zc ≤ wc),其中zmin取决于深度裁剪模式(NEGATIVE_ONE_TO_ONE或ZERO_TO_ONE)。
    • 除了视口视锥体之外,还可以结合多达MAX_CLIP_DISTANCES个客户端定义的裁剪半空间来形成最终的裁剪体积(clip volume)。如果没有启用自定义裁剪半空间,那么裁剪体积就是视口视锥体本身。
    • 最后一个顶点处理阶段通过gl_ClipDistance[]数组为每个启用的裁剪半空间写入相应的裁剪距离。当顶点P满足ci§ ≥ 0时,它位于裁剪半空间i内,对于点、线段和三角形,裁剪距离会进行适当的插值计算。
  3. Clipping Process for Different Primitives

    • 对于不同类型的几何体,裁剪过程有不同的处理方式:
      • 点:若点位于近裁剪平面和远裁剪平面之间及所有启用的半空间内,则保留;否则可能被丢弃。此外,实现也可以选择丢弃超出视口视锥体的点。
      • 线段:完全位于裁剪体积内的线段不做处理;完全位于体积外则丢弃;部分位于内外则会被裁剪,并根据裁剪算法计算新的顶点坐标。
      • 多边形:多边形需保证所有边缘都在裁剪体积内才能被完整保留;否则将进行裁剪操作。裁剪后的多边形可能会导致边缘被裁剪,为了保持多边形连接性,会在裁剪边界上添加新的边缘,这可能导致引入新顶点。
  4. Depth Clamping

    • 深度钳制功能通过调用Enable或Disable并指定DEPTH_CLAMP目标来开启或关闭。如果开启深度钳制,系统将在裁剪阶段忽略视锥体关于深度坐标的限制(即不再有明确的近/远裁剪平面)。
  5. Complementarity Criterion and Initial State

    • 使用用户定义半空间裁剪的几何体必须满足互补准则,即无论何时更改裁剪距离符号,都不能导致像素丢失或重复绘制。
    • 初始状态下,裁剪控制原点设为LOWER_LEFT,深度模式默认为NEGATIVE_ONE_TO_ONE,所有用户定义的半空间均未启用。
  6. Implementation Considerations

    • 实现可以灵活地处理输入几何体,即使在某些情况下不改变几何体或者因为实现依赖的原因将一个输入几何体输出为多个裁剪后的几何体,只要保证最终渲染结果不变即可。

裁剪着色器输出 Clipping Shader Outputs

在图形渲染管线中,顶点着色器输出值的裁剪阶段紧随顶点处理阶段之后。对于位于裁剪体积内的顶点,其关联的输出值不受裁剪影响。

然而,如果一个图元(如三角形、线段等)被裁剪了,那么由裁剪产生的新顶点所对应的输出值会受到裁剪的影响。例如:

假设未裁剪边P1和P2上的两个顶点分别具有输出值c1和c2。对于被裁剪点P,我们可以通过计算出的t值,按照以下方式获取与点P相关的输出值c:

c = t c1 + (1 − t) c2

这里的乘法操作意味着每个输出分量x、y、z和w都会与标量t进行相应的乘法运算。

当进行多边形裁剪时,可能会在线框与裁剪体积边界相交的地方创建新的裁剪顶点。这种情况通过观察到多边形裁剪是沿着单个半空间逐一进行的来处理。同样地,输出值的裁剪也按此方式进行,确保裁剪点总是出现在已裁剪或未裁剪的多边形边缘与裁剪体积边界的交点上。

对于最后一级顶点着色器指定不采用透视校正插值(使用noperspective限定符)的输出值,在计算与点P相关联的输出值时,所使用的t值会被调整以保证结果在屏幕空间内呈线性变化。

另外,实现无需支持整型或无符号整型类型的输出值插值,因为所有这类属性必须采用平面着色技术,即同一图元的所有片段共享同一个顶点属性值,而不是在片段间进行插值计算。

图元裁剪查询 Primitive Clipping Queries

,以及。

调用 glBeginQuery,并设置目标如下值时:

  1. CLIPPING_INPUT_PRIMITIVES 查询
    跟踪经过原始图元裁剪阶段处理的图元数量。

  2. CLIPPING_OUTPUT_PRIMITIVES 查询
    通过原始图元裁剪阶段并进一步由光栅化阶段处理的图元数量。对于特定输入图元,实际通过裁剪阶段输出的图元数量依赖于具体实现,但必须满足以下条件:

    • 如果输入图元至少有一个顶点位于裁剪体积内,则计数器至少增加1。
    • 否则,计数器可能增加0或更多。
  3. RASTERIZER_DISCARD 状态的影响
    当启用 GL_RASTERIZER_DISCARD 标志时,实施允许在可选的变换反馈状态之后立即丢弃图元(参见第14.1节)。因此,如果启用了 RASTERIZER_DISCARD,裁剪输入和输出图元的计数器可能不会递增。这意味着在这种情况下,即使图元通过了裁剪阶段,也可能因为光栅化阶段被忽略而不计入相关计数器中。

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

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

相关文章

【Spring和Spring Boot的区别——详细讲解】

Spring和Spring Boot的区别 1. 介绍2. Spring框架3. Spring Boot4. 结论 1. 介绍 Spring和Spring Boot都是现代Java开发中常用的技术和框架,它们之间的关系紧密,Spring Boot是建立在Spring之上的,它简化了Spring应用的创建和开发过程。下面是…

Python中使用opencv-python进行人脸检测

Python中使用opencv-python进行人脸检测 之前写过一篇VC中使用OpenCV进行人脸检测的博客。以数字图像处理中经常使用的lena图像为例,如下图所示: 使用OpenCV进行人脸检测十分简单,OpenCV官网给了一个Python人脸检测的示例程序,…

Backtrader 文档学习- Plotting - Plotting Date Ranges

Backtrader 文档学习- Plotting - Plotting Date Ranges 1.概述 1.9.31.x版本增加了绘制部分图形的功能。 可以使用策略实例中保留完整长度的时间戳数组的索引或者使用实际的datetime.date 或datetime.datetime 实例来限制需要绘制的内容。 仍然可以使用标准的cerebro.plot…

TCP Server工具类,BIO阻塞和非阻塞NIO

启动自定义代码的方式 WebServer Initialized Event //Component//ApplicationContext context 注入//PostConstruct//AsyncEventListener(ApplicationReadyEvent.class)Componentpublic class TcpServerListener implements ApplicationListener<WebServerInitializedEven…

静态时序分析:建立时间分析

静态时序分析https://blog.csdn.net/weixin_45791458/category_12567571.html?spm1001.2014.3001.5482 在静态时序分析中&#xff0c;建立时间检查约束了触发器时钟引脚&#xff08;时钟路径&#xff09;和输入数据引脚&#xff08;数据路径&#xff09;之间的时序关系&#x…

android中实现设备尺寸适配

1、引言 设备尺寸适配的重要性想必就不用我多说了&#xff0c;在我发布的历史文章中我曾谈过Qt中的设备尺寸适配问题&#xff0c;那这里我就来教大家如何在android中做设备尺寸适配。在android中设备尺寸适配的方式有好几种&#xff0c;但我喜欢的还是使用获取设备真实尺寸然后…

MySQL基础查询篇(8)-日期和时间函数的应用

MySQL数据库是一种流行的关系型数据库管理系统&#xff0c;具有强大的日期和时间函数&#xff0c;用于对日期和时间数据进行各种操作和计算。在本篇博客中&#xff0c;我们将介绍MySQL数据库中一些常用的日期和时间函数&#xff0c;并提供详细示例说明其用法。 1. CURDATE()和…

简单介绍算法的基本概念

算法的基本概念 计算机所进行的一切操作都是由程序决定的&#xff0c;程序是由人们事先编好并输入计算机的。 一个程序包括以下两方面的内容: 对数据的描述&#xff1a;在程序中要指定用到哪些数据以及这些数据的类型和数据的组织形式&#xff0c;即数据结构 对操作的描述&a…

c语言游戏实战(4):人生重开模拟器

前言&#xff1a; 人生重开模拟器是前段时间非常火的一个小游戏&#xff0c;接下来我们将一起学习使用c语言写一个简易版的人生重开模拟器。 网页版游戏&#xff1a; 人生重开模拟器 (ytecn.com) 1.实现一个简化版的人生重开模拟器 &#xff08;1&#xff09; 游戏开始的时…

Transformer实战-系列教程10:SwinTransformer 源码解读3(SwinTransformerBlock类)

&#x1f6a9;&#x1f6a9;&#x1f6a9;Transformer实战-系列教程总目录 有任何问题欢迎在下面留言 本篇文章的代码运行界面均在Pycharm中进行 本篇文章配套的代码资源已经上传 点我下载源码 SwinTransformer 算法原理 SwinTransformer 源码解读1&#xff08;项目配置/SwinTr…

PLC在物联网中位置—承上启下,与上位机下位机的关联。

谈到物联网&#xff0c;就绕不开PLC&#xff0c;本文着重介绍PLC的定义、与单片机的区分&#xff0c;价值、物联网中的位置&#xff0c;以及和上位机、下位机的关联&#xff0c;让友友们对PLC有个全面的认知。 一、什么是PLC PLC是可编程逻辑控制器&#xff08;Programmable L…

UI自动刷新大法:DataBinding数据绑定

之前我们讲了DataBinding在Activity、Fragment、RecyclerView中的基础使用&#xff0c;而那些常规使用方法里&#xff0c;每当绑定的变量发生数据变化时&#xff0c;都需要ViewDataBinding重新设值才会刷新对应UI。而DataBinding通过内部实现的观察者模式来进行自动刷新UI&…

go消息队列RabbitMQ - 订阅模式-direct

1.发布订阅 在Fanout模式中&#xff0c;一条消息&#xff0c;会被所有订阅的队列都消费。但是&#xff0c;在某些场景下&#xff0c;我们希望不同的消息被不同的队列消费。这时就要用到Direct类型的Exchange。 在Direct模型下&#xff1a; 队列与交换机的绑定&#xff0c;不能…

第 384 场 LeetCode 周赛题解

A 修改矩阵 模拟 class Solution { public:vector<vector<int>> modifiedMatrix(vector<vector<int>> &matrix) {int m matrix.size(), n matrix[0].size();vector<int> mx(n, INT32_MIN);for (int i 0; i < m; i)for (int j 0; j &l…

Java微服务学习Day1

文章目录 认识微服务服务拆分及远程调用服务拆分服务远程调用提供者与消费者 Eureka注册中心介绍构建EurekaServer注册user-serviceorder-service完成服务拉取 Ribbon负载均衡介绍原理策略饥饿加载 Nacos注册中心介绍配置分级存储负载均衡环境隔离nacos注册中心原理 认识微服务…

ChatGPT学习大纲

引言 在2023年2月份左右开始使用ChatGPT时&#xff0c;就被它强大的理解能力和应答效果所折服&#xff0c;这期间一直在断断续续的学习和使用&#xff0c;也没形成一个完整的学习过程&#xff0c;最近刚好有空&#xff0c;就寻思着好好再学习总结一下&#xff0c;故写出了ChatG…

Python : 使用python实现学生管理系统的功能,详细注释

一、学生管理系统 学生描述&#xff1a;姓名、年龄、成绩 学生管理系统功能&#xff1a;添加学生信息、删除学生信息、根据姓名修改学生信息、根据姓名查询学生信息、显示所有学生信息、退出系统 二、代码说明 1. 将每一个学生的信息放一个元组中&#xff0c;再把元组添加到列表…

java中使用Lambda表达式实现参数化方法

Lambda表达式实现参数化方法说明 Lambda表达式在Java中是一种简洁、函数式的方式来表示匿名函数。它们特别适用于那些需要一个函数作为参数的方法&#xff0c;即函数式接口。参数化方法&#xff08;通常指的是泛型方法&#xff09;是那些可以接受类型参数的方法&#xff0c;这…

2.3 Verilog 数据类型

Verilog 最常用的 2 种数据类型就是线网&#xff08;wire&#xff09;与寄存器&#xff08;reg&#xff09;&#xff0c;其余类型可以理解为这两种数据类型的扩展或辅助。 线网&#xff08;wire&#xff09; wire 类型表示硬件单元之间的物理连线&#xff0c;由其连接的器件输…

单片机基础入门:简单介绍51单片机的工作原理

在电子技术领域&#xff0c;单片机是实现智能化控制不可或缺的关键元件。它们集成了许多功能于一身&#xff0c;成为了各种电子系统的心脏。为了更好地理解单片机如何工作&#xff0c;本文将重点介绍51单片机的基本组成和工作原理。 51单片机是一种广泛使用的微控制器&#xf…