├── docs ├── robots.txt ├── favicon.ico ├── img │ └── icons │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── mstile-150x150.png │ │ ├── apple-touch-icon.png │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ ├── apple-touch-icon-60x60.png │ │ ├── apple-touch-icon-76x76.png │ │ ├── apple-touch-icon-120x120.png │ │ ├── apple-touch-icon-152x152.png │ │ ├── apple-touch-icon-180x180.png │ │ ├── msapplication-icon-144x144.png │ │ ├── android-chrome-maskable-192x192.png │ │ ├── android-chrome-maskable-512x512.png │ │ └── safari-pinned-tab.svg ├── manifest.json ├── 404.html ├── js │ ├── about.f7b116dc.js │ ├── about.f7b116dc.js.map │ ├── app.4f999ba6.js │ └── app.4f999ba6.js.map ├── css │ └── app.821394ac.css ├── precache-manifest.11eaf793371c12ab0c9fd29aa150f713.js ├── service-worker.js └── index.html ├── public ├── robots.txt ├── favicon.ico ├── img │ └── icons │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── apple-touch-icon.png │ │ ├── mstile-150x150.png │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ ├── apple-touch-icon-60x60.png │ │ ├── apple-touch-icon-76x76.png │ │ ├── apple-touch-icon-120x120.png │ │ ├── apple-touch-icon-152x152.png │ │ ├── apple-touch-icon-180x180.png │ │ ├── msapplication-icon-144x144.png │ │ ├── android-chrome-maskable-192x192.png │ │ ├── android-chrome-maskable-512x512.png │ │ └── safari-pinned-tab.svg ├── 404.html └── index.html ├── .browserslistrc ├── cypress.json ├── jest.config.js ├── src ├── raytracer │ ├── index.js │ ├── shaders │ │ ├── shader.vert │ │ └── shader.frag │ ├── movie.js │ ├── scene.js │ ├── shader.js │ ├── movies │ │ ├── rectangle-movie.js │ │ └── circle-movie.js │ └── raytracer.js ├── assets │ └── logo.png ├── views │ ├── About.vue │ └── Home.vue ├── store │ └── index.js ├── main.js ├── App.vue ├── helpers │ └── clone.js ├── router │ └── index.js ├── registerServiceWorker.js └── components │ └── HelloWorld.vue ├── babel.config.js ├── .editorconfig ├── tests ├── e2e │ ├── .eslintrc.js │ ├── specs │ │ └── test.js │ ├── support │ │ ├── index.js │ │ └── commands.js │ └── plugins │ │ └── index.js └── unit │ └── example.spec.js ├── vue.config.js ├── .gitignore ├── .eslintrc.js ├── package.json └── README.md /docs/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead 4 | -------------------------------------------------------------------------------- /cypress.json: -------------------------------------------------------------------------------- 1 | { 2 | "pluginsFile": "tests/e2e/plugins/index.js" 3 | } 4 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: '@vue/cli-plugin-unit-jest' 3 | } 4 | -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/infogr-raytracer-webgl/master/docs/favicon.ico -------------------------------------------------------------------------------- /src/raytracer/index.js: -------------------------------------------------------------------------------- 1 | import Raytracer from './raytracer' 2 | 3 | export default Raytracer 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/infogr-raytracer-webgl/master/public/favicon.ico -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/infogr-raytracer-webgl/master/src/assets/logo.png -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /docs/img/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/infogr-raytracer-webgl/master/docs/img/icons/favicon-16x16.png -------------------------------------------------------------------------------- /docs/img/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/infogr-raytracer-webgl/master/docs/img/icons/favicon-32x32.png -------------------------------------------------------------------------------- /docs/img/icons/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/infogr-raytracer-webgl/master/docs/img/icons/mstile-150x150.png -------------------------------------------------------------------------------- /public/img/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/infogr-raytracer-webgl/master/public/img/icons/favicon-16x16.png -------------------------------------------------------------------------------- /public/img/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/infogr-raytracer-webgl/master/public/img/icons/favicon-32x32.png -------------------------------------------------------------------------------- /docs/img/icons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/infogr-raytracer-webgl/master/docs/img/icons/apple-touch-icon.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/infogr-raytracer-webgl/master/public/img/icons/apple-touch-icon.png -------------------------------------------------------------------------------- /public/img/icons/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/infogr-raytracer-webgl/master/public/img/icons/mstile-150x150.png -------------------------------------------------------------------------------- /docs/img/icons/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/infogr-raytracer-webgl/master/docs/img/icons/android-chrome-192x192.png -------------------------------------------------------------------------------- /docs/img/icons/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/infogr-raytracer-webgl/master/docs/img/icons/android-chrome-512x512.png -------------------------------------------------------------------------------- /docs/img/icons/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/infogr-raytracer-webgl/master/docs/img/icons/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /docs/img/icons/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/infogr-raytracer-webgl/master/docs/img/icons/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /docs/img/icons/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/infogr-raytracer-webgl/master/docs/img/icons/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /docs/img/icons/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/infogr-raytracer-webgl/master/docs/img/icons/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /docs/img/icons/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/infogr-raytracer-webgl/master/docs/img/icons/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /public/img/icons/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/infogr-raytracer-webgl/master/public/img/icons/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/img/icons/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/infogr-raytracer-webgl/master/public/img/icons/android-chrome-512x512.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/infogr-raytracer-webgl/master/public/img/icons/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/infogr-raytracer-webgl/master/public/img/icons/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{js,jsx,ts,tsx,vue}] 2 | indent_style = space 3 | indent_size = 2 4 | trim_trailing_whitespace = true 5 | insert_final_newline = true 6 | -------------------------------------------------------------------------------- /docs/img/icons/msapplication-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/infogr-raytracer-webgl/master/docs/img/icons/msapplication-icon-144x144.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/infogr-raytracer-webgl/master/public/img/icons/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/infogr-raytracer-webgl/master/public/img/icons/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/infogr-raytracer-webgl/master/public/img/icons/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /public/img/icons/msapplication-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/infogr-raytracer-webgl/master/public/img/icons/msapplication-icon-144x144.png -------------------------------------------------------------------------------- /docs/img/icons/android-chrome-maskable-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/infogr-raytracer-webgl/master/docs/img/icons/android-chrome-maskable-192x192.png -------------------------------------------------------------------------------- /docs/img/icons/android-chrome-maskable-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/infogr-raytracer-webgl/master/docs/img/icons/android-chrome-maskable-512x512.png -------------------------------------------------------------------------------- /public/img/icons/android-chrome-maskable-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/infogr-raytracer-webgl/master/public/img/icons/android-chrome-maskable-192x192.png -------------------------------------------------------------------------------- /public/img/icons/android-chrome-maskable-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoR/infogr-raytracer-webgl/master/public/img/icons/android-chrome-maskable-512x512.png -------------------------------------------------------------------------------- /tests/e2e/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | 'cypress' 4 | ], 5 | env: { 6 | mocha: true, 7 | 'cypress/globals': true 8 | }, 9 | rules: { 10 | strict: 'off' 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/views/About.vue: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | 4 | Vue.use(Vuex) 5 | 6 | export default new Vuex.Store({ 7 | state: { 8 | }, 9 | mutations: { 10 | }, 11 | actions: { 12 | }, 13 | modules: { 14 | } 15 | }) 16 | -------------------------------------------------------------------------------- /tests/e2e/specs/test.js: -------------------------------------------------------------------------------- 1 | // https://docs.cypress.io/api/introduction/api.html 2 | 3 | describe('My First Test', () => { 4 | it('Visits the app root url', () => { 5 | cy.visit('/') 6 | cy.contains('h1', 'Welcome to Your Vue.js App') 7 | }) 8 | }) 9 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | import './registerServiceWorker' 4 | import router from './router' 5 | import store from './store' 6 | 7 | Vue.config.productionTip = false 8 | 9 | new Vue({ 10 | router, 11 | store, 12 | render: h => h(App) 13 | }).$mount('#app') 14 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | module.exports = { 4 | publicPath: '/infogr-raytracer-webgl/', 5 | outputDir: path.resolve(__dirname, './docs'), 6 | configureWebpack: { 7 | module: { 8 | rules: [ 9 | { 10 | test: /\.vert$|\.frag$/i, 11 | use: 'raw-loader' 12 | } 13 | ] 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | /tests/e2e/videos/ 6 | /tests/e2e/screenshots/ 7 | 8 | # local env files 9 | .env.local 10 | .env.*.local 11 | 12 | # Log files 13 | npm-debug.log* 14 | yarn-debug.log* 15 | yarn-error.log* 16 | 17 | # Editor directories and files 18 | .idea 19 | .vscode 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /src/raytracer/shaders/shader.vert: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | 3 | attribute vec4 aVertexPosition; 4 | 5 | uniform mat4 uModelViewMatrix; 6 | uniform mat4 uProjectionMatrix; 7 | 8 | varying mediump vec2 screenPosition; 9 | 10 | void main() { 11 | screenPosition = aVertexPosition.xy; 12 | gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition; 13 | } 14 | -------------------------------------------------------------------------------- /tests/unit/example.spec.js: -------------------------------------------------------------------------------- 1 | import { shallowMount } from '@vue/test-utils' 2 | import HelloWorld from '@/components/HelloWorld.vue' 3 | 4 | describe('HelloWorld.vue', () => { 5 | it('renders props.msg when passed', () => { 6 | const msg = 'new message' 7 | const wrapper = shallowMount(HelloWorld, { 8 | propsData: { msg } 9 | }) 10 | expect(wrapper.text()).toMatch(msg) 11 | }) 12 | }) 13 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 19 | -------------------------------------------------------------------------------- /docs/manifest.json: -------------------------------------------------------------------------------- 1 | {"name":"infogr-raytracer-webgl","short_name":"infogr-raytracer-webgl","theme_color":"#4DBA87","icons":[{"src":"./img/icons/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"./img/icons/android-chrome-512x512.png","sizes":"512x512","type":"image/png"},{"src":"./img/icons/android-chrome-maskable-192x192.png","sizes":"192x192","type":"image/png","purpose":"maskable"},{"src":"./img/icons/android-chrome-maskable-512x512.png","sizes":"512x512","type":"image/png","purpose":"maskable"}],"start_url":".","display":"standalone","background_color":"#000000"} -------------------------------------------------------------------------------- /docs/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | CorCoder 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 |               18 |               19 |               20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | CorCoder 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 |               18 |               19 |               20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /docs/js/about.f7b116dc.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["about"],{f820:function(t,e,r){"use strict";r.r(e);var n=function(){var t=this,e=t.$createElement;t._self._c;return t._m(0)},a=[function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("div",{staticClass:"about"},[r("h1",[t._v("INFOGR Index WebGL")]),r("p",[t._v("This is a port of the "),r("a",{attrs:{href:"https://github.com/cor/infogr-raytracer"}},[t._v("INFOGR Index")]),t._v(" project to WebGL")])])}],o=r("2877"),s={},c=Object(o["a"])(s,n,a,!1,null,null,null);e["default"]=c.exports}}]); 2 | //# sourceMappingURL=about.f7b116dc.js.map -------------------------------------------------------------------------------- /src/helpers/clone.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @function 3 | * @description Deep clone a class instance. 4 | * @param {object} instance The class instance you want to clone. 5 | * @returns {object} A new cloned instance. 6 | */ 7 | export default function clone (instance) { 8 | return Object.assign( 9 | Object.create( 10 | // Set the prototype of the new object to the prototype of the instance. 11 | // Used to allow new object behave like class instance. 12 | Object.getPrototypeOf(instance) 13 | ), 14 | // Prevent shallow copies of nested structures like arrays, etc 15 | JSON.parse(JSON.stringify(instance)) 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true 5 | }, 6 | extends: [ 7 | 'plugin:vue/essential', 8 | '@vue/standard' 9 | ], 10 | parserOptions: { 11 | parser: 'babel-eslint' 12 | }, 13 | rules: { 14 | 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 15 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off' 16 | }, 17 | overrides: [ 18 | { 19 | files: [ 20 | '**/__tests__/*.{j,t}s?(x)', 21 | '**/tests/unit/**/*.spec.{j,t}s?(x)' 22 | ], 23 | env: { 24 | jest: true 25 | } 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /tests/e2e/support/index.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/index.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands' 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') 21 | -------------------------------------------------------------------------------- /tests/e2e/support/commands.js: -------------------------------------------------------------------------------- 1 | // *********************************************** 2 | // This example commands.js shows you how to 3 | // create various custom commands and overwrite 4 | // existing commands. 5 | // 6 | // For more comprehensive examples of custom 7 | // commands please read more here: 8 | // https://on.cypress.io/custom-commands 9 | // *********************************************** 10 | // 11 | // 12 | // -- This is a parent command -- 13 | // Cypress.Commands.add("login", (email, password) => { ... }) 14 | // 15 | // 16 | // -- This is a child command -- 17 | // Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) 18 | // 19 | // 20 | // -- This is a dual command -- 21 | // Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) 22 | // 23 | // 24 | // -- This is will overwrite an existing command -- 25 | // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) 26 | -------------------------------------------------------------------------------- /docs/css/app.821394ac.css: -------------------------------------------------------------------------------- 1 | :root{font-family:Avenir,Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-align:left;--primary-font-color:#aaa;--secondary-font-color:#777;color:#dfcfbe;background-color:#000}.home{text-align:center}h1{margin-bottom:0}h2{margin:0 8px 16px 8px;font-size:20px}.tablink,h2 a,h2 a:visited{color:inherit}.tablink{border-radius:16px 16px 0 0;background-color:#111;text-decoration:none;font-weight:700;float:left;border:none;outline:none;cursor:pointer;padding:14px 16px;font-size:17px;flex:1;border:solid #222;border-width:4px}.tablink.router-link-active{border-color:#806f69;border-bottom-color:#111}.tablink:not(.router-link-active){border-bottom-color:#806f69}.tablink:not(.router-link-active):hover{border-color:#806f69}.tablink-container{display:flex;width:808px;margin:0 auto}canvas{border:solid #806f69;border-width:0 4px 4px 4px}.description{width:800px;text-align:left;margin:0 auto;font-weight:700}.description a{color:inherit;font-weight:bolder} -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueRouter from 'vue-router' 3 | import Home from '../views/Home.vue' 4 | 5 | Vue.use(VueRouter) 6 | 7 | const routes = [ 8 | { 9 | path: '/', 10 | redirect: '/movie/0', 11 | name: 'Home', 12 | component: Home 13 | }, 14 | { 15 | path: '/movie/:id', 16 | name: 'Movie', 17 | component: Home, 18 | props (route) { 19 | const props = { ...route.params } 20 | props.id = +props.id 21 | return props 22 | } 23 | }, 24 | { 25 | path: '/about', 26 | name: 'About', 27 | // route level code-splitting 28 | // this generates a separate chunk (about.[hash].js) for this route 29 | // which is lazy-loaded when the route is visited. 30 | component: () => import(/* webpackChunkName: "about" */ '../views/About.vue') 31 | } 32 | ] 33 | 34 | const router = new VueRouter({ 35 | mode: 'history', 36 | base: process.env.BASE_URL, 37 | routes 38 | }) 39 | 40 | export default router 41 | -------------------------------------------------------------------------------- /tests/e2e/plugins/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable arrow-body-style */ 2 | // https://docs.cypress.io/guides/guides/plugins-guide.html 3 | 4 | // if you need a custom webpack configuration you can uncomment the following import 5 | // and then use the `file:preprocessor` event 6 | // as explained in the cypress docs 7 | // https://docs.cypress.io/api/plugins/preprocessors-api.html#Examples 8 | 9 | // /* eslint-disable import/no-extraneous-dependencies, global-require */ 10 | // const webpack = require('@cypress/webpack-preprocessor') 11 | 12 | module.exports = (on, config) => { 13 | // on('file:preprocessor', webpack({ 14 | // webpackOptions: require('@vue/cli-service/webpack.config'), 15 | // watchOptions: {} 16 | // })) 17 | 18 | return Object.assign({}, config, { 19 | fixturesFolder: 'tests/e2e/fixtures', 20 | integrationFolder: 'tests/e2e/specs', 21 | screenshotsFolder: 'tests/e2e/screenshots', 22 | videosFolder: 'tests/e2e/videos', 23 | supportFile: 'tests/e2e/support/index.js' 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /src/registerServiceWorker.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | import { register } from 'register-service-worker' 4 | 5 | if (process.env.NODE_ENV === 'production') { 6 | register(`${process.env.BASE_URL}service-worker.js`, { 7 | ready () { 8 | console.log( 9 | 'App is being served from cache by a service worker.\n' + 10 | 'For more details, visit https://goo.gl/AFskqB' 11 | ) 12 | }, 13 | registered () { 14 | console.log('Service worker has been registered.') 15 | }, 16 | cached () { 17 | console.log('Content has been cached for offline use.') 18 | }, 19 | updatefound () { 20 | console.log('New content is downloading.') 21 | }, 22 | updated () { 23 | console.log('New content is available; please refresh.') 24 | }, 25 | offline () { 26 | console.log('No internet connection found. App is running in offline mode.') 27 | }, 28 | error (error) { 29 | console.error('Error during service worker registration:', error) 30 | } 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /docs/precache-manifest.11eaf793371c12ab0c9fd29aa150f713.js: -------------------------------------------------------------------------------- 1 | self.__precacheManifest = (self.__precacheManifest || []).concat([ 2 | { 3 | "revision": "8b488426b3602f27056b0fafc5b142aa", 4 | "url": "/infogr-raytracer-webgl/404.html" 5 | }, 6 | { 7 | "revision": "70f641ed03765cc03b2b", 8 | "url": "/infogr-raytracer-webgl/css/app.821394ac.css" 9 | }, 10 | { 11 | "revision": "cafb2d2a5a01177108981026809d058e", 12 | "url": "/infogr-raytracer-webgl/index.html" 13 | }, 14 | { 15 | "revision": "7c14e24d2ce019106b99", 16 | "url": "/infogr-raytracer-webgl/js/about.f7b116dc.js" 17 | }, 18 | { 19 | "revision": "70f641ed03765cc03b2b", 20 | "url": "/infogr-raytracer-webgl/js/app.4f999ba6.js" 21 | }, 22 | { 23 | "revision": "6fb70a94f3d3cb96936a", 24 | "url": "/infogr-raytracer-webgl/js/chunk-vendors.7cab0d61.js" 25 | }, 26 | { 27 | "revision": "144808dc56323f0dcbff51d3bc4d95c3", 28 | "url": "/infogr-raytracer-webgl/manifest.json" 29 | }, 30 | { 31 | "revision": "b6216d61c03e6ce0c9aea6ca7808f7ca", 32 | "url": "/infogr-raytracer-webgl/robots.txt" 33 | } 34 | ]); -------------------------------------------------------------------------------- /docs/service-worker.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Welcome to your Workbox-powered service worker! 3 | * 4 | * You'll need to register this file in your web app and you should 5 | * disable HTTP caching for this file too. 6 | * See https://goo.gl/nhQhGp 7 | * 8 | * The rest of the code is auto-generated. Please don't update this file 9 | * directly; instead, make changes to your Workbox build configuration 10 | * and re-run your build process. 11 | * See https://goo.gl/2aRDsh 12 | */ 13 | 14 | importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js"); 15 | 16 | importScripts( 17 | "/infogr-raytracer-webgl/precache-manifest.11eaf793371c12ab0c9fd29aa150f713.js" 18 | ); 19 | 20 | workbox.core.setCacheNameDetails({prefix: "infogr-raytracer-webgl"}); 21 | 22 | self.addEventListener('message', (event) => { 23 | if (event.data && event.data.type === 'SKIP_WAITING') { 24 | self.skipWaiting(); 25 | } 26 | }); 27 | 28 | /** 29 | * The workboxSW.precacheAndRoute() method efficiently caches and responds to 30 | * requests for URLs in the manifest. 31 | * See https://goo.gl/S9QRab 32 | */ 33 | self.__precacheManifest = [].concat(self.__precacheManifest || []); 34 | workbox.precaching.precacheAndRoute(self.__precacheManifest, {}); 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "infogr-raytracer-webgl", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "test:unit": "vue-cli-service test:unit", 9 | "test:e2e": "vue-cli-service test:e2e", 10 | "lint": "vue-cli-service lint" 11 | }, 12 | "dependencies": { 13 | "core-js": "^3.6.4", 14 | "register-service-worker": "^1.7.1", 15 | "vue": "^2.6.11", 16 | "vue-router": "^3.1.6", 17 | "vuex": "^3.1.3" 18 | }, 19 | "devDependencies": { 20 | "@vue/cli-plugin-babel": "~4.3.0", 21 | "@vue/cli-plugin-e2e-cypress": "~4.3.0", 22 | "@vue/cli-plugin-eslint": "~4.3.0", 23 | "@vue/cli-plugin-pwa": "~4.3.0", 24 | "@vue/cli-plugin-router": "~4.3.0", 25 | "@vue/cli-plugin-unit-jest": "~4.3.0", 26 | "@vue/cli-plugin-vuex": "~4.3.0", 27 | "@vue/cli-service": "~4.3.0", 28 | "@vue/eslint-config-standard": "^5.1.2", 29 | "@vue/test-utils": "1.0.0-beta.31", 30 | "babel-eslint": "^10.1.0", 31 | "eslint": "^6.7.2", 32 | "eslint-plugin-import": "^2.20.2", 33 | "eslint-plugin-node": "^11.1.0", 34 | "eslint-plugin-promise": "^4.2.1", 35 | "eslint-plugin-standard": "^4.0.0", 36 | "eslint-plugin-vue": "^6.2.2", 37 | "gl-matrix": "^3.3.0", 38 | "raw-loader": "^4.0.1", 39 | "sass": "^1.26.3", 40 | "sass-loader": "^8.0.2", 41 | "vue-template-compiler": "^2.6.11" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /docs/js/about.f7b116dc.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["webpack:///./src/views/About.vue?82b5","webpack:///./src/views/About.vue"],"names":["render","_vm","this","_h","$createElement","_self","_c","_m","staticRenderFns","staticClass","_v","attrs","script","component"],"mappings":"8GAAA,IAAIA,EAAS,WAAa,IAAIC,EAAIC,KAASC,EAAGF,EAAIG,eAAsBH,EAAII,MAAMC,GAAO,OAAOL,EAAIM,GAAG,IACnGC,EAAkB,CAAC,WAAa,IAAIP,EAAIC,KAASC,EAAGF,EAAIG,eAAmBE,EAAGL,EAAII,MAAMC,IAAIH,EAAG,OAAOG,EAAG,MAAM,CAACG,YAAY,SAAS,CAACH,EAAG,KAAK,CAACL,EAAIS,GAAG,wBAAwBJ,EAAG,IAAI,CAACL,EAAIS,GAAG,0BAA0BJ,EAAG,IAAI,CAACK,MAAM,CAAC,KAAO,4CAA4C,CAACV,EAAIS,GAAG,kBAAkBT,EAAIS,GAAG,2B,YCA1TE,EAAS,GAKTC,EAAY,eACdD,EACAZ,EACAQ,GACA,EACA,KACA,KACA,MAIa,aAAAK,E","file":"js/about.f7b116dc.js","sourcesContent":["var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _vm._m(0)}\nvar staticRenderFns = [function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"about\"},[_c('h1',[_vm._v(\"INFOGR Index WebGL\")]),_c('p',[_vm._v(\"This is a port of the \"),_c('a',{attrs:{\"href\":\"https://github.com/cor/infogr-raytracer\"}},[_vm._v(\"INFOGR Index\")]),_vm._v(\" project to WebGL\")])])}]\n\nexport { render, staticRenderFns }","import { render, staticRenderFns } from \"./About.vue?vue&type=template&id=76fef490&\"\nvar script = {}\n\n\n/* normalize component */\nimport normalizer from \"!../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports"],"sourceRoot":""} -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # infogr-raytracer-webgl 2 | On [the demo site](https://cor.github.io/infogr-raytracer-webgl/) you will find two movies; which are actively ray traced withn a WebGL fragment shader. The tracer operates in two dimensions, shooting a ray at each pixel every single frame. WebGL is easily capable of performing this operation at 60FPS, as opposed to traditional CPU implementations. Our CPU-based C# version, from which we originally ported the core logic, runs at a mere 5FPS. We've ported that shader to OpenGL, which supports a more modern shading language, in order to hit 60FPS. 3 | 4 | We wanted to run this project on the web, which is why we ported it to WebGL. However, due to WebGL's limited support for shaders, we wrote our own shader preprocessor in order to add variable-sized arrays to GLSL 1.0 5 | 6 | The movie consists of a set of static scenes, which are interpolated to generate fluid motion. 7 | 8 | As part of our assignment, we first implemented the 2D tracer in C#, after porting the provided template project, removing deprecated OpenGL practices. Next we reimplemented the same ray tracer using OpenGL in a fragment shader. Lastly we ported our OpenGL 3.0 fragment shader to WebGL (which uses OpenGL 1.0 ES). Running the WebGL shader requires NPM and Node, the options are explained in the README. 9 | 10 | 11 | ## Project setup 12 | ``` 13 | npm install 14 | ``` 15 | 16 | ### Compiles and hot-reloads for development 17 | ``` 18 | npm run serve 19 | ``` 20 | 21 | ### Compiles and minifies for production 22 | ``` 23 | npm run build 24 | ``` 25 | 26 | ### Run your unit tests 27 | ``` 28 | npm run test:unit 29 | ``` 30 | 31 | ### Run your end-to-end tests 32 | ``` 33 | npm run test:e2e 34 | ``` 35 | 36 | ### Lints and fixes files 37 | ``` 38 | npm run lint 39 | ``` 40 | 41 | ### Customize configuration 42 | See [Configuration Reference](https://cli.vuejs.org/config/). 43 | -------------------------------------------------------------------------------- /src/raytracer/movie.js: -------------------------------------------------------------------------------- 1 | import Scene from './scene' 2 | 3 | export default class Movie { 4 | id 5 | scenes = [new Scene()] 6 | shaderSourceVars 7 | 8 | constructor (id, lightCount, circleCount, rectangleCount) { 9 | this.id = id 10 | this.shaderSourceVars = { 11 | LIGHT_COUNT: lightCount, 12 | CIRCLE_COUNT: circleCount, 13 | RECTANGLE_COUNT: rectangleCount 14 | } 15 | 16 | for (let i = 1; i < lightCount; i++) { 17 | this.lastScene().addLight() 18 | } 19 | 20 | for (let i = 1; i < circleCount; i++) { 21 | this.lastScene().addCircle() 22 | } 23 | 24 | for (let i = 1; i < rectangleCount; i++) { 25 | this.lastScene().addRectangle() 26 | } 27 | } 28 | 29 | sceneDurations () { 30 | return this.scenes.map(s => s.duration) 31 | } 32 | 33 | duration () { 34 | return this.sceneDurations().reduce((d0, d1) => d0 + d1, 0) 35 | } 36 | 37 | lastScene () { 38 | return this.scenes[this.scenes.length - 1] 39 | } 40 | 41 | addScene () { 42 | this.scenes.push(this.lastScene().clone()) 43 | return this.lastScene() 44 | } 45 | 46 | currentScene (time) { 47 | const sceneDurations = this.sceneDurations() 48 | const sceneCount = this.scenes.length 49 | 50 | time %= this.duration() 51 | 52 | let timeSum = 0 53 | let sceneIndex = -1 54 | while (timeSum < time) { 55 | sceneIndex++ 56 | timeSum += sceneDurations[sceneIndex] 57 | sceneIndex %= sceneCount 58 | } 59 | 60 | const scene0 = this.scenes[sceneIndex] 61 | const scene1 = sceneIndex + 1 === sceneCount 62 | ? this.scenes[0] // go back to scene 0 at the end 63 | : this.scenes[sceneIndex + 1] // otherwise, go the the succeeding scene 64 | 65 | const normalizedTime = (time - timeSum + scene0.duration) / scene0.duration 66 | return scene0.interpolate(scene1, normalizedTime) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | infogr-raytracer-webgl
-------------------------------------------------------------------------------- /src/raytracer/scene.js: -------------------------------------------------------------------------------- 1 | import clone from '../helpers/clone' 2 | 3 | export default class Scene { 4 | lights = [ 5 | { 6 | position: [1, 1], 7 | color: [1, 1, 1] 8 | } 9 | ] 10 | 11 | circles = [ 12 | { 13 | position: [0, 0], 14 | radius: 0.1 15 | } 16 | ] 17 | 18 | rectangles = [ 19 | { 20 | position: [0, 0], 21 | width: 0.1, 22 | height: 0.1, 23 | angle: 0 24 | } 25 | ] 26 | 27 | duration = 1 28 | 29 | clone () { 30 | return clone(this) 31 | } 32 | 33 | interpolate (nextScene, t, linear = false) { 34 | const interpolatedScene = this.clone() 35 | 36 | if (!linear) { // Ease in and ease out 37 | t = (Math.sin((t - 0.5) * Math.PI) + 1) / 2 38 | } 39 | 40 | // Interpolated = Current + t(Next-Current) 41 | this.lights.forEach((light, i) => { 42 | for (const p in interpolatedScene.lights[i].position) { 43 | interpolatedScene.lights[i].position[p] = 44 | light.position[p] + t * (nextScene.lights[i].position[p] - light.position[p]) 45 | } 46 | 47 | for (const c in interpolatedScene.lights[i].color) { 48 | interpolatedScene.lights[i].color[c] = 49 | light.color[c] + t * (nextScene.lights[i].color[c] - light.color[c]) 50 | } 51 | }) 52 | 53 | this.circles.forEach((circle, i) => { 54 | for (const p in interpolatedScene.circles[i].position) { 55 | interpolatedScene.circles[i].position[p] = 56 | circle.position[p] + t * (nextScene.circles[i].position[p] - circle.position[p]) 57 | } 58 | 59 | interpolatedScene.circles[i].radius = 60 | circle.radius + t * (nextScene.circles[i].radius - circle.radius) 61 | }) 62 | 63 | this.rectangles.forEach((rectangle, i) => { 64 | for (const p in interpolatedScene.rectangles[i].position) { 65 | interpolatedScene.rectangles[i].position[p] = 66 | rectangle.position[p] + t * (nextScene.rectangles[i].position[p] - rectangle.position[p]) 67 | } 68 | 69 | interpolatedScene.rectangles[i].width = 70 | rectangle.width + t * (nextScene.rectangles[i].width - rectangle.width) 71 | 72 | interpolatedScene.rectangles[i].height = 73 | rectangle.height + t * (nextScene.rectangles[i].height - rectangle.height) 74 | 75 | interpolatedScene.rectangles[i].angle = 76 | rectangle.angle + t * (nextScene.rectangles[i].angle - rectangle.angle) 77 | }) 78 | 79 | return interpolatedScene 80 | } 81 | 82 | addLight () { 83 | this.lights.push(clone(this.lights[this.lights.length - 1])) 84 | } 85 | 86 | addCircle () { 87 | this.circles.push(clone(this.circles[this.circles.length - 1])) 88 | } 89 | 90 | addRectangle () { 91 | this.rectangles.push(clone(this.rectangles[this.rectangles.length - 1])) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 37 | 38 | 46 | 47 | 48 | 64 | -------------------------------------------------------------------------------- /src/raytracer/shader.js: -------------------------------------------------------------------------------- 1 | export default class Shader { 2 | gl 3 | program 4 | 5 | constructor (gl, vsSource, fsSource, sourceVars) { 6 | this.gl = gl 7 | 8 | // preprocess sources 9 | vsSource = this.preprocessSource(vsSource, sourceVars) 10 | fsSource = this.preprocessSource(fsSource, sourceVars) 11 | 12 | this.program = this.initShaderProgram(gl, vsSource, fsSource) 13 | } 14 | 15 | // 16 | // Initialize a shader program, so WebGL knows how to draw our data 17 | // 18 | initShaderProgram (gl, vsSource, fsSource) { 19 | const vertexShader = this.loadShader(gl, gl.VERTEX_SHADER, vsSource) 20 | const fragmentShader = this.loadShader(gl, gl.FRAGMENT_SHADER, fsSource) 21 | 22 | // Create the shader program 23 | const shaderProgram = gl.createProgram() 24 | gl.attachShader(shaderProgram, vertexShader) 25 | gl.attachShader(shaderProgram, fragmentShader) 26 | gl.linkProgram(shaderProgram) 27 | 28 | // If creating the shader program failed, alert 29 | if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { 30 | alert(`Unable to initialize the shader program: ${gl.getProgramInfoLog(shaderProgram)}`) 31 | return null 32 | } 33 | 34 | return shaderProgram 35 | } 36 | 37 | // 38 | // preprocesses our shader source files 39 | // it replaces variables in the format 42//$VARIABLE_NAME$// with the VARIABLE_NAME 40 | // item from the sourceVars object 41 | // 42 | preprocessSource (source, sourceVars) { 43 | const sourceVarPattern = /42\/\/\$(.*)\$\/\//g 44 | return source.replace(sourceVarPattern, (_, varName) => sourceVars[varName]) 45 | } 46 | 47 | // 48 | // creates a shader of the given type, uploads the source and 49 | // compiles it. 50 | // 51 | loadShader (gl, type, source) { 52 | const shader = gl.createShader(type) 53 | 54 | // Send the source to the shader object 55 | gl.shaderSource(shader, source) 56 | 57 | // Compile the shader program 58 | gl.compileShader(shader) 59 | 60 | // See if it compiled successfully 61 | if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { 62 | console.error(`An error occurred compiling the shaders:\n${gl.getShaderInfoLog(shader)}`) 63 | gl.deleteShader(shader) 64 | return null 65 | } 66 | 67 | return shader 68 | } 69 | 70 | use () { 71 | this.gl.useProgram(this.program) 72 | } 73 | 74 | setUniform1f (name, value) { 75 | this.gl.uniform1f(this.getUniformLocation(name), value) 76 | } 77 | 78 | setUniformMatrix4fv (name, value) { 79 | this.gl.uniformMatrix4fv(this.getUniformLocation(name), false, value) 80 | } 81 | 82 | setUniform2fv (name, value) { 83 | this.gl.uniform2fv(this.getUniformLocation(name), value) 84 | } 85 | 86 | setUniform3fv (name, value) { 87 | this.gl.uniform3fv(this.getUniformLocation(name), value) 88 | } 89 | 90 | getUniformLocation (name) { 91 | // TODO: Cache uniform locations 92 | return this.gl.getUniformLocation(this.program, name) 93 | } 94 | 95 | getAttribLocation (name) { 96 | // TODO: Cache attrib locations 97 | return this.gl.getAttribLocation(this.program, name) 98 | } 99 | 100 | delete () { 101 | this.gl.deleteProgram(this.program) 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/raytracer/movies/rectangle-movie.js: -------------------------------------------------------------------------------- 1 | import Movie from '../movie' 2 | 3 | export default function rectangleMovie () { 4 | const m = new Movie('Rectangle Movie', 2, 1, 9) 5 | let s = m.lastScene() 6 | 7 | const brightness = 2 8 | 9 | s.lights[0].position = [1, 1] 10 | s.lights[0].color = [brightness, 0, brightness] 11 | 12 | s.lights[1].position = [-1, 0] 13 | s.lights[1].color = [0, 0, 0] 14 | 15 | s.circles[0].radius = 0 16 | 17 | const spacing = 0.4 18 | const sideDimension = 0.2 19 | 20 | const combinedWidth = spacing * 3 - sideDimension 21 | 22 | for (let i = 0; i < 9; i++) { 23 | const x = i % 3 24 | const y = ~~(i / 3) 25 | 26 | s.rectangles[i].width = 0 27 | s.rectangles[i].height = 0 28 | s.rectangles[i].position[0] = spacing * x - spacing 29 | s.rectangles[i].position[1] = spacing * y - spacing 30 | } 31 | 32 | s.rectangles[4].width = combinedWidth 33 | s.rectangles[4].height = combinedWidth 34 | 35 | s = m.addScene() 36 | s.lights[0].position[1] = -1 37 | 38 | s = m.addScene() 39 | s.lights[0].position[0] = -1 40 | 41 | // combine rects for split 42 | s.rectangles[1].width = combinedWidth 43 | s.rectangles[1].height = sideDimension 44 | 45 | s.rectangles[7].width = combinedWidth 46 | s.rectangles[7].height = sideDimension 47 | 48 | s = m.addScene() 49 | s.lights[0].position[1] = 0 50 | 51 | s = m.addScene() 52 | s.rectangles[4].height = sideDimension 53 | 54 | s = m.addScene() 55 | s.lights[0].color = [brightness, 0, 0] 56 | s.lights[1].color = [0, 0, brightness] 57 | 58 | s = m.addScene() 59 | s.lights[0].position[1] = spacing / 4 60 | s.lights[1].position[1] = -spacing / 4 61 | 62 | s = m.addScene() 63 | s.lights[0].position[1] = spacing / 2 64 | s.lights[1].position[1] = -spacing / 2 65 | 66 | s = m.addScene() 67 | s.lights[0].position[0] = 1 68 | 69 | s = m.addScene() 70 | s.lights[1].position[0] = -1 71 | 72 | s = m.addScene() 73 | s.lights[1].position[0] = 1 74 | s.lights[1].position[0] = -1 75 | 76 | const pointBetweenRects = spacing / 2 77 | s = m.addScene() 78 | s.lights[0].position[0] = pointBetweenRects 79 | s.lights[1].position[0] = -pointBetweenRects 80 | 81 | for (const i of [0, 2, 3, 5, 6, 8]) { 82 | s.rectangles[i].width = sideDimension 83 | s.rectangles[i].height = sideDimension 84 | } 85 | 86 | s = m.addScene() 87 | s.duration = 3 88 | for (const i of [1, 4, 7]) { 89 | s.rectangles[i].width = sideDimension 90 | s.rectangles[i].height = sideDimension 91 | } 92 | 93 | s = m.addScene() 94 | s.rectangles[4].angle = 3 * Math.PI 95 | s.duration = 0.5 96 | 97 | s = m.addScene() 98 | s.lights[0].position[1] = -pointBetweenRects 99 | s.lights[1].position[1] = pointBetweenRects 100 | 101 | s = m.addScene() 102 | s.lights[0].position[0] = -pointBetweenRects 103 | s.lights[1].position[0] = pointBetweenRects 104 | s.duration = 1 105 | 106 | s = m.addScene() 107 | s.lights[0].position[1] = 1 108 | s.lights[1].position[1] = -1 109 | 110 | s = m.addScene() 111 | s.lights[0].position[1] = -1 112 | s.lights[1].position[1] = 1 113 | 114 | s = m.addScene() 115 | s.lights[0].position[1] = pointBetweenRects 116 | s.lights[1].position[1] = -pointBetweenRects 117 | 118 | s = m.addScene() 119 | s.lights[0].position[1] = 1 120 | s.lights[1].position[1] = 1 121 | s.duration = 0.5 122 | 123 | s = m.addScene() 124 | s.lights[0].position[0] = -pointBetweenRects / 2 125 | s.lights[1].position[0] = pointBetweenRects / 2 126 | 127 | s = m.addScene() 128 | s.lights[0].position[0] = 0 129 | s.lights[1].position[0] = 0 130 | s.duration = 1 131 | 132 | s.rectangles[4].width = combinedWidth 133 | s.rectangles[4].height = combinedWidth 134 | 135 | s = m.addScene() 136 | s.lights[0].color = [2, 0, 2] 137 | s.lights[1].color = [0, 0, 0] 138 | for (const i of [0, 1, 2, 3, 5, 6, 7, 8]) { 139 | s.rectangles[i].width = 0 140 | s.rectangles[i].height = 0 141 | } 142 | s.duration = 2 143 | 144 | return m 145 | } 146 | -------------------------------------------------------------------------------- /src/raytracer/movies/circle-movie.js: -------------------------------------------------------------------------------- 1 | import Movie from '../movie' 2 | 3 | export default function circleMovie () { 4 | const m = new Movie('Circle Movie', 4, 9, 1) 5 | 6 | // Setup inital scene 7 | let s = m.lastScene() 8 | s.rectangles[0].width = 0 9 | s.rectangles[0].height = 0 10 | s.rectangles[0].position = [-10, 0] 11 | s.addCircle() 12 | 13 | s.lights[0].position = [-2.5, 2.5] 14 | s.lights[0].color = [0, 0, 0] 15 | s.lights[1].position = [0, 0] 16 | s.lights[2].position = [0, 0] 17 | s.lights[3].position = [0, 0] 18 | s.lights[1].color = [0, 0, 0] 19 | s.lights[2].color = [0, 0, 0] 20 | s.lights[3].color = [0, 0, 0] 21 | 22 | s = m.addScene() 23 | s.lights[0].color = [2, 2, 2] 24 | s.duration = 2 25 | 26 | s = m.addScene() 27 | 28 | s = m.addScene() 29 | s.lights[0].position = [-0.5, 0.5] 30 | s.duration = 0.5 31 | 32 | s = m.addScene() 33 | 34 | s = m.addScene() 35 | s.lights[0].position = [0.5, 0.5] 36 | s.lights[0].color = [2, 0, 0] 37 | 38 | s = m.addScene() 39 | s.lights[0].position = [0.5, -0.5] 40 | s.lights[0].color = [1, 1, 0] 41 | 42 | s = m.addScene() 43 | s.lights[0].position = [-0.5, -0.5] 44 | s.lights[0].color = [0, 2, 0] 45 | 46 | s = m.addScene() 47 | s.lights[0].position = [-0.5, 0.5] 48 | s.lights[0].color = [0, 0, 2] 49 | 50 | s = m.addScene() 51 | s.lights[0].color = [2, 1, 3] 52 | s.duration = 2 53 | 54 | s = m.addScene() 55 | s.circles[0].position = [-1, -1] 56 | s.circles[1].position = [1, 1] 57 | s.circles[2].position = [1, -1] 58 | s.circles[3].position = [1, -1] 59 | s.circles[4].position = [1, -1] 60 | s.circles[5].position = [1, -1] 61 | s.circles[6].position = [1, -1] 62 | s.circles[7].position = [1, -1] 63 | s.circles[8].position = [1, -1] 64 | 65 | s.lights[0].position = [0, 0] 66 | 67 | s = m.addScene() 68 | s.circles[0].radius = 0.4 69 | s.circles[1].radius = 0.5 70 | s.lights[0].color = [9, 4, 8] 71 | s.circles[3].position = [1, 0] 72 | s.circles[4].position = [0, -1] 73 | 74 | s = m.addScene() 75 | s.circles[0].radius = 0.2 76 | s.circles[1].radius = 0.2 77 | s.circles[3].radius = 0.15 78 | s.circles[4].radius = 0.15 79 | 80 | s = m.addScene() 81 | s.lights[0].color = [1, 1, 4] 82 | 83 | s = m.addScene() 84 | s.lights[1].color = [9, 4, 8] 85 | 86 | s = m.addScene() 87 | s.lights[1].position = [0, 1.8] 88 | s.duration = 1 89 | 90 | s = m.addScene() 91 | s.lights[1].position = [1.8, 1.8] 92 | 93 | s = m.addScene() 94 | s.lights[1].position = [1.8, -1.8] 95 | 96 | s = m.addScene() 97 | s.lights[1].position = [-1.8, -1.8] 98 | 99 | s = m.addScene() 100 | s.lights[1].position = [-1.8, 0] 101 | 102 | s = m.addScene() 103 | s.lights[1].position = [0, 0] 104 | 105 | s = m.addScene() 106 | s.circles[0].radius = 0.1 107 | s.circles[1].radius = 0.1 108 | s.circles[2].radius = 0.1 109 | s.circles[3].radius = 0.1 110 | s.circles[4].radius = 0.1 111 | 112 | s = m.addScene() 113 | s.circles[5].position = [1, 0.5] 114 | s.circles[6].position = [1, -0.5] 115 | s.circles[7].position = [0.5, -1] 116 | s.circles[8].position = [-0.5, -1] 117 | 118 | s = m.addScene() 119 | s.lights[0].color = [0, 0, 0] 120 | s.lights[1].color = [0, 0, 0] 121 | s.lights[2].color = [0, 0, 0] 122 | s.lights[3].color = [0, 0, 0] 123 | 124 | s = m.addScene() 125 | s.lights[0].position = [1.8, 0.7] 126 | s.lights[1].position = [1.8, -0.7] 127 | s.lights[2].position = [0.7, -1.8] 128 | s.lights[3].position = [-0.7, -1.8] 129 | s.duration = 0.5 130 | 131 | s = m.addScene() 132 | s.lights[0].color = [0, 2, 0] 133 | 134 | s = m.addScene() 135 | s.lights[0].color = [0, 0, 0] 136 | s.lights[1].color = [0, 2, 0] 137 | 138 | s = m.addScene() 139 | s.lights[1].color = [0, 0, 0] 140 | s.lights[2].color = [0, 2, 0] 141 | 142 | s = m.addScene() 143 | s.lights[2].color = [0, 0, 0] 144 | s.lights[3].color = [0, 2, 0] 145 | 146 | s = m.addScene() 147 | s.lights[3].color = [0, 0, 0] 148 | 149 | s = m.addScene() 150 | s.lights[0].color = [0, 2, 0] 151 | s.lights[1].color = [0, 2, 0] 152 | s.lights[2].color = [0, 2, 0] 153 | s.lights[3].color = [0, 2, 0] 154 | s.duration = 3 155 | 156 | s = m.addScene() 157 | s.lights[0].position = [1.8, -1.8] 158 | s.lights[1].position = [1.8, -1.8] 159 | s.lights[2].position = [1.8, -1.8] 160 | s.lights[3].position = [1.8, -1.8] 161 | 162 | s = m.addScene() 163 | s.lights[0].position = [5, -5] 164 | s.lights[1].position = [5, -5] 165 | s.lights[2].position = [5, -5] 166 | s.lights[3].position = [5, -5] 167 | 168 | s = m.addScene() 169 | s.duration = 1 170 | s.lights[0].position = [-2.5, 2.5] 171 | s.lights[1].position = [-2.5, 2.5] 172 | s.lights[2].position = [-2.5, 2.5] 173 | s.lights[3].position = [-2.5, 2.5] 174 | s.lights[0].color = [0, 0, 0] 175 | s.lights[1].color = [0, 0, 0] 176 | s.lights[2].color = [0, 0, 0] 177 | s.lights[3].color = [0, 0, 0] 178 | 179 | return m 180 | } 181 | -------------------------------------------------------------------------------- /src/raytracer/raytracer.js: -------------------------------------------------------------------------------- 1 | import { mat4 } from 'gl-matrix' 2 | import Shader from './shader' 3 | 4 | import fsSource from './shaders/shader.frag' 5 | import vsSource from './shaders/shader.vert' 6 | 7 | export default class Raytracer { 8 | gl 9 | shader 10 | buffers 11 | 12 | constructor (gl, shaderSourceVars) { 13 | this.gl = gl 14 | this.shader = new Shader(gl, vsSource, fsSource, shaderSourceVars) 15 | this.buffers = this.initBuffers() 16 | } 17 | 18 | recompileShader (shaderSourceVars) { 19 | this.shader.delete() 20 | this.shader = new Shader(this.gl, vsSource, fsSource, shaderSourceVars) 21 | } 22 | 23 | initBuffers () { 24 | // Create a buffer for the square's positions. 25 | 26 | const positionBuffer = this.gl.createBuffer() 27 | 28 | // Select the positionBuffer as the one to apply buffer 29 | // operations to from here out. 30 | 31 | this.gl.bindBuffer(this.gl.ARRAY_BUFFER, positionBuffer) 32 | 33 | // Now create an array of positions for the square. 34 | 35 | const positions = [ 36 | -1.0, 1.0, 37 | 1.0, 1.0, 38 | -1.0, -1.0, 39 | 1.0, -1.0 40 | ] 41 | 42 | // Now pass the list of positions into WebGL to build the 43 | // shape. We do this by creating a Float32Array from the 44 | // JavaScript array, then use it to fill the current buffer. 45 | this.gl.bufferData(this.gl.ARRAY_BUFFER, 46 | new Float32Array(positions), 47 | this.gl.STATIC_DRAW) 48 | 49 | return { 50 | position: positionBuffer 51 | } 52 | } 53 | 54 | drawScene (scene) { 55 | this.gl.clearColor(0.0, 0.0, 0.0, 1.0) // Clear to black, fully opaque 56 | this.gl.clear(this.gl.COLOR_BUFFER_BIT) 57 | this.gl.clearDepth(1.0) // Clear everything 58 | this.gl.enable(this.gl.DEPTH_TEST) // Enable depth testing 59 | this.gl.depthFunc(this.gl.LEQUAL) // Near things obscure far things 60 | 61 | // Clear the canvas before we start drawing on it. 62 | 63 | this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT) 64 | 65 | // Create a perspective matrix, a special matrix that is 66 | // used to simulate the distortion of perspective in a camera. 67 | const fieldOfView = 43.5 * Math.PI / 180 // in radians 68 | const aspect = this.gl.canvas.clientWidth / this.gl.canvas.clientHeight 69 | const zNear = 0.1 70 | const zFar = 100.0 71 | const projectionMatrix = mat4.create() 72 | 73 | // gl-matrix.js always has the first argument as the destination to receive the result. 74 | mat4.perspective(projectionMatrix, fieldOfView, aspect, zNear, zFar) 75 | 76 | // Set the drawing position to the "identity" point, which is 77 | // the center of the scene. 78 | const modelViewMatrix = mat4.create() 79 | 80 | // Now move the drawing position a bit to where we want to 81 | // start drawing the square. 82 | mat4.translate(modelViewMatrix, // destination matrix 83 | modelViewMatrix, // matrix to translate 84 | [0.0, 0.0, -2.5]) // amount to translate 85 | 86 | // Tell WebGL how to pull out the positions from the position 87 | // buffer into the vertexPosition attribute. 88 | const vertexPositionLocation = this.shader.getAttribLocation('aVertexPosition') 89 | this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffers.position) 90 | this.gl.vertexAttribPointer(vertexPositionLocation, 2, this.gl.FLOAT, false, 0, 0) 91 | this.gl.enableVertexAttribArray(vertexPositionLocation) 92 | 93 | // Tell WebGL to use our program when drawing 94 | this.shader.use() 95 | 96 | this.setLightUniforms(scene) 97 | this.setCircleUniforms(scene) 98 | this.setRectUniforms(scene) 99 | 100 | // Set the shader uniforms 101 | this.shader.setUniformMatrix4fv('uProjectionMatrix', projectionMatrix) 102 | this.shader.setUniformMatrix4fv('uModelViewMatrix', modelViewMatrix) 103 | { 104 | const offset = 0 105 | const vertexCount = 4 106 | this.gl.drawArrays(this.gl.TRIANGLE_STRIP, offset, vertexCount) 107 | } 108 | } 109 | 110 | setLightUniforms (scene) { 111 | for (const [i, light] of scene.lights.entries()) { 112 | this.shader.setUniform2fv(`lights[${i}].position`, new Float32Array(light.position)) 113 | this.shader.setUniform3fv(`lights[${i}].color`, new Float32Array(light.color)) 114 | } 115 | } 116 | 117 | setCircleUniforms (scene) { 118 | for (const [i, circle] of scene.circles.entries()) { 119 | this.shader.setUniform2fv(`circles[${i}].position`, new Float32Array(circle.position)) 120 | this.shader.setUniform1f(`circles[${i}].radius`, circle.radius) 121 | } 122 | } 123 | 124 | setRectUniforms (scene) { 125 | for (const [i, rect] of scene.rectangles.entries()) { 126 | this.shader.setUniform2fv(`rectangles[${i}].position`, new Float32Array(rect.position)) 127 | this.shader.setUniform1f(`rectangles[${i}].width`, rect.width) 128 | this.shader.setUniform1f(`rectangles[${i}].height`, rect.height) 129 | this.shader.setUniform1f(`rectangles[${i}].angle`, rect.angle) 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/views/Home.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 99 | 100 | 179 | -------------------------------------------------------------------------------- /src/raytracer/shaders/shader.frag: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | 3 | #define M_PI 3.1415926535897932384626433832795 4 | 5 | varying mediump vec2 screenPosition; 6 | 7 | struct Camera 8 | { 9 | vec2 position; 10 | float zoom; 11 | } camera = Camera (vec2(0.0, 0.0), 0.5); 12 | 13 | struct Ray 14 | { 15 | vec2 origin; 16 | vec2 direction; 17 | float magnitude; 18 | }; 19 | 20 | struct Light 21 | { 22 | vec2 position; 23 | vec3 color; 24 | }; 25 | 26 | struct Circle 27 | { 28 | vec2 position; 29 | float radius; 30 | }; 31 | 32 | #define LIGHT_COUNT 42//$LIGHT_COUNT$// 33 | uniform Light lights[LIGHT_COUNT]; 34 | 35 | #define CIRCLE_COUNT 42//$CIRCLE_COUNT$// 36 | uniform Circle circles[CIRCLE_COUNT]; 37 | 38 | 39 | // INTERSECTION CHECKS 40 | bool circleIntersects(Circle circle, Ray ray) 41 | { 42 | vec2 posToOrigin = ray.origin - circle.position; 43 | float a = dot(ray.direction, ray.direction); 44 | float b = dot(ray.direction, posToOrigin); 45 | float c = dot(posToOrigin, posToOrigin) - (circle.radius * circle.radius); 46 | float d = (b * b) - (a * c); 47 | 48 | if (d < 0.0) return false; 49 | 50 | float sqrtD = sqrt(d); 51 | float distance = (-b - sqrtD) / a; 52 | if (distance < 0.0) distance = (-b + sqrtD) / a; 53 | 54 | return distance > 0.0 && distance < ray.magnitude; 55 | } 56 | 57 | struct Rectangle { 58 | vec2 lu; 59 | vec2 ru; 60 | vec2 rl; 61 | vec2 ll; 62 | }; 63 | 64 | Rectangle newRectangle(in vec2 position, float width, float height, float angle) { 65 | vec2 corners[4]; 66 | corners[0] = vec2(-width * 0.5, height * 0.5); //lu 67 | corners[1] = vec2(width * 0.5, height * 0.5); // ru 68 | corners[2] = vec2(width * 0.5, -height * 0.5); // rl 69 | corners[3] = vec2(-width * 0.5, -height * 0.5); // ll 70 | 71 | if (angle != 0.0) { 72 | for (int i = 0; i < 4; i++) { 73 | vec2 corner = corners[i]; 74 | corners[i] = vec2( 75 | corner.x * cos(angle) - corner.y * sin(angle), 76 | corner.xy * sin(angle) + corner.y * cos(angle) 77 | ); 78 | } 79 | } 80 | 81 | for (int i = 0; i < 4; i++) { 82 | corners[i] += position; 83 | } 84 | 85 | return Rectangle(corners[0], corners[1], corners[2], corners[3]); 86 | } 87 | 88 | struct RawRect { 89 | vec2 position; 90 | float width; 91 | float height; 92 | float angle; 93 | }; 94 | 95 | #define RECTANGLE_COUNT 42//$RECTANGLE_COUNT$// 96 | uniform RawRect rectangles[RECTANGLE_COUNT]; 97 | Rectangle rects[RECTANGLE_COUNT]; 98 | bool lineIntersects(Ray ray, vec2 lineStart, vec2 lineEnd) { 99 | vec2 rayStart = ray.origin; 100 | vec2 rayEnd = ray.origin + ray.direction; 101 | 102 | vec2 rayStoLineS = lineStart - rayStart; 103 | vec2 r = ray.direction * ray.magnitude; 104 | vec2 s = lineEnd - lineStart; 105 | 106 | float crossR = (rayStoLineS.x * r.y) - (rayStoLineS.y * r.x); 107 | float crossS = (rayStoLineS.x * s.y) - (rayStoLineS.y * s.x); 108 | float rCrossS = r.x * s.y - r.y * s.x; 109 | 110 | if (crossR == 0.0) { 111 | return ((lineStart.x - rayStart.x < 0.0) != (lineStart.x - rayEnd.x < 0.0)) || 112 | ((lineStart.y - rayStart.y < 0.0) != (lineStart.y - rayEnd.y < 0.0)); 113 | } 114 | 115 | if (rCrossS == 0.0) return false; 116 | 117 | float t = crossS / rCrossS; 118 | float u = crossR / rCrossS; 119 | 120 | return (t >= 0.0) && (t <= 1.0) && (u >= 0.0) && (u <= 1.0); 121 | } 122 | 123 | bool rectangleIntersect(Ray ray, Rectangle rect) { 124 | return lineIntersects(ray, rect.ll, rect.lu) || 125 | lineIntersects(ray, rect.lu, rect.ru) || 126 | lineIntersects(ray, rect.ru, rect.rl) || 127 | lineIntersects(ray, rect.rl, rect.ll); 128 | } 129 | 130 | vec2 ToWorldSpace(vec2 screenSpacePoint) 131 | { 132 | return (screenSpacePoint + camera.position) / camera.zoom; 133 | } 134 | 135 | vec3 Trace(vec2 worldPoint) 136 | { 137 | vec3 colorAtPixel = vec3(0.0, 0.0, 0.0); 138 | 139 | // Cache the rectangle transformations. 140 | Rectangle rects[RECTANGLE_COUNT]; 141 | for (int i = 0; i < RECTANGLE_COUNT; i++) { 142 | RawRect raw = rectangles[i]; 143 | rects[i] = newRectangle(raw.position, raw.width, raw.height, raw.angle); 144 | } 145 | 146 | for (int i = 0; i < LIGHT_COUNT; i++) { 147 | 148 | vec2 vectorToLight = lights[i].position - worldPoint; 149 | 150 | // Don't forget to normalize the ray's direction 151 | Ray ray = Ray(worldPoint, vectorToLight, length(vectorToLight)); 152 | ray.direction = normalize(ray.direction); 153 | 154 | // Check for occlusions between ray 155 | bool occluded = false; 156 | for (int c = 0; c < CIRCLE_COUNT; c++) { 157 | Circle circle = circles[c]; 158 | if (circleIntersects(circle, ray)) { 159 | occluded = true; 160 | break; 161 | } 162 | } 163 | if (occluded) continue; 164 | 165 | for (int r = 0; r < RECTANGLE_COUNT; r++) { 166 | Rectangle rect = rects[r]; 167 | if (rectangleIntersect(ray, rect)) { 168 | occluded = true; 169 | break; 170 | } 171 | } 172 | if (occluded) continue; 173 | 174 | 175 | float distanceToLight = length(vectorToLight); 176 | float intensity = 1.0 / (4.0 * M_PI * distanceToLight); 177 | 178 | colorAtPixel += lights[i].color * intensity; 179 | } 180 | 181 | 182 | return colorAtPixel; 183 | } 184 | 185 | 186 | void main() { 187 | gl_FragColor = vec4(Trace(ToWorldSpace(screenPosition)), 1.0); 188 | } 189 | -------------------------------------------------------------------------------- /docs/img/icons/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Created by potrace 1.11, written by Peter Selinger 2001-2013 9 | 10 | 12 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /public/img/icons/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Created by potrace 1.11, written by Peter Selinger 2001-2013 9 | 10 | 12 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /docs/js/app.4f999ba6.js: -------------------------------------------------------------------------------- 1 | (function(e){function t(t){for(var n,s,a=t[0],c=t[1],l=t[2],h=0,g=[];h2&&void 0!==arguments[2]&&arguments[2],n=this.clone();return i||(t=(Math.sin((t-.5)*Math.PI)+1)/2),this.lights.forEach((function(i,r){for(var o in n.lights[r].position)n.lights[r].position[o]=i.position[o]+t*(e.lights[r].position[o]-i.position[o]);for(var s in n.lights[r].color)n.lights[r].color[s]=i.color[s]+t*(e.lights[r].color[s]-i.color[s])})),this.circles.forEach((function(i,r){for(var o in n.circles[r].position)n.circles[r].position[o]=i.position[o]+t*(e.circles[r].position[o]-i.position[o]);n.circles[r].radius=i.radius+t*(e.circles[r].radius-i.radius)})),this.rectangles.forEach((function(i,r){for(var o in n.rectangles[r].position)n.rectangles[r].position[o]=i.position[o]+t*(e.rectangles[r].position[o]-i.position[o]);n.rectangles[r].width=i.width+t*(e.rectangles[r].width-i.width),n.rectangles[r].height=i.height+t*(e.rectangles[r].height-i.height),n.rectangles[r].angle=i.angle+t*(e.rectangles[r].angle-i.angle)})),n}},{key:"addLight",value:function(){this.lights.push(R(this.lights[this.lights.length-1]))}},{key:"addCircle",value:function(){this.circles.push(R(this.circles[this.circles.length-1]))}},{key:"addRectangle",value:function(){this.rectangles.push(R(this.rectangles[this.rectangles.length-1]))}}]),e}(),E=function(){function e(t,i,n,r){Object(m["a"])(this,e),Object(S["a"])(this,"id",void 0),Object(S["a"])(this,"scenes",[new _]),Object(S["a"])(this,"shaderSourceVars",void 0),this.id=t,this.shaderSourceVars={LIGHT_COUNT:i,CIRCLE_COUNT:n,RECTANGLE_COUNT:r};for(var o=1;o sourceVars[varName])\n }\n\n //\n // creates a shader of the given type, uploads the source and\n // compiles it.\n //\n loadShader (gl, type, source) {\n const shader = gl.createShader(type)\n\n // Send the source to the shader object\n gl.shaderSource(shader, source)\n\n // Compile the shader program\n gl.compileShader(shader)\n\n // See if it compiled successfully\n if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\n console.error(`An error occurred compiling the shaders:\\n${gl.getShaderInfoLog(shader)}`)\n gl.deleteShader(shader)\n return null\n }\n\n return shader\n }\n\n use () {\n this.gl.useProgram(this.program)\n }\n\n setUniform1f (name, value) {\n this.gl.uniform1f(this.getUniformLocation(name), value)\n }\n\n setUniformMatrix4fv (name, value) {\n this.gl.uniformMatrix4fv(this.getUniformLocation(name), false, value)\n }\n\n setUniform2fv (name, value) {\n this.gl.uniform2fv(this.getUniformLocation(name), value)\n }\n\n setUniform3fv (name, value) {\n this.gl.uniform3fv(this.getUniformLocation(name), value)\n }\n\n getUniformLocation (name) {\n // TODO: Cache uniform locations\n return this.gl.getUniformLocation(this.program, name)\n }\n\n getAttribLocation (name) {\n // TODO: Cache attrib locations\n return this.gl.getAttribLocation(this.program, name)\n }\n\n delete () {\n this.gl.deleteProgram(this.program)\n }\n}\n","export default \"precision highp float;\\n\\n#define M_PI 3.1415926535897932384626433832795\\n\\nvarying mediump vec2 screenPosition;\\n\\nstruct Camera\\n{\\n vec2 position;\\n float zoom;\\n} camera = Camera (vec2(0.0, 0.0), 0.5);\\n\\nstruct Ray\\n{\\n vec2 origin;\\n vec2 direction;\\n float magnitude;\\n};\\n\\nstruct Light\\n{\\n vec2 position;\\n vec3 color;\\n};\\n\\nstruct Circle\\n{\\n vec2 position;\\n float radius;\\n};\\n\\n#define LIGHT_COUNT 42//$LIGHT_COUNT$//\\nuniform Light lights[LIGHT_COUNT];\\n\\n#define CIRCLE_COUNT 42//$CIRCLE_COUNT$//\\nuniform Circle circles[CIRCLE_COUNT];\\n\\n\\n// INTERSECTION CHECKS\\nbool circleIntersects(Circle circle, Ray ray)\\n{\\n vec2 posToOrigin = ray.origin - circle.position;\\n float a = dot(ray.direction, ray.direction);\\n float b = dot(ray.direction, posToOrigin);\\n float c = dot(posToOrigin, posToOrigin) - (circle.radius * circle.radius);\\n float d = (b * b) - (a * c);\\n\\n if (d < 0.0) return false;\\n\\n float sqrtD = sqrt(d);\\n float distance = (-b - sqrtD) / a;\\n if (distance < 0.0) distance = (-b + sqrtD) / a;\\n\\n return distance > 0.0 && distance < ray.magnitude;\\n}\\n\\nstruct Rectangle {\\n vec2 lu;\\n vec2 ru;\\n vec2 rl;\\n vec2 ll;\\n};\\n\\nRectangle newRectangle(in vec2 position, float width, float height, float angle) {\\n vec2 corners[4];\\n corners[0] = vec2(-width * 0.5, height * 0.5); //lu\\n corners[1] = vec2(width * 0.5, height * 0.5); // ru\\n corners[2] = vec2(width * 0.5, -height * 0.5); // rl\\n corners[3] = vec2(-width * 0.5, -height * 0.5); // ll\\n\\n if (angle != 0.0) {\\n for (int i = 0; i < 4; i++) {\\n vec2 corner = corners[i];\\n corners[i] = vec2(\\n corner.x * cos(angle) - corner.y * sin(angle),\\n corner.xy * sin(angle) + corner.y * cos(angle)\\n );\\n }\\n }\\n\\n for (int i = 0; i < 4; i++) {\\n corners[i] += position;\\n }\\n\\n return Rectangle(corners[0], corners[1], corners[2], corners[3]);\\n}\\n\\nstruct RawRect {\\n vec2 position;\\n float width;\\n float height;\\n float angle;\\n};\\n\\n#define RECTANGLE_COUNT 42//$RECTANGLE_COUNT$//\\nuniform RawRect rectangles[RECTANGLE_COUNT];\\nRectangle rects[RECTANGLE_COUNT];\\nbool lineIntersects(Ray ray, vec2 lineStart, vec2 lineEnd) {\\n vec2 rayStart = ray.origin;\\n vec2 rayEnd = ray.origin + ray.direction;\\n\\n vec2 rayStoLineS = lineStart - rayStart;\\n vec2 r = ray.direction * ray.magnitude;\\n vec2 s = lineEnd - lineStart;\\n\\n float crossR = (rayStoLineS.x * r.y) - (rayStoLineS.y * r.x);\\n float crossS = (rayStoLineS.x * s.y) - (rayStoLineS.y * s.x);\\n float rCrossS = r.x * s.y - r.y * s.x;\\n\\n if (crossR == 0.0) {\\n return ((lineStart.x - rayStart.x < 0.0) != (lineStart.x - rayEnd.x < 0.0)) ||\\n ((lineStart.y - rayStart.y < 0.0) != (lineStart.y - rayEnd.y < 0.0));\\n }\\n\\n if (rCrossS == 0.0) return false;\\n\\n float t = crossS / rCrossS;\\n float u = crossR / rCrossS;\\n\\n return (t >= 0.0) && (t <= 1.0) && (u >= 0.0) && (u <= 1.0);\\n}\\n\\nbool rectangleIntersect(Ray ray, Rectangle rect) {\\n return lineIntersects(ray, rect.ll, rect.lu) ||\\n lineIntersects(ray, rect.lu, rect.ru) ||\\n lineIntersects(ray, rect.ru, rect.rl) ||\\n lineIntersects(ray, rect.rl, rect.ll);\\n}\\n\\nvec2 ToWorldSpace(vec2 screenSpacePoint)\\n{\\n return (screenSpacePoint + camera.position) / camera.zoom;\\n}\\n\\nvec3 Trace(vec2 worldPoint)\\n{\\n vec3 colorAtPixel = vec3(0.0, 0.0, 0.0);\\n\\n // Cache the rectangle transformations.\\n Rectangle rects[RECTANGLE_COUNT];\\n for (int i = 0; i < RECTANGLE_COUNT; i++) {\\n RawRect raw = rectangles[i];\\n rects[i] = newRectangle(raw.position, raw.width, raw.height, raw.angle);\\n }\\n\\n for (int i = 0; i < LIGHT_COUNT; i++) {\\n\\n vec2 vectorToLight = lights[i].position - worldPoint;\\n\\n // Don't forget to normalize the ray's direction\\n Ray ray = Ray(worldPoint, vectorToLight, length(vectorToLight));\\n ray.direction = normalize(ray.direction);\\n\\n // Check for occlusions between ray\\n bool occluded = false;\\n for (int c = 0; c < CIRCLE_COUNT; c++) {\\n Circle circle = circles[c];\\n if (circleIntersects(circle, ray)) {\\n occluded = true;\\n break;\\n }\\n }\\n if (occluded) continue;\\n\\n for (int r = 0; r < RECTANGLE_COUNT; r++) {\\n Rectangle rect = rects[r];\\n if (rectangleIntersect(ray, rect)) {\\n occluded = true;\\n break;\\n }\\n }\\n if (occluded) continue;\\n\\n\\n float distanceToLight = length(vectorToLight);\\n float intensity = 1.0 / (4.0 * M_PI * distanceToLight);\\n\\n colorAtPixel += lights[i].color * intensity;\\n }\\n\\n\\n return colorAtPixel;\\n}\\n\\n\\nvoid main() {\\n gl_FragColor = vec4(Trace(ToWorldSpace(screenPosition)), 1.0);\\n}\\n\";","export default \"precision highp float;\\n\\nattribute vec4 aVertexPosition;\\n\\nuniform mat4 uModelViewMatrix;\\nuniform mat4 uProjectionMatrix;\\n\\nvarying mediump vec2 screenPosition;\\n\\nvoid main() {\\n screenPosition = aVertexPosition.xy;\\n gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition;\\n}\\n\";","import { mat4 } from 'gl-matrix'\nimport Shader from './shader'\n\nimport fsSource from './shaders/shader.frag'\nimport vsSource from './shaders/shader.vert'\n\nexport default class Raytracer {\n gl\n shader\n buffers\n\n constructor (gl, shaderSourceVars) {\n this.gl = gl\n this.shader = new Shader(gl, vsSource, fsSource, shaderSourceVars)\n this.buffers = this.initBuffers()\n }\n\n recompileShader (shaderSourceVars) {\n this.shader.delete()\n this.shader = new Shader(this.gl, vsSource, fsSource, shaderSourceVars)\n }\n\n initBuffers () {\n // Create a buffer for the square's positions.\n\n const positionBuffer = this.gl.createBuffer()\n\n // Select the positionBuffer as the one to apply buffer\n // operations to from here out.\n\n this.gl.bindBuffer(this.gl.ARRAY_BUFFER, positionBuffer)\n\n // Now create an array of positions for the square.\n\n const positions = [\n -1.0, 1.0,\n 1.0, 1.0,\n -1.0, -1.0,\n 1.0, -1.0\n ]\n\n // Now pass the list of positions into WebGL to build the\n // shape. We do this by creating a Float32Array from the\n // JavaScript array, then use it to fill the current buffer.\n this.gl.bufferData(this.gl.ARRAY_BUFFER,\n new Float32Array(positions),\n this.gl.STATIC_DRAW)\n\n return {\n position: positionBuffer\n }\n }\n\n drawScene (scene) {\n this.gl.clearColor(0.0, 0.0, 0.0, 1.0) // Clear to black, fully opaque\n this.gl.clear(this.gl.COLOR_BUFFER_BIT)\n this.gl.clearDepth(1.0) // Clear everything\n this.gl.enable(this.gl.DEPTH_TEST) // Enable depth testing\n this.gl.depthFunc(this.gl.LEQUAL) // Near things obscure far things\n\n // Clear the canvas before we start drawing on it.\n\n this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT)\n\n // Create a perspective matrix, a special matrix that is\n // used to simulate the distortion of perspective in a camera.\n const fieldOfView = 43.5 * Math.PI / 180 // in radians\n const aspect = this.gl.canvas.clientWidth / this.gl.canvas.clientHeight\n const zNear = 0.1\n const zFar = 100.0\n const projectionMatrix = mat4.create()\n\n // gl-matrix.js always has the first argument as the destination to receive the result.\n mat4.perspective(projectionMatrix, fieldOfView, aspect, zNear, zFar)\n\n // Set the drawing position to the \"identity\" point, which is\n // the center of the scene.\n const modelViewMatrix = mat4.create()\n\n // Now move the drawing position a bit to where we want to\n // start drawing the square.\n mat4.translate(modelViewMatrix, // destination matrix\n modelViewMatrix, // matrix to translate\n [0.0, 0.0, -2.5]) // amount to translate\n\n // Tell WebGL how to pull out the positions from the position\n // buffer into the vertexPosition attribute.\n const vertexPositionLocation = this.shader.getAttribLocation('aVertexPosition')\n this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffers.position)\n this.gl.vertexAttribPointer(vertexPositionLocation, 2, this.gl.FLOAT, false, 0, 0)\n this.gl.enableVertexAttribArray(vertexPositionLocation)\n\n // Tell WebGL to use our program when drawing\n this.shader.use()\n\n this.setLightUniforms(scene)\n this.setCircleUniforms(scene)\n this.setRectUniforms(scene)\n\n // Set the shader uniforms\n this.shader.setUniformMatrix4fv('uProjectionMatrix', projectionMatrix)\n this.shader.setUniformMatrix4fv('uModelViewMatrix', modelViewMatrix)\n {\n const offset = 0\n const vertexCount = 4\n this.gl.drawArrays(this.gl.TRIANGLE_STRIP, offset, vertexCount)\n }\n }\n\n setLightUniforms (scene) {\n for (const [i, light] of scene.lights.entries()) {\n this.shader.setUniform2fv(`lights[${i}].position`, new Float32Array(light.position))\n this.shader.setUniform3fv(`lights[${i}].color`, new Float32Array(light.color))\n }\n }\n\n setCircleUniforms (scene) {\n for (const [i, circle] of scene.circles.entries()) {\n this.shader.setUniform2fv(`circles[${i}].position`, new Float32Array(circle.position))\n this.shader.setUniform1f(`circles[${i}].radius`, circle.radius)\n }\n }\n\n setRectUniforms (scene) {\n for (const [i, rect] of scene.rectangles.entries()) {\n this.shader.setUniform2fv(`rectangles[${i}].position`, new Float32Array(rect.position))\n this.shader.setUniform1f(`rectangles[${i}].width`, rect.width)\n this.shader.setUniform1f(`rectangles[${i}].height`, rect.height)\n this.shader.setUniform1f(`rectangles[${i}].angle`, rect.angle)\n }\n }\n}\n","import Raytracer from './raytracer'\n\nexport default Raytracer\n","/**\n * @function\n * @description Deep clone a class instance.\n * @param {object} instance The class instance you want to clone.\n * @returns {object} A new cloned instance.\n */\nexport default function clone (instance) {\n return Object.assign(\n Object.create(\n // Set the prototype of the new object to the prototype of the instance.\n // Used to allow new object behave like class instance.\n Object.getPrototypeOf(instance)\n ),\n // Prevent shallow copies of nested structures like arrays, etc\n JSON.parse(JSON.stringify(instance))\n )\n}\n","import clone from '../helpers/clone'\n\nexport default class Scene {\n lights = [\n {\n position: [1, 1],\n color: [1, 1, 1]\n }\n ]\n\n circles = [\n {\n position: [0, 0],\n radius: 0.1\n }\n ]\n\n rectangles = [\n {\n position: [0, 0],\n width: 0.1,\n height: 0.1,\n angle: 0\n }\n ]\n\n duration = 1\n\n clone () {\n return clone(this)\n }\n\n interpolate (nextScene, t, linear = false) {\n const interpolatedScene = this.clone()\n\n if (!linear) { // Ease in and ease out\n t = (Math.sin((t - 0.5) * Math.PI) + 1) / 2\n }\n\n // Interpolated = Current + t(Next-Current)\n this.lights.forEach((light, i) => {\n for (const p in interpolatedScene.lights[i].position) {\n interpolatedScene.lights[i].position[p] =\n light.position[p] + t * (nextScene.lights[i].position[p] - light.position[p])\n }\n\n for (const c in interpolatedScene.lights[i].color) {\n interpolatedScene.lights[i].color[c] =\n light.color[c] + t * (nextScene.lights[i].color[c] - light.color[c])\n }\n })\n\n this.circles.forEach((circle, i) => {\n for (const p in interpolatedScene.circles[i].position) {\n interpolatedScene.circles[i].position[p] =\n circle.position[p] + t * (nextScene.circles[i].position[p] - circle.position[p])\n }\n\n interpolatedScene.circles[i].radius =\n circle.radius + t * (nextScene.circles[i].radius - circle.radius)\n })\n\n this.rectangles.forEach((rectangle, i) => {\n for (const p in interpolatedScene.rectangles[i].position) {\n interpolatedScene.rectangles[i].position[p] =\n rectangle.position[p] + t * (nextScene.rectangles[i].position[p] - rectangle.position[p])\n }\n\n interpolatedScene.rectangles[i].width =\n rectangle.width + t * (nextScene.rectangles[i].width - rectangle.width)\n\n interpolatedScene.rectangles[i].height =\n rectangle.height + t * (nextScene.rectangles[i].height - rectangle.height)\n\n interpolatedScene.rectangles[i].angle =\n rectangle.angle + t * (nextScene.rectangles[i].angle - rectangle.angle)\n })\n\n return interpolatedScene\n }\n\n addLight () {\n this.lights.push(clone(this.lights[this.lights.length - 1]))\n }\n\n addCircle () {\n this.circles.push(clone(this.circles[this.circles.length - 1]))\n }\n\n addRectangle () {\n this.rectangles.push(clone(this.rectangles[this.rectangles.length - 1]))\n }\n}\n","import Scene from './scene'\n\nexport default class Movie {\n id\n scenes = [new Scene()]\n shaderSourceVars\n\n constructor (id, lightCount, circleCount, rectangleCount) {\n this.id = id\n this.shaderSourceVars = {\n LIGHT_COUNT: lightCount,\n CIRCLE_COUNT: circleCount,\n RECTANGLE_COUNT: rectangleCount\n }\n\n for (let i = 1; i < lightCount; i++) {\n this.lastScene().addLight()\n }\n\n for (let i = 1; i < circleCount; i++) {\n this.lastScene().addCircle()\n }\n\n for (let i = 1; i < rectangleCount; i++) {\n this.lastScene().addRectangle()\n }\n }\n\n sceneDurations () {\n return this.scenes.map(s => s.duration)\n }\n\n duration () {\n return this.sceneDurations().reduce((d0, d1) => d0 + d1, 0)\n }\n\n lastScene () {\n return this.scenes[this.scenes.length - 1]\n }\n\n addScene () {\n this.scenes.push(this.lastScene().clone())\n return this.lastScene()\n }\n\n currentScene (time) {\n const sceneDurations = this.sceneDurations()\n const sceneCount = this.scenes.length\n\n time %= this.duration()\n\n let timeSum = 0\n let sceneIndex = -1\n while (timeSum < time) {\n sceneIndex++\n timeSum += sceneDurations[sceneIndex]\n sceneIndex %= sceneCount\n }\n\n const scene0 = this.scenes[sceneIndex]\n const scene1 = sceneIndex + 1 === sceneCount\n ? this.scenes[0] // go back to scene 0 at the end\n : this.scenes[sceneIndex + 1] // otherwise, go the the succeeding scene\n\n const normalizedTime = (time - timeSum + scene0.duration) / scene0.duration\n return scene0.interpolate(scene1, normalizedTime)\n }\n}\n","import Movie from '../movie'\n\nexport default function rectangleMovie () {\n const m = new Movie('Rectangle Movie', 2, 1, 9)\n let s = m.lastScene()\n\n const brightness = 2\n\n s.lights[0].position = [1, 1]\n s.lights[0].color = [brightness, 0, brightness]\n\n s.lights[1].position = [-1, 0]\n s.lights[1].color = [0, 0, 0]\n\n s.circles[0].radius = 0\n\n const spacing = 0.4\n const sideDimension = 0.2\n\n const combinedWidth = spacing * 3 - sideDimension\n\n for (let i = 0; i < 9; i++) {\n const x = i % 3\n const y = ~~(i / 3)\n\n s.rectangles[i].width = 0\n s.rectangles[i].height = 0\n s.rectangles[i].position[0] = spacing * x - spacing\n s.rectangles[i].position[1] = spacing * y - spacing\n }\n\n s.rectangles[4].width = combinedWidth\n s.rectangles[4].height = combinedWidth\n\n s = m.addScene()\n s.lights[0].position[1] = -1\n\n s = m.addScene()\n s.lights[0].position[0] = -1\n\n // combine rects for split\n s.rectangles[1].width = combinedWidth\n s.rectangles[1].height = sideDimension\n\n s.rectangles[7].width = combinedWidth\n s.rectangles[7].height = sideDimension\n\n s = m.addScene()\n s.lights[0].position[1] = 0\n\n s = m.addScene()\n s.rectangles[4].height = sideDimension\n\n s = m.addScene()\n s.lights[0].color = [brightness, 0, 0]\n s.lights[1].color = [0, 0, brightness]\n\n s = m.addScene()\n s.lights[0].position[1] = spacing / 4\n s.lights[1].position[1] = -spacing / 4\n\n s = m.addScene()\n s.lights[0].position[1] = spacing / 2\n s.lights[1].position[1] = -spacing / 2\n\n s = m.addScene()\n s.lights[0].position[0] = 1\n\n s = m.addScene()\n s.lights[1].position[0] = -1\n\n s = m.addScene()\n s.lights[1].position[0] = 1\n s.lights[1].position[0] = -1\n\n const pointBetweenRects = spacing / 2\n s = m.addScene()\n s.lights[0].position[0] = pointBetweenRects\n s.lights[1].position[0] = -pointBetweenRects\n\n for (const i of [0, 2, 3, 5, 6, 8]) {\n s.rectangles[i].width = sideDimension\n s.rectangles[i].height = sideDimension\n }\n\n s = m.addScene()\n s.duration = 3\n for (const i of [1, 4, 7]) {\n s.rectangles[i].width = sideDimension\n s.rectangles[i].height = sideDimension\n }\n\n s = m.addScene()\n s.rectangles[4].angle = 3 * Math.PI\n s.duration = 0.5\n\n s = m.addScene()\n s.lights[0].position[1] = -pointBetweenRects\n s.lights[1].position[1] = pointBetweenRects\n\n s = m.addScene()\n s.lights[0].position[0] = -pointBetweenRects\n s.lights[1].position[0] = pointBetweenRects\n s.duration = 1\n\n s = m.addScene()\n s.lights[0].position[1] = 1\n s.lights[1].position[1] = -1\n\n s = m.addScene()\n s.lights[0].position[1] = -1\n s.lights[1].position[1] = 1\n\n s = m.addScene()\n s.lights[0].position[1] = pointBetweenRects\n s.lights[1].position[1] = -pointBetweenRects\n\n s = m.addScene()\n s.lights[0].position[1] = 1\n s.lights[1].position[1] = 1\n s.duration = 0.5\n\n s = m.addScene()\n s.lights[0].position[0] = -pointBetweenRects / 2\n s.lights[1].position[0] = pointBetweenRects / 2\n\n s = m.addScene()\n s.lights[0].position[0] = 0\n s.lights[1].position[0] = 0\n s.duration = 1\n\n s.rectangles[4].width = combinedWidth\n s.rectangles[4].height = combinedWidth\n\n s = m.addScene()\n s.lights[0].color = [2, 0, 2]\n s.lights[1].color = [0, 0, 0]\n for (const i of [0, 1, 2, 3, 5, 6, 7, 8]) {\n s.rectangles[i].width = 0\n s.rectangles[i].height = 0\n }\n s.duration = 2\n\n return m\n}\n","import Movie from '../movie'\n\nexport default function circleMovie () {\n const m = new Movie('Circle Movie', 4, 9, 1)\n\n // Setup inital scene\n let s = m.lastScene()\n s.rectangles[0].width = 0\n s.rectangles[0].height = 0\n s.rectangles[0].position = [-10, 0]\n s.addCircle()\n\n s.lights[0].position = [-2.5, 2.5]\n s.lights[0].color = [0, 0, 0]\n s.lights[1].position = [0, 0]\n s.lights[2].position = [0, 0]\n s.lights[3].position = [0, 0]\n s.lights[1].color = [0, 0, 0]\n s.lights[2].color = [0, 0, 0]\n s.lights[3].color = [0, 0, 0]\n\n s = m.addScene()\n s.lights[0].color = [2, 2, 2]\n s.duration = 2\n\n s = m.addScene()\n\n s = m.addScene()\n s.lights[0].position = [-0.5, 0.5]\n s.duration = 0.5\n\n s = m.addScene()\n\n s = m.addScene()\n s.lights[0].position = [0.5, 0.5]\n s.lights[0].color = [2, 0, 0]\n\n s = m.addScene()\n s.lights[0].position = [0.5, -0.5]\n s.lights[0].color = [1, 1, 0]\n\n s = m.addScene()\n s.lights[0].position = [-0.5, -0.5]\n s.lights[0].color = [0, 2, 0]\n\n s = m.addScene()\n s.lights[0].position = [-0.5, 0.5]\n s.lights[0].color = [0, 0, 2]\n\n s = m.addScene()\n s.lights[0].color = [2, 1, 3]\n s.duration = 2\n\n s = m.addScene()\n s.circles[0].position = [-1, -1]\n s.circles[1].position = [1, 1]\n s.circles[2].position = [1, -1]\n s.circles[3].position = [1, -1]\n s.circles[4].position = [1, -1]\n s.circles[5].position = [1, -1]\n s.circles[6].position = [1, -1]\n s.circles[7].position = [1, -1]\n s.circles[8].position = [1, -1]\n\n s.lights[0].position = [0, 0]\n\n s = m.addScene()\n s.circles[0].radius = 0.4\n s.circles[1].radius = 0.5\n s.lights[0].color = [9, 4, 8]\n s.circles[3].position = [1, 0]\n s.circles[4].position = [0, -1]\n\n s = m.addScene()\n s.circles[0].radius = 0.2\n s.circles[1].radius = 0.2\n s.circles[3].radius = 0.15\n s.circles[4].radius = 0.15\n\n s = m.addScene()\n s.lights[0].color = [1, 1, 4]\n\n s = m.addScene()\n s.lights[1].color = [9, 4, 8]\n\n s = m.addScene()\n s.lights[1].position = [0, 1.8]\n s.duration = 1\n\n s = m.addScene()\n s.lights[1].position = [1.8, 1.8]\n\n s = m.addScene()\n s.lights[1].position = [1.8, -1.8]\n\n s = m.addScene()\n s.lights[1].position = [-1.8, -1.8]\n\n s = m.addScene()\n s.lights[1].position = [-1.8, 0]\n\n s = m.addScene()\n s.lights[1].position = [0, 0]\n\n s = m.addScene()\n s.circles[0].radius = 0.1\n s.circles[1].radius = 0.1\n s.circles[2].radius = 0.1\n s.circles[3].radius = 0.1\n s.circles[4].radius = 0.1\n\n s = m.addScene()\n s.circles[5].position = [1, 0.5]\n s.circles[6].position = [1, -0.5]\n s.circles[7].position = [0.5, -1]\n s.circles[8].position = [-0.5, -1]\n\n s = m.addScene()\n s.lights[0].color = [0, 0, 0]\n s.lights[1].color = [0, 0, 0]\n s.lights[2].color = [0, 0, 0]\n s.lights[3].color = [0, 0, 0]\n\n s = m.addScene()\n s.lights[0].position = [1.8, 0.7]\n s.lights[1].position = [1.8, -0.7]\n s.lights[2].position = [0.7, -1.8]\n s.lights[3].position = [-0.7, -1.8]\n s.duration = 0.5\n\n s = m.addScene()\n s.lights[0].color = [0, 2, 0]\n\n s = m.addScene()\n s.lights[0].color = [0, 0, 0]\n s.lights[1].color = [0, 2, 0]\n\n s = m.addScene()\n s.lights[1].color = [0, 0, 0]\n s.lights[2].color = [0, 2, 0]\n\n s = m.addScene()\n s.lights[2].color = [0, 0, 0]\n s.lights[3].color = [0, 2, 0]\n\n s = m.addScene()\n s.lights[3].color = [0, 0, 0]\n\n s = m.addScene()\n s.lights[0].color = [0, 2, 0]\n s.lights[1].color = [0, 2, 0]\n s.lights[2].color = [0, 2, 0]\n s.lights[3].color = [0, 2, 0]\n s.duration = 3\n\n s = m.addScene()\n s.lights[0].position = [1.8, -1.8]\n s.lights[1].position = [1.8, -1.8]\n s.lights[2].position = [1.8, -1.8]\n s.lights[3].position = [1.8, -1.8]\n\n s = m.addScene()\n s.lights[0].position = [5, -5]\n s.lights[1].position = [5, -5]\n s.lights[2].position = [5, -5]\n s.lights[3].position = [5, -5]\n\n s = m.addScene()\n s.duration = 1\n s.lights[0].position = [-2.5, 2.5]\n s.lights[1].position = [-2.5, 2.5]\n s.lights[2].position = [-2.5, 2.5]\n s.lights[3].position = [-2.5, 2.5]\n s.lights[0].color = [0, 0, 0]\n s.lights[1].color = [0, 0, 0]\n s.lights[2].color = [0, 0, 0]\n s.lights[3].color = [0, 0, 0]\n\n return m\n}\n","\n\n\n\n\n","import mod from \"-!../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../node_modules/thread-loader/dist/cjs.js!../../node_modules/babel-loader/lib/index.js!../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Home.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../node_modules/thread-loader/dist/cjs.js!../../node_modules/babel-loader/lib/index.js!../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Home.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./Home.vue?vue&type=template&id=01506eff&\"\nimport script from \"./Home.vue?vue&type=script&lang=js&\"\nexport * from \"./Home.vue?vue&type=script&lang=js&\"\nimport style0 from \"./Home.vue?vue&type=style&index=0&lang=scss&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","import Vue from 'vue'\nimport VueRouter from 'vue-router'\nimport Home from '../views/Home.vue'\n\nVue.use(VueRouter)\n\nconst routes = [\n {\n path: '/',\n redirect: '/movie/0',\n name: 'Home',\n component: Home\n },\n {\n path: '/movie/:id',\n name: 'Movie',\n component: Home,\n props (route) {\n const props = { ...route.params }\n props.id = +props.id\n return props\n }\n },\n {\n path: '/about',\n name: 'About',\n // route level code-splitting\n // this generates a separate chunk (about.[hash].js) for this route\n // which is lazy-loaded when the route is visited.\n component: () => import(/* webpackChunkName: \"about\" */ '../views/About.vue')\n }\n]\n\nconst router = new VueRouter({\n mode: 'history',\n base: process.env.BASE_URL,\n routes\n})\n\nexport default router\n","import Vue from 'vue'\nimport Vuex from 'vuex'\n\nVue.use(Vuex)\n\nexport default new Vuex.Store({\n state: {\n },\n mutations: {\n },\n actions: {\n },\n modules: {\n }\n})\n","import Vue from 'vue'\nimport App from './App.vue'\nimport './registerServiceWorker'\nimport router from './router'\nimport store from './store'\n\nVue.config.productionTip = false\n\nnew Vue({\n router,\n store,\n render: h => h(App)\n}).$mount('#app')\n","import mod from \"-!../node_modules/mini-css-extract-plugin/dist/loader.js??ref--8-oneOf-1-0!../node_modules/css-loader/dist/cjs.js??ref--8-oneOf-1-1!../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../node_modules/postcss-loader/src/index.js??ref--8-oneOf-1-2!../node_modules/sass-loader/dist/cjs.js??ref--8-oneOf-1-3!../node_modules/cache-loader/dist/cjs.js??ref--0-0!../node_modules/vue-loader/lib/index.js??vue-loader-options!./App.vue?vue&type=style&index=0&lang=scss&\"; export default mod; export * from \"-!../node_modules/mini-css-extract-plugin/dist/loader.js??ref--8-oneOf-1-0!../node_modules/css-loader/dist/cjs.js??ref--8-oneOf-1-1!../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../node_modules/postcss-loader/src/index.js??ref--8-oneOf-1-2!../node_modules/sass-loader/dist/cjs.js??ref--8-oneOf-1-3!../node_modules/cache-loader/dist/cjs.js??ref--0-0!../node_modules/vue-loader/lib/index.js??vue-loader-options!./App.vue?vue&type=style&index=0&lang=scss&\""],"sourceRoot":""} --------------------------------------------------------------------------------