View matrix not passed to shader when using ShaderMaterial

I’m writing custom shaders and trying to get the matrices into shaders. It works just fine for worldViewProjection but not for view. As far as I can tell, all the values of view are just 0.0.

But according to the Building Shaders With Babylon.js article, I read it that all the uniform matrices would be set by Babylon.js:

BABYLON.ShaderMaterial can also handle the following matrices for you:

  • world,
  • view,
  • projection,
  • worldView,
  • worldViewProjection.

No need for math anymore. For instance, each time you execute sphere.rotation.y += 0.05, the world matrix of the sphere will be generated for you and transmitted to the GPU.

In the following, I post a minimal example of the problematic source code. It only produces the correct result if line #59 is commented-in, i.e., this one:

planeMaterial.setMatrix("view", camera.getViewMatrix());

but if it is commented-out, the plane is just black, which is not what I would expect.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="../style.css">
    <script src="https://cdn.babylonjs.com/babylon.js"></script>
    <script src="https://cdn.babylonjs.com/loaders/babylonjs.loaders.min.js"></script>
    <script src="https://cdn.babylonjs.com/materialsLibrary/babylonjs.materials.min.js"></script>
    <title>Custom Shaders... view not working</title>
  </head>
  <body>
    <canvas id="renderCanvas"></canvas>
    <script type="text/javascript">
      const canvas = document.getElementById('renderCanvas');
      var engine = new BABYLON.Engine(canvas, true, {preserveDrawingBuffer: true, stencil: true});
      var scene = new BABYLON.Scene(engine);
      var camera = addCameraToScene();

      var ground = BABYLON.Mesh.CreateGround('ground1', 10, 10, 100, scene, false);

      BABYLON.Effect.ShadersStore["customVertexShader"] = `
      precision highp float;

      attribute vec3 position;
      uniform mat4 world;
      uniform mat4 view;
      uniform mat4 worldViewProjection;

      varying vec3 vColor;

      void main() {
          vec4 p = vec4(position, 1.);
          gl_Position = worldViewProjection * p;

          vColor = mat3(view) * position.xyz;
      }
      `;
      BABYLON.Effect.ShadersStore["customFragmentShader"] = `
      precision highp float;

      varying vec3 vColor;

      void main() {
          gl_FragColor = vec4(vColor ,1.);
      }
      `;
      const shaderMaterial = new BABYLON.ShaderMaterial("custom", scene, "./custom", {
        attributes: ["position"],
        uniforms: ["view", "worldViewProjection"]
      });
      var planeMaterial = new BABYLON.ShaderMaterial('custom', scene, 'custom', { });

      ground.material = planeMaterial;

      // run the render loop
      engine.runRenderLoop(function(){
          planeMaterial.diffuseColor = new BABYLON.Color3(1, 0, 0, 1);
          // planeMaterial.setMatrix("view", camera.getViewMatrix());
          scene.render();
      });

      // the canvas/window resize event handler
      window.addEventListener('resize', function(){
          engine.resize();
      });

      // ----------------------------------------------------------------------
      // --- vvv -------------------- FUNCTIONS ----------------------- vvv ---

      function addCameraToScene() {
        // Create an arcball camera, let it focus on (0,0,0), position it at a distance of 10, at rotation of PI/3:
        var camera = new BABYLON.ArcRotateCamera('camera1', 0, 1.047, 10, new BABYLON.Vector3(0, 0, 0), scene);
        // Target the camera to scene origin:
        camera.setTarget(BABYLON.Vector3.Zero());
        // Attach the camera to the canvas:
        camera.attachControl(canvas, false);
        camera.lowerBetaLimit = 0.2; // Almost peak
        camera.upperBetaLimit = 1.5; // Almost straight down to xz-plane
        camera.lowerRadiusLimit = 3; // Not tooo close
        camera.minZ = 1.0;
        return camera;
      }
    </script>
  </body>
</html>

You should assign shaderMaterial instead. planeMaterial is not constructed correctly, the array of uniforms is not passed to the constructor, so a default array is used (["worldViewProjection"]), and this array does not contain the view matrix.

2 Likes