图形管线(一)后处理阶段 alpha测试 模版测试 深度测试 混合

本文详细介绍了图形管线中的关键步骤,包括像素所有权测试,用于确定OpenGL上下文对帧缓冲区像素的所有权;裁剪测试,用于在指定矩形区域内渲染;透明度测试,用于处理透明效果;模板测试,用于基于模板缓冲区进行像素操作;以及深度测试,确保正确的深度顺序。这些测试和操作在实时渲染中至关重要,尤其在游戏和图形应用中。

图形管线是当前两大渲染(光追与光栅化)中的一种,也是图形学渲染的必备!主要是实时渲染,工作中或者面试经常用到或者问道,所以本人今天来梳理总结一番!
光栅化一般分为 三大阶段,应用程序阶段->几何阶段->光栅化阶段,也可以四大阶段 应用程序阶段->几何阶段->光栅化阶段->后处理阶段
在这里插入图片描述

后处理阶段

像素所有权测试→裁剪测试→透明度测试→模板测试→深度测试→透明度混合
在这里插入图片描述

像素所有权测试

当顶点着色器完成它的工作,然后进入原始装配阶段。在 Primitive Assembly 阶段,裁剪正在进行中。然后在几个阶段之后是 Fragment Shader,然后是 Per-Fragment 操作。第一个操作是像素所有权测试
像素所有权测试确定当前 OpenGL 上下文是否拥有对应于特定片段的帧缓冲区中的像素。 如果是,则片段将转到下一个测试。 否则,窗口系统确定是否丢弃片段,或者是否使用该片段执行任何进一步的片段操作。 在此测试中,窗口系统控制行为,例如,OpenGL 窗口被遮盖。

It is not the purpose of the pixel ownership to clip away primitives outside your window/viewport. 像素所有权的目的不是剪掉窗口/视口外的基元。 According to the pipeline definition, that does indeed happen between vertex and fragment shader, around the time primitive assembly and rasterization are performed. 根据管道定义,确实在顶点和片段着色器之间发生,在时间原始组件和光栅化的周围执行The pixel ownership test is used to eliminate pixels that are basically within your window rectangle, but are not visible on the screen. 像素所有权测试用于消除基本在窗口矩形内但在屏幕上不可见的像素。 Common scenarios where this can happen is if your window is partially covered by another window, or your window extends beyond the edge of the display. 出现这种情况的常见情况是,如果您的窗口被另一个窗口部分覆盖,或者您的窗口超出了显示器的边缘。The pixel ownership test only really comes into play if the application renders directly to the framebuffer. 如果应用程序直接渲染到帧缓冲区,则像素所有权测试才真正发挥作用。 I don’t think that’s common anymore with modern window systems. 我认为现代窗户系统不再常见了。 Your application will typically render to an off-screen buffer, and then there’s a compositing step that combines the windows according to their relative positions, stacking order, etc. The only time you still get to render directly to the framebuffer is in full screen mode. 您的应用程序通常会渲染到屏幕外缓冲区,然后有一个合成步骤,根据窗口的相对位置,堆叠顺序等组合窗口。您仍然可以直接渲染到帧缓冲区的全屏模式。Another aspect to keep in mind is that the spec describes how an OpenGL implementation behaves. 要记住的另一个方面是规范描述了OpenGL实现的行为方式。 This does not mean that each implementation actually has to perform the various steps in exactly the order described in the spec. 这并不意味着每个实现实际上都必须按照规范中描述的顺序执行各个步骤。 As long as it behaves as if it performed the steps in that order, it is still compliant. 只要它表现得像按顺序执行步骤一样,它仍然是合规的。 For example, if an implementation can perform the pixel ownership test before the fragment shader, it is at complete liberty to do that, as long as the final result does not change. 例如,如果实现可以在片段着色器之前执行像素所有权测试,那么只要最终结果不会改变,它就可以完全自由地执行。 It is in fact very common to perform some of the tests in the “per-fragment operations” section before the fragment shader for improved efficiency. 事实上,在片段着色器之前的“每片段操作”部分中执行一些测试以提高效率是很常见的。

裁剪测试

测试指定一个任意屏幕对齐的矩形,在该矩形之外将丢弃片段。在 OpenGL 中启用裁剪测试可以在屏幕或者帧缓冲上指定一个矩形区域,然后在该矩形区域内绘制,只有在该区域内的片元才有机会最终进入帧缓冲,不在该区域内的将会被丢弃,裁剪测试的效果就相当于在屏幕上开辟一个矩形区域,在该区域内再单独绘制内容。
可以通过下面的代码来启用或禁用剪裁测试:

glEnable(GL_SCISSOR_TEST);             // 启用剪裁测试
glDisable(GL_SCISSOR_TEST);             // 禁用剪裁测试

```cpp
// 开启裁剪测试        GLES20.glEnable(GLES20.GL_SCISSOR_TEST)
        // 指定开辟的矩形区域
        GLES20.glScissor(x,y,width,height)
        // 清除该区域内的颜色
        GLES20.glClearColor(1f, 0f, 0f, 1f)
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT or GLES20.GL_DEPTH_BUFFER_BIT)
        // 在矩形局域内绘制内容
        mCube.onDrawFrame(gl)
        // 关闭裁剪测试
        GLES20.glDisable(GLES20.GL_SCISSOR_TEST)


原型:

```cpp
void WINAPI glScissor(  GLint  x,
  GLint  y,
  GLsizei width,
  GLsizei height
);
glScissor(10, 10, this->geometry().width()- 20,this->geometry().height()- 20);

透明度测试

透明测试针对的是透明度,要么通过及不透明,不通过就是透明,要达到半透明的效果必须使用blend混合来实现!

glEnable(GL_ALPHA_TEST);   // 启用Alpha测试
glDisable(GL_ALPHA_TEST); // 禁用Alpha测试

可以通过下面的代码来设置Alpha测试条件为“大于0.5则通过”:glAlphaFunc(GL_GREATER, 0.5f);
该函数的第二个参数表示设定值,用于进行比较。第一个参数是比较方式,除了GL_LESS(小于则通过)外,还可以选择:
GL_ALWAYS(始终通过), GL_NEVER(始终不通过), GL_LESS(小于则通过), GL_LEQUAL(小于等于则通过), GL_EQUAL(等于则通过), GL_GEQUAL(大于等于则通过), GL_NOTEQUAL(不等于则通过)。
.

模板测试

glStencilFunc(GLenum func, GLint ref, GLuint mask)一共包含三个参数:
模板缓冲区可以为屏幕上的每一个像素点保存一个无符号整数值(通常为8位int 0-255)

  • func:设置模板测试函数(Stencil Test
    Function)。这个测试函数将会应用到已储存的模板值上和glStencilFunc函数的ref值上。可用的选项有:GL_NEVER、GL_LESS、GL_LEQUAL、GL_GREATER、GL_GEQUAL、GL_EQUAL、GL_NOTEQUAL和GL_ALWAYS。它们的语义和深度缓冲的函数类似。
  • ref:设置了模板测试的参考值(Reference Value)。模板缓冲的内容将会与这个值进行比较。
  • mask:设置一个掩码,它将会与参考值和储存的模板值在测试比较它们之前进行与(AND)运算。初始情况下所有位都为1。

glStencilOp(GLenum sfail, GLenum dpfail, GLenum dppass)一共包含三个选项,我们能够设定每个选项应该采取的行为:

  • sfail:模板测试失败时采取的行为。
  • dpfail:模板测试通过,但深度测试失败时采取的行为。
  • dppass:模板测试和深度测试都通过时采取的行为。

深度测试

深度测试也可以做剔除,比如我们旧引擎就是模板测试支持不太友好,才使用深度贴图做mask~
在这里插入图片描述

深度写入包括两种状态
Zbuffer 、Z-fighting之前都写过来在参考资料~

  • ZWrite OnZWrite Off
  • 当我们开启深度写入,物体被渲染时针对物体在屏幕(FrameBuffer)上每个像素的深度都写入到深度缓冲区。
  • 关闭深度写入状态,物体的深度就不会写入深度缓冲区。

除了ZWrite的是否写入深度缓冲区,更重要的是:是否通过深度测试,也就是Z-Test

  • 如果Z-Test都没通过,也就不会写入深度了。
  • 也就是说,只有ZTest和ZWrite都可行的情况下才写入深度缓冲区

综上,ZWrite有On、Off两种情况;ZTest有通过、不通过两种情况,两者结合的四种情况如下:
在这里插入图片描述
上图案例讲解看参考资料中的《技术美术百人计划》

混合测试

之前我就讲过相关透明在参考资料中所以就在这里简单说一下就好啦~
开启混合:

glEnable(GL_BLEND)

;OpenGL中的混合是通过下面这个方程来实现的:

  1. [Math Processing Error]
    C¯result=C¯source∗Fsource+C¯destination∗Fdestination
  2. [Math Processing Error]C¯source:源颜色向量。这是源自纹理的颜色向量。
  3. [Math Processing Error]C¯destination:目标颜色向量。这是当前储存在颜色缓冲中的颜色向量。
  4. [Math Processing Error]Fsource:源因子值。指定了alpha值对源颜色的影响。
  5. [Math Processing Error]Fdestination:目标因子值。指定了alpha值对目标颜色的影响。

在这里插入图片描述

glBlendFunc(GLenum sfactor, GLenum dfactor)函数接受两个参数,来设置源和目标因子。
在这里插入图片描述

 glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE,   GL_ZERO);

glBlendEquation(GLenum mode)允许我们设置运算符,它提供了三个选项:

  1. GL_FUNC_ADD:默认选项,将两个分量相加:[Math Processing Error]C¯result=Src+Dst。
  2. GL_FUNC_SUBTRACT:将两个分量相减: [Math Processing Error]C¯result=Src−Dst。
  3. GL_FUNC_REVERSE_SUBTRACT:将两个分量相减,但顺序相反:[Math Processing
    Error]C¯result=Dst−Src。

比如用混合做 遮罩

白色区域mask=1, srcColor * mask = 原图
黑色区域mask=0, srcColor * mask = 纯黑

参考资料

透明的渲染
Z-Fighting问题解决方案实例
模板测试
【技术美术百人计划】图形 3.1 深度与模板测试 传送门效果示例
【技术美术百人计划】图形 3.5 Early-z和Z-prepass
裁剪测试
glScissor 函数

<think>根据用户的问题"我想了解OpenGL中先进行深度测试再绘制指定层的原因",结合用户提供的引用资料和之前的对话历史,我将逐步解释这个问题。用户的问题核心在于理解为什么在OpenGL渲染中要先进行深度测试,再绘制特定层次(比如透明物体)。我将从以下几个方面展开解释:1.**深度测试的基本概念**:-深度测试用于确定哪些片段应该被渲染到屏幕上。通过比较当前片段的深度值(z值)和深度缓冲区中存储的值,决定是否丢弃该片段。-引用[4]明确指出:“当深度测试被启用时,OpenGL会将个片段的深度值与深度缓冲的内容进行对比。如果测试通过,深度缓冲会更新为新的深度值;如果失败,片段会被丢弃。”2.**深度测试与绘制顺序的关系**:-在渲染不透明物体时,我们通常启用深度测试(`GL_DEPTH_TEST`)并开启深度写入(`glDepthMask(GL_TRUE)`),同时按照从近到远的顺序绘制物体(或使用深度缓冲自动处理遮挡关系)。-但透明物体需要特殊处理:由于透明效果依赖于混合(Blending),而混合要求按从远到近的顺序绘制,这与不透明物体的绘制顺序矛盾。3.**为什么要先进行深度测试再绘制透明物体**:-引用[5]中提到透明对象显示异常的原因是“没有进行正确的绘制排序(从远到近)”,以及不透明物体被透明对象遮挡是因为“绘制顺序错误,应先绘制不透明对象”。-具体步骤如下:-**第步**:先绘制所有不透明物体,并启用深度测试深度写入。这样深度缓冲区会正确记录最近的不透明片段深度值。-**第二步**:禁用深度写入(`glDepthMask(GL_FALSE)`),但保持深度测试启用。然后按从远到近的顺序绘制透明物体。-**为什么禁用深度写入**:如果透明物体更新了深度缓冲,会导致后续更近的透明物体被错误丢弃。引用[3]说明了如何通过`glDepthMask(GL_FALSE)`禁用深度写入。-**为什么保持深度测试**:确保透明物体只出现在不透明物体前面(深度测试通过与不透明物体的深度对比),避免穿透现象。-**第三步**:绘制完成后恢复深度写入状态。4.**混合功能的作用**:-绘制透明物体时需启用混合(`GL_BLEND`)并设置混合函数,如`glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA)`。这使得当前片段(源)的颜色与已有颜色(目标)按透明混合。-这过程依赖于正确的深度测试结果:只有通过深度测试的片段才会进入混合阶段。5.**性能优化考虑**:-先绘制不透明物体可以利用深度测试的片段丢弃机制(Early-Z),避免对不可见面执行片段着色器计算,提升渲染效率。**总结原因**:>先进行深度测试(针对不透明物体)再绘制透明物体,是为了解决以下问题:>1.**深度遮挡正确性**:确保透明物体不会错误地遮挡不透明物体[^5]。>2.**混合顺序要求**:透明物体必须按从远到近混合,而深度测试提供了基础遮挡关系。>3.**深度写入冲突**:通过禁用透明物体的深度写入,避免破坏深度缓冲区的数据。下面我会按照系统要求的结构化格式组织答案,包含必要的数学公式和代码示例。</think> ### OpenGL中先深度测试再绘制指定层的原因分析 在OpenGL渲染流程中,**先进行深度测试再绘制特定层次(如透明物体)** 的排序策略主要基于以下核心原因: #### 1. 深度测试的优先级机制 - **深度缓冲区的工作原理**:深度测试默认按$z-buffer$算法比较片段深度值,公式为: $$z_{\text{new}} < z_{\text{buffer}} \Rightarrow \text{通过测试}$$ - **渲染顺序依赖**:开启深度测试时,后续绘制的片段会根据深度值自动丢弃或被覆盖(通过`glDepthFunc()`设置比较规则)[^4] - **不透明物体的处理**:对不透明物体采用**从近到远**绘制顺序时: - 近距离物体会覆盖远距离物体 - 深度测试失败时自动丢弃被遮挡片段 - 深度缓冲区持续更新为最近深度值 #### 2. 透明物体的特殊要求 - **混合功能依赖**:透明物体需要启用混合(`GL_BLEND`)和特定混合函数(如`glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)`) - **混合运算的特性**: $$C_{\text{result}} = C_{\text{源}} \times \alpha + C_{\text{目标}} \times (1 - \alpha)$$ 该公式要求按**从远到近**顺序混合才能获得正确视觉效果 - **深度写入冲突**:若透明物体更新深度缓冲区,会导致后续更近的透明物体被错误丢弃[^3] #### 3. 合理渲染管线流程 ```mermaid graph TD A[清空深度缓冲区] --> B[绘制不透明物体<br/>1. 深度测试开启<br/>2. 深度写入开启] B --> C[禁用深度写入<br/>glDepthMask<GL_FALSE>] C --> D[按从远到近顺序<br/>绘制透明物体<br/>1. 深度测试保持开启<br/>2. 混合启用] ``` 此流程的核心优势: 1. **确保遮挡正确性**:不透明物体完整写入深度缓冲后,透明物体的深度测试才能获得正确参考值 2. **避免视觉错误**:防止透明物体错误覆盖不透明物体(如引用[5]提到的遮挡问题) 3. **优化性能**:深度测试通过Early-Z机制提前丢弃被遮挡片段(模板测试深度测试前执行[^2]) > **关键实践**:如引用[5]指出,应明确区分渲染阶段: > - 先绘制所有不透明物体(深度测试+深度写入开启) > - 再绘制透明物体(深度测试开启+深度写入关闭+混合开启)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值