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 counter-clockwise winding (matching the glTF convention). 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,
) {
  super.bind(pass, transientsBuffer, lighting);

  final EnvironmentMap env = environment ?? lighting.environmentMap;

  // FragInfo std140 layout (624 bytes / 156 floats). EngineLightingUniforms
  // packs the shared engine lighting, image-based-lighting, and shadow
  // fields (identical for every lit material); this material fills only its
  // own, disjoint fields:
  //   [0..3]    vec4  color
  //   [4..7]    vec4  emissive_factor
  //   [120]     float vertex_color_weight
  //   [121]     float metallic_factor
  //   [122]     float roughness_factor
  //   [123]     float has_normal_map
  //   [124]     float normal_scale
  //   [125]     float occlusion_strength
  //   [132]     float alpha_mode (0 opaque, 1 mask, 2 blend)
  //   [133]     float alpha_cutoff
  //   [138]     float specular_aa_variance
  //   [139]     float specular_aa_threshold
  // A shared scratch (zeroed each bind, matching a fresh allocation's
  // unwritten slots) instead of a per-draw allocation; emplace below copies
  // the bytes out immediately.
  final fragInfo = _fragInfoScratch..fillRange(0, _fragInfoScratch.length, 0);
  EngineLightingUniforms.packInto(fragInfo, lighting, env);
  fragInfo[0] = baseColorFactor.r;
  fragInfo[1] = baseColorFactor.g;
  fragInfo[2] = baseColorFactor.b;
  fragInfo[3] = baseColorFactor.a;
  fragInfo[4] = emissiveFactor.r;
  fragInfo[5] = emissiveFactor.g;
  fragInfo[6] = emissiveFactor.b;
  fragInfo[7] = emissiveFactor.a;
  fragInfo[120] = vertexColorWeight;
  fragInfo[121] = metallicFactor;
  fragInfo[122] = roughnessFactor;
  fragInfo[123] = normalTexture != null ? 1.0 : 0.0;
  fragInfo[124] = normalScale;
  fragInfo[125] = occlusionStrength;
  fragInfo[132] = alphaMode.index.toDouble();
  fragInfo[133] = alphaCutoff;
  fragInfo[138] = specularAntiAliasingVariance;
  fragInfo[139] = specularAntiAliasingThreshold;
  fragInfo[EngineLightingUniforms.fadeIndex] = lodFade;
  // radiance_blend.zw [162]/[163]: this item's punctual-light slice
  // (count, offset) into the per-frame light-index buffer.
  fragInfo[162] = lightListCount.toDouble();
  fragInfo[163] = lightListOffset.toDouble();
  pass.bindUniform(
    fragmentShader.getUniformSlot("FragInfo"),
    transientsBuffer.emplace(ByteData.sublistView(fragInfo)),
  );

  _bindSlot(pass, 'base_color_texture', baseColorTexture);
  _bindSlot(pass, 'emissive_texture', emissiveTexture);
  _bindSlot(pass, 'metallic_roughness_texture', metallicRoughnessTexture);
  _bindSlot(pass, 'normal_texture', normalTexture, normal: true);
  _bindSlot(pass, 'occlusion_texture', occlusionTexture);
  // Image-based-lighting atlas, BRDF LUT, and shadow map. Shared with
  // PreprocessedMaterial: the sampler choices (radiance repeat/clamp, LUT
  // clamp/clamp, shadow bilinear/clamp) and the white shadow placeholder
  // live in EngineLightingUniforms.
  EngineLightingUniforms.bindEngineTextures(
    pass,
    fragmentShader,
    lighting,
    env,
  );
  EngineLightingUniforms.bindFog(
    pass,
    fragmentShader,
    transientsBuffer,
    lighting,
  );
}