【OpenGL_06】深度测试、模板测试、混合

LearnOpenGL网站学习,记录一下学习笔记

深度测试

深度测试

Z-Buffer相关知识在博客【Notes_4】现代图形学入门——光栅化、离散化三角形、深度测试与抗锯齿

z坐标是指向屏幕里面为正,z值越大,表示物体越靠近里面。

深度缓冲是在片段着色器运行之后,在屏幕空间中运行的。屏幕空间坐标与通过OpenGL的glViewport所定义的视口密切相关。屏幕空间坐标可以使用gl_FragCoord从片段着色器直接访问,glFragCoord的x和y分量代表了片段的屏幕空间坐标(原点位于左下角),z分量包含了片段真正的深度值,也就是要与深度缓冲内容所对比的那个值。

每次使用GL_DEPTH_BUFFER_BIT来清除深度缓冲,这个过程在着色器程序之前运行,清空深度缓冲,为了这一次的渲染的深度测试写入做准备。

深度测试函数

深度值精度

深度值实际上是一个范围为[0.0,1.0]的值。所以我们将片元的z值变换到[0.0,1.0]之间。有两种变换函数:线性和非线性。根据实际效果,我们使用非线性变换函数

线性深度缓冲

far与near:是锥视体的前平面和后平面z值坐标

随着z值变换,深度值的变换:无论z值大小,也就是物体距离相机的远近,都具有相同的精度

非线性深度缓冲

在1.0和2.0之间的z值将会变换至1.0到0.5之间的深度值,这就是一个float提供给我们的一半精度了,这在z值很小的情况下提供了非常大的精度。在50.0和100.0之间的z值将会只占2%的float精度,这正是我们所需要的。

非线性转化为线性深度缓冲

数学原理在博客OpenGL Projection Matrix

深度冲突

一个很常见的视觉错误会在两个平面或者三角形非常紧密地平行排列在一起时会发生,深度缓冲没有足够的精度来决定两个形状哪个在前面。结果就是这两个形状不断地在切换前后顺序,这会导致很奇怪的花纹。这个现象叫做深度冲突(Z-fighting),因为它看起来像是这两个形状在争夺(Fight)谁该处于顶端。

防止深度冲突

1. 第一个也是最重要的技巧是永远不要把多个物体摆得太靠近,以至于它们的一些三角形会重叠。通过在两个物体之间设置一个用户无法注意到的偏移值,你可以完全避免这两个物体之间的深度冲突。

2. 第二个技巧是尽可能将近平面设置远一些。

3. 另外一个很好的技巧是牺牲一些性能,使用更高精度的深度缓冲。

最后,组合使用了上面列举出来的技术,可能不会再需要处理深度冲突了

模板测试

当片段着色器处理完一个片段之后,模板测试(Stencil Test)会开始执行,和深度测试一样,它也可能会丢弃片段。接下来,被保留的片段会进入深度测试,它可能会丢弃更多的片段。模板测试是根据又一个缓冲来进行的,它叫做模板缓冲(Stencil Buffer),我们可以在渲染的时候更新它来获得一些很有意思的效果。

模板缓冲

一个模板缓冲中,(通常)每个模板值(Stencil Value)是8位的。所以每个像素/片段一共能有256种不同的模板值。我们可以将这些模板值设置为我们想要的值,然后当某一个片段有某一个模板值的时候,我们就可以选择丢弃或是保留这个片段了。

模板缓冲操作允许我们在渲染片段时将模板缓冲设定为一个特定的值。通过在渲染时修改模板缓冲的内容,我们写入了模板缓冲。在同一个(或者接下来的)渲染迭代中,我们可以读取这些值,来决定丢弃还是保留某个片段。使用模板缓冲的时候你可以尽情发挥,但大体的步骤如下:

∙ \bullet 启用模板写入

∙ \bullet 渲染物体,更新模板缓冲的内容。
这个过程分为三个步骤:
1. 每次缓冲前清楚模板缓冲
2. 设置模板函数
3. 渲染物体,同时会自动更新模板缓冲。
(1)第一步:每次缓冲前清楚模板缓冲

模板函数

和深度测试一样,我们对模板缓冲应该通过还是失败,以及它应该如何影响模板缓冲,也是有一定控制的。一共有两个函数能够用来配置模板测试:glStencilFunc和glStencilOp。

(2)(3):第二、三步:设置模板函数。
渲染物体,同时会自动更新模板缓冲。

∙ \bullet 禁用模板缓冲的写入。

∙ \bullet 渲染(其它)物体,这次根据模板缓冲的内容丢弃特定的片段。

物体轮廓

上述简单介绍了模板测试的一个应用流程的大致步骤,接下来会应用上述流程绘制正方体的轮廓
先上全部代码,之后在对代码各个部分进行逐个分析。

glEnable(GL_DEPTH_TEST);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);  

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); 

glStencilMask(0x00); // 记得保证我们在绘制地板的时候不会更新模板缓冲
normalShader.use();
DrawFloor()  

glStencilFunc(GL_ALWAYS, 1, 0xFF); 
glStencilMask(0xFF); 
DrawTwoContainers();

glStencilFunc(GL_NOTEQUAL, 1, 0xFF);
glStencilMask(0x00); 
glDisable(GL_DEPTH_TEST);
shaderSingleColor.use();    //绘制放大的正方体
DrawTwoScaledUpContainers();
glStencilMask(0xFF);
glEnable(GL_DEPTH_TEST);  

上述代码的作用:先绘制正方体,并且将正方体的所有片段存储到模板中,设置为1。之后将正方体放大,在绘制模板过程中不为1的片段,这样下来,就会只绘制正方体放大部分,也就是正方体的边缘轮廓部分。

混合

透明的物体可以是完全透明的(让所有的颜色穿过),或者是半透明的(它让颜色通过,同时也会显示自身的颜色)。一个物体的透明度是通过它颜色的aplha值来决定的。Alpha颜色值是颜色向量的第四个分量。

丢弃片段

丢弃片段主要是应用在这样的纹理中——完全透明(Alpha=0.0)和完全不透明(Alpha=1.0)的组成部分。
丢且片段操作在片元着色器中,GLSL语言的discord命令一旦被调用,这一部分的片元不会被处理,不会进入颜色缓冲。

#version 330 core
out vec4 FragColor;

in vec2 TexCoords;

uniform sampler2D texture1;

void main()
{             
    vec4 texColor = texture(texture1, TexCoords);
    if(texColor.a < 0.1)
        discard;
    FragColor = texColor;
}

这样处理的作用是:当某片段Alpha值小于0.1,这部分片段会被丢且。最后的渲染效果

这会出现一个问题:就是在纹理边缘部分,OpenGL会对Alpha进行插值(当我们设置纹理环绕方式为GL_REPEAT时),最后的效果就是有纹理映射的有一个半透明的有色边框。
解决这个,需要将纹理设置方式为:GL_CLAMP_TO_EDGE

混合

片段着色器运行完成后,并且所有的测试都通过之后,这个混合方程(Blend Equation)才会应用到片段颜色输出与当前颜色缓冲中的值(当前片段之前储存的之前片段的颜色)上。源颜色和目标颜色将会由OpenGL自动设定,但源因子和目标因子的值可以由我们来决定:

常见的混合因子设置:

混合多个物体

因为深度测试的原因,绘制两个半透明窗户,前一个窗户会将后一个窗户完全遮挡,也就是说后一个窗户重叠的部分被抛弃掉了

解决方法

1、先绘制所有不透明的物体。

2、对所有透明的物体排序。

3、按顺序绘制所有透明的物体

第二部的具体操作:
排序透明物体的一种方法是,从观察者视角获取物体的距离。这可以通过计算摄像机位置向量和物体的位置向量之间的距离所获得。接下来我们把距离和它对应的位置向量存储到一个STL库的map数据结构中。map会自动根据键值(Key)对它的值排序,所以只要我们添加了所有的位置,并以它的距离作为键,它们就会自动根据距离值排序了。

std::map<float, glm::vec3> sorted;
for (unsigned int i = 0; i < windows.size(); i++)
{
    float distance = glm::length(camera.Position - windows[i]);
    sorted[distance] = windows[i];
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值