diff --git a/.storybook/stories/MeshDistortMaterial.stories.ts b/.storybook/stories/MeshDistortMaterial.stories.ts new file mode 100644 index 0000000..55dab67 --- /dev/null +++ b/.storybook/stories/MeshDistortMaterial.stories.ts @@ -0,0 +1,80 @@ +import * as THREE from 'three' +import { Setup } from '../Setup' +import GUI from 'lil-gui' +import { Meta } from '@storybook/html' +import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls' +import { MeshDistortMaterial } from '../../src/materials/MeshDistortMaterial' + +export default { + title: 'Shaders/MeshDistortMaterial', +} as Meta // TODO: this should be `satisfies Meta` but commit hooks lag behind TS + +let gui: GUI, materialGuiFolder: GUI +let scene: THREE.Scene, + camera: THREE.PerspectiveCamera, + renderer: THREE.WebGLRenderer, + animateLoop: (arg0: (time: number) => void) => void, + meshDistortMaterial: MeshDistortMaterial + +export const MDMStory = async () => { + const setupResult = Setup() + scene = setupResult.scene + camera = setupResult.camera + renderer = setupResult.renderer + animateLoop = setupResult.render + + gui = new GUI({ title: MDMStory.storyName }) + camera.position.set(0, 0, 5) + + const controls = new OrbitControls(camera, renderer.domElement) + controls.update() + + const ambientLight = new THREE.AmbientLight() + scene.add(ambientLight) + + const dirLight = new THREE.DirectionalLight(0xabcdef, 10) + dirLight.position.set(1, 20, 1) + dirLight.castShadow = true + dirLight.shadow.mapSize.width = 1024 + dirLight.shadow.mapSize.height = 1024 + scene.add(dirLight) + + setupMeshDistortMaterial() +} + +async function setupMeshDistortMaterial() { + const geometry = new THREE.SphereGeometry(1, 32, 32) + meshDistortMaterial = new MeshDistortMaterial({ color: '#f25042' }) + + meshDistortMaterial._radius.value = 0.2 + + const mesh = new THREE.Mesh(geometry, meshDistortMaterial) + mesh.scale.set(2, 4, 1) + + scene.add(mesh) + + createMeshDistortGUI() + + animateLoop((time) => { + meshDistortMaterial.time = time * 0.0025 + meshDistortMaterial.distort = THREE.MathUtils.lerp(meshDistortMaterial.distort, 0.4, 0.05) + }) +} + +/** + * Create gui for material properties + */ +function createMeshDistortGUI() { + if (materialGuiFolder) { + materialGuiFolder.destroy() + } + + const matProps = gui.addFolder('MeshDistortMaterial id: ' + meshDistortMaterial.id) + + matProps.addColor(meshDistortMaterial, 'color') + matProps.add(meshDistortMaterial._radius, 'value').min(0.01).max(0.5).step(0.01).name('radius') + + materialGuiFolder = matProps +} + +MDMStory.storyName = 'Sphere' diff --git a/README.md b/README.md index 0a8c241..3fe258f 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ import { pcss, ... } from '@pmndrs/vanilla'
  • MeshReflectorMaterial
  • shaderMaterial
  • MeshDiscardMaterial
  • +
  • MeshDistortMaterial
  • MeshTransmissionMaterial
  • SpotLight
  • @@ -182,6 +183,16 @@ A material that discards fragments. It can be used to render nothing efficiently const mesh = new THREE.Mesh(geometry, new MeshDiscardMaterial()) ``` +#### MeshDistortMaterial + +[![storybook](https://img.shields.io/badge/-storybook-%23ff69b4)](https://pmndrs.github.io/drei-vanilla/?path=/story/shaders-meshtransmissionmaterial--mdm-story) + +

    + Demo +

    + +This material makes your geometry distort following simplex noise. + #### MeshTransmissionMaterial [![storybook](https://img.shields.io/badge/-storybook-%23ff69b4)](https://pmndrs.github.io/drei-vanilla/?path=/story/shaders-meshtransmissionmaterial--mtm-story) diff --git a/package.json b/package.json index b8d903c..6ccdc27 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ "build-storybook": "build-storybook" }, "dependencies": { + "glsl-noise": "^0.0.0", "troika-three-text": "0.47.2" }, "devDependencies": { @@ -102,6 +103,7 @@ "lil-gui": "^0.19.1", "prettier": "^2.4.1", "pretty-quick": "^3.1.0", + "raw-loader": "^4.0.2", "react": "^18.0.0", "react-dom": "^18.0.0", "rimraf": "^3.0.2", @@ -118,5 +120,6 @@ }, "peerDependencies": { "three": ">=0.137" - } + }, + "packageManager": "yarn@1.22.22" } diff --git a/src/helpers/glsl/distort.vert.glsl b/src/helpers/glsl/distort.vert.glsl new file mode 100644 index 0000000..2b66845 --- /dev/null +++ b/src/helpers/glsl/distort.vert.glsl @@ -0,0 +1 @@ +#pragma glslify: snoise3 = require(glsl-noise/simplex/3d) diff --git a/src/materials/MeshDistortMaterial.ts b/src/materials/MeshDistortMaterial.ts new file mode 100644 index 0000000..7ed5000 --- /dev/null +++ b/src/materials/MeshDistortMaterial.ts @@ -0,0 +1,64 @@ +import { IUniform, MeshPhysicalMaterial, MeshPhysicalMaterialParameters } from 'three' +// @ts-ignore +import distort from '../helpers/glsl/distort.vert.glsl' + +export class MeshDistortMaterial extends MeshPhysicalMaterial { + _time: IUniform + _distort: IUniform + _radius: IUniform + + constructor(parameters: MeshPhysicalMaterialParameters = {}) { + super(parameters) + this.setValues(parameters) + this._time = { value: 0 } + this._distort = { value: 0.4 } + this._radius = { value: 1 } + } + + // FIXME Use `THREE.WebGLProgramParametersWithUniforms` type when able to target @types/three@0.160.0 + onBeforeCompile(shader: { vertexShader: string; uniforms: { [uniform: string]: IUniform } }) { + shader.uniforms.time = this._time + shader.uniforms.radius = this._radius + shader.uniforms.distort = this._distort + + shader.vertexShader = ` + uniform float time; + uniform float radius; + uniform float distort; + ${distort} + ${shader.vertexShader} + ` + shader.vertexShader = shader.vertexShader.replace( + '#include ', + ` + float updateTime = time / 50.0; + float noise = snoise(vec3(position / 2.0 + updateTime * 5.0)); + vec3 transformed = vec3(position * (noise * pow(distort, 2.0) + radius)); + ` + ) + } + + get time() { + return this._time.value + } + + set time(v) { + this._time.value = v + } + + get distort() { + return this._distort.value + } + + set distort(v) { + this._distort.value = v + } + + get radius() { + return this._radius.value + } + + set radius(v) { + this._radius.value = v + } +} diff --git a/src/materials/index.ts b/src/materials/index.ts index 15a93a2..576164f 100644 --- a/src/materials/index.ts +++ b/src/materials/index.ts @@ -4,3 +4,4 @@ export * from './SpotLightMaterial' export * from './BlurPass' export * from './ConvolutionMaterial' export * from './MeshReflectorMaterial' +export * from './MeshDistortMaterial' diff --git a/yarn.lock b/yarn.lock index fbf6c36..3aed7b6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7606,6 +7606,11 @@ glsl-inject-defines@^1.0.1: glsl-token-string "^1.0.1" glsl-tokenizer "^2.0.2" +glsl-noise@^0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/glsl-noise/-/glsl-noise-0.0.0.tgz#367745f3a33382c0eeec4cb54b7e99cfc1d7670b" + integrity sha512-b/ZCF6amfAUb7dJM/MxRs7AetQEahYzJ8PtgfrmEdtw6uyGOr+ZSGtgjFm6mfsBkxJ4d2W7kg+Nlqzqvn3Bc0w== + glsl-resolve@0.0.1: version "0.0.1" resolved "https://registry.npmjs.org/glsl-resolve/-/glsl-resolve-0.0.1.tgz" @@ -12115,7 +12120,7 @@ raw-body@2.5.1: raw-loader@^4.0.2: version "4.0.2" - resolved "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.2.tgz" + resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-4.0.2.tgz#1aac6b7d1ad1501e66efdac1522c73e59a584eb6" integrity sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA== dependencies: loader-utils "^2.0.0"