Skip to content

Commit

Permalink
Dots grid (#13)
Browse files Browse the repository at this point in the history
* edges sharpness test

* to match what's done in webgl1-to-webgl2 branch

* split grid spacing to 2 parameters

* dots parameters to pixel values + shape variation fix

* new parameters added

* reactivate other effects

* flip Y coordinates

* opacity range added

* few preset drafts

* docs added for dots grid

* shapes selection

* more on presets

* Merge main and update a few DX things

* triangles improvement

* triangle stroke-width adjustment

* randomizer fix; color blend fix + opacity variety range

* merged with docs

* example folder removed

* renaming dot-grid to dots-grid to fix the preview and keep consistency with dots-orbit

* more on presets

* presets update

* prettier

---------

Co-authored-by: Stephen Haney <[email protected]>
  • Loading branch information
uuuulala and StephenHaney authored Jan 15, 2025
1 parent 8bc8486 commit 7416861
Show file tree
Hide file tree
Showing 6 changed files with 454 additions and 0 deletions.
9 changes: 9 additions & 0 deletions docs/src/app/dots-grid/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Metadata } from 'next';

export const metadata: Metadata = {
title: 'Dot Grid Shader | Paper',
};

export default function Layout({ children }: { children: React.ReactNode }) {
return <>{children}</>;
}
80 changes: 80 additions & 0 deletions docs/src/app/dots-grid/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
'use client';

import { DotsGrid, type DotsGridParams, dotsGridPresets } from '@paper-design/shaders-react';

import { useControls, button, folder } from 'leva';
import { setParamsSafe, useResetLevaParams } from '@/helpers/use-reset-leva-params';
import { usePresetHighlight } from '@/helpers/use-preset-highlight';
import Link from 'next/link';
import { BackButton } from '@/components/back-button';
import { DotsGridShapes } from '@paper-design/shaders';

/**
* You can copy/paste this example to use DotsGrid in your app
*/
// const DotsGridExample = () => {
// return (
// <DotsGrid
// colorBack="#222222"
// colorFill="#e48b97"
// colorStroke="#f5d03b"
// dotSize={4}
// gridSpacingX={50}
// gridSpacingY={50}
// strokeWidth={2}
// sizeRange={0}
// opacityRange={0}
// shape={DotsGridShapes.Circle}
// style={{position: 'fixed', width: '100%', height: '100%'}}
// />
// );
// };

/**
* This example has controls added so you can play with settings in the example app
*/

const defaults = dotsGridPresets[0].params;

const DotsGridWithControls = () => {
const [params, setParams] = useControls(() => {
const presets: DotsGridParams = Object.fromEntries(
dotsGridPresets.map((preset) => [preset.name, button(() => setParamsSafe(params, setParams, preset.params))])
);
return {
Parameters: folder(
{
colorBack: { value: defaults.colorBack, order: 1 },
colorFill: { value: defaults.colorFill, order: 2 },
colorStroke: { value: defaults.colorStroke, order: 3 },
dotSize: { value: defaults.dotSize, order: 4, min: 0.1, max: 100 },
gridSpacingX: { value: defaults.gridSpacingX, order: 5, min: 2, max: 500 },
gridSpacingY: { value: defaults.gridSpacingY, order: 6, min: 2, max: 500 },
strokeWidth: { value: defaults.dotSize, order: 7, min: 0, max: 50 },
sizeRange: { value: defaults.gridSpacingY, order: 8, min: 0, max: 1 },
opacityRange: { value: defaults.gridSpacingY, order: 9, min: 0, max: 2 },
shape: { value: defaults.shape, order: 10, options: DotsGridShapes },
},
{ order: 1 }
),
Presets: folder(presets, { order: 2 }),
};
});

// Reset to defaults on mount, so that Leva doesn't show values from other
// shaders when navigating (if two shaders have a color1 param for example)
useResetLevaParams(params, setParams, defaults);

usePresetHighlight(dotsGridPresets, params);

return (
<>
<Link href="/">
<BackButton />
</Link>
<DotsGrid {...params} style={{ position: 'fixed', width: '100%', height: '100%' }} />
</>
);
};

export default DotsGridWithControls;
6 changes: 6 additions & 0 deletions packages/shaders-react/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ export { type DotsOrbitProps } from './shaders/dots-orbit';
export { type DotsOrbitParams } from './shaders/dots-orbit';
export { type DotsOrbitUniforms } from '@paper-design/shaders';

// Dot Grid
export { DotsGrid, dotsGridPresets } from './shaders/dots-grid';
export { type DotsGridProps } from './shaders/dots-grid';
export { type DotsGridParams } from './shaders/dots-grid';
export { type DotsGridUniforms, DotsGridShapes, type DotsGridShape } from '@paper-design/shaders';

// Stepped simplex noise
export { SteppedSimplexNoise, steppedSimplexNoisePresets } from './shaders/stepped-simplex-noise';
export { type SteppedSimplexNoiseProps } from './shaders/stepped-simplex-noise';
Expand Down
203 changes: 203 additions & 0 deletions packages/shaders-react/src/shaders/dots-grid.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
import { useMemo } from 'react';
import { ShaderMount, type ShaderMountProps } from '../shader-mount';
import {
dotsGridFragmentShader,
getShaderColorFromString,
type DotsGridUniforms,
type DotsGridShape,
DotsGridShapes,
} from '@paper-design/shaders';

export type DotsGridParams = {
colorBack?: string;
colorFill?: string;
colorStroke?: string;
dotSize?: number;
gridSpacingX?: number;
gridSpacingY?: number;
strokeWidth?: number;
sizeRange?: number;
opacityRange?: number;
shape?: DotsGridShape;
};

export type DotsGridProps = Omit<ShaderMountProps, 'fragmentShader'> & DotsGridParams;

type DotsGridPreset = { name: string; params: Required<DotsGridParams> };

export const defaultPreset: DotsGridPreset = {
name: 'Default',
params: {
// Note: Keep default colors in HSLA format so that our Leva controls show a transparency channel (rgba and hex8 do not work)
colorBack: 'hsla(358.2, 66.1%, 48.6%, 0)',
colorFill: 'hsla(145.2, 30.1%, 10%, 1)',
colorStroke: 'hsla(39.4, 87.7%, 52.4%, 1)',
dotSize: 2,
gridSpacingX: 50,
gridSpacingY: 50,
strokeWidth: 0,
sizeRange: 0,
opacityRange: 0,
shape: DotsGridShapes.Circle,
},
} as const;

const preset1: DotsGridPreset = {
name: '1',
params: {
// Note: Keep default colors in HSLA format so that our Leva controls show a transparency channel (rgba and hex8 do not work)
colorBack: 'hsla(0, 0%, 100%, 1)',
colorFill: 'hsla(0, 0%, 100%, 1)',
colorStroke: 'hsla(0, 0%, 0%, .5)',
dotSize: 5,
gridSpacingX: 32,
gridSpacingY: 32,
strokeWidth: 1,
sizeRange: 0,
opacityRange: 0,
shape: DotsGridShapes.Triangle,
},
} as const;

const preset2: DotsGridPreset = {
name: '2',
params: {
// Note: Keep default colors in HSLA format so that our Leva controls show a transparency channel (rgba and hex8 do not work)
colorBack: 'hsla(234, 100%, 31%, .5)',
colorFill: 'hsla(100, 30.1%, 100%, 1)',
colorStroke: 'hsla(0, 100%, 0%, 1)',
dotSize: 28,
gridSpacingX: 60,
gridSpacingY: 60,
strokeWidth: 12,
sizeRange: 0.7,
opacityRange: 1.3,
shape: DotsGridShapes.Circle,
},
} as const;

const preset3: DotsGridPreset = {
name: '3',
params: {
// Note: Keep default colors in HSLA format so that our Leva controls show a transparency channel (rgba and hex8 do not work)
colorBack: 'hsla(100, 100%, 36%, .05)',
colorFill: 'hsla(150, 80%, 10%, 1)',
colorStroke: 'hsla(0, 0%, 0%, 1)',
dotSize: 8,
gridSpacingX: 20,
gridSpacingY: 90,
strokeWidth: 0,
sizeRange: 1,
opacityRange: 0.6,
shape: DotsGridShapes.Circle,
},
} as const;

const preset4: DotsGridPreset = {
name: '4',
params: {
// Note: Keep default colors in HSLA format so that our Leva controls show a transparency channel (rgba and hex8 do not work)
colorBack: 'hsla(0, 0%, 0%, 0)',
colorFill: 'hsla(0, 100%, 50%, 1)',
colorStroke: 'hsla(0, 0%, 0%, 1)',
dotSize: 15,
gridSpacingX: 30,
gridSpacingY: 30,
strokeWidth: 0,
sizeRange: 0,
opacityRange: 2,
shape: DotsGridShapes.Diamond,
},
} as const;

const preset5: DotsGridPreset = {
name: '5',
params: {
// Note: Keep default colors in HSLA format so that our Leva controls show a transparency channel (rgba and hex8 do not work)
colorBack: 'hsla(154, 33%, 19%, 1)',
colorFill: 'hsla(0, 0%, 0%, 0)',
colorStroke: 'hsla(36, 48%, 58%, 1)',
dotSize: 9,
gridSpacingX: 32,
gridSpacingY: 32,
strokeWidth: 1,
sizeRange: 0,
opacityRange: 0,
shape: DotsGridShapes.Diamond,
},
} as const;

const preset6: DotsGridPreset = {
name: '6',
params: {
// Note: Keep default colors in HSLA format so that our Leva controls show a transparency channel (rgba and hex8 do not work)
colorBack: 'hsla(0, 100%, 0%, 1)',
colorFill: 'hsla(182, 100%, 64%, 1)',
colorStroke: 'hsla(0, 100%, 100%, 0)',
dotSize: 2,
gridSpacingX: 10,
gridSpacingY: 10,
strokeWidth: 0.5,
sizeRange: 0.25,
opacityRange: 1,
shape: DotsGridShapes.Triangle,
},
} as const;

const preset7: DotsGridPreset = {
name: '7',
params: {
// Note: Keep default colors in HSLA format so that our Leva controls show a transparency channel (rgba and hex8 do not work)
colorBack: 'hsla(0, 100%, 100%, 1)',
colorFill: 'hsla(227, 93%, 38%, 1)',
colorStroke: 'hsla(0, 0%, 0%, 0)',
dotSize: 100,
gridSpacingX: 2,
gridSpacingY: 215,
strokeWidth: 0,
sizeRange: 1,
opacityRange: 0,
shape: DotsGridShapes.Square,
},
} as const;

export const dotsGridPresets: DotsGridPreset[] = [
defaultPreset,
preset1,
preset2,
preset3,
preset4,
preset5,
preset6,
preset7,
];

export const DotsGrid = (props: DotsGridProps): JSX.Element => {
const uniforms: DotsGridUniforms = useMemo(() => {
return {
u_colorBack: getShaderColorFromString(props.colorBack, defaultPreset.params.colorBack),
u_colorFill: getShaderColorFromString(props.colorFill, defaultPreset.params.colorStroke),
u_colorStroke: getShaderColorFromString(props.colorStroke, defaultPreset.params.colorStroke),
u_dotSize: props.dotSize ?? defaultPreset.params.dotSize,
u_gridSpacingX: props.gridSpacingX ?? defaultPreset.params.gridSpacingX,
u_gridSpacingY: props.gridSpacingY ?? defaultPreset.params.gridSpacingY,
u_strokeWidth: props.strokeWidth ?? defaultPreset.params.strokeWidth,
u_sizeRange: props.sizeRange ?? defaultPreset.params.sizeRange,
u_opacityRange: props.opacityRange ?? defaultPreset.params.opacityRange,
u_shape: props.shape ?? defaultPreset.params.shape,
};
}, [
props.colorBack,
props.colorFill,
props.colorStroke,
props.dotSize,
props.gridSpacingX,
props.gridSpacingY,
props.strokeWidth,
props.sizeRange,
props.opacityRange,
props.shape,
]);

return <ShaderMount {...props} fragmentShader={dotsGridFragmentShader} uniforms={uniforms} />;
};
4 changes: 4 additions & 0 deletions packages/shaders/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ export { neuroNoiseFragmentShader, type NeuroNoiseUniforms } from './shaders/neu
/** A shader rendering an animated dots pattern based on Voronoi diagram */
export { dotsOrbitFragmentShader, type DotsOrbitUniforms } from './shaders/dots-orbit';

// ----- Dot Grid ----- //
/** A shader rendering a static dots pattern */
export { dotsGridFragmentShader, DotsGridShapes, type DotsGridShape, type DotsGridUniforms } from './shaders/dots-grid';

// ----- Stepped simplex noise ----- //
/** A shader that calculates a combination of 2 simplex noises with result rendered as a stepped gradient */
export { steppedSimplexNoiseFragmentShader, type SteppedSimplexNoiseUniforms } from './shaders/stepped-simplex-noise';
Expand Down
Loading

0 comments on commit 7416861

Please sign in to comment.