├── .editorconfig
├── .eslintrc.js
├── .gitignore
├── .nvmrc
├── .prettierrc
├── README.md
├── favicon.ico
├── package-lock.json
├── package.json
├── src
├── assets
│ ├── fonts
│ │ ├── FuturaStd-Bold.woff
│ │ ├── FuturaStd-Bold.woff2
│ │ ├── FuturaStd-Book.woff
│ │ ├── FuturaStd-Book.woff2
│ │ ├── FuturaStd-ExtraBold.woff
│ │ ├── FuturaStd-ExtraBold.woff2
│ │ ├── FuturaStd-Heavy.woff
│ │ ├── FuturaStd-Heavy.woff2
│ │ ├── FuturaStd-Light.woff
│ │ ├── FuturaStd-Light.woff2
│ │ ├── FuturaStd-Medium.woff
│ │ └── FuturaStd-Medium.woff2
│ └── images
│ │ └── clouds
│ │ ├── 1.jpg
│ │ └── 2.jpg
├── css
│ ├── fonts.css
│ └── main.css
├── data
│ ├── colors.json
│ └── index.js
├── js
│ ├── components
│ │ ├── Canvas
│ │ │ ├── Camera
│ │ │ │ ├── Controls.js
│ │ │ │ └── index.js
│ │ │ ├── Environment
│ │ │ │ ├── Background
│ │ │ │ │ └── index.js
│ │ │ │ ├── Cloud
│ │ │ │ │ └── index.js
│ │ │ │ └── index.js
│ │ │ ├── Sphere
│ │ │ │ └── index.js
│ │ │ └── index.js
│ │ └── UI
│ │ │ └── Credits
│ │ │ └── index.js
│ ├── helpers
│ │ ├── gui.js
│ │ └── loadingManager.js
│ ├── hooks
│ │ ├── index.js
│ │ ├── useAssets.js
│ │ ├── useDebugMode.js
│ │ ├── useImage.js
│ │ ├── useRawData.js
│ │ └── useTexture.js
│ └── index.js
├── pages
│ └── index.html
└── shaders
│ ├── cloud.frag
│ ├── cloud.vert
│ ├── default.vert
│ ├── environment.frag
│ └── levels.glsl
└── webpack.config.js
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = false
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | browser: true,
4 | es6: true,
5 | },
6 | extends: ["google"],
7 | globals: {
8 | Atomics: "readonly",
9 | SharedArrayBuffer: "readonly",
10 | },
11 | parserOptions: {
12 | ecmaFeatures: {
13 | jsx: true,
14 | },
15 | ecmaVersion: 2018,
16 | sourceType: "module",
17 | },
18 | plugins: ["react", "react-hooks"],
19 | rules: {
20 | indent: ["error", 2, { SwitchCase: 1 }],
21 | "valid-jsdoc": 0,
22 | "require-jsdoc": 0,
23 | "no-unused-vars": 1,
24 | "new-cap": 0,
25 | "prefer-template": 2,
26 | semi: ["error", "never"],
27 | "react/jsx-uses-react": "error",
28 | "react/jsx-uses-vars": "error",
29 | "react/jsx-first-prop-new-line": "error",
30 | "react/jsx-max-props-per-line": ["error", { maximum: 1 }],
31 | "react-hooks/rules-of-hooks": "error",
32 | "react-hooks/exhaustive-deps": 0,
33 | },
34 | }
35 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules/
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 10.15.3
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "endOfLine": "lf",
3 | "semi": false,
4 | "singleQuote": false,
5 | "tabWidth": 2,
6 | "trailingComma": "es5"
7 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # Procedural Clouds Extending Three.js' Sprite
3 |
4 | *Learn how to simulate a cloud on a Threejs' Sprite using React-Three-Fiber*, *by Robert Borghesi*
5 |
6 | 
7 |
8 | [Article on Codrops](https://tympanus.net/codrops/?p=46546)
9 |
10 | [Demo](http://tympanus.net/Tutorials/ProceduralClouds/)
11 |
12 |
13 | ## Instructions
14 |
15 | - `npm install` to install dependencies
16 | - `npm run dev` to start the server
17 | - `npm run build` to create the build
18 |
19 | If you want to understand why I used the `Sprite` material just add `/?debug=true` in the URL, it will enable the Orbit Controls and it will add some meshes.
20 |
21 |
22 | ## Credits
23 |
24 | [Threejs](https://threejs.org/) — [React Three Fiber](https://github.com/react-spring/react-three-fiber)
25 |
26 | This is part of two bigger projects made at [LOW](http://low.thebignow.it/), you can see them online on [LetGirlsDream](https://www.letgirlsdream.org/) and [1955Horsebit](http://1955horsebit.gucci.com/)
27 |
28 |
29 | ## License
30 | This resource can be used freely if integrated or build upon in personal or commercial projects such as websites, web apps and web templates intended for sale. It is not allowed to take the resource "as-is" and sell it, redistribute, re-publish it, or sell "pluginized" versions of it. Free plugins built using this resource should have a visible mention and link to the original work. Always consider the licenses of all included libraries, scripts and images used.
31 |
32 |
33 | ## Misc
34 |
35 | Follow **Robert**: [Twitter](https://twitter.com/dghez_),
36 |
37 | Follow Codrops: [Twitter](http://www.twitter.com/codrops), [Facebook](http://www.facebook.com/codrops), [Google+](https://plus.google.com/101095823814290637419), [GitHub](https://github.com/codrops), [Pinterest](http://www.pinterest.com/codrops/), [Instagram](https://www.instagram.com/codropsss/)
38 |
39 |
40 | [© Codrops 2019](http://www.codrops.com)
41 |
--------------------------------------------------------------------------------
/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dghez/THREEJS_Procedural-clouds/029dce52c5027d2f29753ce8ae5083b8a593374a/favicon.ico
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "procedural-clouds",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "dev": "cross-env NODE_ENV=dev webpack-dev-server --open --host 0.0.0.0 --port 3000",
8 | "build": "cross-env NODE_ENV=test webpack",
9 | "build-prod": "cross-env NODE_ENV=prod webpack"
10 | },
11 | "repository": {
12 | "type": "git",
13 | "url": "git@github.com:dghez/THREEJS_Procedural-cloud.git"
14 | },
15 | "keywords": [],
16 | "author": "",
17 | "license": "ISC",
18 | "devDependencies": {
19 | "@babel/core": "^7.5.5",
20 | "@babel/plugin-proposal-class-properties": "^7.5.5",
21 | "@babel/preset-env": "^7.5.5",
22 | "@babel/preset-react": "^7.0.0",
23 | "babel-loader": "^8.0.6",
24 | "babel-polyfill": "^6.26.0",
25 | "clean-webpack-plugin": "^3.0.0",
26 | "copy-webpack-plugin": "^5.0.4",
27 | "cross-env": "^7.0.0",
28 | "css-loader": "^3.1.0",
29 | "eslint": "^6.1.0",
30 | "eslint-config-google": "^0.13.0",
31 | "eslint-plugin-react": "^7.14.3",
32 | "eslint-plugin-react-hooks": "^1.6.1",
33 | "file-loader": "^4.1.0",
34 | "glslify-loader": "^2.0.0",
35 | "html-webpack-plugin": "^3.2.0",
36 | "raw-loader": "^3.1.0",
37 | "sass-loader": "^8.0.0",
38 | "style-loader": "^1.0.0",
39 | "webpack": "^4.39.1",
40 | "webpack-cli": "^3.3.6",
41 | "webpack-dev-server": "^3.7.2"
42 | },
43 | "dependencies": {
44 | "core-js": "^3.1.4",
45 | "dat.gui": "^0.7.6",
46 | "glsl-fractal-brownian-noise": "^1.1.0",
47 | "glsl-noise": "0.0.0",
48 | "interpolation": "^1.0.0",
49 | "lodash": "^4.17.15",
50 | "mobile-detect": "^1.4.3",
51 | "normalize.css": "^8.0.1",
52 | "pepjs": "^0.5.2",
53 | "query-string": "^6.8.2",
54 | "react": "^16.9",
55 | "react-dom": "^16.9.0",
56 | "react-three-fiber": "^4.0.7",
57 | "string": "^3.3.3",
58 | "styled-jsx": "^3.2.1",
59 | "three": "^0.112.0"
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/assets/fonts/FuturaStd-Bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dghez/THREEJS_Procedural-clouds/029dce52c5027d2f29753ce8ae5083b8a593374a/src/assets/fonts/FuturaStd-Bold.woff
--------------------------------------------------------------------------------
/src/assets/fonts/FuturaStd-Bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dghez/THREEJS_Procedural-clouds/029dce52c5027d2f29753ce8ae5083b8a593374a/src/assets/fonts/FuturaStd-Bold.woff2
--------------------------------------------------------------------------------
/src/assets/fonts/FuturaStd-Book.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dghez/THREEJS_Procedural-clouds/029dce52c5027d2f29753ce8ae5083b8a593374a/src/assets/fonts/FuturaStd-Book.woff
--------------------------------------------------------------------------------
/src/assets/fonts/FuturaStd-Book.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dghez/THREEJS_Procedural-clouds/029dce52c5027d2f29753ce8ae5083b8a593374a/src/assets/fonts/FuturaStd-Book.woff2
--------------------------------------------------------------------------------
/src/assets/fonts/FuturaStd-ExtraBold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dghez/THREEJS_Procedural-clouds/029dce52c5027d2f29753ce8ae5083b8a593374a/src/assets/fonts/FuturaStd-ExtraBold.woff
--------------------------------------------------------------------------------
/src/assets/fonts/FuturaStd-ExtraBold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dghez/THREEJS_Procedural-clouds/029dce52c5027d2f29753ce8ae5083b8a593374a/src/assets/fonts/FuturaStd-ExtraBold.woff2
--------------------------------------------------------------------------------
/src/assets/fonts/FuturaStd-Heavy.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dghez/THREEJS_Procedural-clouds/029dce52c5027d2f29753ce8ae5083b8a593374a/src/assets/fonts/FuturaStd-Heavy.woff
--------------------------------------------------------------------------------
/src/assets/fonts/FuturaStd-Heavy.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dghez/THREEJS_Procedural-clouds/029dce52c5027d2f29753ce8ae5083b8a593374a/src/assets/fonts/FuturaStd-Heavy.woff2
--------------------------------------------------------------------------------
/src/assets/fonts/FuturaStd-Light.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dghez/THREEJS_Procedural-clouds/029dce52c5027d2f29753ce8ae5083b8a593374a/src/assets/fonts/FuturaStd-Light.woff
--------------------------------------------------------------------------------
/src/assets/fonts/FuturaStd-Light.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dghez/THREEJS_Procedural-clouds/029dce52c5027d2f29753ce8ae5083b8a593374a/src/assets/fonts/FuturaStd-Light.woff2
--------------------------------------------------------------------------------
/src/assets/fonts/FuturaStd-Medium.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dghez/THREEJS_Procedural-clouds/029dce52c5027d2f29753ce8ae5083b8a593374a/src/assets/fonts/FuturaStd-Medium.woff
--------------------------------------------------------------------------------
/src/assets/fonts/FuturaStd-Medium.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dghez/THREEJS_Procedural-clouds/029dce52c5027d2f29753ce8ae5083b8a593374a/src/assets/fonts/FuturaStd-Medium.woff2
--------------------------------------------------------------------------------
/src/assets/images/clouds/1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dghez/THREEJS_Procedural-clouds/029dce52c5027d2f29753ce8ae5083b8a593374a/src/assets/images/clouds/1.jpg
--------------------------------------------------------------------------------
/src/assets/images/clouds/2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dghez/THREEJS_Procedural-clouds/029dce52c5027d2f29753ce8ae5083b8a593374a/src/assets/images/clouds/2.jpg
--------------------------------------------------------------------------------
/src/css/fonts.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Open+Sans&display=swap');
--------------------------------------------------------------------------------
/src/css/main.css:
--------------------------------------------------------------------------------
1 | html,
2 | body {
3 | box-sizing: border-box;
4 | background: #ababab;
5 | overscroll-behavior: none;
6 | width: 100%;
7 | height: 100%;
8 | }
9 |
10 | *,
11 | *:before,
12 | *:after {
13 | box-sizing: inherit;
14 | -webkit-font-smoothing: antialiased;
15 | -moz-font-smoothing: antialiased;
16 | -o-font-smoothing: antialiased;
17 | -moz-osx-font-smoothing: grayscale;
18 | text-rendering: optimizeLegibility;
19 | -webkit-tap-highlight-color: transparent;
20 | }
21 |
22 | ::selection {
23 | background: #e7e3f1;
24 | }
25 |
26 | ::-moz-selection {
27 | background: #e7e3f1;
28 | }
29 |
30 | a {
31 | color: inherit;
32 | text-decoration: none;
33 | }
34 |
35 | h1,
36 | h2,
37 | h3,
38 | h4,
39 | h5,
40 | h6,
41 | p {
42 | margin: 0;
43 | }
44 |
45 | #app {
46 | position: fixed;
47 | top: 0;
48 | left: 0;
49 | width: 100%;
50 | height: 100%;
51 | }
52 |
--------------------------------------------------------------------------------
/src/data/colors.json:
--------------------------------------------------------------------------------
1 | {
2 | "gradients": [
3 | {
4 | "top": "#0747ab",
5 | "bottom": "#6796b5",
6 | "spot1": "#a7b9c7",
7 | "spot2": "#a7b9c7"
8 | }
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/src/data/index.js:
--------------------------------------------------------------------------------
1 | import colors from './colors.json'
2 |
3 | export default {
4 | colors,
5 | }
6 |
--------------------------------------------------------------------------------
/src/js/components/Canvas/Camera/Controls.js:
--------------------------------------------------------------------------------
1 | import React, {useRef, useEffect} from 'react'
2 | import {extend, useFrame, useThree} from 'react-three-fiber'
3 |
4 | import {OrbitControls} from 'three/examples/jsm/controls/OrbitControls'
5 | extend({OrbitControls})
6 |
7 | export default () => {
8 | const ref = useRef()
9 | const {gl, camera} = useThree()
10 |
11 | useFrame(() => ref.current.update())
12 |
13 | useEffect(()=>{
14 | ref.current.object = camera
15 | }, [camera])
16 |
17 | return (
18 |
28 | )
29 | }
30 |
--------------------------------------------------------------------------------
/src/js/components/Canvas/Camera/index.js:
--------------------------------------------------------------------------------
1 | import React, {useRef, useEffect} from 'react'
2 | import {useThree} from 'react-three-fiber'
3 |
4 | import {useDebugMode} from '~js/hooks'
5 |
6 | import Controls from './Controls'
7 |
8 | export default () => {
9 | const camera = useRef()
10 | const {size, setDefaultCamera} = useThree()
11 | const {width, height} = size
12 | const debugMode = useDebugMode()
13 |
14 | useEffect( () => {
15 | setDefaultCamera(camera.current)
16 | }, [])
17 |
18 | return (
19 | <>
20 |
25 | {debugMode && }
26 | >
27 | )
28 | }
29 |
--------------------------------------------------------------------------------
/src/js/components/Canvas/Environment/Background/index.js:
--------------------------------------------------------------------------------
1 | import React, {useRef, useMemo} from 'react'
2 | import {BackSide, Color} from 'three'
3 | import {useRawData} from '~js/hooks'
4 |
5 | import vertex from '~shaders/default.vert'
6 | import fragment from '~shaders/environment.frag'
7 |
8 | export default () => {
9 | const mesh = useRef()
10 | const colorSteps = useRawData('colors.gradients')
11 | const radius = 8
12 |
13 | const uniforms = useMemo(() => {
14 | return {
15 | uTopColor: {value: new Color(colorSteps[0].top)},
16 | uBottomColor: {value: new Color(colorSteps[0].bottom)},
17 | uSpot1Color: {value: new Color(colorSteps[0].spot1)},
18 | uSpot1Position: {value: [0.4, 0.7]},
19 | uSpot2Color: {value: new Color(colorSteps[0].spot2)},
20 | uSpot2Position: {value: [0.6, 0.4]},
21 | }
22 | }, [])
23 |
24 | return (
25 | <>
26 |
30 |
33 |
42 |
43 | >
44 | )
45 | }
46 |
--------------------------------------------------------------------------------
/src/js/components/Canvas/Environment/Cloud/index.js:
--------------------------------------------------------------------------------
1 | import React, {useRef, useEffect, useMemo} from 'react'
2 | import {useFrame} from 'react-three-fiber'
3 | import {ShaderMaterial, UniformsUtils, ShaderLib} from 'three'
4 |
5 | import {useAssets, useTexture} from '~js/hooks'
6 | import gui from '~js/helpers/gui'
7 | import fragment from '~shaders/cloud.frag'
8 | import vertex from '~shaders/cloud.vert'
9 |
10 |
11 | export default ({size}) => {
12 | const group = useRef()
13 | const mesh = useRef()
14 | const [width, height] = size
15 |
16 | const src1 = useAssets('images/clouds/1.jpg')
17 | const t1 = useTexture(src1)
18 |
19 | const src2 = useAssets('images/clouds/2.jpg')
20 | const t2 = useTexture(src2)
21 |
22 | const myUniforms = useMemo(() => ({
23 | uTime: {value: 0},
24 | uTxtShape: {value: t1},
25 | uTxtCloudNoise: {value: t2},
26 | uFac1: {value: 17.8},
27 | uFac2: {value: 2.7},
28 | uTimeFactor1: {value: 0.002},
29 | uTimeFactor2: {value: 0.0015},
30 | uDisplStrenght1: {value: 0.04},
31 | uDisplStrenght2: {value: 0.08},
32 | }), [t1])
33 |
34 | const material = useMemo(() => {
35 | const mat = new ShaderMaterial({
36 | uniforms: {...UniformsUtils.clone(ShaderLib.sprite.uniforms), ...myUniforms},
37 | vertexShader: vertex,
38 | fragmentShader: fragment,
39 | transparent: true,
40 | })
41 |
42 | return mat
43 | }, [])
44 |
45 | useEffect( () => {
46 | if (material) {
47 | material.uniforms.uTxtShape.value = t1
48 | }
49 | }, [t1])
50 |
51 | useEffect( () => {
52 | if (material) {
53 | material.uniforms.uTxtCloudNoise.value = t2
54 | }
55 | }, [t2])
56 |
57 | useFrame(()=> {
58 | if (material) {
59 | material.uniforms.uTime.value += 1
60 | }
61 | })
62 |
63 | /**
64 | * DAT GUI
65 | */
66 | useEffect(() => {
67 | if (material) {
68 | gui.get((gui) => {
69 | gui.add(material.uniforms.uFac1, 'value', 0.00001, 30).step(0.1).name('1-ScaleFactor')
70 | gui.add(material.uniforms.uTimeFactor1, 'value', 0.00001, 0.009).step(0.0001).name('1-TimeFactor')
71 | gui.add(material.uniforms.uDisplStrenght1, 'value', 0.00001, 0.3).step(0.01).name('1-Strength')
72 | gui.add(material.uniforms.uTimeFactor2, 'value', 0.00001, 0.009).step(0.0001).name('2-TimeFactor')
73 | gui.add(material.uniforms.uFac2, 'value', 0.00001, 100).name('2-ScaleFactor')
74 | gui.add(material.uniforms.uDisplStrenght2, 'value', 0.00001, 0.3).step(0.01).name('2-Strength')
75 | })
76 | }
77 | }, [material])
78 |
79 |
80 | return (
81 |
82 |
87 |
90 |
94 |
95 |
96 | )
97 | }
98 |
--------------------------------------------------------------------------------
/src/js/components/Canvas/Environment/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import Background from './Background'
4 | import Cloud from './Cloud'
5 |
6 | export default () => {
7 | return (
8 | <>
9 |
10 |
11 | >
12 | )
13 | }
14 |
--------------------------------------------------------------------------------
/src/js/components/Canvas/Sphere/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {map} from 'lodash'
3 |
4 | const positions = [
5 | [1, 0, 0],
6 | [-1, 0, 0],
7 | [0, 1, 0],
8 | [0, -1, 0],
9 | [0, 0, 1],
10 | [0, 0, -1],
11 | ]
12 |
13 | const Sphere = ({position}) => {
14 | return (
15 |
18 |
22 |
26 |
27 | )
28 | }
29 |
30 | export default () => {
31 | const sphereMeshes = map(positions, (el, i) => {
32 | return
35 | })
36 |
37 | return (
38 | <>
39 | {sphereMeshes}
40 |
41 |
45 |
46 | >
47 | )
48 | }
49 |
--------------------------------------------------------------------------------
/src/js/components/Canvas/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {Canvas} from 'react-three-fiber'
3 |
4 | import {useDebugMode} from '~js/hooks'
5 |
6 | export default ({children}) => {
7 | return (
8 |
9 |
14 |
15 |
24 |
25 | )
26 | }
27 |
--------------------------------------------------------------------------------
/src/js/components/UI/Credits/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | export default () => {
4 | return (
5 |
29 | )
30 | }
31 |
--------------------------------------------------------------------------------
/src/js/helpers/gui.js:
--------------------------------------------------------------------------------
1 | import * as dat from 'dat.gui'
2 |
3 | let gui
4 | const callbacks = []
5 |
6 | const init = () => {
7 | if (!gui) {
8 | gui = new dat.GUI({width: 300})
9 |
10 | while (callbacks.length) {
11 | callbacks.shift()(gui)
12 | }
13 | }
14 | }
15 |
16 | export default {
17 | init,
18 | get: (callback) => {
19 | if (gui) {
20 | callback(gui)
21 | } else {
22 | callbacks.push(callback)
23 | }
24 | },
25 | }
26 |
--------------------------------------------------------------------------------
/src/js/helpers/loadingManager.js:
--------------------------------------------------------------------------------
1 | import {LoadingManager, TextureLoader, RepeatWrapping} from 'three'
2 |
3 | import {GLTFLoader} from 'three/examples/jsm/loaders/GLTFLoader'
4 | import {DRACOLoader} from 'three/examples/jsm/loaders/DRACOLoader'
5 |
6 | const loadingManager = new LoadingManager()
7 | const textureLoader = new TextureLoader(loadingManager)
8 |
9 | const gltfLoader = new GLTFLoader(loadingManager)
10 | const dracoLoader = new DRACOLoader()
11 | dracoLoader.setDecoderPath('/static-threejs/draco/')
12 | gltfLoader.setDRACOLoader(dracoLoader)
13 |
14 | const onLoadCallbacks = []
15 | const onProgressCallbacks = []
16 |
17 | loadingManager.onLoad = () => {
18 | while (onLoadCallbacks.length) {
19 | onLoadCallbacks.shift().call()
20 | }
21 | }
22 |
23 | loadingManager.onProgress = (item, loaded, total) => {
24 | const _onProgressCallbacks = [...onProgressCallbacks]
25 |
26 | while (_onProgressCallbacks.length) {
27 | _onProgressCallbacks.shift()(loaded / total)
28 | }
29 | }
30 |
31 | const loadTexture = (src, callback) => {
32 | const texture = textureLoader.load(src, () => {
33 | typeof callback === 'function' && callback(texture)
34 | })
35 |
36 | texture.wrapS = texture.wrapT = RepeatWrapping
37 |
38 | return texture
39 | }
40 |
41 | const loadModelGLTF = (src) => {
42 | return new Promise((resolve, reject) => {
43 | gltfLoader.load(src, (resources) => {
44 | resolve(resources.scene)
45 | })
46 | })
47 | }
48 |
49 | const loadGLTF = async (modelSrc, diffuseSrc, normalSrc, aoSrc) => {
50 | const mesh = await loadModelGLTF(modelSrc)
51 | const diffuseMap = loadTexture(diffuseSrc)
52 | const normalMap = loadTexture(normalSrc)
53 | const aoMap = loadTexture(aoSrc)
54 |
55 | return {mesh, diffuseMap, normalMap, aoMap}
56 | }
57 |
58 | const onLoad = (callback) => {
59 | onLoadCallbacks.push(callback)
60 |
61 | window.aa = loadingManager
62 | }
63 |
64 | const onProgress = (callback) => {
65 | onProgressCallbacks.push(callback)
66 | }
67 |
68 | export {loadGLTF, loadTexture, onLoad, onProgress}
69 |
--------------------------------------------------------------------------------
/src/js/hooks/index.js:
--------------------------------------------------------------------------------
1 | import useDebugMode from './useDebugMode'
2 | import useRawData from './useRawData'
3 | import useAssets from './useAssets'
4 | import useImage from './useImage'
5 | import useTexture from './useTexture'
6 |
7 | export {
8 | useDebugMode,
9 | useRawData,
10 | useAssets,
11 | useImage,
12 | useTexture,
13 | }
14 |
--------------------------------------------------------------------------------
/src/js/hooks/useAssets.js:
--------------------------------------------------------------------------------
1 | export default function useAssets(path) {
2 | const src = ''
3 |
4 | if (path) {
5 | try {
6 | return require(`~assets/${path}`)
7 | } catch (err) {
8 | console.warn(err)
9 | }
10 | }
11 |
12 | return src
13 | }
14 |
--------------------------------------------------------------------------------
/src/js/hooks/useDebugMode.js:
--------------------------------------------------------------------------------
1 | import {useState, useEffect} from 'react'
2 | import {get} from 'lodash'
3 |
4 | function parseQueryString(url) {
5 | const vars = {}
6 |
7 | url.replace(/[?&]+([^=&]+)=([^&]*)/gi, (m, key, value) => {
8 | vars[key] = value
9 | })
10 |
11 | return vars
12 | }
13 |
14 | export default function useDebugMode() {
15 | const [debugMode, setDebugMode] = useState(false)
16 | const {search} = window.location
17 |
18 | useEffect(() => {
19 | const parsed = parseQueryString(search)
20 |
21 | setDebugMode(get(parsed, 'debug') === 'true')
22 | }, [location])
23 |
24 | return debugMode
25 | }
26 |
--------------------------------------------------------------------------------
/src/js/hooks/useImage.js:
--------------------------------------------------------------------------------
1 | import {useState, useEffect} from 'react'
2 |
3 | export default (src) => {
4 | const [image, setImage] = useState(null)
5 |
6 | useEffect(() => {
7 | const image = new Image()
8 |
9 | image.src = src
10 |
11 | image.onload = () => setImage(image)
12 | }, [src])
13 |
14 | return image
15 | }
16 |
--------------------------------------------------------------------------------
/src/js/hooks/useRawData.js:
--------------------------------------------------------------------------------
1 | import {useMemo} from 'react'
2 | import data from '~data'
3 | import {get} from 'lodash'
4 |
5 | export default function useRawData(key) {
6 | const response = useMemo(() => {
7 | return key ? get(data, key, '') : data
8 | }, [key])
9 |
10 | return response
11 | }
12 |
--------------------------------------------------------------------------------
/src/js/hooks/useTexture.js:
--------------------------------------------------------------------------------
1 | import {useState, useEffect} from 'react'
2 | import {Texture} from 'three'
3 |
4 | import {loadTexture} from '~js/helpers/loadingManager'
5 |
6 | export default function useTexture(src) {
7 | const [texture, setTexture] = useState(new Texture())
8 |
9 | useEffect(()=> {
10 | loadTexture(src, setTexture)
11 | }, [])
12 |
13 | return texture
14 | }
15 |
--------------------------------------------------------------------------------
/src/js/index.js:
--------------------------------------------------------------------------------
1 | import 'normalize.css'
2 | import '~css/fonts.css'
3 | import '~css/main.css'
4 | import 'core-js/stable'
5 | import 'regenerator-runtime/runtime'
6 |
7 | import React, {useEffect} from 'react'
8 | import {render} from 'react-dom'
9 |
10 | import gui from '~js/helpers/gui'
11 | import {useDebugMode} from '~js/hooks'
12 |
13 | import Canvas from '~js/components/Canvas'
14 | import Camera from '~js/components/Canvas/Camera'
15 | import Sphere from '~js/components/Canvas/Sphere'
16 | import Environment from '~js/components/Canvas/Environment'
17 | import Credits from '~js/components/UI/Credits'
18 |
19 | /**
20 | * app
21 | */
22 | const App = () => {
23 | const debugMode = useDebugMode()
24 |
25 | useEffect(() => {
26 | gui.init()
27 | }, [])
28 |
29 | return (
30 | <>
31 |
32 |
37 | >
38 | )
39 | }
40 |
41 | /**
42 | * render app
43 | */
44 | render(
45 | ,
46 | document.getElementById('app')
47 | )
48 |
--------------------------------------------------------------------------------
/src/pages/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Document
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/shaders/cloud.frag:
--------------------------------------------------------------------------------
1 | #pragma glslify: fbm3d = require('glsl-fractal-brownian-noise/3d')
2 | #pragma glslify: snoise3 = require(glsl-noise/simplex/3d)
3 | #pragma glslify: levels = require('./levels')
4 |
5 |
6 | uniform sampler2D uTxtShape;
7 | uniform sampler2D uTxtCloudNoise;
8 | uniform float uTime;
9 |
10 | uniform float uFac1;
11 | uniform float uFac2;
12 | uniform float uTimeFactor1;
13 | uniform float uTimeFactor2;
14 | uniform float uDisplStrenght1;
15 | uniform float uDisplStrenght2;
16 |
17 | varying vec2 vUv;
18 |
19 | void main() {
20 | vec2 newUv = vUv;
21 |
22 | vec4 txtNoise1 = texture2D(uTxtCloudNoise, vec2(vUv.x + uTime * 0.0001, vUv.y - uTime * 0.00014)); // noise txt
23 | vec4 txtNoise2 = texture2D(uTxtCloudNoise, vec2(vUv.x - uTime * 0.00002, vUv.y + uTime * 0.000017 + 0.2)); // noise txt
24 |
25 | float noiseBig = fbm3d(vec3(vUv * uFac1, uTime * uTimeFactor1), 4)+ 1.0 * 0.5;
26 | newUv += noiseBig * uDisplStrenght1;
27 |
28 | float noiseSmall = snoise3(vec3(newUv * uFac2, uTime * uTimeFactor2));
29 |
30 | newUv += noiseSmall * uDisplStrenght2;
31 |
32 | vec4 txtShape = texture2D(uTxtShape, newUv);
33 |
34 | float alpha = levels((txtNoise1 + txtNoise2) * 0.6, 0.2, 0.4, 0.7).r;
35 | alpha *= txtShape.r;
36 |
37 | gl_FragColor = vec4(vec3(0.95,0.95,0.95), alpha);
38 | }
39 |
--------------------------------------------------------------------------------
/src/shaders/cloud.vert:
--------------------------------------------------------------------------------
1 | uniform float rotation;
2 | uniform vec2 center;
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | varying vec2 vUv;
10 |
11 | void main() {
12 | // #include
13 | vUv = uv;
14 |
15 | vec4 mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );
16 | vec2 scale;
17 | scale.x = length( vec3( modelMatrix[ 0 ].x, modelMatrix[ 0 ].y, modelMatrix[ 0 ].z ) );
18 | scale.y = length( vec3( modelMatrix[ 1 ].x, modelMatrix[ 1 ].y, modelMatrix[ 1 ].z ) );
19 |
20 | vec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale;
21 | vec2 rotatedPosition;
22 | rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;
23 | rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;
24 | mvPosition.xy += rotatedPosition;
25 | gl_Position = projectionMatrix * mvPosition;
26 | #include
27 | #include
28 | #include
29 | }
--------------------------------------------------------------------------------
/src/shaders/default.vert:
--------------------------------------------------------------------------------
1 | varying vec2 vUv;
2 |
3 | void main() {
4 |
5 | vUv = uv;
6 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
7 | }
--------------------------------------------------------------------------------
/src/shaders/environment.frag:
--------------------------------------------------------------------------------
1 | uniform vec3 uTopColor;
2 | uniform vec3 uBottomColor;
3 | uniform vec3 uSpot1Color;
4 | uniform vec3 uSpot2Color;
5 | uniform vec2 uSpot1Position;
6 | uniform vec2 uSpot2Position;
7 |
8 | varying vec2 vUv;
9 |
10 | float distanceFromPoint(vec2 uv, vec2 point, float max){
11 | float d = distance(uv, point);
12 | d = smoothstep(0.0, max, d);
13 | d = 1.0 - d;
14 | return d;
15 | }
16 |
17 | void main() {
18 |
19 | float d1 = distanceFromPoint(vUv, vec2(uSpot1Position), 0.3);
20 | float d2 = distanceFromPoint(vUv, vec2(uSpot2Position), 0.4);
21 |
22 | vec4 colorSpot1 = vec4(uSpot1Color, 1.0 * d1 * 0.8);
23 | vec4 colorSpot2 = vec4(uSpot2Color, 1.0 * d2 * 0.8);
24 | vec4 verticalGradient = vec4(mix(uBottomColor,uTopColor, vUv.y), 1.0);
25 | vec4 mixVS1 = mix(verticalGradient, colorSpot1, colorSpot1.a);
26 | vec4 final = mix(mixVS1, colorSpot2, colorSpot2.a);
27 |
28 | gl_FragColor = vec4(final.rgb, 1.0);
29 | }
30 |
--------------------------------------------------------------------------------
/src/shaders/levels.glsl:
--------------------------------------------------------------------------------
1 | vec4 gammaCorrect(vec4 color, float gamma){
2 | return pow(color, vec4(1.0 / gamma));
3 | }
4 |
5 | vec4 levelRange(vec4 color, float minInput, float maxInput){
6 | return min(max(color - vec4(minInput), vec4(0.0)) / (vec4(maxInput) - vec4(minInput)), vec4(1.0));
7 | }
8 |
9 | vec4 levels(vec4 color, float minInput, float gamma, float maxInput){
10 | return gammaCorrect(levelRange(color, minInput, maxInput), gamma);
11 | }
12 |
13 | #pragma glslify: export(levels);
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const {CleanWebpackPlugin} = require('clean-webpack-plugin')
3 | const HtmlWebpackPlugin = require('html-webpack-plugin')
4 | const CopyPlugin = require('copy-webpack-plugin')
5 |
6 | const env = process.env.NODE_ENV
7 | const devMode = env === 'dev'
8 |
9 | module.exports = {
10 | mode: devMode ? 'development' : 'production',
11 | entry: './src/js',
12 | output: {
13 | filename: devMode === 'development' ? 'assets/js/main.js' : 'assets/js/main.[contenthash].js',
14 | publicPath: '/',
15 | },
16 | resolve: {
17 | alias: {
18 | '~js': path.resolve(__dirname, 'src/js'),
19 | '~css': path.resolve(__dirname, 'src/css'),
20 | '~data': path.resolve(__dirname, 'src/data'),
21 | '~assets': path.resolve(__dirname, 'src/assets'),
22 | '~shaders': path.resolve(__dirname, 'src/shaders'),
23 | },
24 | },
25 | plugins: [
26 | new CleanWebpackPlugin(),
27 | new CopyPlugin([
28 | {
29 | from: './src/static/**/*',
30 | to: './',
31 | transformPath(targetPath) {
32 | return targetPath.replace('src/static/', '')
33 | },
34 | },
35 | ]),
36 | new HtmlWebpackPlugin({
37 | template: './src/pages/index.html',
38 | filename: 'index.html',
39 | env,
40 | }),
41 | ],
42 | module: {
43 | rules: [
44 | {
45 | test: /\.pug$/,
46 | loader: 'pug-loader',
47 | options: {
48 | pretty: true,
49 | root: path.resolve(__dirname, 'src'),
50 | },
51 | },
52 | {
53 | test: /\.(jpg|png|svg|gif|mp4)$/,
54 | loader: 'file-loader',
55 | options: {
56 | name: 'assets/images/[hash].[ext]',
57 | },
58 | },
59 | {
60 | test: /\.(dae|obj|gltf)$/,
61 | loader: 'file-loader',
62 | options: {
63 | name: 'assets/objects/[name].[ext]',
64 | },
65 | },
66 | {
67 | test: /\.(woff|woff2|eot|ttf|otf)$/,
68 | loader: 'file-loader',
69 | options: {
70 | name: 'assets/fonts/[name].[ext]',
71 | },
72 | },
73 | {
74 | test: /\.(sa|sc|c)ss$/,
75 | use: ['style-loader', 'css-loader'],
76 | },
77 | {
78 | test: /\.(frag|vert|glsl)$/,
79 | exclude: /node_modules/,
80 | use: ['raw-loader', 'glslify-loader'],
81 | },
82 | {
83 | test: /\.m?js$/,
84 | exclude: /node_modules\/(?!(swiper|dom7)\/).*/,
85 | use: {
86 | loader: 'babel-loader',
87 | options: {
88 | presets: ['@babel/preset-env', '@babel/preset-react'],
89 | plugins: ['styled-jsx/babel', ['@babel/plugin-proposal-class-properties', {loose: true}]],
90 | },
91 | },
92 | },
93 | ],
94 | },
95 | }
96 |
--------------------------------------------------------------------------------