Using a LUT to speed up a trig heavy shader for mobile devices I am trying to get this shader to run on a really old iDevice, as well as eventually Androids.

Even when I reduce the code down to 2 sine functions per fragment the shader runs at about 20fps.

I have considered taking a leaf out of the book of old shading techniques and creating an array that holds a bunch of predefined trig values and somehow using those to approximate the shader.

In the shader I linked above I am already simulating that by rounding the values sent to the trig function the further left the mouse (while down) goes the less quality of the shader. It's actually pretty cool because really close to the left side it looks like a completely different and pretty cool shader.

Anyway I have two dilemmas:


I don't know what the most efficient way to have an array of like 360 values in it in a GLSL shader, constant or uniform?
I can't figure out how to put a number in a range like usually if I wanted an angle between 0 and 360 (yes I know GPUs use radians) I would do it like so.

func range(float angle)
{
   float temp = angle
   while (temp &gt; 360) {temp -= 360;}
   while (temp &lt; 0)   {temp += 360;}
   return temp;
}


However GLSL does not allow while loops or recursive functions.
It has been suggested in comments repeatedly, but noone felt the need to give a proper answer, so for the sake of completeness, a straight-forward and common solution to this problem might be to use a texture as lookup table, specifically a 1D texture that contains all the values of your function for the possible input range (i.e. $[0,360)$ / $[0,2\pi)$). This has various advantages:


It uses normalized coordinates, i.e. you access the texture by mapping your angles from $[0,360]$ to $[0,1]$. This means your shader doesn't actually have to care about the specific amount of values. You can adjust its size according to whatever memory/speed vs. quality tradeoff you want (and especially on older/embedded hardware you might want a power of two as texture size anyway).
You get the additional benefit of not having to do your loop-like interval adjustment (although, you wouldn't need loops anyway and could just use a modulus operation). Just use GL_REPEAT as wrapping mode for the texture and it will automatically start at the beginning again when accessing with arguments > 1 (and similarly for negative arguments).
And you also get the benefit of linearly interpolating between two values in the array basically for free (or let's say almost free) by using GL_LINEAR as texture filter, this way getting values you didn't even store. Of course linear interpolation isn't 100% accurate for trigonometric functions, but it's certainly better than no interpolation.
You can store more than one value in the texture by using an RGBA texture (or however many components you need). This way you can get e.g. sin and cos with a single texture lookup.
For sin and cos you only need to store values in $[-1,1]$ anyway, which you can naturally upscale from the normalized $[0,1]$ range of a common 8-bit fixed-point format. However, that might not be enough precision for your needs. Some people suggested using 16-bit floating point values, as they're more precise than the usual 8-bit normalized fixed point values but less memory intensive than real 32-bit floats. But then again, I also don't know if your implementation supports floating point textures to begin with. If not, then maybe you can use 2 8-bit fixed point components and combine them into a single value with something like float sin = 2.0 * (texValue.r + texValue.g / 256.0) - 1.0; (or even more components for finer grain). This lets you profit from multi-component textures yet again.


Of course it still has to be evaluated if this is a better solution, since texture access isn't entirely free either, as well as what the best combination of texture size and format would be.

As to filling the texture with data and adressing one of your comments, you have to consider that texture filtering returns the exact value at the texel center, i.e. a texture coordinate off by half the texel size. So yes, you should generate values at .5 texels, i.e. something like this in application code:

float texels[256];
for(unsigned int i = 0; i &lt; 256; ++i)
    texels[i] = sin((i + .5f) / 256.f) * TWO_PI);
glTexImage1D(GL_TEXTURE_1D, 0, ..., 256, 0, GL_RED, GL_FLOAT, texels);




You might, however, still want to compare this approach's performance against an approach using a small uniform array (i.e. uniform float sinTable[361], or maybe less in practice, keep an eye on your implementation's limit on uniform array size) which you just load with the respective values using glUniform1fv and access by adjustig your angle to $[0,360)$ using the mod function and rounding it to the nearest value:

angle = mod(angle, 360.0);
float value = sinTable[int(((angle &lt; 0.0) ? (angle + 360.0) : angle) + 0.5)];