What's the "accepted" way of driving LOTS of 2-digit, 16-segment, common-cathode displays?
Dimness and variable brightness might have two causes:


You're MUXing too many (36?).
Your LED current-limiting resistor(s) are in the COMMON leg, not in each of the LED legs.


Muxing more than about 8 requires the common-cathode pull-down switch to pull too much current, especially when all your digits are "8". A discrete N-channel MOSfet might be able to handle the current required of the common-cathode driver. You might consider daisy-chaining four shift registers and pushing it to nine MUX  (4 x 9 array).

For the anodes, you might get away with HCMOS shift registers (HC164, HC595). Even so, it helps to start with efficient LED segments that give decent brightness @ 1mA DC. Shifts can be done quite fast, so that the time to shift five daisy-chained HC164 doesn't eat into the MUX period (which should loop faster than 30 per second, else it becomes optically annoying). You should still optimize the shifting code for speed. The basic shift-cell might look like this (do 9 more rows of this):







simulate this circuit &ndash; Schematic created using CircuitLab

Those nine MOSfets need a way to drive their gates. An HC595 might be used to drive the MOSfets, simply shifting a single "1" in a ring. You might need two HC595 to get the extra bit (to get a 9-bit shift register). It's tri-state output might be used to turn off all the LED's while you're shifting if interrupt routines take too long, or if background flicker is distracting: the 5k pull-down gate resistor will ensure that all MOSfets turn off. That's a lot of wiring, and a lot of resistors. It'll likely cause some RFI: try to wire it all very compact.
All the shifting could be done as a background process - it doesn't need a timer and can easily be interrupted by your other peripherals so long as their handlers finish in a reasonable time. Consider using the watch-dog timer as the MUX timer (if you're short of timers), if it can be crudely set to about 3 ms.