<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>
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.