Well since Wordpress's sorting features don't include that kind of functionality, you should probably ask Matt Mullenweg... haha...
No, but really, you can probably use query_posts() for each letter (in other words call the loop up to 26 times, once for each letter. Then, for each loop, make include some code that'll check to see if the first letter of the post meta_value fits with the corresponding letter. If there are no posts that match that letter, then make just skip that letter.
You currently only have one loop. You might have to write a for() loop that will in turn generate each wordpress loop.
Something like this (just a rough draft):
<ul class="list-ensemble">
<? for ($i=65; $i<91; $i++) : // 65 through 90 represent the uppercase alphabet
query_posts('post_type=person&post_status=publish&meta_key=last_name&orderby=meta_value&order=ASC');
//PUT SOME CODE HERE TO CHECK IF THE FIRST LETTER IS EQUAL TO $i
//Set some variable $letter_matches == true if the letter matches.
?>
<h3 class="letter">
<?php //ECHO THE LETTER CORRESPONDING TO $i HERE ?>
</h3>
<?
if ( have_posts() and $letter_matches ) while ( have_posts() ) : the_post(); ?>
<li data-id="<?php the_ID(); ?>">
<a href="<?php the_permalink(); ?>" class="ensemble-single-link">
<?php if ( has_post_thumbnail() ) { the_post_thumbnail(thumbnail); } ?>
</a>
</li>
<?php endwhile; // end of the loop. ?>
<?endfor; ?>
</ul>
So essentially you will have up to 26 wordpress loops in the page. I'm not sure how straining that is on the server, but it should work. It's the first thing I thought of. Lemme know how it goes!