Jump to content

Advanced Transparency and Refraction in Vulkan


Josh

5,258 views

 Share

Heat haze is a difficult problem. A particle emitter is created with a transparent material, and each particle warps the background a bit. The combined effect of lots of particles gives the whole background a nice shimmering wavy appearance. The problem is that when two particles overlap one another they don't blend together, because the last particle drawn is using the background of the solid world for the refracted image. This can result in a "popping" effect when particles disappear, as well as apparent seams on the edges of polygons.

out.gif.fd5cabbd667ee63be14f1f27b9dfdf7f.gif

In order to do transparency with refraction the right way, we are going to render all our transparent objects into a separate color texture and then draw that texture on top of the solid scene. We do this in order to accommodate multiple layers of transparency and refraction. Now, the correct way to handle multiple layers would be to render the solid world, render the first transparency object, then switch to another framebuffer and use the previous framebuffer color attachment for the source of your refraction image. This could be done per-object, although it could get very expensive, flipping back and forth between two framebuffers, but that still wouldn't be enough.

If we render all the transparent surfaces into a single image, we can blend their normals, refractive index, and other properties, and come up with a single refraction vector that combined the underlying surfaces in the best way possible.

To do this, the transparent surface color is rendered into the first color attachment. Unlike deferred lighting, the pixels at this point are fully lit.

color.thumb.jpg.443a4c6adf978770469352c6244fe925.jpg

The screen normals are stored in an additional color attachment. I am using world normals in this shot but later below I switched to screen normals:

norm.thumb.jpg.8ce4651e02aeb56120c93b5e628aa036.jpg

These images are drawn on top of the solid scene to render all transparent objects at once. Here we see the green box in the foreground is appearing in the refraction behind the glass dragon.

errro.thumb.jpg.95732691a7b3030ba535b640fbdfeca4.jpg

To prevent this from happening, we need add another color texture to the framebuffer and render the pixel Z position into it. I am using the R32_SFLOAT format. I use the separate blend mode feature in Vulkan, and set the blend mode to minimum so that the smallest value always gets saved in the texture. The Z-position is divided by the camera far range in the fragment shader, so that the saved values are always between 0 and 1. The clear color for this attachment is set to 1,1,1,1, so any value written into the buffer will replace the background. Note this is the depth of the transparent pixels, not the whole scene, so the area in the center where the dragon is occluded by the box is pure white, since those pixels were not drawn.

depth.thumb.jpg.f3b298fec19252ba3c9544c7a10db0f6.jpg

In the transparency pass, the Z position of the transparent pixel is compared to the Z position at the refracted texcoords. If the refracted position is closer to the camera than the transparent surface, the refraction is disabled for that pixel and the background directly behind the pixel is shown instead. There is some very slight red visible in the refraction, but no green.

fix.thumb.jpg.2dc3017ae234fac17d956fa35fd0a5d0.jpg

Now let's see how well this handles heat haze / distortion. We want to prevent the problem when two particles overlap. Here is what a particle emitter looks like when rendered to the transparency framebuffer, this time using screen-space normals. The particles aren't rotating so there are visible repetitions in the pattern, but that's okay for now.

out.gif.7b7fa988a1272293ef4ff3e3f0e7efc8.gif

And finally here is the result of the full render. As you can see, the seams and popping is gone, and we have a heavy but smooth distortion effect. Particles can safely overlap without causing any artifacts, as their normals are just blended together and combined to create a single refraction angle.

heat.gif.ef419e234029f4ea484ee1b20c2297c6.gif

  • Like 7
 Share

7 Comments


Recommended Comments

Here is the code for creating a heat haze material:

    //Creaqte the material
    auto heathaze = CreateMaterial();

    //Make it see-through
    heathaze->SetTransparent(true);

    //Set the alpha component of the color to zero
    heathaze->SetColor(1, 1, 1, 0);

    //Apply a texture for the alpha mask. Even though the alpha color is set to zero, 
    //this will control the intensity of the refraction effect.
    heathaze->SetTexture(LoadTexture("Materials/FX/particle.dds"), TEXTURE_BASE);

    //Set the normal map
    heathaze->SetTexture(LoadTexture("Materials/FX/heathaze.dds"), TEXTURE_NORMAL);

    //Set the refractive index
    heathaze->SetRefractiveIndex(1.3);

 

Link to comment

Here it is with two layers of refractive glass. There are some slight artifacts on the edges of the barrels but I think this is acceptable.

out.thumb.gif.872f8e4c7e3bc352022ccf30c7f2312d.gif

  • Like 4
Link to comment

I'd be really curious what it would look like at a good bitrate and as video instead of a GIF.  As a GIF it looks a bit weird.  But if it's just to give us an idea, then mission accomplished and pretty exciting.

Link to comment
Guest
Add a comment...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...