bind method

  1. @override
void bind(
  1. RenderPass pass,
  2. TransientWriter transientsBuffer,
  3. Lighting lighting
)
override

Binds this material's render-pass state, uniforms, and textures.

The base implementation enables back-face culling with clockwise winding on the Y-down rasterizer (accepting model-space CCW front faces). Subclasses must call super.bind and then bind any per-material uniforms and textures expected by their fragment shader. lighting carries the IBL EnvironmentMap (and its intensity) plus the analytic lights and shadow resources that materials shade against.

Implementation

@override
void bind(
  gpu.RenderPass pass,
  TransientWriter transientsBuffer,
  Lighting lighting,
) {
  pass.setCullMode(renderCullMode);
  pass.setWindingOrder(gpu.WindingOrder.clockwise);
  final shader = fragmentShaderForLighting(lighting);

  if (shadingModel == FmatShadingModel.shadowCatcher) {
    _packEngineFragInfo(lighting, lighting.environmentMap);
    pass.bindUniform(
      shader.cachedUniformSlot('FragInfo'),
      transientsBuffer.emplace(_fragInfoBytes),
    );
    // The catcher's generated fragment samples only the shadow atlas, the
    // occlusion chain, and (in its shadow variant) the punctual textures.
    // The radiance/BRDF/SH samplers and the fog block are compiled out of
    // it, and binding an absent slot fails, so it takes its own bind set.
    EngineLightingUniforms.bindShadowCatcherTextures(pass, shader, lighting);
  } else {
    final lit = shadingModel != FmatShadingModel.unlit;
    final env = environment ?? lighting.environmentMap;
    // An unlit material that declares engine inputs carries the FragInfo
    // block too (the screen-UV mapping, the input gates, and the camera
    // basis its accessors unproject with), but none of the lighting
    // samplers, so it costs no texture unit. The generated shader declares
    // the block only in that case, so this condition must match the
    // emitter's.
    if (lit || _sceneInputs.isNotEmpty) {
      _packEngineFragInfo(lighting, env);
      pass.bindUniform(
        shader.cachedUniformSlot('FragInfo'),
        transientsBuffer.emplace(_fragInfoBytes),
      );
      if (_sceneInputs.isNotEmpty) {
        EngineLightingUniforms.bindSceneInputTextures(
          pass,
          shader,
          lighting,
          _sceneInputs,
        );
      }
    }
    if (lit) {
      // TODO(material-permutations): add an SSAO sampler to filtered scene
      // color permutations without exceeding the backend sampler limit.
      EngineLightingUniforms.bindEngineTextures(
        pass,
        shader,
        lighting,
        env,
        bindSsao:
            _environmentLighting &&
            !_sceneInputs.contains(RenderInput.filteredSceneColor),
        bindEnvironment: _environmentLighting,
        bindShadows: lighting.shadowMap != null,
        bindDiffuseSh: !usesLightmapVariant,
        cubeShader: usesRadianceCubeVariant(lighting),
      );
      if (usesLightmapVariant) {
        bindLightmap(pass, shader, transientsBuffer);
      }
      if (_usesPlanarReflection) {
        _bindPlanarReflection(pass, shader, transientsBuffer, lighting);
      }
      // Lit `.fmat` shaders include the lighting framework (and thus
      // fog.glsl), so they carry the FogInfo block. Unlit `.fmat` shaders do
      // not; fog on those is a TODO(fog): give the unlit `.fmat` template
      // the fog block.
      EngineLightingUniforms.bindFog(
        pass,
        shader,
        transientsBuffer,
        lighting,
      );
    }
  }
  // Every shading model's Surface() and debug hook may read
  // GetViewDirection.
  EngineLightingUniforms.bindViewInfo(
    pass,
    shader,
    transientsBuffer,
    lighting,
  );

  parameters.bind(pass, shader, transientsBuffer);
  // Bind the fragment keep-alive block (name matches kFragmentKeepAliveBlock
  // in the emitter) to zero. The generated fragment references every
  // declared resource through it (the MaterialParams block, any unreferenced
  // sampler, and the engine scene inputs) so none can be optimized out; the
  // emitter declares it whenever the material has a parameter or an engine
  // input, so this condition must match. `planar_reflection` is an engine
  // input to the emitter but not a scene input here, so it is named
  // separately.
  if (parameters.hasAnyParameters ||
      _sceneInputs.isNotEmpty ||
      _usesPlanarReflection) {
    pass.bindUniform(
      shader.cachedUniformSlot('FragmentKeepAlive'),
      transientsBuffer.emplace(_zeroKeepAlive),
    );
  }
}