JSFiddle - React, Tailwind, and code Playground
HTML
<script src="https://threejs.org/build/three.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script src="https://threejs.org/examples/js/loaders/GLTFLoader.js"></script>
<div class="container-canvas"></div>
<script id="vs" type="f">
#define TAU 6.28318530718
varying vec3 worldNormal;
varying vec3 viewDirection;
void main() {
vec4 worldPosition = modelMatrix * vec4( position, 1.0);
worldNormal = normalize( modelViewMatrix * vec4(normal, 0.)).xyz;
viewDirection = normalize(worldPosition.xyz - cameraPosition);
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
</script>
<script id="fs" type="f">
#define TAU 6.28318530718
#define PI 3.14159265359
#define S(a,b,n) smoothstep(a,b,n)
uniform sampler2D envMap;
uniform vec2 uReso;
varying vec3 worldNormal;
varying vec3 viewDirection;
void main() {
float ior = 1.5;
// screen coordinates
vec2 uv = gl_FragCoord.xy / uReso;
// combine backface and frontface normal
vec3 normal = worldNormal;
// calculate refraction and apply to uv
vec3 refracted = refract(viewDirection, normal, 1.0/ior);
uv += refracted.xy;
// sample environment texture
vec4 tex = texture2D(envMap, uv);
vec4 color = tex;
gl_FragColor = vec4(color.rgb, 1.0);
}
</script>
CSS
body{
margin: 0;
}
canvas{
width: 100%;
height: 100vh;
display: block;
}
JavaScript
class Helper{
constructor(scene){
this.scene = scene
}
axes(size){
const _axesHelper = new THREE.AxesHelper( size );
this.scene.add( _axesHelper );
}
grid(size, divisions){
const _gridHelper = new THREE.GridHelper( size, divisions );
this.scene.add( _gridHelper );
}
direction(light, type, size = 1){
let _helper = ''
if(type === 'directional'){
_helper = new THREE.DirectionalLightHelper( light, size );
}else if(type === 'hemisphere'){
_helper = new THREE.HemisphereLightHelper( light, size );
}else if(type === 'point'){
_helper = new THREE.PointLightHelper( light, size );
}else if(type === 'rectarea'){
_helper = new THREE.RectAreaLightHelper( light );
}else if(type === 'spot'){
_helper = new THREE.SpotLightHelper( light );
}
this.scene.add( _helper );
}
camera(cam){
const _helper = new THREE.CameraHelper(cam);
this.scene.add(_helper);
}
}
class RefractionObject {
constructor(scene, renderer, orthoCamera, model, texture) {
this.scene = scene;
this.orthoCamera = orthoCamera;
this.renderer = renderer;
this.model = model;
this.texture = texture;
this.orthoCamera.position.z = 5;
this.orthoCamera.layers.set(1);
this.createBackground();
this.createModel();
}
createBackground() {
const PG = new THREE.PlaneBufferGeometry();
const PM = new THREE.MeshBasicMaterial({
map: this.texture
});
this.plane = new THREE.Mesh(PG, PM);
this.plane.scale.set(window.innerWidth, window.innerHeight, 1);
this.plane.layers.set(1);
this.scene.add(this.plane);
this.envFbo = new THREE.WebGLRenderTarget(
window.innerWidth,
window.innerHeight
);
this.renderer.autoClear = false;
}
createModel() {
this.refractionMaterial = new THREE.ShaderMaterial({
uniforms: {
envMap: { value: this.envFbo.texture },
uReso: { value: new...