assignFrom (new prop) adds declarative post-process + scene image processing

There is a new property “assignFrom” that adds a lot of new potential especially for binding to 3D model materials. The assignFrom property (opposite of “assignTo”, which I put in first example on purpose) was added recently for material configurations, so that the “clearCoat” or other configurations could be recognized and updated by the reconciler:

<box name="plane" width={65} height={1} depth={65}>
  <pbrMaterial name="wood" reflectionTexture={cubeTexture} environmentIntensity={1} specularIntensity={.3}
    albedoColor={Color3.White()} useMicroSurfaceFromReflectivityMapAlpha
  >
    <texture url="assets/textures/reflectivity.png" assignTo="reflectivityTexture" />
    <texture url="assets/textures/albedo.png" assignTo="albedoTexture" />
    <pbrClearCoatConfiguration assignFrom='clearCoat' isEnabled roughness={roughness} />
  </pbrMaterial>
</box>

So, that lets you set the roughness of a PBR material clear coat as a prop. There was a recent request to add declarative Post Processing. The post process also use configurations and there is image processing and render post processing. One example of image processing is colour grading on a scene - I needed to make a couple of small changes to allow assignFrom to be able to assign from the scene (as it was the parent component, but not part of renderer). Anyway, the JSX is this:

// https://playground.babylonjs.com/#17VHYI#15
export const ColorGrading = () => {
  const [colorGradingTexture, setColorGradingTexture] = useState(null);
  const colorGradingTextureRef = useCallback(cg => {
    setColorGradingTexture(cg);
  }, []);

  useEffect(() => {
    if (colorGradingTexture !== null) {
      let i = 0;
      const handle = setInterval(() => {
        if (colorGradingTexture !== null) {
          colorGradingTexture.level = Math.sin(i++ / 120) * 0.5 + 0.5;
        }
      }, 10);
      return (() => {
        clearInterval(handle);
      })
    }
  }, [colorGradingTexture]);

  return (
    <div>
      <Engine antialias adaptToDeviceRatio canvasId='babylonJS' >
        <Scene>
          <imageProcessingConfiguration assignFrom='imageProcessingConfiguration' colorGradingEnabled={colorGradingTexture !== null} colorGradingTexture={colorGradingTexture} />
          <colorGradingTexture ref={colorGradingTextureRef} url='assets/textures/LateSunset.3dl' />
          <arcRotateCamera name='Camera1' radius={10} alpha={Math.PI / 2} beta={(Math.PI / 2)} target={Vector3.Zero()} />
          <pointLight name='omni0' position={new Vector3(-17.6, 18.8, -49.9)} />
          <SkyboxAndSphere />
        </Scene>
      </Engine>
    </div>
  )
}

The default rendering pipeline works similar, but you assign the PostProcess directly and can be dropped anywhere in the component hierarchy:

<defaultRenderingPipeline hdr chromaticAberrationEnabled grainEnabled>
  <chromaticAberrationPostProcess assignFrom='chromaticAberration' aberrationAmount={-100} radialIntensity={0.2} />
  <grainPostProcess assignFrom='grain' intensity={15} />
</defaultRenderingPipeline>

The underlying reason for creating “assignFrom” is that you can’t do in react object.property={value}. Some libraries get around that by replacing - kebab-case with dots at runtime (I do that with Vector3’s and x,y,z), but actually you lose the strong typing benefits and explode the allowed properties, so it’s actually beneficial and easier to follow this structure. So, it’s not really a “law of demeter” thing that makes it a benefit, but more the strong typing (on JSX intrinsic elements) and editor help as well when you are coding.

There is another cool undocumented feature added recently for dynamically adding custom libraries - I got @babylonjs/materials working declaratively by adding a runtime registration for the reconciler, if anybody is interested can explain more about that. I may end up using it to lean down the primary library and make GUI an addon.

3 Likes

You are unstoppable ;D