<p>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 <strong>texture as lookup table, specifically a 1D texture that contains all the values of your function for the possible input range</strong> (i.e. $[0,360)$ / $[0,2\pi)$). This has various advantages:</p>

<ul>
<li>It uses normalized coordinates, i.e. you access the texture by mapping your angles from $[0,360]$ to $[0,1]$. This means your <strong>shader doesn't actually have to care about the specific amount of values</strong>. 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).</li>
<li>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 <strong><code>GL_REPEAT</code> as wrapping mode for the texture and it will automatically start at the beginning again</strong> when accessing with arguments > 1 (and similarly for negative arguments).</li>
<li>And you also get the benefit of <strong>linearly interpolating between two values in the array</strong> basically for free (or let's say almost free) by using <code>GL_LINEAR</code> 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 <em>no</em> interpolation.</li>
<li>You can store <strong>more than one value in the texture</strong> 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.</li>
<li>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 <code>float sin = 2.0 * (texValue.r + texValue.g / 256.0) - 1.0;</code> (or even more components for finer grain). This lets you profit from multi-component textures yet again.</li>
</ul>

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

<p>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 <em>texel center</em>, i.e. a texture coordinate off by half the texel size. So yes, you should <strong>generate values at <code>.5</code> texels</strong>, i.e. something like this in application code:</p>

<pre><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);
</code></pre>

<hr>

<p>You might, however, still want to compare this approach's performance against an approach using a small uniform array (i.e. <code>uniform float sinTable[361]</code>, 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 <code>glUniform1fv</code> and access by adjustig your angle to $[0,360)$ using the <code>mod</code> function and rounding it to the nearest value:</p>

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

CC BY-SA 3.0
2017-10-03T17:14:18.673Z
Here's an interesting extension to storing look up tables in textures. It (ab)uses N-linear texture interpolation to get higher order interpolation (aka better than linear) of data points, surfaces, volumes, and hypervolumes.
https://blog.demofox.org/2016/02/22/gpu-texture-sampler-bezier-curve-evaluation/
Alan Wolfe
CC BY-SA 4.0
2020-06-30T02:59:24.467Z
Would a read only float buffer be faster than a texture?
wduk
CC BY-SA 3.0
2017-10-01T15:16:56.470Z
2018-03-13T10:54:25.730Z
2018-03-13T10:54:25.730Z
Christian Rau
<p>I am trying to get <a href="https://www.shadertoy.com/view/lsKSDt" rel="noreferrer">this shader</a> to run on a really old iDevice, as well as eventually Androids.</p>

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

<p>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.</p>

<p>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.</p>

<p>Anyway I have two dilemmas:</p>

<ol>
<li>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?</li>
<li><p>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.</p>

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

<p>However GLSL does not allow while loops or recursive functions.</p></li>
</ol>

CC BY-SA 3.0
2016-07-02T19:26:16.430Z
I don't know if it would be practical to implement this, but would it help to have the precomputed sine values spaced unevenly, with more closely clustered values where the slope of the sine curve is steepest, and fewer values where it levels out and doesn't change as much? Would this allow higher accuracy where it is needed, without needing to store a large number of values?
trichoplax is on Codidact now
CC BY-SA 3.0
2016-07-03T01:10:42.147Z
Regarding #2, the built-in [`mod` function](http://docs.gl/sl4/mod) is what you want. You would write `mod(angle, 360.0)`.
Nathan Reed
CC BY-SA 3.0
2016-07-03T01:35:25.583Z
@trichoplax brilliant idea but I don't know how you would be able to look up values on the table then. Let's say we put them in an array with some of them more concentrared. How could we find the right index?
J.Doe
CC BY-SA 3.0
2016-07-03T01:35:56.930Z
@racarate actually will probably do that. Feel free to post that as the answer.
J.Doe
CC BY-SA 3.0
2016-07-04T05:31:04.567Z
How about putting your values into a 3-channel 1D texture? That way you can get sin, cos and tan out for the price of a single texture lookup. If you map 0 - 2pi angle to 0 - 1 UV and use repeat texture mode you don't even need the mod call, it will 'wrap' automatically, and you can also get linear-filtered approximations in between your stored values rather than snapping to the nearest one.
russ
CC BY-SA 3.0
2016-07-04T08:27:06.563Z
A lot of the time you can eliminate trig functions when used for geometry by not using the angle but start and end with the sin/cos pair and use trig identities for half angles and such.
ratchet freak
CC BY-SA 3.0
2016-07-06T19:15:29.220Z
Russ has, I think, the best solution here.  If you only need sine, then a single channel half precision will work best, as it'll be important for performance that the 1d texture to fit in cache (less precision than half float will likely not reproduce the sine function very well).
GroverManheim
CC BY-SA 3.0
2016-07-06T20:11:04.593Z
That is brilliant! Though filling up a texture with values would be a new technique for me. I can write to a texture but I can't ensure that I'm writing exact pixels rather then say .5 pixels
J.Doe
CC BY-SA 3.0
2016-07-06T20:11:33.710Z
And I don't quite understand how I would look up values on the texture for a given radian value.
J.Doe
CC BY-SA 3.0
2017-01-03T11:28:56.160Z
When you say "really old iDevice" how old do you mean? For example, [page 8 of the series 6](http://cdn.imgtec.com/sdk-documentation/PowerVR+Series6+Compiler.Instruction+Set+Reference.pdf) gives the "fred" instruction which is meant for trig range reduction (admittedly for Radians), which can then be followed by fsinc. It's been a while since I worked on these (i.e. developing the HW algorithms) but can't imagine there's a much faster way of calculating sine/cosine.
Simon F
CC BY-SA 3.0
2017-01-03T22:22:35.723Z
If sin instruction and texture lookup are expensive, you might also try approximating sin with a polynomial
JarkkoL
<fragment-shader>
Using a LUT to speed up a trig heavy shader for mobile devices
J.Doe
# Using a LUT to speed up a trig heavy shader for mobile devices

(Asked by: username_0 on 2016-07-02)

I am trying to get [this shader](https://www.shadertoy.com/view/lsKSDt) 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:

1. 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?
2. 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 > 360) {temp -= 360;}
      while (temp < 0)   {temp += 360;}
      return temp;
   }

   ```

   However GLSL does not allow while loops or recursive functions.

**Tags:** fragment-shader

> **Comments:**
> **username_1**: I don't know if it would be practical to implement this, but would it help to have the precomputed sine values spaced unevenly, with more closely clustered values where the slope of the sine curve is steepest, and fewer values where it levels out and doesn't change as much? Would this allow higher accuracy where it is needed, without needing to store a large number of values?  
> **username_2**: Regarding #2, the built-in [`mod` function](http://docs.gl/sl4/mod) is what you want. You would write `mod(angle, 360.0)`.  
> **username_0**: @username_1 brilliant idea but I don't know how you would be able to look up values on the table then. Let's say we put them in an array with some of them more concentrared. How could we find the right index?  
> **username_0**: @racarate actually will probably do that. Feel free to post that as the answer.  
> **username_3**: How about putting your values into a 3-channel 1D texture? That way you can get sin, cos and tan out for the price of a single texture lookup. If you map 0 - 2pi angle to 0 - 1 UV and use repeat texture mode you don't even need the mod call, it will 'wrap' automatically, and you can also get linear-filtered approximations in between your stored values rather than snapping to the nearest one.  
> **username_4**: A lot of the time you can eliminate trig functions when used for geometry by not using the angle but start and end with the sin/cos pair and use trig identities for half angles and such.  
> **username_5**: username_3 has, I think, the best solution here.  If you only need sine, then a single channel half precision will work best, as it'll be important for performance that the 1d texture to fit in cache (less precision than half float will likely not reproduce the sine function very well).  
> **username_0**: That is brilliant! Though filling up a texture with values would be a new technique for me. I can write to a texture but I can't ensure that I'm writing exact pixels rather then say .5 pixels  
> **username_0**: And I don't quite understand how I would look up values on the texture for a given radian value.  
> **username_6**: When you say "really old iDevice" how old do you mean? For example, [page 8 of the series 6](http://cdn.imgtec.com/sdk-documentation/PowerVR+Series6+Compiler.Instruction+Set+Reference.pdf) gives the "fred" instruction which is meant for trig range reduction (admittedly for Radians), which can then be followed by fsinc. It's been a while since I worked on these (i.e. developing the HW algorithms) but can't imagine there's a much faster way of calculating sine/cosine.  
> **username_7**: If sin instruction and texture lookup are expensive, you might also try approximating sin with a polynomial  

---

## 1 answers

### Answer by username_8 on 2017-10-01. Score: 9

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 < 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 < 0.0) ? (angle + 360.0) : angle) + 0.5)];

```

> **Comments:**
> **username_9**: Here's an interesting extension to storing look up tables in textures. It (ab)uses N-linear texture interpolation to get higher order interpolation (aka better than linear) of data points, surfaces, volumes, and hypervolumes. https://blog.demofox.org/2016/02/22/gpu-texture-sampler-bezier-curve-evaluation/  
> **username_10**: Would a read only float buffer be faster than a texture?  

---

