Shader Uniform Reference
The exact uniform names the built-in pipelines upload, so a custom shader or effect can declare the ones it consumes and inherit the rest of the scene data.
The contract is what the pipeline resolves and uploads (the F# side), not what the shipped GLSL/HLSL happens to declare. Sources of truth:
Mibo.Raylib/Graphics3D/Pipelines/SceneUpload.fs+ForwardPbrPipeline.fs,Mibo.MonoGame/Graphics3D/Pipelines/SceneUpload.fs+ForwardPipeline.fs, andMibo.Raylib/Graphics2D/Lighting/LightContext.fsfor 2D.
Integration points
How custom shading gets into the pipeline, per backend:
Escape hatch |
raylib |
MonoGame |
Uniforms you receive |
|---|---|---|---|
|
✓ ( |
✓ ( |
Scene data by name: declare only what you use; absent ones are skipped. Instanced draws are shaded by your shader when it opts in (see Instancing). |
Per-mesh-part effect draw |
(none) |
✓ |
|
|
✓ |
✓ |
None: the pipeline shader is bypassed; you get a |
.beginEffect(...) / .endEffect(): the inherited uniform contract
Both backends resolve the same uniform names (mirrored SceneUpload
modules). Declare a subset in your shader; the pipeline uploads only what's
present. Absent uniforms are a no-op (-1 location on raylib, null parameter
on MonoGame).
Matrices
Uniform |
Type |
Source |
|---|---|---|
|
|
Per-draw world matrix |
|
|
|
|
|
|
|
|
Active camera world position |
The built-in raylib PBR shaders use a precomposed
mvp; the MonoGame shaders have nomvpuniform (they composematModelwith a precomposedviewProj). On both backends,beginEffectdoes not setmvp: declarematModel+viewProjand compose the clip-space transform yourself.
Animation clock
Uniform |
Type |
Source |
|---|---|---|
|
|
Total elapsed game time, seconds. Absent on the default PBR shaders; declare it for water/flow effects. |
Material
Uniform |
Type |
Source |
|---|---|---|
|
|
Base color tint |
|
|
Scalar roughness |
|
|
Scalar metallic |
|
|
Emission tint |
|
|
Alpha multiplier |
|
|
UV tiling |
|
|
|
|
|
|
|
|
Albedo map |
|
|
Metalness map (raylib) / Roughness map (MonoGame |
|
|
Normal map |
|
|
Roughness map (raylib) / Metallic map (MonoGame |
|
|
Emission map |
Lights
In single-camera frames these reflect every light command in the buffer, frame-globally. In frames with more than one camera block they describe the active camera block's light set: a block that issues its own light commands resets to the frame defaults (lights emitted outside any camera block) and applies them in order; a block that issues none inherits the running set. Only the first directional light is shaded, and only it can cast shadows.
Uniform |
Type |
Source |
|---|---|---|
|
|
Ambient color |
|
|
Ambient brightness |
|
|
Directional light direction (travel direction) |
|
|
Directional color |
|
|
Directional brightness |
|
|
Active point lights |
|
|
Per-light position (array, default max 8) |
|
|
Per-light color |
|
|
Per-light brightness |
|
|
Per-light radius |
|
|
Per-light falloff exponent |
|
|
Active spot lights |
|
|
Per-light position (array, default max 4) |
|
|
Per-light direction |
|
|
Per-light color |
|
|
Per-light brightness |
|
|
Per-light radius |
|
|
Per-light inner cone cosine |
|
|
Per-light outer cone cosine |
Shadows (opt-in by declaration)
Only uploaded when the active camera block produced a shadow atlas: in buffers with more than one camera block these are re-uploaded at each block's start and always describe the block being drawn. A shader that declares none of these renders unshadowed at no cost.
Uniform |
Type |
Source |
|---|---|---|
|
|
|
|
|
Per-caster view-projection (default max 16 casters) |
|
|
Per-caster atlas region (xy=offset, zw=scale) |
|
|
|
|
|
Per-caster receiver-side bias (prevents self-shadow acne; raylib adds an in-shader slope-scale term, MonoGame applies it directly) |
|
|
Per-point-light caster slot, |
|
|
Per-spot-light caster slot, |
|
|
The depth atlas (MonoGame DX11: |
Shadow sampler slot differs by backend. raylib binds the atlas to slot 15 (and sets the
shadowAtlassampler uniform to 15). MonoGame binds it to slot 5 (PointClamp) and exposes it through the effect'sshadowAtlasparameter (mgfxc names the parameter after its HLSL declaration, not the register slot: so on DX11 declareTexture2D shadowAtlas : register(t5); SamplerState shadowAtlasSampler : register(s5);and sample withshadowAtlas.Sample(shadowAtlasSampler, uv); on DX12/Vulkan (SM6) the sampler declaration isSamplerState shadowSampler : register(s5);and the sample isshadowAtlas.SampleLevel(shadowSampler, uv, 0.0); on OpenGL declaresampler2D shadowAtlas : register(s5). The built-inForwardPbr.fxswitches between the three forms with#if defined(SM6)/#elif OPENGL/#else). Both backends use the same uniform name:shadowAtlas.
Skinning (only for skinned draws)
Uniform |
Type |
Source |
|---|---|---|
|
|
Bone palette (uploaded only when bones are supplied) |
Instancing (opt-in)
An .instanced(...) draw inside a .beginEffect(...) scope is shaded by your
shader when it declares the instancing input; otherwise it falls back to the
built-in PBR instanced path. The opt-in convention differs by backend because
each engine feeds per-instance data differently: raylib uses a single vertex
attribute and sets the divisor itself, while MonoGame requires two explicit
vertex streams. The data is the same in both cases: a per-instance 4×4 world
matrix.
matModel is not used for instanced draws (the per-instance transform is
the model matrix); the pipeline uploads identity for it, so a shader that still
declares matModel sees a benign value.
raylib (GLSL #version 330): declare the per-instance attribute.
|
MonoGame (HLSL, SM 3.0/5.0): expose a technique named Instanced whose
vertex shader reads the per-instance matrix as four float4 rows on
TEXCOORD1..4 (usage indices 1-4, to avoid colliding with the mesh's own
TEXCOORD0 on stream 0). This matches ForwardPbr.fx's VS_INPUT_INSTANCED
and the minimal Instanced.fx.
|
Per-instance color (optional, MonoGame only). When the draw supplies a
colors array, the pipeline feeds each instance's tint as an additional
float4 on TEXCOORD5 (offset 64 in the instance vertex, right after the four
matrix rows). Declare it in your input struct to receive it:
|
The declaration is optional: an effect that omits it still works; the built-in
fallback shades colored draws instead. Instances beyond the colors array
length receive white.
Skinned + instanced. An animatedModelInstanced draw inside a
.beginEffect(...) scope is shaded by your shader when it opts in; otherwise it
falls back to the built-in PBR skinned-instanced path. Per-instance bone
palettes ride a palette texture (RGBA32F, width = boneCount * 4 texels,
height = instance count, four consecutive texels per bone matrix) instead of
the boneMatrices uniform array.
raylib (GLSL #version 330): declare the instancing attribute, the bone
attributes, and the palette sampler. The instance row is gl_InstanceID;
texel boneIndex*4+c is column c of the bone's matrix (same raw layout the
boneMatrices uniform path uploads).
|
MonoGame (HLSL): expose a technique named SkinnedInstanced whose
vertex shader combines the skinned input (BLENDWEIGHT0/BLENDINDICES0), the
instance rows (TEXCOORD1..4), and a per-instance palette row index
(PaletteOffset : TEXCOORD6), sampling the palette texture at LOD 0. Texel
boneIndex*4+r is row r of the bone's matrix; the texel-center UV is
((boneIndex*4+r + 0.5) / paletteTexSize.x, (instance + 0.5) / paletteTexSize.y).
The technique ships only where vertex texture fetch exists (DX11/Vulkan: the
OpenGL profile compiles it out, see the note below), so declare the texture
the SM 4+ way: Texture2D + SamplerState + .SampleLevel. (The built-in
ForwardPbr.fx hides this split behind DECLARE_TEX/SAMPLE_TEX_LOD macros;
those macros are not visible to your effect, so spell the pair out.)
|
The OpenGL shader profile has no vertex texture fetch, so
SkinnedInstanceddoes not exist there: theSkinnedInstancedtechnique probe is skipped on that backend and the framework draws per-instance through theSkinnedpath (yourSkinnedtechnique, if declared, applies). DX12 note: the MonoGame DX12 backend does not support vertex texture fetch (NotSupportedException), so VTF is unavailable. Instead, the framework loads an isolatedForwardPbrGrouped.fx(DX12-only) whoseSkinnedInstancedGrouped/SkinnedInstancedGroupedColortechniques read bone palettes from abonePaletteGroup[448]constant array in$Globals, indexed by the per-instancePaletteOffsetpre-multiplied by the bone count (agroupBoneCountuniform does not survive DX12 mgfx reflection, so the stride multiply happens at staging time). The mainForwardPbr.fxcannot serve these techniques on DX12 because the mgfx reflection parser drops thebonePaletteGroupparam when all 8 techniques are present in one file. User effects that declare aSkinnedInstancedtechnique fall back to per-instanceSkinneddraws on DX12 (the grouped path is framework-PBR-only). A model with more than 448 bones exceeds the group budget and takes the same per-instance fallback.
drawMeshEffect (MonoGame only)
A fully user-owned Effect. The pipeline sets only the camera + transform
matrices via the effect's IEffectMatrices interface:
Property |
Source |
|---|---|
|
The draw's transform matrix |
|
Active camera view |
|
Active camera projection |
Your effect must implement
IEffectMatrices(asBasicEffect,SkinnedEffect, etc. do). A raw compiledEffectfrom a.mgfxthat doesn't expose the interface gets nothing set: own all parameters yourself and set them before issuing the draw.
raylib has no drawMeshEffect equivalent; use beginEffect (inherits scene
data) or drawImmediate (raw rlgl/raylib calls).
drawImmediate (both backends)
The pipeline shader is bypassed: there is no uniform contract. The callback
receives a SceneContext record with the raw device plus the gathered scene
fields (as F# values, not shader uniforms):
Field |
Type |
Notes |
|---|---|---|
|
|
raylib uses global |
|
camera view matrix |
|
|
camera projection matrix |
|
|
active |
position, target, up, fov, planes |
|
|
ambient + directional + point + spot accumulators (in multi-block buffers, the current camera block's set) |
|
|
|
|
|
Total elapsed game time, seconds |
Set whatever uniforms your own shader needs directly from these values.
2D lit-sprite uniforms
A custom lit-sprite shader must match the built-in uniform layout. Names differ
by backend (raylib camelCase, MonoGame PascalCase): unlike the 3D
beginEffect contract.
raylib ( |
MonoGame ( |
Type |
Source |
|---|---|---|---|
|
|
|
Ambient color |
|
|
|
Active directional lights (max 4) |
|
|
|
Per-light direction |
|
|
|
Per-light color |
|
|
|
Per-light brightness |
|
|
|
|
|
|
|
Active point lights (max 16) |
|
|
|
Per-light position |
|
|
|
Per-light color |
|
|
|
Per-light brightness |
|
|
|
Per-light radius |
|
|
|
Per-light falloff exponent |
|
|
|
|
|
|
|
Line segment (xy=p1, zw=p2) |
|
|
|
Active occluder segments |
|
|
|
Penumbra softness |
|
|
|
Max raymarch distance |
|
|
|
Normal-map sampler |
(none) |
|
|
View-projection (MonoGame only) |
The MAX_DIR_LIGHTS (4), MAX_POINT_LIGHTS (16), and MAX_OCCLUDERS (128 on
DX, 32 on OpenGL) constants must match between your shader and the
LightContext2D constructor args.
Worked examples
MonoGame: minimal HLSL for beginEffect
A toon shader that consumes camera + the directional light + material:
|
buffer
.beginCamera(camera)
.beginEffect(toonEffect)
.model(model, transform)
.endEffect()
.endCamera()
.drop()
raylib: minimal GLSL for beginEffect
|
// Fragment shader declares the same uniforms it consumes; load both, then:
buffer
.beginCamera(camera)
.beginEffect(toonShader)
.model(model, transform)
.endEffect()
.endCamera()
.drop()
Convention notes
-
Matrix multiply direction. MonoGame HLSL uses row-vector convention
(
mul(position, matrix)); raylib GLSL uses column-vector (matrix * position). Compose clip space accordingly:mul(mul(position, matModel), viewProj)(HLSL) vsviewProj * matModel * position(GLSL).viewProjis uploaded asview * projectionon both backends. -
Setting raylib uniforms.
[<DisableRuntimeMarshalling>]requiresfixed + NativePtr.toVoidPtrfor scalar/vectorSetShaderValuecalls. See Shaders for the full caveat;SetShaderValueMatrixis exempt. -
Light/shadow budgets. Point-light/spot-light array sizes come from the
pipeline constructor (raylib) or baked
#defines (MonoGame). See 3D Lighting for the limits and how to change them. -
MonoGame/OpenGL: index uniform arrays dynamically. On the DesktopGL
backend, a uniform array that is only ever indexed with compile-time constants
(e.g.
shadowViewProjs[0]) can be trimmed by the MojoShader/GLSL toolchain down to the referenced elements: while the effect's parameter table still reports the declared element count. MonoGame's GL constant-buffer upload then writes past the end of the shader's constant buffer and crashes inEffectPass.Apply(anArgumentExceptionfromBuffer.BlockCopy). DirectX and Vulkan keep the declared size, so this surfaces as an OpenGL-only crash. To avoid it, index the array with a uniform value somewhere in the shader (the wayForwardPbr.fxindexes withpointLightShadowIdx[i]/spotLightShadowIdx[j]), or declare exactly the slots the shader reads. Declaring fewer slots than the pipeline maximums is safe on the upload side: uploads are clamped to the effect's declared element count, and the light count uniforms (pointLightCount/spotLightCount) are clamped to the declared slots as well.
Contract changes
Breaking changes and additions to this contract, by Mibo version. Additions are opt-in: a shader that declares nothing new renders exactly as before.
4.x
-
Breaking: 3.x -> 4.x: raylib skinned palettes. Palettes handed to
skinnedMesh/DrawSkinnedMesh: and returned byAnimatedMesh.computeBoneMatrices: are now plain System.Numerics row-major matrices (palette[i] = InverseBindPose[i] * pose[i]); the pipeline transposes at upload. In 3.x the same APIs expected pre-transposed (raylib-native) matrices. If you build palettes yourself, drop your own transpose. -
Added: 4.x: skinned + instanced opt-in. The palette texture (MonoGame
paletteText6 /paletteTexSamplers6; raylibbonePaletteunit 14), thepaletteTexSize/bonePaletteSizesize uniforms, thePaletteOffset : TEXCOORD6instance field, and theSkinnedInstancedtechnique name: see Instancing (opt-in). No existing uniform changed name, meaning, or slot. -
Limits: 4.x. MonoGame DX12: the grouped path holds at most 448 bone
matrices in the forward effect (
bonePaletteGroup[448]) and 500 in the depth effect; models with more than 448 bones fall back to per-instanceSkinneddraws. raylib: the palette texture isboneCount * 4texels wide: OpenGL only guarantees a 1024-texel texture (256 bones); larger skeletons depend on the driver's limit (8192+ texels: 2048+ bones: is typical on desktop).
See also
-
Shaders: Loading custom shaders, setting parameters, the
DisableRuntimeMarshallingcaveat - 3D Rendering Overview: When to use each escape hatch
- 3D Lighting: Light types, limits, shadow config
- 2D Lighting & Shadows: The 2D lit-sprite pipeline
Mibo