24

I'm trying to get this tutorial to work but I ran into two issues, one of which is the following.

When I run the code as is I get an error in the fragment shader saying: THREE.WebGLShader: gl.getShaderInfoLog() ERROR: 0:2: '' : No precision specified for (float). So what I did was specifying a precision for every float/vector I define like so varying highp vec3 vNormal. This eliminates the error but I don't get why? I can't find any other example where precision values are added to variable declarations. Can anybody explain why this occurs? Does it have something to do with my Browser (Chrome 38)?

Flavio
  • 1,507
  • 5
  • 17
  • 30
  • 2
    Does this post bring some light? http://stackoverflow.com/questions/5366416 – vabada Nov 21 '14 at 13:06
  • In answer to the title of your question, the answer is basically "because the spec says so". WebGL is based on the OpenGL ES 2.0 spec and that spec requires you specify precision. – gman Nov 23 '14 at 06:44

2 Answers2

28

There is no default precision in WebGL fragment shaders. (High precision is default for vertex shaders.) The easiest solution is to add

precision highp float;

to all of your fragment shaders, which will eliminate the need to define the precision for all floating point vector variables, but generally,

precision mediump float;

will be preferable, for performance. I do not advise lowp; the good mobile hardware of today doesn't even support it anymore, and does the equivalent of typedeffing lowp to mediump.

  • Cool thanks a lot for the answer, that explains a lot. What I still don't get though is why I never see any of these precision declarations in other shader programs. They aren't even in the [tutorial](http://aerotwist.com/tutorials/an-introduction-to-shaders-part-2/) I tried to get to work. – Flavio Nov 21 '14 at 18:49
  • 2
    You shouldn't use `highp` unless you know you need it. Most phones can't support `highp` so your shader won't run on the phone. Few shaders need `highp` so use `mediump`. As for why you don't see it in the shaders it's because three.js adds it to the shaders you write. Install the [WebGL Inspector](http://benvanik.github.io/WebGL-Inspector/), run a three.js app, click "capture" and "ui" then click "programs" and look at the actual shaders that were given to WebGL by three.js. You'll see they specify precision. – gman Nov 23 '14 at 06:46
  • @gman What POS goes so far as to completely disable a shader instead of running highp as mediump? –  Nov 23 '14 at 13:59
  • 3
    I'm not sure what your point is. If you mean why don't GPUs just lie and use mediump when the user asked for highp? Because that would be massively irresponsible. You don't know they are using it just for graphics. They could be doing medical or finacial calculations. – gman Nov 24 '14 at 01:54
  • 1
    If the precisions actually adhered to a standard, your argument would hold water, but OpenGL ES has only enforced a minimum requirement, which has changed between 2.0 and 3.0. I'm looking now to see what WebGL's requirements are, but can't even find that. I'll go with the assumption that we're to assume only 2.0's minimum precision, but anyone doing not-graphics knows WebGL and OpenGL ES is not suited for their work. Fortunately, there's a mobile GPU language with enforced IEEE compliance now. http://bit.ly/1pe8zqB –  Nov 24 '14 at 07:34
8

Jessy's answer is correct that most fragment shaders set a default precision at the top of the fragment shader code.

However you are using Three.js's RawShaderMaterial which does not prepend any of the built-in uniforms, attributes, and precision declarations. So you have to define it yourself.

On the other hand the tutorial you linked to is using Three.js's ShaderMaterial for its material so Three.js will have the precision declaration prepended automatically.

If you remove the default uniforms/attributes from your shader code and use ShaderMaterial instead it will work without the precision code.

Vertex Shader

varying vec3 vNormal;

void main() {
    vNormal = normal;
    gl_Position = projectionMatrix *
                modelViewMatrix *
                vec4(position,1.0);
}

Fragment Shader

varying vec3 vNormal;

void main() {
    vec3 light = vec3(0.5, 0.2, 1.0);

    // ensure it's normalized
    light = normalize(light);

    // calculate the dot product of
    // the light to the vertex normal
    float dProd = max(0.0, dot(vNormal, light));

    // feed into our frag colour
    gl_FragColor = vec4(dProd, // R
                        dProd, // G
                        dProd, // B
                        1.0);  // A
}

Update to the material

// create the sphere's material
var shaderMaterial = new THREE.ShaderMaterial({
    vertexShader:   document.getElementById('vertex-shader').innerHTML,
    fragmentShader: document.getElementById('fragment-shader').innerHTML
});

Here is a fiddle of your code without the precision declarations.

Anton
  • 12,285
  • 20
  • 64
  • 84
  • 1
    Ooh man I feel pretty silly now having spent several hours on trying to find out what's wrong. I have no idea why I used `RawShaderMaterial()` that wasn't my intention. Thanks so much for your help! – Flavio Nov 21 '14 at 22:16