Spatial shaders
Spatial shaders are used for shading 3D objects. They are the most complex type of shader Godot offers. Spatial shaders are highly configurable with different render modes and different rendering options (e.g. Subsurface Scattering, Transmission, Ambient Occlusion, Rim lighting etc). Users can optionally write vertex, fragment, and light processor functions to affect how objects are drawn.
Render modes
Built-ins
Values marked as “in” are read-only. Values marked as “out” are for optional writing and will not necessarily contain sensible values. Values marked as “inout” provide a sensible default value, and can optionally be written to. Samplers are not subjects of writing and they are not marked.
Global built-ins
Global built-ins are available everywhere, including custom functions.
Vertex built-ins
VERTEX, NORMAL, TANGENT, BITANGENT) are presented in local model space. If not written to, these values will not be modified and be passed through as they came.
world_vertex_coords render mode.
Users can disable the built-in modelview transform (projection will still happen later) and do it manually with the following code:
shader_type spatial;render_mode skip_vertex_transform;void vertex() { VERTEX = (MODELVIEW_MATRIX * vec4(VERTEX, 1.0)).xyz; NORMAL = normalize((MODELVIEW_MATRIX * vec4(NORMAL, 0.0)).xyz); // same as above for binormal and tangent, if normal mapping is used}
Other built-ins, such as UV, UV2 and COLOR, are also passed through to the fragment function if not modified.
POSITION built-in. When POSITION is used, the value from VERTEX is ignored and projection does not happen. However, the value passed to the fragment shader still comes from VERTEX.
For instancing, the INSTANCE_CUSTOM variable contains the instance custom data. When using particles, this information is usually:
- x: Rotation angle in radians.
- y: Phase during lifetime (0 to 1).
- z: Animation frame.
This allows you to easily adjust the shader to a particle system using default particles material. When writing a custom particle shader, this value can be used as desired.
Note
MODELVIEW_MATRIXcombines both theWORLD_MATRIXandINV_CAMERA_MATRIXand is better suited when floating point issues may arise. For example, if the object is very far away from the world origin, you may run into floating point issues when using the seperatedWORLD_MATRIXandINV_CAMERA_MATRIX.Fragment built-ins
The default use of a Godot fragment processor function is to set up the material properties of your object and to let the built-in renderer handle the final shading. However, you are not required to use all these properties, and if you don’t write to them, Godot will optimize away the corresponding functionality. Below are examples of common variables calculated using the built-ins:
Notevec3 model_world_space = WORLD_MATRIX[3].xyz; // Object's world space position. This is the equivalent to global_transform.origin in GDScript.mat3 model_transform_basis = mat3(WORLD_MATRIX); // Object's world space transform basis. This is the equivalent to global_transform.basis in GDScript.vec3 camera_world_space = CAMERA_MATRIX[3].xyz; // Camera's world space position. This is the equivalent to camera.global_transform.origin in GDScript.vec3 camera_eye_world_space = INV_CAMERA_MATRIX[3].xyz; // Camera eye vector in world space direction of the camera.vec3 camera_to_object_world_space = normalize(WORLD_MATRIX[3].xyz - CAMERA_MATRIX[3].xyz); // Camera's direction to the object in world space.
WORLD_MATRIX[3].xyzis to usevec3 origin = (WORLD_MATRIX * vec4(0,0,0,1)).xyz. It is more efficient to useWORLD_MATRIX[3].xyzas it avoids the matrix multiplication. NoteALPHAis written to may exhibit transparency sorting issues. Read the transparency sorting section in the 3D rendering limitations page for more information and ways to avoid issues.Light built-ins
unshaded. If no light function is written, Godot will use the material properties written to in the fragment function to calculate the lighting for you (subject to the render_mode).DIFFUSE_LIGHTorSPECULAR_LIGHT. Assigning nothing means no light is processed. The light function is called for every light in every pixel. It is called within a loop for each light type. Below is an example of a custom light function using a Lambertian lighting model:void light() { DIFFUSE_LIGHT += clamp(dot(NORMAL, LIGHT), 0.0, 1.0) * ATTENUATION * ALBEDO;}
DIFFUSE_LIGHTusing+=, rather than overwriting it. WarningDIFFUSE_LIGHTusing=. This is due to lighting being computed in multiple passes (one for each light), unlike GLES3. Warninglight()function won’t be run if thevertex_lightingrender mode is enabled, or if Rendering > Quality > Shading > Force Vertex Shading is enabled in the Project Settings. (It’s enabled by default on mobile platforms.) NoteALPHAis written to may exhibit transparency sorting issues. Read the transparency sorting section in the 3D rendering limitations page for more information and ways to avoid issues.
