godot-demo-projects icon indicating copy to clipboard operation
godot-demo-projects copied to clipboard

Add a 3D sky shaders demo

Open Calinou opened this issue 3 years ago • 15 comments

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

Calinou avatar Dec 26 '22 18:12 Calinou

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

clayjohn avatar Jan 02 '23 19:01 clayjohn

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:

https://user-images.githubusercontent.com/180032/210283273-da670e1f-2a58-459a-a3cb-3a0dadf37c38.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 avatar Jan 02 '23 23:01 Calinou

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

clayjohn avatar Jan 09 '23 21:01 clayjohn

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.

Rytelier avatar Jan 11 '23 10:01 Rytelier

@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

clayjohn avatar Jan 11 '23 17:01 clayjohn

@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.

Rytelier avatar Jan 11 '23 19:01 Rytelier

@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.

clayjohn avatar Jan 11 '23 19:01 clayjohn

@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.

Rytelier avatar Jan 12 '23 12:01 Rytelier

@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

clayjohn avatar Jan 12 '23 18:01 clayjohn

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

Calinou avatar Feb 26 '23 22:02 Calinou

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?

GeorgeS2019 avatar Mar 29 '23 21:03 GeorgeS2019

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.

Calinou avatar Mar 29 '23 22:03 Calinou

@Calinou Not related to this issue, I could not track the latest Godot4 3D IK demo status, except a few have feedback it is not working compared to Godot3

GeorgeS2019 avatar Mar 29 '23 23:03 GeorgeS2019

@Calinou Not related to this issue, I could not track the latest Godot4 3D IK demo status, except a few have feedback it is not working compared to Godot3

IK in general is currently broken in Godot 4, as it needs to be rewritten from scratch in a future Godot 4.x release. SkeletonModificationStack3D was removed from 4.0 before 4.0.stable was released as it was unfinished.

Calinou avatar Mar 29 '23 23:03 Calinou

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).

Calinou avatar Oct 18 '23 01:10 Calinou