<!--
check out the playlist here:
https://open.spotify.com/playlist/64lTO7UojBC6xoDljFdrVu?si=i6Shy45qSkWm34HgPsl4Xg
-->
<script id="vertexShader" type="x-shader/x-vertex">
uniform float mRefractionRatio;
uniform float mFresnelBias;
uniform float mFresnelScale;
uniform float mFresnelPower;

varying vec3 vReflect;
varying vec3 vRefract[3];
varying float vReflectionFactor;

varying vec2 f_uv;
varying vec3 f_normal;
varying vec3 f_position;
void main() {
	f_uv = uv;
	f_normal = normalize( normalMatrix * normal );
	f_position = position;
	
	vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
	vec4 worldPosition = modelMatrix * vec4( position, 1.0 );
	
	vec3 worldNormal = normalize( mat3( modelMatrix[0].xyz, modelMatrix[1].xyz, modelMatrix[2].xyz ) * normal );
	
	vec3 I = worldPosition.xyz - cameraPosition;
	
	vReflect = reflect( I, worldNormal );
	vRefract[0] = refract( normalize( I ), worldNormal, mRefractionRatio );
	vRefract[1] = refract( normalize( I ), worldNormal, mRefractionRatio * 0.99 );
	vRefract[2] = refract( normalize( I ), worldNormal, mRefractionRatio * 0.98 );
	vReflectionFactor = mFresnelBias + mFresnelScale * pow( 1.0 + dot( normalize( I ), worldNormal ), mFresnelPower );
	
	gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
uniform samplerCube tCube;

varying vec3 vReflect;
varying vec3 vRefract[3];
varying float vReflectionFactor;
	
uniform sampler2D texture;
uniform int u_useTexture;
uniform vec3 u_albedo;
uniform vec3 u_ambient;
uniform vec3 u_lightPos;
uniform vec3 u_lightCol;
uniform float u_lightIntensity;
uniform vec3 CamPos;

varying vec3 f_position;
varying vec3 f_normal;
varying vec2 f_uv;

void main() {
	vec4 color = vec4(u_albedo, 1.0);
	
	if (u_useTexture == 1) {
		color = texture2D(texture, f_uv);
	}
	float d = clamp(dot(f_normal, normalize(u_lightPos - f_position)), 0.0, 1.0);
	float t = clamp(dot(f_normal, normalize(CamPos - f_position)), 0.0, 1.0);
	
	float red = 0.5 + 0.5*(cos(6.28*(t)));
	float green = 0.5 + 0.5*(cos(6.28*(t+0.33)));
	float blue = 0.5 + 0.5*(cos(6.28*(t+0.67)));
	
	vec3 iridescent_color = vec3(red, green, blue);
	
	vec4 reflectedColor = textureCube( tCube, vec3( -vReflect.x, vReflect.yz ) );
	vec4 refractedColor = vec4( 1.0 );
	
	refractedColor.r = mix(textureCube( tCube, vec3( -vRefract[0].x, vRefract[0].yz ) ).r, red, 0.8);
	refractedColor.g = mix(textureCube( tCube, vec3( -vRefract[1].x, vRefract[1].yz ) ).g, green, 0.8);
	refractedColor.b = mix(textureCube( tCube, vec3( -vRefract[2].x, vRefract[2].yz ) ).b, blue, 0.8);
	gl_FragColor = mix( refractedColor, reflectedColor, clamp( vReflectionFactor, 0.0, 1.0 ) );
}
</script>
html, body{overflow:hidden;}
View Compiled
var container;
var camera, scene, renderer, textureCube, object;
var mouseX = 0, mouseY = 0;
var clock = new THREE.Clock();
var path = 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/253981/';
var format = ".png";
var order = ["sky", "sky", "sky", "sky", "cov", "sky"];
var urls = [];
order.forEach(side => {urls.push(`${path}${side}${format}`);});
var loaded = false;
init();

function init() {
	renderer = new THREE.WebGLRenderer({antialias:true, alpha: true });
	renderer.setPixelRatio( window.devicePixelRatio );
	renderer.setSize( window.innerWidth, window.innerHeight );
	container = document.createElement( 'div' );
	document.body.appendChild( container );
	container.appendChild( renderer.domElement );
	var textureCube = new THREE.CubeTextureLoader().load(urls);
	textureCube.format = THREE.RGBFormat;
	
	height = window.innerHeight;
	width = window.innerWidth;
	camera = new THREE.PerspectiveCamera( 45, width / height, 1, 1000 );
	camera.position.z =-200;
	scene = new THREE.Scene();
	//renderer.setClearColor( 0x000000, 0 );
	scene.background = new THREE.Color( 0x111111 );
	
	var ambientLight = new THREE.AmbientLight( 0xcccccc, 0.5 );
	scene.add( ambientLight );
	var pointLight = new THREE.PointLight( 0xffffff, 0.9);
	camera.add( pointLight );
	scene.add( camera );
	uniforms = {
		mRefractionRatio: { value: 0.96 },
		mFresnelBias: { value: 0.05 },
		mFresnelPower: { value: 2.0 },
		mFresnelScale: { value: 0.5 },
		tCube: { value: textureCube },
		texture: {type: 't',value: null},
		u_useTexture: {type: 'i',value: true},
		u_albedo: {type: 'v3',value: new THREE.Color(0x000000)},
		u_ambient: {type: 'v3',value: new THREE.Color(0x000000)},
		u_lightPos: {type: 'v3',value: new THREE.Vector3(-600, 0, 400)},
		u_lightCol: {type: 'v3',value: new THREE.Color(0xffffff)},
		u_lightIntensity: {type: 'f',value: .6},
		CamPos: {type: 'v3',value:  new THREE.Vector3(-600, 0, 400)}
	}
	group = new THREE.Group();
	material = new THREE.ShaderMaterial( {
		uniforms: uniforms,
		vertexShader: document.getElementById( 'vertexShader' ).textContent,
		fragmentShader: document.getElementById( 'fragmentShader' ).textContent
	});			
	var manager = new THREE.LoadingManager( function(){
		object.traverse( function ( child ) {
			if ( child.isMesh ) {
				child.material = material;
				child.material.side = THREE.DoubleSide;
			} 
		});
		scale = 90;
		object.scale.set(scale,scale,scale);
		group.position.y = 3;
		//group.rotation.y =Math.PI * -.5;
		object.castShadow = true;
		object.receiveShadow = true;
		group.add(object);
		scene.add( group );
		;		
	} );
	var loader = new THREE.OBJLoader( manager );
	loader.load( 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/253981/heart10.obj', function ( obj ) {
		object = obj;
		loaded = true;
		animate();
	});

	window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
	camera.aspect = window.innerWidth / window.innerHeight;
	camera.updateProjectionMatrix();
	renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
	requestAnimationFrame( animate );
	render();
}
function render() {
	timer = 0.001 * Date.now();
	if(loaded){
		group.rotation.x = Math.sin(timer * .5  ) * (Math.PI / 4) * -.5;
		group.rotation.y = (Math.PI / 2) + Math.sin(timer * .125  ) * (Math.PI);
		group.rotation.z = Math.sin(timer * .25  ) * (Math.PI / 8);
		group.position.z = Math.sin(timer *.125  ) * -20;
	}
	camera.lookAt( scene.position );
	camera.position.y = -5;
	renderer.render( scene, camera );
}

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/three.js/110/three.min.js
  2. https://s3-us-west-2.amazonaws.com/s.cdpn.io/253981/OBJLoader.js