Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions doc/classes/CanvasItem.xml
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,13 @@
Returns the transform from the local coordinate system of this [CanvasItem] to the [Viewport]s coordinate system.
</description>
</method>
<method name="get_instance_shader_parameter" qualifiers="const">
<return type="Variant" />
<param index="0" name="name" type="StringName" />
<description>
Get the value of a shader parameter as set on this instance.
</description>
</method>
<method name="get_local_mouse_position" qualifiers="const">
<return type="Vector2" />
<description>
Expand Down Expand Up @@ -562,6 +569,16 @@
Queues the [CanvasItem] to redraw. During idle time, if [CanvasItem] is visible, [constant NOTIFICATION_DRAW] is sent and [method _draw] is called. This only occurs [b]once[/b] per frame, even if this method has been called multiple times.
</description>
</method>
<method name="set_instance_shader_parameter">
<return type="void" />
<param index="0" name="name" type="StringName" />
<param index="1" name="value" type="Variant" />
<description>
Set the value of a shader uniform for this instance only ([url=$DOCS_URL/tutorials/shaders/shader_reference/shading_language.html#per-instance-uniforms]per-instance uniform[/url]). See also [method ShaderMaterial.set_shader_parameter] to assign a uniform on all instances using the same [ShaderMaterial].
[b]Note:[/b] For a shader uniform to be assignable on a per-instance basis, it [i]must[/i] be defined with [code]instance uniform ...[/code] rather than [code]uniform ...[/code] in the shader code.
[b]Note:[/b] [param name] is case-sensitive and must match the name of the uniform in the code exactly (not the capitalized name in the inspector).
</description>
</method>
<method name="set_notify_local_transform">
<return type="void" />
<param index="0" name="enable" type="bool" />
Expand Down
2 changes: 1 addition & 1 deletion doc/classes/GeometryInstance3D.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
Set the value of a shader uniform for this instance only ([url=$DOCS_URL/tutorials/shaders/shader_reference/shading_language.html#per-instance-uniforms]per-instance uniform[/url]). See also [method ShaderMaterial.set_shader_parameter] to assign a uniform on all instances using the same [ShaderMaterial].
[b]Note:[/b] For a shader uniform to be assignable on a per-instance basis, it [i]must[/i] be defined with [code]instance uniform ...[/code] rather than [code]uniform ...[/code] in the shader code.
[b]Note:[/b] [param name] is case-sensitive and must match the name of the uniform in the code exactly (not the capitalized name in the inspector).
[b]Note:[/b] Per-instance shader uniforms are currently only available in 3D, so there is no 2D equivalent of this method.
[b]Note:[/b] Per-instance shader uniforms are only available in Spatial and CanvasItem shaders, but not for Fog, Sky, or Particles shaders.
</description>
</method>
</methods>
Expand Down
33 changes: 33 additions & 0 deletions doc/classes/RenderingServer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,30 @@
[b]Note:[/b] The equivalent node is [CanvasItem].
</description>
</method>
<method name="canvas_item_get_instance_shader_parameter" qualifiers="const">
<return type="Variant" />
<param index="0" name="instance" type="RID" />
<param index="1" name="parameter" type="StringName" />
<description>
Returns the value of the per-instance shader uniform from the specified canvas item instance. Equivalent to [method CanvasItem.get_instance_shader_parameter].
</description>
</method>
<method name="canvas_item_get_instance_shader_parameter_default_value" qualifiers="const">
<return type="Variant" />
<param index="0" name="instance" type="RID" />
<param index="1" name="parameter" type="StringName" />
<description>
Returns the default value of the per-instance shader uniform from the specified canvas item instance. Equivalent to [method CanvasItem.get_instance_shader_parameter].
</description>
</method>
<method name="canvas_item_get_instance_shader_parameter_list" qualifiers="const">
<return type="Dictionary[]" />
<param index="0" name="instance" type="RID" />
<description>
Returns a dictionary of per-instance shader uniform names of the per-instance shader uniform from the specified canvas item instance.
The returned dictionary is in PropertyInfo format, with the keys [code]name[/code], [code]class_name[/code], [code]type[/code], [code]hint[/code], [code]hint_string[/code], and [code]usage[/code].
</description>
</method>
<method name="canvas_item_reset_physics_interpolation">
<return type="void" />
<param index="0" name="item" type="RID" />
Expand Down Expand Up @@ -524,6 +548,15 @@
Sets the index for the [CanvasItem].
</description>
</method>
<method name="canvas_item_set_instance_shader_parameter">
<return type="void" />
<param index="0" name="instance" type="RID" />
<param index="1" name="parameter" type="StringName" />
<param index="2" name="value" type="Variant" />
<description>
Sets the per-instance shader uniform on the specified canvas item instance. Equivalent to [method CanvasItem.set_instance_shader_parameter].
</description>
</method>
<method name="canvas_item_set_interpolated">
<return type="void" />
<param index="0" name="item" type="RID" />
Expand Down
15 changes: 8 additions & 7 deletions drivers/gles3/rasterizer_canvas_gles3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -727,6 +727,7 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou

// Bind per-batch uniforms.
material_storage->shaders.canvas_shader.version_set_uniform(CanvasShaderGLES3::BATCH_FLAGS, state.canvas_instance_batches[i].flags, shader_version, variant, specialization);
material_storage->shaders.canvas_shader.version_set_uniform(CanvasShaderGLES3::SPECULAR_SHININESS_IN, state.canvas_instance_batches[i].specular_shininess, shader_version, variant, specialization);

GLES3::CanvasShaderData::BlendMode blend_mode = state.canvas_instance_batches[i].blend_mode;
Color blend_color = state.canvas_instance_batches[i].blend_color;
Expand Down Expand Up @@ -907,15 +908,15 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
state.instance_data_array[r_index].color_texture_pixel_size[0] = 0.0;
state.instance_data_array[r_index].color_texture_pixel_size[1] = 0.0;

state.instance_data_array[r_index].pad[0] = 0.0;
state.instance_data_array[r_index].pad[1] = 0.0;

state.instance_data_array[r_index].lights[0] = lights[0];
state.instance_data_array[r_index].lights[1] = lights[1];
state.instance_data_array[r_index].lights[2] = lights[2];
state.instance_data_array[r_index].lights[3] = lights[3];

state.instance_data_array[r_index].flags = base_flags;
state.instance_data_array[r_index].instance_uniforms_ofs = p_item->instance_allocated_shader_uniforms_offset;

state.instance_data_array[r_index].flags = base_flags | (state.instance_data_array[r_index == 0 ? 0 : r_index - 1].flags & (BATCH_FLAGS_DEFAULT_NORMAL_MAP_USED | BATCH_FLAGS_DEFAULT_SPECULAR_MAP_USED)); // Reset on each command for safety, keep canvastexture binding config.

Color blend_color = base_color;
GLES3::CanvasShaderData::BlendMode blend_mode = p_blend_mode;
Expand Down Expand Up @@ -2387,10 +2388,10 @@ void RasterizerCanvasGLES3::_prepare_canvas_texture(RID p_texture, RS::CanvasIte
state.canvas_instance_batches[state.current_batch_index].flags &= ~BATCH_FLAGS_DEFAULT_NORMAL_MAP_USED;
}

state.instance_data_array[r_index].specular_shininess = uint32_t(CLAMP(ct->specular_color.a * 255.0, 0, 255)) << 24;
state.instance_data_array[r_index].specular_shininess |= uint32_t(CLAMP(ct->specular_color.b * 255.0, 0, 255)) << 16;
state.instance_data_array[r_index].specular_shininess |= uint32_t(CLAMP(ct->specular_color.g * 255.0, 0, 255)) << 8;
state.instance_data_array[r_index].specular_shininess |= uint32_t(CLAMP(ct->specular_color.r * 255.0, 0, 255));
state.canvas_instance_batches[state.current_batch_index].specular_shininess = uint32_t(CLAMP(ct->specular_color.a * 255.0, 0, 255)) << 24;
state.canvas_instance_batches[state.current_batch_index].specular_shininess |= uint32_t(CLAMP(ct->specular_color.b * 255.0, 0, 255)) << 16;
state.canvas_instance_batches[state.current_batch_index].specular_shininess |= uint32_t(CLAMP(ct->specular_color.g * 255.0, 0, 255)) << 8;
state.canvas_instance_batches[state.current_batch_index].specular_shininess |= uint32_t(CLAMP(ct->specular_color.r * 255.0, 0, 255));

r_texpixel_size.x = 1.0 / float(size_cache.x);
r_texpixel_size.y = 1.0 / float(size_cache.y);
Expand Down
5 changes: 4 additions & 1 deletion drivers/gles3/rasterizer_canvas_gles3.h
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ class RasterizerCanvasGLES3 : public RendererCanvasRender {
};
};
uint32_t flags;
uint32_t specular_shininess;
uint32_t instance_uniforms_ofs;
uint32_t lights[4];
};

Expand Down Expand Up @@ -279,6 +279,9 @@ class RasterizerCanvasGLES3 : public RendererCanvasRender {
uint32_t primitive_points = 0;

uint32_t flags = 0;
uint32_t specular_shininess = 0.0;

bool lights_disabled = false;
};

// DataBuffer contains our per-frame data. I.e. the resources that are updated each frame.
Expand Down
9 changes: 5 additions & 4 deletions drivers/gles3/shaders/canvas.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ layout(location = 15) in highp uvec4 attrib_H;
#endif

#define read_draw_data_flags attrib_G.z
#define read_draw_data_specular_shininess attrib_G.w
#define read_draw_data_instance_offset attrib_G.w
#define read_draw_data_lights attrib_H

// Varyings so the per-instance info can be used in the fragment shader
Expand Down Expand Up @@ -142,7 +142,7 @@ void main() {
#endif // !USE_ATTRIBUTES
#endif // USE_PRIMITIVE

varying_F = uvec2(read_draw_data_flags, read_draw_data_specular_shininess);
varying_F = uvec2(read_draw_data_flags, read_draw_data_instance_offset);
varying_G = read_draw_data_lights;

vec4 instance_custom = vec4(0.0);
Expand Down Expand Up @@ -325,7 +325,7 @@ flat in vec4 varying_E;
flat in uvec2 varying_F;
flat in uvec4 varying_G;
#define read_draw_data_flags varying_F.x
#define read_draw_data_specular_shininess varying_F.y
#define read_draw_data_instance_offset varying_F.y
#define read_draw_data_lights varying_G

#ifndef DISABLE_LIGHTING
Expand All @@ -340,6 +340,7 @@ uniform sampler2D specular_texture; //texunit:-7
uniform sampler2D color_texture; //texunit:0

uniform mediump uint batch_flags;
uniform highp uint specular_shininess_in;

layout(location = 0) out vec4 frag_color;

Expand Down Expand Up @@ -660,7 +661,7 @@ void main() {

if (specular_shininess_used || (using_light && normal_used && bool(batch_flags & BATCH_FLAGS_DEFAULT_SPECULAR_MAP_USED))) {
specular_shininess = texture(specular_texture, uv);
specular_shininess *= godot_unpackUnorm4x8(read_draw_data_specular_shininess);
specular_shininess *= godot_unpackUnorm4x8(specular_shininess_in);
specular_shininess_used = true;
} else {
specular_shininess = vec4(1.0);
Expand Down
1 change: 1 addition & 0 deletions drivers/gles3/storage/material_storage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1193,6 +1193,7 @@ MaterialStorage::MaterialStorage() {
actions.render_mode_defines["world_vertex_coords"] = "#define USE_WORLD_VERTEX_COORDS\n";

actions.global_buffer_array_variable = "global_shader_uniforms";
actions.instance_uniform_index_variable = "read_draw_data_instance_offset";

shaders.compiler_canvas.initialize(actions);
}
Expand Down
77 changes: 77 additions & 0 deletions scene/main/canvas_item.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,60 @@ int CanvasItem::get_light_mask() const {
return light_mask;
}

const StringName *CanvasItem::_instance_shader_parameter_get_remap(const StringName &p_name) const {
StringName *r = instance_shader_parameter_property_remap.getptr(p_name);
if (!r) {
String s = p_name;
if (s.begins_with("instance_shader_parameters/")) {
StringName name = s.trim_prefix("instance_shader_parameters/");
instance_shader_parameter_property_remap[p_name] = name;
return instance_shader_parameter_property_remap.getptr(p_name);
}
return nullptr;
}
return r;
}

bool CanvasItem::_set(const StringName &p_name, const Variant &p_value) {
const StringName *r = _instance_shader_parameter_get_remap(p_name);
if (r) {
set_instance_shader_parameter(*r, p_value);
return true;
}
return false;
}

bool CanvasItem::_get(const StringName &p_name, Variant &r_ret) const {
const StringName *r = _instance_shader_parameter_get_remap(p_name);
if (r) {
r_ret = get_instance_shader_parameter(*r);
return true;
}

return false;
}

void CanvasItem::_get_property_list(List<PropertyInfo> *p_list) const {
List<PropertyInfo> pinfo;
RS::get_singleton()->canvas_item_get_instance_shader_parameter_list(get_canvas_item(), &pinfo);

for (PropertyInfo &pi : pinfo) {
bool has_def_value = false;
Variant def_value = RS::get_singleton()->canvas_item_get_instance_shader_parameter_default_value(get_canvas_item(), pi.name);
if (def_value.get_type() != Variant::NIL) {
has_def_value = true;
}
if (instance_shader_parameters.has(pi.name)) {
pi.usage = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE | (has_def_value ? (PROPERTY_USAGE_CHECKABLE | PROPERTY_USAGE_CHECKED) : PROPERTY_USAGE_NONE);
} else {
pi.usage = PROPERTY_USAGE_EDITOR | (has_def_value ? PROPERTY_USAGE_CHECKABLE : PROPERTY_USAGE_NONE); // Do not save if not changed.
}

pi.name = "instance_shader_parameters/" + pi.name;
p_list->push_back(pi);
}
}

void CanvasItem::item_rect_changed(bool p_size_changed) {
ERR_MAIN_THREAD_GUARD;
if (p_size_changed) {
Expand Down Expand Up @@ -1082,6 +1136,26 @@ void CanvasItem::set_use_parent_material(bool p_use_parent_material) {
RS::get_singleton()->canvas_item_set_use_parent_material(canvas_item, p_use_parent_material);
}

void CanvasItem::set_instance_shader_parameter(const StringName &p_name, const Variant &p_value) {
if (p_value.get_type() == Variant::NIL) {
Variant def_value = RS::get_singleton()->canvas_item_get_instance_shader_parameter_default_value(get_canvas_item(), p_name);
RS::get_singleton()->canvas_item_set_instance_shader_parameter(get_canvas_item(), p_name, def_value);
instance_shader_parameters.erase(p_value);
} else {
instance_shader_parameters[p_name] = p_value;
if (p_value.get_type() == Variant::OBJECT) {
RID tex_id = p_value;
RS::get_singleton()->canvas_item_set_instance_shader_parameter(get_canvas_item(), p_name, tex_id);
} else {
RS::get_singleton()->canvas_item_set_instance_shader_parameter(get_canvas_item(), p_name, p_value);
}
}
}

Variant CanvasItem::get_instance_shader_parameter(const StringName &p_name) const {
return RS::get_singleton()->canvas_item_get_instance_shader_parameter(get_canvas_item(), p_name);
}

bool CanvasItem::get_use_parent_material() const {
ERR_READ_THREAD_GUARD_V(false);
return use_parent_material;
Expand Down Expand Up @@ -1244,6 +1318,9 @@ void CanvasItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_material", "material"), &CanvasItem::set_material);
ClassDB::bind_method(D_METHOD("get_material"), &CanvasItem::get_material);

ClassDB::bind_method(D_METHOD("set_instance_shader_parameter", "name", "value"), &CanvasItem::set_instance_shader_parameter);
ClassDB::bind_method(D_METHOD("get_instance_shader_parameter", "name"), &CanvasItem::get_instance_shader_parameter);

ClassDB::bind_method(D_METHOD("set_use_parent_material", "enable"), &CanvasItem::set_use_parent_material);
ClassDB::bind_method(D_METHOD("get_use_parent_material"), &CanvasItem::get_use_parent_material);

Expand Down
10 changes: 10 additions & 0 deletions scene/main/canvas_item.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ class CanvasItem : public Node {
TextureRepeat texture_repeat = TEXTURE_REPEAT_PARENT_NODE;

Ref<Material> material;
mutable HashMap<StringName, Variant> instance_shader_parameters;
mutable HashMap<StringName, StringName> instance_shader_parameter_property_remap;

mutable Transform2D global_transform;
mutable MTFlag global_invalid;
Expand Down Expand Up @@ -150,8 +152,13 @@ class CanvasItem : public Node {
void _update_texture_filter_changed(bool p_propagate);

void _notify_transform_deferred();
const StringName *_instance_shader_parameter_get_remap(const StringName &p_name) const;

protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;

virtual void _update_self_texture_repeat(RS::CanvasItemTextureRepeat p_texture_repeat);
virtual void _update_self_texture_filter(RS::CanvasItemTextureFilter p_texture_filter);

Expand Down Expand Up @@ -355,6 +362,9 @@ class CanvasItem : public Node {
virtual void set_material(const Ref<Material> &p_material);
Ref<Material> get_material() const;

void set_instance_shader_parameter(const StringName &p_name, const Variant &p_value);
Variant get_instance_shader_parameter(const StringName &p_name) const;

virtual void set_use_parent_material(bool p_use_parent_material);
bool get_use_parent_material() const;

Expand Down
Loading