jiaozi158 / UnityVolumetricCloudsURP

Volumetric Clouds for Unity URP (Universal Render Pipeline).
MIT License
186 stars 10 forks source link

Transparency behind local clouds gets drawn in front #4

Open cheeztaco opened 7 months ago

cheeztaco commented 7 months ago

Hey I think that I might have found an issue with transparent objects. Placing transparent object behind local clouds, renders them in front of the clouds. This does actually make sense because the rendering of the clouds is after the skybox (defined in VolumetricCloudsURP.cs) and just before transparent objects. Rendering the clouds after the transparent objects renders the clouds in front of the transparent objects (the other way around).

I guess the problem exists because transparent objects do not write to depth, but the clouds use depth to cull. Writing to depth with transparent objects is hardly a solution as it defeats the purpose of them.

I saw that this problem also exists in HDRP, even in 2023, so I guess this is a limitation of the entire cloud system? Maybe this can be fixed in this port.

The gray objects in the image are transparent objects located at Y 0. The clouds are roughly at Y 1000 but the problem consists if we switch the positions. image

jiaozi158 commented 7 months ago

Hi, this is expected because clouds are semi-transparent objects, which makes it difficult to blend correctly with other (semi-transparent) objects.

HDRP (and this repo) uses opaque scene depth ("_CameraDepthTexture" in URP) to control the "draw distance" of clouds to achieve clouds blending. OpaqueCloudsBlending

This prevents clouds on the back from appearing in front of opaque objects. (hope the picture below is easy to understand) CloudsBlending

Check VolumetricClouds.hlsl

// If the current pixel depth is not sky
bool isOccluded = depth != UNITY_RAW_FAR_CLIP_VALUE;

// Compute the max cloud ray length
// For opaque objects, we only care about clouds in front of them.
#ifdef _LOCAL_VOLUMETRIC_CLOUDS
    // The depth may from a high-res texture which isn't ideal but can save performance.
    float distance = LinearEyeDepth(depth, _ZBufferParams) * rcp(dot(ray.direction, -UNITY_MATRIX_V[2].xyz));
    // 
    ray.maxRayLength = lerp(MAX_SKYBOX_VOLUMETRIC_CLOUDS_DISTANCE, distance, isOccluded);
#else

In HDRP, if the transparent object is not semi-transparent (which doesn't really need to be transparent, ex. use opaque cutout), you can check the "Before Refraction" to make it exist in depth texture and it should be visually correct because it's not semi-transparent (expected to see the clouds behind it).

In URP, we don't have "Before Refraction" render pass.

cheeztaco commented 7 months ago

But we could sample the _VolumetricCloudsColorTexture created in the renderer feature inside a transparent ShaderGraph and lerp over the result, to achieve blending with transparent materials that way. image

Using it this way we do not need the "Before Refraction" refraction pass in URP, and the result is a correct render even for semi transparent objects behind the clouds. The red cube in the image is transparent. image

I currently do not see any problems that might occur solving it this way, please correct me if I'm wrong. I think Unreal Engine does it like this with the "Apply Cloud Fogging" option on their custom materials too.

jiaozi158 commented 7 months ago

Thank you for sharing a workaround to this!

Yes, it should produce no problem except transparent objects intersecting with clouds. (first pic in the previous comment, opaque objects can have front clouds and occlude clouds on the back)

In the future after the repo supports Render Graph, I might add an option to output cloud's average depth to "_CameraDepthTexture" (Scene Depth node) so that transparent shaders (and post-processing effects) can access the depth value of clouds, which makes it possible to support basic ("Before" part in previous comment) in-clouds semi-transparency.

Adding an "Apply Cloud Fogging" checkbox in the Lit shader (and shader graph) material is a great idea, but only Unity's RP team could do that fix in HDRP. (maybe post on this Unity Forum thread if you'd like)

cheeztaco commented 7 months ago

Hey just a quick update. Seems like my approach was a bit too naive. The effect breaks pretty quick if you look from the bottom up to the clouds. image

I guess you are totally right with the average depth and right now I am out of ideas

jiaozi158 commented 7 months ago

A simple solution might be to check the y-coordinate of (world space) camera position in the transparent shader.

If the camera is inside or above the volumetric clouds (> lowest altitude), then disable clouds blending.

When the "output depth" option is added, you can compare the depth value sampled from depth texture to the current pixel's depth (ex. z-coordinate of clip space position) in your transparent shader so that blending is only applied if clouds are in front of the object.

jiaozi158 commented 5 months ago

Hi, I added an option to output the clouds average depth to "_VolumetricCloudsDepthTexture". RendererFeature

cheeztaco commented 5 months ago

First of all, thank you for the changes and sorry for my lack of response. I testet it a bit and I might have a suggestion. I created a small shader, that uses the pixels clip.z and compared it with the depth texture. If its greater or equal the cloud depth it multiplies the _VolumetricCloudsColorTexture.a with the alpha of the object we want to render. image This is done in ASE with a transparent, cull back, blend alpha shader. It's easily ported to shader graph.

The result what a bit "grainy" (the red cube uses the transparent shader, the white one is a basic opaque shader) image

The problem lies within the depth setting in VolumetricClouds.shader line 132 cloudsDepth = rayHit.invalidRay || cloudsColor.a >= 0.2 ? UNITY_RAW_FAR_CLIP_VALUE : TransformWorldToHClip(cloudPosWS).z; Removing the alpha threshold completely eliminated the grainy result and gives crystal clear transparency with the method I used. cloudsDepth = rayHit.invalidRay ? UNITY_RAW_FAR_CLIP_VALUE : TransformWorldToHClip(cloudPosWS).z;

image This is perfectly rendered with transparency this way.

Here is also a view from below with .5 alpha to verify that the depth settings and transparency works perfect image

Including a basic shader graph with my implementation as an example or better yet a custom function that handles this automatically for all transparent shader graphs, would help every user to start out with all sorts of transparency related effects with volumetric clouds. I could also assist with that if you want.

And again thank you, This makes urp 100 times better in my opinion!

jiaozi158 commented 5 months ago

Thank you very much for the feedback!

The cloudsColor.a >= 0.2 is a density threshold that ignores all the clouds with a density less than it. I'll double check later to see if it's really necessary and should be exposed as a setting.

As for providing a custom function node (input: alpha & world position, output: final alpha) that automatically does transparent sorting, I'll consider adding one in the next update.

BTW:

The original plan was to output clouds depth to the "_CameraDepthTexture" so that all features that need depth (lens flare, ssr, volumetric fog, water, ...) could benefit from it.

However, since clouds are usually rendered at a lower resolution, their depth need to be upscaled before writing to "_CameraDepthTexture". This isn't a first priority, but definitely worth trying in the future.

cheeztaco commented 5 months ago

Having an extra option in the future to add them to the "_CameraDepthTexture" would be nice indeed. I also think it's quite funny, that it would be the complete opposite to HDRP. We also have the option of preparing a depth texture with a custom renderer feature only for certain transparent objects (basically the same method as in HDRP with before refraction) and include that texture in the max ray length calculation of the clouds. This likely has the side effect, that clouds wont get rendered behind said transparent objects anymore which would make it look weird with semi transparent effects.

I noticed the lower resolution yesterday while looking across the clouds at a transparent object with my shader. I tried simple upscaling with bilinear filtering on the cloud depth texture itself, but the only thing that removed the fuzzyness was setting the integrationNoise of the ray to 0. Temporal accumulation is likely needed if im correct.

K3rhos commented 2 months ago

Recently tried this package and yep it looks like I have the same issue, my clouds get rendered on front of my car windows !

image image

From what I understand this is bcs URP is missing a shader feature that doesn't allow to properly solve that ?!

EDIT: Btw I also have reflection on the car so there is a reflection in the car body paint and in the car windows, but when disabling the reflection there is still the weird clouds appearing on top of the windows texture so definitely nothing to do with the reflection.

jiaozi158 commented 2 months ago

but when disabling the reflection there is still the weird clouds appearing on top of the windows texture so definitely nothing to do with the reflection.

Hi @K3rhos , it looks like the clouds on the car windows are from the default sky reflection probe. (assuming there's no reflection probe in the scene)

You may try adjusting the Environment Reflections Intensity Multiplier to check if it's relevant. ("Window → Rendering → Lighting → Environment")

SkyReflectionProbe_Intensity

To disable volumetric clouds in reflection probes, you can uncheck Reflection Probe in the renderer feature.

This repo handles transparency in much the same way as HDRP's, so by default, transparent objects will always be in front of the volumetric clouds.

K3rhos commented 2 months ago

Like I said in my previous post, this is not linked with reflection, it was already like that before, I added reflection probe since bcs I want reflection in the car, but this has nothing to do with it, the clouds are glitched (appear on front, even if there is something in between) with every transparent materials ! As you can see: image I disabled everything... and now look... image Clouds appear super bright with a "dark background" on top.

K3rhos commented 2 months ago

Oh okay I found the issue, this is because the interior of my car use a "Unlit" shader material, since the interior is not modelized I'm using a "pure dark" unlit material to hide the interior of the car, and this what causing the weird clouds appearance when looking through an other transparent material (in this case the car windows).

However, I'm not sure if this is a bug from the Unity Unlit Shader or your Volumetrics Clouds shader 🤷

jiaozi158 commented 2 months ago

However, I'm not sure if this is a bug from the Unity Lit Shader or your Volumetrics Clouds shader 🤷

I recommend checking this in Unity's Frame Debugger. For example, you can preview the resources used when rendering the car interior part.

K3rhos commented 2 months ago

image Actually I was using the wrong Unlit shader, instead of the URP Unlit, this is what was causing my issue, sorry for bothering you again lol, it's 100% my fault here.

EDIT: And yep I said "Lit" in my comment above, I wanted to say "Unlit" material.

jiaozi158 commented 2 months ago

That’s okay, I’m glad to hear the issue got resolved!