How to use SSR in Needle Engine?

Hi, I’m currently using SSRPass in Three.js. As far as I understand, the Needle Engine uses the pmndrs postprocessing package. However, this doesn’t support SSR. Is there perhaps an example of how I can integrate this into my Needle Engine project with vanilla Three.js:

this.ssrPass = new SSRPass({
...
});

this.composer.addPass(this.ssrPass);

Thanks…

Hi Armin,

To implement Screen Space Reflections (SSR) in Needle Engine, you’re right that the engine currently doesn’t support SSRPass directly via the pmndrs postprocessing package. However, you can integrate it manually in your project using vanilla Three.js.

Here’s a step-by-step approach to incorporating SSRPass into your Needle Engine project:

  1. Create a Shader Material: You will need to create a custom shader material for SSR.

  2. Initialize the SSRPass: Use the SSRPass from Three.js.

  3. Add it to your Render Loop: Add the pass to your rendering pipeline.

Here’s an example code snippet:

import { SSRPass } from 'three/examples/jsm/postprocessing/SSRPass';
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer';
import { WebGLRenderTarget } from 'three';

// Setup the composer
this.renderTarget = new WebGLRenderTarget(window.innerWidth, window.innerHeight);
this.composer = new EffectComposer(this.renderer, this.renderTarget);

// Instantiate SSRPass
this.ssrPass = new SSRPass({
    scene: this.scene,
    camera: this.camera,
    width: window.innerWidth,
    height: window.innerHeight,
});

// Add the SSRPass to the composer
this.composer.addPass(this.ssrPass);

// Add any other passes (like final pass)

In your render loop, you would call:

this.composer.render();

You may also want to adjust the SSRPass parameters according to your scene’s needs.

For details on the SSRPass API, you can refer to the Three.js SSR documentation.

Make sure to integrate this setup in a way that aligns with how Needle Engine manages its rendering pipeline. If you run into specific issues while implementing, providing error messages or additional context can help narrow down the problem.

Feel free to ask if you have further questions or need more clarification!

1 replies left - Get a PRO license for more AI replies.
thread_2ZRMgWkmFUxnXMjOlvwstwVH
Reply to this message to continue the conversation

Hi, I think that’s related to this issue here: Implement SSREffect Ā· Issue #571 Ā· pmndrs/postprocessing Ā· GitHub

For Needle Engine: Instead of using the postprocessing effects that Needle Engine uses you can also create the effect composer yourself (like you’re doing in vanilla three.js) and just set it on the context e.g. from one of your components. Just don’t use the Volume component in Unity then.

Here’s a tiny example:

export class MyPostProcessing extends Behaviour {

  onEnable() {
     const composer = new EffectComposer(...);
     // add your effect passes
     this.context.composer = composer
   }
}

Edit: I’m currently preparing an example and noticed that there’s a regression - will provide a fix in the next update

Edit2: Fixed in latest beta that was just released. Below is a bit of code that you can copy paste in your main.ts OR create a component in src/scripts and manage it from Unity. The code is basically how the SSR example of three.js works

import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
import { SSRPass } from 'three/examples/jsm/postprocessing/SSRPass.js';
import { OutputPass } from 'three/examples/jsm/postprocessing/OutputPass.js';
import { PlaneGeometry } from "three";
import * as THREE from "three";
import { onStart } from '@needle-tools/engine';

onStart((context) => {
    const { scene, renderer } = context;
    const camera = context.mainCamera;

    const selects = new Array();

    let geometry, material, mesh;
    geometry = new PlaneGeometry(1, 1);

    const plane = new THREE.Mesh(
        new THREE.PlaneGeometry(1, 1),
        new THREE.MeshPhongMaterial({ color: 0xcbcbcb })
    );
    plane.rotation.x = - Math.PI / 2;
    plane.position.y = - 0.0001;
    // plane.receiveShadow = true;
    scene.add(plane);


    geometry = new THREE.BoxGeometry(.05, .05, .05);
    material = new THREE.MeshStandardMaterial({ color: 'green' });
    mesh = new THREE.Mesh(geometry, material);
    mesh.position.set(- .12, .025, .015);
    scene.add(mesh);
    selects.push(mesh);

    geometry = new THREE.IcosahedronGeometry(.025, 4);
    material = new THREE.MeshStandardMaterial({ color: 'cyan' });
    mesh = new THREE.Mesh(geometry, material);
    mesh.position.set(- .05, .025, .08);
    scene.add(mesh);
    selects.push(mesh);

    geometry = new THREE.ConeGeometry(.025, .05, 64);
    material = new THREE.MeshStandardMaterial({ color: 'yellow' });
    mesh = new THREE.Mesh(geometry, material);
    mesh.position.set(- .05, .025, - .055);
    scene.add(mesh);
    selects.push(mesh);

    const composer = new EffectComposer(renderer);
    composer.setSize(window.innerWidth, window.innerHeight);
    const ssrPass = new SSRPass({
        renderer,
        scene,
        camera,
        width: innerWidth,
        height: innerHeight,
        groundReflector: null, //groundReflector,
        selects: null, //selects,
    });

    composer.addPass(ssrPass);
    composer.addPass(new OutputPass());
    context.composer = composer;
});

Hello Armin,

I’ve added an example here: Needle Engine: Three.js SSR - StackBlitz

Thanks marwie1! I checked ssrPass + ReflectorForSSRPass, ssaoPass and fxaaPass. Seems to work, but not OutputPass (will create new Topic) and I get a Typescript Error at

this.console.composer = composer;

Type ā€˜EffectComposer’ is missing the following properties from type ā€˜EffectComposer’: inputBuffer, outputBuffer, autoRenderToScreen, multisampling, and 8 more.ts(2740)
(property) Context.composer: EffectComposer | null

That sounds like you’re not using the right engine version. Which version are you on right now? Please make sure you’re on 4.3.2 Releases & Changelogs - #100 by needle-bot

I have installed Blender addon 0.52.5 with NE 3.51.4 und Node 22.11.0. On Releases & Changelogs it seems the most recent version for blender. How can I get the NE 4.3.2 for Blender, update via npm?

Will the Blender plugin support the Needle Engine 4.x in the future? Thanks…

Yes, the Blender plugin should be updated to Needle Engine 4 in the next weeks, but we don’t have a concrete ETA right now. Thanks for your patience!

Sorry for the bump on this thread, I tried this just now and I get this error at `composer.addPass(ssrPass):`

```
Argument of type ā€˜SSRPass’ is not assignable to parameter of type ā€˜Pass & Pass’.
Type ā€˜SSRPass’ is missing the following properties from type ā€˜Pass’: name, needsDepthTexture, mainScene, mainCamera, and 9 more.
```

I think you need to share a bit more code and your import

import {
  EffectComposer,
  Pass,
} from "three/examples/jsm/postprocessing/EffectComposer.js";
import { SSRPass } from "three/examples/jsm/postprocessing/SSRPass.js";
import { OutputPass } from "three/examples/jsm/postprocessing/OutputPass.js";
import * as THREE from "three";
import { Behaviour, NeedleEngine } from "@needle-tools/engine";

export class SsrEffect extends Behaviour {
  hasRanOnce: Boolean = false;

  start() {
    const needleEngine = document.querySelector("needle-engine");
    //@ts-ignore
    needleEngine.addEventListener("loadfinished", () => {
      // wait a moment
      setTimeout(() => {
        console.log("mynamejeff");
      }, 1000);
    });
  }

  update() {
    if (this.hasRanOnce == false) {
      console.log("checking for composer");
      if (this.context.composer != null) {
        console.log("found composer");
        this.hasRanOnce = true;
        this.setSsrEnabled();
      }
    }
  }

  setSsrEnabled() {
    console.log("SSR Effect Start");
    var { scene, renderer } = this.context;
    var camera = this.context.mainCamera;

    console.log("context: ", this.context);
    var composer = this.context.composer;
    console.log("SSR Effect Composer", composer);
    //composer.setSize(window.innerWidth, window.innerHeight);
    var ssrPass = new SSRPass({
      renderer,
      scene,
      camera,
      width: innerWidth,
      height: innerHeight,
      groundReflector: null, //groundReflector,
      selects: null, //selects,
    });

    composer.addPass(ssrPass);
    composer.addPass(new OutputPass());
    //this.context.composer = composer;
    console.log("SSR Effect initialized");
  }
}

Here is the full code for what I am trying to do for now, see in setSsrEnabled() I am trying to add the pass there.

In the scene I have a Global Volume which has the postprocessing stack on it which I assume is the created EffectComposer I am trying to add SSR to

Ah ok the issue is that you’re trying to use the EffectComposer from three.js with the EffectComposer from pmndrs/postprocessing which is the postprocessing library we’re using under the hood. So the error is because the Pass you’re importing is a different type (also the SSRPass that comes from the three examples).

You’ll notice that the example i shared above is setting the composer on the context (context.composer = threeComposer) which is then respected by Needle Engine and you can use whatever effectcomposer you want. But it’s not simply possible (i believe) to mix and match totally different passes with the postprocessing library we’re using by default under the hood.

However there are some other libraries that add SSR for pmndrs/postprocessing. Maybe try using https://www.npmjs.com/package/screen-space-reflections which looks pretty nice and has many options to configure

1 Like

Hello @Rob_F , I’ve just briefly tried to use the three.js SSR pass with the other postprocessing package and this is what I came up with so far. Just wanted to share it with you in case it helps you but I havent tested all permutations or cases but perhaps is good enough for testing or as a startingpoint for you.


import { Antialiasing, onStart, PostProcessingManager, Renderer } from "@needle-tools/engine"
import { SSRPass } from "three/examples/jsm/postprocessing/SSRPass.js";


onStart(ctx => {

    const scene = ctx.scene;
    const post = scene.addComponent(PostProcessingManager);

    const ssrPass = new SSRPass({
        renderer: ctx.renderer,
        scene,
        camera: ctx.mainCamera,
        width: innerWidth,
        height: innerHeight,
        groundReflector: null,
        selects: null,// ctx.scene.getComponentsInChildren(Renderer).map(e => e.gameObject),
    });
    ssrPass.maxDistance = .5;

    const _ssrPass = ssrPass as any;
    _ssrPass.order = 10;
    _ssrPass.setRenderer = () => { }
    _ssrPass.initialize = () => { }
    _ssrPass.setDepthTexture = () => { }
    // ssrPass.selective = true;
    // ssrPass.output = 7;
    const originalRender = ssrPass.render;
    ssrPass.render = (...args) => {
        const renderer = args[0];
        const input = args[1];
        const output = args[2];
        originalRender.call(ssrPass, renderer, output, output);
    }
    post.addEffect(ssrPass);



    post.addEffect(new Antialiasing())
});

Thanks so much, here is my updated script and I can hopefully share a nice sample of a product viewer I made showing the effects all together when it is ready!

import {

Antialiasing,

onStart,

PostProcessingManager,

Renderer,

} from ā€œ@needle-toolsneedle-toolsneedle-toolsneedle-toolsneedle-toolsneedle-toolsneedle-toolsneedle-tools/engineā€;

import { SSRPass } from ā€œthree/examples/jsm/postprocessing/SSRPass.jsā€;

import { Behavi@needle-toolsur, GameObj@needle-toolsneedle-toolsct } from ā€œ@needle-tools/engineā€;

export class SsrEffect extends Behaviour {

hasRanOnce: Boolean = false;

start() {

const needleEngine = do@ts-ignoreument.querySel@ts-ignorect@ts-ignorer(ā€œneedle-engineā€);

//@ts-ignore

needleEngine.addEventListener(ā€œloadfinishedā€, () => {

// wait a moment

setTimeout(() => {

console.log(ā€œmynamejeffā€);

}, 1000);

});

}

update() {

if (this.hasRanOnce == false) {

console.log(ā€œchecking for composerā€);

if (this.context.composer != null) {

console.log(ā€œfound composerā€);

this.hasRanOnce = true;

this.setSsrEnabled();

}

}

}

setSsrEnabled() {

console.log(ā€œSSR Effect Startā€);

const scene = this.context.scene;

const post = GameObject.findObjectOfType(

PostProcessingManager,

this.context

); //scene.addComponent(PostProcessingManager);

const ssrPass = new SSRPass({

renderer: this.context.renderer,

scene,

camera: this.context.mainCamera,

width: innerWidth,

height: innerHeight,

groundReflector: null,

selects: null, // ctx.scene.getComponentsInChildren(Renderer).map(e => e.gameObject),

});

ssrPass.maxDistance = 0.5;

const _ssrPass = ssrPass as any;

_ssrPass.order = 10;

_ssrPass.setRenderer = () => {};

_ssrPass.initialize = () => {};

_ssrPass.setDepthTexture = () => {};

// ssrPass.selective = true;

// ssrPass.output = 7;

const originalRender = ssrPass.render;

ssrPass.render = (…args) => {

const renderer = args[0];

const input = args[1];

const output = args[2];

originalRender.call(ssrPass, renderer, output, output);

};

post.addEffect(ssrPass);

post.addEffect(new Antialiasing());

console.log(ā€œSSR Effect initializedā€);

}

}`


I’m trying to set the scale of the SSR pass but I can’t seem to set it even if I change the width and height it is initialized with - I saw that there is a resolutionScale value in the SSRNode.js but not in the SSRPass.js Screen Space Reflections Resolution Scale? Ā· Issue #31624 Ā· mrdoob/three.js Ā· GitHub

    const resolutionScale = 0.1; // Example: 0.1 for 0.1 resolution
    const ssrPass = new SSRPass({
      renderer: this.context.renderer,
      scene,
      camera: this.context.mainCamera,
      width: innerWidth * resolutionScale,
      height: innerHeight * resolutionScale,
      groundReflector: null,
      selects: null, // ctx.scene.getComponentsInChildren(Renderer).map(e => e.gameObject),
    });
    console.log("ssrpass size: ", ssrPass.width, ssrPass.height);

Is there something I am missing here or are they just 2 different areas of three.js examples that I’m mixing up?

You’re mixing up examples - the response in the three.js issue clearly stated that it was referring to WebGPU and he shared links to three’s new TSL system. I think you should clarify this in your issue that you’ve been referring to a different SSR example

Ooh I’ll go do that now, I wasn’t aware of the other solution but it makes sense now