feat(about): widen and fade the river
This commit is contained in:
@@ -12,15 +12,10 @@ import {
|
|||||||
} from './scenePrimitives';
|
} from './scenePrimitives';
|
||||||
import styles from './Scenes.module.css';
|
import styles from './Scenes.module.css';
|
||||||
|
|
||||||
// Sharp 90-degree orthogonal isometric river path:
|
// Straight isometric river running between the two platform groups.
|
||||||
// Enters from bottom-left along the isometric Z axis,
|
|
||||||
// takes a sharp 90-degree orthogonal step across the isometric X axis at the center,
|
|
||||||
// and turns 90 degrees to continue along the isometric Z axis towards the top-right.
|
|
||||||
const riverPath = [
|
const riverPath = [
|
||||||
new THREE.Vector2(-1.3, -16),
|
new THREE.Vector2(0, -20),
|
||||||
new THREE.Vector2(-1.3, 0),
|
new THREE.Vector2(0, 20)
|
||||||
new THREE.Vector2(1.3, 0),
|
|
||||||
new THREE.Vector2(1.3, 16),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const createAboutScene: SceneFactory = ({ pointer, theme, reduceMotion }) => {
|
const createAboutScene: SceneFactory = ({ pointer, theme, reduceMotion }) => {
|
||||||
@@ -111,19 +106,96 @@ const createAboutScene: SceneFactory = ({ pointer, theme, reduceMotion }) => {
|
|||||||
addPlate(secondaryPlates, x, z, width, depth, 0.5, theme === 'dark' ? 0x0b2f5f : 0xe6f0fe),
|
addPlate(secondaryPlates, x, z, width, depth, 0.5, theme === 'dark' ? 0x0b2f5f : 0xe6f0fe),
|
||||||
);
|
);
|
||||||
|
|
||||||
const riverGeometry = new THREE.ShapeGeometry(createRibbonShape(riverPath, 1.8));
|
const riverGeometry = new THREE.ShapeGeometry(createRibbonShape(riverPath, 4.5));
|
||||||
|
const riverEndFade = 0.16;
|
||||||
|
const fadeTextureSize = 256;
|
||||||
|
const fadeTextureData = new Uint8Array(fadeTextureSize * 4);
|
||||||
|
|
||||||
|
for (let index = 0; index < fadeTextureSize; index += 1) {
|
||||||
|
const progress = index / (fadeTextureSize - 1);
|
||||||
|
const distanceFromEnd = Math.min(progress, 1 - progress);
|
||||||
|
const opacity = Math.round(255 * Math.min(1, distanceFromEnd / riverEndFade));
|
||||||
|
const offset = index * 4;
|
||||||
|
|
||||||
|
fadeTextureData[offset] = opacity;
|
||||||
|
fadeTextureData[offset + 1] = opacity;
|
||||||
|
fadeTextureData[offset + 2] = opacity;
|
||||||
|
fadeTextureData[offset + 3] = 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
const riverFadeTexture = new THREE.DataTexture(
|
||||||
|
fadeTextureData,
|
||||||
|
1,
|
||||||
|
fadeTextureSize,
|
||||||
|
THREE.RGBAFormat,
|
||||||
|
);
|
||||||
|
riverFadeTexture.minFilter = THREE.LinearFilter;
|
||||||
|
riverFadeTexture.magFilter = THREE.LinearFilter;
|
||||||
|
riverFadeTexture.needsUpdate = true;
|
||||||
|
|
||||||
|
riverGeometry.computeBoundingBox();
|
||||||
|
const riverBounds = riverGeometry.boundingBox;
|
||||||
|
const riverMinY = riverBounds?.min.y ?? -16;
|
||||||
|
const riverLength = Math.max((riverBounds?.max.y ?? 16) - riverMinY, 1);
|
||||||
|
const riverPositions = riverGeometry.getAttribute('position');
|
||||||
|
const riverUvs = riverGeometry.getAttribute('uv');
|
||||||
|
|
||||||
|
for (let index = 0; index < riverPositions.count; index += 1) {
|
||||||
|
const pathProgress = (riverPositions.getY(index) - riverMinY) / riverLength;
|
||||||
|
riverUvs.setXY(index, 0.5, pathProgress);
|
||||||
|
}
|
||||||
|
riverUvs.needsUpdate = true;
|
||||||
|
|
||||||
const waterMaterial = new THREE.MeshStandardMaterial({
|
const waterMaterial = new THREE.MeshStandardMaterial({
|
||||||
color: palette.water,
|
color: palette.water,
|
||||||
emissive: palette.water,
|
emissive: palette.water,
|
||||||
emissiveIntensity: theme === 'dark' ? 0.28 : 0.1,
|
emissiveIntensity: theme === 'dark' ? 0.28 : 0.1,
|
||||||
roughness: 0.52,
|
roughness: 0.52,
|
||||||
|
alphaMap: riverFadeTexture,
|
||||||
|
transparent: true,
|
||||||
});
|
});
|
||||||
const river = new THREE.Mesh(riverGeometry, waterMaterial);
|
const river = new THREE.Mesh(riverGeometry, waterMaterial);
|
||||||
river.rotation.x = -Math.PI / 2;
|
river.rotation.x = -Math.PI / 2;
|
||||||
river.position.y = -0.23;
|
river.position.y = -0.23;
|
||||||
world.add(river);
|
world.add(river);
|
||||||
|
|
||||||
const riverEdge = new THREE.LineSegments(new THREE.EdgesGeometry(riverGeometry), edgeMaterial);
|
const riverEdgeMaterial = new THREE.ShaderMaterial({
|
||||||
|
transparent: true,
|
||||||
|
depthWrite: false,
|
||||||
|
uniforms: {
|
||||||
|
edgeColor: { value: new THREE.Color(palette.edge) },
|
||||||
|
edgeOpacity: { value: theme === 'dark' ? 0.48 : 0.56 },
|
||||||
|
fadeFraction: { value: riverEndFade },
|
||||||
|
pathMinY: { value: riverMinY },
|
||||||
|
pathLength: { value: riverLength },
|
||||||
|
},
|
||||||
|
vertexShader: `
|
||||||
|
uniform float pathMinY;
|
||||||
|
uniform float pathLength;
|
||||||
|
varying float pathProgress;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
pathProgress = clamp((position.y - pathMinY) / pathLength, 0.0, 1.0);
|
||||||
|
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
fragmentShader: `
|
||||||
|
uniform vec3 edgeColor;
|
||||||
|
uniform float edgeOpacity;
|
||||||
|
uniform float fadeFraction;
|
||||||
|
varying float pathProgress;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
float startFade = smoothstep(0.0, fadeFraction, pathProgress);
|
||||||
|
float endFade = smoothstep(0.0, fadeFraction, 1.0 - pathProgress);
|
||||||
|
gl_FragColor = vec4(edgeColor, edgeOpacity * startFade * endFade);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
const riverEdge = new THREE.LineSegments(
|
||||||
|
new THREE.EdgesGeometry(riverGeometry),
|
||||||
|
riverEdgeMaterial,
|
||||||
|
);
|
||||||
riverEdge.rotation.copy(river.rotation);
|
riverEdge.rotation.copy(river.rotation);
|
||||||
riverEdge.position.y = -0.21;
|
riverEdge.position.y = -0.21;
|
||||||
world.add(riverEdge);
|
world.add(riverEdge);
|
||||||
@@ -133,7 +205,8 @@ const createAboutScene: SceneFactory = ({ pointer, theme, reduceMotion }) => {
|
|||||||
const direction = end.clone().sub(start);
|
const direction = end.clone().sub(start);
|
||||||
const length = direction.length();
|
const length = direction.length();
|
||||||
direction.normalize();
|
direction.normalize();
|
||||||
for (let distance = 1.3; distance < length - 0.7; distance += 1.35) {
|
const fadeDistance = length * riverEndFade;
|
||||||
|
for (let distance = fadeDistance; distance < length - fadeDistance; distance += 1.35) {
|
||||||
const at = start.clone().addScaledVector(direction, distance);
|
const at = start.clone().addScaledVector(direction, distance);
|
||||||
const seam = new THREE.Mesh(
|
const seam = new THREE.Mesh(
|
||||||
new THREE.PlaneGeometry(1.72, 0.016),
|
new THREE.PlaneGeometry(1.72, 0.016),
|
||||||
|
|||||||
Reference in New Issue
Block a user