Logarithmic zbuffer artifacts fix

In cameni's Journal of Lethargic Programmers, I've been very interested by his idea about using a logarithmic zbuffer.

Unfortunately, his idea comes with a couple of very annoying artifacts, due to the linear interpolation of the logarithm (non-linear) based formula. It particularly shows on thin or huge triangles where one or more vertices fall off the edges of the screen. As cameni explains himself in his journal, basically for negative Z values, the triangles tend to pop in/out randomly.

It was suggested to keep a high tesselation of the scene to avoid the problem, or to use geometry shaders to automatically tesselate the geometry.

I'm proposing a solution that is much more simple and that works on pixel shaders 2.0+: simply generate the correct Z value at the pixel shader level.
我提出了一种简单得多并且在像素着色器(pixel shaders)2.0+上面能运行的解决方法:只需要在像素渲染阶段生成正确的Z值即可。

In the vertex shader, just use an interpolator to pass the vertex position in clip space (GLSL) (here I'm using tex coord interpolator#6):

void main()
  vec4 vertexPosClip = gl_ModelViewProjectionMatrix * gl_Vertex;
  gl_Position = vertexPosClip;
  gl_TexCoord[6] = vertexPosClip;

Then you override the depth value in the pixel shader:

void main()
  gl_FragColor = ...
  const float C = 1.0;
  const float far = 1000000000.0;
  const float offset = 1.0;
  gl_FragDepth = (log(C * gl_TexCoord[6].z + offset) / log(C * far + offset));

Note that as cameni indicated before, the 1/log(C*far+1.0) can be optimized as a constant. You're only really paying the price for a mad and a log.

Quality-wise, I've found that solution to work perfectly: no artifacts at all. In fact, I went so far as testing a city with centimeter to meter details seen from thousands of kilometers away using a very very small field-of-view to simulate zooming. I'm amazed by the quality I got. It's almost magical. ZBuffer precision problems will become a thing of the past, even when using large scales such as needed for a planetary engine.

There's a performance hit due to the fact that fast-Z is disabled, but to be honnest in my tests I haven't seen a difference in the framerate. Plus, tesselating the scene more or using geometry shaders would very likely cost even more performance than that.

I've also found that to control the znear clipping and reduce/remove it, you simply have to adjust the "offset" constant in the code above. Cameni used a value of 1.0, but with a value of 2.0 in my setup scene, it moved the znear clipping to a few centimeters.
我同时还发现为控制znear clipping对它的拉近/删除,你只需要在以上代码中调整“offset”常量。卡梅尼使用了1.0这个值,但是我建立场景时使用的2.0这个值,结果它把znear clipping移动了几厘米。

Settings of the test:
- znear = 1.0 inch
- zfar = 39370.0 * 100000.0 inches = 100K kilometers
- camera is at 205 kilometers from the scene and uses a field-of-view of 0.01°
- zbuffer = 24 bits

Normal zbuffer:

Logarithmic zbuffer:

Future works
    Could that trick be used to increase precision of shadow maps?

原文链接:http://www.infinity-universe.com ... 5&topic=11849.0

个人分类: 图形图像技术
想对作者说点什么? 我来说一句


2011年05月16日 3.57MB 下载


2009年06月10日 560KB 下载