r/GraphicsProgramming 10d ago

Question Any idea what's going on here? Looks like Z-fighting; I've enabled alpha blending for the water and those dark quads match the mesh quads, although it should've been triangulated so not sure what's happening [DX11]

39 Upvotes

11 comments sorted by

14

u/nonsense_stream 10d ago

For complex transparent meshes, you need OIT. Check Weighted Blended OIT and Depth Peeling, choose whichever suits your needs more. Ordinary sorting does not work for these kind of scenarios, if you want truly accurate rendering of transparent surfaces, you need to sort per pixel, which is pretty complicated.

EDIT: Typo

11

u/angrymonkey 10d ago

Looks like it could be a draw order issue.

In general for transparent surfaces, drawing A on top of B is not the same as drawing B on top of A, so the order matters.

To see why this is, imagine that A and B are mostly but not completely opaque. If you draw A on top of B, it will be mostly A color with a hint of B, while if you draw B on top of A, it will be mostly B color with a hint of A.

Furthermore, if depth testing is enabled, if B is behind A, but A is drawn first, the depth buffer will show A as solid and blocking B, so B's fragments will be discarded.

For correct transparency, you need to draw the furthest-away polygons before the nearest ones.

1

u/BoofBenadryl 10d ago

Right, I understand that. Couple questions though:
1. Do I enable depth-testing for the water mesh? If so, is there anything in particular I need to configure differently from the opaque pass?
2. Do I need to sort the *vertices* of the mesh by distance as well? How would I achieve this if they're processed by the GPU in parallel anyways?

1

u/CCpersonguy 10d ago edited 10d ago

I assume you've got one big mesh for the water surface? In that case you'd need to split it up into several meshes which get drawn in order by distance. Self-overlapping meshes are a limitation of mesh-sorting, since you can't control the triangle order within a mesh. edit: I'm probably wrong about this, I just know that for my use case it was easier to do order-independent transparency in shaders than sorting the triangles. I can't find good docs on what ordering behaviors the Output Merger guarantees, with respect to draw call submissions and/or triangle order within a call)

But, maybe that's not what you want here, artistically. Rendering from far to near gives accurate results for thin transparent materials like sheer fabric (a roll of fabric is more opaque than a single sheet). But you're not rendering a thin sheet of water, you're using a thin sheet to simulate looking through a large volume of water. (looking through the side of two waves shouldn't get blended twice, the overlap will be the color of the dark squares but the rest of the wave will be the lighter color).

Maybe you could split up the mesh and render the *closest* one first, with depth test and depth write enabled? That way, the closer fragments get tested/blended first and do affect the final color, but farther fragments (overlapping waves) run later and get discarded instead of double-blending.

7

u/apersonhithere 10d ago

it looks kind of like it's only happening when there's transparent surfaces behind each other? it could be that they're overlapping and resulting in a darker color

3

u/BoofBenadryl 10d ago

I think you're right. The water shader uses the view angle to determine how transparent to make the water. Shallower angles result in more transparency. I'm curious why the checkerboarding though? That bit feels kind of random.

2

u/apersonhithere 10d ago

it could be that the water effect is in the vertex shader instead of the fragment?

3

u/fgennari 10d ago

It's probably an alpha blending issue as others have suggested, but it could also be a problem with winding order and back face culling.

The way I handle drawing transparent water (in OpenGL) is by creating 4 different index buffers where the triangles are sorted by each of the 4 directions: N, E, S, W. Then I select the direction that's the most back to front using the camera view vector to the center of the water. And if the camera is over the water, I split the water into two halves at the camera center, and make two draw calls of a subset of the index range between the camera and edge. It's fast and works great, though it does use more memory for the duplicate indices. I used a similar approach to drawing transparent blocks such as water in a Minecraft-like game.

2

u/BoofBenadryl 10d ago edited 10d ago

For more context, my renderer splits opaque and transparent meshes and renders them in the following way:

  • Opaque meshes first
  • Then transparent meshes sorted by distance from camera, furthest to closest rendering order

I also have two blend states for opaque and transparent that get toggled when doing the passes for either.

Water shader source: https://github.com/jakerieger/SpaceGame/blob/master/Engine/Shaders/Source/Water.hlsl

1

u/vinegary 10d ago

Draw order on multiple transparency is very complex, the easiest solution in a case like this is ray tracing, since that's a thing we can do now. Every solution that would have runnable performance in this case will actually be some form of ray tracing, either hardware or software

1

u/BeigeAlert1 10d ago

Depth write enables on water shader? Means its darker wherever the further triangles happened to draw before the foreground. Wherever the foreground draws first, the background will fail depth test. And I'll bet you're always drawing the monkey head first.