diff --git a/.storybook/stories/Example.stories.tsx b/.storybook/stories/Example.stories.tsx new file mode 100644 index 0000000..abb4b93 --- /dev/null +++ b/.storybook/stories/Example.stories.tsx @@ -0,0 +1,48 @@ +import React, { memo } from 'react' +import * as THREE from 'three' +import type { Meta, StoryObj } from '@storybook/react' +import { Box, useTexture } from '@react-three/drei' + +import { Setup } from '../Setup' +import { EffectComposer, Example } from '../../src' +import { BlendFunction } from 'postprocessing' + +// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction +const meta = { + title: 'Effect/Example', + component: Example, + decorators: [(Story) => {Story()}], + tags: ['autodocs'], + argTypes: { + blendFunction: { + control: 'select', + options: Object.keys(BlendFunction), + mapping: Object.keys(BlendFunction).reduce((acc, k) => { + acc[k] = BlendFunction[k] + return acc + }, {}), + }, + opacity: { control: { type: 'range', min: 0, max: 1, step: 0.001 } }, + color: { control: { type: 'color' } }, + }, +} satisfies Meta + +export default meta +type Story = StoryObj + +// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args +export const Primary: Story = { + render: ({ color, ...args }) => ( + <> + + + + + + + ), + args: { + blendFunction: BlendFunction.ADD, + color: 'red' as unknown as THREE.Color, + }, +} diff --git a/src/effects/Example.tsx b/src/effects/Example.tsx new file mode 100644 index 0000000..b3ef27b --- /dev/null +++ b/src/effects/Example.tsx @@ -0,0 +1,77 @@ +import * as THREE from 'three' +import { useRef } from 'react' +import { useFrame, useThree } from '@react-three/fiber' +import { BlendFunction, Effect } from 'postprocessing' + +import { wrapEffect } from '../util' + +// +// Effect +// + +const ExampleShader = { + fragment: ` + uniform vec3 color; + uniform float time; + + void mainImage(const in vec4 inputColor, const in vec2 uv, out vec4 outputColor) { + outputColor = vec4(color, 0.5 + (cos(time) / 2.0 + 0.5)); + } + `, +} + +type ExampleEffectOptions = { + /** The color for this effect */ + color: THREE.Color + /** The blend function of this effect */ + blendFunction?: BlendFunction +} + +export class ExampleEffect extends Effect { + constructor({ color, blendFunction }: ExampleEffectOptions) { + super('LensFlareEffect', ExampleShader.fragment, { + blendFunction, + uniforms: new Map([ + ['color', new THREE.Uniform(color)], + ['time', new THREE.Uniform(0)], + ]), + }) + } + + update(_renderer: any, _inputBuffer: any, deltaTime: number) { + const time = this.uniforms.get('time') + if (time) { + time.value += deltaTime + } + } +} + +// +// Component +// + +const ExampleWrapped = wrapEffect(ExampleEffect) + +type ExampleProps = React.ComponentPropsWithoutRef & { + /** mouse */ + mouse?: boolean +} + +export const Example = ({ mouse = false, ...props }: ExampleProps) => { + const pointer = useThree(({ pointer }) => pointer) + + const ref = useRef(null) + + useFrame(() => { + if (!mouse) return + if (!ref?.current) return + + const uColor = ref.current.uniforms.get('color') + if (!uColor) return + + uColor.value.r = pointer.x / 2.0 + 0.5 + uColor.value.g = pointer.y / 2.0 + 0.5 + }) + + return +} diff --git a/src/index.tsx b/src/index.tsx index c13a02f..71816ee 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -12,6 +12,7 @@ export * from './effects/ColorDepth' export * from './effects/Depth' export * from './effects/DepthOfField' export * from './effects/DotScreen' +export * from './effects/Example' export * from './effects/Glitch' export * from './effects/GodRays' export * from './effects/Grid'