使用shadow2D时的OpenGL暗疮痤疮 [英] Opengl shadow acne when using shadow2D

查看:81
本文介绍了使用shadow2D时的OpenGL暗疮痤疮的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在opengl中实现阴影映射.它可以正常工作,但是可以看到锯齿,因此我决定将sampler2Dshadow与shadow2D一起使用,因为我已将其用作简单的抗锯齿解决方案.但是,一旦我使用它,它会在整个场景中引起非常显着的暗疮痤疮.请注意,将sampler2D与texture2D一起使用时,没有任何东西. 这是故意的吗?如果是这样,我应该如何解决?

这是使用sampler2DShadow时的外观:

这是片段着色器的一部分,可处理阴影:

    float theta = clamp(dot(normalize(lightVector), normalize(vertNormal)), 0, 1);
    float bias = 0.005*tan(acos(theta));
    float visibility = 1.0;
    if ( shadow2D(shadowTex, shadowCoord).z < shadowCoord.z - bias){
        visibility = 0.5;
    }
    color = vec4(color.xyz * visibility, 1);

解决方案

为什么基于灯光和表面之间的角度产生偏差?这就是造成这些怪异的莫尔纹的原因.通常,如果要针对角度对深度进行偏向(例如,将其视为阴影贴图的各向异性偏向),则在创建阴影贴图时将使用glPolygonOffset (...);在着色器中,您将使用恒定偏倚或变化的偏倚.稍有距离(但没有角度).

但是,多边形偏移的问题在于,启用后它将禁用分层Z缓冲(因此,阴影贴图构造填充率理论上会下降).偏移发生在 原语汇编之后.这不是一个完美的解决方案,但是绝对比您现在拥有的更好.

通过稍微偏置W坐标,您也许可以在阴影贴图的顶点着色器中实现基于斜率的偏置,这将允许使用基本装配阶段的结果进行早期Z拒绝...但是将很难找到对W来说物有所值.


...所以我决定将sampler2Dshadow与shadow2D一起使用,因为我已阅读它作为简单的抗锯齿解决方案.

关于sampler2Dsampler2DShadow之间的区别,当用于调用texture (...)(在旧版GLSL中是shadow2D (...) )时,具有纹理的sampler2DShadow启用比较将产生二进制结果-如果通过测试功能,则为 1.0 ,如果失败,则为 0.0 .考虑到您的片段着色器已经执行了比较,并且希望采样器返回的值是深度,而不是您应该使用sampler2D 通过/失败 结果- -在这种情况下,请忽略sampler2DShadow的存在.

             nbsp; b ;;; alt =在此处输入图片描述">

我不知道您使用的是哪个版本的GLSL,因为您使用的是shadow2D (...),该版本早已被弃用.但是,如果您查看GLSL 版本440的规范, pg. 153(8.9-纹理函数),它将使您参考上面列出的表格.

对于阴影形式(sampler参数是阴影类型),按照OpenGL图形系统规范的第8.22节纹理比较模式"中所述,对绑定到采样器的深度纹理进行深度比较查找.请参见下表,其中哪个组件指定了D ref .绑定到采样器的纹理必须是深度纹理,否则结果是不确定的.如果在打开深度比较的情况下对表示深度纹理的采样器进行了非阴影纹理调用,则结果是不确定的.如果在关闭深度比较的情况下对表示深度纹理的采样器进行了阴影纹理调用,则结果不确定.如果对不代表深度纹理的采样器进行阴影纹理调用,则结果是不确定的.

有效地sampler2DShadow会减少混叠,但这只是因为它会产生两个准确的结果: 1.0 0.0 ,并且这些结果的平均值比平均深度更有意义(我将在下面解释).因此,该声明有一定道理,但我认为您已脱离上下文.

实际上,如果要减少阴影混叠,则应该取N个深度测试的平均值.使用线性过滤通常是减少传统纹理中混叠的有效方法,但是对于深度纹理,实际上假定中使用存储在纹理中的值进行比较.如果要使用线性过滤和sampler2D,则GPU会在阴影贴图中收集4个最接近的深度,然后对它们进行平均,然后对平均深度执行一次通过/未通过测试.实际上,这对您没有任何帮助,因为您的片段仍然可以通过或失败.

您要做的是收集4个最接近的深度,分别测试它们,然后平均通过/失败结果.然后,您可能天真地有0%,25%,50%,75%或100%的障碍物,而不是通过或失败的每个碎片-实际上,平均值可以由每个样本到样本坐标的距离加权.这称为更紧密过滤百分比(PCF),如果在某些驱动程序上启用了线性过滤,则shadow2D (...)sampler2DShadow实际上可以做到这一点.他们没有对采样深度进行平均,而是对采样深度进行了测试,然后对所有测试结果进行平均.

实际上是sampler2DShadow的抗锯齿属性的来源,但是您必须正确构造GLSL着色器以利用它.您可以使用带有 NEAREST 过滤器的sampler2D自己轻松(并更方便地)采样4个点,并自己进行测试和取平均值...


此外,请尽量不要阅读深度纹理的z组件.您很幸运,对于GLSL> = 130的版本,采样的深度纹理会产生结果:vec4 (r, r, r, 1.0),但是在较旧的GLSL版本中,其行为取决于称为深度纹理模式的事物. z也可能是 0.0 或完全未定义的pre-GL3.您应该阅读rx,因为深度纹理本质上是单组分的.上表甚至表明了这一点-计算结果存储在r中,而不是在b中.

I'm trying to implement shadow mapping in opengl. It's working, however aliasing is visible, so I decided to use sampler2Dshadow with shadow2D, cause I've read it serves as simple anti-aliasing solution. But as soon as I use it, it causes very significant shadow acne across whole scene. Note that when using sampler2D with texture2D, there's none. Is this intended? If so, how should I solved it?

This is how it looks while using sampler2DShadow:

This is part of fragment shader that handles shadows:

    float theta = clamp(dot(normalize(lightVector), normalize(vertNormal)), 0, 1);
    float bias = 0.005*tan(acos(theta));
    float visibility = 1.0;
    if ( shadow2D(shadowTex, shadowCoord).z < shadowCoord.z - bias){
        visibility = 0.5;
    }
    color = vec4(color.xyz * visibility, 1);

解决方案

Why is your bias based on the angle between the light and surface? That is what is causing these bizarre moiré patterns. Usually if you want to bias the depth against angle (think of it like anisotropic bias for shadow maps) you would use glPolygonOffset (...) when you create the shadow map, and in your shader you would use a constant bias, or a bias that varies slightly with distance (but not angle).

The problem with polygon offset, however, is that it will disable hierarchical Z-Buffering when enabled (so shadow map construction fill-rate will theoretically dip). The offset occurs after primitive assembly. It is not a perfect solution, but definitely better than what you have right now.

You might be able to implement slope based biasing in your shadow map's vertex shader by biasing the W coordinate slightly, this will allow early Z rejection using the results of the primitive assembly stage... but it will be harder to find a good value to use for W.


... so I decided to use sampler2Dshadow with shadow2D, cause I've read it serves as simple anti-aliasing solution.

As for the difference between sampler2D and sampler2DShadow, when used in a call to texture (...) (or shadow2D (...) in older versions of GLSL) a sampler2DShadow that has texture comparison enabled will produce a binary result - 1.0 if it passes the test function, 0.0 if it fails. Considering your fragment shader already performs a comparison and expects the value returned by the sampler to be the depth, and not a pass/fail result you should be using sampler2D -- ignore the existence of sampler2DShadow in this case.

                                      

I have no idea what version of GLSL you are using, since you make use of shadow2D (...), which has long been deprecated. However, if you take a look at the GLSL specification for version 440, pg. 153 (8.9 - Texture Functions) it will refer you to the table I included above.

For shadow forms (the sampler parameter is a shadow-type), a depth comparison lookup on the depth texture bound to sampler is done as described in section 8.22 "Texture Comparison Modes" of the OpenGL Graphics System Specification. See the table below for which component specifies Dref. The texture bound to sampler must be a depth texture, or results are undefined. If a non-shadow texture call is made to a sampler that represents a depth texture with depth comparisons turned on, then results are undefined. If a shadow texture call is made to a sampler that represents a depth texture with depth comparisons turned off, then results are undefined. If a shadow texture call is made to a sampler that does not represent a depth texture, then results are undefined.

Effectively sampler2DShadow will reduce aliasing, but only because it produces exactly two results: 1.0 and 0.0 and the average of these results is more meaningful than the average depth (as I will explain below). So there is some truth to that statement, but you have taken it out of context I believe.

In reality, if you want to reduce shadow aliasing what you ought to do is take the average of N-many depth tests. Using linear filtering is usually a valid approach to reduce aliasing in traditional textures, but with depth textures the value stored in the texture is actually supposed to be used for comparison purposes. If you were to use linear filtering and sampler2D, the GPU would gather the 4 nearest depths in the shadow map, average them and then you would perform a single pass/fail test on the averaged depth. This actually gets you nothing useful because your fragments still either pass or fail.

What you want to do is gather the 4 nearest depths, test them individually and then average the pass/fail result. Then instead of each fragment either passing or failing, you may naively have 0%, 25%, 50%, 75% or 100% obstruction -- in fact, the average may be weighted by distance of each sample from the sample coordinate. This is known as Percentage Closer Filtering (PCF), shadow2D (...) and sampler2DShadow actually do exactly this if you enable linear filtering on some drivers. Instead of averaging sampled depths, they test the sampled depths and then average the results of all tests.

This is actually where the anti-aliasing properties of sampler2DShadow come from, but you must structure your GLSL shader properly to take advantage of it. You can just as easily (and more portably) sample 4 points yourself using a sampler2D with a NEAREST filter and perform the testing and averaging yourself...


Also, try not to read the z component of a depth texture. You are lucky that for versions of GLSL >= 130, a sampled depth texture produces the result: vec4 (r, r, r, 1.0), but in older versions of GLSL the behavior depends on something called a depth texture mode. It is equally possible for z to be 0.0, or completely undefined pre-GL3. You should read r or x, since depth textures are single-component by nature. The table above even indicates this - the computed result is stored in r, not b.

这篇关于使用shadow2D时的OpenGL暗疮痤疮的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆