pmndrs / react-three-fiber

šŸ‡ØšŸ‡­ A React renderer for Three.js
https://docs.pmnd.rs/react-three-fiber
MIT License
27.22k stars 1.56k forks source link

css3d renderer example #366

Closed rectalogic closed 4 years ago

rectalogic commented 4 years ago

An example of using the css3d renderer would be useful. https://github.com/react-spring/react-three-fiber/blob/master/examples/src/demos/dev/CSS3DRenderer.js is empty

I expected it would be something like this, but not sure how to properly use CSS3DObject.

import { Canvas, extend } from 'react-three-fiber/css3d'
import { CSS3DObject } from 'three/examples/jsm/renderers/CSS3DRenderer'
extend({ CSS3DObject })
ReactDOM.render(
  <Canvas>
    <CSS3DObject><div>hello</div></CSS3DObject>
  </Canvas>,
  document.getElementById('root')
)
rectalogic commented 4 years ago

Iā€™m hoping to be able to overlay and synchronize a WebGLRenderer and a CSS3DRenderer to mix DOM elements with WebGL as an alternative to the current experimental Dom support https://github.com/react-spring/react-three-fiber/blob/master/readme.md#dom-experimental-web-only

Similar to what is described here http://learningthreejs.com/blog/2013/04/30/closing-the-gap-between-html-and-webgl/

rectalogic commented 4 years ago

I came up with this which is basically working but seems kind of klunky https://codesandbox.io/s/react-three-fiber-css3d-working-gxke6?file=/src/index.tsx

import React, { useRef, Fragment, useMemo, useEffect } from 'react'
import ReactDOM from 'react-dom'
import { Canvas, useFrame, useThree } from 'react-three-fiber/css3d'
import { CSS3DObject } from 'three/examples/jsm/renderers/CSS3DRenderer'
import './styles.css'

interface DOMObjectProps {
  dom: React.RefObject<HTMLElement>
}
function DOMObject({ dom }: DOMObjectProps) {
  const { scene } = useThree()
  const ref = useRef<CSS3DObject | null>(null)
  useFrame(() => (ref.current!.rotation.x = ref.current!.rotation.y += 0.01))
  useEffect(() => {
    ref.current = new CSS3DObject(dom.current)
    scene.add(ref.current)
    return () => scene.remove(ref.current)
  }, [dom, scene])
  return null
}

interface PortalProps {
  children: React.ReactNode
}
function Portal({ children }: PortalProps) {
  const root = useMemo(() => document.createElement('div'), [])
  return ReactDOM.createPortal(<Fragment>{children}</Fragment>, root)
}

function World() {
  const ref = useRef(null)
  return (
    <Fragment>
      <Canvas camera={{ position: [0, 0, 15] }}>
        <DOMObject dom={ref} />
      </Canvas>
      <Portal>
        <div ref={ref}>hello</div>
      </Portal>
    </Fragment>
  )
}

ReactDOM.render(<World />, document.getElementById('root'))
danieledler commented 3 years ago

Thanks @rectalogic for your example, I'm also interested in a convenient solution for this. With the latest dependencies (react@17.0.0, react-three-fiber@5.3.18 and three@0.125.2) I can't get your example to work though, for me the DOMObject function is never called. Have you continued working with this?

mszekiel commented 3 years ago

Hi, I'm having similar problem as @danieledler, after updating to the latest packages

    "@react-three/drei": "^4.1.8",
    "@react-three/fiber": "^6.0.16",

my code stopped working, I see two empty canvas in the DOM. 3Dgl renderer is working but the CSS renderer is missing.

bolti95 commented 3 years ago

Is css3D not working now with ^4.1.8? I am trying to import it with no luck

drcmda commented 3 years ago

You don't need css3d for anything. dreis html component does everything it could do and more.

3d transforms: https://twitter.com/0xca0a/status/1398633764931178498

occlusion: https://twitter.com/0xca0a/status/1407758860203573251

there is no /css3d render target any longer because you can now just exchange the renderer: <Canvas gl={css3dInstance} or using the render function render(<App />, { gl: css3dInstance but that would make little sense since <Html> exists.

Marviel commented 3 years ago

@drcmda thanks for the occlusion example!

I notice that while that does handle the case of the centroid of the 2D componentry being occluded -- it does not handle the occlusion of, say, a corner of the 2D HTML component. Is that something that is in the works?

drcmda commented 3 years ago

that would be much harder to implement i think. css2drenderer to my knowledge has no occlusion at all though so i think it's still a step up.

Marviel commented 3 years ago

I don't disagree with you -- it would be a lot more difficult. Personally for my use case, it is very helpful to have a "true occlusion."

I have a workaround for this issue, but it is a little hacky and requires using a rendering to a svg html tag, with the foreignObject tag inside.

Importantly, there is a lot of work to be done to allow it to use the same stylesheets as the rest of the application.

drcmda commented 3 years ago

if its doable and you know the math would be nice to make a trial in drei, as a pr perhaps. the current implementation also went through a couple of them. the current one is really naive and it gives it away immediately that it's all smoke and mirrors.

Marviel commented 3 years ago

Yeah totally agree. My current implementation is kind of ugly, but might be helpful for some limited cases. I'll see what I can come up with in the next week or so and get back to you.

On Thu, Jul 8, 2021, 6:31 PM ā€” @.***> wrote:

if its doable and you know the math would be nice to make a trial in drei, as a pr perhaps. the current implementation also went through a couple of them. the current one is really naive and it gives it away immediately that it's all smoke and mirrors.

ā€” You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/pmndrs/react-three-fiber/issues/366#issuecomment-876786443, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAPRLLN3TJ2ZNVI3UXVNEGDTWYRMHANCNFSM4MWYY4EA .

MaximilianFranz commented 2 years ago

Hey,

on a similar note, shouln't it be possible to create our own CSS2DRenderer, attach it to the canvas element and call it in a custom useFrame like this:

  // Takes over the render-loop, the user has the responsibility to render
  useFrame(({ gl, scene, camera }) => {
    gl.render(scene, camera)
    labelRenderer.render(scene, camera)
  }, 1)

with


    const labelRenderer = new CSS2DRenderer({ element: canvasDOM })
    labelRenderer.domElement.style.position = 'absolute'
    labelRenderer.domElement.style.top = '0px'
    setLabelRenderer(labelRenderer)

where canvasDOM is the ref passed to <Canvas>

Any hint, why this isn't working?

drcmda commented 2 years ago

i forgot how to use that class, but yes, you could render it in useframe. thought again, there is a very powerful html renderer in drei that does everything css2d could, and tons more. in react having to mess with imperative createelement just feels off. but either way, it's probably just a set up issue, "canvasDOM" being the wrong element or something like that.

MaximilianFranz commented 2 years ago

i forgot how to use that class, but yes, you could render it in useframe. thought again, there is a very powerful html renderer in drei that does everything css2d could, and tons more. in react having to mess with imperative createelement just feels off. but either way, it's probably just a set up issue, "canvasDOM" being the wrong element or something like that.

Thanks for the fast reply!

You're right. I am just trying to step-by-step port some old code to react-three-fiber and don't wanna do it all in one go. Hence I thought I could get everything "up and running" without changing the structure much at first.

Will look into it some more and report my results here :)

sovetski commented 1 year ago

You don't need css3d for anything. dreis html component does everything it could do and more.

3d transforms: https://twitter.com/0xca0a/status/1398633764931178498

occlusion: https://twitter.com/0xca0a/status/1407758860203573251

there is no /css3d render target any longer because you can now just exchange the renderer: <Canvas gl={css3dInstance} or using the render function render(<App />, { gl: css3dInstance but that would make little sense since <Html> exists.

man you saved my day, thank you!!!