> defold-shaders-editing

Creates and edits Defold shader files (.vp, .fp, .glsl). Use when asked to create, modify, or configure any Defold vertex shader, fragment shader, or GLSL include file.

fetch
$curl "https://skillshub.wtf/indiesoftby/defold-agent-config/defold-shaders-editing?format=md"
SKILL.mddefold-shaders-editing

Editing Defold Shaders

Creates and edits Defold shader files: vertex programs (.vp), fragment programs (.fp), and GLSL include snippets (.glsl).

When to use

This skill covers GLSL shader files used in Defold's rendering pipeline. It does NOT cover .material files — for those, use the defold-proto-file-editing skill (see references/material.md).

Shader pipeline and GLSL version

Defold supports two shader pipelines:

  1. Legacy pipeline — shaders written in OpenGL ES 2.0 compatible GLSL (no #version directive). Deprecated since Defold 1.9.2.
  2. Modern pipeline — shaders written in SPIR-V compatible GLSL with #version 140 or higher. This is the current standard.

Always write shaders using #version 140 (OpenGL 3.1) at the top of the file. This directive selects the modern pipeline during the build process. If no #version is found, Defold falls back to the legacy pipeline.

#version 140

Cross-compilation and platform targets

Defold compiles shaders for multiple graphics APIs from a single GLSL source:

  • OpenGL 3.x / 4.x (desktop: Windows, macOS, Linux)
  • OpenGL ES 2.0 / 3.0 (mobile: Android, iOS)
  • WebGL 1.0 / 2.0 (HTML5)
  • SPIR-V (Vulkan on Android, desktop)
  • Metal (iOS, macOS — via SPIR-V cross-compilation)

Because of this cross-compilation, not all GLSL features are available everywhere. Some functions (e.g., dFdx, dFdy, fwidth) require extensions on ES 2.0 / WebGL 1.0 targets but are built-in on ES 3.0+ / desktop GL. Use #extension and preprocessor guards for conditional features:

#ifdef GL_OES_standard_derivatives
#extension GL_OES_standard_derivatives : enable
#endif

#if !defined(GL_ES) || __VERSION__ >= 300 || defined(GL_OES_standard_derivatives)
    // Use derivative functions
#else
    // Provide fallback
#endif

Key limitations to keep in mind:

  • Dynamic loops with variable bounds may not work on ES 2.0 / WebGL 1.0
  • Integer operations are limited on ES 2.0
  • sampler2DArray requires ES 3.0+ / WebGL 2.0+
  • Storage buffers and compute shaders require Vulkan / Metal

File types

Vertex program (.vp)

Runs once per vertex. Transforms vertex positions from model/world space to screen space. Outputs gl_Position and passes data to the fragment shader via out variables.

Fragment program (.fp)

Runs once per fragment (pixel). Computes the final color. Outputs to a user-defined out vec4 variable (not gl_FragColor, which is deprecated in #version 140).

GLSL include snippet (.glsl)

Reusable GLSL code included by .vp or .fp files via #include. Does not run standalone. Use header guards to prevent double-inclusion.

Modern GLSL syntax rules (#version 140)

Attributes → in

In vertex shaders, use in instead of attribute:

in highp vec4 position;
in mediump vec2 texcoord0;

Fragment shaders do NOT have vertex attributes.

Varyings → out / in

In vertex shaders, use out instead of varying. In fragment shaders, use in:

// vertex shader
out mediump vec2 var_texcoord0;

// fragment shader
in mediump vec2 var_texcoord0;

Fragment output → out vec4

Use a declared out variable instead of the deprecated gl_FragColor:

out vec4 out_fragColor;

void main()
{
    out_fragColor = vec4(1.0, 0.0, 0.0, 1.0);
}

Uniform blocks

Non-opaque uniforms (matrices, vectors, floats) must be placed in a uniform block. Use vs_uniforms for vertex shaders and fs_uniforms for fragment shaders (by convention):

uniform vs_uniforms
{
    highp mat4 view_proj;
};

Opaque uniforms (samplers, images) remain standalone:

uniform mediump sampler2D texture_sampler;

Members of the uniform block are accessed directly by name (no block prefix):

gl_Position = view_proj * vec4(position.xyz, 1.0);

Texture sampling → texture()

Use texture() instead of the deprecated texture2D() / texture2DArray():

vec4 color = texture(texture_sampler, var_texcoord0.xy);

Precision qualifiers

Explicit precision (lowp, mediump, highp) is optional in #version 140 — the pipeline sets precision automatically for platforms that need it. However, you may still use them for clarity or to match existing code style in the project. Follow the convention of surrounding files.

Editor-specific code (EDITOR define)

When shaders are rendered in the Defold Editor viewport, the preprocessor define EDITOR is available. Use #ifdef EDITOR to write code that behaves differently in the editor vs the game:

#ifdef EDITOR
    // Simplified rendering for editor preview
    out_fragColor = texture(texture_sampler, var_texcoord0.xy);
#else
    // Full rendering with effects for the game
    out_fragColor = apply_effects(texture(texture_sampler, var_texcoord0.xy));
#endif

Common use cases:

  • Disable expensive effects (RGSS, post-processing) in editor for performance
  • Show debug/fallback visuals for materials that don't preview well
  • Skip features that depend on runtime-only data (e.g., skinning)

Including snippets (#include)

Shader files can include .glsl snippets using #include with project-relative or file-relative paths:

// Absolute (project-relative) path
#include "/main/my-snippet.glsl"

// Relative to current file
#include "my-snippet.glsl"
#include "../shared/utils.glsl"

Rules:

  • Only .glsl files can be included
  • Paths must be within the project (or library dependencies)
  • Absolute paths start with /
  • #include cannot be used inline within a statement

Header guards

Use header guards in .glsl snippets to prevent double-inclusion:

#ifndef MY_SNIPPET_GLSL
#define MY_SNIPPET_GLSL

// ... snippet code ...

#endif // MY_SNIPPET_GLSL

Relationship with materials

Shaders and materials (.material) are tightly coupled. The material file defines what data the shader receives. For material file editing, use the defold-proto-file-editing skill (reference: references/material.md).

Constants (uniforms)

Constants declared in the material's vertex_constants / fragment_constants become uniform variables in the shader. Place them inside the uniform block:

Material constant typeShader uniform typeDescription
CONSTANT_TYPE_VIEWPROJmat4Combined view × projection matrix
CONSTANT_TYPE_WORLDmat4World transform matrix
CONSTANT_TYPE_VIEWmat4View (camera) matrix
CONSTANT_TYPE_PROJECTIONmat4Projection matrix
CONSTANT_TYPE_WORLDVIEWmat4World × view matrix
CONSTANT_TYPE_WORLDVIEWPROJmat4World × view × projection matrix
CONSTANT_TYPE_NORMALmat4Normal matrix — transpose(inverse(view * world)). Produces view-space normals, not world-space.
CONSTANT_TYPE_USERvec4Custom data, mutable via go.set() / go.animate()
CONSTANT_TYPE_USER_MATRIX4mat4Custom matrix data, mutable via go.set()

The name in the material must match the variable name in the uniform block.

Samplers

Samplers declared in the material's samplers section become sampler2D uniforms. The sampler name in the material must match the uniform name in the shader:

uniform mediump sampler2D texture_sampler;  // Matches samplers { name: "texture_sampler" ... }

For sprites, tilemaps, GUI, and particles — the first sampler2D is automatically bound to the component's image.

Vertex attributes

Attributes declared in the material's attributes section (or default attributes provided by the engine) become in variables in the vertex shader.

Default attributes by component type:

ComponentAttributes
Spriteposition, texcoord0
Tilemapposition, texcoord0
GUI nodeposition, texcoord0, color
ParticleFXposition, texcoord0, color
Modelposition, texcoord0, normal
Fontposition, texcoord0, face_color, outline_color, shadow_color

Vertex space

The material's vertex_space setting affects how position data arrives:

  • VERTEX_SPACE_WORLD (default) — positions are pre-transformed to world space. Used for 2D components (sprites, tilemaps). Use view_proj (or CONSTANT_TYPE_VIEWPROJ) to go directly to screen space.
  • VERTEX_SPACE_LOCAL — positions are in local/object space. Used for 3D models. You must transform through world → view → projection in the shader.

Instancing

For instanced rendering (Model components), declare mtx_world and mtx_normal as in attributes (not uniforms):

in mediump mat4 mtx_world;
in mediump mat4 mtx_normal;

The material must have vertex_space: VERTEX_SPACE_LOCAL. These attributes are automatically configured for per-instance step function.

Normal matrix (mtx_normal) — view-space vs world-space

The built-in CONSTANT_TYPE_NORMAL computes transpose(inverse(view * world)) on the CPU. This produces normals in view-space (camera-space), not world-space.

View-space normals are fine when:

  • All lighting calculations are done in view-space
  • Camera position is implicitly at origin (0,0,0) (simplifies specular)

World-space normals are needed for:

  • Cubemap reflections, environment mapping
  • World-space lighting, world-space effects

To get world-space normals, compute the normal matrix from mtx_world in the vertex shader using the adjugate matrix trick (cheaper than full inverse() — 3 cross products instead of cofactor expansion):

// transpose(adjugate(M)) for upper-left 3x3 of mat4.
// Equivalent to transpose(inverse(M)) up to a uniform scale factor,
// which is eliminated by normalize().
mat3 adjoint(mat4 m)
{
    return mat3(
        cross(m[1].xyz, m[2].xyz),
        cross(m[2].xyz, m[0].xyz),
        cross(m[0].xyz, m[1].xyz)
    );
}

Usage in vertex shader:

var_world_normal = normalize(adjoint(mtx_world) * normal.xyz);
GoalMethod
View-space normalsUse built-in mtx_normal (CONSTANT_TYPE_NORMAL)
World-space normalsCompute normalize(adjoint(mtx_world) * normal.xyz) in shader

Key rule: Never mix coordinate spaces — if light direction is in world-space, normals must also be in world-space.

Canonical examples

Builtin reference shaders

Use .deps/builtins/materials/ as reference for standard shaders:

  • sprite.vp / sprite.fp — 2D sprite (world space, view_proj, tint)
  • model.vp / model.fp — 3D model with lighting (local space, mtx_worldview, mtx_normal, light)
  • model_instanced.vp — 3D model with instancing (mtx_world, mtx_normal as in attributes)
  • gui.vp / gui.fp — GUI nodes (world space, color attribute, premultiplied alpha)
  • particlefx.vp / particlefx.fp — Particle effects
  • tile_map.vp / tile_map.fp — Tilemaps
  • skinning.glsl — Skeletal animation include (uses #ifdef EDITOR for fallback)

GLSL include snippet (with header guards and extension)

#ifndef MY_UTILS_GLSL
#define MY_UTILS_GLSL

#ifdef GL_OES_standard_derivatives
#extension GL_OES_standard_derivatives : enable
#endif

// Utility function available on platforms that support derivatives
#if !defined(GL_ES) || __VERSION__ >= 300 || defined(GL_OES_standard_derivatives)
mediump float edge_smoothing(mediump float dist)
{
    return smoothstep(0.0, fwidth(dist), dist);
}
#else
mediump float edge_smoothing(mediump float dist)
{
    return step(0.0, dist);
}
#endif

#endif // MY_UTILS_GLSL

Workflow

Creating a new shader pair (.vp + .fp)

  1. Determine the component type (sprite, model, GUI, etc.) to know which attributes and vertex space to use.
  2. Check the corresponding .material file (or plan one) to know which constants, samplers, and attributes the shader will receive. Use the defold-proto-file-editing skill for material editing.
  3. Start both files with #version 140.
  4. In the .vp: declare in attributes, out varyings, uniform block with constants, and compute gl_Position.
  5. In the .fp: declare in varyings, out vec4 for color output, samplers, uniform block, and compute the output color.
  6. Ensure uniform names match the material's constant names exactly.
  7. Ensure sampler names match the material's sampler names exactly.

Creating a GLSL include snippet

  1. Create a .glsl file.
  2. Add header guards (#ifndef / #define / #endif).
  3. Add #extension directives with preprocessor guards if using features not available on all targets.
  4. Write reusable functions or constants.

Editing an existing shader

  1. Read the current shader file and its corresponding .material file.
  2. Modify only the requested parts.
  3. Keep the existing code style (precision qualifiers, naming, spacing).
  4. Ensure any new uniforms are also added to the material file (use defold-proto-file-editing skill).
  5. Ensure any removed uniforms are also removed from the material file.

┌ stats

installs/wk0
░░░░░░░░░░
github stars39
████████░░
first seenMar 17, 2026
└────────────

┌ repo

indiesoftby/defold-agent-config
by indiesoftby
└────────────

┌ tags

└────────────