Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a 3D sky shaders demo #809

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

Calinou
Copy link
Member

@Calinou Calinou commented Dec 26, 2022

This uses https://github.com/Rytelier/godot-sky-and-volumetric-clouds as a base, with several changes made:

  • Spheres are now used to represent radiance map reflections of varying roughness and metallic values.
  • A day/night cycle is now featured.
  • Mipmaps are enabled on the weather texture as an optimization.
  • The default radiance map settings are more conservative to account for the real-time sky shader.
    • This makes reflections lower quality, but it's not too noticeable in most real world scenes (especially if using GI techniques for reflections).
  • Debanding is now applied in the project settings, rather than on the sky shader. This is significantly faster (over 0.1 ms saved on a RX 6900 XT in 3840×2160). It also has the benefit of working on materials, which can exhibit banding if not textured.
    • The sky shader has debanding commented out in case it's needed. This debanding also applies to the lower half of the sky as well in this case, as it was required to get rid of noticeable banding on the lower half.
  • Cloud coverage and density uniform hints now allow values as low as 0.001.

cc @Rytelier

Preview

Sky shaders

@clayjohn
Copy link
Member

clayjohn commented Jan 2, 2023

In the screenshot I am noticing significant striping artifacts in the clouds that look like severe undersampling. How noticeable are they in motion?

3d/sky_shaders/sky_volumetric_clouds.gdshader Outdated Show resolved Hide resolved
3d/sky_shaders/sky_volumetric_clouds.gdshader Outdated Show resolved Hide resolved
Comment on lines 172 to 182
vec3 p = pip;
p.x += time * 10.0 * cloud_time_scale + cloud_time_offset;
float height_fraction = GetHeightFractionForPoint(length(p));
vec4 n = textureLod(perlworlnoise, p.xyz*0.00008, mip-2.0);
float fbm = n.g*0.625+n.b*0.25+n.a*0.125;
float g = densityHeightGradient(height_fraction, weather.r);
float base_cloud = remap(n.r, -(1.0-fbm), 1.0, 0.0, 1.0);
float weather_coverage = cloud_coverage*weather.b;
base_cloud = remap(base_cloud*g, 1.0-(weather_coverage), 1.0, 0.0, 1.0);
base_cloud *= weather_coverage;
p.xy -= time * 40.0;
vec3 hn = textureLod(worlnoise, p*0.001, mip).rgb;
float hfbm = hn.r*0.625+hn.g*0.25+hn.b*0.125;
hfbm = mix(hfbm, 1.0-hfbm, clamp(height_fraction*4.0, 0.0, 1.0));
base_cloud = remap(base_cloud, hfbm*0.4 * height_fraction, 1.0, 0.0, 1.0);
return pow(clamp(base_cloud, 0.0, 1.0), (1.0 - height_fraction) * 0.8 + 0.5);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add some comments and whitespace here to make this easier to read. If you aren't comfortable doing that let me know and I can add some suggestions.

Copy link
Member Author

@Calinou Calinou Jan 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make suggestions here, I'm not 100% sure how that code works. Note that I've force-pushed a commit to make the shader better comply with the style guide.

@Calinou
Copy link
Member Author

Calinou commented Jan 2, 2023

In the screenshot I am noticing significant striping artifacts in the clouds that look like severe undersampling. How noticeable are they in motion?

It looks like this in motion at the default project window size. Note that moving the camera makes artifacts more noticeable than what's shown in the video:

simplescreenrecorder-2023-01-02_23.54.55.mp4


Increasing Cloud Steps Range from (96, 54) to (128, 72) improves cloud quality a fair bit, but it increases GPU time by 0.5 ms on a Radeon RX 6900 XT in 4K. This performance impact becomes larger as the cloud density increases. We could expose a slider for this in the demo, as I'm not sure we can afford increasing the quality by default on typical mid-range GPUs like a GTX 1060.

I've increased the default value to (128, 72) still.

PS: According to the editor's FPS counter, I've noticed that FPS drops whenever the editor camera is moving (even though the shader doesn't use TIME anymore and SDFGI is disabled). Drops are more extreme with high radiance sizes in the sky properties, which suggests the radiance map is being updated every frame for no reason when the camera moves.

@Calinou Calinou force-pushed the add-3d-sky-shaders-demo branch 3 times, most recently from 41f0895 to c2ce73f Compare January 3, 2023 01:29
@clayjohn
Copy link
Member

clayjohn commented Jan 9, 2023

PS: According to the editor's FPS counter, I've noticed that FPS drops whenever the editor camera is moving (even though the shader doesn't use TIME anymore and SDFGI is disabled). Drops are more extreme with high radiance sizes in the sky properties, which suggests the radiance map is being updated every frame for no reason when the camera moves.

That happens because the shader is using the POSITION builtin. Which is the camera's position in world space. If used, the radiance map needs to be updated every time the camera moves.

Even with your last push the artifacts I noticed are still present.

Here is a comparison between the original shader and your modified version:

Modified: Note the horizontal lines
Screenshot from 2023-01-09 13-04-05

Original: even at horizon no horizontal lines (there are still undersampling artifacts, but they aren't as pronounced)
Screenshot from 2023-01-09 13-02-47

@Rytelier
Copy link

Even with your last push the artifacts I noticed are still present.

Possible reason I can think of is that the original shader calculated clouds in full res pass and then downscaled them for half res pass, while I moved all the calculations to half res pass.

@clayjohn
Copy link
Member

@Rytelier The original shader doesn't calculate clouds in full res, it calculates them in half res for the background and quarter res for reflections

Code here:
https://github.com/clayjohn/godot-volumetric-cloud-demo/blob/e6ade4938e15a989a4a304a8151f8bc84ef0276f/clouds.gdshader#L253-L263

@Rytelier
Copy link

@clayjohn The march() function is called in the full screen pass and only its output is applied on half-res pass. After I moved the march() to half-res pass, the performance became much higher.

@clayjohn
Copy link
Member

@clayjohn The march() function is called in the full screen pass and only its output is applied on half-res pass. After I moved the march() to half-res pass, the performance became much higher.

Take a look at the code I linked, all the full screen pass does is read from the HALF_RES_COLOR variable. Which means all it does is read from the half res buffer. The code above that assigns to col doesn't run in the full screen pass because it doesn't have an impact on the final output. Only code that impacts the final fragment color gets compiled into the shader, everything else is excluded.

To illustrate my point, take a look at the ISA code emitted by the following shaders

#version 460

layout (location = 0) out vec4 fragColor;

#define HALF_RES_PASS false

void main()
{
    vec3 col = vec3(0.0, 0.0, 0.0);
    
	fragColor = vec4(col, 1.0);
}
  v_mov_b32     v0, 0                                   // 000000000000: 7E000280
  v_mov_b32     v1, 0x3c000000                          // 000000000004: 7E0202FF 3C000000
  exp           mrt0, v0, v0, v1, v1 done compr vm      // 00000000000C: C4001C0F 00000100
  s_endpgm                                              // 000000000014: BF810000

https://shader-playground.timjones.io/6886ba21c71b03fb08d5c337eb8c5dcc

#version 460

layout (location = 0) out vec4 fragColor;

#define HALF_RES_PASS false

void main()
{
    vec3 col = vec3(0.0, 0.0, 0.0);
    if (HALF_RES_PASS) {
    	col += vec3(1.0, 0.5, 3.7);
    	vec3 a = gl_FragCoord.xyz;
        col = a / col;
    }
	fragColor = vec4(col, 1.0);
}
  v_mov_b32     v0, 0                                   // 000000000000: 7E000280
  v_mov_b32     v1, 0x3c000000                          // 000000000004: 7E0202FF 3C000000
  exp           mrt0, v0, v0, v1, v1 done compr vm      // 00000000000C: C4001C0F 00000100
  s_endpgm                                              // 000000000014: BF810000

https://shader-playground.timjones.io/5f6bf0e36efe75b29ec6c2f1e6c3f2a9

And for good measure, here is what happens when HALF_RES_PASS becomes true:

#version 460

layout (location = 0) out vec4 fragColor;

#define HALF_RES_PASS true

void main()
{
    vec3 col = vec3(0.0, 0.0, 0.0);
    if (HALF_RES_PASS) {
    	col += vec3(1.0, 0.5, 3.7);
    	vec3 a = gl_FragCoord.xyz;
        col = a / col;
    }
	fragColor = vec4(col, 1.0);
}
  v_mul_f32     v0, 2.0, v3                             // 000000000000: 0A0006F4
  v_mul_f32     v1, 0x3e8a60dd, v4                      // 000000000004: 0A0208FF 3E8A60DD
  v_cvt_pkrtz_f16_f32  v0, v2, v0                       // 00000000000C: D2960000 00020102
  v_cvt_pkrtz_f16_f32  v1, v1, 1.0                      // 000000000014: D2960001 0001E501
  exp           mrt0, v0, v0, v1, v1 done compr vm      // 00000000001C: C4001C0F 00000100
  s_endpgm                                              // 000000000024: BF810000

https://shader-playground.timjones.io/0ee23ea5a7f96ce07c87a2370ddfa5cb
Note how the extra instructions only get included when HALF_RES_PASS is true.

@Rytelier
Copy link

@clayjohn I think there's some misunderstanding, I'll elaborate: notice the code block starting from if (dir.y>0.0) { is placed outside of the AT_HALF_RES_PASS, so all its contents is calculated at full screen pass.
If you are still not convinced how it works, just compare the performance of your version and my edits, when I did these edits the performance got significantly higher.

@clayjohn
Copy link
Member

clayjohn commented Jan 12, 2023

@clayjohn I think there's some misunderstanding, I'll elaborate: notice the code block starting from if (dir.y>0.0) { is placed outside of the AT_HALF_RES_PASS, so all its contents is calculated at full screen pass. If you are still not convinced how it works, just compare the performance of your version and my edits, when I did these edits the performance got significantly higher.

Here are two more analogous examples so you can see what the shader compiler does with this type of code.

Note that the ISA output is exactly the same whether the logic is inside or outside the if statement. Since col is unused when HALF_RES_PASS is false the entire block of code is compiled out. Shaders only include code that contributes to the final output.

To be even more clear, I tested my shader with the cloud logic inside and outside the if statements and it did not change performance at all.

#version 460

layout (location = 0) out vec4 fragColor;

#define HALF_RES_PASS true

void main()
{
    vec3 col = vec3(0.0, 0.0, 0.0);
    col += vec3(1.0, 0.5, 3.7);
    vec3 a = gl_FragCoord.xyz;
    col = a / col;
    if (HALF_RES_PASS) {
        fragColor = vec4(col, 1.0);
    } else {
        fragColor = vec4(1.0);
    }
}
  v_mul_f32     v0, 2.0, v3                             // 000000000000: 0A0006F4
  v_mul_f32     v1, 0x3e8a60dd, v4                      // 000000000004: 0A0208FF 3E8A60DD
  v_cvt_pkrtz_f16_f32  v0, v2, v0                       // 00000000000C: D2960000 00020102
  v_cvt_pkrtz_f16_f32  v1, v1, 1.0                      // 000000000014: D2960001 0001E501
  exp           mrt0, v0, v0, v1, v1 done compr vm      // 00000000001C: C4001C0F 00000100
  s_endpgm                                              // 000000000024: BF810000

https://shader-playground.timjones.io/90f0f20d12035384352dec124ed78944

#version 460

layout (location = 0) out vec4 fragColor;

#define HALF_RES_PASS false

void main()
{
    vec3 col = vec3(0.0, 0.0, 0.0);
    col += vec3(1.0, 0.5, 3.7);
    vec3 a = gl_FragCoord.xyz;
    col = a / col;
    if (HALF_RES_PASS) {
        fragColor = vec4(col, 1.0);
    } else {
        fragColor = vec4(1.0);
    }
}
  v_mov_b32     v0, 0x3c003c00                          // 000000000000: 7E0002FF 3C003C00
  v_mov_b32     v1, 0x3c003c00                          // 000000000008: 7E0202FF 3C003C00
  exp           mrt0, v0, v0, v1, v1 done compr vm      // 000000000010: C4001C0F 00000100
  s_endpgm                                              // 000000000018: BF810000

https://shader-playground.timjones.io/1101dfe53170e0139243faaad304b448

@Calinou
Copy link
Member Author

Calinou commented Feb 26, 2023

Is there anything actionable I can do to move this PR forward?

@GeorgeS2019
Copy link

I use the latest Godot 4 .NET and I could not see the sky, perhaps I do not know how to set it up. Is it possible to see what is expected of the screenshot the moment the demo is loaded?

@Calinou Calinou force-pushed the add-3d-sky-shaders-demo branch from c2ce73f to d3c7ba8 Compare March 29, 2023 22:52
@Calinou
Copy link
Member Author

Calinou commented Mar 29, 2023

Fixed the demo to work on 4.0.1. This comment still stands though; I don't know how to make the sky shader look better without decreasing performance too much.

@GeorgeS2019

This comment was marked as off-topic.

@Calinou

This comment was marked as off-topic.

This uses https://github.com/Rytelier/godot-sky-and-volumetric-clouds
as a base, with several changes made:

- Spheres are now used to represent radiance map reflections of varying
  roughness and metallic values.
- A day/night cycle is now featured.
- Mipmaps are enabled on the weather texture as an optimization.
- The default radiance map settings are more conservative to account
  for the real-time sky shader.
  - This makes reflections lower quality, but it's not too noticeable
    in most real world scenes (especially if using GI techniques for reflections).
- Debanding is now applied in the project settings, rather than on the sky shader.
  This is significantly faster (over 0.1 ms saved on a RX 6900 XT in 3840×2160).
  It also has the benefit of working on materials, which can exhibit banding
  if not textured.
  - The sky shader has debanding commented out in case it's needed.
    This debanding also applies to the lower half of the sky as well in this case,
    as it was required to get rid of noticeable banding on the lower half.
- Cloud coverage and density uniform hints now allow values as low as 0.001.

Co-authored-by: Rytelier <[email protected]>
Co-authored-by: Clay John <[email protected]>
@Calinou Calinou force-pushed the add-3d-sky-shaders-demo branch from d3c7ba8 to d07c18d Compare October 18, 2023 01:14
@Calinou
Copy link
Member Author

Calinou commented Oct 18, 2023

I've amended the PR to fix an issue related to radiance size (it's now set correctly after switching from Real-Time to High-Quality or High-Quality Incremental).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants