Blitz3D+ Guides

Help home · Beginner · Intermediate · Advanced authoring · Command reference · Samples

Using Shaders: Intermediate

This guide is for using built-in, downloaded, or team-authored shader assets without needing to understand their HLSL implementation. It explains how shader instances fit into a real project, how attachments are resolved, and how assets are built and deployed.

If terms such as material shader and post effect are still unfamiliar, start with Shaders for Beginners. To create or change the shader code itself, continue to the Advanced Shader Authoring Guide.

1. Programs, assets, and instances

A file-backed shader is loaded from a .b3shader descriptor:

water=LoadShader("fx/water.b3shader")

The descriptor tells Blitz3D whether it is a surface, raw, or post shader, where its compiled code comes from, and which named parameters are public. Treat the descriptor and its accompanying files as one asset.

Blitz3D caches the compiled GPU program, but every call to LoadShader returns a fresh instance. Instances share the expensive compiled code while keeping independent parameter values and texture bindings. The same is true of builtin: aliases.

calm=LoadShader("fx/water.b3shader")
stormy=LoadShader("fx/water.b3shader")
ShaderFloat calm,"WaveHeight",.05
ShaderFloat stormy,"WaveHeight",.4

A failed load raises a descriptive runtime error. ShaderError returns any diagnostic retained by a valid instance.

2. Working with named parameters

A shader asset declares the names, types, and defaults which form its public interface. Names are matched without regard to case. Use the spelling supplied by the asset so your program remains easy to compare with its documentation.

Declared typeBlitz commandExample
floatShaderFloatShaderFloat shader,"Roughness",.8
intShaderIntShaderInt shader,"Mode",1
vectorShaderVectorShaderVector shader,"Wind",1,0,.2,0
colorShaderColorShaderColor shader,"Tint",80,160,255
textureShaderTextureShaderTexture shader,"NormalMap",map,frame

Colours use familiar 0 to 255 components. Vectors use numeric components directly. A texture parameter accepts an ordinary Blitz texture and optional animation frame; the shader retains the resource while it is bound.

Asking for a missing name, using the wrong setter type, or choosing an invalid texture frame is a runtime error. This strictness catches mistakes close to the line which caused them.

3. Entity and brush precedence

EntityShader applies one material shader to every rendered surface on an entity. BrushShader stores a material shader in a brush; surfaces painted from the brush retain it. At draw time the choice is:

  1. Use the entity's EntityShader, if present.
  2. Otherwise use the surface brush's BrushShader, if present.
  3. Otherwise use the brush's Standard or classic appearance.
shader=LoadShader("builtin:rim-light")

; One shader for the whole model.
EntityShader character,shader

; Or place it on a reusable brush.
BrushShader specialBrush,shader
surface=GetSurface(machine,2)
PaintSurface surface,specialBrush

Pass zero to EntityShader or BrushShader to remove the override. Entity colour, alpha, textures, blending, FX flags, fog, and lighting remain engine state. Whether a custom shader uses all of that state depends on how its author wrote it.

Common surprise: changing a surface brush shader has no visible effect while the owning entity has an entity shader, because the entity override takes priority.

4. Managing camera-effect stacks

CameraEffect appends a post shader to a camera. Effects retain attachment order within their declared stage, with each result feeding the next:

bloom=LoadShader("builtin:bloom")
grade=LoadShader("builtin:color-adjust")
CameraEffect camera,bloom
CameraEffect camera,grade

Here bloom runs first and colour adjustment runs second. Use the same shader handle to control its attachment:

CameraEffectEnabled camera,bloom,False
CameraEffectEnabled camera,bloom,True
RemoveCameraEffect camera,grade

Bypassing retains the effect's position and settings. Removing it does not free your script handle. Effects are restricted to the owning camera's viewport. Attaching the same shader handle to two cameras shares its settings; use CopyShader before attaching when each camera needs independent values.

Post processing occurs after that camera's 3D render. 2D drawing performed later is unaffected. Post shaders cannot be assigned through EntityShader or BrushShader.

Format-2 effects declare scene-linear, display-linear, or presentation. The renderer always runs those stages in that order, inserting its one tone-map/output pass between scene and display work. Consequently, a presentation vignette attached before bloom still executes after scene-linear bloom.

Quality and optional resources

Expensive effects use the shared integer Quality convention: 0 low, 1 balanced, 2 high. Depth, normal/roughness, velocity, object-mask, opaque-colour, blue-noise, and history resources are created only if an enabled attachment or material declares them. Bypassing the last consumer releases persistent optional targets after the renderer's bounded lifetime. Use DrawRenderStats 8,8,1023 to see actual pass count, optional-buffer mask, target memory, and history state.

Render scale, viewports, and history

Pass scales are relative to the current internal render size and remain confined to the camera viewport. Multiple presentation targets own independent scene, capture, transient, and temporal resources. TAA/history resets on camera cuts, resize, render-scale and AA changes, retain/discard changes, and device rebuild.

5. Copying and lifetime

CopyShader copies all current values and texture bindings while sharing the compiled program. Later changes affect only the selected copy:

blue=LoadShader("builtin:rim-light")
orange=CopyShader(blue)
ShaderColor blue,"RimColor",40,140,255
ShaderColor orange,"RimColor",255,100,20

Prefer this over loading the same asset again when a new instance should begin with an existing instance's settings.

Entities, brushes, and cameras retain the instance when it is attached. Changes made through the original handle remain visible on those attachments. FreeShader releases only the supplied script handle; it does not leave an attached object dangling. Do not use a handle after freeing it. Free textures only after accounting for any other code which still needs them; shader texture bindings retain their underlying resources.

6. Source assets, the IDE, and deployment

A supplied shader may contain compiled bytecode, or it may be source-backed. The IDE discovers literal loads such as:

shader=LoadShader("fx/glow.b3shader")

Check and Run compile a discovered source asset, report HLSL errors with file and line information, and package its descriptor, bytecode, and dependencies beside the program. Save all shader source and include files before running.

Keep the asset's relative directory structure intact when distributing a program. If a shader filename is assembled at runtime, the IDE cannot discover it statically:

shader=LoadShader("fx/"+effectName$+".b3shader")

In that case, add every possible descriptor and its dependencies to the distribution yourself. Built-in aliases require none of these nearby files.

During debug development, source-backed shaders follow a last-known-good rule. A successfully changed source is rebuilt; if the new source fails, the previous valid shader remains active and diagnostics report the failure.

7. Troubleshooting and performance

  • Start from the asset's complete working sample, then change one value at a time.
  • Read the first compiler or runtime error first; later messages may only be consequences.
  • Check the parameter spelling and setter type against the asset documentation.
  • If a brush shader appears ignored, check for an entity shader override.
  • If a shader loads in the IDE but not after copying the executable, check its packaged files and relative paths.
  • Use ShaderError to inspect retained diagnostic text on a valid instance.
  • Prefer CopyShader for variants and update parameters only when they change.
  • Camera effects require intermediate colour and depth targets. Bypass or remove effects which are not visible, especially on secondary cameras.

8. Command map

TaskCommands
Load, copy, free, or inspectLoadShader, CopyShader, FreeShader, ShaderError
Set named valuesShaderFloat, ShaderInt, ShaderVector, ShaderColor, ShaderTexture
Apply material shadersEntityShader, BrushShader
Manage post effectsCameraEffect, CameraEffectEnabled, RemoveCameraEffect

Each command has a runnable example in the Shader group of the command reference.

9. Next: authoring

The Advanced Shader Authoring Guide explains the descriptor format, HLSL includes, surface and post callbacks, raw shaders, vertex displacement, shadow policies, layout 3 bindings, compilation, and authoring diagnostics.

Shader instances · asset use · deployment · Extended mode