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)
+
+
+
+
+
+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"