Skip to content

Shading / Vignette

Flat shading and vignette improve visual depth after the main wall projection is already known. They do not alter DDA traversal, hit distance, or wall geometry. The active color transforms are the ceiling/floor flat shading pass and the world-level vignette pass.

Color post-processing

Shading alters color output, never geometry

These passes run after geometry and texture coordinates are already known. Their job is to improve depth perception and contrast while keeping the underlying wall projection unchanged.

01
three distinct shading passes

Flat shading and vignette are active in the current frame path; wall shading exists as a helper.

01 apply_wall_shading(...) helper

available distance/face helper in bonus code; not currently called by draw_wall_column

                factor = 1.0 / (1.0 + perp_dist * DIST_FACTOR)
factor = max(factor, AMBIENT_MIN)
if (side == 1)
  factor *= FACE_FACTOR
              
02 apply_flat_shading(color, y, screen_h)

vertical gradient for ceiling and floor fills around the horizon line

                dist_factor = abs(y - screen_h / 2) / (screen_h / 2)
factor = FLAT_MIN + dist_factor * FLAT_RANGE
              
03 apply_world_vignette(frame)

radial post-pass darkening after world render and before the HUD overlay

                dx = (x - W / 2) / (W / 2)
dy = (y - H / 2) / (H / 2)
d = sqrt(dx * dx + dy * dy)
factor = max(0, 1.0 - pow(d, VIGNETTE_POWER) * VIGNETTE_STR)
              
02
interactive shading preview

Adjust attenuation and vignette constants to see how the same scene changes.

visual preview of available shading constants and active vignette/flat passes
03
frame order constraint

Vignette belongs to the world pass, so HUD elements are drawn after it.

1. world texels

walls are sampled directly; ceiling and floor pass through flat shading

2. vignette pass

radial darkening is applied on the world image only

3. HUD draw

UI stays sharp and readable because it is not part of the vignette pass

visual depth improves without touching projection

Geometry, DDA hits, and texture bounds stay the same. Only final colors are transformed before the frame is presented.

geometry unchanged texels validated first HUD unaffected by vignette constants remain tunable

In the current frame flow:

  1. wall texels are sampled during wall draw
  2. ceiling and floor colors pass through apply_flat_shading(...)
  3. the vignette pass darkens the world near the end of world rendering
  4. the HUD is drawn after that pass

That ordering keeps the UI readable even when the world view is darkened.

The bonus code contains an apply_wall_shading(...) helper, but the current wall draw path does not call it. Wall texels are sampled directly, then the world-level vignette pass can darken the final frame.

If wall shading is reconnected later, it is designed to depend on:

  • perpendicular distance
  • side hit
  • optional face-specific factors

Those inputs are not part of the active wall draw path until the helper is wired back into draw_wall_column(...).

Flat shading is applied to the ceiling and floor fills.

Instead of using texture coordinates, it derives a factor from the pixel row relative to the horizon. That creates a simple depth cue on large flat regions without requiring textured floors or ceilings.

The vignette pass computes a radial factor from the screen center and darkens pixels near the borders.

Its purpose is visual focus:

  • center remains more visible
  • borders are darker
  • effect is purely post-process

These passes must preserve several guarantees:

  • geometry is unchanged
  • texel bounds were validated before shading
  • frame buffer writes stay valid
  • HUD remains outside the vignette pass

The tuning constants for these effects live in:

  • include/defines_bonus.h

That includes face factors, minimum ambient values, flat shading ranges, and vignette strength / power values.

  • srcs_bonus/retro/shading.c
  • srcs_bonus/retro/flat_shading.c
  • srcs/render/draw_vignette.c
  • srcs/render/render_frame.c
  • include/defines_bonus.h