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

Implement blending in particle flipbook animation #50442

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

Conversation

Calinou
Copy link
Member

@Calinou Calinou commented Jul 14, 2021

This makes changes between frames look smoother, making flipbook particle animation more viable for realistic particle effects such as fire.

This works with both GPUParticles3D and CPUParticles3D.

This closes godotengine/godot-proposals#1636.

Testing project: test_particle_animation.zip (master only, won't work in 3.x)

Preview

particle_flipbook_blend.mp4

TODO

  • Implement this feature in CanvasItemMaterial for 2D particles.
  • See if we can keep UV2 free for other purposes when this feature is active, and ideally not require UV2 in meshes in the first place.
  • Investigate whether it's better to use a different shader version rather than a conditional. From my testing, the conditional doesn't affect performance in a meaningful way – I get 342 FPS instead of 343 FPS in my test project. However, this is a material feature that won't be used often, so perhaps it's still worth moving it to separate shader version (which requires compiling another shader when this feature is used).

@Calinou Calinou force-pushed the particle-flipbook-add-blending branch from 8643ba7 to b223d39 Compare July 14, 2021 18:45
@clayjohn
Copy link
Member

Will you be adding this to CPUParticles3D in this PR or a separate PR?

@Calinou
Copy link
Member Author

Calinou commented Jul 14, 2021

Will you be adding this to CPUParticles3D in this PR or a separate PR?

It already works with CPUParticles3D – I just tested it 🙂
You can test this in the MRP by selecting a GPUParticles3D node and choosing GPUParticles3D > Convert to CPUParticles3D at the top of the 3D editor.

That said, what do you think about the conditional-vs-shader version issue?

I'm working on adding 2D support right now.
Edit: Since the default CanvasItemMaterial does not have a fragment() function, I'm not sure how to implement this in 2D…

This makes changes between frames look smoother, making flipbook
particle animation more viable for realistic particle effects
such as fire.
@Calinou Calinou force-pushed the particle-flipbook-add-blending branch from b223d39 to cce713e Compare July 14, 2021 19:05
@clayjohn
Copy link
Member

clayjohn commented Jul 14, 2021

It already works with CPUParticles3D – I just tested it 🙂
You can test this in the MRP by selecting a GPUParticles3D node and choosing GPUParticles3D > Convert to CPUParticles3D at the top of the 3D editor.

Of course! Because this is in the StandardMaterial code, not in the ParticlesMaterial code.

That said, what do you think about the conditional-vs-shader version issue?

I think we probably should do another shader version. In theory it should be slightly faster and in practice I find that it is nicer to have as little as possible in the generated code when a user selects "Convert to StandardMaterial3D". I don't have a strong preference though, the performance impact is probably balanced out by the pain of having another shader permutation.

edit: it could even be done like below where there is a shader permutation for particles_animation. That way the extra cost is only there for animated particles

Since the default CanvasItemMaterial does not have a fragment() function, I'm not sure how to implement this in 2D…

You will have to add a fragment function when using animation blending. It shouldn't be too hard, as you are just inserting the string of characters, the same as in 3D. You add it all here:

if (particles_animation) {
code += "uniform int particles_anim_h_frames;\n";
code += "uniform int particles_anim_v_frames;\n";
code += "uniform bool particles_anim_loop;\n";
code += "void vertex() {\n";
code += "\tfloat h_frames = float(particles_anim_h_frames);\n";
code += "\tfloat v_frames = float(particles_anim_v_frames);\n";
code += "\tVERTEX.xy /= vec2(h_frames, v_frames);\n";
code += "\tfloat particle_total_frames = float(particles_anim_h_frames * particles_anim_v_frames);\n";
code += "\tfloat particle_frame = floor(INSTANCE_CUSTOM.z * float(particle_total_frames));\n";
code += "\tif (!particles_anim_loop) {\n";
code += "\t\tparticle_frame = clamp(particle_frame, 0.0, particle_total_frames - 1.0);\n";
code += "\t} else {\n";
code += "\t\tparticle_frame = mod(particle_frame, particle_total_frames);\n";
code += "\t}";
code += "\tUV /= vec2(h_frames, v_frames);\n";
code += "\tUV += vec2(mod(particle_frame, h_frames) / h_frames, floor(particle_frame / h_frames) / v_frames);\n";
code += "}\n";
}

Copy link
Member

@clayjohn clayjohn left a comment

Choose a reason for hiding this comment

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

Actually, this is also going to break if force_srgb is used or use_vertex_colors is enabled.

Probably what you should do is add something like the following at line 1003:

if (particles_anim_blend) {
    albedo_tex = mix(albedo_tex, texture(texture_albedo, particles_anim_next_frame_uv_coord), particles_anim_next_frame_mix);
}

This would allow you to delete the two conditions when assigned ALBEDO and ALPHA and would allow it to automatically use SRGB and COLOR as appropriate.

Comment on lines +1013 to +1015
code += "\tif (particles_anim_blend) {\n";
code += "\t\tALBEDO = albedo.rgb * mix(albedo_tex.rgb, texture(texture_albedo, particles_anim_next_frame_uv_coord).rgb, particles_anim_next_frame_mix);\n";
code += "\t} else {\n";
Copy link
Member

Choose a reason for hiding this comment

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

You should read and store the albedo_blend_tex here. This will allow you to only read the texture once. Below when reading alpha, you can just use albedo_blend_tex.a.

Suggested change
code += "\tif (particles_anim_blend) {\n";
code += "\t\tALBEDO = albedo.rgb * mix(albedo_tex.rgb, texture(texture_albedo, particles_anim_next_frame_uv_coord).rgb, particles_anim_next_frame_mix);\n";
code += "\t} else {\n";
code += "\tvec4 albedo_blend_tex = vec4(1.0);\n";
code += "\tif (particles_anim_blend) {\n";
code += "\t\talbedo_blend_tex = texture(texture_albedo, particles_anim_next_frame_uv_coord);\n";
code += "\t\tALBEDO = albedo.rgb * mix(albedo_tex.rgb, albedo_blend_tex.rgb, particles_anim_next_frame_mix);\n";
code += "\t} else {\n";

@@ -1084,7 +1113,11 @@ void BaseMaterial3D::_update_shader() {
code += "\tALPHA = 1.0;\n";

} else if (transparency != TRANSPARENCY_DISABLED || flags[FLAG_USE_SHADOW_TO_OPACITY] || (distance_fade == DISTANCE_FADE_PIXEL_ALPHA) || proximity_fade_enabled) {
code += "\tif (particles_anim_blend) {\n";
code += "\t\tALPHA = albedo.a * mix(albedo_tex.a, texture(texture_albedo, particles_anim_next_frame_uv_coord).a, particles_anim_next_frame_mix);\n";
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
code += "\t\tALPHA = albedo.a * mix(albedo_tex.a, texture(texture_albedo, particles_anim_next_frame_uv_coord).a, particles_anim_next_frame_mix);\n";
code += "\t\tALPHA = albedo.a * mix(albedo_tex.a, albedo_blend_tex.a, particles_anim_next_frame_mix);\n";

@Calinou
Copy link
Member Author

Calinou commented Jul 16, 2021

@clayjohn Thanks for the review! I tried implementing your changes locally, but unfortunately, it worsened the overall blending. It's possible that I've applied them incorrectly, so I included the diff below (relative to the PR's current state) for reference.

These were my local changes so far:

diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index 6f987602e2..b36539a322 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -1001,6 +1001,10 @@ void BaseMaterial3D::_update_shader() {
 		}
 	}
 
+	code += "\tif (particles_anim_blend) {\n";
+	code += "\t\talbedo_tex = mix(albedo_tex, texture(texture_albedo, particles_anim_next_frame_uv_coord), particles_anim_next_frame_mix);\n";
+	code += "\t}\n";
+
 	if (flags[FLAG_ALBEDO_TEXTURE_FORCE_SRGB]) {
 		code += "\talbedo_tex.rgb = mix(pow((albedo_tex.rgb + vec3(0.055)) * (1.0 / (1.0 + 0.055)),vec3(2.4)),albedo_tex.rgb.rgb * (1.0 / 12.92),lessThan(albedo_tex.rgb,vec3(0.04045)));\n";
 	}
@@ -1010,8 +1014,10 @@ void BaseMaterial3D::_update_shader() {
 	}
 
 	// Use particle flipbook animation if specified.
+	code += "\tvec4 albedo_blend_tex = vec4(1.0);\n";
 	code += "\tif (particles_anim_blend) {\n";
-	code += "\t\tALBEDO = albedo.rgb * mix(albedo_tex.rgb, texture(texture_albedo, particles_anim_next_frame_uv_coord).rgb, particles_anim_next_frame_mix);\n";
+	code += "\t\talbedo_blend_tex = texture(texture_albedo, particles_anim_next_frame_uv_coord);\n";
+	code += "\t\tALBEDO = albedo.rgb * mix(albedo_tex.rgb, albedo_blend_tex.rgb, particles_anim_next_frame_mix);\n";
 	code += "\t} else {\n";
 	code += "\t\tALBEDO = albedo.rgb * albedo_tex.rgb;\n";
 	code += "\t}\n";
@@ -1114,7 +1120,7 @@ void BaseMaterial3D::_update_shader() {
 
 	} else if (transparency != TRANSPARENCY_DISABLED || flags[FLAG_USE_SHADOW_TO_OPACITY] || (distance_fade == DISTANCE_FADE_PIXEL_ALPHA) || proximity_fade_enabled) {
 		code += "\tif (particles_anim_blend) {\n";
-		code += "\t\tALPHA = albedo.a * mix(albedo_tex.a, texture(texture_albedo, particles_anim_next_frame_uv_coord).a, particles_anim_next_frame_mix);\n";
+		code += "\t\tALPHA = albedo.a * mix(albedo_tex.a, albedo_blend_tex.a, particles_anim_next_frame_mix);\n";
 		code += "\t} else {\n";
 		code += "\tALPHA = albedo.a * albedo_tex.a;\n";
 		code += "\t}\n";

On the videos below, look at the number 4 fading in and out. Before, it faded in and out properly, but it no longer does. (The animation is marked to loop.)

Before

numbers_blending_before.mp4

After

numbers_blending_after.mp4

@clayjohn
Copy link
Member

I see the mistake, you added both alternatives that I mentioned. Sorry that review was confusing. I proposed two alternative ways of doing the same thing. All you need is the top 4 changed lines in your diff.

code += "\tif (particles_anim_blend) {\n";
code += "\t\talbedo_tex = mix(albedo_tex, texture(texture_albedo, particles_anim_next_frame_uv_coord), particles_anim_next_frame_mix);\n";
code += "\t}\n";

@LoganDark
Copy link

LoganDark commented Feb 5, 2022

Is this change optional? i.e. can it be opted out of by game devs?

@Calinou
Copy link
Member Author

Calinou commented Feb 5, 2022

Is this change optional? i.e. can it be opted out of by game devs?

Yes; it's disabled by default.

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

Successfully merging this pull request may close these issues.

Add a frame interpolation option for particle flipbook animation
4 participants