THREE.WebGLShader "Error: To many uniforms" when creating a lot of Point Lights

THREE.WebGLShader "Error: To many uniforms" when creating a lot of Point Lights

背景

我正在尝试制作一圈光,正如您在下面的函数中看到的那样

function createLightRing(radius, options) {

  let nopIsDefault = false;
  
  ...

但是当我 运行 它得到这个错误

THREE.WebGLProgram: shader error:  0 35715 false gl.getProgramInfoLog invalid shaders  THREE.WebGLShader: gl.getShaderInfoLog() fragment
ERROR: too many uniforms
1: #version 300 es
2: #define varying in
3: out highp vec4 pc_fragColor;
4: #define gl_FragColor pc_fragColor
5: #define gl_FragDepthEXT gl_FragDepth
6: #define texture2D texture
7: #define textureCube texture
...

我只是想做个戒指:(

我试过的

一些代码!!!

我的代码已在 repl.it, so check it out here

上线

我的函数:

function createLightRing(radius, options) {

  let nopIsDefault = false;

  let lights = [];

  const defaultOptions = {
    start: 0,
    end: 360,
    copyLights: [],
    color: 0xffffff, // White
    lightType: "PointLight",
    intensity: 1,
    copyLightMap: "loop", // loops the arrays from colors and other values like it
    rotation: new p5.Vector(0, 0, 0),
    target: null,
    quality: 6,
    numberOfPoints: null,
    override: false
  }

  // applys the default to options
  options = {...defaultOptions, ...options}

  const copyLightMap = options.copyLightMap.toString().toLowerCase();
  
  // runs only if "numberOfPoints" exists
  if (!options.numberOfPoints) {
    options.numberOfPoints = (options.end - options.start) * options.quality

    nopIsDefault = true;
  }


  // Mode 1 (soft / soft-(force / end) / 1, forces the loop to end when it finishes lopping thought "copyLights", but makes sure to finnish the loop. TL;DR: Sets "numberOfPoints" to "copyLights"'s length)
  if (copyLightMap == '1' || copyLightMap == 'soft' || copyLightMap == 'soft-end' || copyLightMap == 'soft-force') {  
    options.numberOfPoints = options.copyLights.length

    nopIsDefault = true;
  }

  // updates "quality" to the right value
  options.quality = nopIsDefault ? options.quality : options.numberOfPoints / (options.end - options.start)

  for (let i = options.start; i <= options.end; i += 1 / options.quality) {
        
    let light;
    let dontCheck = false;
    let realI = (1 / options.quality) * i;


    const x = radius * cos(i);
    const y = radius * sin(i);


    // try to create a light with the spesified type
    try {
      light = new THREE[options.lightType]();
    }
    // if it fails, just use the defult
    catch(err) {
      light = new THREE[defaultOptions.lightType]();
    }

    light = setLightProprties(light, options);     // inits the light proprties

    // only runs the switch if both "copyLights" has at lest one element and it can run in the first place
    if (options.copyLights[0] && dontCheck) {

      const copyLightMap = options.copyLightMap.toString().toLowerCase();

      switch (copyLightMap) {
        
        // Mode 0 (force / end / 0, forces the loop to end when done)
        case 'force':
        case 'end':
        case '0':
          if (realI < circleLoop) {
            dontCheck = true;
            break;
          }

          light = options.copyLights[realI];
          break;
        
        // Mode 2 (loop / 2, go back to beggining copyLights when there are no more values left in it)
        case 'loop':
        case '2':
          light = options.copyLights[realI % options.copyLights.lenght];
          break;
      }
    
    }

    if (options.override) {

      light = setLightProprties(light, options);   // updates light's proprties

    }

    light.position.set(x, y, 0);   // sets the light's position

    lights.push(light);   // adds the new light to the array
  }

  return lights;
}

其他的都在我的 repl.it 里,所以去看看吧!

答案在你的错误中。对于您正在使用的browser/platform,您的着色器中的制服太多了。

打开浏览器:https://webglreport.com/

Vertex ShaderFragment Shader下你会看到一个“Max _____ Uniform Vectors:”。这是制服尺寸的硬性限制,根据您的 browser/GPU,您会在此处看到不同的值。这些值由 GL 实现控制,可以通过 GL 常量访问。这是一个 运行 可用的片段,展示了如何获得这些值。

const canvas1 = document.createElement('canvas')
const gl1 = canvas1.getContext('webgl')
if (gl1) {
  document.getElementById('wg1-mvuv').innerText = gl1.getParameter(gl1.MAX_VERTEX_UNIFORM_VECTORS)
  document.getElementById('wg1-mfuv').innerText = gl1.getParameter(gl1.MAX_FRAGMENT_UNIFORM_VECTORS)
}

const canvas2 = document.createElement('canvas')
const gl2 = canvas2.getContext('webgl2')
if (gl2) {
  document.getElementById('wg2-mvuv').innerText = gl2.getParameter(gl2.MAX_VERTEX_UNIFORM_VECTORS)
  document.getElementById('wg2-mfuv').innerText = gl2.getParameter(gl2.MAX_FRAGMENT_UNIFORM_VECTORS)
}
html * {
  font-family: monospace;
}

td {
  border: 1px black solid;
}
<table>
  <caption>WebGL</caption>
  <tr>
    <td>GL_MAX_VERTEX_UNIFORM_VECTORS</td>
    <td id="wg1-mvuv">UNSUPPORTED</td>
  </tr>
  <tr>
    <td>GL_MAX_FRAGMENT_UNIFORM_VECTORS</td>
    <td id="wg1-mfuv">UNSUPPORTED</td>
  </tr>
</table>

<table>
  <caption>WebGL 2</caption>
  <tr>
    <td>GL_MAX_VERTEX_UNIFORM_VECTORS</td>
    <td id="wg2-mvuv">UNSUPPORTED</td>
  </tr>
  <tr>
    <td>GL_MAX_FRAGMENT_UNIFORM_VECTORS</td>
    <td id="wg2-mfuv">UNSUPPORTED</td>
  </tr>
</table>

您是否将任何大型数组传递给制服,或者您的(最终)着色器代码是否有数百个制服? (有 WebGL 检查器浏览器插件可以帮助您查看最终的着色器代码。)创建着色器的功能尽可能最少是一个很好的经验法则,然后在内存允许的情况下添加。

就个人而言,我在内存更稀缺的移动平台上看到最多。