In my case the terrain can be modified, it's not a static procedural defined shape.
So? This is even worse for dynamic terrain... for example: grass "generating" underneath a clump of dirt you just blew off an instant ago.
I don't see why I couldn't have grass in caves, the greyscale map just has to be three dimensional as you said. But once again, since the terrain can be modified I don't see why I'd want to store the grass density in any map, it should be part of the voxel data.
Yes, that is obvious. The problem we wanted to address was memory consumption. I guess if you only used a single type for each block, that would only be an additional byte (though only allowing 256 types, but that's quite a lot anyway) ... So if you don't think this is a problem to you at all, then go ahead. I recommend you add an additional byte for every voxel, if that is a satisfactory way for you to integrate material types. At least integrate them in some minimal way or another, because they're important.
Oh yeah, I just realized... if the form of your terrain simply requires each voxel to specify whether it is solid or not, then just use the same byte and any material type not equal to zero will be solid (type 0 = empty). You probably already know that. I feel stupid.