Apache It's nice to see I'm not the only one interested in this!
First I'll leave the relevant part of the source code, hope it's not too confusing since it's not the cleanest implementation. I clarify the heroine is just the name for the character in this case.
//Don't draw pixels behind heroine
//position = position of the fragment in the world
//heroine_side = side length of the texture (texture is square), could be a uniform but hardcoded here
//character_present = uniform that is true if the character is in the scene
//heroine_flip = uniform that is true if the heroine sprite is flipped
//heroine_position = the position of the character in the world
//center = position of the bottom of each instanced grass blade
//Here we calculate the uv of the heroine appropriate for the position of the fragment
//This so we can sample the heroine/character texture
//Because the position is the center of the texture, we want the corner of where the texture sprite would start
vec2 heroine_corner = heroine_position - vec2(heroine_side, heroine_side) * 0.5;
vec2 heroine_uv = (position - heroine_corner) / heroine_side;
heroine_uv.x = max(0.0, min(1.0, heroine_uv.x));
heroine_uv.y = max(0.0, min(1.0, heroine_uv.y));
//This is in case the sprite is flipped
vec2 flipped_heroine_uv = vec2((1.0 - heroine_uv.x), heroine_uv.y);
heroine_uv = heroine_flip * flipped_heroine_uv + (1.0 - heroine_flip) * heroine_uv;
//Condition if the blade of grass is actually located behind the character in the world
//Since it's top down view, grass with a higher y value should be technically behind the character
//That is the grass that should not be rendered on top of the character
float behind_character = float(center.y < (heroine_position.y));
//Condition if the FRAGMENT is rendered in the same place as a non invisible pixel on the screen
//We use the alpha value, the 0.6 is chosen instead of 0 to avoid blurrier edges from the character
float not_visible = float(texture(heroine_sprite, heroine_uv).w >= 0.6);
//If these three conditions are true, the grass is invisible
COLOR = COLOR - COLOR * round(behind_character * not_visible * character_present);
It's worth noting that the scenes are ordered so without the shader the grass renders on TOP of the character. This way we technically render all the grass and just make a portion invisible in some conditions.
Here's what it looks like when the character is invisible, you can see a part of the grass is simply not rendered.
As for your question: I absolutely think this could work with a lot of enemies, tho you'd have to be careful with memory bandwidth if optimization is important to the project, because of all the textures that would be transferred to the GPU each frame. Shouldn't be a problem but worth keeping in mind.
Hope this was informative and the code isn't too bad!