I used it in some not-so-frequent cases to store normals, so I think I do need those.
I’ll ignore the 4-byte alignment issue for a moment to mention a more practical concern. 16-bit half-floats spend 5 bits on their exponent. The components of a normal are never greater than 1 (or less than -1). Which means that, at best, you’re wasting a bit of your exponent, since your exponent will always be either 0 or negative.
Worse still is the whole floating-point nature of a float. The purpose of a floating-point number is that the mantissa provides a regular amount of precision throughout (most of) the entire range of the representable numbers. A 16-bit float can represent 0.000123 just as exactly as it can 0.123. While a normalized, signed integer can’t represent the former exactly.
However, given the range restriction on normals, the floating-point nature… just isn’t very useful. So you can store 0.000123 exactly. Is it really that important to store ultra-small components like that? Or is it more important to exactly represent larger numbers? Because a normalized, 16-bit integer can store 0.12345, while the half-float will have to round it. This is because the half-float has to spend 5 bits on an exponent, while the normalized integer can use all its bits for the mantissa.
You’d be better off using normalized, signed 16-bit integers. They’d take up the same room and give you better fidelity where it counts. Then again, you’d probably be better off using GL_INT_2_10_10_10_REV, which packs 3 10-bit signed integers into a single 4-byte word. These have the same effective bitdepth as the half-float’s mantissa, but without that pesky exponent taking up unnecessary space. Not only is it smaller, you also have 2 bits to play with, if you want to add some Boolean vertex property.
As for the 4-byte alignment thing, I recall reading (a long time ago, so take that for what it’s worth) that it’s a bad idea performance-wise for any particular attribute to not be at least 4-byte aligned. So that might be something worth investigating.
Generally speaking, I’ve always found half-float vertex attributes to be… dubious. They always sound like a great idea. But I’m never really sure where they’d be more useful than something else. Maybe for texture coordinates that have to get big. And yet, if they get big, I’d much rather have the precision of a 32-bit float, where you can get into the thousands and still have 3 decimal digits of precision (texture coordinate precision is nothing to skimp on).
GLSL has native support for those half types
Do not confuse the parameters for glVertexAttribPointer (and its ilk) with what “GLSL” supports. The vertex format defines what the vertex pulling and decoding hardware does. That is not GLSL itself (though it could involve programmable hardware, and even be partially implemented in the VS, but that’s an implementation detail).
The only mention I see made in GLSL 4.50’s spec to “hvec4” is in a list of reserved keywords. And that particular list is for reserved keywords that are unused. The spec clearly says, “The following are the keywords reserved for future use. Using them will result in a compile-time error:”
So while you can use half-floats as vertex attributes, GLSL itself does not support them in code.