Agent skill

forge-environment-mapping

Add cube map environment mapping with a skybox and reflective surfaces. Use when someone needs a 360-degree background, reflection mapping, or cube map textures in SDL3 GPU.

Stars 163
Forks 31

Install this agent skill to your Project

npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/forge-environment-mapping

SKILL.md

Environment Mapping — SDL3 GPU Skill

Add a cube map skybox and environment reflections to a 3D scene.

When to use this skill

  • Rendering a 360-degree background (space, sky, indoor environment)
  • Adding reflections to metallic or glossy surfaces
  • Working with SDL_GPU_TEXTURETYPE_CUBE textures
  • Combining multiple graphics pipelines in one render pass

Cube map texture creation

c
/* Create a cube map texture with 6 faces. */
SDL_GPUTextureCreateInfo tex_info;
SDL_zero(tex_info);
tex_info.type                 = SDL_GPU_TEXTURETYPE_CUBE;
tex_info.format               = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM_SRGB;
tex_info.usage                = SDL_GPU_TEXTUREUSAGE_SAMPLER;
tex_info.width                = face_size;
tex_info.height               = face_size;
tex_info.layer_count_or_depth = 6;  /* MUST be 6 for cube maps */
tex_info.num_levels           = 1;

SDL_GPUTexture *cubemap = SDL_CreateGPUTexture(device, &tex_info);

/* Upload each face as a separate layer (0=+X, 1=-X, 2=+Y, 3=-Y, 4=+Z, 5=-Z). */
for (int face = 0; face < 6; face++) {
    /* Load face image, convert to ABGR8888, map transfer buffer... */

    SDL_GPUTextureRegion dst;
    SDL_zero(dst);
    dst.texture = cubemap;
    dst.layer   = (Uint32)face;  /* Face index = layer index */
    dst.w       = face_size;
    dst.h       = face_size;
    dst.d       = 1;

    SDL_UploadToGPUTexture(copy_pass, &src, &dst, false);
}

Skybox rendering pattern

Vertex shader — depth = 1.0

hlsl
cbuffer VertUniforms : register(b0, space1)
{
    column_major float4x4 vp_no_translation;
};

struct VSOutput
{
    float4 clip_pos  : SV_Position;
    float3 direction : TEXCOORD0;
};

VSOutput main(VSInput input)
{
    VSOutput output;
    output.direction = input.position;

    float4 pos = mul(vp_no_translation, float4(input.position, 1.0));
    output.clip_pos = pos.xyww;  /* z = w → depth = 1.0 after divide */

    return output;
}

Fragment shader — sample cube map

hlsl
TextureCube  skybox_tex : register(t0, space2);
SamplerState smp        : register(s0, space2);

float4 main(PSInput input) : SV_Target
{
    return skybox_tex.Sample(smp, input.direction);
}

Pipeline settings

c
/* Cull FRONT faces — we're inside the cube. */
pipe_info.rasterizer_state.cull_mode = SDL_GPU_CULLMODE_FRONT;

/* Depth test LESS_OR_EQUAL to pass at depth=1.0.
 * Depth write DISABLED — skybox should never occlude anything. */
pipe_info.depth_stencil_state.enable_depth_test  = true;
pipe_info.depth_stencil_state.enable_depth_write = false;
pipe_info.depth_stencil_state.compare_op = SDL_GPU_COMPAREOP_LESS_OR_EQUAL;

Rotation-only view matrix

c
/* Strip translation so the skybox always surrounds the camera. */
mat4 view_rot = view;
view_rot.m[12] = 0.0f;
view_rot.m[13] = 0.0f;
view_rot.m[14] = 0.0f;
mat4 vp_sky = mat4_multiply(proj, view_rot);

Reflection mapping pattern

Fragment shader with reflections

hlsl
TextureCube env_tex : register(t1, space2);

/* Compute reflection direction */
float3 V = normalize(eye_pos.xyz - world_pos);
float3 R = reflect(-V, N);
float3 env_color = env_tex.Sample(env_smp, R).rgb;

/* Blend diffuse with environment reflection */
float3 blended = lerp(surface_color.rgb, env_color, reflectivity);

Render order

  1. Clear depth to 1.0
  2. Draw skybox (depth write off, passes at depth=1.0)
  3. Draw objects (depth write on, depth < 1.0 always passes)

Common mistakes

  1. Forgetting layer_count_or_depth = 6 — cube maps require exactly 6 layers. Without this, texture creation fails silently or renders black.

  2. Wrong face orderSDL_GPUCubeMapFace enum order is +X, -X, +Y, -Y, +Z, -Z. Mixing up faces produces seams or incorrect reflections.

  3. Depth write enabled on skybox — the skybox writes depth=1.0 everywhere, occluding all subsequent geometry. Always disable depth write for the skybox.

  4. Using full view matrix for skybox — includes camera translation, causing visible parallax when moving. Strip translation columns (m[12], m[13], m[14]).

  5. Wrong cull mode for skybox — the camera is inside the cube, so front faces point inward (toward the camera). Cull front faces, not back faces.

  6. Not negating V in reflect()reflect() expects the incident vector pointing toward the surface. The view direction V points away from the surface, so pass -V.

File layout

text
shaders/
├── skybox.vert.hlsl      # Unit cube → clip space with depth=1.0
├── skybox.frag.hlsl      # Sample TextureCube
├── object.vert.hlsl      # Standard MVP + model (same as lighting)
└── object.frag.hlsl      # Blinn-Phong + reflect() + cube map sample

References

  • Lesson 14 — Environment Mapping
  • Lesson 10 — Basic Lighting (Blinn-Phong)
  • Math Lesson 01 — Vectors (dot product, reflection)

Expand your agent's capabilities with these related and highly-rated skills.

Didn't find tool you were looking for?

Be as detailed as possible for better results