Skip to main content

forge-atlas-rendering

Add atlas-based texture rendering to an SDL GPU project. Load atlas metadata from JSON, remap UVs in the fragment shader, and reduce texture bind state changes.

설치로 이동

소스 정보

저장소
Nebulavenus/forge-gpu
최근 소스 활동
2026년 3월 22일 09:48
감지된 SKILL.md 언어
영어
스타
38
포크
7

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
forge-atlas-rendering
description
Add atlas-based texture rendering to an SDL GPU project. Load atlas metadata from JSON, remap UVs in the fragment shader, and reduce texture bind state changes.
trigger
atlas rendering, texture atlas GPU, atlas UV remap, reduce texture binds
# Atlas-Based Texture Rendering Add texture atlas rendering to an SDL GPU project. Loads pre-packed atlas metadata (UV offset/scale per material), remaps UVs in the fragment shader, and binds a single atlas texture instead of N per-material textures. Based on [GPU Lesson 47](../../../lessons/gpu/47-texture-atlas-rendering/). ## Prerequisites - An atlas image (PNG) and metadata file (atlas.json) generated by the pipeline's atlas plugin ([Asset Lesson 17](../../../lessons/assets/17-texture-atlas/)) - `forge_pipeline.h` for `forge_pipeline_load_atlas()` - cJSON (third_party/cJSON) for JSON parsing ## Atlas metadata format ```json { "version": 1, "width": 2048, "height": 2048, "padding": 4, "utilization": 0.499, "entries": { "material_name": { "x": 4, "y": 4, "width": 256, "height": 256, "u_offset": 0.002, "v_offset": 0.002, "u_scale": 0.125, "v_scale": 0.125 } } } ``` ## C setup ```c #define FORGE_PIPELINE_IMPLEMENTATION #include "pipeline/forge_pipeline.h" /* Load atlas metadata */ ForgePipelineAtlas atlas_meta; if (!forge_pipeline_load_atlas("assets/atlas.json", &atlas_meta)) { SDL_Log("Failed to load atlas metadata"); return SDL_APP_FAILURE; } /* Load atlas texture (single image containing all materials) */ SDL_GPUTexture *atlas_texture = load_png_texture(device, "assets/atlas.png"); /* Create sampler with clamp-to-edge to prevent atlas bleeding */ SDL_GPUSamplerCreateInfo si; SDL_zero(si); si.min_filter = SDL_GPU_FILTER_LINEAR; si.mag_filter = SDL_GPU_FILTER_LINEAR; si.mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_LINEAR; si.address_mode_u = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE; si.address_mode_v = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE; si.address_mode_w = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE; ``` ## Key API calls - `forge_pipeline_load_atlas(path, &atlas)` — parse atlas.json into `ForgePipelineAtlas` with per-entry UV offset/scale - `forge_pipeline_free_atlas(&atlas)` — release parsed atlas metadata - `SDL_BindGPUFragmentSamplers(pass, 0, &bind, 1)` — bind the atlas texture once before the draw loop (the state change this pattern eliminates) - `SDL_PushGPUFragmentUniformData(cmd, 0, &fu, sizeof(fu))` — push per-object UV transform selecting the correct atlas region ## Fragment shader (HLSL) ```hlsl cbuffer FragUniforms : register(b0, space3) { float4 uv_transform; /* xy = offset, zw = scale */ /* ... lighting params ... */ }; Texture2D material_tex : register(t0, space2); SamplerState material_smp : register(s0, space2); float4 main(PSInput input) : SV_Target { /* Atlas UV remapping: transform original UVs to atlas coordinates */ float2 atlas_uv = input.uv * uv_transform.zw + uv_transform.xy; float4 albedo = material_tex.Sample(material_smp, atlas_uv); /* ... lighting ... */ } ``` ## Atlas entry lookup Build a material-to-entry index table at init time to avoid per-frame string comparisons and to decouple material order from atlas JSON insertion order: ```c /* Init: build material → atlas entry index lookup */ int atlas_entry_idx[MATERIAL_COUNT]; for (int i = 0; i < MATERIAL_COUNT; i++) { atlas_entry_idx[i] = -1; for (int j = 0; j < atlas_meta.entry_count; j++) { if (SDL_strcmp(atlas_meta.entries[j].name, MATERIAL_NAMES[i]) == 0) { atlas_entry_idx[i] = j; break; } } } ``` ## Draw loop Use `forge_scene_bind_textured_resources()` once before the loop, then `forge_scene_draw_textured_mesh_no_bind()` per mesh to avoid redundant pipeline and sampler binds: ```c /* Bind atlas texture + pipeline ONCE before the draw loop */ forge_scene_bind_textured_resources(scene, atlas_texture, sampler); for (int i = 0; i < material_count; i++) { /* Look up this material's UV transform via precomputed index */ int idx = atlas_entry_idx[i]; float uv[4] = { 0.0f, 0.0f, 1.0f, 1.0f }; /* identity fallback */ if (idx >= 0) { ForgePipelineAtlasEntry *e = &atlas_meta.entries[idx]; uv[0] = e->u_offset; uv[1] = e->v_offset; uv[2] = e->u_scale; uv[3] = e->v_scale; } forge_scene_draw_textured_mesh_no_bind( scene, vb, ib, index_count, model, uv); } ``` ## Backwards compatibility For models that don't use an atlas, pass identity UV transform values: ```c fu.uv_transform[0] = 0.0f; fu.uv_transform[1] = 0.0f; fu.uv_transform[2] = 1.0f; fu.uv_transform[3] = 1.0f; ``` This makes the shader work identically for both atlas and individual texture modes — only the bound texture and uniform values differ. ## Cleanup ```c forge_pipeline_free_atlas(&atlas_meta); SDL_ReleaseGPUTexture(device, atlas_texture); SDL_ReleaseGPUSampler(device, sampler); ``` ## Common mistakes - **Using `REPEAT` address mode**: atlas textures must use `CLAMP_TO_EDGE` — repeat mode wraps UVs into adjacent material regions, producing visible seams and color bleeding. - **Forgetting identity transform for non-atlas objects**: when mixing atlas and individual textures in the same shader, pass `vec4_create(0.0f, 0.0f, 1.0f, 1.0f)` as the UV transform for individual textures so the remap becomes a no-op. - **Looking up entries by string every frame**: atlas entry lookup by material name involves string comparison. Build an index mapping (material → entry index) once at init time, not per-frame. - **Ignoring mipmap bleeding**: at lower mip levels, bilinear filtering samples across atlas region boundaries. The atlas packer adds padding to mitigate this, but padding halves at each mip level — visible artifacts appear at mip 3+. ## Tradeoffs - **No tiling**: clamp-to-edge prevents sampling adjacent materials but disables texture wrapping - **Mipmap bleeding**: padding halves at each mip level; visible at mip 3+ - **Resolution uniformity**: all materials share the atlas resolution budget ## CMakeLists.txt ```cmake add_executable(my-target WIN32 main.c ${CMAKE_SOURCE_DIR}/third_party/cJSON/cJSON.c ) target_include_directories(my-target PRIVATE ${FORGE_COMMON_DIR} ${CMAKE_SOURCE_DIR}/third_party/cJSON ) target_link_libraries(my-target PRIVATE SDL3::SDL3 $<$<NOT:$<C_COMPILER_ID:MSVC>>:m>) ```
GitHub에서 보기