Shader Spec

Reference for writing effect shaders in Arshes. Shaders are written in Slang and compiled to Metal on device. User code is self-contained — there are no modules to import.

Entry Points

Fragment

Entry point. Receives UV (0.0-1.0) or custom VertexOutput, returns RGBA.

[shader("fragment")]
float4 fragmentMain(float2 uv : TEXCOORD) : SV_Target {
    return float4(uv, 0.0, 1.0);
}

Vertex (Optional)

Custom vertex shader with [primitive(type, count)]. Overrides default fullscreen quad.

struct VertexOutput {
    float4 position : SV_Position;
    float4 color : COLOR;
};

[primitive(4, 1000)]
[shader("vertex")]
VertexOutput vertexMain(uint vid : SV_VertexID, uint iid : SV_InstanceID) {
    Particle p = particles[iid];
    // vid: 0~3 (quad corners)
    // iid: 0~999 (which particle)
}

Compute (Optional)

Runs before fragment, in declaration order. Multiple compute shaders can be declared. Use [dispatch] and [numthreads] to set thread count.

struct State {
    float value;
};

[size(1)]
RWStructuredBuffer<State> state;

[dispatch(1, 1, 1)]
[numthreads(1, 1, 1)]
[shader("compute")]
void computeMain(uint3 id : SV_DispatchThreadID) {
    state[0].value = lerp(state[0].value, microphone, 0.1);
}

Built-in Uniforms

Values and textures provided by the engine. Declare them with the exact names below and they are bound automatically.

time

Elapsed time in seconds since the shader started.

uniform float time;

deltaTime

Time in seconds since the last frame.

uniform float deltaTime;

frame

Frame count since the shader started. Resets on recompile.

uniform int frame;

resolution

Screen resolution in pixels (width, height). RESOLUTION_X/Y macros are also available as int.

uniform float2 resolution;

cameraTex

Camera RGB texture in linear color space (no color grading applied). The filter (color grading) and sRGB encoding are applied after your effect.

uniform Texture2D<float3> cameraTex;

depthTex

Depth texture. Available with LiDAR or dual wide camera. DEPTH_RESOLUTION_X / DEPTH_RESOLUTION_Y macros expose its dimensions as int.

uniform Texture2D<float> depthTex;

backbuffer

Previous frame buffer.

uniform Texture2D<float4> backbuffer;

blueNoiseTex

256x256 single-channel blue noise texture. Read exact texels with .Load(int3(px % 256, py % 256, 0)) to preserve the noise (no sampler filtering). Tileable across the 256x256 boundary.

uniform Texture2D<float> blueNoiseTex;

sampler

Sampler state for texture sampling. Any name can be used.

uniform SamplerState sampler;

microphone

Microphone input volume (RMS). Ranges from 0.0 to 1.0.

uniform float microphone;

touch

Touch input. xy: position (0,0)-(1,1), z: 1.0 while touching, 0.0 when released. xy retains last position.

uniform float3 touch;

cameraPosition

Camera world position. Available when AR is enabled.

uniform float3 cameraPosition;

invViewMatrix

Inverse view matrix of the camera. Available when AR is enabled.

uniform float4x4 invViewMatrix;

invProjectionMatrix

Inverse projection matrix of the camera. Available when AR is enabled.

uniform float4x4 invProjectionMatrix;

Read-Write Resources

Persistent resources you declare yourself (any name). Their contents survive across frames.

RWStructuredBuffer

Persistent read-write buffer for compute shaders. Supports custom structs. Use [size(N)] to set element count.

[size(4)]
RWStructuredBuffer<float4> state;

RWTexture2D

Persistent read-write 2D texture. Use [size2d(w, h)] for dimensions. Optionally declare a matching Texture2D (sans "RW" prefix) for bilinear sampling.

[size2d(RESOLUTION_X, RESOLUTION_Y)]
RWTexture2D<float4> canvasRW;

// read-only view of canvasRW
Texture2D<float4> canvas;

Attributes

[range(min, max, default)]

Exposes a uniform float/int as a slider.

[range(0.0, 1.0, 0.5)]
uniform float brightness;

[rgb(r, g, b)]

Exposes a uniform float3 as a color picker. Colors are authored in sRGB and automatically converted to linear before reaching your shader (your effect works in linear space), so the picked color appears as-is.

[rgb(1.0, 0.0, 0.0)]
uniform float3 tintColor;

[rgba(r, g, b, a)]

Exposes a uniform float4 as a color picker with alpha. RGB is authored in sRGB and converted to linear automatically; alpha is passed through unchanged.

[rgba(1.0, 1.0, 1.0, 0.5)]
uniform float4 bgColor;

[toggle(default)]

Exposes a uniform bool as a toggle.

[toggle(true)]
uniform bool useEffect;

[size(N)]

Sets the element count for RWStructuredBuffer. Default: 1.

[size(64)]
RWStructuredBuffer<float4> particles;

[size2d(w, h)]

Sets the texture dimensions for RWTexture2D. Use RESOLUTION_X/Y for screen-linked sizing, or DEPTH_RESOLUTION_X/Y for depth-linked sizing.

[size2d(RESOLUTION_X / 2, RESOLUTION_Y / 2)]
RWTexture2D<float4> canvas;

[dispatch(x, y, z)]

Sets threadgroup count for a compute shader. Total threads = dispatch × numthreads.

[dispatch(RESOLUTION_X / 8, RESOLUTION_Y / 8, 1)]
[numthreads(8, 8, 1)]
[shader("compute")]
void computeMain(uint3 id : SV_DispatchThreadID) { }

[primitive(type, count)]

Defines geometry for a custom vertex shader. type: verts per primitive (1=point, 3=tri, 4=quad). count: instances.

[primitive(4, 1000)]
[shader("vertex")]
VertexOutput vertexMain(uint vid : SV_VertexID, uint iid : SV_InstanceID) {
    // vid: 0~3 (quad corners), iid: 0~999 (particle index)
}

[pass(index)]

Defines multi-pass rendering. Passes render sequentially (pass 0 clears, 1+ preserves). Max 8.

[pass(0)]
[shader("fragment")]
float4 background(float2 uv : TEXCOORD) : SV_Target {
    return float4(uv, 0.5, 1.0);
}

[pass(1)]
[primitive(3, 1)]
[shader("vertex")]
VertexOutput triVert(uint vid : SV_VertexID, uint iid : SV_InstanceID) { ... }

[pass(1)]
[shader("fragment")]
float4 triangle(VertexOutput input) : SV_Target {
    return input.color;
}

[blend(blendMode)]

Sets blend mode: BlendMode.None (overwrite), BlendMode.Additive (src+dst), BlendMode.Alpha (standard alpha blend).

[blend(BlendMode.Alpha)]
[shader("fragment")]
float4 overlay(float2 uv : TEXCOORD) : SV_Target {
    float d = length(uv - 0.5);
    float alpha = smoothstep(0.3, 0.2, d);
    return float4(1.0, 0.5, 0.0, alpha);
}