AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified Zlib Self-run

Forge Hdr Tone Mapping

skill-nebulavenus-forge-gpu-forge-hdr-tone-mapping · by Nebulavenus

>

No reviews yet
0 installs
25 views
0.0% view→install

Install

$ agentstack add skill-nebulavenus-forge-gpu-forge-hdr-tone-mapping

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-nebulavenus-forge-gpu-forge-hdr-tone-mapping)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
5mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Forge Hdr Tone Mapping? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

HDR Tone Mapping Skill

Adds a two-pass HDR rendering pipeline to any SDL3 GPU scene. Derived from Lesson 21.

When to use

  • Scene lighting produces values above 1.0 (bright specular highlights,

multiple lights, emissive surfaces)

  • Highlights are clamping to flat white and losing detail
  • You want filmic color grading (ACES) instead of raw linear output
  • You need a post-processing pass architecture (foundation for bloom, SSAO)

HDR render target setup

Create a floating-point texture with both render and sample usage:

#define HDR_FORMAT SDL_GPU_TEXTUREFORMAT_R16G16B16A16_FLOAT

SDL_GPUTextureCreateInfo hdr_info;
SDL_zero(hdr_info);
hdr_info.type                 = SDL_GPU_TEXTURETYPE_2D;
hdr_info.format               = HDR_FORMAT;
hdr_info.width                = width;
hdr_info.height               = height;
hdr_info.layer_count_or_depth = 1;
hdr_info.num_levels           = 1;
hdr_info.usage = SDL_GPU_TEXTUREUSAGE_COLOR_TARGET   /* render to it */
               | SDL_GPU_TEXTUREUSAGE_SAMPLER;        /* sample from it */

SDL_GPUTexture *hdr_target = SDL_CreateGPUTexture(device, &hdr_info);

COLOR_TARGET lets the scene pass render into it. SAMPLER lets the tone map pass read from it. Both flags are required.

Tone map fragment shader (HLSL)

Texture2D    hdr_texture : register(t0, space0);
SamplerState hdr_sampler : register(s0, space0);

cbuffer TonemapUniforms : register(b0, space3) {
    float exposure;
    uint  tonemap_mode;   /* 0 = clamp, 1 = Reinhard, 2 = ACES */
    float2 _pad;
};

/* Reinhard: simple, maps 0->0, inf->1 */
float3 reinhard(float3 c) { return c / (c + 1.0); }

/* ACES filmic: S-curve with lifted shadows, rich highlights */
float3 aces(float3 c) {
    return saturate((c * (2.51 * c + 0.03)) /
                    (c * (2.43 * c + 0.59) + 0.14));
}

float4 main(float2 uv : TEXCOORD0) : SV_Target {
    float3 hdr = hdr_texture.Sample(hdr_sampler, uv).rgb;
    hdr *= exposure;

    float3 ldr;
    if      (tonemap_mode == 1) ldr = reinhard(hdr);
    else if (tonemap_mode == 2) ldr = aces(hdr);
    else                        ldr = saturate(hdr);

    return float4(ldr, 1.0);
}

Fullscreen blit vertex shader (no vertex buffer)

struct VS_Output {
    float4 clip_pos : SV_Position;
    float2 uv       : TEXCOORD0;
};

VS_Output main(uint vertex_id : SV_VertexID) {
    VS_Output output;
    /* Generate 6 vertices (2 triangles) covering the screen */
    float2 uv;
    uv.x = (vertex_id == 1 || vertex_id == 4 || vertex_id == 5) ? 1.0 : 0.0;
    uv.y = (vertex_id == 2 || vertex_id == 3 || vertex_id == 5) ? 1.0 : 0.0;
    output.clip_pos = float4(uv.x * 2.0 - 1.0, -(uv.y * 2.0 - 1.0), 0.0, 1.0);
    output.uv = uv;
    return output;
}

No vertex buffer, no index buffer. Draw with SDL_DrawGPUPrimitives(pass, 6, 1, 0, 0).

Two-pass rendering pattern (C)

/* ── Pass 1: Scene → HDR buffer ──────────────────────────────── */
SDL_GPUColorTargetInfo scene_ct;
SDL_zero(scene_ct);
scene_ct.texture     = hdr_target;
scene_ct.load_op     = SDL_GPU_LOADOP_CLEAR;
scene_ct.store_op    = SDL_GPU_STOREOP_STORE;
scene_ct.clear_color = (SDL_FColor){ 0.01f, 0.01f, 0.03f, 1.0f };

SDL_GPURenderPass *scene_pass =
    SDL_BeginGPURenderPass(cmd, &scene_ct, 1, &depth_target);
/* ... bind scene pipeline, draw geometry ... */
SDL_EndGPURenderPass(scene_pass);

/* ── Pass 2: Tone map → swapchain ────────────────────────────── */
SDL_GPUColorTargetInfo tone_ct;
SDL_zero(tone_ct);
tone_ct.texture  = swapchain_texture;
tone_ct.load_op  = SDL_GPU_LOADOP_DONT_CARE;
tone_ct.store_op = SDL_GPU_STOREOP_STORE;

SDL_GPURenderPass *tone_pass =
    SDL_BeginGPURenderPass(cmd, &tone_ct, 1, NULL);
SDL_BindGPUGraphicsPipeline(tone_pass, tonemap_pipeline);

/* Bind the HDR buffer as a texture for sampling */
SDL_GPUTextureSamplerBinding hdr_binding = {
    .texture = hdr_target,
    .sampler = hdr_sampler
};
SDL_BindGPUFragmentSamplers(tone_pass, 0, &hdr_binding, 1);

/* Push tone map uniforms */
struct { float exposure; Uint32 mode; float pad[2]; } tu;
tu.exposure = exposure_value;
tu.mode     = tonemap_mode;
SDL_PushGPUFragmentUniformData(cmd, 0, &tu, sizeof(tu));

SDL_DrawGPUPrimitives(tone_pass, 6, 1, 0, 0);
SDL_EndGPURenderPass(tone_pass);

Tone map pipeline setup

The tone map pipeline has no vertex input and no depth test:

SDL_GPUGraphicsPipelineCreateInfo tone_info;
SDL_zero(tone_info);
tone_info.vertex_shader   = tonemap_vert;
tone_info.fragment_shader = tonemap_frag;
tone_info.primitive_type  = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST;

/* No vertex input — positions generated from SV_VertexID */
tone_info.vertex_input_state.num_vertex_attributes = 0;
tone_info.vertex_input_state.num_vertex_buffers    = 0;

/* No depth test — fullscreen quad always draws */
tone_info.depth_stencil_state.enable_depth_test  = false;
tone_info.depth_stencil_state.enable_depth_write = false;

/* Target the sRGB swapchain format (query after SetGPUSwapchainParameters) */
SDL_GPUColorTargetDescription tone_ct_desc;
SDL_zero(tone_ct_desc);
tone_ct_desc.format = swapchain_format;
tone_info.target_info.num_color_targets          = 1;
tone_info.target_info.color_target_descriptions  = &tone_ct_desc;
tone_info.target_info.has_depth_stencil_target   = false;

sRGB swapchain (automatic gamma correction)

SDL_SetGPUSwapchainParameters(device, window,
    SDL_GPU_SWAPCHAINCOMPOSITION_SDR_LINEAR,
    SDL_GPU_PRESENTMODE_VSYNC);

SDR_LINEAR gives a B8G8R8A8_UNORM_SRGB format. The GPU automatically converts linear values to sRGB on write. No manual pow(color, 1.0/2.2) needed in the shader.

Typical parameter values

| Parameter | Value | Notes | |-----------|-------|-------| | exposure | 1.0 | Default; increase to brighten, decrease to darken | | lightintensity | 3.0 | Multiplier on diffuse+specular to create HDR values | | tonemapmode | 2 (ACES) | Best for most scenes; Reinhard for softer look |

Key rules

  1. Scene pipeline targets HDR format. The scene graphics pipeline's color

target must use R16G16B16A16_FLOAT, not the swapchain format. The tone map pipeline targets the swapchain format.

  1. Don't clamp lighting in the scene shader. The whole point of HDR is to

preserve values above 1.0. Remove any saturate() or min() on the final lighting result in the scene fragment shader.

  1. Push uniforms use the command buffer. SDL_PushGPUVertexUniformData

and SDL_PushGPUFragmentUniformData take SDL_GPUCommandBuffer*, not SDL_GPURenderPass*. Bind/draw calls use the render pass.

  1. Recreate HDR target on resize. When the window resizes, release and

recreate the HDR texture to match the new dimensions.

  1. Exposure before tone mapping. Multiply HDR values by exposure before

applying the tone mapping operator, not after.

Common mistakes

  • Using swapchain format for scene pipeline — values above 1.0 get

clamped, defeating the purpose of HDR.

  • Forgetting SAMPLER usage on HDR texture — the tone map pass can't

sample from a texture that only has COLOR_TARGET usage.

  • Manual gamma in the shader — with an sRGB swapchain, adding

pow(color, 1/2.2) applies gamma twice, making the image washed out.

  • Passing render pass to push uniform functions — causes a type mismatch

compile error. Push uniforms use the command buffer.

  • Not matching HDR target size to window — after resize, the tone map

pass samples a stale smaller texture, leaving black borders.

Reference

  • [Lesson 21 — HDR Tone Mapping](../../../lessons/gpu/21-hdr-tone-mapping/)
  • [Scene shader](../../../lessons/gpu/21-hdr-tone-mapping/shaders/scene.frag.hlsl)
  • [Tone map shader](../../../lessons/gpu/21-hdr-tone-mapping/shaders/tonemap.frag.hlsl)
  • [Math Lesson 11 — Color Spaces](../../../lessons/math/11-color-spaces/)

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.