mrdoob / three.js

JavaScript 3D Library.
https://threejs.org/
MIT License
102.81k stars 35.38k forks source link

Mirror mirror... #3856

Closed Slayvin closed 6 years ago

Slayvin commented 11 years ago

Following the discussion about the user defined clip plane (#647), I created a 'class' for the mirror objects that would really simplify their usage.

Here are a few examples:

  1. a simple scene with only one mirror (relevant lines are 60-63 and 139): http://jsfiddle.net/PT32b/7/
  2. the same scene, but with a second mirror. Note the usage of the renderWithMirror method (line 142-143), to correctly render a mirror into another one. Currently, only one additional mirror can be rendered inside a mirror (it would probably possible to have more than one by changing the code, but that would be too slow...): http://jsfiddle.net/PT32b/5/
  3. With this method, an infinite mirrors effect is possible, but with some glitches. Only the first 2 reflections are "true" reflections. The other ones are from the previous rendered frames and are not completely accurate: http://jsfiddle.net/PT32b/6/
  4. Finally, the first scene with a third mirror. Because of the two mirror limitation, the reflection of the cut sphere is not correctly rendered in the other mirrors: http://jsfiddle.net/PT32b/8/

Note that you don't have to use the FlatMirror.material member as material, you can also directly use the FlatMirror texture in your own shader, as long as you use the correct uniforms (I'll try to make an example of that later).

You can look at the code of the class here: https://code.google.com/p/mirror-three-js/source/browse/FlatMirror.js

Before submitting a pull request, I'd like some suggestions though.

As it is now, you have to create a mirror like this: myMirror = new THREE.FlatMirror(...) by passing the renderer and the camera as parameters. Then you have to add the mirror to the scene, and assign the mirror.material member to a Mesh. And finally, in your update/render loop, you just call myMirror.render() before your main render call. The scene that is rendered in the mirror is the scene the mirror belongs to. Is it a good idea? Maybe I should try to stay consistent with the render() method of THREE.WebGLRenderer, so passing the scene (and the camera?) when rendering the mirror?

Another question: I've added the required uniforms and shaders in the same .js file as the FlatMirror class. Can this stay like that, or should this part separated and added to WebGLShaders.js (not sure about that, since it's an add-on ) or FlatMirrorShaders.js maybe?

Finally, where would be the best place for FlatMirror.js? I would put it into src\extras\objects. Would that be ok? Another location?

I still have a few optimizations to do on the code, so if you have any suggestions/comments about it, I'd be glad to hear them.

WestLangley commented 11 years ago

+1

I would suggest that this go somewhere in the examples/js directory, but that is up to @mrdoob. I'd also include as many examples as you feel is necessary. I like the examples you have already -- maybe you could combine them into one.

As far as your questions go, I need to look at them at a later date.

danielribeiro commented 11 years ago

This is pretty cool. Would love to see a pull request. @stemkoski has some examples on reflection here: http://stemkoski.github.io/Three.js/Reflection.html, but having this supported natively by Three.js would be great.

I wonder the performance impact of infinite reflection on a more complex scene though. Portal developers had similar issues: http://en.wikibooks.org/wiki/OpenGL_Programming/Mini-Portal#cite_note-1

stemkoski commented 11 years ago

+1

I would also like a pull request. This is great work!

mrdoob commented 11 years ago

Great stuff indeed!

I wonder how hard would it be to do a rendertarget-less version? A guess as a plugin for WebGLRenderer? That way the reflection will always look good resolution independent. Somehow I feel that that's more important than supporting more than 1 mirror or recursive mirrors...?

Also, it would be nice to also have portal plugin too ;)

Slayvin commented 11 years ago

@stemkoski : Thanks! Your examples were really helpful!

I wonder how hard would it be to do a rendertarget-less version?

I know that my solution is not the best one, because each time you create a mirror, you create a new renderTarget (two actually, if you use the two-mirror method). So what would be the best approach to get rid of rendertargets? Using stencil buffer? Wouldn't that cause issue with transparent/alpha materials? Would you still be able to use the "rendered view" as a texture in a custom shader (for a water effect for example)? I guess it would be simpler to handle recursive rendering though.

Also, it would be nice to also have portal plugin too ;)

+1, it would not be really hard to adapt the current code to create portals.

mrdoob commented 11 years ago

So what would be the best approach to get rid of rendertargets? Using stencil buffer? Wouldn't that cause issue with transparent/alpha materials?

Maybe @Miibond has some ideas for this?

Would you still be able to use the "rendered view" as a texture in a custom shader (for a water effect for example)?

I guess this would still require rendertargets...

Also, it would be nice to also have portal plugin too ;)

+1, it would not be really hard to adapt the current code to create portals.

Nice! ^^

MiiBond commented 11 years ago

Hey @Slayvin, nice job!

I think I would prefer this type of thing to use a render target. Though using a stencil buffer would incur less of a memory footprint and possibly less per-pixel cost than a render target reflection would (assuming that the main scene and mirror have equivalent resolution), I think a render target offers more flexibility. For example, I can render the reflection at a significantly lower resolution for a cheap, non-glossy, reflection or I can update the reflection less frequently (say, every other frame) to save some rendering cost (if my perspective is changing slowly enough). And, as mentioned, having the reflection as a texture that we can pipe into any shader would also be very, very useful.

I used to work in UE3 a few years ago and we made heavy use of their SceneCaptureReflectActor which was essentially what Slayvin built. You could change settings on it to control how often it was updated as well as whether fog, post-process, or lighting was done in the render. You can build a lot of functionality on something as generic as this.

stemkoski commented 11 years ago

@Slayvin with a few additions, your FlatMirror shader can be adapted into an animated water-surface-effect shader. As a rough attempt, I put together http://stemkoski.github.io/Three.js/FlatMirror-Water.html (which uses http://stemkoski.github.io/Three.js/js/ShaderWater.js). I basically dumped a bunch of extra things into your shader -- a base texture for blending, a "noise" texture to distort both the base texture and displace the vertices (to create water ripples), and transparency. The framerate seems to take a hit, so hopefully some optimization would be possible. Think of this demo as a proof-of-concept of what @MiiBond was referring to -- the FlatMirror shader could be the basis for some really neat effects.

waterreflect

Slayvin commented 11 years ago

Nice!

Actually, the shader I wrote was indeed very basic, but it is on purpose. This is just to give users who don't want to deal with shaders the possibility to have a mirror "out of the box" :) You don't have to use the FlatMirror.material if you don't want to. Just use your own ShaderMaterial with the required uniforms and pass the FlatMirror.texture and FlatMirror.textureMatrix to it, that's it!

Here is an example of a custom shader using the FlatMirror texture: http://www.slayvin.net/webgl/camaro/

So, I would not change the default shader because the usage can be completely different for each user. I'll add an option so you can initialize the FlatMirror with a customShader, and then the default material would not be created.

I also thought I could rename the class to a more generic name (like "sceneCapture" ?) and then create a class for mirrors and portals. What do you think?

@MiiBond : for all you said about the renderTarget flexibility, I have to agree, since it was my goal from the beginning ;)

mrdoob commented 11 years ago

Here is an example of a custom shader using the FlatMirror texture: http://www.slayvin.net/webgl/camaro/

Niiiice! May I add that one in the examples folder too?

I also thought I could rename the class to a more generic name (like "sceneCapture" ?) and then create a class for mirrors and portals. What do you think?

I think Mirror is good for the moment. If we get to do Portal and there is a lot of dupe code maybe we can abstract it more?

Slayvin commented 11 years ago

Niiiice! May I add that one in the examples folder too?

Sure :)

I'm adapting the code for portals, then I'll submit a pull request

MKHenson commented 11 years ago

This is awesome! Thanks Slayvin!

Digitoxin commented 11 years ago

Cooooool. Have to mess around with this!

zz85 commented 11 years ago

wow, this is very nice, great work! just add random displacement and you get a flooded room :baby: screen shot 2013-09-21 at 10 01 08 am

the camaro example feels like it could create the rainy scenes like in http://www.youtube.com/watch?v=KLd8kEQJIzw and http://www.youtube.com/watch?v=34wCtZDYAUI

brokedRobot commented 11 years ago

Hey guys, sorry to interject. I've used this mirror effect yesterday to create a working water example, here: http://www.prismshards.com/testing/3DgsProject/project/project.htm , gotta give it a minute to load the ground/caustic texture. Also, this is my first attempt at a shader and I just finished, so now I need to go back and make the code a lot more efficient and more object oriented, which I will do. Shrinking your browser window will make it run faster, since the water quad size is based on the window width/height. It also has an ocean shader that works and is based off of OutsideOfSociety's ocean example using the normal shader and color/specular ramping with simplex noise which I adapted for the newest version of three.js ( http://www.webgl.com/2012/03/webgl-demo-water-ocean/ ), and also it has a colliding sine wave generator that can be toggled based on ( http://www.jayconrod.com/posts/50/water-simulation-demo-in-webgl ), both of which work fine (though I haven't tested them since I changed the code a bit so they might need small tweaking). I haven't added a way to toggle them outside of the code yet, but I would be willing to do that if you guys thought this would be a good water solution (but obviously I need to change the code a bunch, needs less render targets, more object oriented, less operations in shaders, etc. etc.). Alternatively, it would also be very easy to just load an animated texture into the height map and have that alter the whole pipeline of the water surface now (for quaking water). The caustics are based on Evan Wallace's WebGL water. I would be willing to make like a beach scene and add a gui for changing all the settings if anyone thinks it's worth it.

Basically I had a question for Slayvin. I started to implement Oblique Frustum Culling in this water, but then right after I started, I found this thread. The issue is that, when you set the mirror's material to THREE.DoubleSide you get a strange result (visually, not mathematically). It appears to me like the texture matrix then goes to double or halve itself so that you get a rotating radial view of the reflection. Or maybe it has something to do with the Texture2DProj() function in the shader which I haven't had time to look up yet. My question was though, is there a way to change the original textureMatrix so that it will handle double-sided mirrors? Right now I am using a trick so that I am flipping the mirror's normal if the camera is below the water surface and also flipping the material from THREE.FrontSide to THREE.Backside. This is easy if you know which side of the surface the camera is on, but will probably get more tricky if the water surface is rotated at angles, and it also requires that you set those things in your update loop, which adds complexity if you want more than one water surface. Anyways, thanks for reading, and great work on the mirror!

mrdoob commented 11 years ago

I would be willing to make like a beach scene and add a gui for changing all the settings if anyone thinks it's worth it.

Definitely worth it! :D

Slayvin commented 11 years ago

@brokedRobot , I guess it would be possible to handle double-sided mirrors by modifying the textureMatrix, in theory. You would have to do the same test as you're doing now anyway: check if the camera is in front or behind the mirror, and then adjust the textureMatrix. I'll have a look at this as soon as I have some time free, I'm quite busy these days...

brokedRobot commented 11 years ago

@mrdoob Ok, I'll start working on it, it might be a week or two because I'm working on other things. Adding a gui should help me make it more object oriented though.

@Slayvin Thanks for the info. I guess if I am going to have to do that check anyway it might just be easier to flip the normal than to mess with the textureMatrix. I can figure out a function to check what side of a plane a point is on based on its normal and then add code to handle flipping the mirror in the water's render/update function. That might be easiest.

s9k commented 10 years ago

My hack for double-sided mirror:

// in the animation loop
mirror.render();

if (mirror.material.side === THREE.DoubleSide) {
    var c = mirror.mirrorCamera.position;
    var m = mirror.localToWorld(mirror.position.clone());
    var v = new THREE.Vector3().subVectors(m, c).normalize();
    var angle = Math.acos(v.dot(mirror.normal));

    if (angle > Math.PI / 2) {
        mirror.scale.z *= -1;
    }
}
jbouny commented 10 years ago

Hi everyone, I started to play with three.js and I propose my first contribution to the library.

Based on the work of @Slayvin, reuse the idea of @stemkoski for the water, and use of other sources, I think I managed to obtain a good effect!

There is a basic demo here : http://jeremybouny.fr/ocean/demo/ All sources are here : https://github.com/jbouny/ocean

ocean1

mrdoob commented 10 years ago

@jbouny looks great!! may I add it to the examples folder then? :)

jbouny commented 10 years ago

Sure you can ! Thanks for this awesome library.

zz85 commented 10 years ago

lovely, +1 for the ocean example!

zz85 commented 10 years ago

i just notice if you "zoom" into the rocks, you unlock an underwater cave.. screen shot 2013-12-16 at 4 38 40 am

MKHenson commented 10 years ago

Woah thats awesome! :D

zz85 commented 10 years ago

I'm just imported a simplistic version of @jbouny shader to a three.js example. I realize that @stemkoski's example make uses of vertex displacements, but @jbouny's version doesn't. For some reasons, I think makes the water looks realistic when viewed at a distance but less so when viewed nearby? Would anyone know if adding vertex displacement to the water shader would be a good idea?

brokedRobot commented 10 years ago

Sorry I flaked out on this...I ended up getting a new job and moving to a new city. I'm still hoping to have time to make a revised example, but I've had less free time since I've been doing some extra stuff for work. There's actually a sine wave vertex shader in the example I posted that might work for what you're talking about? It's not enabled in the example but I'll try to fix it up a little this weekend and post it. The main issue with the example I made is that I used a bunch of render targets because I kept overwriting accidentally at one point, but I think theoretically I should be able to use just one effects composer and set swap buffers. Or you could use one of the height maps for displacement. Vertex shaders get pretty costly though when you introduce all the new triangles along with the complicated fragment shader. So a giant body of water could be really slow. I miss learning about graphics now :(

MiiBond commented 10 years ago

I think it really depends on exactly the effect you're going for. Basically, you only need to bother with vertex displacement if you need to see the height difference in the waves. e.g. The player is riding large swells like in the movie, The Perfect Storm. Or maybe large waves are moving against a vertical surface (e.g. a rock face) and you need to see the wave shape to achieve the effect you want. And, of course, once you've decided to use vertex displacement, you can drive it in a number of different ways depending on whether the water is interacting with something external or statically animated. Displacement, of course, requires more polys so it generally works best at larger scales. Up close, you can achieve the best water effects with overlapping, panning normal maps (of different scales), reflections and distortion.

I've been meaning to rough up my own water demo since I have a fair bit of experience with rendering water in games but I just haven't gotten around to it.

On Wed, Jan 1, 2014 at 2:39 AM, zz85 notifications@github.com wrote:

I'm just imported a simplistic version of @jbounyhttps://github.com/jbounyshader to a three.js example. I realize that @stemkoski https://github.com/stemkoski's example make uses of vertex displacements, but @jbouny https://github.com/jbouny's version doesn't. For some reasons, I think makes the water looks realistic when viewed at a distance but less so when viewed nearby? Would anyone know if adding vertex displacement to the water shader would be a good idea?

— Reply to this email directly or view it on GitHubhttps://github.com/mrdoob/three.js/issues/3856#issuecomment-31419883 .

zz85 commented 10 years ago

nice discussion... seems vertex displacement is slightly more realistic at close range because waves tends to overlap each other, at the tradeoff of more polygons. (as seen in @oosmoxiecode‎ and @brokedRobot's examples)

from a distance, calculating reflections would seem good enough to look like a realistic ocean (approach taken by @jbouny and @jwagner)

it is also possible to archive what vertex displacement does in the fragment shader by using sphere tracing (aka raymarching)

a trade off between doing more polygons could be to use some sort of LOD or using a radial geometry as described in this gpu gem

there's also a whole lot more research done on ocean simulating like this which has been used in Houdini and Blender.

anyways I think I'm drifting off topic already from the original mirror topic and I think I'll stop pursuing this rabbit hole for now... would still love to see any ocean progresses any of you have!

mrdoob commented 6 years ago

https://threejs.org/examples/#webgl_mirror https://threejs.org/examples/#webgl_water