diff --git a/addons/material_maker/engine/gen_base.gd b/addons/material_maker/engine/gen_base.gd
index e665262..17793a5 100644
--- a/addons/material_maker/engine/gen_base.gd
+++ b/addons/material_maker/engine/gen_base.gd
@@ -53,6 +53,9 @@ func toggle_editable() -> bool:
func is_template() -> bool:
return model != null
+func get_template_name() -> bool:
+ return model
+
func is_editable() -> bool:
return false
diff --git a/addons/material_maker/nodes/tones.mmg b/addons/material_maker/nodes/tones.mmg
new file mode 100644
index 0000000..be161f0
--- /dev/null
+++ b/addons/material_maker/nodes/tones.mmg
@@ -0,0 +1,122 @@
+{
+ "name": "levels",
+ "node_position": {
+ "x": 0,
+ "y": 0
+ },
+ "parameters": {
+ "in_max": {
+ "a": 1,
+ "b": 1,
+ "g": 1,
+ "r": 1,
+ "type": "Color"
+ },
+ "in_mid": {
+ "a": 0.5,
+ "b": 0.5,
+ "g": 0.5,
+ "r": 0.5,
+ "type": "Color"
+ },
+ "in_min": {
+ "a": 0,
+ "b": 0,
+ "g": 0,
+ "r": 0,
+ "type": "Color"
+ },
+ "out_max": {
+ "a": 1,
+ "b": 1,
+ "g": 1,
+ "r": 1,
+ "type": "Color"
+ },
+ "out_min": {
+ "a": 0,
+ "b": 0,
+ "g": 0,
+ "r": 0,
+ "type": "Color"
+ }
+ },
+ "shader_model": {
+ "code": "",
+ "global": "vec4 adjust_levels(vec4 input, vec4 in_min, vec4 in_mid, vec4 in_max, vec4 out_min, vec4 out_max) {\n\tinput = clamp((input-in_min)/(in_max-in_min), 0.0, 1.0);\n\tin_mid = (in_mid-in_min)/(in_max-in_min);\n\tvec4 dark = step(in_mid, input);\n\tinput = 0.5*mix(input/(in_mid), 1.0+(input-in_mid)/(1.0-in_mid), dark);\n\treturn out_min+input*(out_max-out_min);\n}\n",
+ "inputs": [
+ {
+ "default": "vec4(1.0)",
+ "label": "",
+ "name": "input",
+ "type": "rgba"
+ }
+ ],
+ "instance": "",
+ "name": "Levels",
+ "outputs": [
+ {
+ "rgba": "adjust_levels($input($uv), $in_min, $in_mid, $in_max, $out_min, $out_max)",
+ "type": "rgba"
+ }
+ ],
+ "parameters": [
+ {
+ "default": {
+ "a": 0,
+ "b": 0,
+ "g": 0,
+ "r": 0
+ },
+ "label": "",
+ "name": "in_min",
+ "type": "color"
+ },
+ {
+ "default": {
+ "a": 0.498039,
+ "b": 0.498039,
+ "g": 0.498039,
+ "r": 0.498039
+ },
+ "label": "",
+ "name": "in_mid",
+ "type": "color"
+ },
+ {
+ "default": {
+ "a": 1,
+ "b": 1,
+ "g": 1,
+ "r": 1
+ },
+ "label": "",
+ "name": "in_max",
+ "type": "color"
+ },
+ {
+ "default": {
+ "a": 1,
+ "b": 0,
+ "g": 0,
+ "r": 0
+ },
+ "label": "",
+ "name": "out_min",
+ "type": "color"
+ },
+ {
+ "default": {
+ "a": 1,
+ "b": 1,
+ "g": 1,
+ "r": 1
+ },
+ "label": "",
+ "name": "out_max",
+ "type": "color"
+ }
+ ]
+ },
+ "type": "shader"
+}
\ No newline at end of file
diff --git a/material_maker/doc/images/node_filter_tones.png b/material_maker/doc/images/node_filter_tones.png
new file mode 100644
index 0000000..a2acbf2
Binary files /dev/null and b/material_maker/doc/images/node_filter_tones.png differ
diff --git a/material_maker/doc/node_filter_tones.rst b/material_maker/doc/node_filter_tones.rst
new file mode 100644
index 0000000..e4cbb26
--- /dev/null
+++ b/material_maker/doc/node_filter_tones.rst
@@ -0,0 +1,38 @@
+Tones node
+~~~~~~~~~~
+
+The **Tones** node provides an easy interface to adjust the tones of its input. It can be applied
+homogeneously to the R, G and B channels, or separately on R, G, B and A.
+
+The nodes shows an histogram of all channels of its input, and 3 cursors to modify the input
+adjustment at the top, and 2 cursors to modify the output adjustment.
+
+.. image:: images/node_filter_tones.png
+ :align: center
+
+Inputs
+++++++
+
+The **Tones** node requires an RGBA input texture.
+
+Outputs
++++++++
+
+The **Tones** node provides a single RGBA texture.
+
+Parameters
+++++++++++
+
+At the top of the node, a control can be used to select the active channel (Luminance, Red,
+Green, Blue and Alpha).
+
+The button can be used to adjust automatically the Tones to the node's input to obtain better
+contrast.
+
+The 3 cursors at the top of the histogram can be used to select the input colors that will be
+remapped to black (value = 0 for single channel), mid-grey (value = 0.5) and white (value = 1).
+Values between those defined by cursors are interpolated linearly, and all values are clamped
+between 0 and 1.
+
+The 2 bottom cursors define the output color for black (value = 0 for single channel) and
+white (value = 1).
\ No newline at end of file
diff --git a/material_maker/doc/nodes_filter.rst b/material_maker/doc/nodes_filter.rst
index 06d11e9..1d04094 100644
--- a/material_maker/doc/nodes_filter.rst
+++ b/material_maker/doc/nodes_filter.rst
@@ -9,6 +9,7 @@ The filter nodes accept one or several inputs and generate one or several images
node_filter_invert
node_filter_brightness_contrast
node_filter_adjust_hsv
+ node_filter_tones
node_filter_greyscale
node_filter_colorize
node_filter_combine
diff --git a/material_maker/graph_edit.gd b/material_maker/graph_edit.gd
index 4c2e658..ba166a1 100644
--- a/material_maker/graph_edit.gd
+++ b/material_maker/graph_edit.gd
@@ -166,7 +166,7 @@ func clear_material() -> void:
func update_graph(generators, connections) -> Array:
var rv = []
for g in generators:
- var node = node_factory.create_node(g.get_type())
+ var node = node_factory.create_node(g.get_template_name() if g.is_template() else "", g.get_type())
if node != null:
node.name = "node_"+g.name
add_node(node)
diff --git a/material_maker/icons/icons.svg b/material_maker/icons/icons.svg
index ab74fb5..f4a511c 100644
--- a/material_maker/icons/icons.svg
+++ b/material_maker/icons/icons.svg
@@ -55,9 +55,9 @@
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
- inkscape:zoom="40.459032"
- inkscape:cx="12.31719"
- inkscape:cy="41.781829"
+ inkscape:zoom="7.152214"
+ inkscape:cx="122.70346"
+ inkscape:cy="36.247815"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
@@ -605,5 +605,42 @@
sodipodi:end="0.0018129957"
sodipodi:open="true"
d="m 15.411452,304.10377 a 7.4148879,7.4346347 0 0 1 -7.4502054,7.39166 7.4148879,7.4346347 0 0 1 -7.37939334,-7.46273 7.4148879,7.4346347 0 0 1 7.43561054,-7.40642 7.4148879,7.4346347 0 0 1 7.3941002,7.44808" />
+
+
+
+
+
diff --git a/material_maker/library/base.json b/material_maker/library/base.json
index a2962cc..b792392 100644
--- a/material_maker/library/base.json
+++ b/material_maker/library/base.json
@@ -2289,6 +2289,50 @@
"tree_item": "Filter/AdjustHSV",
"type": "adjust_hsv"
},
+ {
+ "collapsed": true,
+ "icon": "filter_tones",
+ "name": "tones",
+ "parameters": {
+ "in_max": {
+ "a": 1,
+ "b": 1,
+ "g": 1,
+ "r": 1,
+ "type": "Color"
+ },
+ "in_mid": {
+ "a": 0.5,
+ "b": 0.5,
+ "g": 0.5,
+ "r": 0.5,
+ "type": "Color"
+ },
+ "in_min": {
+ "a": 0,
+ "b": 0,
+ "g": 0,
+ "r": 0,
+ "type": "Color"
+ },
+ "out_max": {
+ "a": 1,
+ "b": 1,
+ "g": 1,
+ "r": 1,
+ "type": "Color"
+ },
+ "out_min": {
+ "a": 0,
+ "b": 0,
+ "g": 0,
+ "r": 0,
+ "type": "Color"
+ }
+ },
+ "tree_item": "Filter/Tones",
+ "type": "tones"
+ },
{
"collapsed": true,
"icon": "filter_greyscale",
diff --git a/material_maker/library/base/filter_tones.png b/material_maker/library/base/filter_tones.png
new file mode 100644
index 0000000..b2809d0
Binary files /dev/null and b/material_maker/library/base/filter_tones.png differ
diff --git a/material_maker/main_window.gd b/material_maker/main_window.gd
index 0434639..be7af06 100644
--- a/material_maker/main_window.gd
+++ b/material_maker/main_window.gd
@@ -462,7 +462,7 @@ func make_selected_nodes_editable() -> void:
var selected_nodes = get_selected_nodes()
if !selected_nodes.empty():
for n in selected_nodes:
- if n.generator.toggle_editable():
+ if n.generator.toggle_editable() and n.has_method("update_node"):
n.update_node()
func add_to_user_library() -> void:
diff --git a/material_maker/node_factory.gd b/material_maker/node_factory.gd
index 12a908e..7548ff4 100644
--- a/material_maker/node_factory.gd
+++ b/material_maker/node_factory.gd
@@ -2,13 +2,16 @@ extends Node
var includes
-func create_node(type) -> Node:
+func create_node(model : String, type : String) -> Node:
+ var node_type = null
var node = null
- var file_name = "res://material_maker/nodes/"+type+".tscn"
+ var file_name = "res://material_maker/nodes/"+model+".tscn"
+ if ! ResourceLoader.exists(file_name):
+ file_name = "res://material_maker/nodes/"+type+".tscn"
if ResourceLoader.exists(file_name):
- var node_type = load(file_name)
- if node_type != null:
- node = node_type.instance()
+ node_type = load(file_name)
+ if node_type != null:
+ node = node_type.instance()
if node == null:
node = preload("res://material_maker/nodes/generic.tscn").instance()
return node
diff --git a/material_maker/nodes/tones.gd b/material_maker/nodes/tones.gd
new file mode 100644
index 0000000..aff0e33
--- /dev/null
+++ b/material_maker/nodes/tones.gd
@@ -0,0 +1,170 @@
+extends MMGraphNodeBase
+
+class Cursor:
+ extends Control
+
+ var color : Color
+ var top : bool = true
+ var position : float
+
+ const WIDTH : int = 8
+ const HEIGHT : int = 8
+
+ func _init(c, p, t = true):
+ color = c
+ position = p
+ top = t
+
+ func _ready() -> void:
+ rect_position.y = -2 if top else get_parent().rect_size.y+2-HEIGHT
+ set_value(position)
+ rect_size = Vector2(WIDTH, HEIGHT)
+
+ func _draw() -> void:
+ var polygon : PoolVector2Array
+ if top:
+ polygon = PoolVector2Array([Vector2(0, 0), Vector2(WIDTH/2, HEIGHT), Vector2(WIDTH, 0), Vector2(0, 0)])
+ else:
+ polygon = PoolVector2Array([Vector2(0, HEIGHT), Vector2(WIDTH/2, 0), Vector2(WIDTH, HEIGHT), Vector2(0, HEIGHT)])
+ var c = color
+ c.a = 1.0
+ draw_colored_polygon(polygon, c)
+ var outline_color = 0.0 if position > 0.5 else 1.0
+ draw_polyline(polygon, Color(outline_color, outline_color, outline_color), 1.0, true)
+
+ func _gui_input(ev) -> void:
+ if ev is InputEventMouseMotion && (ev.button_mask & 1) != 0:
+ rect_position.x += ev.relative.x
+ rect_position.x = min(max(-0.5*WIDTH, rect_position.x), get_parent().rect_size.x-0.5*WIDTH)
+ update_value((rect_position.x+0.5*WIDTH)/get_parent().rect_size.x)
+
+ func update_value(p : float) -> void:
+ if p != position:
+ set_value(p)
+ get_parent().get_parent().update_value(self, position)
+ update()
+
+ func set_value(v : float):
+ position = v
+ rect_position.x = position * get_parent().rect_size.x - 0.5*WIDTH
+
+var cursor_in_min : Cursor
+var cursor_in_mid : Cursor
+var cursor_in_max : Cursor
+var cursor_out_min : Cursor
+var cursor_out_max : Cursor
+
+func _ready() -> void:
+ var slot_color = mm_io_types.types["rgba"].color
+ var slot_type = mm_io_types.types["rgba"].slot_type
+ set_slot(0, true, slot_type, slot_color, true, slot_type, slot_color)
+ cursor_in_min = Cursor.new(Color(0.0, 0.0, 0.0), 0.0)
+ $Histogram.add_child(cursor_in_min)
+ cursor_in_mid = Cursor.new(Color(0.5, 0.5, 0.5), 0.5)
+ $Histogram.add_child(cursor_in_mid)
+ cursor_in_max = Cursor.new(Color(1.0, 1.0, 1.0), 1.0)
+ $Histogram.add_child(cursor_in_max)
+ cursor_out_min = Cursor.new(Color(0.0, 0.0, 0.0), 0.0, false)
+ $Histogram.add_child(cursor_out_min)
+ cursor_out_max = Cursor.new(Color(1.0, 1.0, 1.0), 1.0, false)
+ $Histogram.add_child(cursor_out_max)
+
+func set_generator(g) -> void:
+ .set_generator(g)
+ generator.connect("parameter_changed", self, "on_parameter_changed")
+ _on_Mode_item_selected(0)
+
+func on_parameter_changed(p, v) -> void:
+ if p == "__input_changed__":
+ var source = generator.get_source(0)
+ var result = source.generator.render(source.output_index, 128, true)
+ while result is GDScriptFunctionState:
+ result = yield(result, "completed")
+ result.copy_to_texture($Histogram.get_image_texture())
+ result.release()
+
+func get_parameter(n : String) -> float:
+ var value = generator.get_parameter(n)
+ match $Bar/Mode.selected:
+ 1:
+ return value.r
+ 2:
+ return value.g
+ 3:
+ return value.b
+ 4:
+ return value.a
+ return (value.r+value.g+value.b)/3.0
+
+func _on_Mode_item_selected(_id):
+ cursor_in_min.set_value(get_parameter("in_min"))
+ cursor_in_mid.set_value(get_parameter("in_mid"))
+ cursor_in_max.set_value(get_parameter("in_max"))
+ cursor_out_min.set_value(get_parameter("out_min"))
+ cursor_out_max.set_value(get_parameter("out_max"))
+
+func set_parameter(n : String, v : float, d : float) -> void:
+ var value = generator.get_parameter(n)
+ match $Bar/Mode.selected:
+ 0:
+ value.r = v
+ value.g = v
+ value.b = v
+ value.a = d
+ 1:
+ value.r = v
+ 2:
+ value.g = v
+ 3:
+ value.b = v
+ 4:
+ value.a = v
+ generator.set_parameter(n, value)
+
+func update_value(control : Cursor, value : float) -> void:
+ match control:
+ cursor_in_min:
+ set_parameter("in_min", value, 0)
+ cursor_in_mid:
+ set_parameter("in_mid", value, 0.5)
+ cursor_in_max:
+ set_parameter("in_max", value, 1)
+ cursor_out_min:
+ set_parameter("out_min", value, 0)
+ cursor_out_max:
+ set_parameter("out_max", value, 1)
+ get_parent().send_changed_signal()
+
+func _on_Auto_pressed():
+ var histogram = $Histogram.get_histogram_texture().get_data()
+ histogram.lock()
+ var in_min : int = -1
+ var in_mid : int = -1
+ var in_mid_value : float = 0
+ var in_max : int = -1
+ var histogram_size = histogram.get_size().x
+ for i in range(histogram_size):
+ var color : Color = histogram.get_pixel(i, 0)
+ var value : float
+ match $Bar/Mode.selected:
+ 0:
+ value = (color.r+color.g+color.b)/3.0
+ 1:
+ value = color.r
+ 2:
+ value = color.g
+ 3:
+ value = color.b
+ 4:
+ value = color.a
+ if value > 0.0:
+ if in_min == -1:
+ in_min = i
+ in_max = i
+ if in_mid_value < value:
+ in_mid = i
+ in_mid_value = value
+ histogram.unlock()
+ cursor_in_min.update_value(in_min/(histogram_size-1))
+ cursor_in_mid.update_value(in_mid/(histogram_size-1))
+ cursor_in_max.update_value(in_max/(histogram_size-1))
diff --git a/material_maker/nodes/tones.tscn b/material_maker/nodes/tones.tscn
new file mode 100644
index 0000000..bd5f634
--- /dev/null
+++ b/material_maker/nodes/tones.tscn
@@ -0,0 +1,103 @@
+[gd_scene load_steps=5 format=2]
+
+[ext_resource path="res://material_maker/nodes/tones.gd" type="Script" id=1]
+[ext_resource path="res://material_maker/widgets/histogram/histogram.tscn" type="PackedScene" id=2]
+[ext_resource path="res://material_maker/icons/icons.svg" type="Texture" id=3]
+
+[sub_resource type="AtlasTexture" id=1]
+flags = 4
+atlas = ExtResource( 3 )
+region = Rect2( 16, 80, 16, 16 )
+
+[node name="Tones" type="GraphNode"]
+anchor_right = 1.0
+anchor_bottom = 1.0
+margin_left = 2.75074
+margin_top = 1.78928
+margin_right = -1053.25
+margin_bottom = -529.211
+title = "Tones"
+show_close = true
+slot/0/left_enabled = true
+slot/0/left_type = 0
+slot/0/left_color = Color( 0, 0.396078, 1, 1 )
+slot/0/right_enabled = true
+slot/0/right_type = 0
+slot/0/right_color = Color( 0, 0.415686, 1, 1 )
+slot/1/left_enabled = false
+slot/1/left_type = 0
+slot/1/left_color = Color( 1, 1, 1, 1 )
+slot/1/right_enabled = false
+slot/1/right_type = 0
+slot/1/right_color = Color( 1, 1, 1, 1 )
+slot/2/left_enabled = false
+slot/2/left_type = 0
+slot/2/left_color = Color( 1, 1, 1, 1 )
+slot/2/right_enabled = false
+slot/2/right_type = 0
+slot/2/right_color = Color( 1, 1, 1, 1 )
+slot/3/left_enabled = false
+slot/3/left_type = 0
+slot/3/left_color = Color( 1, 1, 1, 1 )
+slot/3/right_enabled = false
+slot/3/right_type = 0
+slot/3/right_color = Color( 1, 1, 1, 1 )
+script = ExtResource( 1 )
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="Bar" type="HBoxContainer" parent="."]
+margin_left = 16.0
+margin_top = 24.0
+margin_right = 208.0
+margin_bottom = 44.0
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="Mode" type="OptionButton" parent="Bar"]
+margin_right = 172.0
+margin_bottom = 20.0
+size_flags_horizontal = 3
+text = "Luminance"
+items = [ "Luminance", null, false, 0, null, "Red", null, false, 1, null, "Green", null, false, 2, null, "Blue", null, false, 3, null, "Alpha", null, false, 4, null ]
+selected = 0
+
+[node name="Auto" type="TextureButton" parent="Bar"]
+margin_left = 176.0
+margin_top = 2.0
+margin_right = 192.0
+margin_bottom = 18.0
+hint_tooltip = "Set levels automatically"
+size_flags_vertical = 4
+texture_normal = SubResource( 1 )
+
+[node name="Spacer1" type="Control" parent="."]
+margin_left = 16.0
+margin_top = 45.0
+margin_right = 208.0
+margin_bottom = 49.0
+rect_min_size = Vector2( 0, 4 )
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="Histogram" parent="." instance=ExtResource( 2 )]
+margin_left = 16.0
+margin_top = 50.0
+margin_right = 208.0
+margin_bottom = 178.0
+rect_min_size = Vector2( 192, 128 )
+
+[node name="Spacer2" type="Control" parent="."]
+margin_left = 16.0
+margin_top = 179.0
+margin_right = 208.0
+margin_bottom = 183.0
+rect_min_size = Vector2( 0, 4 )
+__meta__ = {
+"_edit_use_anchors_": false
+}
+[connection signal="item_selected" from="Bar/Mode" to="." method="_on_Mode_item_selected"]
+[connection signal="pressed" from="Bar/Auto" to="." method="_on_Auto_pressed"]
diff --git a/material_maker/widgets/histogram/histogram.gd b/material_maker/widgets/histogram/histogram.gd
new file mode 100644
index 0000000..d2ae909
--- /dev/null
+++ b/material_maker/widgets/histogram/histogram.gd
@@ -0,0 +1,10 @@
+extends Control
+
+func update_histogram() -> void:
+ pass
+
+func get_image_texture() -> ImageTexture:
+ return $ViewportImage/ColorRect.material.get_shader_param("tex")
+
+func get_histogram_texture() -> ImageTexture:
+ return $Control.material.get_shader_param("tex")
diff --git a/material_maker/widgets/histogram/histogram.tscn b/material_maker/widgets/histogram/histogram.tscn
new file mode 100644
index 0000000..02670ed
--- /dev/null
+++ b/material_maker/widgets/histogram/histogram.tscn
@@ -0,0 +1,157 @@
+[gd_scene load_steps=14 format=2]
+
+[ext_resource path="res://material_maker/widgets/histogram/histogram.gd" type="Script" id=1]
+
+[sub_resource type="Shader" id=1]
+resource_local_to_scene = true
+code = "shader_type canvas_item;
+render_mode blend_disabled;
+
+uniform sampler2D tex;
+
+void fragment() {
+ COLOR = texture(tex, UV);
+}
+"
+
+[sub_resource type="ImageTexture" id=2]
+resource_local_to_scene = true
+
+[sub_resource type="ShaderMaterial" id=3]
+resource_local_to_scene = true
+shader = SubResource( 1 )
+shader_param/tex = SubResource( 2 )
+
+[sub_resource type="Shader" id=4]
+code = "shader_type canvas_item;
+render_mode blend_disabled;
+
+uniform sampler2D tex;
+
+void fragment() {
+ vec4 sum = vec4(0.0);
+ for (int i = 0; i < 128; ++i) {
+ sum += step(abs(texture(tex, vec2(UV.x, float(i)/127.0))-UV.y), vec4(0.02));
+ }
+ COLOR = sum/255.0;
+}
+"
+
+[sub_resource type="ViewportTexture" id=5]
+viewport_path = NodePath("ViewportImage")
+
+[sub_resource type="ShaderMaterial" id=6]
+resource_local_to_scene = true
+shader = SubResource( 4 )
+shader_param/tex = SubResource( 5 )
+
+[sub_resource type="Shader" id=7]
+code = "shader_type canvas_item;
+render_mode blend_disabled;
+
+uniform sampler2D tex;
+
+void fragment() {
+ vec4 sum = vec4(0.0);
+ for (int i = 0; i < 128; ++i) {
+ sum += texture(tex, vec2(float(i)/127.0, UV.x));
+ }
+ COLOR = sum/255.0;
+}"
+
+[sub_resource type="ViewportTexture" id=8]
+viewport_path = NodePath("ViewportHistogram1")
+
+[sub_resource type="ShaderMaterial" id=9]
+resource_local_to_scene = true
+shader = SubResource( 7 )
+shader_param/tex = SubResource( 8 )
+
+[sub_resource type="Shader" id=10]
+code = "shader_type canvas_item;
+
+uniform sampler2D tex;
+render_mode blend_disabled;
+
+void fragment() {
+ if (abs(fract(UV.y+0.05)) < 0.1) {
+ COLOR = vec4(vec3(UV.x), 1.0);
+ } else {
+ vec4 highest = vec4(0.0);
+ for (int i = 0; i < 128; ++i) {
+ highest = max(highest, texture(tex, vec2(float(i)/128.0, 0.0)));
+ }
+ vec4 value = step(vec4(0.95-UV.y)*highest/0.9, texture(tex, vec2(UV.x, 0.0)));
+ float alpha = step(0.1, dot(value, vec4(1.0)));
+ COLOR = vec4(mix(value.rgb, vec3(0.5), 0.3*value.a), alpha);
+ }
+}"
+
+[sub_resource type="ViewportTexture" id=11]
+flags = 4
+viewport_path = NodePath("ViewportHistogram2")
+
+[sub_resource type="ShaderMaterial" id=12]
+resource_local_to_scene = true
+shader = SubResource( 10 )
+shader_param/tex = SubResource( 11 )
+
+[node name="Histogram" type="Control"]
+margin_right = 64.0
+margin_bottom = 64.0
+script = ExtResource( 1 )
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="ViewportImage" type="Viewport" parent="."]
+size = Vector2( 256, 256 )
+transparent_bg = true
+hdr = false
+usage = 0
+render_target_v_flip = true
+render_target_update_mode = 3
+
+[node name="ColorRect" type="ColorRect" parent="ViewportImage"]
+material = SubResource( 3 )
+margin_right = 256.0
+margin_bottom = 128.0
+rect_min_size = Vector2( 256, 256 )
+
+[node name="ViewportHistogram1" type="Viewport" parent="."]
+size = Vector2( 128, 128 )
+own_world = true
+transparent_bg = true
+hdr = false
+usage = 0
+render_target_v_flip = true
+render_target_update_mode = 3
+
+[node name="ColorRect" type="ColorRect" parent="ViewportHistogram1"]
+material = SubResource( 6 )
+margin_right = 128.0
+margin_bottom = 128.0
+rect_min_size = Vector2( 128, 128 )
+
+[node name="ViewportHistogram2" type="Viewport" parent="."]
+size = Vector2( 128, 2 )
+transparent_bg = true
+disable_3d = true
+keep_3d_linear = true
+usage = 0
+render_target_v_flip = true
+render_target_update_mode = 3
+
+[node name="ColorRect" type="ColorRect" parent="ViewportHistogram2"]
+material = SubResource( 9 )
+margin_right = 128.0
+margin_bottom = 2.0
+rect_min_size = Vector2( 128, 2 )
+
+[node name="Control" type="ColorRect" parent="."]
+material = SubResource( 12 )
+anchor_right = 1.0
+anchor_bottom = 1.0
+__meta__ = {
+"_edit_use_anchors_": false
+}