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 查看