preface
Hello, this is CSS magic – Alphardex.
In this paper, we will use three.js to draw a dazzling liquid crystal sphere. The following is the effect picture of the final implementation
Let’s get started!
The preparatory work
The author’s three.js template can be copied by clicking on fork in the lower right corner
To modularize shaders, glslify is needed
The following NPM packages will also be required: GLSL-Noise, GLSL-Constants
positive
Scenario building
Create a sphere
class LiquidCrystal extends Base {
constructor(sel: string, debug: boolean) {
super(sel, debug);
this.clock = new THREE.Clock();
this.cameraPosition = new THREE.Vector3(0.0.25);
this.params = {
timeScale: 0.1.iriBoost: 8}; }/ / initialization
init() {
this.createScene();
this.createPerspectiveCamera();
this.createRenderer();
this.createLiquidCrystalMaterial();
this.createSphere();
this.trackMousePos();
this.createOrbitControls();
this.addListeners();
this.setLoop();
}
// Create a liquid crystal material
createLiquidCrystalMaterial() {
const liquidCrystalMaterial = new THREE.ShaderMaterial({
vertexShader: liquidCrystalVertexShader,
fragmentShader: liquidCrystalFragmentShader,
side: THREE.DoubleSide,
uniforms: {
uTime: {
value: 0,},uResolution: {
value: new THREE.Vector2(window.innerWidth, window.innerHeight),
},
uMouse: {
value: new THREE.Vector2(0.0),},// Comment out the following lines and restore them when writing the chip shader
// uIriMap: {
// value: new ThinFilmFresnelMap(1000, 1.2, 3.2, 64),
// },
// uIriBoost: {
// value: this.params.iriBoost,
// },}});this.liquidCrystalMaterial = liquidCrystalMaterial;
}
// Create sphere
createSphere() {
const geometry = new THREE.SphereBufferGeometry(10.64.64);
const material = this.liquidCrystalMaterial;
this.createMesh({
geometry,
material,
});
}
/ / animation
update() {
const elapsedTime = this.clock.getElapsedTime();
const time = elapsedTime * this.params.timeScale;
const mousePos = this.mousePos;
if (this.liquidCrystalMaterial) {
this.liquidCrystalMaterial.uniforms.uTime.value = time;
this.liquidCrystalMaterial.uniforms.uMouse.value = mousePos; }}}Copy the code
Vertex shader
Use Simplex Noise to create a distortion effect. It’s a bit more free. You can do whatever you want, as long as it looks good
There is a point to note: after the distortion of position to correct the normal line, otherwise it will display error, foreign forumhas a better solution, directly used
#pragma glslify:snoise=require(glsl-noise/simplex/3d)
#pragma glslify:PI=require(glsl-constants/PI)
#pragma glslify:getWorldNormal=require(.. /modules/getWorldNormal)
uniform float uTime;
uniform vec2 uMouse;
varying vec2 vUv;
varying vec3 vWorldNormal;
vec3 distort(vec3 p){
vec3 pointDirection=normalize(p);
vec3 mousePoint=vec3(uMouse,1.);
vec3 mouseDirection=normalize(mousePoint);
float mousePointAngle=dot(pointDirection,mouseDirection);
float freq=1.5;
float t=uTime*100.;
float f=PI*freq;
float fc=mousePointAngle*f;
vec3 n11=pointDirection*1.5;
vec3 n12=vec3(uTime)*4.;
float dist=smoothstep(4..1.,mousePointAngle);
float n1a=dist*2.;
float noise1=snoise(n11+n12)*n1a;
vec3 n21=pointDirection*1.5;
vec3 n22=vec3(0..0.,uTime)*2.;
vec3 n23=vec3(uMouse,0.) *2.;
float n2a=8.;
float noise2=snoise(n21+n22+n23)*n2a;
float mouseN1=sin(fc+PI+t);
float mouseN2=smoothstep(f,f*2.,fc+t);
float mouseN3=smoothstep(f*2.,f,fc+t);
float mouseNa=4.;
float mouseNoise=mouseN1*mouseN2*mouseN3*mouseNa;
float noise=noise1+noise2+mouseNoise;
vec3 distortion=pointDirection*(noise+length(p));
return distortion;
}
#pragma glslify:fixNormal=require(.. /modules/fixNormal,map=distort)
void main(){
vec3 pos=position;
pos=distort(pos);
vec4 modelPosition=modelMatrix*vec4(pos,1.);
vec4 viewPosition=viewMatrix*modelPosition;
vec4 projectedPosition=projectionMatrix*viewPosition;
gl_Position=projectedPosition;
vec3 distortedNormal=fixNormal(position,pos,normal);
vUv=uv;
vWorldNormal=getWorldNormal(modelMatrix,distortedNormal).xyz;
}
Copy the code
Fixed the normal function fixnormal.glsl
#pragma glslify:orthogonal=require(./orthogonal)
vec3 fixNormal(vec3 position,vec3 distortedPosition,vec3 normal){
vec3 tangent=orthogonal(normal);
vec3 bitangent=normalize(cross(normal,tangent));
float offset=1.;
vec3 neighbour1=position+tangent*offset;
vec3 neighbour2=position+bitangent*offset;
vec3 displacedNeighbour1=map(neighbour1);
vec3 displacedNeighbour2=map(neighbour2);
vec3 displacedTangent=displacedNeighbour1-distortedPosition;
vec3 displacedBitangent=displacedNeighbour2-distortedPosition;
vec3 displacedNormal=normalize(cross(displacedTangent,displacedBitangent));
return displacedNormal;
}
#pragma glslify:export(fixNormal)
Copy the code
Orthogonal functions orthogonal. GLSL
vec3 orthogonal(vec3 v){
return normalize(abs(v.x)>abs(v.z)?vec3(-v.y,v.x,0.)
:vec3(0.,-v.z,v.y));
}
#pragma glslify:export(orthogonal);
Copy the code
Get the world normal function getWorldNormal.glsl
vec4 getWorldNormal(mat4 modelMat,vec3 normal){
vec4 worldNormal=normalize((modelMat*vec4(normal,0.)));
return worldNormal;
}
#pragma glslify:export(getWorldNormal)
Copy the code
Chip shader
Use PBR to generate lighting, plus a colorful material
Dazzlingly textures directly drag thinFilmFresnelmap.js into the LiquidCrystal class.
#pragma glslify:snoise=require(glsl-noise/simplex/3d)
#pragma glslify:invert=require(.. /modules/invert)
uniform float uTime;
uniform vec2 uMouse;
uniform vec2 uResolution;
uniform sampler2D uIriMap;
uniform float uIriBoost;
varying vec2 vUv;
varying vec3 vWorldNormal;
void main(){
vec2 newUv=vUv;
// pbr
float noise=snoise(vWorldNormal*5.) *3.;
vec3 N=normalize(vWorldNormal+vec3(noise));
vec3 V=normalize(cameraPosition);
float NdotV=max(dot(N,V),0.);
float colorStrength=smoothstep(0..8.,NdotV);
vec3 color=invert(vec3(colorStrength));
// iri
vec3 airy=texture2D(uIriMap,vec2(NdotV*99..0.)).rgb;
airy*=airy;
vec3 specularLight=vWorldNormal*airy*uIriBoost;
float mixStrength=smoothstep(3..6.,NdotV);
vec3 finalColor=mix(specularLight,color,mixStrength);
gl_FragColor=vec4(finalColor,0.);
}
Copy the code
Invert function invert. GLSL
float invert(float n){
return 1.-n;
}
vec3 invert(vec3 n){
return 1.-n;
}
#pragma glslify:export(invert)
Copy the code
The end result is as follows
The project address
Liquid Crystal