This page provides an overview of the visual node-based shader system: the VisualShader resource, the VisualShaderNode hierarchy, the graph data model, and how the graph is compiled to GLSL. For information on the text-based shader language and its compiler, see Shader Language & Compiler. For the rendering pipelines that consume the resulting Shader code, see Scene Rendering Pipelines.
The Visual Shader System allows users to construct shaders by connecting nodes in a directed acyclic graph (DAG) rather than writing GLSL by hand. The graph is stored in a VisualShader resource. When the graph changes, VisualShader generates a complete GLSL text shader and submits it to the RenderingServer through the same path as a hand-written Shader.
The system comprises:
VisualShader resource (graph container and code generator)VisualShaderNode base class and all concrete node typesVisualShaderNodeCustom extension point for scripted nodesFigure: High-level system structure
Sources: scene/resources/visual_shader.h42-260 scene/resources/visual_shader.cpp931-963
VisualShader ResourceVisualShader inherits from Shader and is registered in scene/register_scene_types.cpp. It is the top-level object that holds the entire graph state.
The graph is stored as a fixed-size array of Graph structs, one per VisualShader::Type stage:
scene/resources/visual_shader.h117-128
Connections are stored as Connection structs containing {from_node, from_port, to_node, to_port}. The output node always has NODE_ID_OUTPUT = 0. User-added nodes must have IDs ≥ 2.
| Enum Value | Integer | Used In |
|---|---|---|
TYPE_VERTEX | 0 | Spatial, CanvasItem — vertex stage |
TYPE_FRAGMENT | 1 | Spatial, CanvasItem — fragment stage |
TYPE_LIGHT | 2 | Spatial, CanvasItem — light stage |
TYPE_START | 3 | Particle shader — start function |
TYPE_PROCESS | 4 | Particle shader — process function |
TYPE_COLLIDE | 5 | Particle shader — collision function |
TYPE_START_CUSTOM | 6 | Particle shader — start with custom output |
TYPE_PROCESS_CUSTOM | 7 | Particle shader — process with custom output |
TYPE_SKY | 8 | Sky shader |
TYPE_FOG | 9 | Volumetric fog shader |
TYPE_TEXTURE_BLIT | 10 | DrawableTexture blit shader |
Sources: scene/resources/visual_shader.h46-59 doc/classes/VisualShader.xml194-230
Any mutation (adding a node, connecting ports, changing a node property) calls _queue_update(), which sets a SafeFlag dirty bit. The actual GLSL regeneration happens lazily in _update_shader(), which is a virtual override of Shader::_update_shader().
Sources: scene/resources/visual_shader.h149-151 scene/resources/visual_shader.cpp836-848
VisualShaderNode Base ClassEvery node in the graph is a Resource that inherits from VisualShaderNode. The class defines the complete interface for port declaration and code generation.
Figure: VisualShaderNode interface mapped to code
Sources: scene/resources/visual_shader.h269-390 scene/resources/visual_shader.h394-490 scene/resources/visual_shader.h494-551 scene/resources/visual_shader.h555-590
PortType Enum | GLSL Type | Notes |
|---|---|---|
PORT_TYPE_SCALAR | float | |
PORT_TYPE_SCALAR_INT | int | |
PORT_TYPE_SCALAR_UINT | uint | |
PORT_TYPE_VECTOR_2D | vec2 | |
PORT_TYPE_VECTOR_3D | vec3 | |
PORT_TYPE_VECTOR_4D | vec4 | |
PORT_TYPE_BOOLEAN | bool | |
PORT_TYPE_TRANSFORM | mat4 | |
PORT_TYPE_SAMPLER | sampler uniform ref | Input ports only on non-uniform nodes |
Sources: scene/resources/visual_shader.h273-284 doc/classes/VisualShaderNode.xml72-102
Unconnected input ports fall back to a default value stored in default_input_values: HashMap<int, Variant>. The type coercion logic in set_input_port_default_value() converts between compatible types (e.g., scalar → vec2 by broadcasting) when the port type changes.
Sources: scene/resources/visual_shader.cpp62-196
Vector output ports (vec2, vec3, vec4) can be expanded into their component scalar ports. This is tracked per port in expanded_output_ports: HashMap<int, bool>. get_expanded_output_port_count() accounts for the additional ports.
Sources: scene/resources/visual_shader.cpp280-330
Figure: Code generation flow from graph to GLSL
Sources: scene/resources/visual_shader.h163 scene/resources/visual_shader.cpp1-50
_write_node() RecursionThe private method _write_node() performs a depth-first traversal starting from the output node. For each node it visits:
r_processed set to avoid duplicates.generate_global() for declarations needed once per shader.generate_global_per_node() for global helpers emitted once per node class.generate_global_per_func() for code inserted once per shader function.generate_code() for inline body code with the resolved p_input_vars and p_output_vars arrays.The signature of _write_node:
Error _write_node(
Type p_type,
StringBuilder *p_global_code,
StringBuilder *p_global_code_per_node,
HashMap<Type, StringBuilder> *p_global_code_per_func,
StringBuilder &r_code,
Vector<DefaultTextureParam> &r_def_tex_params,
const HashMap<ConnectionKey, const List<Connection>::Element *> &p_input_connections,
int p_node,
HashSet<int> &r_processed,
bool p_for_preview,
HashSet<StringName> &r_classes) const;
Sources: scene/resources/visual_shader.h163
generate_preview_shader() generates a minimal shader to preview the output of a single node's port in the editor. It short-circuits the full pipeline and terminates at the requested node.
Sources: scene/resources/visual_shader.h254
Varyings allow values to be passed between shader stages. They are declared at the VisualShader level and accessed via VisualShaderNodeVaryingSetter and VisualShaderNodeVaryingGetter nodes.
VaryingMode | Direction |
|---|---|
VARYING_MODE_VERTEX_TO_FRAG_LIGHT | vertex → fragment and light stages |
VARYING_MODE_FRAG_TO_LIGHT | fragment → light stage |
Sources: scene/resources/visual_shader.h73-115 doc/classes/VisualShader.xml231-264
The concrete node types are split across three files:
| File | Contents |
|---|---|
scene/resources/visual_shader_nodes.h/cpp | General-purpose nodes (math, texture, parameters, control flow, etc.) |
scene/resources/visual_shader_particle_nodes.h/cpp | Particle shader–specific nodes |
scene/resources/visual_shader_sdf_nodes.h/cpp | 2D signed-distance field nodes |
Each node reports a Category value used by the editor to organize the add-node menu:
| Category Enum | Example Nodes |
|---|---|
CATEGORY_INPUT | VisualShaderNodeInput, all VisualShaderNodeConstant subtypes |
CATEGORY_OUTPUT | VisualShaderNodeOutput |
CATEGORY_SCALAR | VisualShaderNodeFloatOp, VisualShaderNodeFloatFunc |
CATEGORY_VECTOR | VisualShaderNodeVectorOp, VisualShaderNodeVectorFunc |
CATEGORY_COLOR | VisualShaderNodeColorOp, VisualShaderNodeColorFunc |
CATEGORY_TRANSFORM | VisualShaderNodeTransformOp, VisualShaderNodeTransformVecMult |
CATEGORY_TEXTURES | VisualShaderNodeTexture, VisualShaderNodeCubemap, VisualShaderNodeSample3D |
CATEGORY_CONDITIONAL | VisualShaderNodeIf, VisualShaderNodeSwitch, VisualShaderNodeCompare |
CATEGORY_UTILITY | VisualShaderNodeExpression, VisualShaderNodeGlobalExpression |
CATEGORY_SPECIAL | VisualShaderNodeVaryingSetter, VisualShaderNodeVaryingGetter, VisualShaderNodeReroute |
CATEGORY_PARTICLE | All particle emitter/helper nodes |
Sources: scene/resources/visual_shader.h286-300 scene/resources/visual_shader_nodes.h43-80 scene/resources/visual_shader_particle_nodes.h59
All constant nodes extend VisualShaderNodeConstant and have zero input ports. Each generate_code() emits a single assignment:
| Class | GLSL emitted |
|---|---|
VisualShaderNodeFloatConstant | out = 0.000000; |
VisualShaderNodeIntConstant | out = 0; |
VisualShaderNodeUIntConstant | out = 0u; |
VisualShaderNodeBooleanConstant | out = true; |
VisualShaderNodeColorConstant | out = vec4(r, g, b, a); |
VisualShaderNodeVec2Constant | out = vec2(x, y); |
VisualShaderNodeVec3Constant | out = vec3(x, y, z); |
VisualShaderNodeVec4Constant | out = vec4(x, y, z, w); |
VisualShaderNodeTransformConstant | out = mat4(...); |
Sources: scene/resources/visual_shader_nodes.cpp108-682
Parameter nodes (VisualShaderNodeParameter subtypes) generate uniform declarations via generate_global(). They expose shader properties to the material inspector. Examples: VisualShaderNodeFloatParameter, VisualShaderNodeVec3Parameter, VisualShaderNodeTexture2DParameter.
VisualShaderNodeParameterRef references a parameter declared elsewhere in the same shader by name.
Sources: scene/resources/visual_shader_nodes.h1
VisualShaderNodeExpression — inline raw GLSL inserted into the shader function body. Ports are dynamically configurable.VisualShaderNodeGlobalExpression — same, but output is placed in the global scope via generate_global_per_node().Sources: scene/resources/visual_shader_nodes.h1
Particle nodes live in scene/resources/visual_shader_particle_nodes.h/cpp and are only valid in TYPE_START, TYPE_PROCESS, TYPE_COLLIDE, TYPE_START_CUSTOM, and TYPE_PROCESS_CUSTOM stages.
Figure: Particle node hierarchy
Sources: scene/resources/visual_shader_particle_nodes.h39-162 scene/resources/visual_shader_particle_nodes.cpp37-282
Emitter nodes call generate_global_per_node() to emit shared helper functions (e.g., __get_random_point_in_sphere) into the global scope, deduplicating them across multiple instances of the same node type.
VisualShaderNodeParticleMeshEmitter bakes mesh geometry data (positions, normals, colors, UVs) into ImageTexture resources and references them via generate_global().
The SDF node set in scene/resources/visual_shader_sdf_nodes.h/cpp is a small collection for 2D CanvasItem signed-distance field operations:
| Class | GLSL Emitted |
|---|---|
VisualShaderNodeSDFToScreenUV | sdf_to_screen_uv(sdf_pos) |
VisualShaderNodeScreenUVToSDF | screen_uv_to_sdf(uv) |
VisualShaderNodeTextureSDF | texture_sdf(sdf_pos) |
VisualShaderNodeTextureSDFNormal | texture_sdf_normal(sdf_pos) |
VisualShaderNodeSDFRaymarch | sdf_raymarch(...) |
These nodes all belong to CATEGORY_TEXTURES and are only meaningful in CanvasItem shaders.
Sources: scene/resources/visual_shader_sdf_nodes.cpp35 scene/resources/visual_shader_sdf_nodes.h35
VisualShaderNodeCustom)VisualShaderNodeCustom allows GDScript (or other scripted languages) to define new node types that appear in the visual shader editor. The node uses GDVIRTUAL bindings so that virtual methods are dispatched to script.
Required virtual methods:
| Method | Purpose |
|---|---|
_get_name() | Node label in the graph |
_get_input_port_count() | Number of input ports |
_get_output_port_count() | Number of output ports |
_get_code(input_vars, output_vars, mode, type) | Inline GLSL body |
Optional virtual methods:
| Method | Purpose |
|---|---|
_get_description() | Tooltip in member dialog |
_get_category() | Menu path (e.g., "MyGame/Noise") |
_get_input_port_type(port) | Port type (default: SCALAR) |
_get_input_port_name(port) | Port label |
_get_output_port_type(port) | Port type |
_get_output_port_name(port) | Port label |
_get_global_code(mode) | Global declarations |
_get_func_code(mode, type) | Per-function prologue code |
_is_available(mode, type) | Restricts to specific shader modes/types |
_get_property_count() | Number of dropdown properties |
_get_property_name(index) | Dropdown label |
_get_property_options(index) | Dropdown choices |
A custom node script must use the @tool annotation and declare a class_name to be auto-discovered by the editor:
Sources: scene/resources/visual_shader.h394-490 scene/resources/visual_shader.cpp469-823 doc/classes/VisualShaderNodeCustom.xml1
When a custom node is initialized or a dropdown property changes, the internal C++ method update_ports() is called. It re-invokes the GDVIRTUAL methods _get_input_port_count, _get_input_port_type, etc. and rebuilds the input_ports and output_ports lists.
Sources: scene/resources/visual_shader.cpp493-565
All visual shader classes are registered in scene/register_scene_types.cpp using GDREGISTER_CLASS / GDREGISTER_ABSTRACT_CLASS. The relevant block begins at:
scene/register_scene_types.cpp775-892
This covers VisualShader, VisualShaderNode, VisualShaderNodeCustom, VisualShaderNodeInput, VisualShaderNodeOutput, all built-in node types, particle nodes, and SDF nodes.
| File | Role |
|---|---|
scene/resources/visual_shader.h | VisualShader, VisualShaderNode, VisualShaderNodeCustom, VisualShaderNodeInput, VisualShaderNodeOutput declarations |
scene/resources/visual_shader.cpp | Graph management, code generation, varying management, custom node dispatch |
scene/resources/visual_shader_nodes.h/cpp | All built-in general-purpose nodes |
scene/resources/visual_shader_particle_nodes.h/cpp | Particle-stage nodes |
scene/resources/visual_shader_sdf_nodes.h/cpp | 2D SDF nodes |
scene/register_scene_types.cpp | Class registration |
doc/classes/VisualShader.xml | Public API documentation |
doc/classes/VisualShaderNode.xml | Port type documentation |
doc/classes/VisualShaderNodeCustom.xml | Custom node API documentation |
Refresh this wiki
This wiki was recently refreshed. Please wait 2 days to refresh again.