Quick answer: Render at a low base resolution. Disable texture filtering. Scale application surface to window by integer factors. Camera zoom changes view size in integer steps.
Here is how to fix GameMaker pixel art that blurs or warps when the camera zooms. Pixel-perfect requires integer scaling and disabled filtering.
The Symptom
Camera zoom changes from 1.0 to 1.5. Sprites blur, edges shimmer, the look loses crispness.
What Causes This
Non-integer zoom. 1.5x scaling samples between source pixels; bilinear filtering blurs.
Bilinear filtering on. Smooths between texels; ruins crisp pixel art.
Application surface variable size. Resizing produces non-integer outputs.
The Fix
Step 1: Disable texture filtering. Game Options → Graphics → Texture Page Size and uncheck Bilinear Filtering. Or per-frame:
gpu_set_tex_filter(false);
Step 2: Render at fixed low resolution.
// Game Start
surface_resize(application_surface, 320, 180);
window_set_size(320 * 3, 180 * 3); // 3x scale
Application surface is the low-res render target; window is 3x.
Step 3: Camera zoom in integer steps.
// 1x
view_set_size(0, 320, 180);
// 2x zoom-in (smaller view)
view_set_size(0, 160, 90);
// 0.5x zoom-out
view_set_size(0, 640, 360);
All values divide evenly into the application surface for crisp result.
Step 4: Disable application_surface_draw_enable for custom upscale.
application_surface_draw_enable(false);
// Post Draw event
draw_surface_stretched(application_surface, 0, 0,
window_get_width(), window_get_height());
Force integer-scale upscale to prevent fractional sampling.
Step 5: Window aspect ratio handling. If window is not perfect 16:9, letterbox or pillarbox by computing the largest integer multiple that fits. Avoid stretching to fill.
“Low-res surface. Filtering off. Integer scale. Pixel art crisp.”
Related Issues
For surface lifecycle, see Surface Lost. For shader uniforms, see Shader Uniform.
Filtering off. Fixed low res. Integer scale. Pixel art preserved.