Closed speigg closed 10 years ago
The goal of Threestrap is to wrap existing functionality as simply as possible. Supporting multiple renderers directly is tricky. Just the fact that there are multiple DOM elements kind of throws it off, as e.g. the resize plug-in would now have to resize two or more divs. The cameracontrols plugin might have to bind the events twice, etc.
It would be much simpler if we just had a THREE.MultiRenderer. It could spawn the sub-renderers and coordinate them. You could wrap e.g. two canvases or a canvas + a css3D container. It would act as normal THREE renderer and expose a single DOM element.
If it existed, Threestrap could use it already. You could specify:
renderer: {
klass: THREE.MultiRenderer,
parameters: [ new THREE.WebGLRenderer(options), new THREE.CSS3DRenderer(options) ]
}
I could also add some syntactic sugar so you can just do:
renderer: {
klass: [ THREE.WebGLRenderer, THREE.CSS3DRenderer ]
}
But it is really up to Three.js to support it first. Otherwise Threestrap becomes a framework of hacks instead of wrappers.
Thanks for your quick response! I think it's just a matter of separating the concerns of each plugin a bit more cleanly.
size
plugin currently calculates the renderer's size, applies the size to the renderer's element, and emits a resize
event. It seems to me that it should be simple to refactor this so that the size
plugin knows nothing about the three.renderer
, and simply emits the appropriate resize
event, which the renderer
plugin can then listen to and respond to. This way, the size
plugin only needs to know about changes to the container's size (three.element
), and nothing else, and the renderer resizing logic is contained within the renderer
plugin.render
plugin can similarly be folded into the renderer
plugin, by having the renderer
plugin listen to the render
event and render all of the renderers. I think that basically it.... do you think this makes sense?
Oh, and about the camera controls, can't the events be bound to the three.element
container?
I just checked some of the three.js examples, and they do exactly that: bind the controls to the container, and not the renderer's element.
The renderer could indeed listen for a resize event, this is a minor code change. But regardless, many three.js components need a tight coupling to the renderer, it cannot all be abstracted through events (again: keep it simple). So replacing one renderer with many is still a bad idea.
As for the render plugin, it should be separate, because you may replace it with e.g. a multi-pass effects composer. The core set up of rendering the scene directly to the screen with the default camera is just a sane default.
Re: camera controls. I bind it to the domElement because it may be positioned with CSS. This is how fixed aspect ratios are implemented, with "black bars" that are just margins. It also makes threestrap more agnostic with regards to custom CSS.
By the way, you may be able to just do this by creating one ["core"] threestrap and another that's ["empty", "render"]. Then you manually set three.scene and three.camera on the second one.
Okay. Thanks for the feedback. I may go the route of implementing my own MultiRenderer as you originally suggested.
I just tried the two threestraps approach, works fine:
https://github.com/unconed/threestrap/blob/master/examples/multiple_renderers.html
This is two separate scenes, but you can link the scenes too.
Interesting. Thanks for the example!
So I spent last night creating a very simple MultiRenderer that integrates nicely (I think) with threestrap & three.js renderers.
https://gist.github.com/speigg/6af4527aac8c10d2e46a
These is how I use it in threestrap:
options.renderer = {
klass: MultiRenderer,
parameters: {
renderers: [THREE.WebGLRenderer, THREE.CSS3DRenderer], // stacked back to front
parameters: [
{
alpha: true,
depth: true,
stencil: true,
preserveDrawingBuffer: true,
antialias: true
},
{} // CSS3DRenderer doesn't have any parameters
]
}
}
This works out of the box with almost all threestrap capabilites. In order to support the size
plugin's renderWidth and renderHeight features, the following can be added (somewhere):
three.on('resize', function (event, three) {
var w = event.viewWidth
var h = event.viewHeight
var rw = event.renderWidth
var rh = event.renderHeight
// The MultiRenderer can also accept an additional two arguments in setSize
// Any other renderers will ignore the additional arguments.
three.renderer.setSize(w, h, rw, rh)
});
Feel free to do what you want with this. If it were integrated into threestrap it would work even better, since rw
and rh
can be passed directly as 3rd and 4th parameters to three.renderer.setSize
in the size
plugin.
I had the same idea yesterday, of passing in rw/rh. But WebGLRenderer and CanvasRenderer already have a 3rd parameter (updateStyle). I'd prefer to not unofficially change the signature of that API, this is again something that three.js needs to figure out first, especially in light of retina / high-DPI.
The use case of having render width / render height differ from view width / height was included mainly for high-end demos that would otherwise max out your GPU. I'm not sure if it makes sense to apply it to non-canvas renderers... you would have to set css zoom or something, and it wouldn't lower the resolution, just make some of the text look worse.
I missed that updateStyle
parameter, thanks. Will have to tweak this. Perhaps I'll add a setRenderSize
function.
If you look at the gist, the MultiRenderer is only using the render width / render height on canvas-based renderers, as you say.
I updated the gist with the setRenderSize
approach. This seems cleaner anyways. Only requires adding the following to get everything working:
three.on('resize', function (event, three) {
if (three.renderer.setRenderSize)
three.renderer.setRenderSize(event.renderWidth, event.renderHeight)
});
Seems to work well in a quick test I made.
Btw, I suggest renaming the capWidth
and capHeight
options to capRenderWidth
and capRenderHeight
for clarity.
I just tested everything (scale/capWidth/capHeight), works great. Thanks for the feedback! And aside from high-end demos, I think threestrap's render width / render height feature is also very useful for low end systems (mobile devices).
I renamed them to maxRenderWidth / maxRenderHeight and moved the setSize call to renderer. I also added a check for a setRenderSize call. This is called before setSize for non-canvas based renderers that support it (like multirenderer).
It's sitting in master right now, 0.0.8-dev. If you could check to make sure it all works right with multi renderer, I can tag the release since there's a few other minor tweaks that can go out.
If you want to make an example for multi renderer with webgl + css3d, that would be very welcome by the way.
Awesome. I'll test it out today. Did you want to include MultiRenderer in this repository? I suppose it's needed for the example. I'll submit it to the three.js repository as well, and if they do end up including it then threestrap can remove it later.
All done, in #2 :)
I placed MultiRenderer
and CSS3DRenderer
in a vendor/renderers
directory.
I realized that even if three.js puts MultiRenderer
in their repository, it would probably go in their examples
directory, so threestrap can probably just keep it in vendor
like the controls classes.
It would be great if there were an easy way to specify multiple renderers and their stacking order :)
For example: CSS3DRenderer on top of a WebGLRenderer.
Thanks for creating this, it's very useful already!