Hi there,
recently I created a panoramic viewer with a reflecting glass sphere in the centre:
As a next step I tried to make the sphere transparent and refracting instead of reflecting. Struggled for hours and finally found out that there was a simple solution:
Simply use THREE.EquirectangularRefractionMapping instead of THREE.EquirectangularReflectionMapping:
However there is one issue left: When looking at real glass spheres, e. g. here:
it’s obvious that my virtual version is not really realistic. On the real sphere there is some reflection on top.
Is there a way to simulate this effect too?
Best regards - Ulrich
Hi Mugen87, thanks for these hints, I modified my code accordingly.
Regarding the flipping: This is just the effect I intended to achieve. A real glass sphere does it the same way. Have a look at the photos on Pinterest.
My latest version:
Any ideas how to make the refracting version more realistic? I mean the faint bright light on top. I suspect I would have to add another sphere with a solid surface and some light. However I don’t know how to combine both.
One solution may be screen-space-reflection + screen-space-refraction.
There are already many implementations listed below, but I’m making my own implementation suit with latest three.js and can use with WebGL1, now just screen-space-reflection, intend to make screen-space-refraction next.
Hi gonnavis and thanks for this info. Seems to be a complex subject and I’m not shure if my skills are sufficient to dive into.
When I look at real photos of a glass sphere closely it seems to me that there is a combination of refraction and reflection: In the center the sphere is transparent while near to the border the background is reflected. Just like the surface of the virtual water: When the viewing angle is large, it’s transparent and when the angle is small the water is reflecting. Not shure if it’s possible to simulate this by use of three.js.
This kind of physically correct rendering will be hard to achieve. I have not seen it before with any type of realtime 3D engine. You probably need raytracing for this.
In contrast, shining light was fairly easy. The illuminated surface could be merged with the refraction or reflection but this was not what was in my mind and was visible on the photos of real spheres.
This is called Fresnel Effect, I implemented it using a very simple dot calculation ( commit ). Should not be physically correct, but I think the feeling is ok.
Even more, if you combine what @Mugen87 and @gonnavis said (maybe except SSR) you should be able to create a realistic glass ball like in the pinterest you linked.
You don’t need opacity / transparency at all, the ball you linked is 100% opaque refraction (the reflections you see on it are not transparency, it’s a reflection environment mapping, transparency will result in a flat reflection.)
It should be quite simple to create a fragment shader that mixes refraction envmap with reflection envmap (same example, just toggle the switch on the right, it’s just 2 textures with different mapping).
If you’d like edges to fade to some color opaque (ie. fresnel) you can also mix in fresnel shader.
In this example, https://sbcode.net/threejs/refract-reflect/
I create an inner and outer shine effect by creating 2 objects with different materials.
You can experiment with the gui sliders on the link above.
Yeah, you’re right, I probably messed up the sliders… (But mind - the only realistic value for opacity is 1.0. Even a bubble should refract edges afair, since it’s a curved surface. Transparency kinda breaks the effect of realistic surface.)
So yeah, it all got me intrigued, and I tried to recreate this image with every - @seanwasere, @gonnavis, and Fresnel - approach, and in the end this glass ball is as far as I managed to get without a custom shader:
(1) @seanwasere approach, ie. 2 spheres + 2 env is the easiest and looks quite realistic enough. In a super-simplified version it technically needs just 3-4 params for materials (just don’t use opacity, that’s what envMaps and cubemap cameras are for. )
(2) As little as it makes sense I tried the experimental SSR branch, since I have it locally - but because it’s post-processing, reflections affect the rendered lights and it just looked a bit awkward. (the white light-dots on glass surface disappeared etc.)
(3) Fresnel shader looks by far the best - but first, it works only with cube map textures, second, it doesn’t seem to be affected by scene lights which kills the purpose.
But it’d still be best to do a custom shader, since none of these could do the focused light effect on the other side of the ball (at least dynamically, and it could be easily calculated from sun position and IOR) :
Many thanks for the numerous answers and information. Unfortunately, as I’m a novice to threejs, I was not able to understand all of this completely. @seanwasere Your latest demo is looking very fine. I intented to download it and fiddle but when using the downloading function of my browser it didn’t download the modules being imported. I tried Opera, Chrome, Firefox, Edge.
Is there an easy way of downloading a complete page including javascript modules?
Best regards - Ulrich
@mjurczyk I tested your demo as well and after some fiddling it seems to me that I understand how it works. Many thanks for this contribution! There are two issues:
The reflection at the edge is a bit too faint
The reflection in the center is too strong when looking at a dark ground: The sphere reflects the blue sky then.
You mentioned a custom shader. Can this be implemented by javascript? If so I would do some research and try. If not, it would be too complex for me.
That’s caused by your requirement for the reflection to be flipped - not much can be done about that, unless you’ll involve real raytracing Mugen mentioned earlier.
Gotchu, so first of all - there’s a bit of magic happening with these materials that make them look ok - that includes never touching transmission: .99. Otherwise you’ll render body colour and reflections of the external sphere.
To make the edge show more / less, you can add envMapIntensity and set it to anything from 0.0 to infinity (technically you’re only allowed to set it between 0.0 - 1.0, but rules are there to be broken. Setting intensity above 1.0 basically counters the fact that we set transmission to nearly 1.0):
// Add refraction (inner sphere)
mesh.add(new THREE.Mesh(
new THREE.SphereBufferGeometry(2, 32, 32),
new THREE.MeshStandardMaterial({
envMap: createEnvMap('refraction'),
metalness: 1.0,
roughness: 0.1
})
));
// Add reflection (half-transparent outer sphere)
mesh.add(new THREE.Mesh(
new THREE.SphereBufferGeometry(2 + Number.MIN_VALUE, 32, 32),
new THREE.MeshPhysicalMaterial({
envMap: createEnvMap('reflection'),
envMapIntensity: 2.0, // <- Changing this changes the fake fresnel effect
roughness: 0.1,
transmission: .99, // <- Changing this will reveal that it's just fake fresnel, so lets never do that
transparent: true
})
));
If you’d change transmission or opacity (or going too high with envMapIntensity) on either sphere you’ll start seeing this reflections also in the middle of the sphere, and it’s not good:
I wouldn’t judge it that fast. GLSL is very simple - it’s the math that can make shaders complex. Here’s an example of a shader that creates a real Fresnel effect:
Ignoring the horrible code readability, the entire shader is like ~20 lines of code, out of which 75% is addition and multiplication of some texture values and vectors.