Skip to content

Commit

Permalink
Added premult alpha blending to 3D (spatial) shaders.
Browse files Browse the repository at this point in the history
Co-authored-by: jitspoe <jitspoe@yahoo.com>
Co-authored-by: Clay John <claynjohn@gmail.com>
  • Loading branch information
3 people committed Apr 30, 2024
1 parent d282e4f commit 41a2b0e
Show file tree
Hide file tree
Showing 14 changed files with 76 additions and 6 deletions.
3 changes: 3 additions & 0 deletions doc/classes/BaseMaterial3D.xml
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,9 @@
<constant name="BLEND_MODE_MUL" value="3" enum="BlendMode">
The color of the object is multiplied by the background.
</constant>
<constant name="BLEND_MODE_PREMULT_ALPHA" value="4" enum="BlendMode">
The color of the object is added to the background and the alpha channel is used to mask out the background. This is effectively a hybrid of the blend mix and add modes, useful for FX like fire where you want the flame to add but the smoke to mix. By default, this works with unshaded materials using premultiplied textures. For shaded materials, use the PREMUL_ALPHA_FACTOR built-in so that lighting can be modulated as well.
</constant>
<constant name="ALPHA_ANTIALIASING_OFF" value="0" enum="AlphaAntiAliasing">
Disables Alpha AntiAliasing for the material.
</constant>
Expand Down
5 changes: 5 additions & 0 deletions drivers/gles3/rasterizer_scene_gles3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3040,6 +3040,11 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
glBlendFuncSeparate(GL_DST_COLOR, GL_ZERO, GL_ZERO, GL_ONE);
}

} break;
case GLES3::SceneShaderData::BLEND_MODE_PREMULT_ALPHA: {
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

} break;
case GLES3::SceneShaderData::BLEND_MODE_ALPHA_TO_COVERAGE: {
// Do nothing for now.
Expand Down
8 changes: 7 additions & 1 deletion drivers/gles3/shaders/scene.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -1442,6 +1442,9 @@ void main() {
float clearcoat_roughness = 0.0;
float anisotropy = 0.0;
vec2 anisotropy_flow = vec2(1.0, 0.0);
#ifdef PREMULT_ALPHA_USED
float premult_alpha = 1.0;
#endif
#ifndef FOG_DISABLED
vec4 fog = vec4(0.0);
#endif // !FOG_DISABLED
Expand Down Expand Up @@ -2141,9 +2144,12 @@ void main() {

frag_color.rgb += additive_light_color;
#endif // USE_ADDITIVE_LIGHTING

frag_color.rgb *= scene_data.luminance_multiplier;

#endif // !RENDER_MATERIAL
#endif // !MODE_RENDER_DEPTH

#ifdef PREMULT_ALPHA_USED
frag_color.rgb *= premult_alpha;
#endif
}
3 changes: 3 additions & 0 deletions drivers/gles3/storage/material_storage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1243,6 +1243,7 @@ MaterialStorage::MaterialStorage() {
actions.renames["NORMAL_MAP_DEPTH"] = "normal_map_depth";
actions.renames["ALBEDO"] = "albedo";
actions.renames["ALPHA"] = "alpha";
actions.renames["PREMULT_ALPHA_FACTOR"] = "premult_alpha";
actions.renames["METALLIC"] = "metallic";
actions.renames["SPECULAR"] = "specular";
actions.renames["ROUGHNESS"] = "roughness";
Expand Down Expand Up @@ -1327,6 +1328,7 @@ MaterialStorage::MaterialStorage() {
actions.usage_defines["ALPHA_HASH_SCALE"] = "#define ALPHA_HASH_USED\n";
actions.usage_defines["ALPHA_ANTIALIASING_EDGE"] = "#define ALPHA_ANTIALIASING_EDGE_USED\n";
actions.usage_defines["ALPHA_TEXTURE_COORDINATE"] = "@ALPHA_ANTIALIASING_EDGE";
actions.usage_defines["PREMULT_ALPHA_FACTOR"] = "#define PREMULT_ALPHA_USED";

actions.usage_defines["SSS_STRENGTH"] = "#define ENABLE_SSS\n";
actions.usage_defines["SSS_TRANSMITTANCE_DEPTH"] = "#define ENABLE_TRANSMITTANCE\n";
Expand Down Expand Up @@ -2908,6 +2910,7 @@ void SceneShaderData::set_code(const String &p_code) {
actions.render_mode_values["blend_mix"] = Pair<int *, int>(&blend_modei, BLEND_MODE_MIX);
actions.render_mode_values["blend_sub"] = Pair<int *, int>(&blend_modei, BLEND_MODE_SUB);
actions.render_mode_values["blend_mul"] = Pair<int *, int>(&blend_modei, BLEND_MODE_MUL);
actions.render_mode_values["blend_premult_alpha"] = Pair<int *, int>(&blend_modei, BLEND_MODE_PREMULT_ALPHA);

actions.render_mode_values["alpha_to_coverage"] = Pair<int *, int>(&alpha_antialiasing_modei, ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE);
actions.render_mode_values["alpha_to_coverage_and_one"] = Pair<int *, int>(&alpha_antialiasing_modei, ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE_AND_TO_ONE);
Expand Down
1 change: 1 addition & 0 deletions drivers/gles3/storage/material_storage.h
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ struct SceneShaderData : public ShaderData {
BLEND_MODE_ADD,
BLEND_MODE_SUB,
BLEND_MODE_MUL,
BLEND_MODE_PREMULT_ALPHA,
BLEND_MODE_ALPHA_TO_COVERAGE
};

Expand Down
11 changes: 10 additions & 1 deletion scene/resources/material.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,9 @@ void BaseMaterial3D::_update_shader() {
case BLEND_MODE_MUL:
code += "blend_mul";
break;
case BLEND_MODE_PREMULT_ALPHA:
code += "blend_premul_alpha";
break;
case BLEND_MODE_MAX:
break; // Internal value, skip.
}
Expand Down Expand Up @@ -1819,6 +1822,11 @@ void fragment() {)";
vec3 detail = mix(ALBEDO.rgb, ALBEDO.rgb * detail_tex.rgb, detail_tex.a);
)";
} break;
case BLEND_MODE_PREMULT_ALPHA: {
// This is unlikely to ever be used for detail textures, and in order for it to function in the editor, another bit must be used in MaterialKey,
// but there are only 5 bits left, so I'm going to leave this disabled unless it's actually requested.
//code += "\tvec3 detail = (1.0-detail_tex.a)*ALBEDO.rgb+detail_tex.rgb;\n";
} break;
case BLEND_MODE_MAX:
break; // Internal value, skip.
}
Expand Down Expand Up @@ -3040,7 +3048,7 @@ void BaseMaterial3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "alpha_hash_scale", PROPERTY_HINT_RANGE, "0,2,0.01"), "set_alpha_hash_scale", "get_alpha_hash_scale");
ADD_PROPERTY(PropertyInfo(Variant::INT, "alpha_antialiasing_mode", PROPERTY_HINT_ENUM, "Disabled,Alpha Edge Blend,Alpha Edge Clip"), "set_alpha_antialiasing", "get_alpha_antialiasing");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "alpha_antialiasing_edge", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_alpha_antialiasing_edge", "get_alpha_antialiasing_edge");
ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_mode", PROPERTY_HINT_ENUM, "Mix,Add,Subtract,Multiply"), "set_blend_mode", "get_blend_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_mode", PROPERTY_HINT_ENUM, "Mix,Add,Subtract,Multiply,Premultiplied Alpha"), "set_blend_mode", "get_blend_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "cull_mode", PROPERTY_HINT_ENUM, "Back,Front,Disabled"), "set_cull_mode", "get_cull_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "depth_draw_mode", PROPERTY_HINT_ENUM, "Opaque Only,Always,Never"), "set_depth_draw_mode", "get_depth_draw_mode");
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "no_depth_test"), "set_flag", "get_flag", FLAG_DISABLE_DEPTH_TEST);
Expand Down Expand Up @@ -3269,6 +3277,7 @@ void BaseMaterial3D::_bind_methods() {
BIND_ENUM_CONSTANT(BLEND_MODE_ADD);
BIND_ENUM_CONSTANT(BLEND_MODE_SUB);
BIND_ENUM_CONSTANT(BLEND_MODE_MUL);
BIND_ENUM_CONSTANT(BLEND_MODE_PREMULT_ALPHA);

BIND_ENUM_CONSTANT(ALPHA_ANTIALIASING_OFF);
BIND_ENUM_CONSTANT(ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE);
Expand Down
1 change: 1 addition & 0 deletions scene/resources/material.h
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ class BaseMaterial3D : public Material {
BLEND_MODE_ADD,
BLEND_MODE_SUB,
BLEND_MODE_MUL,
BLEND_MODE_PREMULT_ALPHA,
BLEND_MODE_MAX
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) {
actions.render_mode_values["blend_mix"] = Pair<int *, int>(&blend_mode, BLEND_MODE_MIX);
actions.render_mode_values["blend_sub"] = Pair<int *, int>(&blend_mode, BLEND_MODE_SUB);
actions.render_mode_values["blend_mul"] = Pair<int *, int>(&blend_mode, BLEND_MODE_MUL);
actions.render_mode_values["blend_premult_alpha"] = Pair<int *, int>(&blend_mode, BLEND_MODE_PREMULT_ALPHA);

actions.render_mode_values["alpha_to_coverage"] = Pair<int *, int>(&alpha_antialiasing_mode, ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE);
actions.render_mode_values["alpha_to_coverage_and_one"] = Pair<int *, int>(&alpha_antialiasing_mode, ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE_AND_TO_ONE);
Expand Down Expand Up @@ -244,7 +245,17 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) {
blend_attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
blend_attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
blend_attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ZERO;
}
} break;
case BLEND_MODE_PREMULT_ALPHA: {
blend_attachment.enable_blend = true;
blend_attachment.alpha_blend_op = RD::BLEND_OP_ADD;
blend_attachment.color_blend_op = RD::BLEND_OP_ADD;
blend_attachment.src_color_blend_factor = RD::BLEND_FACTOR_ONE;
blend_attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
blend_attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
blend_attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
uses_blend_alpha = true; // Force alpha used because of blend.
} break;
}

// Color pass -> attachment 0: Color/Diffuse, attachment 1: Separate Specular, attachment 2: Motion Vectors
Expand Down Expand Up @@ -593,6 +604,7 @@ void SceneShaderForwardClustered::init(const String p_defines) {
actions.renames["NORMAL_MAP_DEPTH"] = "normal_map_depth";
actions.renames["ALBEDO"] = "albedo";
actions.renames["ALPHA"] = "alpha";
actions.renames["PREMULT_ALPHA_FACTOR"] = "premult_alpha";
actions.renames["METALLIC"] = "metallic";
actions.renames["SPECULAR"] = "specular";
actions.renames["ROUGHNESS"] = "roughness";
Expand Down Expand Up @@ -672,6 +684,7 @@ void SceneShaderForwardClustered::init(const String p_defines) {
actions.usage_defines["INSTANCE_CUSTOM"] = "#define ENABLE_INSTANCE_CUSTOM\n";
actions.usage_defines["POSITION"] = "#define OVERRIDE_POSITION\n";
actions.usage_defines["LIGHT_VERTEX"] = "#define LIGHT_VERTEX_USED\n";
actions.usage_defines["PREMULT_ALPHA_FACTOR"] = "#define PREMULT_ALPHA_USED\n";

actions.usage_defines["ALPHA_SCISSOR_THRESHOLD"] = "#define ALPHA_SCISSOR_USED\n";
actions.usage_defines["ALPHA_HASH_SCALE"] = "#define ALPHA_HASH_USED\n";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ class SceneShaderForwardClustered {
BLEND_MODE_ADD,
BLEND_MODE_SUB,
BLEND_MODE_MUL,
BLEND_MODE_ALPHA_TO_COVERAGE
BLEND_MODE_ALPHA_TO_COVERAGE,
BLEND_MODE_PREMULT_ALPHA,
};

enum DepthDraw {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) {
actions.render_mode_values["blend_mix"] = Pair<int *, int>(&blend_mode, BLEND_MODE_MIX);
actions.render_mode_values["blend_sub"] = Pair<int *, int>(&blend_mode, BLEND_MODE_SUB);
actions.render_mode_values["blend_mul"] = Pair<int *, int>(&blend_mode, BLEND_MODE_MUL);
actions.render_mode_values["blend_premult_alpha"] = Pair<int *, int>(&blend_mode, BLEND_MODE_PREMULT_ALPHA);

actions.render_mode_values["alpha_to_coverage"] = Pair<int *, int>(&alpha_antialiasing_mode, ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE);
actions.render_mode_values["alpha_to_coverage_and_one"] = Pair<int *, int>(&alpha_antialiasing_mode, ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE_AND_TO_ONE);
Expand Down Expand Up @@ -255,7 +256,17 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) {
blend_attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
blend_attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
blend_attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ZERO;
}
} break;
case BLEND_MODE_PREMULT_ALPHA: {
blend_attachment.enable_blend = true;
blend_attachment.alpha_blend_op = RD::BLEND_OP_ADD;
blend_attachment.color_blend_op = RD::BLEND_OP_ADD;
blend_attachment.src_color_blend_factor = RD::BLEND_FACTOR_ONE;
blend_attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
blend_attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
blend_attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
uses_blend_alpha = true; // Force alpha used because of blend.
} break;
}

RD::PipelineColorBlendState blend_state_blend;
Expand Down Expand Up @@ -497,6 +508,7 @@ void SceneShaderForwardMobile::init(const String p_defines) {
actions.renames["NORMAL_MAP_DEPTH"] = "normal_map_depth";
actions.renames["ALBEDO"] = "albedo";
actions.renames["ALPHA"] = "alpha";
actions.renames["PREMULT_ALPHA_FACTOR"] = "premult_alpha";
actions.renames["METALLIC"] = "metallic";
actions.renames["SPECULAR"] = "specular";
actions.renames["ROUGHNESS"] = "roughness";
Expand Down Expand Up @@ -581,6 +593,7 @@ void SceneShaderForwardMobile::init(const String p_defines) {
actions.usage_defines["ALPHA_HASH_SCALE"] = "#define ALPHA_HASH_USED\n";
actions.usage_defines["ALPHA_ANTIALIASING_EDGE"] = "#define ALPHA_ANTIALIASING_EDGE_USED\n";
actions.usage_defines["ALPHA_TEXTURE_COORDINATE"] = "@ALPHA_ANTIALIASING_EDGE";
actions.usage_defines["PREMULT_ALPHA_FACTOR"] = "#define PREMULT_ALPHA_USED";

actions.usage_defines["SSS_STRENGTH"] = "#define ENABLE_SSS\n";
actions.usage_defines["SSS_TRANSMITTANCE_DEPTH"] = "#define ENABLE_TRANSMITTANCE\n";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ class SceneShaderForwardMobile {
BLEND_MODE_ADD,
BLEND_MODE_SUB,
BLEND_MODE_MUL,
BLEND_MODE_PREMULT_ALPHA,
BLEND_MODE_ALPHA_TO_COVERAGE
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -914,6 +914,9 @@ vec3 encode24(vec3 v) {
void fragment_shader(in SceneData scene_data) {
uint instance_index = instance_index_interp;

#ifdef PREMULT_ALPHA_USED
float premult_alpha = 1.0;
#endif // PREMULT_ALPHA_USED
//lay out everything, whatever is unused is optimized away anyway
vec3 vertex = vertex_interp;
#ifdef USE_MULTIVIEW
Expand Down Expand Up @@ -2458,6 +2461,10 @@ void fragment_shader(in SceneData scene_data) {

motion_vector = prev_position_uv - position_uv;
#endif

#if defined(PREMULT_ALPHA_USED) && !defined(MODE_RENDER_DEPTH)
frag_color.rgb *= premult_alpha;
#endif //PREMULT_ALPHA_USED
}

void main() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,9 @@ void main() {
float clearcoat_roughness = 0.0;
float anisotropy = 0.0;
vec2 anisotropy_flow = vec2(1.0, 0.0);
#ifdef PREMULT_ALPHA_USED
float premult_alpha = 1.0;
#endif
#ifndef FOG_DISABLED
vec4 fog = vec4(0.0);
#endif // !FOG_DISABLED
Expand Down Expand Up @@ -1846,6 +1849,9 @@ void main() {
// On mobile we use a UNORM buffer with 10bpp which results in a range from 0.0 - 1.0 resulting in HDR breaking
// We divide by sc_luminance_multiplier to support a range from 0.0 - 2.0 both increasing precision on bright and darker images
frag_color.rgb = frag_color.rgb / sc_luminance_multiplier;
#ifdef PREMULT_ALPHA_USED
frag_color.rgb *= premult_alpha;
#endif

#endif //MODE_MULTIPLE_RENDER_TARGETS

Expand Down
3 changes: 2 additions & 1 deletion servers/rendering/shader_types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ ShaderTypes::ShaderTypes() {
shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["UV2"] = constt(ShaderLanguage::TYPE_VEC2);
shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["COLOR"] = constt(ShaderLanguage::TYPE_VEC4);
shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["ALBEDO"] = ShaderLanguage::TYPE_VEC3;
shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["PREMULT_ALPHA_FACTOR"] = ShaderLanguage::TYPE_FLOAT;
shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["ALPHA"] = ShaderLanguage::TYPE_FLOAT;
shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["METALLIC"] = ShaderLanguage::TYPE_FLOAT;
shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["SPECULAR"] = ShaderLanguage::TYPE_FLOAT;
Expand Down Expand Up @@ -208,7 +209,7 @@ ShaderTypes::ShaderTypes() {

// spatial render modes
{
shader_modes[RS::SHADER_SPATIAL].modes.push_back({ PNAME("blend"), "mix", "add", "sub", "mul" });
shader_modes[RS::SHADER_SPATIAL].modes.push_back({ PNAME("blend"), "mix", "add", "sub", "mul", "premult_alpha" });
shader_modes[RS::SHADER_SPATIAL].modes.push_back({ PNAME("depth_draw"), "opaque", "always", "never" });
shader_modes[RS::SHADER_SPATIAL].modes.push_back({ PNAME("depth_prepass_alpha") });
shader_modes[RS::SHADER_SPATIAL].modes.push_back({ PNAME("depth_test_disabled") });
Expand Down

0 comments on commit 41a2b0e

Please sign in to comment.