washingtondc-emu / washingtondc

Open-source Sega Dreamcast emulator
http://www.washemu.org
GNU General Public License v3.0
241 stars 7 forks source link

tracking issue for games with depth precision bugs #90

Open snickerbockers opened 3 years ago

snickerbockers commented 3 years ago

commit 29b5b9dca14f468859bfbf00b30d1f985c56d9ce eliminated the perspective divide and fixed most of WashingtonDC's problems with depth precision but there are still a few problematic cases. Ironically, some of these cases were actually fixed by the perspective divide.

snickerbockers commented 3 years ago

SoulCalibur

Large polygons briefly obscure all or part of the screen at a few points during the intro. This happens for the gl renderers, but not soft_gfx so that means it's probably a depth buffer-related bug. This game is known to render vertices with near-infinite 1/z values so it's possibly related to that.

2020-10-28-12-58-34 2020-10-28-12-58-39

snickerbockers commented 3 years ago

Sonic Adventure

Missing polygons in menu. This happens in both the character select menu and the options menu. In the character select menu they will flash, but in the options menu the missing polygons are never visible. This is a classic z-fighting bug, as the differences in 1/z of different vertices in these menus is extremely, extremely tiny. mapping depth range to -1..1 introduces enough imprecision to over-ride these small differences between polygons even if we don't remap these depth ranges to -1..1, the distance between some of these z-values is still too small for OpenGL's 24-bit fixed point depth values. Also this game doesn't disable depth writes when drawing the background in menus, so the menu polygons aren't just z-fighting with each other but also the background.

2020-10-28-13-03-57 2020-10-28-13-04-06

snickerbockers commented 3 years ago

regarding the z-fighting bug in Sonic Adventure, one thought I've had is that since the 1/z coordinate is only used for depth testing and not projection, we can freely translate vertices back-and-forth along the 1/z axis so long as all vertices in a given polygon are translated by the same amount (so not to interfere with interpolation), and the relationship between different polygons is preserved (ie no polygon is translated so far that it is in front of or behind a polygon it was not originally in front of or behind, and all polygons which intersect are translated by the same amount). Effectively, the emulator would be performing the depth test in software based on its own rules so that we don't have to shoehorn Dreamcast's 1/z buffer into OpenGL's paradigm.

One obvious weakness with this approach is that in a sufficiently complex scene with many polygons intersecting each other, it may be very difficult or impossible to pull the polygons apart in a way that does not effect how the scene. Another obvious weakness is that this would force us to separate triangle strips into individual triangles (this is something WashingtonDC already does for the sake of convenience but I had always hoped to be able to fix that some day).

As for determining when polygons do or do not intersect with each other, a simple off-screen software-rasterizer without shading or texturing could be used. I have read that some modern game engines actually implement occlusion queries in this way because they don't want to introduce synchronization between CPU and GPU. WashingtonDC could do the same thing.

Even if we implement high-resolution rendering, it ought to still be safe to use the native resolution of the program for these occlusion queries, so this approach would scale well.

snickerbockers commented 3 years ago

Actually, it looks like I don't need anything that complicated, using GL_DEPTH_COMPONENT32F seems to get me the precision I need when used in conjunction with glClipControl(GL_ZERO_TO_ONE)

snickerbockers commented 3 years ago

one thought I've had is that since the 1/z coordinate is only used for depth testing and not projection, we can freely translate vertices back-and-forth along the 1/z axis so long as all vertices in a given polygon are translated by the same amount

Without saying too much, I have determined that there is at least one major closed-source dreamcast emulator that actually does this and it gets very good results even on 24-bit fixed-point depth buffer. Not sure how they come up with the adjusted depth values or how they handle intersecting geometry (if at all), but they're definitely adjusting 1/z for sake of working around OpenGL implementations that have worse depth precision than the Dreamcast.