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,裁剪输入和输出图元的计数器可能不会递增。这意味着在这种情况下,即使图元通过了裁剪阶段,也可能因为光栅化阶段被忽略而不计入相关计数器中。

你可能感兴趣的:(OpenGL,图形渲染)