# Cheap ways to do scaling ops in shader?

I've got an extensive world terrain that uses vec3 for the vertex position attribute. That's good, because the terrain has endless gradations due to the use of floating point. But I'm thinking about how to reduce the amount of data uploaded to the GPU. For my terrain, which uses discrete / grid-based vertex positions in x and z, it's pretty clear that I can replace my vec3s (floats, really) with shorts, halving the per-vertex position attribute cost from 12 bytes each to 6 bytes. Considering I've got little enough other vertex data, and an enormous amount of terrain data to push into the world, it's a major gain.

Currently in my code, one unit in GLSL shaders is equal to 1m in the world. I like that scale. If I move over to using shorts, though, I won't be able to use the same scale, as I would then have a very blocky world where every step in height is an entire metre.

So I see these potential solutions to scale the positional data correctly once it arrives at the vertex shader stage:

• Use 10:1 scaling, i.e. 1 short unit = 1 decimetre in CPU-side code. Do a division by 10 in the vertex shader to scale incoming decimetre values back to metres. Arbirary (non-PoT) divisions tend to be slow, however.

• Use (some-power-of-two):1 scaling (eg. 8:1), which enables the use of a bitshift (eg. val >> 3) to do the division... not sure how performant this is in shaders, though. Not as intuitive to read values, but possibly quite a bit faster than div by a non-PoT value.

• Use a texture as lookup table to convert the value from decimetres to metres. I've heard that this is really fast.

Or whatever solutions others can offer to achieve the same results -- minimal vertex data with sensible scaling.

• You're already multiplying your vertex positions by a matrix to transform them to clip space, yes? Just incorporate the scaling into the matrix. – Nathan Reed Nov 30 '12 at 19:24
• @NathanReed Hmm, Sam suggested same. Thing is, it wouldn't make any difference to processing cost, would it? (as opposed to doing it at a later stage of the pipeline) Because in the ordinary case, the values would be multiplied by the identity matrix which is one and thus probably gets factored out of the calculation by the shader code compiler(?). In other words, one way or another, it amounts to extra calculations, to my mind. – Engineer Nov 30 '12 at 19:26
• @NickWigill In a regular vertex shader you'd already be multiplying by a matrix, even if you weren't scaling your terrain. I mean, you have to get your verts from world space to clip space somehow, right? So just bake the scaling into that matrix when you compute it on the CPU once per frame. No extra vertex shader code needed. – Nathan Reed Nov 30 '12 at 20:15
• @NathanReed Totally with you there. However what I'm suggesting is that ordinarily, the values representing scale in the matrix are all 1f; certainly they are in my terrain chunks, at present. Because they are one, I'm speculating that they may well be optimised out of the calculation, and thus don't contribute to cost when no scaling is present, this sort of thing being quite common nowadays. Whereas, as soon as they become non-1, they do begin to contribute -- each component as an extra multiply. In fairness though, this may be a minor quibble, but just replying to what you were suggesting. – Engineer Nov 30 '12 at 20:26
• Two points: (1) No, they're not optimized out of the computation. The shader compiler can't know what values you're going to put in the matrix at runtime, so it has to assume they could be any float values at all. It doesn't recompile when you change the matrix. (2) The same components of the matrix are used for both rotation and scaling, so if you're doing any rotation (including camera rotation), those components are non-identity already. – Nathan Reed Nov 30 '12 at 21:54

It seems to me that you only need to push 4 bytes to the GPU and retain your full precision: only send the X and Z components once, since they will never change. It is also possible to not send any X or Z data by using gl_VertexID (I can provide you with some examples but this is slightly beyond the scope of the question).
• @NickWiggill Maybe I haven't been clear enough. The idea behind gl_VertexID is that in the case of a heightmap based on a rectangular grid you do not send vertex data at all. You build the X and Z coordinates by applying modulus and division to the value of gl_VertexID, which is a built-in variable that costs nothing in terms of data bandwidth. – sam hocevar Dec 1 '12 at 20:18