2

I am currently developing a 3D engine using WebGL, and I encountered a problem with the lighting shader. I am using an array of structure in order to pass the light parameters to the vertex and fragment shaders (with the same array passed as "uniform"). It actualy works very well with Firefox 35, but not at all with Google chrome and I have no idea what is wrong with this code.

Here is the code for the vertex shader:

precision mediump float;
precision mediump int;

struct light{
  vec3 ambient;
  vec3 specular;
  vec3 diffuse;
  vec3 vector;
};

attribute vec3 aVertexPosition;
attribute vec4 aVertexColor;
attribute vec3 aVertexNormal;

uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
uniform mat4 uNormalMatrix;

varying vec4 vMaterialColor;
varying vec3 N;
varying vec3 V;

const int MAX_LIGHT = 3;
uniform light lights[MAX_LIGHT];
varying vec3 L[MAX_LIGHT];
uniform int lightCount;

void main(void) {
  vec4 position4 = uMVMatrix * vec4(aVertexPosition, 1.0);
  vec3 position = position4.xyz / position4.w;
  V = normalize(-position);

  for(int i = 0 ; i < MAX_LIGHT ; i++){
      if (i < lightCount){
        L[i] = normalize(lights[i].vector - position);
      }
  }

  N = normalize(vec3(uNormalMatrix * vec4(aVertexNormal,0.0)));

  gl_Position = uPMatrix * position4;
  vMaterialColor = aVertexColor;
}

And the code for the fragment shader is:

precision mediump float;
precision mediump int;

varying vec3 N;
varying vec3 V;

struct materialProperties {
   vec3 ambient; 
   vec3 diffuse; 
   vec3 specular;
   vec3 emissive;
   float shininess;
};

struct light{
   vec3 ambient;
   vec3 specular;
   vec3 diffuse;
   vec3 vector;
};

// Material 
varying vec4 vMaterialColor; 
uniform materialProperties uMaterial;

// Light
const int MAX_LIGHT = 3;
uniform light lights[MAX_LIGHT];
varying vec3 L[MAX_LIGHT];
uniform int lightCount;

void main(void) {
    vec3 color = vec3(0.0);
    for(int i = 0; i < MAX_LIGHT ; i++){
        if(i <  lightCount){
            float dot_LN = max(dot(N,L[i]), 0.0);
            float specular = 0.0;

            if(dot_LN > 0.0){
                vec3 H = normalize(L[i]+V);
                float dot_HN = max(dot(H, N), 0.0);
                specular = pow(dot_HN, uMaterial.shininess);
            }
            vec3 ambiantComponent = uMaterial.ambient * lights[i].ambient;
            vec3 diffuseComponent = uMaterial.diffuse * vMaterialColor.xyz * dot_LN * lights[i].diffuse;
            vec3 specularComponent = uMaterial.specular * specular * lights[i].specular;

            color += ambiantComponent + diffuseComponent + specularComponent;
        }
    }
    gl_FragColor = vec4(color, vMaterialColor.w);
  }

I am sending the light informations with gl.uniformxx:

for(var i = 0 ; i < light.length ; i++){
    gl.uniform3fv(shaderProgram.light[i].vector, light[i]._lightVector);
    gl.uniform3fv(shaderProgram.light[i].ambient, light[i].ambient);
    gl.uniform3fv(shaderProgram.light[i].specular, light[i].specular);
    gl.uniform3fv(shaderProgram.light[i].diffuse, light[i].diffuse);
}
gl.uniform1i(shaderProgram.lightCount, light.length);

And shaderProgram is declared with the following code:

shaderProgram.light = [];

for(var i = 0 ; i < 3 ; i++){
    var l = {};

    l.ambient = gl.getUniformLocation(shaderProgram,"lights["+i+"].ambient");
    l.specular = gl.getUniformLocation(shaderProgram,"lights["+i+"].specular");
    l.vector = gl.getUniformLocation(shaderProgram,"lights["+i+"].vector");
    l.diffuse = gl.getUniformLocation(shaderProgram,"lights["+i+"].diffuse");

    shaderProgram.light.push(l);
} 

    shaderProgram.lightCount = gl.getUniformLocation(shaderProgram,"lightCount");

The rendered scene with Google Chrome is a black image, instead of Firefox which renders the scene as expected.

Do you know where is the problem in this code?

Edit: The following minimal code presents the same bug:

<!doctype html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Fail</title>
    </head>

    <script id="shader-fs" type="x-shader/x-fragment">
      precision mediump float;
      precision mediump int;

      struct test_t{
        vec3 a;
        vec3 b;
        vec3 c;
        vec3 d;
      };

      uniform test_t test;
      varying vec3 sum;

    void main(void) {
        gl_FragColor = vec4(test.a+test.b+test.c-sum, 1.0);
    }
    </script>

    <script id="shader-vs" type="x-shader/x-vertex">
      precision mediump float;
      precision mediump int;

      struct test_t{
        vec3 a;
        vec3 b;
        vec3 c;
        vec3 d;
      };

      attribute vec3 aVertexPosition;

      uniform test_t test;
      varying vec3 sum;

      void main(void) {
        sum = test.a + test.b;

        gl_Position = vec4(aVertexPosition,1.0);
      }
    </script>

    <script type="application/javascript">
        var canvas, gl, shaderProgram, triangleVertexPositionBuffer;
        function start() {
            canvas = document.getElementById("webgl-canvas");
            initWebGL(canvas);      // Initialize the GL context

            if (gl) {
                gl.clearColor(0.0, 0.0, 0.0, 1.0);  // Clear to black, fully opaque
                gl.clearDepth(1.0);                 // Clear everything
                gl.enable(gl.DEPTH_TEST);           // Enable depth testing
                gl.depthFunc(gl.LEQUAL);            // Near things obscure far things

                initShaders();

                triangleVertexPositionBuffer = gl.createBuffer();
                gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexPositionBuffer);
                var vertices = [
                    0.0,  0.5,  -0.5,
                    -0.5, -0.5,  -0.5,
                    0.5, -0.5,  -0.5
                ];

                gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
                triangleVertexPositionBuffer.itemSize = 3;
                triangleVertexPositionBuffer.numItems = 3;

                setInterval(drawScene, 15);
            }
        }


        function initWebGL() {
            gl = null;

            try {
                gl = canvas.getContext("experimental-webgl");
            }
            catch(e) {
            }

            if (!gl) 
                alert("Unable to initialize WebGL");
        }

        function initShaders() {
            var fragmentShader = getShader(gl, "shader-fs");
            var vertexShader = getShader(gl, "shader-vs");

            shaderProgram = gl.createProgram();
            gl.attachShader(shaderProgram, vertexShader);
            gl.attachShader(shaderProgram, fragmentShader);
            gl.linkProgram(shaderProgram);

            if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
                alert("Unable to initialize the shader program.");
            }

            gl.useProgram(shaderProgram);

            vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
            gl.enableVertexAttribArray(vertexPositionAttribute);

            shaderProgram.test = {}
            shaderProgram.test.a = gl.getUniformLocation(shaderProgram,"test.a");
            shaderProgram.test.b = gl.getUniformLocation(shaderProgram,"test.b");
            shaderProgram.test.c = gl.getUniformLocation(shaderProgram,"test.c");
            shaderProgram.test.d = gl.getUniformLocation(shaderProgram,"test.d");
        }

        function getShader(gl, id) {
            var shaderScript = document.getElementById(id);
            if (!shaderScript) {
                return null;
            }
            var theSource = "";
            var currentChild = shaderScript.firstChild;
            while(currentChild) {
                if (currentChild.nodeType == 3) {
                    theSource += currentChild.textContent;
                }
                currentChild = currentChild.nextSibling;
            }
            var shader;
            if (shaderScript.type == "x-shader/x-fragment") {
                shader = gl.createShader(gl.FRAGMENT_SHADER);
            } else if (shaderScript.type == "x-shader/x-vertex") {
                shader = gl.createShader(gl.VERTEX_SHADER);
            } else {
                return null;  // Unknown shader type
            }
            gl.shaderSource(shader, theSource);
            gl.compileShader(shader);
            if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
                alert("An error occurred compiling the shaders: " + gl.getShaderInfoLog(shader));
                return null;
            }
            return shader;
        }

        function drawScene() {
            gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

            gl.uniform3fv(shaderProgram.test.a, [1.0,0.0,0.0]);
            gl.uniform3fv(shaderProgram.test.b, [0.0,1.0,0.0]);
            gl.uniform3fv(shaderProgram.test.c, [0.0,0.0,1.0]);
            gl.uniform3fv(shaderProgram.test.d, [1.0,1.0,1.0]);

            gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexPositionBuffer);
            gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, triangleVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
            gl.drawArrays(gl.TRIANGLES, 0, triangleVertexPositionBuffer.numItems);
        }
    </script>

    <body onload="start()">
        <canvas id="webgl-canvas" width="500" height="500"></canvas>
    </body>
</html>

The triangle is yellow/white and blinking on Google Chrome, and blue on Mozilla firefox. The problem seems to be specific to windows platform (tested with windows 8.1 x64).

Edit: The issue has been reported to the chromium team, the fix should be developped soon.

Ender
  • 63
  • 8
  • any errors in the console? – gman Feb 28 '15 at 19:41
  • There is no error in the console (Firefox or Chrome). I also used the [WebGL Inspector](http://bit.ly/17Gm7Dm), and the [calls seem to be correct..](http://pastebin.com/i4kNM526) – Ender Feb 28 '15 at 19:56
  • can you put running code somewhere or embed it? Your shader doesn't seem to reference any textures so it should be easy no? – gman Feb 28 '15 at 20:30
  • You can download the source code [here](http://bit.ly/1LXlUej). And you guess right, textures are not currently supported in the engine. "so it should be easy no": Well, I'm a new WebGL user, so it is not really easy for me to find the solution, but you are probably right. – Ender Feb 28 '15 at 20:41
  • It's not clear what's missing. [Here's what it looks like for me](http://imgur.com/SMNgBnn). Are you running Chrome and Firefox on the same machine? – gman Mar 01 '15 at 00:42
  • Yes I run it on the same machine, [here is the result](http://i.imgur.com/qCAaVtc.png). As you can see you have the correct scene with chrome, it is weird.. I tried some webgl demos on Chrome, and they work like a charm, so webgl is available in the browser. I am using windows 8.1 and the Chome version is 40.0.2214.115 m. – Ender Mar 01 '15 at 09:51
  • Have you tried running it from a server? If you have python 2.x installed cd to your folder and type `python -m SimpleHTTPServer` then go to `http://localhost:8000` or see this http://stackoverflow.com/a/12905427/128511 Another idea, turn off all your chrome extensions, maybe one of them is doing something weird – gman Mar 01 '15 at 10:09
  • I used the [portable version of chrome](http://portableapps.com/apps/internet/google_chrome_portable), but the result is the same. I also tried to run the code on my laptop (W8.1), unsuccessfully. However, the code runs correctly on Archlinux with chromium or firefox. So the problem seems to be specific to windows OS. Edit: I also tried to run it from a server, but it did not change anything. – Ender Mar 01 '15 at 10:53
  • I have edited the question to add a minimal code presenting the problem (bug?). – Ender Mar 01 '15 at 14:02

1 Answers1

3

I tested your minimal code with win7 Chrome and IE11 and win8 Chrome and IE11. And as you said, it is not working with win8 Chrome (but works with win7 Chrome).

I did a few modifications to find out what's wrong. In both vertex and fragment shader I see uniform test_t test has only property a = [1,0,0], but properties b, c, d seems to be [0,0,0], (or not being initialized so whatever values).

There are two tests:

https://www.khronos.org/registry/webgl/sdk/tests/conformance/glsl/misc/shader-with-array-of-structs-uniform.html

https://www.khronos.org/registry/webgl/sdk/tests/conformance/glsl/misc/shader-with-array-of-structs-containing-arrays.html

but both are passing in my chrome. It might be because they don't cover every possible setup and theirs target is vec4 only.

Temporary solution you can do (and is working for me) is to use vec4 instead of vec3. Or wait till the Chrome team fixes it.

Peter O.
  • 32,158
  • 14
  • 82
  • 96
Entity Black
  • 3,401
  • 2
  • 23
  • 38
  • 1
    I modified my code by using vec4 type instead of vec3, and it solves the problem! I will check if the problem is known on the chromium issue tracking website. Nice answer, thank you! (Thank you to gman too) – Ender Mar 01 '15 at 18:37
  • I am experiencing this issue on Windows 7 as well - changing vec3 stored in structures to vec4 fixed it. Thanks! – Mike Weir Mar 29 '15 at 18:04