图形管线是当前两大渲染(光追与光栅化)中的一种,也是图形学渲染的必备!主要是实时渲染,工作中或者面试经常用到或者问道,所以本人今天来梳理总结一番!
光栅化一般分为 三大阶段,应用程序阶段->几何阶段->光栅化阶段,也可以四大阶段 应用程序阶段->几何阶段->光栅化阶段->后处理阶段
像素所有权测试→裁剪测试→透明度测试→模板测试→深度测试→透明度混合
当顶点着色器完成它的工作,然后进入原始装配阶段。在 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)
glStencilOp(GLenum sfail, GLenum dpfail, GLenum dppass)一共包含三个选项,我们能够设定每个选项应该采取的行为:
深度测试也可以做剔除,比如我们旧引擎就是模板测试支持不太友好,才使用深度贴图做mask~
深度写入包括两种状态
Zbuffer 、Z-fighting之前都写过来在参考资料~
除了ZWrite的是否写入深度缓冲区,更重要的是:是否通过深度测试,也就是Z-Test
综上,ZWrite有On、Off两种情况;ZTest有通过、不通过两种情况,两者结合的四种情况如下:
上图案例讲解看参考资料中的《技术美术百人计划》
之前我就讲过相关透明在参考资料中所以就在这里简单说一下就好啦~
开启混合:
glEnable(GL_BLEND)
;OpenGL中的混合是通过下面这个方程来实现的:
glBlendFunc(GLenum sfactor, GLenum dfactor)函数接受两个参数,来设置源和目标因子。
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
glBlendEquation(GLenum mode)
允许我们设置运算符,它提供了三个选项:
比如用混合做 遮罩
白色区域mask=1, srcColor * mask = 原图
黑色区域mask=0, srcColor * mask = 纯黑
透明的渲染
Z-Fighting问题解决方案实例
模板测试
【技术美术百人计划】图形 3.1 深度与模板测试 传送门效果示例
【技术美术百人计划】图形 3.5 Early-z和Z-prepass
裁剪测试
glScissor 函数