来源网站
https://www.shadertoy.com/view/4sccWr
效果预览
代码演示
将shadertory上的代码转成了threejs可以直接用的代码,引入文件的material,并在创建mesh或已有物体上使用material即可,使用时请注意uv对齐。
import { DoubleSide, ShaderChunk, ShaderMaterial } from "three";
//source: https://www.shadertoy.com/view/4sccWr
// Into You - by Martijn Steinrucken aka BigWings - 2018
// Email:countfrolic@gmail.com Twitter:@The_ArtOfCode
// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.// You can change the focal plane by moving the mouse up and down.// A video of the effect can be found here:
// https://www.youtube.com/watch?v=lrMiME82Cuk// Making of video part1:
// https://www.youtube.com/watch?v=dXyPOLf2MbU// Android Phone Wallpaper:
// https://play.google.com/store/apps/details?id=com.TheArtOfCode.FallingHearts// Music - Novo Amor - Anchor
// https://soundcloud.com/mrsuicidesheep/novo-amor-anchorconst vertex = `
${ShaderChunk.logdepthbuf_pars_vertex}
bool isPerspectiveMatrix(mat4) {return true;
}varying vec4 m_pos;
varying vec2 vUv;void main () {vUv = uv;// 从贴图中采样颜色值vec3 newPosition = normal*vec3(0,0,0)+position;gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);${ShaderChunk.logdepthbuf_vertex}
}`;// 片元着色器代码
const fragment = /*glsl*/`
${ShaderChunk.logdepthbuf_pars_fragment}
precision mediump float;
varying vec2 vUv;
uniform float uTime;
uniform float uSpeed;#define S(a, b, t) smoothstep(a, b, t)
#define sat(x) clamp(x, 0., 1.)
#define HEARTCOL vec3(1., .01, .01)
#define NUM_HEARTS 50.
#define LIGHT_DIR vec3(.577, -.577, -.577)// Polynomial smooth max from IQ
float smax( float a, float b, float k ) {float h = sat( .5 + .5*(b-a)/k );return mix( a, b, h ) + k*h*(1.-h);
}
// Quaternion rotation functions from Ollj
vec4 qmulq(vec4 q1, vec4 q2){return vec4(q1.xyz*q2.w+q2.xyz*q1.w+cross(q1.xyz,q2.xyz),(q1.w*q2.w)-dot(q1.xyz,q2.xyz));}
vec4 aa2q(vec3 axis, float angle){return vec4(normalize(axis)*sin(angle*0.5),cos(angle*0.5));}
vec4 qinv(vec4 q){return vec4(-q.xyz,q.w)/dot(q,q);}
vec3 qmulv(vec4 q, vec3 p){return qmulq(q,qmulq(vec4(p,.0),qinv(q))).xyz;}vec2 RaySphere(vec3 rd, vec3 p) {float l = dot(rd, p);float det = l*l - dot(p, p) + 1.;if (det < 0.) return vec2(-1);float sd = sqrt(det);return vec2(l - sd, l+sd);
}struct sphereInfo {vec3 p1, p2, n1, n2;vec2 uv1, uv2;
};sphereInfo GetSphereUvs(vec3 rd, vec2 i, vec2 rot, vec3 s) {sphereInfo res;rot *= 6.2831;vec4 q = aa2q(vec3(cos(rot.x),sin(rot.x),0), rot.y);vec3 o = qmulv(q, -s)+s;vec3 d = qmulv(q, rd);res.p1 = rd*i.x;vec3 p = o+d*i.x-s;res.uv1 = vec2(atan(p.x, p.z), p.y);res.n1 = res.p1-s;res.p2 = rd*i.y;p = o+d*i.y-s;res.uv2 = vec2(atan(p.x, p.z), p.y);res.n2 = s-res.p2;return res;
}float Heart(vec2 uv, float b) {uv.x*=.5;float shape = smax(sqrt(abs(uv.x)), b, .3*b)*.5;uv.y -= shape*(1.-b);return S(b, -b, length(uv)-.5);
}vec4 HeartBall(vec3 rd, vec3 p, vec2 rot, float t, float blur) {vec2 d = RaySphere(rd, p);vec4 col = vec4(0);if(d.x>0.) {sphereInfo info = GetSphereUvs(rd, d, rot, p);float sd = length(cross(p, rd));float edge = S(1., mix(1., 0.1, blur), sd);float backMask = Heart(info.uv2, blur)*edge; float frontMask = Heart(info.uv1, blur)*edge; float frontLight = sat(dot(LIGHT_DIR, info.n1)*.8+.2);float backLight = sat(dot(LIGHT_DIR, info.n2)*.8+.2)*.9;col = mix(vec4(backLight*HEARTCOL, backMask), vec4(frontLight*HEARTCOL, frontMask), frontMask);}return col;
}void main() {vec2 uv = vUv;uv-=.5;vec2 m =vec2(.5);float t = uTime*.3*uSpeed;vec3 rd = normalize(vec3(uv, 1));// m.y = iMouse.z>0. ? 1.-m.y : .4;vec2 rot = t*vec2(.12, .18);vec4 col = vec4(0);for(float i=0.; i<1.; i+=(1./NUM_HEARTS)) {float x = (fract(cos(i*536.3)*7464.4)-.5)*15.;float y = (fract(-t*.2+i*7.64)-.5)*15.;float z = mix(14., 2., i);float blur = mix(.03, .35, S(.0, .4, abs(m.y-i)));rot += (fract(sin(i*vec2(536.3, 23.4))*vec2(764.4, 987.3))-.5);vec4 heart = HeartBall(rd, vec3(x, y, z), rot, t, blur);col = mix(col, heart, heart.a);}gl_FragColor = vec4(col);${ShaderChunk.logdepthbuf_fragment}
}`;const uniforms = {uTime: { value: 1.0 },
};
const CircleGridShaderMaterial = new ShaderMaterial({uniforms: {uTime: { value: 1.0 },uSpeed: { value: 1.0, max: 20, min: 0.1 }},vertexShader: vertex,fragmentShader: fragment,side: DoubleSide,transparent: true,
});const loop = () => {requestAnimationFrame(loop)CircleGridShaderMaterial.uniforms.uTime.value += .001
}
loop()
setInterval(() => {// CircleGridShaderMaterial.uniforms.uTime.value += .0001
}, 5000);
export default CircleGridShaderMaterial
页面展示
flowHeart