Agent skill
forge-depth-and-3d
Set up depth buffer, 3D MVP pipeline, back-face culling, and window resize handling. Use when someone needs to render 3D geometry with correct depth ordering, perspective projection, or a view camera in SDL3 GPU.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/forge-depth-and-3d
SKILL.md
Depth Buffer & 3D Transforms — MVP Pipeline, Depth Testing, and Culling
This skill teaches how to render 3D geometry with the full Model-View-Projection
pipeline, depth testing, back-face culling, and window resize handling. It builds
on the uniforms-and-motion skill (push uniforms) and first-triangle skill
(vertex buffers, pipelines).
When to use
- Rendering 3D geometry (cubes, meshes, scenes)
- Setting up perspective projection with a camera
- Enabling depth testing so closer surfaces draw over farther ones
- Adding back-face culling for closed meshes
- Handling window resize for depth textures
- Transitioning from 2D (float2) to 3D (float3) vertex positions
Key API calls (ordered)
SDL_CreateGPUTexture— create depth texture withDEPTH_STENCIL_TARGETusageSDL_CreateGPUGraphicsPipeline— with depth stencil state and back-face cullingmat4_look_at/mat4_perspective/mat4_multiply— build MVP matrix per frameSDL_PushGPUVertexUniformData— push MVP matrix to vertex shaderSDL_BeginGPURenderPass— with both color target AND depth targetSDL_DrawGPUIndexedPrimitives— draw 3D geometry
Code template
Depth texture creation
static SDL_GPUTexture *create_depth_texture(SDL_GPUDevice *device,
Uint32 w, Uint32 h)
{
SDL_GPUTextureCreateInfo info;
SDL_zero(info);
info.type = SDL_GPU_TEXTURETYPE_2D;
info.format = SDL_GPU_TEXTUREFORMAT_D16_UNORM;
info.usage = SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET;
info.width = w;
info.height = h;
info.layer_count_or_depth = 1;
info.num_levels = 1;
SDL_GPUTexture *texture = SDL_CreateGPUTexture(device, &info);
if (!texture) {
SDL_Log("Failed to create depth texture: %s", SDL_GetError());
}
return texture;
}
Pipeline setup with depth and culling
SDL_GPUGraphicsPipelineCreateInfo pipeline_info;
SDL_zero(pipeline_info);
/* ... vertex input, shaders, etc. ... */
/* Back-face culling — skip faces pointing away from camera */
pipeline_info.rasterizer_state.cull_mode = SDL_GPU_CULLMODE_BACK;
pipeline_info.rasterizer_state.front_face = SDL_GPU_FRONTFACE_COUNTER_CLOCKWISE;
/* Depth testing — closer fragments win */
pipeline_info.depth_stencil_state.enable_depth_test = true;
pipeline_info.depth_stencil_state.enable_depth_write = true;
pipeline_info.depth_stencil_state.compare_op = SDL_GPU_COMPAREOP_LESS_OR_EQUAL;
/* Color target */
SDL_GPUColorTargetDescription color_desc;
SDL_zero(color_desc);
color_desc.format = SDL_GetGPUSwapchainTextureFormat(device, window);
pipeline_info.target_info.color_target_descriptions = &color_desc;
pipeline_info.target_info.num_color_targets = 1;
/* Depth target — MUST declare format in pipeline */
pipeline_info.target_info.has_depth_stencil_target = true;
pipeline_info.target_info.depth_stencil_format = SDL_GPU_TEXTUREFORMAT_D16_UNORM;
MVP matrix computation
#include "math/forge_math.h"
/* Model: position and orient the object */
mat4 model = mat4_multiply(
mat4_translate(vec3_create(0.0f, 0.0f, 0.0f)),
mat4_multiply(mat4_rotate_y(angle_y), mat4_rotate_x(angle_x))
);
/* View: position and orient the camera */
mat4 view = mat4_look_at(
vec3_create(0.0f, 1.5f, 3.0f), /* eye position */
vec3_create(0.0f, 0.0f, 0.0f), /* look-at target */
vec3_create(0.0f, 1.0f, 0.0f) /* world up */
);
/* Projection: perspective with correct aspect ratio */
float fov = 60.0f * FORGE_DEG2RAD;
float aspect = (float)window_w / (float)window_h;
mat4 proj = mat4_perspective(fov, aspect, 0.1f, 100.0f);
/* Compose: MVP = Projection * View * Model */
mat4 mvp = mat4_multiply(mat4_multiply(proj, view), model);
Render pass with depth target
SDL_GPUColorTargetInfo color_target;
SDL_zero(color_target);
color_target.texture = swapchain;
color_target.load_op = SDL_GPU_LOADOP_CLEAR;
color_target.store_op = SDL_GPU_STOREOP_STORE;
color_target.clear_color = (SDL_FColor){ 0.02f, 0.02f, 0.04f, 1.0f };
SDL_GPUDepthStencilTargetInfo depth_target;
SDL_zero(depth_target);
depth_target.texture = depth_texture;
depth_target.load_op = SDL_GPU_LOADOP_CLEAR;
depth_target.store_op = SDL_GPU_STOREOP_DONT_CARE;
depth_target.stencil_load_op = SDL_GPU_LOADOP_DONT_CARE;
depth_target.stencil_store_op = SDL_GPU_STOREOP_DONT_CARE;
depth_target.clear_depth = 1.0f; /* far plane */
SDL_GPURenderPass *pass = SDL_BeginGPURenderPass(
cmd, &color_target, 1, &depth_target);
Window resize handling
/* Check each frame — recreate depth texture if window size changed */
int w, h;
SDL_GetWindowSizeInPixels(window, &w, &h);
if ((Uint32)w != depth_width || (Uint32)h != depth_height) {
SDL_ReleaseGPUTexture(device, depth_texture);
depth_texture = create_depth_texture(device, (Uint32)w, (Uint32)h);
depth_width = (Uint32)w;
depth_height = (Uint32)h;
}
Vertex shader (HLSL)
cbuffer Uniforms : register(b0, space1)
{
column_major float4x4 mvp;
};
struct VSInput
{
float3 position : TEXCOORD0;
float3 color : TEXCOORD1;
};
struct VSOutput
{
float4 position : SV_Position;
float4 color : TEXCOORD0;
};
VSOutput main(VSInput input)
{
VSOutput output;
output.position = mul(mvp, float4(input.position, 1.0));
output.color = float4(input.color, 1.0);
return output;
}
Vertex format for 3D
typedef struct Vertex {
vec3 position; /* 3D model-space position (FLOAT3) */
vec3 color; /* RGB per-vertex color (FLOAT3) */
} Vertex;
/* Vertex attributes */
attrs[0].format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT3; /* position */
attrs[0].offset = offsetof(Vertex, position);
attrs[1].format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT3; /* color */
attrs[1].offset = offsetof(Vertex, color);
Uniform struct
typedef struct Uniforms {
mat4 mvp; /* 64 bytes — naturally 16-byte aligned */
} Uniforms;
A single mat4 is exactly 64 bytes and naturally 16-byte aligned, so no
padding is needed (unlike the time + aspect + pad structs from earlier lessons).
HLSL column-major compatibility
forge_math.h stores matrices column-major. HLSL defaults to column_major for
float4x4. Use mul(mvp, float4(pos, 1.0)) — no transpose needed.
If you accidentally see an inside-out or mirrored cube, check:
- Matrix storage order matches shader declaration
mul(matrix, vector)notmul(vector, matrix)- Winding order matches
front_facesetting
Depth format choices
| Format | Bits | Precision | Use case |
|---|---|---|---|
D16_UNORM |
16 | Low | Simple scenes, universally supported |
D24_UNORM |
24 | Medium | Most 3D games |
D32_FLOAT |
32 | High | Large worlds, reverse-Z |
For learning and simple scenes, D16_UNORM is sufficient and universally
supported.
Common mistakes
| Mistake | Fix |
|---|---|
| Cube looks inside-out | Enable depth testing (enable_depth_test = true) |
| No depth target in pipeline | Set has_depth_stencil_target = true and depth_stencil_format |
| Crash on window resize | Recreate depth texture when window size changes |
| Cube appears mirrored | Check mul(mvp, vertex) order matches column-major storage |
| Back faces visible | Set cull_mode = CULLMODE_BACK with correct front_face |
| Depth buffer not clearing | Set load_op = LOADOP_CLEAR and clear_depth = 1.0f |
| Missing perspective | Use mat4_perspective not mat4_orthographic |
Cleanup
SDL_ReleaseGPUTexture(device, depth_texture);
SDL_ReleaseGPUBuffer(device, index_buffer);
SDL_ReleaseGPUBuffer(device, vertex_buffer);
SDL_ReleaseGPUGraphicsPipeline(device, pipeline);
Reference
- GPU Lesson 06 — Depth Buffer & 3D Transforms — full implementation
- Math Lesson 05 — Matrices — transform theory
- uniforms-and-motion skill — push uniforms
- first-triangle skill — vertex buffers, pipelines
mat4_perspective(),mat4_look_at()incommon/math/forge_math.h
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?