├── 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 ; --------------------------------------------------------------------------------