Generate a Coloured Hillshade from DTM or DSM in QGIS

QGIStutorialhillshadeterrain visualizationdrone mappingaerial surveyremote sensingPythonDTMDSM
Generate a Coloured Hillshade from DTM or DSM in QGIS

A raw DTM or DSM raster, styled with QGIS’s default single-band grey renderer, looks like flat noise — no depth, no readable terrain. The moment you add hillshade shading and a colour ramp, the same file becomes something a client can actually look at and understand. It’s the base layer behind almost every other image in this QGIS script series — the DSM/DTM hillshade pairs you’ve seen in the CHM, contour, lake volume, and tree height posts are all generated by this one script.

What it does

The generate_hillshade.py script reads your DTM or DSM directly with GDAL, generates a multidirectional hillshade (light averaged from multiple azimuths so there are no blind, shadow-flattened valleys), and applies a viridis colour ramp — purple → blue → teal → green → yellow, the same palette WebODM uses — blended pixel-by-pixel with the shading. Working at the pixel level with numpy sidesteps QGIS’s own raster renderer entirely, which is what makes the output consistent regardless of the source raster’s own styling.

Two behaviours worth knowing about:

  • Auto-downsampling. Rasters over 50 MB are scaled down before processing so a large drone-derived DTM/DSM doesn’t hang QGIS. The threshold is configurable.
  • Adaptive colour normalisation. Terrain with a large elevation range (a ridge-and-valley estate, say) is normalised with straight min/max. Terrain with a small range — a flat plot, or a pond site where most of the file is near-level ground — is normalised with 2nd–98th percentile instead, so the full colour palette still spreads across the subtle variation rather than collapsing to one or two colours.

Output: {name}_hillshade_colour.tif — an RGBA GeoTIFF with the colour hillshade in the RGB bands and nodata pixels made transparent via the alpha band — plus a companion .jpg for quick sharing. Both save next to the source raster and the GeoTIFF is added to your QGIS project automatically.

What you need

DSM (surface elevation, canopy included) from a real coffee estate survey:

DSM hillshade of a forested estate, coloured by elevation, canopy visible as texture

DTM (bare ground, canopy stripped out) — the same script, same file, run against the DTM instead:

DTM hillshade of the same estate, coloured by elevation

Steps

  1. Load your DTM or DSM raster into QGIS.
  2. Check (tick) the layer visible in the Layers panel.
  3. Open Plugins → Python Console → Show Editor.
  4. Paste the script. Set ELEVATION_SOURCE to "dtm" or "dsm" depending on which you loaded.
  5. Click Run. The coloured hillshade GeoTIFF and JPEG are generated and the GeoTIFF is added to your project.

Configurable parameters

At the bottom of the file, the generate_hillshade() call takes three lighting parameters:

  • z_factor — vertical exaggeration. Raise this on very flat terrain to make subtle relief more visible.
  • azimuth — light direction in degrees (default 315, northwest).
  • altitude — sun angle above the horizon (default 35). Lower values throw longer shadows and give more contrast, at the cost of some detail in shadowed areas.

ELEVATION_SOURCE, DOWNSAMPLE_THRESHOLD_MB, and SMALL_RANGE_THRESHOLD_M are set near the top of the script.

Reading flat terrain

The percentile normalisation matters most on sites without much elevation range — a pond or lake basin is a good example, since most of the surrounding ground sits close to one elevation and only the excavated basin drops away:

DTM hillshade of a pond site, coloured by elevation, showing the excavated basin as a darker depression against the surrounding ground

Without percentile stretching, a file like this would render almost entirely as one or two colours from the ramp. With it, the basin depth and the surrounding ground both stay visible.

Where this shows up elsewhere

Coloured hillshade generated by this script is the visual backdrop used throughout this series to show what a script’s source raster actually looks like: the ridge lines in Smooth Contour Generator, the excavated basin in Lake Volume Calculator, and the individual tree canopies in Tree Height Extractor and the full tree-health workflow are all this same script, run against different projects.

Get the script

generate_hillshade.py is available on its own from the Coloured Hillshade Generator product page for $20, or as part of the Drone Mapping, Terrain & Multispectral Analysis QGIS Package — all 16 QGIS automation scripts at $99 USD.