function setUniforms(setters) {
  // eslint-disable-line
  var actualSetters = setters.uniformSetters || setters;
  var numArgs = arguments.length <= 1 ? 0 : arguments.length - 1;
  for (var aNdx = 0; aNdx < numArgs; ++aNdx) {
    var values = aNdx + 1 < 1 || arguments.length <= aNdx + 1 ? undefined : arguments[aNdx + 1];
    if (Array.isArray(values)) {
      var numValues = values.length;
      for (var ii = 0; ii < numValues; ++ii) {
        setUniforms(actualSetters, values[ii]);
      }
    } else {
      for (var name in values) {
        var setter = actualSetters[name];
        if (setter) {
          setter(values[name]);
        }
      }
    }
  }
}
Set uniforms and binds related textures.

example:

    const programInfo = createProgramInfo(
        gl, ["some-vs", "some-fs"]);

    const tex1 = gl.createTexture();
    const tex2 = gl.createTexture();

    ... assume we setup the textures with data ...

    const uniforms = {
      u_someSampler: tex1,
      u_someOtherSampler: tex2,
      u_someColor: [1,0,0,1],
      u_somePosition: [0,1,1],
      u_someMatrix: [
        1,0,0,0,
        0,1,0,0,
        0,0,1,0,
        0,0,0,0,
      ],
    };

    gl.useProgram(programInfo.program);

This will automatically bind the textures AND set the
uniforms.

    twgl.setUniforms(programInfo, uniforms);

For the example above it is equivalent to

    let texUnit = 0;
    gl.activeTexture(gl.TEXTURE0 + texUnit);
    gl.bindTexture(gl.TEXTURE_2D, tex1);
    gl.uniform1i(u_someSamplerLocation, texUnit++);
    gl.activeTexture(gl.TEXTURE0 + texUnit);
    gl.bindTexture(gl.TEXTURE_2D, tex2);
    gl.uniform1i(u_someSamplerLocation, texUnit++);
    gl.uniform4fv(u_someColorLocation, [1, 0, 0, 1]);
    gl.uniform3fv(u_somePositionLocation, [0, 1, 1]);
    gl.uniformMatrix4fv(u_someMatrix, false, [
        1,0,0,0,
        0,1,0,0,
        0,0,1,0,
        0,0,0,0,
      ]);

Note it is perfectly reasonable to call `setUniforms` multiple times. For example

    const uniforms = {
      u_someSampler: tex1,
      u_someOtherSampler: tex2,
    };

    const moreUniforms {
      u_someColor: [1,0,0,1],
      u_somePosition: [0,1,1],
      u_someMatrix: [
        1,0,0,0,
        0,1,0,0,
        0,0,1,0,
        0,0,0,0,
      ],
    };

    twgl.setUniforms(programInfo, uniforms);
    twgl.setUniforms(programInfo, moreUniforms);

You can also add WebGLSamplers to uniform samplers as in

    const uniforms = {
      u_someSampler: {
        texture: someWebGLTexture,
        sampler: someWebGLSampler,
      },
    };

In which case both the sampler and texture will be bound to the
same unit.

@param {(module:twgl.ProgramInfo|Object.<string, function>)} setters a `ProgramInfo` as returned from `createProgramInfo` or the setters returned from
       `createUniformSetters`.
@param {Object.<string, ?>} values an object with values for the
       uniforms.
  You can pass multiple objects by putting them in an array or by calling with more arguments.For example

    const sharedUniforms = {
      u_fogNear: 10,
      u_projection: ...
      ...
    };

    const localUniforms = {
      u_world: ...
      u_diffuseColor: ...
    };

    twgl.setUniforms(programInfo, sharedUniforms, localUniforms);

    // is the same as

    twgl.setUniforms(programInfo, [sharedUniforms, localUniforms]);

    // is the same as

    twgl.setUniforms(programInfo, sharedUniforms);
    twgl.setUniforms(programInfo, localUniforms};

  You can also fill out structure and array values either via
  shortcut. Example

    // -- in shader --
    struct Light {
      float intensity;
      vec4 color;
      float nearFar[2];
    };
    uniform Light lights[2];

    // in JavaScript

    twgl.setUniforms(programInfo, {
      lights: [
        { intensity: 5.0, color: [1, 0, 0, 1], nearFar[0.1, 10] },
        { intensity: 2.0, color: [0, 0, 1, 1], nearFar[0.2, 15] },
      ],
    });

  or the more traditional way

    twgl.setUniforms(programInfo, {
      "lights[0].intensity": 5.0,
      "lights[0].color": [1, 0, 0, 1],
      "lights[0].nearFar": [0.1, 10],
      "lights[1].intensity": 2.0,
      "lights[1].color": [0, 0, 1, 1],
      "lights[1].nearFar": [0.2, 15],
    });

  You can also specify partial paths

    twgl.setUniforms(programInfo, {
      'lights[1]': { intensity: 5.0, color: [1, 0, 0, 1], nearFar[0.2, 15] },
    });

  But you can not specify leaf array indices

    twgl.setUniforms(programInfo, {
      'lights[1].nearFar[1]': 15,     // BAD! nearFar is a leaf
      'lights[1].nearFar': [0.2, 15], // GOOD
    });

@memberOf module:twgl/programs