webgl 光照着色器适用于 firefox 但不适用于 chrome

webgl lighting shader working with firefox but not with chrome

我目前正在使用 WebGL 开发 3D 引擎,我遇到了光照着色器的问题。我正在使用一个结构数组,以便将光参数传递给顶点和片段着色器(使用与 "uniform" 相同的数组传递)。它实际上在 Firefox 35 上运行得很好,但在 Google chrome 上就完全不行了,我不知道这段代码有什么问题。

这是顶点着色器的代码:

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;
}

片段着色器的代码是:

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);
  }

我正在发送灯光信息 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);

并且使用以下代码声明 shaderProgram:

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");

Google Chrome 渲染的场景是黑色图像,而不是 Firefox 按预期渲染场景。

你知道这段代码的问题在哪里吗?

编辑: 以下最小代码呈现相同的错误:

<!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>

三角形为 yellow/white,在 Google Chrome 上闪烁,在 Mozilla firefox 上闪烁蓝色。该问题似乎特定于 windows 平台(使用 windows 8.1 x64 测试)。

编辑: 问题已 reported 提交给 chromium 团队,应该会尽快修复。

我用 win7 Chrome 和 IE11 以及 win8 Chrome 和 IE11 测试了你的最小代码。正如您所说,它不适用于 win8 Chrome(但适用于 win7 Chrome)。

我做了一些修改以找出问题所在。在顶点着色器和片段着色器中,我看到 uniform test_t test 只有 属性 a = [1,0,0],但是属性 b, c, d 似乎是 [0,0,0],(或者没有被初始化所以任何值).

有两个测试:

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

但两者都在传递我的 chrome。这可能是因为他们没有涵盖所有可能的设置,而且他们的目标只是 vec4。

您可以做的临时解决方案(并且对我有效)是使用 vec4 而不是 vec3。或者等到 Chrome 团队修复它。