Жидкая маска

Видеоинструкция по подключению
ID Шаблона
Категория
Последнее обновление
20 августа, 2026
В подписке, Эффекты, Three.js
183582009
Скриншоты шаблона

Пошаговая инструкция с подключением кода

Шаг 1
В Zero-блоке создаем HTML-блок и добавляем часть кода. Код будет в конце страницы
Шаг 2
В конце страницы или футере добавляем остальной код, через блок T123.
CSS / JS

<!--Жидкая маска-->
<!--https://mt-webdesign.ru/fluid-->
<!--перенести в zero-блок-->
<div class="mt-fluid-reveal">
<canvas class="mt-fluid-reveal__canvas"></canvas>
</div>
<style>
/*меняем высоту для холста*/
:root {
--mt-fluid-height-1200: 650px;
--mt-fluid-height-960: 650px;
--mt-fluid-height-640: 650px;
--mt-fluid-height-480: 660px;
--mt-fluid-height-320: 580px;
}
.mt-fluid-reveal {
position: relative;
width: 100%;
height: var(--mt-fluid-height-1200);
min-height: var(--mt-fluid-height-1200);
overflow: hidden;
background: #ffffff;
}
@media (max-width: 1199px) {
.mt-fluid-reveal {
height: var(--mt-fluid-height-960);
min-height: var(--mt-fluid-height-960);
}
}
@media (max-width: 959px) {
.mt-fluid-reveal {
height: var(--mt-fluid-height-640);
min-height: var(--mt-fluid-height-640);
}
}
@media (max-width: 639px) {
.mt-fluid-reveal {
height: var(--mt-fluid-height-480);
min-height: var(--mt-fluid-height-480);
}
}
@media (max-width: 479px) {
.mt-fluid-reveal {
height: var(--mt-fluid-height-320);
min-height: var(--mt-fluid-height-320);
}
}
.mt-fluid-reveal__canvas {
position: absolute;
inset: 0;
display: block;
width: 100%;
height: 100%;
pointer-events: none;
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/0.160.1/three.min.js"></script>
<script>
(function () {
const container =
document.querySelector('.mt-fluid-reveal');
const canvas =
document.querySelector('.mt-fluid-reveal__canvas');
if (
!container ||
!canvas ||
!window.THREE
) {
return;
}
// загрузить изображения в зависимости от разрешения
const IMAGE_SETS = {
desktop1200: {
base:
'https://static.tildacdn.com/tild3234-6134-4965-b431-313935656161/1200-1.jpg',
reveal:
'https://static.tildacdn.com/tild6538-6464-4831-a136-376464643562/1200-2.jpg'
},
desktop960: {
base:
'https://static.tildacdn.com/tild3634-3461-4166-b834-333938666664/960-1.jpg',
reveal:
'https://static.tildacdn.com/tild6566-6265-4432-b639-366539643039/960-2.jpg'
},
tablet640: {
base:
'https://static.tildacdn.com/tild6639-3936-4362-b665-383866373733/640-1.jpg',
reveal:
'https://static.tildacdn.com/tild3765-3337-4862-b730-613966653261/640-2.jpg'
},
mobile480: {
base:
'https://static.tildacdn.com/tild6362-6538-4163-b235-346630353862/480-1.jpg',
reveal:
'https://static.tildacdn.com/tild6264-3565-4063-a464-373039653332/480-2.jpg'
},
mobile320: {
base:
'https://static.tildacdn.com/tild3565-3034-4637-a338-376261313032/375-1.jpg',
reveal:
'https://static.tildacdn.com/tild3930-3364-4935-a136-343366356438/375-2.jpg'
}
};
function getImageSetKey() {
const w =
window.innerWidth;
if (w >= 1200) {
return 'desktop1200';
}
if (w >= 960) {
return 'desktop960';
}
if (w >= 640) {
return 'tablet640';
}
if (w >= 480) {
return 'mobile480';
}
return 'mobile320';
}
const settings = {
simResolution: 256,
dyeResolution: 512,
velocityDissipation: 0.962,
dyeDissipation: 0.988,
pressureIterations: 20,
curlStrength: 0,
splatRadius: 0.00075,
splatForce: 5900,
revealSize: 3.9,
edgeSoftness: 0.5,
edgeWidth: 0.01
};
const renderer =
new THREE.WebGLRenderer({
canvas: canvas,
antialias: false,
alpha: false,
premultipliedAlpha: false,
powerPreference:
'high-performance'
});
renderer.setPixelRatio(
Math.min(
window.devicePixelRatio || 1,
2
)
);
renderer.setClearColor(
0x000000,
1
);
renderer.autoClear = false;
const simScene =
new THREE.Scene();
const simCamera =
new THREE.OrthographicCamera(
-1,
1,
1,
-1,
0,
1
);
const simGeometry =
new THREE.PlaneGeometry(
2,
2
);
const simMesh =
new THREE.Mesh(
simGeometry
);
simScene.add(
simMesh
);
const finalScene =
new THREE.Scene();
const finalCamera =
new THREE.OrthographicCamera(
-1,
1,
1,
-1,
0,
1
);
const finalGeometry =
new THREE.PlaneGeometry(
2,
2
);
function createRenderTarget(
width,
height,
filter
) {
return new THREE.WebGLRenderTarget(
width,
height,
{
minFilter:
filter,
magFilter:
filter,
format:
THREE.RGBAFormat,
type:
THREE.HalfFloatType,
depthBuffer:
false,
stencilBuffer:
false
}
);
}
function createDoubleFBO(
width,
height,
filter
) {
return {
read:
createRenderTarget(
width,
height,
filter
),
write:
createRenderTarget(
width,
height,
filter
),
swap() {
const temp =
this.read;
this.read =
this.write;
this.write =
temp;
}
};
}
const velocity =
createDoubleFBO(
settings.simResolution,
settings.simResolution,
THREE.LinearFilter
);
const dye =
createDoubleFBO(
settings.dyeResolution,
settings.dyeResolution,
THREE.LinearFilter
);
const pressure =
createDoubleFBO(
settings.simResolution,
settings.simResolution,
THREE.NearestFilter
);
const divergence =
createRenderTarget(
settings.simResolution,
settings.simResolution,
THREE.NearestFilter
);
const curl =
createRenderTarget(
settings.simResolution,
settings.simResolution,
THREE.NearestFilter
);
const simTexelSize =
new THREE.Vector2(
1 /
settings.simResolution,
1 /
settings.simResolution
);
const dyeTexelSize =
new THREE.Vector2(
1 /
settings.dyeResolution,
1 /
settings.dyeResolution
);
const fullscreenVertex = `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position =
vec4(
position.xy,
0.0,
1.0
);
}
`;
const advectionFragment = `
precision highp float;
uniform sampler2D uVelocity;
uniform sampler2D uSource;
uniform vec2 uTexelSize;
uniform float uDt;
uniform float uDissipation;
varying vec2 vUv;
vec4 bilerp(
sampler2D sam,
vec2 uv,
vec2 tsize
) {
vec2 st =
uv /
tsize -
0.5;
vec2 iuv =
floor(st);
vec2 fuv =
fract(st);
vec4 a =
texture2D(
sam,
(
iuv +
vec2(
0.5,
0.5
)
)
*
tsize
);
vec4 b =
texture2D(
sam,
(
iuv +
vec2(
1.5,
0.5
)
)
*
tsize
);
vec4 c =
texture2D(
sam,
(
iuv +
vec2(
0.5,
1.5
)
)
*
tsize
);
vec4 d =
texture2D(
sam,
(
iuv +
vec2(
1.5,
1.5
)
)
*
tsize
);
return
mix(
mix(
a,
b,
fuv.x
),
mix(
c,
d,
fuv.x
),
fuv.y
);
}
void main() {
vec2 coord =
vUv -
uDt *
texture2D(
uVelocity,
vUv
).xy *
uTexelSize;
vec4 result =
uDissipation *
bilerp(
uSource,
coord,
uTexelSize
);
gl_FragColor =
result;
}
`;
const splatFragment = `
precision highp float;
uniform sampler2D uTarget;
uniform float uAspectRatio;
uniform vec2 uPoint;
uniform vec3 uColor;
uniform float uRadius;
varying vec2 vUv;
void main() {
vec2 p =
vUv -
uPoint;
p.x *=
uAspectRatio;
vec3 splat =
exp(
-dot(
p,
p
)
/
uRadius
)
*
uColor;
vec3 base =
texture2D(
uTarget,
vUv
).xyz;
gl_FragColor =
vec4(
base +
splat,
1.0
);
}
`;
const curlFragment = `
precision highp float;
uniform sampler2D uVelocity;
uniform vec2 uTexelSize;
varying vec2 vUv;
void main() {
float L =
texture2D(
uVelocity,
vUv -
vec2(
uTexelSize.x,
0.0
)
).y;
float R =
texture2D(
uVelocity,
vUv +
vec2(
uTexelSize.x,
0.0
)
).y;
float T =
texture2D(
uVelocity,
vUv +
vec2(
0.0,
uTexelSize.y
)
).x;
float B =
texture2D(
uVelocity,
vUv -
vec2(
0.0,
uTexelSize.y
)
).x;
float vorticity =
R -
L -
T +
B;
gl_FragColor =
vec4(
0.5 *
vorticity,
0.0,
0.0,
1.0
);
}
`;
const vorticityFragment = `
precision highp float;
uniform sampler2D uVelocity;
uniform sampler2D uCurl;
uniform vec2 uTexelSize;
uniform float uCurlStrength;
uniform float uDt;
varying vec2 vUv;
void main() {
float L =
texture2D(
uCurl,
vUv -
vec2(
uTexelSize.x,
0.0
)
).x;
float R =
texture2D(
uCurl,
vUv +
vec2(
uTexelSize.x,
0.0
)
).x;
float T =
texture2D(
uCurl,
vUv +
vec2(
0.0,
uTexelSize.y
)
).x;
float B =
texture2D(
uCurl,
vUv -
vec2(
0.0,
uTexelSize.y
)
).x;
float C =
texture2D(
uCurl,
vUv
).x;
vec2 force =
0.5 *
vec2(
abs(T) -
abs(B),
abs(R) -
abs(L)
);
float len =
length(force) +
0.0001;
force =
force /
len *
uCurlStrength *
C;
vec2 vel =
texture2D(
uVelocity,
vUv
).xy;
vel +=
force *
uDt;
gl_FragColor =
vec4(
vel,
0.0,
1.0
);
}
`;
const divergenceFragment = `
precision highp float;
uniform sampler2D uVelocity;
uniform vec2 uTexelSize;
varying vec2 vUv;
void main() {
float L =
texture2D(
uVelocity,
vUv -
vec2(
uTexelSize.x,
0.0
)
).x;
float R =
texture2D(
uVelocity,
vUv +
vec2(
uTexelSize.x,
0.0
)
).x;
float T =
texture2D(
uVelocity,
vUv +
vec2(
0.0,
uTexelSize.y
)
).y;
float B =
texture2D(
uVelocity,
vUv -
vec2(
0.0,
uTexelSize.y
)
).y;
float div =
0.5 *
(
R -
L +
T -
B
);
gl_FragColor =
vec4(
div,
0.0,
0.0,
1.0
);
}
`;
const pressureFragment = `
precision highp float;
uniform sampler2D uPressure;
uniform sampler2D uDivergence;
uniform vec2 uTexelSize;
varying vec2 vUv;
void main() {
float L =
texture2D(
uPressure,
vUv -
vec2(
uTexelSize.x,
0.0
)
).x;
float R =
texture2D(
uPressure,
vUv +
vec2(
uTexelSize.x,
0.0
)
).x;
float T =
texture2D(
uPressure,
vUv +
vec2(
0.0,
uTexelSize.y
)
).x;
float B =
texture2D(
uPressure,
vUv -
vec2(
0.0,
uTexelSize.y
)
).x;
float C =
texture2D(
uDivergence,
vUv
).x;
float pressure =
(
L +
R +
B +
T -
C
)
*
0.25;
gl_FragColor =
vec4(
pressure,
0.0,
0.0,
1.0
);
}
`;
const gradientFragment = `
precision highp float;
uniform sampler2D uPressure;
uniform sampler2D uVelocity;
uniform vec2 uTexelSize;
varying vec2 vUv;
void main() {
float L =
texture2D(
uPressure,
vUv -
vec2(
uTexelSize.x,
0.0
)
).x;
float R =
texture2D(
uPressure,
vUv +
vec2(
uTexelSize.x,
0.0
)
).x;
float T =
texture2D(
uPressure,
vUv +
vec2(
0.0,
uTexelSize.y
)
).x;
float B =
texture2D(
uPressure,
vUv -
vec2(
0.0,
uTexelSize.y
)
).x;
vec2 velocity =
texture2D(
uVelocity,
vUv
).xy;
velocity -=
vec2(
R -
L,
T -
B
)
*
0.5;
gl_FragColor =
vec4(
velocity,
0.0,
1.0
);
}
`;
function makeMaterial(
fragmentShader,
uniforms
) {
return new THREE.ShaderMaterial({
vertexShader:
fullscreenVertex,
fragmentShader:
fragmentShader,
uniforms:
uniforms,
depthTest:
false,
depthWrite:
false
});
}
const curlMaterial =
makeMaterial(
curlFragment,
{
uVelocity: {
value: null
},
uTexelSize: {
value:
simTexelSize
}
}
);
const vorticityMaterial =
makeMaterial(
vorticityFragment,
{
uVelocity: {
value: null
},
uCurl: {
value: null
},
uTexelSize: {
value:
simTexelSize
},
uCurlStrength: {
value:
settings.curlStrength
},
uDt: {
value:
0.016
}
}
);
const advectionMaterial =
makeMaterial(
advectionFragment,
{
uVelocity: {
value: null
},
uSource: {
value: null
},
uTexelSize: {
value:
simTexelSize
},
uDt: {
value:
1
},
uDissipation: {
value:
settings.velocityDissipation
}
}
);
const splatMaterial =
makeMaterial(
splatFragment,
{
uTarget: {
value: null
},
uAspectRatio: {
value:
1
},
uPoint: {
value:
new THREE.Vector2()
},
uColor: {
value:
new THREE.Vector3()
},
uRadius: {
value:
settings.splatRadius
}
}
);
const divergenceMaterial =
makeMaterial(
divergenceFragment,
{
uVelocity: {
value: null
},
uTexelSize: {
value:
simTexelSize
}
}
);
const pressureMaterial =
makeMaterial(
pressureFragment,
{
uPressure: {
value: null
},
uDivergence: {
value: null
},
uTexelSize: {
value:
simTexelSize
}
}
);
const gradientMaterial =
makeMaterial(
gradientFragment,
{
uPressure: {
value: null
},
uVelocity: {
value: null
},
uTexelSize: {
value:
simTexelSize
}
}
);
const finalFragment = `
precision highp float;
uniform sampler2D uBaseTexture;
uniform sampler2D uRevealTexture;
uniform sampler2D uDye;
uniform float uRevealSize;
uniform float uEdgeSoftness;
uniform float uEdgeWidth;
uniform float uBaseImageAspect;
uniform float uRevealImageAspect;
uniform float uPlaneAspect;
varying vec2 vUv;
vec2 coverUv(
vec2 uv,
float imageAspect,
float planeAspect
) {
vec2 scale =
vec2(1.0);
if (
planeAspect >
imageAspect
) {
scale.y =
imageAspect /
planeAspect;
}
else {
scale.x =
planeAspect /
imageAspect;
}
return
(
uv -
0.5
)
*
scale
+
0.5;
}
void main() {
float dye =
texture2D(
uDye,
vUv
).r;
float raw =
dye *
uRevealSize;
float mask =
smoothstep(
uEdgeSoftness,
uEdgeSoftness +
uEdgeWidth,
raw
);
mask =
clamp(
mask,
0.0,
1.0
);
vec2 baseUv =
coverUv(
vUv,
uBaseImageAspect,
uPlaneAspect
);
baseUv =
clamp(
baseUv,
0.001,
0.999
);
vec4 baseImage =
texture2D(
uBaseTexture,
baseUv
);
vec2 revealUv =
coverUv(
vUv,
uRevealImageAspect,
uPlaneAspect
);
revealUv =
clamp(
revealUv,
0.001,
0.999
);
vec4 revealImage =
texture2D(
uRevealTexture,
revealUv
);
gl_FragColor =
mix(
baseImage,
revealImage,
mask
);
}
`;
const textureLoader =
new THREE.TextureLoader();
textureLoader.setCrossOrigin(
'anonymous'
);
let baseAspect =
16 / 9;
let revealAspect =
16 / 9;
let finalMaterial =
null;
let currentImageSetKey =
getImageSetKey();
let baseTexture =
null;
let revealTexture =
null;
function prepareTexture(
texture
) {
texture.minFilter =
THREE.LinearFilter;
texture.magFilter =
THREE.LinearFilter;
texture.wrapS =
THREE.ClampToEdgeWrapping;
texture.wrapT =
THREE.ClampToEdgeWrapping;
return texture;
}
function loadTexture(
url,
onReady
) {
return prepareTexture(
textureLoader.load(
url,
function (texture) {
const image =
texture.image;
if (
image &&
image.width &&
image.height
) {
onReady(
texture,
image.width /
image.height
);
}
}
)
);
}
function loadImageSet(
key,
initial = false
) {
const set =
IMAGE_SETS[key];
if (!set) {
return;
}
const oldBaseTexture =
baseTexture;
const oldRevealTexture =
revealTexture;
const nextBaseTexture =
loadTexture(
set.base,
function (
texture,
aspect
) {
baseAspect =
aspect;
if (
finalMaterial &&
currentImageSetKey === key
) {
finalMaterial
.uniforms
.uBaseTexture
.value =
texture;
finalMaterial
.uniforms
.uBaseImageAspect
.value =
baseAspect;
}
}
);
const nextRevealTexture =
loadTexture(
set.reveal,
function (
texture,
aspect
) {
revealAspect =
aspect;
if (
finalMaterial &&
currentImageSetKey === key
) {
finalMaterial
.uniforms
.uRevealTexture
.value =
texture;
finalMaterial
.uniforms
.uRevealImageAspect
.value =
revealAspect;
}
}
);
baseTexture =
nextBaseTexture;
revealTexture =
nextRevealTexture;
if (
finalMaterial &&
!initial
) {
finalMaterial
.uniforms
.uBaseTexture
.value =
baseTexture;
finalMaterial
.uniforms
.uRevealTexture
.value =
revealTexture;
}
if (
oldBaseTexture &&
oldBaseTexture !== baseTexture
) {
setTimeout(
function () {
oldBaseTexture.dispose();
},
1000
);
}
if (
oldRevealTexture &&
oldRevealTexture !== revealTexture
) {
setTimeout(
function () {
oldRevealTexture.dispose();
},
1000
);
}
}
loadImageSet(
currentImageSetKey,
true
);
finalMaterial =
new THREE.ShaderMaterial({
vertexShader:
fullscreenVertex,
fragmentShader:
finalFragment,
depthTest:
false,
depthWrite:
false,
uniforms: {
uBaseTexture: {
value:
baseTexture
},
uRevealTexture: {
value:
revealTexture
},
uDye: {
value:
dye.read.texture
},
uRevealSize: {
value:
settings.revealSize
},
uEdgeSoftness: {
value:
settings.edgeSoftness
},
uEdgeWidth: {
value:
settings.edgeWidth
},
uBaseImageAspect: {
value:
baseAspect
},
uRevealImageAspect: {
value:
revealAspect
},
uPlaneAspect: {
value:
1
}
}
});
const finalMesh =
new THREE.Mesh(
finalGeometry,
finalMaterial
);
finalScene.add(
finalMesh
);
function renderPass(
material,
target
) {
simMesh.material =
material;
renderer.setRenderTarget(
target
);
renderer.render(
simScene,
simCamera
);
}
const mouse =
new THREE.Vector2(
0.5,
0.5
);
const previousMouse =
new THREE.Vector2(
0.5,
0.5
);
let mouseMoved =
false;
let pointerInitialized =
false;
function pointerMove(
event
) {
const rect =
canvas.getBoundingClientRect();
if (
event.clientX <
rect.left ||
event.clientX >
rect.right ||
event.clientY <
rect.top ||
event.clientY >
rect.bottom
) {
return;
}
mouse.x =
(
event.clientX -
rect.left
)
/
rect.width;
mouse.y =
1 -
(
event.clientY -
rect.top
)
/
rect.height;
if (!pointerInitialized) {
previousMouse.copy(
mouse
);
pointerInitialized =
true;
return;
}
mouseMoved =
true;
}
window.addEventListener(
'mousemove',
pointerMove,
{
passive: true
}
);
window.addEventListener(
'touchmove',
function (event) {
if (
!event.touches.length
) {
return;
}
const touch =
event.touches[0];
pointerMove(
touch
);
},
{
passive: true
}
);
let width =
1;
let height =
1;
function resize() {
const nextImageSetKey =
getImageSetKey();
if (
nextImageSetKey !==
currentImageSetKey
) {
currentImageSetKey =
nextImageSetKey;
loadImageSet(
currentImageSetKey
);
}
const rect =
container.getBoundingClientRect();
width =
Math.max(
1,
rect.width
);
height =
Math.max(
1,
rect.height
);
renderer.setSize(
width,
height,
false
);
finalMaterial
.uniforms
.uPlaneAspect
.value =
width /
height;
finalMaterial
.uniforms
.uBaseImageAspect
.value =
baseAspect;
finalMaterial
.uniforms
.uRevealImageAspect
.value =
revealAspect;
}
resize();
window.addEventListener(
'resize',
resize
);
if (
typeof ResizeObserver !==
'undefined'
) {
const observer =
new ResizeObserver(
resize
);
observer.observe(
container
);
}
function clearTarget(
target
) {
renderer.setRenderTarget(
target
);
renderer.setClearColor(
0x000000,
0
);
renderer.clear();
}
clearTarget(
velocity.read
);
clearTarget(
velocity.write
);
clearTarget(
dye.read
);
clearTarget(
dye.write
);
clearTarget(
pressure.read
);
clearTarget(
pressure.write
);
clearTarget(
divergence
);
clearTarget(
curl
);
renderer.setRenderTarget(
null
);
function step() {
const aspectRatio =
width /
height;
if (
mouseMoved &&
pointerInitialized
) {
const dx =
mouse.x -
previousMouse.x;
const dy =
mouse.y -
previousMouse.y;
const distance =
Math.sqrt(
dx *
dx +
dy *
dy
);
if (
distance >
0
) {
splatMaterial
.uniforms
.uTarget
.value =
velocity
.read
.texture;
splatMaterial
.uniforms
.uAspectRatio
.value =
aspectRatio;
splatMaterial
.uniforms
.uPoint
.value
.set(
mouse.x,
mouse.y
);
splatMaterial
.uniforms
.uColor
.value
.set(
dx *
settings.splatForce,
dy *
settings.splatForce,
0
);
splatMaterial
.uniforms
.uRadius
.value =
settings.splatRadius;
renderPass(
splatMaterial,
velocity.write
);
velocity.swap();
splatMaterial
.uniforms
.uTarget
.value =
dye
.read
.texture;
splatMaterial
.uniforms
.uColor
.value
.set(
1,
1,
1
);
splatMaterial
.uniforms
.uRadius
.value =
settings.splatRadius;
renderPass(
splatMaterial,
dye.write
);
dye.swap();
}
previousMouse.copy(
mouse
);
mouseMoved =
false;
}
curlMaterial
.uniforms
.uVelocity
.value =
velocity
.read
.texture;
renderPass(
curlMaterial,
curl
);
vorticityMaterial
.uniforms
.uVelocity
.value =
velocity
.read
.texture;
vorticityMaterial
.uniforms
.uCurl
.value =
curl.texture;
vorticityMaterial
.uniforms
.uCurlStrength
.value =
settings.curlStrength;
renderPass(
vorticityMaterial,
velocity.write
);
velocity.swap();
advectionMaterial
.uniforms
.uVelocity
.value =
velocity
.read
.texture;
advectionMaterial
.uniforms
.uSource
.value =
velocity
.read
.texture;
advectionMaterial
.uniforms
.uTexelSize
.value =
simTexelSize;
advectionMaterial
.uniforms
.uDissipation
.value =
settings
.velocityDissipation;
renderPass(
advectionMaterial,
velocity.write
);
velocity.swap();
advectionMaterial
.uniforms
.uVelocity
.value =
velocity
.read
.texture;
advectionMaterial
.uniforms
.uSource
.value =
dye
.read
.texture;
advectionMaterial
.uniforms
.uTexelSize
.value =
dyeTexelSize;
advectionMaterial
.uniforms
.uDissipation
.value =
settings
.dyeDissipation;
renderPass(
advectionMaterial,
dye.write
);
dye.swap();
divergenceMaterial
.uniforms
.uVelocity
.value =
velocity
.read
.texture;
renderPass(
divergenceMaterial,
divergence
);
renderer.setRenderTarget(
pressure.read
);
renderer.setClearColor(
0x000000,
0
);
renderer.clear();
pressureMaterial
.uniforms
.uDivergence
.value =
divergence.texture;
for (
let i = 0;
i <
settings.pressureIterations;
i++
) {
pressureMaterial
.uniforms
.uPressure
.value =
pressure
.read
.texture;
renderPass(
pressureMaterial,
pressure.write
);
pressure.swap();
}
gradientMaterial
.uniforms
.uPressure
.value =
pressure
.read
.texture;
gradientMaterial
.uniforms
.uVelocity
.value =
velocity
.read
.texture;
renderPass(
gradientMaterial,
velocity.write
);
velocity.swap();
finalMaterial
.uniforms
.uDye
.value =
dye
.read
.texture;
finalMaterial
.uniforms
.uRevealSize
.value =
settings.revealSize;
finalMaterial
.uniforms
.uEdgeSoftness
.value =
settings.edgeSoftness;
finalMaterial
.uniforms
.uEdgeWidth
.value =
settings.edgeWidth;
finalMaterial
.uniforms
.uBaseImageAspect
.value =
baseAspect;
finalMaterial
.uniforms
.uRevealImageAspect
.value =
revealAspect;
renderer.setRenderTarget(
null
);
renderer.setClearColor(
0x000000,
1
);
renderer.clear();
renderer.render(
finalScene,
finalCamera
);
}
function animate() {
requestAnimationFrame(
animate
);
step();
}
animate();
})();
</script>
Шаг 3
В коде заменяем изображения на свои для каждого разрешения, а также настраиваем высоту холста под нужный размер.

Более подробные настройки, возможности взаимодействия и подключение в видео

Инструкция с копированием шаблона к себе

Создаём новую страницу на сайте
Шаг 1
Скроллим в самый низ и нажимаем кнопку «Указать ID шаблона»
Шаг 2
В графе указываем номер шаблона (ID Шаблона), нажимаем кнопку «Выбрать»
Шаг 3