├── .editorconfig ├── .gitignore ├── README.md ├── assets └── README.md ├── components ├── Artwork │ ├── index.vue │ └── js │ │ ├── ArtworkGL.js │ │ ├── Common.js │ │ ├── Shape.js │ │ ├── calcShape.js │ │ └── glsl │ │ ├── shape.frag │ │ └── shape.vert ├── Nav.vue └── README.md ├── layouts ├── README.md ├── default.vue └── error.vue ├── middleware └── README.md ├── nuxt.config.js ├── package.json ├── pages ├── README.md ├── about.vue ├── contact.vue └── index.vue ├── plugins └── README.md ├── static ├── README.md └── favicon.ico ├── store └── README.md ├── utils └── event-bus.js └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # 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 = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Node template 3 | # Logs 4 | /logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | 10 | # Runtime data 11 | pids 12 | *.pid 13 | *.seed 14 | *.pid.lock 15 | 16 | # Directory for instrumented libs generated by jscoverage/JSCover 17 | lib-cov 18 | 19 | # Coverage directory used by tools like istanbul 20 | coverage 21 | 22 | # nyc test coverage 23 | .nyc_output 24 | 25 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 26 | .grunt 27 | 28 | # Bower dependency directory (https://bower.io/) 29 | bower_components 30 | 31 | # node-waf configuration 32 | .lock-wscript 33 | 34 | # Compiled binary addons (https://nodejs.org/api/addons.html) 35 | build/Release 36 | 37 | # Dependency directories 38 | node_modules/ 39 | jspm_packages/ 40 | 41 | # TypeScript v1 declaration files 42 | typings/ 43 | 44 | # Optional npm cache directory 45 | .npm 46 | 47 | # Optional eslint cache 48 | .eslintcache 49 | 50 | # Optional REPL history 51 | .node_repl_history 52 | 53 | # Output of 'npm pack' 54 | *.tgz 55 | 56 | # Yarn Integrity file 57 | .yarn-integrity 58 | 59 | # dotenv environment variables file 60 | .env 61 | 62 | # parcel-bundler cache (https://parceljs.org/) 63 | .cache 64 | 65 | # next.js build output 66 | .next 67 | 68 | # nuxt.js build output 69 | .nuxt 70 | 71 | # Nuxt generate 72 | dist 73 | 74 | # vuepress build output 75 | .vuepress/dist 76 | 77 | # Serverless directories 78 | .serverless 79 | 80 | # IDE / Editor 81 | .idea 82 | 83 | # Service worker 84 | sw.* 85 | 86 | # Mac OSX 87 | .DS_Store 88 | 89 | # Vim swap files 90 | *.swp 91 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # threejs-nuxt-sample 2 | 3 | > My geometric Nuxt.js project 4 | 5 | ## Build Setup 6 | 7 | ``` bash 8 | # install dependencies 9 | $ yarn install 10 | 11 | # serve with hot reload at localhost:3000 12 | $ yarn dev 13 | 14 | # build for production and launch server 15 | $ yarn build 16 | $ yarn start 17 | 18 | # generate static project 19 | $ yarn generate 20 | ``` 21 | 22 | For detailed explanation on how things work, check out [Nuxt.js docs](https://nuxtjs.org). 23 | -------------------------------------------------------------------------------- /assets/README.md: -------------------------------------------------------------------------------- 1 | # ASSETS 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your un-compiled assets such as LESS, SASS, or JavaScript. 6 | 7 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#webpacked). 8 | -------------------------------------------------------------------------------- /components/Artwork/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 35 | 45 | -------------------------------------------------------------------------------- /components/Artwork/js/ArtworkGL.js: -------------------------------------------------------------------------------- 1 | import Common from "./Common"; 2 | import Shape from "./Shape" 3 | 4 | export default class ArtworkGL{ 5 | constructor(props){ 6 | this.props = props; 7 | this.init(); 8 | } 9 | 10 | init(){ 11 | Common.init(this.props.$canvas); 12 | this.shape = new Shape(); 13 | window.addEventListener("resize", this.resize.bind(this)); 14 | this.loop(); 15 | } 16 | 17 | resize(){ 18 | Common.resize(); 19 | } 20 | 21 | loop(){ 22 | this.render(); 23 | requestAnimationFrame(this.loop.bind(this)); 24 | } 25 | 26 | render(){ 27 | this.shape.update(); 28 | Common.render(); 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /components/Artwork/js/Common.js: -------------------------------------------------------------------------------- 1 | import * as THREE from "three"; 2 | 3 | class Common{ 4 | constructor(){ 5 | this.scene = null; 6 | this.camera = null; 7 | this.renderer = null; 8 | 9 | this.size = { 10 | windowW: null, 11 | windowH: null 12 | }; 13 | 14 | this.clock = null; 15 | 16 | this.time = { 17 | total: null, 18 | delta: null 19 | }; 20 | } 21 | 22 | init($canvas){ 23 | this.setSize(); 24 | 25 | this.scene = new THREE.Scene(); 26 | this.camera = new THREE.PerspectiveCamera( 27 | 45, 28 | this.size.windowW / this.size.windowH, 29 | 0.1, 30 | 10000 31 | ); 32 | this.camera.position.set(0, 10, -10); 33 | this.camera.lookAt(this.scene.position); 34 | 35 | this.renderer = new THREE.WebGLRenderer({ 36 | canvas: $canvas 37 | }); 38 | 39 | this.renderer.setPixelRatio(window.devicePixelRatio); 40 | 41 | this.renderer.setClearColor(0xEAF2F5); 42 | this.renderer.setSize(this.size.windowW, this.size.windowH); 43 | 44 | this.clock = new THREE.Clock(); 45 | this.clock.start(); 46 | } 47 | 48 | setSize(){ 49 | this.size = { 50 | windowW: window.innerWidth, 51 | windowH: window.innerHeight 52 | } 53 | } 54 | 55 | resize(){ 56 | this.setSize(); 57 | this.camera.aspect = this.size.windowW / this.size.windowH; 58 | this.camera.updateProjectionMatrix(); 59 | this.renderer.setSize(this.size.windowW, this.size.windowH); 60 | } 61 | 62 | render(){ 63 | this.time.delta = this.clock.getDelta(); 64 | this.time.total += this.delta; 65 | 66 | this.renderer.render(this.scene, this.camera); 67 | } 68 | } 69 | 70 | export default new Common(); -------------------------------------------------------------------------------- /components/Artwork/js/Shape.js: -------------------------------------------------------------------------------- 1 | import * as THREE from "three"; 2 | import Common from "./Common"; 3 | import calcShape from "./calcShape"; 4 | 5 | import vertexShader from "./glsl/shape.vert"; 6 | import fragmentShader from "./glsl/shape.frag"; 7 | 8 | import EventBus from "~/utils/event-bus"; 9 | 10 | export default class Shape{ 11 | constructor(){ 12 | this.segments = 80; 13 | this.init(); 14 | 15 | } 16 | 17 | init(){ 18 | EventBus.$on("TRANSITION", this.onTransition.bind(this)); 19 | 20 | this.transitionTarget = new THREE.Vector4(0, 0, 0, 0); 21 | 22 | this.geometry = new THREE.BufferGeometry(); 23 | this.setPositions(); 24 | this.currentNum = 0; 25 | this.uniforms = { 26 | uProgress: { 27 | value: new THREE.Vector4(0, 0, 0, 0) 28 | }, 29 | }; 30 | 31 | this.material = new THREE.ShaderMaterial({ 32 | uniforms: this.uniforms, 33 | vertexShader: vertexShader, 34 | fragmentShader: fragmentShader, 35 | flatShading: true, 36 | side: THREE.DoubleSide, 37 | // wireframe: true 38 | }); 39 | 40 | this.mesh = new THREE.Mesh(this.geometry, this.material); 41 | this.mesh.rotation.x = Math.PI / 4; 42 | Common.scene.add(this.mesh); 43 | } 44 | 45 | onTransition(path){ 46 | switch(path){ 47 | case "index": 48 | this.transitionTarget.set(1, 0, 0, 0); 49 | break; 50 | 51 | case "about": 52 | this.transitionTarget.set(0, 1, 0, 0); 53 | break; 54 | 55 | case "contact": 56 | this.transitionTarget.set(0, 0, 1, 0); 57 | break; 58 | 59 | default: 60 | this.transitionTarget.set(0, 0, 0, 1); 61 | break; 62 | 63 | } 64 | } 65 | 66 | setPositions(){ 67 | const positions = []; 68 | const vertices1 = []; 69 | const vertices2 = []; 70 | const vertices3 = []; 71 | const vertices4 = []; 72 | 73 | 74 | 75 | for(let i = 0; i <= this.segments; i++){ 76 | const v = i / this.segments; 77 | for(let j = 0; j <= this.segments; j++){ 78 | const u = j / this.segments; 79 | const shape1 = calcShape.sphere(u, v); 80 | const shape2 = calcShape.twistTorus(u, v); 81 | const shape3 = calcShape.torus(u, v); 82 | const shape4 = calcShape.ribbon(u, v); 83 | 84 | vertices1.push(shape1.x, shape1.y, shape1.z); 85 | vertices2.push(shape2.x, shape2.y, shape2.z); 86 | vertices3.push(shape3.x, shape3.y, shape3.z); 87 | vertices4.push(shape4.x, shape4.y, shape4.z); 88 | 89 | positions.push(0, 0, 0); 90 | } 91 | } 92 | 93 | // generate indices 94 | const indices = []; 95 | var sliceCount = this.segments + 1; 96 | for ( let i = 0; i < this.segments; i ++ ) { 97 | for ( let j = 0; j < this.segments; j ++ ) { 98 | 99 | var a = i * sliceCount + j; 100 | var b = i * sliceCount + j + 1; 101 | var c = ( i + 1 ) * sliceCount + j + 1; 102 | var d = ( i + 1 ) * sliceCount + j; 103 | 104 | // faces one and two 105 | indices.push( a, b, d ); 106 | indices.push( b, c, d ); 107 | } 108 | } 109 | 110 | this.setAttribute("aShape1", vertices1); 111 | this.setAttribute("aShape2", vertices2); 112 | this.setAttribute("aShape3", vertices3); 113 | this.setAttribute("aShape4", vertices4); 114 | 115 | this.setAttribute("position", positions); 116 | 117 | this.geometry.setIndex( indices ); 118 | } 119 | 120 | setAttribute(name, vertices){ 121 | var array = new Float32Array( vertices ); 122 | this.geometry.setAttribute( name, new THREE.BufferAttribute( array, 3 ) ); 123 | } 124 | 125 | update(){ 126 | this.mesh.rotation.y += Common.time.delta; 127 | const easing = Math.min(1.0, 3.5 * Common.time.delta) 128 | this.uniforms.uProgress.value.lerp(this.transitionTarget, easing); 129 | } 130 | } -------------------------------------------------------------------------------- /components/Artwork/js/calcShape.js: -------------------------------------------------------------------------------- 1 | export default { 2 | sphere: function(x, y){ 3 | const u = -x * 2.0 * Math.PI; 4 | const v = y * Math.PI; 5 | 6 | const _x = Math.sin(u) * Math.sin(v); 7 | const _y = Math.cos(u) * Math.sin(v); 8 | const _z = Math.cos(v); 9 | 10 | return {x: _x * 3.0, y: _y * 3.0, z: _z * 3.0}; 11 | }, 12 | twistTorus: function(x, y){ 13 | const a = 3.0; 14 | const n = 3.0; 15 | const m = 1.0; 16 | 17 | const u = x * 4.0 * Math.PI; 18 | const v = y * 2.0 * Math.PI; 19 | 20 | const _x = (a + Math.cos(n * u / 2.0) * Math.sin(v) - Math.sin(n * u / 2.0) * Math.sin(2.0 * v)) * Math.cos(m * u / 2.0); 21 | const _y = (a + Math.cos(n * u / 2.0) * Math.sin(v) - Math.sin(n * u / 2.0) * Math.sin(2.0 * v)) * Math.sin(m * u / 2.0); 22 | const _z = Math.sin(n * u / 2.0) * Math.sin(v) + Math.cos(n * u / 2.0) * Math.sin(2.0 * v); 23 | 24 | return {x: _x, y: _y, z: _z}; 25 | }, 26 | torus: function(x, y){ 27 | const a = 3.0; 28 | const n = 1.5; 29 | 30 | const u = x * 2.0 * Math.PI; 31 | const v = y * 2.0 * Math.PI; 32 | 33 | const _x = ( a + n * Math.cos( v ) ) * Math.cos( u ); 34 | const _y = ( a + n * Math.cos( v ) ) * Math.sin( u ); 35 | const _z = n * Math.sin( v ); 36 | 37 | return {x: _x, y: _y, z: _z}; 38 | }, 39 | ribbon: function(x, y){ 40 | const u = -x * 2.0 * Math.PI; 41 | const v = y * Math.PI; 42 | 43 | const _x = Math.sin(u) * Math.sin(v); 44 | const _y = Math.cos(u) * Math.cos(v); 45 | const _z = Math.cos(v); 46 | 47 | return {x: _x * 3.0, y: _y * 3.0, z: _z * 3.0}; 48 | } 49 | } -------------------------------------------------------------------------------- /components/Artwork/js/glsl/shape.frag: -------------------------------------------------------------------------------- 1 | varying vec3 vPos; 2 | 3 | const vec3 objColor = vec3(1.0); 4 | 5 | const vec3 hemiLight_g = vec3(0.86,0.86,0.86); 6 | 7 | const vec3 hemiLight_s_1 = vec3(0.5882352941176471,0.8274509803921568,0.8823529411764706); 8 | const vec3 hemiLight_s_2 = vec3(0.9686274509803922,0.8509803921568627,0.6666666666666666); 9 | const vec3 hemiLight_s_3 = vec3(0.8784313725490196,0.5882352941176471,0.7647058823529411); 10 | 11 | const vec3 dirLight = vec3(0.16); 12 | const vec3 dirLight_2 = vec3(0.02); 13 | 14 | 15 | const vec3 hemiLightPos_1 = vec3(100.0, 100.0, -100.0); 16 | const vec3 hemiLightPos_2 = vec3(-100.0, -100.0, 100.0); 17 | const vec3 hemiLightPos_3 = vec3(-100.0, 100.0, 100.0); 18 | 19 | const vec3 dirLightPos = vec3(-50, 50, 50); 20 | const vec3 dirLightPos_2 = vec3(30, -50, -50); 21 | 22 | vec3 calcIrradiance_hemi(vec3 newNormal, vec3 lightPos, vec3 grd, vec3 sky){ 23 | float dotNL = dot(newNormal, normalize(lightPos)); 24 | float hemiDiffuseWeight = 0.5 * dotNL + 0.5; 25 | 26 | return mix(grd, sky, hemiDiffuseWeight); 27 | } 28 | 29 | vec3 calcIrradiance_dir(vec3 newNormal, vec3 lightPos, vec3 light){ 30 | float dotNL = dot(newNormal, normalize(lightPos)); 31 | 32 | return light * max(0.0, dotNL); 33 | } 34 | 35 | void main(){ 36 | vec3 normal = normalize(cross(dFdx(vPos), dFdy(vPos))); 37 | 38 | vec3 hemiColor = vec3(0.0); 39 | hemiColor += calcIrradiance_hemi(normal, hemiLightPos_1, hemiLight_g, hemiLight_s_1) * 0.38; 40 | hemiColor += calcIrradiance_hemi(normal, hemiLightPos_2, hemiLight_g, hemiLight_s_2) * 0.26; 41 | hemiColor += calcIrradiance_hemi(normal, hemiLightPos_3, hemiLight_g, hemiLight_s_3) * 0.36; 42 | 43 | vec3 dirColor = vec3(0.0); 44 | dirColor += calcIrradiance_dir(normal, dirLightPos, dirLight); 45 | dirColor += calcIrradiance_dir(normal, dirLightPos_2, dirLight_2); 46 | 47 | 48 | vec3 color = objColor * hemiColor; 49 | color += dirColor; 50 | 51 | 52 | gl_FragColor = vec4(color, 1.0); 53 | } -------------------------------------------------------------------------------- /components/Artwork/js/glsl/shape.vert: -------------------------------------------------------------------------------- 1 | attribute vec3 aShape1; 2 | attribute vec3 aShape2; 3 | attribute vec3 aShape3; 4 | attribute vec3 aShape4; 5 | 6 | uniform vec4 uProgress; 7 | 8 | varying vec3 vPos; 9 | 10 | void main(){ 11 | vec3 pos = aShape1 * uProgress.x + aShape2 * uProgress.y + aShape3 * uProgress.z + aShape4 * uProgress.w; 12 | vec4 worldPosition = modelMatrix * vec4(pos, 1.0); 13 | vPos = worldPosition.xyz; 14 | 15 | gl_Position = projectionMatrix * viewMatrix * worldPosition; 16 | } -------------------------------------------------------------------------------- /components/Nav.vue: -------------------------------------------------------------------------------- 1 | 16 | 34 | -------------------------------------------------------------------------------- /components/README.md: -------------------------------------------------------------------------------- 1 | # COMPONENTS 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | The components directory contains your Vue.js Components. 6 | 7 | _Nuxt.js doesn't supercharge these components._ 8 | -------------------------------------------------------------------------------- /layouts/README.md: -------------------------------------------------------------------------------- 1 | # LAYOUTS 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your Application Layouts. 6 | 7 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/views#layouts). 8 | -------------------------------------------------------------------------------- /layouts/default.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 20 | 21 | 23 | -------------------------------------------------------------------------------- /layouts/error.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 15 | 16 | -------------------------------------------------------------------------------- /middleware/README.md: -------------------------------------------------------------------------------- 1 | # MIDDLEWARE 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your application middleware. 6 | Middleware let you define custom functions that can be run before rendering either a page or a group of pages. 7 | 8 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing#middleware). 9 | -------------------------------------------------------------------------------- /nuxt.config.js: -------------------------------------------------------------------------------- 1 | 2 | export default { 3 | mode: 'universal', 4 | /* 5 | ** Headers of the page 6 | */ 7 | head: { 8 | title: process.env.npm_package_name || '', 9 | meta: [ 10 | { charset: 'utf-8' }, 11 | { name: 'viewport', content: 'width=device-width, initial-scale=1' }, 12 | { hid: 'description', name: 'description', content: process.env.npm_package_description || '' } 13 | ], 14 | link: [ 15 | { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' } 16 | ] 17 | }, 18 | /* 19 | ** Customize the progress-bar color 20 | */ 21 | loading: { color: '#fff' }, 22 | /* 23 | ** Global CSS 24 | */ 25 | css: [ 26 | ], 27 | /* 28 | ** Plugins to load before mounting the App 29 | */ 30 | plugins: [ 31 | ], 32 | /* 33 | ** Nuxt.js dev-modules 34 | */ 35 | buildModules: [ 36 | ], 37 | /* 38 | ** Nuxt.js modules 39 | */ 40 | modules: ['@nuxtjs/style-resources'], 41 | /* 42 | ** Build configuration 43 | */ 44 | build: { 45 | /* 46 | ** You can extend webpack config here 47 | */ 48 | transpile: [ 49 | 'three', 50 | ], 51 | extend (config, ctx) { 52 | if (!!config.module) { 53 | config.module.rules.push({ test: /\.(vert|frag)$/i, use: ["raw-loader"] }); 54 | } 55 | } 56 | }, 57 | generate: { 58 | fallback: true, 59 | routes: [ 60 | '/', 61 | '/about', 62 | '/contact', 63 | '404' 64 | ] 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "threejs-nuxt-sample", 3 | "version": "1.0.0", 4 | "description": "My geometric Nuxt.js project", 5 | "author": "Misaki", 6 | "private": true, 7 | "scripts": { 8 | "dev": "nuxt", 9 | "build": "nuxt build", 10 | "start": "nuxt start", 11 | "generate": "nuxt generate" 12 | }, 13 | "dependencies": { 14 | "node-sass": "^7.0.1", 15 | "nuxt": "^2.15.8", 16 | "raw-loader": "^4.0.2", 17 | "sass": "^1.49.9", 18 | "sass-loader": "10", 19 | "three": "^0.138.0" 20 | }, 21 | "devDependencies": { 22 | "@nuxtjs/style-resources": "^1.2.1" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /pages/README.md: -------------------------------------------------------------------------------- 1 | # PAGES 2 | 3 | This directory contains your Application Views and Routes. 4 | The framework reads all the `*.vue` files inside this directory and creates the router of your application. 5 | 6 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing). 7 | -------------------------------------------------------------------------------- /pages/about.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 12 | 13 | 15 | -------------------------------------------------------------------------------- /pages/contact.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 12 | 14 | -------------------------------------------------------------------------------- /pages/index.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 12 | 13 | 15 | -------------------------------------------------------------------------------- /plugins/README.md: -------------------------------------------------------------------------------- 1 | # PLUGINS 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains Javascript plugins that you want to run before mounting the root Vue.js application. 6 | 7 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/plugins). 8 | -------------------------------------------------------------------------------- /static/README.md: -------------------------------------------------------------------------------- 1 | # STATIC 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your static files. 6 | Each file inside this directory is mapped to `/`. 7 | Thus you'd want to delete this README.md before deploying to production. 8 | 9 | Example: `/static/robots.txt` is mapped as `/robots.txt`. 10 | 11 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#static). 12 | -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mnmxmx/threejs-nuxt-sample/5428051f25c15a682f91ede16423dead791cadc5/static/favicon.ico -------------------------------------------------------------------------------- /store/README.md: -------------------------------------------------------------------------------- 1 | # STORE 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your Vuex Store files. 6 | Vuex Store option is implemented in the Nuxt.js framework. 7 | 8 | Creating a file in this directory automatically activates the option in the framework. 9 | 10 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/vuex-store). 11 | -------------------------------------------------------------------------------- /utils/event-bus.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | const EventBus = new Vue(); 3 | export default EventBus; --------------------------------------------------------------------------------