Hi betelge,

Thank you so much for your response- this is very useful information for me.

I have suspected for a short time that the reason my precision code is not working correctly is because the glsl compiler is optimising away the some of the code, just as you mention; several psychedelic debug frames later reveal that some of my routines leave the low part of the emulated double-float as zero.

I am working on iOS, and I was wondering if you happen to know what #pragma equivalents I should use? If you do, I suspect that this would fix my major problem; amazingly, I can find no information about this for iOS shaders.

I really like your vertex shader splitting trick, very neat! Also, I have highp float at 23 bits of precision in the fragment shader, and I am actually using (1 + 2^(precision+1)) as the split because of the implicit bit in the float (so the split is 1 << 12 + 1)- does this seem reasonable?

Anyway, thanks for such an informative answer, I was starting to get quite frustrated about this and a fresh perspective has really helped me.

Thanks,

Jon

Hi jonsl,

It turns out I’ve had two posts just sitting there as unpublished drafts for two years without realizing it. I’ve published them now. One of them goes through how to do this kind of computations in the vertex shader. Thanks for reminding me.

One way of sending a high precision value to the fragment shader is by passing it to the vertex shader as a highp float uniform, assuming your vertex shader highp precision is high enough. And then in the vertex shader use splitf() and pass those results to the fragment shader as two mediump floats or a mediump vec2.

Another way is by splitting the high precision value on the CPU and passing it as two float uniforms or one vec2 uniform like your code does. You just need a more complicated `set2d()`

function.

This should work:

uniform float split = 1025; // 2^(bits of precision) + 1 vec2 splitf(float a) { float t, hi; t = split * a; hi = t - (t-a); return vec2(hi, a-hi); }

the `split`

constant performs a bit shift by `p`

bits and should be set to 2^`p`

+1. The value in the example, 1025, works for 10 bit mediump floats. And if you are using Nvidia hardware you might need the following lines in your shader code:

#pragma optionNV(fastmath off) #pragma optionNV(fastprecision off)

otherwise the GLSL compiler will simplify the `hi = t - (t-a)`

to `hi = a`

.

You can see the entire source code of the finished app on my GitHub page here.

A very interesting article, and I was very eager to read “how to use the precision of the vertex shader to greatly improve the details” article, but I could not find it..

I have a question: if the fragment shader supports only 16-bit floats, how do I send a ‘double-float’ value to the shader?

I have a function (using cpu 32-bit floats), and then I set the two floats in the shader:

static inline vec2 set2d(double a) {

vec2 b;

b.x = (float)a;

b.y = (float)(a – b.x);

return b;

}

But, obviously b.y = (float)(a – b.x); does not calulate the correct value because bx is interpreted as a 16-bit float in the shader, therefore b.y is wrong. How could I fix this?

Thanks for your great article.

]]>