Skip to content

Commit b3f9eab

Browse files
authored
feat: add meshPortalMaterial (#76)
* feat: add meshPortalMaterial
1 parent d77ce41 commit b3f9eab

File tree

9 files changed

+708
-7
lines changed

9 files changed

+708
-7
lines changed

.storybook/Setup.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { PerspectiveCamera, Scene, sRGBEncoding, WebGLRenderer, REVISION, ACESFilmicToneMapping } from 'three'
1+
import { PerspectiveCamera, Scene, WebGLRenderer, ACESFilmicToneMapping } from 'three'
22
import { addons } from '@storybook/addons'
33
import { STORY_CHANGED } from '@storybook/core-events'
44

@@ -29,6 +29,7 @@ declare global {
2929

3030
window.canvas = root.appendChild(document.createElement('canvas'))
3131
window.context = canvas.getContext('webgl2')!
32+
window.canvas.style.display = 'block'
3233

3334
export const Setup = () => {
3435
const renderer = new WebGLRenderer({ alpha: true, canvas, context })
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
import * as THREE from 'three'
2+
import GUI from 'lil-gui'
3+
import { Meta } from '@storybook/html'
4+
import { Setup } from '../Setup'
5+
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
6+
import { MeshPortalMaterial } from '../../src/core/MeshPortalMaterial'
7+
import { EXRLoader } from 'three/examples/jsm/Addons.js'
8+
export default {
9+
title: 'Shaders/MeshPortalMaterial/basic',
10+
} as Meta // TODO: this should be `satisfies Meta` but commit hooks lag behind TS
11+
12+
let gui: GUI
13+
let scene: THREE.Scene,
14+
portalScene: THREE.Scene,
15+
camera: THREE.PerspectiveCamera,
16+
renderer: THREE.WebGLRenderer,
17+
portalMesh: THREE.Mesh
18+
19+
const rendererSize = new THREE.Vector2()
20+
21+
const portalParams = {
22+
resolution: 1024,
23+
renderTarget: new THREE.WebGLRenderTarget(),
24+
}
25+
26+
export const MPMStory = async () => {
27+
gui = new GUI({ title: MPMStory.storyName })
28+
29+
renderer = new THREE.WebGLRenderer({ alpha: true, canvas, context })
30+
renderer.toneMapping = THREE.ACESFilmicToneMapping
31+
32+
scene = new THREE.Scene()
33+
portalScene = new THREE.Scene()
34+
35+
camera = new THREE.PerspectiveCamera(45, 1, 1, 1000)
36+
camera.position.set(2.5, 0, 2.5)
37+
38+
const controls = new OrbitControls(camera, renderer.domElement)
39+
controls.update()
40+
41+
setupMainScene()
42+
setupPortalScene()
43+
44+
const onResize = () => {
45+
// resize canvas
46+
renderer.setPixelRatio(Math.min(2, Math.max(1, window.devicePixelRatio)))
47+
renderer.setSize(root.clientWidth, root.clientHeight)
48+
camera.aspect = root.clientWidth / root.clientHeight
49+
camera.updateProjectionMatrix()
50+
51+
// update 'rendererSize' vector
52+
renderer.getSize(rendererSize)
53+
rendererSize.multiplyScalar(renderer.getPixelRatio())
54+
}
55+
56+
onResize()
57+
window.addEventListener('resize', onResize)
58+
59+
renderer.setAnimationLoop(() => {
60+
// render portal scene
61+
renderer.setRenderTarget(portalParams.renderTarget)
62+
renderer.render(portalScene, camera)
63+
renderer.setRenderTarget(null)
64+
65+
// render main scene
66+
renderer.render(scene, camera)
67+
})
68+
}
69+
70+
function setupMainScene() {
71+
// in the main scene use basic lights
72+
const ambientLight = new THREE.AmbientLight()
73+
scene.add(ambientLight)
74+
75+
const dirLight = new THREE.DirectionalLight(0xabcdef, 10)
76+
dirLight.position.set(1, 20, 1)
77+
scene.add(dirLight)
78+
79+
scene.background = new THREE.Color().set(0xffffff * Math.random())
80+
const geometry = new THREE.TorusKnotGeometry(0.5, 0.25, 150, 20)
81+
const material = new THREE.MeshStandardMaterial({
82+
metalness: 0,
83+
roughness: 0.2,
84+
color: 0xffffff * Math.random(),
85+
})
86+
const torusMesh = new THREE.Mesh(geometry, material)
87+
portalScene.add(torusMesh)
88+
torusMesh.position.z = -1
89+
scene.add(torusMesh)
90+
91+
// add GUI
92+
const fol = gui.addFolder('Main scene')
93+
fol.open()
94+
fol.addColor(scene, 'background')
95+
fol.addColor(material, 'color').name('torus color')
96+
}
97+
98+
function setupPortalScene() {
99+
// in the portal scene just use and hdri for lighting
100+
const exrLoader = new EXRLoader()
101+
exrLoader.load('round_platform_1k.exr', (exrTex) => {
102+
// exr from polyhaven.com
103+
exrTex.mapping = THREE.EquirectangularReflectionMapping
104+
portalScene.environment = exrTex
105+
portalScene.background = exrTex
106+
})
107+
108+
// setup the portal
109+
portalParams.renderTarget.setSize(portalParams.resolution, portalParams.resolution)
110+
111+
renderer.getSize(rendererSize)
112+
rendererSize.multiplyScalar(renderer.getPixelRatio())
113+
114+
const portalGeometry = new THREE.PlaneGeometry(2, 2)
115+
const portalMaterial = new MeshPortalMaterial({
116+
map: portalParams.renderTarget.texture,
117+
resolution: rendererSize,
118+
})
119+
120+
portalMesh = new THREE.Mesh(portalGeometry, portalMaterial)
121+
scene.add(portalMesh)
122+
123+
const geometry = new THREE.TorusKnotGeometry(0.5, 0.25, 150, 20)
124+
const material = new THREE.MeshStandardMaterial({
125+
metalness: 1,
126+
roughness: 0.2,
127+
color: 0xffffff * Math.random(),
128+
})
129+
const torusMesh = new THREE.Mesh(geometry, material)
130+
torusMesh.position.z = -1
131+
portalScene.add(torusMesh)
132+
133+
// add gui
134+
const fol = gui.addFolder('Portal Scene')
135+
fol.open()
136+
fol.add(portalScene, 'backgroundBlurriness', 0, 1)
137+
fol.addColor(material, 'color').name('torus color')
138+
139+
const pFol = fol.addFolder('Portal settings')
140+
pFol.add(portalParams, 'resolution', 128, 2048, 256).onChange(() => {
141+
portalParams.renderTarget.setSize(portalParams.resolution, portalParams.resolution)
142+
})
143+
pFol.add(portalMesh.material, 'toneMapped')
144+
pFol.add(portalMesh.scale, 'x', 0.1, 2).name('scale X')
145+
pFol.add(portalMesh.scale, 'y', 0.1, 2).name('scale Y')
146+
}
147+
148+
MPMStory.storyName = 'plane'
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
import * as THREE from 'three'
2+
import GUI from 'lil-gui'
3+
import { Meta } from '@storybook/html'
4+
import { Setup } from '../Setup'
5+
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
6+
import { MeshPortalMaterial, meshPortalMaterialApplySDF } from '../../src/core/MeshPortalMaterial'
7+
import { EXRLoader } from 'three/examples/jsm/Addons.js'
8+
9+
export default {
10+
title: 'Shaders/MeshPortalMaterial/sdf',
11+
} as Meta // TODO: this should be `satisfies Meta` but commit hooks lag behind TS
12+
13+
let gui: GUI
14+
let scene: THREE.Scene,
15+
portalScene: THREE.Scene,
16+
camera: THREE.PerspectiveCamera,
17+
renderer: THREE.WebGLRenderer,
18+
portalMesh: THREE.Mesh
19+
20+
const rendererSize = new THREE.Vector2()
21+
22+
const portalParams = {
23+
resolution: 1024,
24+
renderTarget: new THREE.WebGLRenderTarget(),
25+
}
26+
27+
export const MPMStory = async () => {
28+
gui = new GUI({ title: MPMStory.storyName })
29+
30+
renderer = new THREE.WebGLRenderer({ alpha: true, canvas, context })
31+
renderer.toneMapping = THREE.ACESFilmicToneMapping
32+
33+
scene = new THREE.Scene()
34+
portalScene = new THREE.Scene()
35+
36+
camera = new THREE.PerspectiveCamera(45, 1, 1, 1000)
37+
camera.position.set(2.5, 0, 2.5)
38+
39+
const controls = new OrbitControls(camera, renderer.domElement)
40+
controls.update()
41+
42+
setupMainScene()
43+
setupPortalScene()
44+
45+
const onResize = () => {
46+
// resize canvas
47+
renderer.setPixelRatio(Math.min(2, Math.max(1, window.devicePixelRatio)))
48+
renderer.setSize(root.clientWidth, root.clientHeight)
49+
camera.aspect = root.clientWidth / root.clientHeight
50+
camera.updateProjectionMatrix()
51+
52+
// update 'rendererSize' vector
53+
renderer.getSize(rendererSize)
54+
rendererSize.multiplyScalar(renderer.getPixelRatio())
55+
}
56+
57+
onResize()
58+
window.addEventListener('resize', onResize)
59+
60+
renderer.setAnimationLoop(() => {
61+
// render portal scene
62+
renderer.setRenderTarget(portalParams.renderTarget)
63+
renderer.render(portalScene, camera)
64+
renderer.setRenderTarget(null)
65+
66+
// render main scene
67+
renderer.render(scene, camera)
68+
})
69+
}
70+
71+
/**
72+
* Setup the main/root scene
73+
*/
74+
function setupMainScene() {
75+
// in the main scene use basic lights
76+
const ambientLight = new THREE.AmbientLight()
77+
scene.add(ambientLight)
78+
79+
const dirLight = new THREE.DirectionalLight(0xabcdef, 10)
80+
dirLight.position.set(1, 20, 1)
81+
scene.add(dirLight)
82+
83+
scene.background = new THREE.Color().set(0xffffff * Math.random())
84+
const geometry = new THREE.TorusKnotGeometry(0.5, 0.25, 150, 20)
85+
const material = new THREE.MeshStandardMaterial({
86+
metalness: 0,
87+
roughness: 0.2,
88+
color: 0xffffff * Math.random(),
89+
})
90+
const torusMesh = new THREE.Mesh(geometry, material)
91+
portalScene.add(torusMesh)
92+
torusMesh.position.z = -1
93+
scene.add(torusMesh)
94+
95+
// add GUI
96+
const fol = gui.addFolder('Main scene')
97+
fol.open()
98+
fol.addColor(scene, 'background')
99+
fol.addColor(material, 'color').name('torus color')
100+
}
101+
102+
/**
103+
* Setup the portal and the contents in the portalScene
104+
*/
105+
function setupPortalScene() {
106+
// in the portal scene just use and hdri for lighting
107+
const exrLoader = new EXRLoader()
108+
exrLoader.load('round_platform_1k.exr', (exrTex) => {
109+
// exr from polyhaven.com
110+
exrTex.mapping = THREE.EquirectangularReflectionMapping
111+
portalScene.environment = exrTex
112+
portalScene.background = exrTex
113+
})
114+
115+
// setup the portal
116+
portalParams.renderTarget.setSize(portalParams.resolution, portalParams.resolution)
117+
118+
renderer.getSize(rendererSize)
119+
rendererSize.multiplyScalar(renderer.getPixelRatio())
120+
121+
const portalGeometry = new THREE.CircleGeometry(1.5, 64)
122+
const portalMaterial = new MeshPortalMaterial({
123+
map: portalParams.renderTarget.texture,
124+
resolution: rendererSize,
125+
transparent: true,
126+
blur: 0.5,
127+
})
128+
129+
portalMesh = new THREE.Mesh(portalGeometry, portalMaterial)
130+
meshPortalMaterialApplySDF(portalMesh, 512, renderer)
131+
scene.add(portalMesh)
132+
133+
// add another torusKnot in the same spot as the main scene
134+
const geometry = new THREE.TorusKnotGeometry(0.5, 0.25, 150, 20)
135+
const material = new THREE.MeshStandardMaterial({
136+
metalness: 1,
137+
roughness: 0.2,
138+
color: 0xffffff * Math.random(),
139+
})
140+
const torusMesh = new THREE.Mesh(geometry, material)
141+
torusMesh.position.z = -1
142+
portalScene.add(torusMesh)
143+
144+
// add gui
145+
const fol = gui.addFolder('Portal Scene')
146+
fol.open()
147+
fol.add(portalScene, 'backgroundBlurriness', 0, 1)
148+
fol.addColor(material, 'color').name('torus color')
149+
150+
const pFol = fol.addFolder('Portal settings')
151+
pFol.add(portalMesh.material, 'blur', 0, 2)
152+
pFol.add(portalParams, 'resolution', 128, 2048, 256).onChange(() => {
153+
portalParams.renderTarget.setSize(portalParams.resolution, portalParams.resolution)
154+
})
155+
pFol.add(portalMesh.material, 'toneMapped')
156+
157+
pFol.add(portalMesh.scale, 'x', 0.1, 2).name('scale X')
158+
pFol.add(portalMesh.scale, 'y', 0.1, 2).name('scale Y')
159+
}
160+
161+
MPMStory.storyName = 'circle'

0 commit comments

Comments
 (0)