firtoz / react-three-renderer

Render into a three.js canvas using React.
https://toxicfork.github.com/react-three-renderer-example/
MIT License
1.49k stars 155 forks source link

Access WebGLRenderer #27

Closed mkraessig closed 8 years ago

mkraessig commented 8 years ago

Hi there!

I am trying to implement the Stereo Effect, but as an argument it takes the renderer. Is there any way I can pass the Three.WebGLRenderer used to the StereoEffect function?

Thanks so much! — Maxi

toxicFork commented 8 years ago

Until I implement a nice function to get it, for now,

In your component that will contain React3, you can add a ref like so:

<React3 ref="react3" ...other properties here ...>

This way, for example within componentDidMount:

    console.log(react3.refs.canvas.userData.markup.childrenMarkup[0].threeObject._renderer);

should print the renderer.

I am thinking of adding a prop e.g. onRendererCreated or something to React3.

mkraessig commented 8 years ago

Totally suffices for now! Thank you so much, @toxicFork!

This may be another issue, but I still can't seem to get the Stereo Effect to work. Have you ever tried that or do you know on the spot whether something like that's even possible with react-three-renderer?

toxicFork commented 8 years ago

Hmm... http://threejs.org/examples/js/effects/StereoEffect.js doesn't look very complicated, I can add support for stereoCamera, or you can try to duplicate it by combining it with https://github.com/mrdoob/three.js/blob/master/src/cameras/StereoCamera.js

I will add support for StereoCamera anyway, as well as onRendererCreated :)

mkraessig commented 8 years ago

Perfect, thanks! Yup, should be pretty straightforward - I kinda missed StereoCamera there ;) Thanks a bunch!

toxicFork commented 8 years ago

In v0.1.0 now :) Please reopen if there are any issues!

toxicFork commented 8 years ago

See https://github.com/toxicFork/react-three-renderer/wiki/react3#onrendererupdated

iDVB commented 8 years ago

Please bare with as I am brand new ThreeJS. We've got react-three-renderer working GREAT!

We're now just trying to dive into post processing and running into a few issues. First, when looking into the post processing examples on Threejs.org, all the examples directly edit the global Three object when they want to implement a module. Additionally, those examples have a ton of tiny libs/objects/modules that do not seem to be part of any npm package.

We are trying to use three-effectcomposer with react-three-renderer and we're not getting any errors, just a warning, and there is no noticiable change to the render.

here is our code:


import './scss/ParticleScene.scss';

import React from 'react';
import React3 from 'react-three-renderer';
import THREE from 'three';
import ReactDOM from 'react-dom';
import TweenLite from 'gsap';
import BinaryLoader from '../../utils/BinaryLoader.js';

import Particle from '../Particle/Particle.jsx';
import ParticleField from '../ParticleField/ParticleField.jsx';
import textureImage from './textures/UV_Grid_Sm.jpg';

import cellModel from './objects/stemcell2/stemCell_lp.json';
import cellTexture from './objects/stemcell2/stemCell_lp-TM_AO_u0_v0.jpg';
import cellNormalMap from './objects/stemcell2/stemCell_lp-NM_u0_v0.jpg';
import cellDisplacmentMap from './objects/stemcell2/stemCell_lp-DM1024_8bit_u0_v0.jpg';
import cellAOMap from './objects/stemcell2/stemCell_lp-AO_u0_v0.jpg';

const EffectComposer = require('three-effectcomposer')(THREE);

THREE.DotScreenShader = {
  uniforms: {
    "tDiffuse": { type: "t", value: null },
    "tSize":    { type: "v2", value: new THREE.Vector2( 256, 256 ) },
    "center":   { type: "v2", value: new THREE.Vector2( 0.5, 0.5 ) },
    "angle":    { type: "f", value: 1.57 },
    "scale":    { type: "f", value: 1.0 }
  },
  vertexShader: [
    "varying vec2 vUv;",
    "void main() {",
      "vUv = uv;",
      "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
    "}"
  ].join("\n"),
  fragmentShader: [
    "uniform vec2 center;",
    "uniform float angle;",
    "uniform float scale;",
    "uniform vec2 tSize;",
    "uniform sampler2D tDiffuse;",
    "varying vec2 vUv;",
    "float pattern() {",
      "float s = sin( angle ), c = cos( angle );",
      "vec2 tex = vUv * tSize - center;",
      "vec2 point = vec2( c * tex.x - s * tex.y, s * tex.x + c * tex.y ) * scale;",
      "return ( sin( point.x ) * sin( point.y ) ) * 4.0;",
    "}",
    "void main() {",
      "vec4 color = texture2D( tDiffuse, vUv );",
      "float average = ( color.r + color.g + color.b ) / 3.0;",
      "gl_FragColor = vec4( vec3( average * 10.0 - 5.0 + pattern() ), color.a );",
    "}"
  ].join("\n")
};

THREE.RGBShiftShader = {
  uniforms: {
    "tDiffuse": { type: "t", value: null },
    "amount":   { type: "f", value: 0.005 },
    "angle":    { type: "f", value: 0.0 }
  },
  vertexShader: [
    "varying vec2 vUv;",
    "void main() {",
      "vUv = uv;",
      "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
    "}"
  ].join("\n"),
  fragmentShader: [
    "uniform sampler2D tDiffuse;",
    "uniform float amount;",
    "uniform float angle;",
    "varying vec2 vUv;",
    "void main() {",
      "vec2 offset = amount * vec2( cos(angle), sin(angle));",
      "vec4 cr = texture2D(tDiffuse, vUv + offset);",
      "vec4 cga = texture2D(tDiffuse, vUv);",
      "vec4 cb = texture2D(tDiffuse, vUv - offset);",
      "gl_FragColor = vec4(cr.r, cga.g, cb.b, cga.a);",
    "}"
  ].join("\n")
};

export default class ParticleScene extends React.Component {
  constructor(props, context) {
    super(props, context);

    // SCENE POSITION
    this.scenePosition = new THREE.Vector3(0, 0, 0);

    // construct the position vector here, because if we use 'new' within render,
    // React will think that things have changed when they have not.
    this.cameraPosition = new THREE.Vector3(0, 0, 5);
    this.cameraRotation = new THREE.Euler(0, 0, 90 * Math.PI / 180);

    // LIGHT POSITION
    this.directionalLightPosition = new THREE.Vector3(0, 1, 0);

    //PARTICLE POSITION
    this.particlePosition = new THREE.Vector3(0, 0, 4);

    // FOG SETUP
    this.fog = new THREE.Fog(0x4994c8, 0, 10);

    //Mesh Geometry
    this.cellGeometry = this.parseMeshFromJSON(cellModel);

    this.state = {
      viewerWidth : window.innerWidth,
      viewerHeight : window.innerHeight
    };

    this._onAnimate = this._onAnimate.bind(this);
    this._handleWindowResize = this._handleWindowResize.bind(this);
    this._handleRenderUpdated = this._handleRenderUpdated.bind(this);
  }

  // Native Functions
  // -----------------
  componentDidMount() {
    window.addEventListener('resize', this._handleWindowResize, false);
    this.refs.meshPhongMaterial.normalMap = this.loadTexture(cellNormalMap);
    this.refs.meshPhongMaterial.displacementMap = this.loadTexture(cellDisplacmentMap);
    this.refs.meshPhongMaterial.displacementBias = 2;
    // this.refs.meshPhongMaterial.wireframe = true;
    this.refs.meshPhongMaterial.displacementScale = 0.4;
    this.refs.meshPhongMaterial.aoMap = this.loadTexture(cellAOMap);

    // Post-Processing

    this.composer = new EffectComposer(this.renderer);
    this.composer.addPass(new EffectComposer.RenderPass(this.refs.mainScene, this.refs.mainCamera));

    // Redraw with a shader
    const effect = new EffectComposer.ShaderPass(THREE.DotScreenShader);
    this.composer.addPass(effect);

    // And another shader, drawing to the screen at this point
    const effect2 = new EffectComposer.ShaderPass(THREE.RGBShiftShader);
    effect2.renderToScreen = true;
    this.composer.addPass(effect2);
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this._handleWindowResize, false);
  }

  // Custom Functions
  // -----------------
  _onAnimate() {
    // we will get this callback every frame
    this.refs.CellParticlesClose.update();
    this.refs.CellParticlesSuperClose.update();
    this.refs.CellParticlesMed.update();
    this.refs.CellParticlesFar.update();

    this.composer.render();
  };

  // <--- Removed code for brevity

  _handleRenderUpdated(renderer) {
    if(renderer !== null) {
      console.log(renderer instanceof THREE.WebGLRenderer); // true
      this.renderer = renderer;
    } else {
      // renderer is just destroyed or will be recreated soon
    }
  }

  // <--- Removed code for brevity

  render() {
    const {
      viewerWidth,
      viewerHeight
    } = this.state;

    return (<div className="ParticleScene"><React3
      mainCamera="mainCamera" // this points to the perspectiveCamera which has the name set to "camera" below
      width={viewerWidth}
      height={viewerHeight}
      antialias = {true}
      onAnimate={this._onAnimate}
      clearColor={this.fog.color}
      ref="react3"
      onRendererUpdated={this._handleRenderUpdated}
    >
      <resources>
        <geometry
          resourceId="cellGeometry"
          {...this.cellGeometry}
        />
        <texture
          resourceId="cellTexture"
          url={cellTexture}
        />
        <meshPhongMaterial
          resourceId="cellMaterial"
          ref="meshPhongMaterial"
        >
          <textureResource
            resourceId="cellTexture"
          />
        </meshPhongMaterial>

        <sphereGeometry
          resourceId="particleGeometry"
          radius={0.1}
          widthSegments={20}
          heightSegments={20}
        />
        <sphereGeometry
          resourceId="particleGeometrySmall"
          radius={0.0001}
          widthSegments={50}
          heightSegments={50}
        />
        <sphereGeometry
          resourceId="particleGeometryLow"
          radius={0.1}
          widthSegments={10}
          heightSegments={10}
        />
        <texture
          resourceId="texture"
          url={textureImage}
          wrapS={THREE.RepeatWrapping}
          wrapT={THREE.RepeatWrapping}
          anisotropy={16}
        />
        <meshLambertMaterial
          resourceId="particleMaterial"
          side={THREE.DoubleSide}
        >
          <textureResource
            resourceId="texture"
          />
        </meshLambertMaterial>
      </resources>
      <scene
        fog={this.fog}
        ref="mainScene"
      >
        <perspectiveCamera
          name="mainCamera"
          fov={75}
          aspect={viewerWidth / viewerHeight}
          near={0.1}
          far={1000}
          position={this.cameraPosition}
          rotation={this.cameraRotation}
          ref="mainCamera"
        />
        <ambientLight
          color={0x404040}
        />
        <directionalLight
          color={0xffffff}
          position={this.directionalLightPosition}
          lookAt={this.scenePosition}
        />

        <ParticleField
          numberOfParticles = {3}
          particleFieldRadius = {5}
          particleFieldCenter = {this.particlePosition}
          particleScale = {0.1}
          particleGeometryResourceId = "particleGeometry"
          particleMaterialResourceId = "cellMaterial"
          ref = "CellParticlesSuperClose"
        />

        <ParticleField
          numberOfParticles = {20}
          particleFieldRadius = {5}
          particleFieldCenter = {this.cameraPosition}
          particleScale = {1}
          particleGeometryResourceId = "particleGeometry"
          particleMaterialResourceId = "particleMaterial"
          ref = "CellParticlesClose"
        />

        <ParticleField
          numberOfParticles = {50}
          particleFieldRadius = {10}
          particleFieldCenter = {this.cameraPosition}
          particleScale = {1}
          particleGeometryResourceId = "particleGeometryLow"
          particleMaterialResourceId = "particleMaterial"
          ref = "CellParticlesMed"
        />

        <ParticleField
          numberOfParticles = {300}
          particleFieldRadius = {50}
          particleFieldCenter = {this.cameraPosition}
          particleScale = {1}
          particleGeometryResourceId = "particleGeometryLow"
          particleMaterialResourceId = "particleMaterial"
          ref = "CellParticlesFar"
        />

      </scene>
    </React3></div>);
  }
}
FredHasselot commented 7 years ago

Hi,

have you found any solution? I've got the same issue.

Thanks

toxicFork commented 7 years ago

@FredHasselot does https://github.com/toxicFork/react-three-renderer/wiki/react3#onrendererupdated work for your problem? If not we can improve it :)

@iDVB apologies I thought I had responded but it seems the response didn't go through github...

It looks like it should work but it's hard to guess what may have gone wrong without stepping into the code unfortunately

FredHasselot commented 7 years ago

Hi,

I tried to add : composer.render(); after a list of effects on the onRenderUpdated event function.

something like:

onRendererUpdated={this.renderedUpdated.bind(this)}

on the React3 tag

then something like:

renderedUpdated(r3nderer) {

  composer = new THREE.EffectComposer( r3nderer);
  composer.addPass( new THREE.RenderPass( this.refs.scene, this.refs.mainCamera ) );

  let dotScreenEffect = new THREE.ShaderPass( THREE.DotScreenShader );
  dotScreenEffect.uniforms[ 'scale' ].value = 4;
  composer.addPass( dotScreenEffect );

  let rgbEffect = new THREE.ShaderPass( THREE.RGBShiftShader );
  rgbEffect.uniforms[ 'amount' ].value = 0.0015;
  rgbEffect.renderToScreen = true;
  composer.addPass( rgbEffect );

  composer.render()
}

But this.refs.scene and this.refs.mainCamera were undefined if i remember... So i did something similar on the componentDidMount event and later i tried on the update function as well. without success.

I read, somewhere on the EffectComposer documentation, that the render have to be done this way : composer.render() and not this way : renderer.render() maybe the issue is related to that?

toxicFork commented 7 years ago

If you do the binding within render function it may get called repeatedly, and in some cases the r3nderer variable will be undefined (for example when a canvas needs to be re-created)

Yes composer.render sounds better, but still if you can provide an example that I can run, it will be easier to look at what's going on :)

oortlieb commented 7 years ago

Has anybody had any luck getting post-processing working with an EffectComposer? We're trying to implement an object "outline" feature, and it seems like the best way to do it is with EventComposer post-processing.

I've tried putting the composer in a few places, but haven't had any luck yet. Any info about successfully using an EventComposer with this library would be very appreciated.

Danathus commented 6 years ago

Yes @oortlieb -- after reading this thread and doing a little digging, I got this working, applying the shader examples provided by iDVB as a test case. (Cool effect btw @iDVB )

I will provide a walkthrough of my steps here to help anyone who stumbles across this and is in a similar predicament. Be warned however that this is a little hacky and very dependent on the specific "internal" variable arrangement of r3r types, which is very prone to change as it continues to develop -- so if your version doesn't match mine (3.2.4), you may need to do some digging in the source and make some adjustments. (At least until a clean API is provided for this case, s'il vous plaît @toxicFork )

I hooked up React3 in the following manner for reference:

<React3 ref={react3 => (this.react3 = react3) }

Then, in componentDidMount(), after creating this.composer, I added the following line:

this.react3._canvas.userData.markup.childrenMarkup[0].threeObject._renderScene = camera => { this.composer.render(); }

This effectively replaces what would be a call to WebGLRenderer.renderScene() with a call to EffectComposer.render() -- the fundamental otherwise missing step to get this post-process effect working.

I arrived at this from starting from toxicFork's comment https://github.com/toxicFork/react-three-renderer/issues/27#issuecomment-185429288. I ran a text search through the source for react-three-renderer and found where .render is called -- which is only in React3Instance.js, in the one call _renderScene() (which makes it easy).

Note that this effectively skips some debug logic which looks like it shows some overlaid highlight scene for non-production builds; in my case I don't care about this. Ideally though r3r would provide some hook to override (or add another layer of processing) around the render call (which would I think be all that is necessary to support this).