<!DOCTYPE html>

<html lang="en">

<head>

<meta name="viewport" content="width=device-width,initial-scale=1.0">

<title>Photon Holographic Interference</title>

<style>body{margin:0;overflow:hidden}canvas{display:block}</style>

</head>

<body>

<canvas id="glcanvas"></canvas>

<script src="https://cdn.jsdelivr.net/npm/three@0.152.2/build/three.min.js"></script>

<script>

const canvas=document.getElementById("glcanvas");

const renderer=new THREE.WebGLRenderer({canvas,antialias:true});

renderer.setSize(window.innerWidth,window.innerHeight);

document.body.appendChild(renderer.domElement);

const scene=new THREE.Scene();

const camera=new THREE.PerspectiveCamera(60,window.innerWidth/window.innerHeight,0.1,1000);

camera.position.set(0,0,5);

const geometry=new THREE.TorusKnotGeometry(1,0.3,256,32);

const waveSources=[

  new THREE.Vector3(2.0,0.0,0.0),

  new THREE.Vector3(-2.0,1.0,0.0),

  new THREE.Vector3(0.0,-2.0,1.0)

];

const uniforms={

  time:{value:0},

  camPos:{value:camera.position.clone()},

  lightDir:{value:new THREE.Vector3(0.2,0.8,1.0).normalize()},

  baseColor:{value:new THREE.Color(0.25,0.2,0.7)},

  F0:{value:new THREE.Color(1.0,0.9,0.8)},

  roughness:{value:0.3},

  waveSources:{value:waveSources},

  waveFrequency:{value:4.0},

  waveSpeed:{value:1.5},

  waveAmplitude:{value:0.1}

};

const vertexShader=`

varying vec3 vPos;

varying vec3 vNormal;

void main(){

  vNormal=normalize(normalMatrix*normal);

  vec4 worldPos=modelMatrix*vec4(position,1.0);

  vPos=worldPos.xyz;

  gl_Position=projectionMatrix*viewMatrix*worldPos;

}

`;

const fragmentShader=`

uniform float time;

uniform vec3 camPos;

uniform vec3 lightDir;

uniform vec3 baseColor;

uniform vec3 F0;

uniform float roughness;

uniform vec3 waveSources[3];

uniform float waveFrequency;

uniform float waveSpeed;

uniform float waveAmplitude;

varying vec3 vPos;

varying vec3 vNormal;

float beckmannDist(vec3 N, vec3 H, float alpha){

  float ndoth=max(dot(N,H),0.0);

  float alpha2=alpha*alpha;

  float ndoth2=ndoth*ndoth;

  float denom=3.14159*alpha2*pow(ndoth,4.0);

  float exponent=(ndoth2-1.0)/(ndoth2*alpha2);

  return exp(exponent)/max(denom,1e-5);

}

float geometrySmith(vec3 N, vec3 V, vec3 L, float alpha){

  float ndotv=max(dot(N,V),0.0);

  float ndotl=max(dot(N,L),0.0);

  float k=alpha*0.5;

  float gv=ndotv/(ndotv*(1.0-k)+k);

  float gl=ndotl/(ndotl*(1.0-k)+k);

  return gv*gl;

}

vec3 fresnelSchlick(float cosTheta, vec3 F0){

  return F0 + (vec3(1.0)-F0)*pow(1.0-cosTheta,5.0);

}

void main(){

  vec3 N=normalize(vNormal);

  vec3 V=normalize(camPos - vPos);

  vec3 L=normalize(lightDir);

  vec3 H=normalize(V+L);

  float ndotv=max(dot(N,V),0.0);

  float ndotl=max(dot(N,L),0.0);

  float ndoth=max(dot(N,H),0.0);

  float alpha=roughness*roughness;

  float D=beckmannDist(N,H,alpha);

  float G=geometrySmith(N,V,L,alpha);

  vec3  F=fresnelSchlick(ndoth,F0);

  vec3  cookTorrance=(D*G*F)/(4.0*ndotv*ndotl+1e-5);

  vec3  diffuse=baseColor*ndotl;

  vec3  shading=diffuse + cookTorrance;

  // Photon-like wave interference

  float interference=0.0;

  for(int i=0;i<3;i++){

    float dist=distance(vPos,waveSources[i]);

    float phase=waveFrequency*dist - waveSpeed*time;

    interference+=sin(phase)*waveAmplitude;

  }

  // Combine

  vec3 color=shading + vec3(interference);

  gl_FragColor=vec4(color,1.0);

}

`;

const material=new THREE.ShaderMaterial({

  vertexShader,

  fragmentShader,

  uniforms

});

const mesh=new THREE.Mesh(geometry,material);

scene.add(mesh);

function onResize(){

  renderer.setSize(window.innerWidth,window.innerHeight);

  camera.aspect=window.innerWidth/window.innerHeight;

  camera.updateProjectionMatrix();

}

window.addEventListener("resize",onResize);

function animate(t){

  uniforms.time.value=t*0.001;

  mesh.rotation.x+=0.002;

  mesh.rotation.y+=0.003;

  renderer.render(scene,camera);

  requestAnimationFrame(animate);

}

requestAnimationFrame(animate);

</script>

</body>

</html>

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.