dylanroscover / glsl-shaders
MUST READ before writing GLSL or creating a glslTOP, glslmultiTOP, glslMAT or glslPOP: docked DATs, uniforms and the Colors page, TD's built-in functions and samplers, feedback and multi-pass, reading compile errors. Every built-in named here compiled on TD 2025.33230.
<!-- Generated by Embody/Envoy - Do not remove this comment - sha:ba697c6b4d3a77c7 -->
GLSL Shaders
Three hosts: glslTOP/glslmultiTOP (pixels, or compute over an image), glslMAT (vertex + pixel over geometry), glslPOP (compute over points; the POP side lives in /pop-networks). Every function and parameter below compiled live on TD 2025.33230 (probed 2026-09-25). Anything you add beyond them: check the parameter with describe_op_type and the function with the docked info DAT before trusting it.
The docked-DAT contract
- Creating a
glslTOP/glslmultiTOPdocks<name>_pixel,<name>_compute,<name>_info, withpixeldat/computedatalready pointing at them. AglslMATdocks<name>_vertex,<name>_pixel,<name>_info(vdat/pdat). AglslPOPdocks<name>_computeand<name>_info. - Name the operator at creation (
glsl_<purpose>): dock names and the*datreferences are snapshots taken then and do not follow a later rename. - Write the shader INTO the docked DAT (
set_dat_content/edit_dat_content). Delete the dock you do not use (_computefor a pixel shader) so it does not ship as dead weight. One exception: several GLSL ops running identical code point at ONE shared DAT and differ by uniforms (tdxn-economy.md). create_ophugs the docks under the host; anexecute_pythonbuild must place them itself (/create-operator).- Read compile results from
<name>_info:op.errors()says only "Compile failed".get_op_errorsreturns them asshaderErrors, and every DAT write to a shader rides back_effects.new_shader_errors. Fix those before building further.
What TD gives you (never redeclare, never paste an alternative)
- No
#versionline: TD injects one, and writing your own fails with "'#version' : must occur first in shader".texture(), nevertexture2D()("function not supported in this version; use texture() instead"). - Inputs:
sTD2DInputs[i],sTD3DInputs[i],sTD2DArrayInputs[i],sTDCubeInputs[i], indexed per TYPE, not per connector.TD_NUM_2D_INPUTS(and the 3D/array/cube twins) bound a loop and guard a dynamic index.uTD2DInfos[i].resanduTDOutputInfo.resare(1/w, 1/h, w, h).vUV.stis the 0-1 texture coordinate when there is no custom vertex shader.uTDPassis the multi-pass index,uTDCurrentDepththe 3D slice. - Lookups:
sTDNoiseMap(256x256 random),sTDSineLookup. - Functions:
TDOutputSwizzle(vec4)on EVERY color you write out (channel order is wrong without it),TDDither,TDPerlinNoise(vec2|vec3|vec4)andTDSimplexNoise(vec2|vec3|vec4)(both in -1..1, available in TOP, MAT and glslPOP shaders on this build),TDHSVToRGB,TDRGBToHSV,TDRotateOnAxis(rad, axis),TDRotateX/Y/Z(rad). - Compute in a
glslTOP(mode=compute): declarelayout(local_size_x = 8, local_size_y = 8) in;, write withTDImageStoreOutput(0, ivec3(gl_GlobalInvocationID.xy, 0), TDOutputSwizzle(color)), read the output back withTDImageLoadOutput(0, ivec3(...)). glslTOPhas exactly 3 input connectors.glslmultiTOPtakes more and adds POP buffer reads (buffer0pop,buffer0attr,buffer0name->TDBuffer_<Name>(idx)in the shader). Reach forglslmultiTOPwhenever you need a fourth input or a POP attribute.
Uniforms
- Vectors page:
vec0name+vec0valuex/y/z/w, a sequence (grow it withop.seq.vec.numBlocks = N). On a TOP or MAT the SHADER declares the uniform and TD infers the type from the declaration (uniform vec4 uParams;); there is novec0typehere, and an undeclared name silently reads nothing. Pack four values per vec; the values take expressions (absTime.seconds * parent.Fx.par.Speed). - Colors page:
color0name+color0rgbr/g/b+color0alpha->uniform vec4 uTint;with a colour swatch in the dialog. Use it for colours instead of a raw vec. - Constants page:
const0name/const0valueare baked in at compile time, so every change RECOMPILES the shader. Runtime values belong on Vectors; constants only for true compile-time switches. glslPOPinverts one rule: its uniforms are AUTO-DECLARED from the Vectors page (uniform highp float uSpeed;by default). Declaring them again is a "Redeclaration" error, and the type comes fromvec0type(float,vec2..vec4,int,uint, ...). Samplers, Arrays, Matrices and Buffers pages follow the same shape on every host.
Pixel shader minimum
uniform vec4 uParams; // Vectors page: vec0name = uParams
layout(location = 0) out vec4 fragColor;
void main() {
vec4 c = texture(sTD2DInputs[0], vUV.st);
fragColor = TDOutputSwizzle(c * uParams.x);
}Output format and resolution are parameters, not shader code: format (rgba8fixed clamps to 0-1 -- data, feedback and HDR need rgba16float or rgba32float), outputresolution (useinput, custom with resolutionw/resolutionh, fit, ...). There is no resolution parameter. premultrgbbyalpha defaults ON and multiplies RGB by your alpha -- turn it off on an op whose alpha carries data. Multiple render targets: numcolorbufs, declare layout(location = 1) out vec4 fragField;, and pull buffer N with a Render Select TOP (set its format to float).
glslMAT
One combined DAT with #ifdef TD_VERTEX_SHADER / #ifdef TD_PIXEL_SHADER guards, set as BOTH vdat and pdat; keep _info.
- Vertex:
TDDeform(TDPos())gives the world position (never rawTDPos()-- instancing and deformers ride on TDDeform),TDDeformNorm(TDNormal())the world normal,TDWorldToProj(worldPos)isgl_Position,TDCameraIndex()goes out as aflat intvarying,TDPointColor()/TDInstanceColor(c)carry point and instance colour. - Pixel:
TDCheckDiscard()first;uTDMats[cam].camInverse[3].xyzis the camera position; loopTD_NUM_LIGHTSwithTDLightingPBR(i, diffuseColor, specularColor, worldPos, normal, shadowStrength, shadowColor, viewDir, roughness)(aTDPBRResultwith.diffuse/.specular) andTD_NUM_ENV_LIGHTSwithTDEnvLightingPBR(inout diffuse, inout specular, i, diffuseColor, specularColor, normal, viewDir, roughness, ambientOcclusion);TDFog(color, worldPos, cam); thenTDOutputSwizzle(TDDither(color))intooFragColor[0], declaredlayout(location = 0) out vec4 oFragColor[TD_NUM_COLOR_BUFFERS];. - Transparency needs
blendingON and honestsrcblend/destblend(one,zero,sa,omsa, ...) with the alpha pairsrcblenda/destblenda;cullface=neithershows both faces of filled POP geometry. - Extending a PBR look: start from a
pbrMATand pulseoutputshader-- TD writes its own vertex and pixel code into a new glslMAT for you to edit.
Feedback and multi-pass
- Feedback: Constant TOP (clear) -> Feedback TOP -> glsl -> Null TOP; the Feedback TOP's
topnames the null that closes the loop,resetpulseresets it. Close the loop BEFORE compiling shaders that depend on the fed-back texture. Bound every loop perperformance.md: fixed resolution inside it, decay below 1, reset wired, bypassed while wiring. - One feedback is one iteration per frame. Iterative solvers use
npasseson the GLSL op instead: pass N+1 sees pass N on input 0,uTDPasstells you which. - 3D output:
type=texture3d,depth=custom,customdepth=N; the shader runs once per slice withuTDCurrentDepth.constantTOPandnoiseTOPhave nocustomdepth, so seed a volume from a smallglslTOP, not from them.
Discipline
- A built-in operator beats a shader when one exists (Noise, Ramp, Lookup, Composite, Level TOPs; Math and Lookup CHOPs). GLSL is for what they cannot express: advection, solvers, custom lighting, per-point compute.
- Bound every loop with a constant and guard dynamic sampler or array indexes with
TD_NUM_*: an out-of-bounds index can take TD down (/td-api-reference, Heavy-Build Safety). centroidis a reserved word in GLSL 4.60.me.time.stepdoes not exist: delta time isabsTime.stepSeconds(or1.0 / me.time.rate).- Externalize shader DATs with
externalize_op(tagglsl) so they diff in git; edit the file, TD recompiles. - Verify by looking:
capture_topthe output (aQuality: FAILis a black or flat frame) and read<name>_infoafter every edit. A shader that compiles and renders TD's fallback pattern still reads as an error in the info DAT.
Topics cross-checked against Derivative's TDMCPSkills td-glsl-shaders with permission; every claim above was recompiled live on 2025.33230 before it was written down.