├── static
├── .gitkeep
├── favicon.ico
├── preview.jpg
├── background.exr
├── background.jpg
├── favicon-16x16.png
├── favicon-32x32.png
├── apple-touch-icon.png
├── android-chrome-192x192.png
├── android-chrome-512x512.png
├── kylo_rens_helmet-transformed.glb
├── digital_painting_golden_hour_sunset.jpg
└── site.webmanifest
├── vite.config.js
├── package.json
├── LICENSE
├── src
├── style.css
├── index.html
├── script.js
└── HolographicMaterialVanilla.js
├── .gitignore
└── README.md
/static/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ektogamat/threejs-vanilla-holographic-material/HEAD/static/favicon.ico
--------------------------------------------------------------------------------
/static/preview.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ektogamat/threejs-vanilla-holographic-material/HEAD/static/preview.jpg
--------------------------------------------------------------------------------
/static/background.exr:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ektogamat/threejs-vanilla-holographic-material/HEAD/static/background.exr
--------------------------------------------------------------------------------
/static/background.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ektogamat/threejs-vanilla-holographic-material/HEAD/static/background.jpg
--------------------------------------------------------------------------------
/static/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ektogamat/threejs-vanilla-holographic-material/HEAD/static/favicon-16x16.png
--------------------------------------------------------------------------------
/static/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ektogamat/threejs-vanilla-holographic-material/HEAD/static/favicon-32x32.png
--------------------------------------------------------------------------------
/static/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ektogamat/threejs-vanilla-holographic-material/HEAD/static/apple-touch-icon.png
--------------------------------------------------------------------------------
/static/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ektogamat/threejs-vanilla-holographic-material/HEAD/static/android-chrome-192x192.png
--------------------------------------------------------------------------------
/static/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ektogamat/threejs-vanilla-holographic-material/HEAD/static/android-chrome-512x512.png
--------------------------------------------------------------------------------
/static/kylo_rens_helmet-transformed.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ektogamat/threejs-vanilla-holographic-material/HEAD/static/kylo_rens_helmet-transformed.glb
--------------------------------------------------------------------------------
/static/digital_painting_golden_hour_sunset.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ektogamat/threejs-vanilla-holographic-material/HEAD/static/digital_painting_golden_hour_sunset.jpg
--------------------------------------------------------------------------------
/static/site.webmanifest:
--------------------------------------------------------------------------------
1 | {"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
--------------------------------------------------------------------------------
/vite.config.js:
--------------------------------------------------------------------------------
1 | const isCodeSandbox = 'SANDBOX_URL' in process.env || 'CODESANDBOX_HOST' in process.env
2 |
3 | export default {
4 | root: 'src/',
5 | publicDir: '../static/',
6 | base: './',
7 | server:
8 | {
9 | host: true,
10 | open: !isCodeSandbox // Open if it's not a CodeSandbox
11 | },
12 | build:
13 | {
14 | outDir: '../dist',
15 | emptyOutDir: true,
16 | sourcemap: true
17 | }
18 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hologram-material",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "deploy": "vercel --prod"
10 | },
11 | "devDependencies": {
12 | "vite": "^4.3.9"
13 | },
14 | "dependencies": {
15 | "lil-gui": "^0.18.1",
16 | "maath": "^0.7.0",
17 | "postprocessing": "^6.33.4",
18 | "three": "^0.159.0",
19 | "vercel": "^31.0.4"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Anderson Mancini
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/style.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css2?family=Bebas+Neue&family=Moirai+One&display=swap');
2 | * {
3 | margin: 0;
4 | padding: 0;
5 | }
6 |
7 | html,
8 | body {
9 | overflow: hidden;
10 | user-select: none;
11 | font-family: 'Bebas Neue', sans-serif;
12 | }
13 |
14 | .webgl {
15 | position: fixed;
16 | top: 0;
17 | left: 0;
18 | outline: none;
19 | }
20 |
21 | .container {
22 | position: absolute;
23 | top: 85%;
24 | left: 50%;
25 | z-index: 1;
26 | text-align: center;
27 | pointer-events: none;
28 | transform: translate(-50%, -50%);
29 | width: 100%;
30 | line-height: 6em;
31 | color: rgba(255, 255, 255, 0.754);
32 | }
33 |
34 | h1 {
35 | font-size: 9vw;
36 | font-family: 'Moirai One', sans-serif;
37 | }
38 |
39 | p {
40 | font-size: 3vw;
41 | line-height: 0;
42 | margin-top: 60px;
43 | }
44 |
45 | a,
46 | small {
47 | color: rgba(255, 255, 255, 0.492);
48 | cursor: pointer;
49 | pointer-events: all;
50 | }
51 |
52 | .buttons {
53 | display: flex;
54 | gap: 30px;
55 | justify-content: center;
56 | line-height: 0;
57 | margin-top: 40px;
58 | }
59 |
60 | button {
61 | padding: 16px 26px;
62 | border: none;
63 | font-size: 0.7em;
64 | border-radius: 50px;
65 | background-color: rgba(255, 255, 255, 0.818);
66 | backdrop-filter: blur(5px);
67 | color: rgb(2, 58, 61);
68 | cursor: pointer;
69 | pointer-events: all;
70 | transition: all 0.6s ease-in-out;
71 | }
72 |
73 | button:hover {
74 | background-color: rgba(0, 255, 255, 0.381);
75 | box-shadow: 0 0 20px cyan;
76 | transform: scale(1.05);
77 | color: rgb(88, 247, 255);
78 | }
79 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 | .pnpm-debug.log*
9 |
10 | # Diagnostic reports (https://nodejs.org/api/report.html)
11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
12 |
13 | # Runtime data
14 | pids
15 | *.pid
16 | *.seed
17 | *.pid.lock
18 |
19 | # Directory for instrumented libs generated by jscoverage/JSCover
20 | lib-cov
21 |
22 | # Coverage directory used by tools like istanbul
23 | coverage
24 | *.lcov
25 |
26 | # nyc test coverage
27 | .nyc_output
28 |
29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
30 | .grunt
31 |
32 | # Bower dependency directory (https://bower.io/)
33 | bower_components
34 |
35 | # node-waf configuration
36 | .lock-wscript
37 |
38 | # Compiled binary addons (https://nodejs.org/api/addons.html)
39 | build/Release
40 |
41 | # Dependency directories
42 | node_modules/
43 | jspm_packages/
44 |
45 | # Snowpack dependency directory (https://snowpack.dev/)
46 | web_modules/
47 |
48 | # TypeScript cache
49 | *.tsbuildinfo
50 |
51 | # Optional npm cache directory
52 | .npm
53 |
54 | # Optional eslint cache
55 | .eslintcache
56 |
57 | # Optional stylelint cache
58 | .stylelintcache
59 |
60 | # Microbundle cache
61 | .rpt2_cache/
62 | .rts2_cache_cjs/
63 | .rts2_cache_es/
64 | .rts2_cache_umd/
65 |
66 | # Optional REPL history
67 | .node_repl_history
68 |
69 | # Output of 'npm pack'
70 | *.tgz
71 |
72 | # Yarn Integrity file
73 | .yarn-integrity
74 |
75 | # dotenv environment variable files
76 | .env
77 | .env.development.local
78 | .env.test.local
79 | .env.production.local
80 | .env.local
81 |
82 | # parcel-bundler cache (https://parceljs.org/)
83 | .cache
84 | .parcel-cache
85 |
86 | # Next.js build output
87 | .next
88 | out
89 |
90 | # Nuxt.js build / generate output
91 | .nuxt
92 | dist
93 |
94 | # Gatsby files
95 | .cache/
96 | # Comment in the public line in if your project uses Gatsby and not Next.js
97 | # https://nextjs.org/blog/next-9-1#public-directory-support
98 | # public
99 |
100 | # vuepress build output
101 | .vuepress/dist
102 |
103 | # vuepress v2.x temp and cache directory
104 | .temp
105 | .cache
106 |
107 | # Docusaurus cache and generated files
108 | .docusaurus
109 |
110 | # Serverless directories
111 | .serverless/
112 |
113 | # FuseBox cache
114 | .fusebox/
115 |
116 | # DynamoDB Local files
117 | .dynamodb/
118 |
119 | # TernJS port file
120 | .tern-port
121 |
122 | # Stores VSCode versions used for testing VSCode extensions
123 | .vscode-test
124 |
125 | # yarn v2
126 | .yarn/cache
127 | .yarn/unplugged
128 | .yarn/build-state.yml
129 | .yarn/install-state.gz
130 | .pnp.*
131 | .vercel
132 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Holographic Material for Three.js - by Anderson Mancini
8 |
9 |
11 |
12 |
13 |
14 |
16 |
18 |
19 |
20 |
21 |
22 |
23 |
25 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
HOLOGRAPHIC
43 |
NEW MATERIAL FOR VANILLA THREEJS
44 |
45 |
46 |
47 |
51 |
57 |
58 |
59 |
Created with 💛 by Anderson Mancini.DEV - Model
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/src/script.js:
--------------------------------------------------------------------------------
1 | import * as THREE from 'three'
2 | import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
3 | import * as dat from 'lil-gui'
4 | import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
5 | import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js'
6 | import {
7 | BloomEffect,
8 | BrightnessContrastEffect,
9 | DepthOfFieldEffect,
10 | EffectComposer,
11 | EffectPass,
12 | RenderPass,
13 | SMAAEffect,
14 | VignetteEffect
15 | } from 'postprocessing'
16 |
17 | import HolographicMaterial from './HolographicMaterialVanilla.js'
18 |
19 | const dracoLoader = new DRACOLoader()
20 | const loader = new GLTFLoader()
21 | dracoLoader.setDecoderPath('https://www.gstatic.com/draco/v1/decoders/')
22 | dracoLoader.setDecoderConfig({ type: 'js' })
23 | loader.setDRACOLoader(dracoLoader)
24 |
25 | /**
26 | * Scene
27 | */
28 | const canvas = document.querySelector('canvas.webgl')
29 | const scene = new THREE.Scene()
30 |
31 | /**
32 | * ScreenResolution
33 | */
34 | const screenRes = {
35 | width: window.innerWidth,
36 | height: window.innerHeight
37 | }
38 |
39 | window.addEventListener('resize', () => {
40 | screenRes.width = window.innerWidth
41 | screenRes.height = window.innerHeight
42 |
43 | camera.aspect = screenRes.width / screenRes.height
44 | camera.updateProjectionMatrix()
45 |
46 | renderer.setSize(screenRes.width, screenRes.height)
47 | renderer.setPixelRatio(Math.min(window.devicePixelRatio, 1))
48 | })
49 |
50 | /**
51 | * Camera
52 | */
53 | const camera = new THREE.PerspectiveCamera(
54 | 35,
55 | screenRes.width / screenRes.height,
56 | 0.1,
57 | 1000
58 | )
59 | camera.position.set(0, 0, 5)
60 | scene.add(camera)
61 |
62 | /**
63 | * Controls
64 | */
65 | const controls = new OrbitControls(camera, canvas)
66 | controls.enableDamping = true
67 | controls.maxDistance = 6
68 | controls.minDistance = 2
69 | controls.maxPolarAngle = Math.PI / 1.7
70 | controls.minPolarAngle = 1.1
71 |
72 | /**
73 | * Lights
74 | */
75 | const light = new THREE.DirectionalLight()
76 | light.intensity = 1
77 | light.position.set(-20, 20, 50)
78 | scene.add(light)
79 |
80 | const ambientLight = new THREE.AmbientLight()
81 | ambientLight.intensity = 2.9
82 | scene.add(ambientLight)
83 |
84 | /**
85 | * Renderer
86 | */
87 | const renderer = new THREE.WebGLRenderer({
88 | canvas: canvas,
89 | powerPreference: 'high-performance',
90 | antialias: false,
91 | stencil: false,
92 | depth: false
93 | })
94 | renderer.outputColorSpace = THREE.LinearSRGBColorSpace
95 | renderer.toneMapping = THREE.LinearToneMapping
96 | renderer.toneMappingExposure = 0.7
97 | renderer.setSize(screenRes.width, screenRes.height)
98 | renderer.setPixelRatio(Math.min(window.devicePixelRatio, 1))
99 |
100 | /**
101 | * SkyBox
102 | */
103 | const geometry = new THREE.SphereGeometry(8, 40, 40)
104 | const texture = new THREE.TextureLoader().load('background.jpg')
105 | texture.flipY = true
106 | const material = new THREE.MeshBasicMaterial({
107 | map: texture,
108 | side: THREE.BackSide
109 | })
110 |
111 | const skyBox = new THREE.Mesh(geometry, material)
112 | scene.add(skyBox)
113 | skyBox.rotation.y = -1
114 |
115 | /**
116 | * KyloRen Helmet
117 | */
118 | let KyloRen
119 | const holoMaterial1 = new HolographicMaterial({
120 | hologramColor: '#00d5ff',
121 | fresnelAmount: 0.7,
122 | blendMode: THREE.NormalBlending,
123 | scanlineSize: 3.7,
124 | signalSpeed: 0.18,
125 | hologramOpacity: 0.7,
126 | blinkFresnelOnly: true,
127 | depthTest: true,
128 | hologramBrightness: 1.6
129 | })
130 |
131 | const holoMaterial2 = new HolographicMaterial({
132 | hologramColor: '#00ffaa',
133 | fresnelAmount: 0.6,
134 | blendMode: THREE.NormalBlending,
135 | scanlineSize: 30,
136 | signalSpeed: 1,
137 | blinkFresnelOnly: true,
138 | hologramOpacity: 0.5,
139 | hologramBrightness: 2,
140 | depthTest: false
141 | })
142 | loader.load('kylo_rens_helmet-transformed.glb', function (gltf) {
143 | KyloRen = gltf.scene
144 |
145 | const hologramMesh = KyloRen.children
146 |
147 | hologramMesh[0].material = holoMaterial1
148 | hologramMesh[1].material = holoMaterial2
149 | hologramMesh[2].material = holoMaterial1
150 |
151 | scene.add(KyloRen)
152 | hologramMesh[1].scale.set(0.315, 0.315, 0.315)
153 |
154 | KyloRen.position.set(0, 0, -1.5)
155 | KyloRen.scale.set(1.6, 1.6, 1.6)
156 | })
157 |
158 | /**
159 | * Set up the GUI for manipulating parameters
160 | */
161 |
162 | const gui = new dat.GUI()
163 |
164 | // Add controls for each parameter
165 | gui
166 | .add(holoMaterial1.uniforms.fresnelOpacity, 'value')
167 | .min(0)
168 | .max(1)
169 | .step(0.01)
170 | .name('Fresnel Opacity')
171 | gui
172 | .add(holoMaterial1.uniforms.fresnelAmount, 'value')
173 | .min(0)
174 | .max(2)
175 | .step(0.01)
176 | .name('Fresnel Amount')
177 | gui
178 | .add(holoMaterial1.uniforms.scanlineSize, 'value')
179 | .min(0)
180 | .max(20)
181 | .step(0.01)
182 | .name('Scanline Size')
183 | gui
184 | .add(holoMaterial1.uniforms.hologramBrightness, 'value')
185 | .min(0)
186 | .max(2)
187 | .step(0.01)
188 | .name('Hologram Brightness')
189 | gui
190 | .add(holoMaterial1.uniforms.signalSpeed, 'value')
191 | .min(0)
192 | .max(2)
193 | .step(0.01)
194 | .name('Signal Speed')
195 | gui
196 | .addColor(
197 | {
198 | HologramColor: holoMaterial1.uniforms.hologramColor.value.getStyle()
199 | },
200 | 'HologramColor'
201 | )
202 | .onChange((color) => {
203 | holoMaterial1.uniforms.hologramColor.value.setStyle(color)
204 | holoMaterial1.needsUpdate = true
205 | })
206 | gui.add(holoMaterial1.uniforms.enableBlinking, 'value').name('Enable Blinking')
207 | gui
208 | .add(holoMaterial1.uniforms.blinkFresnelOnly, 'value')
209 | .name('Blink Fresnel Only')
210 | gui
211 | .add(holoMaterial1.uniforms.hologramOpacity, 'value')
212 | .min(0)
213 | .max(1)
214 | .step(0.01)
215 | .name('Hologram Opacity')
216 |
217 | /**
218 | * Post processing
219 | */
220 | const composer = new EffectComposer(renderer)
221 | composer.addPass(new RenderPass(scene, camera))
222 | composer.addPass(
223 | new EffectPass(
224 | camera,
225 | new DepthOfFieldEffect(camera, { focusRange: 0.048, focalLength: 0.4, bokehScale: 80 })
226 | )
227 | )
228 | composer.addPass(
229 | new EffectPass(
230 | camera,
231 | new BloomEffect({
232 | luminanceThreshold: 0.6,
233 | intensity: 1.2,
234 | mipmapBlur: true,
235 | radius: 0.8
236 | })
237 | )
238 | )
239 | composer.addPass(
240 | new EffectPass(
241 | camera,
242 | new BloomEffect({
243 | luminanceThreshold: 0.2,
244 | intensity: 0.6,
245 | mipmapBlur: true,
246 | radius: 1
247 | })
248 | )
249 | )
250 | composer.addPass(
251 | new EffectPass(
252 | camera,
253 | new BrightnessContrastEffect({
254 | contrast: 0.2
255 | })
256 | )
257 | )
258 | composer.addPass(
259 | new EffectPass(
260 | camera,
261 | new SMAAEffect()
262 | )
263 | )
264 | composer.addPass(new EffectPass(camera, new VignetteEffect({ darkness: 0.7 })))
265 |
266 | /**
267 | * Animate
268 | */
269 | const clock = new THREE.Clock()
270 | const tick = () => {
271 | controls.update()
272 | composer.render()
273 | holoMaterial1.update()
274 | holoMaterial2.update()
275 |
276 | if (KyloRen) {
277 | KyloRen.position.y = Math.sin(clock.getElapsedTime()) / 8.3
278 | }
279 |
280 | window.requestAnimationFrame(tick)
281 | }
282 |
283 | tick()
284 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Holographic Material for Vanilla Three.js
2 |
3 | A simple to use holographic material for threejs
4 |
5 |
6 |
7 |
8 |
9 | Live link: https://threejs-vanilla-holographic-material.vercel.app/
10 |
11 | CodeSandBox simplest example: https://codesandbox.io/p/sandbox/three-js-vanilla-holographic-material-8clmrd
12 |
13 | # Introduction
14 |
15 | Dive into a world of mesmerizing holographic wonders with the HolographicMaterial for vanilla three.js. This enchanting three.js material brings your virtual reality experiences to life, infusing them with a burst of vibrant colors, dynamic scanlines, and a touch of futuristic brilliance.
16 |
17 | While this material operates independently of any post-processing, it achieves an enhanced visual appeal when coupled with bloom effects. The utilization of bloom proves particularly effective in rendering a captivating glow effect, especially in areas where overexposure is prevalent.
18 |
19 | # HOW TO USE?
20 | You can see a video tutorial on how to use it here: https://www.youtube.com/watch?v=EPcd8iEcna8
21 |
22 | ### 1: Download the Material
23 |
24 | Create a new file with the contents of [this gist](https://gist.github.com/ektogamat/b149d9154f86c128c9fea52c974dda1a) on your project and save it as 'HolographicMaterialVanilla.js'
25 |
26 | ### 2: Import the material
27 |
28 | ```
29 | import HolographicMaterial from './HolographicMaterialVanilla.js'
30 | ```
31 |
32 | ### 3: Add this as a material for your mesh
33 |
34 | ```
35 | // ...
36 | const geometry = new THREE.BoxGeometry();
37 | const holographicMaterial = new HolographicMaterial();
38 | const cube = new THREE.Mesh(geometry, holographicMaterial);
39 | scene.add(cube);
40 | // ...
41 | ...
42 |
43 | ```
44 |
45 | ### 4: Update the material inside the render loop
46 |
47 | ```
48 | // ...
49 | const tick = () => {
50 | holographicMaterial.update() // Update the holographic material time uniform
51 | window.requestAnimationFrame(tick)
52 | }
53 |
54 | tick()
55 | // ...
56 | ```
57 |
58 | That's all that you need to use this material. Of course, you can customize it as shown below.
59 |
60 | # Properties
61 |
62 | | Name | Type | Default | Description |
63 | | ------------------ | ------------------------------------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------- |
64 | | fresnelAmount | Number | 0.45 | Controls the value of the Fresnel effect. Ranges from 0.0 to 1.0. |
65 | | fresnelOpacity | Number | 1.0 | Controls the opacity of the Fresnel effect. Ranges from 0.0 to 1.0. |
66 | | scanlineSize | Number | 8.0 | Controls the size of the scanlines. Ranges from 1 to 15. |
67 | | hologramBrightness | Number | 1.2 | Controls the brightness of the hologram. Ranges from 0.0 to 2.0. |
68 | | signalSpeed | Number | 0.45 | Controls the speed of the signal effect. Ranges from 0.0 to 2.0. |
69 | | hologramColor | String | "#00d5ff" | Specifies the color of the hologram. Use hexadecimal format. |
70 | | enableBlinking | Boolean | true | Enables or disables the blinking effect. Defaults to true. |
71 | | hologramOpacity | Number | 1.0 | Specifies the opacity of the hologram. Defaults to 1.0. |
72 | | enableBlinking | Boolean | true | Enables or disables the blinking effect. Defaults to true. |
73 | | blinkFresnelOnly | Boolean | true | Enables or disables the blinking effect for the Fresnel only. Defaults to true. |
74 | | enableAdditive | Boolean | true | Enables or disables the Additive Blend Mode. Defaults to true. |
75 | | side | THREE.FrontSide, THREE.BackSide, THREE.DoubleSide | FrontSide | Specifies side for the material, as String. Options are "FrontSide", "BackSide", "DoubleSide". Defaults to "THREE.FrontSide". |
76 |
77 | Here is an example of how you can pass these props. If you pass any of those props, the default values will be overwritten.
78 |
79 | ```
80 | const holographicMaterial = new HolographicMaterial({
81 | fresnelAmount: 0.2,
82 | fresnelOpacity: 0.15,
83 | hologramBrightness: 0.7,
84 | scanlineSize: 6,
85 | signalSpeed: 2.3,
86 | hologramColor: "#ff0000",
87 | hologramOpacity: 1.0,
88 | blinkFresnelOnly: true,
89 | enableBlinking: true,
90 | enableAdditive: true,
91 | side: THREE.FrontSide,
92 | });
93 | ```
94 |
95 | You can also use dat.GUI to create a panel, like in the demo, so you can tweak the parameters live.
96 |
97 | ```
98 | // Add dat.GUI controls
99 | const gui = new dat.GUI();
100 | gui.add(holographicMaterial, 'fresnelAmount', 0.0, 1.0).name('Fresnel Amount');
101 | gui.add(holographicMaterial, 'fresnelOpacity', 0.0, 1.0).name('Fresnel Opacity');
102 | gui.add(holographicMaterial, 'scanlineSize', 1.0, 15.0).name('Scanline Size');
103 | gui.add(holographicMaterial, 'hologramBrightness', 0.0, 2.0).name('Hologram Brightness');
104 | gui.add(holographicMaterial, 'signalSpeed', 0.0, 2.0).name('Signal Speed');
105 | gui.addColor(holographicMaterial, 'hologramColor').name('Hologram Color');
106 | gui.add(holographicMaterial, 'enableBlinking').name('Enable Blinking');
107 | gui.add(holographicMaterial, 'blinkFresnelOnly').name('Blink Fresnel Only');
108 | gui.add(holographicMaterial, 'hologramOpacity', 0.0, 1.0).name('Hologram Opacity');
109 | gui.add(holographicMaterial, 'enableAdditive').name('Enable Additive');
110 |
111 | ```
112 |
113 | ### License
114 |
115 | This component is an open-source creation released under the (MIT) license. While you're free to integrate and innovate with it, I would genuinely appreciate knowing when this HolographicMaterial component sparks creativity in your projects!
116 |
117 | ### Resources
118 |
119 | Resources: [Threejs](https://threejs.org/), [WebGL](https://github.com/KhronosGroup/WebGL), [Poimanders post processing](https://github.com/pmndrs/postprocessing)
120 |
121 | # Can you leave a star please?
122 |
123 | Would be really appreciated if you are willing to give me a star here on GitHub 🎉 or buy me a coffee ☕ https://www.buymeacoffee.com/andersonmancini. The money will be used to produce more content about threejs or to buy new courses.
124 |
--------------------------------------------------------------------------------
/src/HolographicMaterialVanilla.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Holographic material by Anderson Mancini - Dec 2023.
3 | */
4 | import { ShaderMaterial, Clock, Uniform, Color, NormalBlending, AdditiveBlending, FrontSide, BackSide, DoubleSide } from 'three';
5 |
6 | class HolographicMaterial extends ShaderMaterial {
7 |
8 | /**
9 | * Create a HolographicMaterial.
10 | *
11 | * @param {Object} parameters - The parameters to configure the material.
12 | * @param {number} [parameters.time=0.0] - The time uniform representing animation time.
13 | * @param {number} [parameters.fresnelOpacity=1.0] - The opacity for the fresnel effect.
14 | * @param {number} [parameters.fresnelAmount=1.0] - The strength of the fresnel effect.
15 | * @param {number} [parameters.scanlineSize=15.0] - The size of the scanline effect.
16 | * @param {number} [parameters.hologramBrightness=1.0] - The brightness of the hologram.
17 | * @param {number} [parameters.signalSpeed=1.0] - The speed of the signal effect.
18 | * @param {Color} [parameters.hologramColor=new Color('#00d5ff')] - The color of the hologram.
19 | * @param {boolean} [parameters.enableBlinking=true] - Enable/disable blinking effect.
20 | * @param {boolean} [parameters.blinkFresnelOnly=false] - Enable blinking only on the fresnel effect.
21 | * @param {number} [parameters.hologramOpacity=1.0] - The opacity of the hologram.
22 | * @param {number} [parameters.blendMode=NormalBlending] - The blending mode. Use `THREE.NormalBlending` or `THREE.AdditiveBlending`.
23 | * @param {number} [parameters.side=FrontSide] - The rendering side. Use `THREE.FrontSide`, `THREE.BackSide`, or `THREE.DoubleSide`.
24 | * @param {Boolean} [parameters.depthTest=true] - Enable or disable depthTest.
25 | */
26 |
27 | constructor(parameters = {}) {
28 | super();
29 |
30 | this.vertexShader = /*GLSL */
31 | `
32 | #define STANDARD
33 | varying vec3 vViewPosition;
34 | #ifdef USE_TRANSMISSION
35 | varying vec3 vWorldPosition;
36 | #endif
37 |
38 | varying vec2 vUv;
39 | varying vec4 vPos;
40 | varying vec3 vNormalW;
41 | varying vec3 vPositionW;
42 |
43 | #include
44 | #include
45 | #include
46 | #include
47 | #include
48 | #include
49 | #include
50 | #include
51 | #include
52 |
53 | void main() {
54 |
55 | #include
56 | #include
57 | #include
58 |
59 | #if defined ( USE_ENVMAP ) || defined ( USE_SKINNING )
60 |
61 | #include
62 | #include
63 | #include
64 | #include
65 | #include
66 |
67 | #endif
68 |
69 | #include
70 | #include
71 | #include
72 | #include
73 | #include
74 | #include
75 |
76 | #include
77 | #include
78 | #include
79 |
80 | mat4 modelViewProjectionMatrix = projectionMatrix * modelViewMatrix;
81 |
82 | vUv = uv;
83 | vPos = projectionMatrix * modelViewMatrix * vec4( transformed, 1.0 );
84 | vPositionW = vec3( vec4( transformed, 1.0 ) * modelMatrix);
85 | vNormalW = normalize( vec3( vec4( normal, 0.0 ) * modelMatrix ) );
86 |
87 | gl_Position = modelViewProjectionMatrix * vec4( transformed, 1.0 );
88 |
89 | }`
90 |
91 | this.fragmentShader = /*GLSL */
92 | `
93 | varying vec2 vUv;
94 | varying vec3 vPositionW;
95 | varying vec4 vPos;
96 | varying vec3 vNormalW;
97 |
98 | uniform float time;
99 | uniform float fresnelOpacity;
100 | uniform float scanlineSize;
101 | uniform float fresnelAmount;
102 | uniform float signalSpeed;
103 | uniform float hologramBrightness;
104 | uniform float hologramOpacity;
105 | uniform bool blinkFresnelOnly;
106 | uniform bool enableBlinking;
107 | uniform vec3 hologramColor;
108 |
109 | float flicker( float amt, float time ) {return clamp( fract( cos( time ) * 43758.5453123 ), amt, 1.0 );}
110 | float random(in float a, in float b) { return fract((cos(dot(vec2(a,b) ,vec2(12.9898,78.233))) * 43758.5453)); }
111 |
112 | void main() {
113 | vec2 vCoords = vPos.xy;
114 | vCoords /= vPos.w;
115 | vCoords = vCoords * 0.5 + 0.5;
116 | vec2 myUV = fract( vCoords );
117 |
118 | // Defines hologram main color
119 | vec4 hologramColor = vec4(hologramColor, mix(hologramBrightness, vUv.y, 0.5));
120 |
121 | // Add scanlines
122 | float scanlines = 10.;
123 | scanlines += 20. * sin(time *signalSpeed * 20.8 - myUV.y * 60. * scanlineSize);
124 | scanlines *= smoothstep(1.3 * cos(time *signalSpeed + myUV.y * scanlineSize), 0.78, 0.9);
125 | scanlines *= max(0.25, sin(time *signalSpeed) * 1.0);
126 |
127 | // Scanlines offsets
128 | float r = random(vUv.x, vUv.y);
129 | float g = random(vUv.y * 20.2, vUv.y * .2);
130 | float b = random(vUv.y * .9, vUv.y * .2);
131 |
132 | // Scanline composition
133 | hologramColor += vec4(r*scanlines, b*scanlines, r, 1.0) / 84.;
134 | vec4 scanlineMix = mix(vec4(0.0), hologramColor, hologramColor.a);
135 |
136 | // Calculates fresnel
137 | vec3 viewDirectionW = normalize(cameraPosition - vPositionW);
138 | float fresnelEffect = dot(viewDirectionW, vNormalW) * (1.6 - fresnelOpacity/2.);
139 | fresnelEffect = clamp(fresnelAmount - fresnelEffect, 0., fresnelOpacity);
140 |
141 | // Blinkin effect
142 | //Suggested by Octano - https://x.com/OtanoDesign?s=20
143 | float blinkValue = enableBlinking ? 0.6 - signalSpeed : 1.0;
144 | float blink = flicker(blinkValue, time * signalSpeed * .02);
145 |
146 | // Final shader composition
147 | vec3 finalColor;
148 |
149 | if(blinkFresnelOnly){
150 | finalColor = scanlineMix.rgb + fresnelEffect * blink;
151 | }else{
152 | finalColor = scanlineMix.rgb * blink + fresnelEffect;
153 | }
154 |
155 | gl_FragColor = vec4( finalColor, hologramOpacity);
156 |
157 | }`
158 |
159 | // Set default values or modify existing properties if needed
160 | this.uniforms = {
161 | /**
162 | * The time uniform representing animation time.
163 | * @type {Uniform}
164 | * @default 0.0
165 | */
166 | time: new Uniform(0),
167 |
168 | /**
169 | * The opacity for the fresnel effect.
170 | * @type {Uniform}
171 | * @default 1.0
172 | */
173 | fresnelOpacity: new Uniform(parameters.fresnelOpacity !== undefined ? parameters.fresnelOpacity : 1.0),
174 |
175 | /**
176 | * The strength of the fresnel effect.
177 | * @type {Uniform}
178 | * @default 1.0
179 | */
180 | fresnelAmount: new Uniform(parameters.fresnelAmount !== undefined ? parameters.fresnelAmount : 0.45),
181 |
182 | /**
183 | * The size of the scanline effect.
184 | * @type {Uniform}
185 | * @default 1.0
186 | */
187 | scanlineSize: new Uniform(parameters.scanlineSize !== undefined ? parameters.scanlineSize : 8.0),
188 |
189 | /**
190 | * The brightness of the hologram.
191 | * @type {Uniform}
192 | * @default 1.0
193 | */
194 | hologramBrightness: new Uniform(parameters.hologramBrightness !== undefined ? parameters.hologramBrightness : 1.0),
195 |
196 | /**
197 | * The speed of the signal effect.
198 | * @type {Uniform}
199 | * @default 1.0
200 | */
201 | signalSpeed: new Uniform(parameters.signalSpeed !== undefined ? parameters.signalSpeed : 1.0),
202 |
203 | /**
204 | * The color of the hologram.
205 | * @type {Uniform}
206 | * @default new Color(0xFFFFFF)
207 | */
208 | hologramColor: new Uniform(parameters.hologramColor !== undefined ? new Color(parameters.hologramColor) : new Color("#00d5ff")),
209 |
210 | /**
211 | * Enable/disable blinking effect.
212 | * @type {Uniform}
213 | * @default true
214 | */
215 | enableBlinking: new Uniform(parameters.enableBlinking !== undefined ? parameters.enableBlinking : true),
216 |
217 | /**
218 | * Enable blinking only on the fresnel effect.
219 | * @type {Uniform}
220 | * @default false
221 | */
222 | blinkFresnelOnly: new Uniform(parameters.blinkFresnelOnly !== undefined ? parameters.blinkFresnelOnly : true),
223 |
224 | /**
225 | * The opacity of the hologram.
226 | * @type {Uniform}
227 | * @default 1.0
228 | */
229 | hologramOpacity: new Uniform(parameters.hologramOpacity !== undefined ? parameters.hologramOpacity : 1.0),
230 | };
231 |
232 | this.clock = new Clock()
233 | this.setValues(parameters);
234 | this.depthTest = parameters.depthTest !== undefined ? parameters.depthTest : false;
235 | this.blending = parameters.blendMode !== undefined ? parameters.blendMode : AdditiveBlending;
236 | this.transparent = true;
237 | this.side = parameters.side !== undefined ? parameters.side : FrontSide;
238 |
239 | }
240 |
241 |
242 | update() {
243 | this.uniforms.time.value = this.clock.getElapsedTime();
244 | }
245 |
246 | }
247 |
248 | export default HolographicMaterial ;
--------------------------------------------------------------------------------