feat(All):Initial
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
/**
|
||||
* @author ErSan
|
||||
* @email mlt131220@163.com
|
||||
* @date 2025/01/07
|
||||
* @description 贴相机的下雨效果
|
||||
*/
|
||||
import * as THREE from 'three';
|
||||
import type CameraControls from 'camera-controls';
|
||||
|
||||
interface IRainOption {
|
||||
speed: number,
|
||||
color: string,
|
||||
size: number,
|
||||
radian: number,
|
||||
alpha: number,
|
||||
}
|
||||
|
||||
export default class Rain{
|
||||
options: IRainOption;
|
||||
mesh:THREE.Mesh;
|
||||
controls: CameraControls;
|
||||
|
||||
constructor(option: IRainOption, controls: CameraControls) {
|
||||
const defaultOption: IRainOption = {
|
||||
speed: 0.4,
|
||||
color: "#ffffff",
|
||||
size: 0.5,
|
||||
radian: 95 * THREE.MathUtils.DEG2RAD,
|
||||
alpha: 0.4
|
||||
};
|
||||
|
||||
this.options = Object.assign({}, defaultOption, option);
|
||||
|
||||
this.controls = controls;
|
||||
|
||||
this.mesh = this.createMesh();
|
||||
|
||||
this.updatePosition();
|
||||
}
|
||||
|
||||
createMesh(){
|
||||
const geometry = new THREE.PlaneGeometry(200, 200);
|
||||
|
||||
const uniforms = {
|
||||
u_time: {
|
||||
type: "f",
|
||||
value: 0.0
|
||||
},
|
||||
tDiffuse: { value: null },
|
||||
u_resolution: {
|
||||
type: "v2",
|
||||
value: new THREE.Vector2(window.innerWidth, window.innerHeight).multiplyScalar(window.devicePixelRatio)
|
||||
},
|
||||
alpha: {
|
||||
type: "f",
|
||||
value: this.options.alpha,
|
||||
},
|
||||
size: { value: this.options.size },
|
||||
radian: { value: this.options.radian * THREE.MathUtils.DEG2RAD },
|
||||
speed: { value: this.options.speed },
|
||||
color: { value: new THREE.Color(this.options.color) }
|
||||
};
|
||||
|
||||
const material = new THREE.ShaderMaterial({
|
||||
transparent: true,
|
||||
uniforms: uniforms,
|
||||
side: 2,
|
||||
vertexShader: `
|
||||
#define GLSLIFY 1
|
||||
varying vec2 vUv;
|
||||
void main() {
|
||||
vUv = uv;
|
||||
gl_Position = vec4( position, 1.0 );
|
||||
}
|
||||
`,
|
||||
fragmentShader: `
|
||||
uniform sampler2D tDiffuse;
|
||||
uniform vec2 u_resolution;
|
||||
uniform float u_time;
|
||||
uniform float speed;
|
||||
uniform vec3 color;
|
||||
uniform float radian;
|
||||
uniform float alpha;
|
||||
uniform float size;
|
||||
varying highp vec2 vUv;
|
||||
float hash(vec2 p){
|
||||
p = 50.0*fract( p*0.3183099 + vec2(0.71,0.113));
|
||||
return -1.0+2.0*fract( p.x*p.y*(p.x+p.y) );
|
||||
}
|
||||
float noise( in vec2 p ){
|
||||
vec2 i = floor( p );
|
||||
vec2 f = fract( p );
|
||||
vec2 u = f*f*(3.0-2.0*f);
|
||||
return mix( mix( hash( i + vec2(0.0,0.0) ),
|
||||
hash( i + vec2(1.0,0.0) ), u.x),
|
||||
mix( hash( i + vec2(0.0,1.0) ),
|
||||
hash( i + vec2(1.0,1.0) ), u.x), u.y);
|
||||
}
|
||||
|
||||
void main(){
|
||||
vec3 col=texture(tDiffuse,vUv).rgb;
|
||||
vec2 q = gl_FragCoord.xy/u_resolution.xy;
|
||||
vec2 p = -1.0+2.0*q;
|
||||
vec2 st = (p * vec2(.5, .01)+vec2(u_time)*0.05*speed)-vec2(q.y*cos(radian),0.0);
|
||||
st*= (1000.0 - size * 500.0);
|
||||
float f = noise(st) * noise(st*.773)* 1.55;
|
||||
f = clamp(pow(abs(f), 23.0) * 13.0, 0.0, q.y*.14) * 2.7;
|
||||
col += clamp(f,0.0,1.0)*color;
|
||||
gl_FragColor = vec4(col, alpha);
|
||||
}
|
||||
`
|
||||
});
|
||||
|
||||
return new THREE.Mesh(geometry, material);
|
||||
}
|
||||
|
||||
updatePosition() {
|
||||
if (this.controls && this.mesh) {
|
||||
const position = this.controls.getPosition(new THREE.Vector3());
|
||||
const center = this.controls.getTarget(new THREE.Vector3());
|
||||
this.mesh.position.copy(center);
|
||||
if (position.y < 100) {
|
||||
this.mesh.position.y = -100;
|
||||
} else {
|
||||
this.mesh.position.y = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateOptions(option) {
|
||||
const material = <THREE.ShaderMaterial>this.mesh.material;
|
||||
for (const key in option) {
|
||||
this.options[key] = option[key];
|
||||
|
||||
if (material.uniforms[key]) {
|
||||
let value = option[key];
|
||||
|
||||
switch(key){
|
||||
case "radian":
|
||||
value *= THREE.MathUtils.DEG2RAD;
|
||||
break;
|
||||
case "color":
|
||||
value = new THREE.Color(value);
|
||||
break;
|
||||
}
|
||||
material.uniforms[key].value = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
update(deltaTime){
|
||||
this.updatePosition();
|
||||
|
||||
if (this.mesh.material && this.mesh.material instanceof THREE.ShaderMaterial) {
|
||||
this.mesh.material.uniforms.u_time.value += deltaTime;
|
||||
}
|
||||
}
|
||||
|
||||
dispose(){
|
||||
this.mesh.geometry.dispose();
|
||||
(<THREE.Material>this.mesh.material).dispose();
|
||||
this.mesh.removeFromParent();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
/**
|
||||
* @author ErSan
|
||||
* @email mlt131220@163.com
|
||||
* @date 2025/01/08
|
||||
* @description 贴相机的下雪效果
|
||||
*/
|
||||
import * as THREE from 'three';
|
||||
import type CameraControls from 'camera-controls';
|
||||
|
||||
interface ISnowOption {
|
||||
size: number;
|
||||
density: number;
|
||||
speed: number;
|
||||
alpha: number;
|
||||
}
|
||||
|
||||
export default class Snow {
|
||||
options: ISnowOption;
|
||||
mesh: THREE.Mesh;
|
||||
controls: CameraControls;
|
||||
|
||||
constructor(option: ISnowOption, controls: CameraControls) {
|
||||
const defaultOption: ISnowOption = {
|
||||
size: 0.05,
|
||||
density: 1.0,
|
||||
speed: 1.0,
|
||||
alpha: 0.4,
|
||||
};
|
||||
|
||||
this.options = Object.assign({}, defaultOption, option);
|
||||
|
||||
this.controls = controls;
|
||||
|
||||
this.mesh = this.createMesh();
|
||||
this.mesh.renderOrder = 100;
|
||||
|
||||
this.updatePosition();
|
||||
}
|
||||
|
||||
createMesh() {
|
||||
const geometry = new THREE.PlaneGeometry(200, 200);
|
||||
|
||||
const uniforms = {
|
||||
iTime: {
|
||||
type: "f",
|
||||
value: 0.0
|
||||
},
|
||||
tDiffuse: { value: null },
|
||||
iResolution: {
|
||||
type: "v2",
|
||||
value: new THREE.Vector2(window.innerWidth, window.innerHeight).multiplyScalar(window.devicePixelRatio)
|
||||
},
|
||||
size: { value: this.options.size },
|
||||
speed: { value: this.options.speed },
|
||||
density: { value: this.options.density },
|
||||
alpha: {
|
||||
type: "f",
|
||||
value: this.options.alpha,
|
||||
},
|
||||
};
|
||||
|
||||
const material = new THREE.ShaderMaterial({
|
||||
transparent: true,
|
||||
uniforms: uniforms,
|
||||
side: 2,
|
||||
depthTest: false, // 禁用深度测试
|
||||
depthWrite: false, // 禁用深度写入
|
||||
blending: THREE.AdditiveBlending, // 使用叠加混合模式
|
||||
vertexShader: `
|
||||
varying highp vec2 vUv;
|
||||
|
||||
void main() {
|
||||
vUv = uv;
|
||||
|
||||
gl_Position = vec4( position, 1.0 );
|
||||
}`,
|
||||
fragmentShader: `
|
||||
#define PI 3.14159265359
|
||||
uniform sampler2D tDiffuse;
|
||||
uniform vec2 iResolution;
|
||||
uniform float iTime;
|
||||
uniform float size; // 输入的 size,显示给用户的值是实际值的十倍
|
||||
uniform float density;
|
||||
uniform float speed;
|
||||
uniform float alpha;
|
||||
varying highp vec2 vUv;
|
||||
|
||||
float ball(vec2 p) {
|
||||
float d = distance(vec2(.5), p);
|
||||
return smoothstep(size / 10.0, size / 10.0 - .05, d); // 将 size 缩小十倍
|
||||
}
|
||||
float N11(float n) {
|
||||
return fract(sin(n * 871.213) * 3134.422);
|
||||
}
|
||||
float N21(vec2 uv) {
|
||||
return N11(N11(uv.x) + uv.y);
|
||||
}
|
||||
|
||||
float snow(vec2 uv, float t) {
|
||||
vec2 org_uv = vec2(uv.x, uv.y);
|
||||
float z = 10.;
|
||||
uv.y += t * .5;
|
||||
vec2 gv = fract(uv*z);
|
||||
vec2 id = floor(uv*z);
|
||||
gv.x += (sin(N21(id) * 128. + t) * .4);
|
||||
gv.y += (sin(N11(N21(id)) * 128. + t) * .4);
|
||||
|
||||
float dots = ball(gv);
|
||||
return dots;
|
||||
}
|
||||
void main(){
|
||||
vec3 col=texture(tDiffuse,vUv).rgb;
|
||||
vec2 uv = gl_FragCoord.xy/iResolution.xy;
|
||||
uv.x *= iResolution.x / iResolution.y;
|
||||
float t = iTime * .3*speed;
|
||||
vec2 gh_uv = uv;
|
||||
|
||||
// Time varying pixel color
|
||||
vec3 colSnow = vec3(0.);
|
||||
|
||||
float m = 0.;
|
||||
|
||||
for(float i =0.; i <= 1.; i += 1. / (32.*density)) {
|
||||
float z = mix(1., .5 , i);
|
||||
vec2 offset = vec2(N11(i), N11(N11(i)));
|
||||
m += snow((uv + offset) * z, t) * .3;
|
||||
}
|
||||
|
||||
colSnow = vec3(m)*1.2;
|
||||
colSnow += col *.8 * mix(.5, 1., uv.y);
|
||||
|
||||
// 如果不是雪的像素,设置为透明
|
||||
// if (m <= 0.0) {
|
||||
// gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); // 完全透明
|
||||
// } else {
|
||||
// gl_FragColor = vec4(colSnow, alpha); // 雪的像素保持原有颜色和透明度
|
||||
// }
|
||||
|
||||
gl_FragColor = vec4(colSnow,alpha);
|
||||
}`
|
||||
});
|
||||
|
||||
return new THREE.Mesh(geometry, material);
|
||||
}
|
||||
|
||||
updatePosition() {
|
||||
if (this.controls && this.mesh) {
|
||||
const position = this.controls.getPosition(new THREE.Vector3());
|
||||
const center = this.controls.getTarget(new THREE.Vector3());
|
||||
|
||||
if (this.mesh.position.x === center.x && this.mesh.position.z === center.z) return;
|
||||
|
||||
this.mesh.position.copy(center);
|
||||
if (position.y < 100) {
|
||||
this.mesh.position.y = -100;
|
||||
} else {
|
||||
this.mesh.position.y = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateOptions(option) {
|
||||
const material = <THREE.ShaderMaterial>this.mesh.material;
|
||||
for (const key in option) {
|
||||
this.options[key] = option[key];
|
||||
|
||||
if (material.uniforms[key]) {
|
||||
material.uniforms[key].value = option[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
update(deltaTime) {
|
||||
this.updatePosition();
|
||||
|
||||
if (this.mesh.material && this.mesh.material instanceof THREE.ShaderMaterial) {
|
||||
this.mesh.material.uniforms.iTime.value += deltaTime;
|
||||
}
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.mesh.geometry.dispose();
|
||||
(<THREE.Material>this.mesh.material).dispose();
|
||||
this.mesh.removeFromParent();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user