├── .gitignore ├── .npmignore ├── README.md ├── code-complete.gif ├── examples ├── anisotropic │ ├── index.html │ └── index.ts ├── base-primitives │ ├── index.html │ └── index.ts ├── compressed-textures │ ├── index.html │ └── index.ts ├── compute-vertex-normal │ ├── index.html │ └── index.ts ├── cube-map │ ├── index.html │ └── index.ts ├── curves │ ├── index.html │ └── index.ts ├── draw-modes │ ├── index.html │ └── index.ts ├── flat-shading-matcap │ ├── index.html │ └── index.ts ├── fog │ ├── index.html │ └── index.ts ├── fresnel │ ├── index.html │ └── index.ts ├── frustum-culling │ ├── index.html │ └── index.ts ├── gpgpu-particles │ ├── index.html │ └── index.ts ├── high-mesh-count │ ├── index.html │ └── index.ts ├── indexed-vs-non-indexed │ ├── index.html │ └── index.ts ├── instancing-gpu-picking │ ├── index.html │ └── index.ts ├── instancing │ ├── index.html │ └── index.ts ├── load-gltf │ ├── index.html │ └── index.ts ├── load-json │ ├── index.html │ └── index.ts ├── mouse-flowmap │ ├── index.html │ └── index.ts ├── mrt │ ├── index.html │ └── index.ts ├── msdf-text │ ├── index.html │ └── index.ts ├── normal-maps │ ├── index.html │ └── index.ts ├── orbit-controls │ ├── index.html │ └── index.ts ├── particles │ ├── index.html │ └── index.ts ├── pbr │ ├── index.html │ └── index.ts ├── point-lighting │ ├── index.html │ └── index.ts ├── polylines │ ├── index.html │ └── index.ts ├── post-bloom │ ├── index.html │ └── index.ts ├── post-fluid-distortion │ ├── index.html │ └── index.ts ├── post-fxaa │ ├── index.html │ └── index.ts ├── raycasting │ ├── index.html │ └── index.ts ├── render-to-texture │ ├── index.html │ └── index.ts ├── scene-graph │ ├── index.html │ └── index.ts ├── shadow-maps │ ├── index.html │ └── index.ts ├── skinning │ ├── index.html │ └── index.ts ├── skydome │ ├── index.html │ └── index.ts ├── sort-transparency │ ├── index.html │ └── index.ts ├── template.html ├── textures │ ├── index.html │ └── index.ts ├── torus │ ├── index.html │ └── index.ts ├── triangle-screen-shader │ ├── index.html │ └── index.ts ├── wireframe-shader │ ├── index.html │ └── index.ts └── wireframe │ ├── index.html │ └── index.ts ├── index.html ├── package-lock.json ├── package.json ├── public └── assets │ ├── acorn.jpg │ ├── acorn.json │ ├── airplane.jpg │ ├── airplane.json │ ├── anim-format.json │ ├── compressed │ ├── astc-m-y.ktx │ ├── etc-m-y.ktx │ ├── etc1-m-y.ktx │ ├── pvrtc-m-y.ktx │ ├── s3tc-m-y.ktx │ └── uv.jpg │ ├── croissant.jpg │ ├── croissant.json │ ├── cube │ ├── negx.jpg │ ├── negy.jpg │ ├── negz.jpg │ ├── posx.jpg │ ├── posy.jpg │ └── posz.jpg │ ├── earth.jpg │ ├── earth_cloud.jpg │ ├── earth_specular.jpg │ ├── favicon.png │ ├── fonts │ ├── FiraSans-Bold.json │ ├── FiraSans-Bold.png │ ├── FiraSans-Bold.ttf │ ├── raleway-bold-webfont.woff │ ├── raleway-bold-webfont.woff2 │ ├── raleway-regular-webfont.woff │ └── raleway-regular-webfont.woff2 │ ├── forest.jpg │ ├── forest.json │ ├── fox.jpg │ ├── fox.json │ ├── gltf │ └── hershel.glb │ ├── goat.jpg │ ├── goat.json │ ├── granite-diffuse.jpg │ ├── granite-normal.jpg │ ├── grid.jpg │ ├── laputa.mp4 │ ├── leaf.jpg │ ├── macaw.jpg │ ├── macaw.json │ ├── main.css │ ├── matcap.jpg │ ├── octopus.jpg │ ├── octopus.json │ ├── ogl.png │ ├── pbr │ ├── black.jpg │ ├── car-ext-color.jpg │ ├── car-ext-emissive.jpg │ ├── car-ext-inner.json │ ├── car-ext-normal.jpg │ ├── car-ext-opacity.jpg │ ├── car-ext-rmo.jpg │ ├── car-ext.json │ ├── car-int-color.jpg │ ├── car-int-normal.jpg │ ├── car-int-rmo.jpg │ ├── car-int.json │ ├── car-shadow.jpg │ ├── car-shadow.png │ ├── lut.png │ ├── waterfall-diffuse-RGBM.png │ ├── waterfall-specular-RGBM.png │ └── white.jpg │ ├── plane.json │ ├── rounded-cube.json │ ├── saddle.jpg │ ├── saddle.json │ ├── sky.jpg │ ├── snout-anim.json │ ├── snout-rig.json │ ├── snout-shadow.jpg │ ├── snout.jpg │ ├── sunset-diffuse-RGBM.png │ ├── sunset-specular-RGBM.png │ ├── sunset.exr │ ├── uv.jpg │ ├── water.jpg │ ├── windmill.jpg │ └── windmill.json ├── src ├── Guards.ts ├── core │ ├── Camera.ts │ ├── Geometry.ts │ ├── Mesh.ts │ ├── Program.ts │ ├── RenderTarget.ts │ ├── Renderer.ts │ ├── Texture.ts │ └── Transform.ts ├── extras │ ├── Animation.ts │ ├── Box.ts │ ├── Curve.ts │ ├── Cylinder.ts │ ├── Flowmap.ts │ ├── GLTFAnimation.ts │ ├── GLTFLoader.ts │ ├── GLTFSkin.ts │ ├── GPGPU.ts │ ├── KTXTexture.ts │ ├── NormalProgram.ts │ ├── Orbit.ts │ ├── Plane.ts │ ├── Polyline.ts │ ├── Post.ts │ ├── Raycast.ts │ ├── Shadow.ts │ ├── Skin.ts │ ├── Sphere.ts │ ├── Text.ts │ ├── TextureLoader.ts │ ├── Torus.ts │ └── Triangle.ts ├── index.ts └── math │ ├── Color.ts │ ├── Euler.ts │ ├── Mat3.ts │ ├── Mat4.ts │ ├── Quat.ts │ ├── Vec2.ts │ ├── Vec3.ts │ ├── Vec4.ts │ └── functions │ ├── ColorFunc.ts │ ├── EulerFunc.ts │ ├── Mat3Func.ts │ ├── Mat4Func.ts │ ├── QuatFunc.ts │ ├── Vec2Func.ts │ ├── Vec3Func.ts │ └── Vec4Func.ts ├── tea.yaml ├── tsconfig.json └── vite.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | lib 4 | dist 5 | yarn-error.log 6 | .vscode 7 | yarn.lock -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | examples -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

OGL-TypeScript

2 | 3 |

4 | 5 | version 6 | 7 | 8 | license 9 | 10 | 11 | dependencies 12 | 13 | 14 | size 15 | 16 |

17 | 18 |

Minimal WebGL library.

19 | 20 |
21 | 22 | [See the Examples!](https://nshen.github.io/ogl-typescript/examples) 23 | 24 | OGL-TypeScript is TypeScript port of [OGL Minimal WebGL library](https://github.com/oframe/ogl) , so it add code completion feature to OGL. 25 | 26 | 27 | 28 | ## Install 29 | 30 | ```bash 31 | npm i ogl-typescript # yarn add ogl-typescript 32 | ``` 33 | 34 | ## Usage 35 | 36 | Same as OGL, just change 37 | 38 | ```typescript 39 | import { ... } from 'ogl'; 40 | ``` 41 | 42 | to 43 | 44 | ```typescript 45 | import { ... } from 'ogl-typescript' 46 | ``` 47 | 48 | You can find [ogl-typescript-starter](https://github.com/nshen/ogl-typescript-starter) here, it's very easy to get start. 49 | 50 | ## Examples 51 | 52 | [Show me what you got!](https://nshen.github.io/ogl-typescript/examples) - Explore a comprehensive list of examples, with comments in the source code. 53 | 54 | ## Build 55 | 56 | dev mode 57 | 58 | ```bash 59 | yarn dev # npm run dev 60 | ``` 61 | 62 | build library 63 | 64 | ```bash 65 | yarn build # npm run build 66 | ``` 67 | 68 | build examples 69 | 70 | ```bash 71 | yarn build:examples # npm run build:examples 72 | ``` 73 | 74 | ## Versions 75 | 76 | | OGL-TypeScript | OGL | | 77 | | -------------- | ------ | --------------------------------------------- | 78 | | 0.1.40 | 0.0.71 | | 79 | | 0.1.39 | 0.0.70 | | 80 | | 0.1.38 | 0.0.69 | | 81 | | 0.1.37 | 0.0.68 | | 82 | | 0.1.36 | 0.0.67 | | 83 | | 0.1.35 | 0.0.65 | | 84 | | 0.1.34 | 0.0.64 | | 85 | | 0.1.33 | 0.0.63 | | 86 | | 0.1.32 | 0.0.62 | | 87 | | 0.1.31 | 0.0.61 | build with [vite](https://vitejs.dev/) | 88 | | 0.1.20 | 0.0.60 | | 89 | | 0.1.19 | 0.0.59 | | 90 | | 0.1.18 | 0.0.58 | | 91 | | 0.1.17 | 0.0.57 | | 92 | | 0.1.16 | | add computeVertexNormal method to Geometry | 93 | | 0.1.15 | 0.0.56 | | 94 | | 0.1.14 | 0.0.55 | | 95 | | 0.1.13 | 0.0.54 | | 96 | | 0.1.12 | 0.0.53 | | 97 | | 0.1.11 | 0.0.52 | | 98 | | 0.1.10 | 0.0.51 | | 99 | | 0.1.9 | 0.0.50 | | 100 | | 0.1.8 | 0.0.49 | | 101 | | 0.1.7 | 0.0.48 | | 102 | | 0.1.6 | 0.0.47 | | 103 | | 0.1.5 | 0.0.46 | | 104 | | 0.1.4 | 0.0.45 | | 105 | | 0.1.3 | 0.0.43 | | 106 | | 0.1.2 | 0.0.42 | | 107 | | 0.1.1 | 0.0.40 | | 108 | -------------------------------------------------------------------------------- /code-complete.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/code-complete.gif -------------------------------------------------------------------------------- /examples/anisotropic/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /examples/anisotropic/index.ts: -------------------------------------------------------------------------------- 1 | import { Renderer, Camera, Transform, Texture, Program, Mesh, Plane } from '../../src'; 2 | 3 | const vertex = /* glsl */ ` 4 | attribute vec2 uv; 5 | attribute vec3 position; 6 | 7 | uniform mat4 modelViewMatrix; 8 | uniform mat4 projectionMatrix; 9 | 10 | varying vec2 vUv; 11 | 12 | void main() { 13 | vUv = uv; 14 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); 15 | } 16 | `; 17 | 18 | const fragment = /* glsl */ ` 19 | precision highp float; 20 | 21 | uniform sampler2D tMap; 22 | uniform sampler2D tMapA; 23 | uniform float fSlide; 24 | 25 | varying vec2 vUv; 26 | 27 | void main() { 28 | vec3 tex = texture2D(tMap, vUv).rgb; 29 | vec3 texA = texture2D(tMapA, vUv).rgb; 30 | 31 | gl_FragColor.rgb = mix(tex, texA, step(fSlide, vUv.x)) + 0.1; 32 | gl_FragColor.a = 1.0; 33 | } 34 | `; 35 | 36 | const renderer = new Renderer({ dpr: 2 }); 37 | const gl = renderer.gl; 38 | document.body.appendChild(gl.canvas); 39 | gl.clearColor(1, 1, 1, 1); 40 | 41 | const camera = new Camera(gl); 42 | camera.position.set(0, 0, 1); 43 | 44 | function resize() { 45 | renderer.setSize(window.innerWidth, window.innerHeight); 46 | camera.perspective({ aspect: gl.canvas.width / gl.canvas.height }); 47 | } 48 | window.addEventListener('resize', resize, false); 49 | resize(); 50 | 51 | const scene = new Transform(); 52 | 53 | // By default, anisotropy is not active 54 | const texture = new Texture(gl); 55 | 56 | // Pass in number of anisotropic samples to activate 57 | const textureAnisotropy = new Texture(gl, { anisotropy: 16 }); 58 | 59 | const img = new Image(); 60 | img.src = '../../assets/grid.jpg'; 61 | img.onload = () => { 62 | texture.image = img; 63 | textureAnisotropy.image = img; 64 | }; 65 | 66 | const geometry = new Plane(gl); 67 | 68 | const program = new Program(gl, { 69 | vertex, 70 | fragment, 71 | uniforms: { 72 | tMap: { value: texture }, 73 | tMapA: { value: textureAnisotropy }, 74 | fSlide: { value: 0.5 }, 75 | }, 76 | }); 77 | 78 | const mesh = new Mesh(gl, { 79 | geometry: geometry, 80 | program: program, 81 | }); 82 | 83 | mesh.scale.set(1, 2, 1); 84 | mesh.rotation.set(-1.5, 0, 0); 85 | 86 | mesh.setParent(scene); 87 | 88 | gl.canvas.addEventListener('mousemove', (event) => { 89 | const x = (2 * event.x) / gl.canvas.width; 90 | program.uniforms.fSlide.value = x; 91 | }); 92 | 93 | requestAnimationFrame(update); 94 | function update(t) { 95 | requestAnimationFrame(update); 96 | renderer.render({ scene, camera }); 97 | } 98 | 99 | document.getElementsByClassName('Info')[0].innerHTML = 'Anisotropic'; 100 | document.getElementsByClassName('Info split')[0].innerHTML = 'Texture anisotropy: None Texture anisotropy: 16'; 101 | document.title = 'OGL • Anisotropic'; 102 | -------------------------------------------------------------------------------- /examples/base-primitives/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | -------------------------------------------------------------------------------- /examples/base-primitives/index.ts: -------------------------------------------------------------------------------- 1 | import { Renderer, Camera, Transform, Program, Mesh, Plane, Sphere, Box, Cylinder, Orbit } from '../../src/index'; 2 | 3 | const vertex = /* glsl */ ` 4 | precision highp float; 5 | precision highp int; 6 | 7 | attribute vec3 position; 8 | attribute vec3 normal; 9 | 10 | uniform mat4 modelViewMatrix; 11 | uniform mat4 projectionMatrix; 12 | uniform mat3 normalMatrix; 13 | 14 | varying vec3 vNormal; 15 | 16 | void main() { 17 | vNormal = normalize(normalMatrix * normal); 18 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); 19 | } 20 | `; 21 | 22 | const fragment = /* glsl */ ` 23 | precision highp float; 24 | precision highp int; 25 | 26 | varying vec3 vNormal; 27 | 28 | void main() { 29 | vec3 normal = normalize(vNormal); 30 | float lighting = dot(normal, normalize(vec3(-0.3, 0.8, 0.6))); 31 | gl_FragColor.rgb = vec3(0.2, 0.8, 1.0) + lighting * 0.1; 32 | gl_FragColor.a = 1.0; 33 | } 34 | `; 35 | 36 | const renderer = new Renderer({ dpr: 2 }); 37 | const gl = renderer.gl; 38 | document.body.appendChild(gl.canvas); 39 | gl.clearColor(1, 1, 1, 1); 40 | 41 | const camera = new Camera(gl, { fov: 35 }); 42 | camera.position.set(0, 1, 7); 43 | camera.lookAt([0, 0, 0]); 44 | const controls = new Orbit(camera); 45 | 46 | function resize() { 47 | renderer.setSize(window.innerWidth, window.innerHeight); 48 | camera.perspective({ aspect: gl.canvas.width / gl.canvas.height }); 49 | } 50 | window.addEventListener('resize', resize, false); 51 | resize(); 52 | 53 | const scene = new Transform(); 54 | 55 | const planeGeometry = new Plane(gl); 56 | const sphereGeometry = new Sphere(gl); 57 | const cubeGeometry = new Box(gl); 58 | const cylinderGeometry = new Cylinder(gl); 59 | 60 | const program = new Program(gl, { 61 | vertex, 62 | fragment, 63 | 64 | // Don't cull faces so that plane is double sided - default is gl.BACK 65 | cullFace: null, 66 | }); 67 | 68 | const plane = new Mesh(gl, { geometry: planeGeometry, program }); 69 | plane.position.set(0, 1.3, 0); 70 | plane.setParent(scene); 71 | 72 | const sphere = new Mesh(gl, { geometry: sphereGeometry, program }); 73 | sphere.position.set(1.3, 0, 0); 74 | sphere.setParent(scene); 75 | 76 | const cube = new Mesh(gl, { geometry: cubeGeometry, program }); 77 | cube.position.set(0, -1.3, 0); 78 | cube.setParent(scene); 79 | 80 | const cylinder = new Mesh(gl, { geometry: cylinderGeometry, program }); 81 | cylinder.position.set(-1.3, 0, 0); 82 | cylinder.setParent(scene); 83 | 84 | requestAnimationFrame(update); 85 | function update() { 86 | requestAnimationFrame(update); 87 | controls.update(); 88 | 89 | plane.rotation.y -= 0.02; 90 | sphere.rotation.y -= 0.03; 91 | cube.rotation.y -= 0.04; 92 | cylinder.rotation.y -= 0.02; 93 | 94 | renderer.render({ scene, camera }); 95 | } 96 | 97 | document.getElementsByClassName('Info')[0].innerHTML = 'Base Primitives - Plane, Cube, Sphere, Cylinder'; 98 | document.title = 'OGL • Base Primitives - Plane, Cube, Sphere, Cylinder'; 99 | -------------------------------------------------------------------------------- /examples/compressed-textures/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /examples/compressed-textures/index.ts: -------------------------------------------------------------------------------- 1 | import { Renderer, Camera, Transform, Texture, Program, Mesh, Plane, Orbit, TextureLoader } from '../../src'; 2 | 3 | document.getElementsByClassName('Info')[0].innerHTML = 'Compressed Textures.'; 4 | document.title = 'OGL • Compressed Textures'; 5 | 6 | const vertex = /* glsl */ ` 7 | attribute vec2 uv; 8 | attribute vec3 position; 9 | 10 | uniform mat4 modelViewMatrix; 11 | uniform mat4 projectionMatrix; 12 | 13 | varying vec2 vUv; 14 | 15 | void main() { 16 | vUv = uv; 17 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); 18 | } 19 | `; 20 | 21 | const fragment = /* glsl */ ` 22 | precision highp float; 23 | 24 | uniform sampler2D tMap; 25 | 26 | varying vec2 vUv; 27 | 28 | void main() { 29 | gl_FragColor = texture2D(tMap, vUv * 2.0); 30 | } 31 | `; 32 | 33 | const renderer = new Renderer({ dpr: 2 }); 34 | const gl = renderer.gl; 35 | document.body.appendChild(gl.canvas); 36 | gl.clearColor(1, 1, 1, 1); 37 | 38 | const camera = new Camera(gl, { fov: 45 }); 39 | camera.position.set(-1, 0.5, 2); 40 | 41 | const controls = new Orbit(camera); 42 | 43 | function resize() { 44 | renderer.setSize(window.innerWidth, window.innerHeight); 45 | camera.perspective({ aspect: gl.canvas.width / gl.canvas.height }); 46 | } 47 | 48 | window.addEventListener('resize', resize, false); 49 | resize(); 50 | 51 | const scene = new Transform(); 52 | 53 | // Compressed textures use the KTXTexture (Khronos Texture) class 54 | // For generation, use https://github.com/TimvanScherpenzeel/texture-compressor 55 | 56 | // For a handy method that handles loading for you, use the TextureLoader.load() method. 57 | // Either pass a `src` string to load it (regardless of support) 58 | // const texture = TextureLoader.load(gl, { 59 | // src: 'assets/compressed/s3tc-m-y.ktx', 60 | // }); 61 | 62 | // Or pass in an object of extension:src key-values, and the function will use the first 63 | // supported format in the list - so order by preference! 64 | const texture = TextureLoader.load(gl, { 65 | src: { 66 | s3tc: '../../assets/compressed/s3tc-m-y.ktx', 67 | etc: '../../assets/compressed/etc-m-y.ktx', 68 | pvrtc: '../../assets/compressed/pvrtc-m-y.ktx', 69 | jpg: '../../assets/compressed/uv.jpg', 70 | }, 71 | wrapS: gl.REPEAT, 72 | wrapT: gl.REPEAT, 73 | }); 74 | // A console warning will show when no supported format was supplied 75 | 76 | // `loaded` property is a promise resolved when the file is loaded and processed 77 | // texture.loaded.then(() => console.log('loaded')); 78 | 79 | // You can check which format was applied using the `ext` property 80 | document.body.querySelector('.Info').textContent += ` Supported format chosen: '${texture.ext}'.`; 81 | 82 | // For direct use of the KTXTexture class, you first need to activate the extensions 83 | // TextureLoader.getSupportedExtensions(); 84 | 85 | // Then create an empty texture 86 | // const texture = new KTXTexture(gl); 87 | 88 | // Then, when the buffer has loaded, parse it using the parseBuffer method 89 | // fetch(src) 90 | // .then(res => res.arrayBuffer()) 91 | // .then(buffer => texture.parseBuffer(buffer)); 92 | 93 | const geometry = new Plane(gl); 94 | 95 | const program = new Program(gl, { 96 | vertex, 97 | fragment, 98 | uniforms: { 99 | tMap: { value: texture }, 100 | }, 101 | cullFace: null, 102 | }); 103 | 104 | const mesh = new Mesh(gl, { geometry, program }); 105 | mesh.setParent(scene); 106 | 107 | requestAnimationFrame(update); 108 | function update(t) { 109 | requestAnimationFrame(update); 110 | 111 | controls.update(); 112 | renderer.render({ scene, camera }); 113 | } 114 | -------------------------------------------------------------------------------- /examples/compute-vertex-normal/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /examples/compute-vertex-normal/index.ts: -------------------------------------------------------------------------------- 1 | import { Renderer, OGLRenderingContext, Geometry, Camera, Transform, Program, Mesh, Orbit, Torus } from '../../src'; 2 | 3 | const vertex = /* glsl */ ` 4 | precision highp float; 5 | precision highp int; 6 | 7 | attribute vec3 position; 8 | attribute vec3 normal; 9 | 10 | uniform mat4 modelViewMatrix; 11 | uniform mat4 projectionMatrix; 12 | uniform mat3 normalMatrix; 13 | 14 | varying vec3 vNormal; 15 | 16 | void main() { 17 | vNormal = normalize(normalMatrix * normal); 18 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); 19 | } 20 | `; 21 | 22 | const fragment = /* glsl */ ` 23 | precision highp float; 24 | precision highp int; 25 | varying vec3 vNormal; 26 | void main() { 27 | vec3 normal = normalize(vNormal); 28 | float lighting = dot(normal, normalize(vec3(-0.3, 0.8, 0.6))); 29 | gl_FragColor.rgb = vec3(0.2, 0.8, 1.0) + lighting * 0.1; 30 | gl_FragColor.a = 1.0; 31 | } 32 | `; 33 | 34 | let renderer: Renderer; 35 | let camera: Camera; 36 | let gl: OGLRenderingContext; 37 | let scene: Transform; 38 | let controls; 39 | 40 | initOGL(); 41 | addTrimesh(); 42 | animate(); 43 | 44 | function initOGL() { 45 | renderer = new Renderer({ dpr: 2 }); 46 | gl = renderer.gl; 47 | document.body.appendChild(gl.canvas); 48 | gl.clearColor(1, 1, 1, 1); 49 | camera = new Camera(gl); 50 | camera.perspective({ 51 | fov: 30, 52 | aspect: gl.drawingBufferWidth / gl.drawingBufferHeight, 53 | near: 1, 54 | far: 10000, 55 | }); 56 | camera.position.set(0, 3, 20); 57 | camera.up.set(0, 1, 0); 58 | camera.lookAt([0, 0, 0]); 59 | controls = new Orbit(camera); 60 | scene = new Transform(); 61 | } 62 | 63 | function addTrimesh() { 64 | const program = new Program(gl, { 65 | vertex, 66 | fragment, 67 | // Don't cull faces so that plane is double sided - default is gl.BACK 68 | cullFace: null, 69 | }); 70 | 71 | let triGeo = new Torus(gl, { 72 | radius: 1, 73 | tube: 0.3, 74 | radialSegments: 16, 75 | tubularSegments: 16, 76 | }); 77 | 78 | const trimesh = new Mesh(gl, { geometry: triGeo, program }); 79 | trimesh.setParent(scene); 80 | trimesh.position.x = -2; // left 81 | 82 | // copy Torus geometry without normal 83 | const vertices = triGeo.attributes.position.data; 84 | const indices = triGeo.attributes.index.data; 85 | let geometry = new Geometry(gl, { 86 | position: { size: 3, data: vertices }, 87 | index: { data: indices }, 88 | // normal: { size: 3, data: normals } 89 | }); 90 | 91 | // runtime compute normal attribute 92 | geometry.computeVertexNormals(); 93 | 94 | const trimesh2 = new Mesh(gl, { geometry, program }); 95 | trimesh2.setParent(scene); 96 | trimesh2.position.x = 2; // right 97 | } 98 | 99 | function animate(time?) { 100 | requestAnimationFrame(animate); 101 | controls.update(); 102 | renderer.render({ scene, camera }); 103 | } 104 | 105 | function resize() { 106 | renderer.setSize(window.innerWidth, window.innerHeight); 107 | camera.perspective({ aspect: gl.canvas.width / gl.canvas.height }); 108 | } 109 | window.addEventListener('resize', resize, false); 110 | resize(); 111 | 112 | document.title = 'OGL • Compute Vertex Normal'; 113 | -------------------------------------------------------------------------------- /examples/cube-map/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /examples/cube-map/index.ts: -------------------------------------------------------------------------------- 1 | import { Renderer, Camera, Transform, Texture, Program, Mesh, Orbit, Box } from '../../src'; 2 | 3 | const vertex = /* glsl */ ` 4 | attribute vec3 position; 5 | 6 | uniform mat4 modelViewMatrix; 7 | uniform mat4 projectionMatrix; 8 | 9 | varying vec3 vDir; 10 | 11 | void main() { 12 | vDir = normalize(position); 13 | 14 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); 15 | } 16 | `; 17 | 18 | const fragment = /* glsl */ ` 19 | precision highp float; 20 | 21 | // uniform type is samplerCube rather than sampler2D 22 | uniform samplerCube tMap; 23 | 24 | varying vec3 vDir; 25 | 26 | void main() { 27 | 28 | // sample function is textureCube rather than texture2D 29 | vec3 tex = textureCube(tMap, vDir).rgb; 30 | 31 | gl_FragColor.rgb = tex; 32 | gl_FragColor.a = 1.0; 33 | } 34 | `; 35 | 36 | const renderer = new Renderer({ dpr: 2 }); 37 | const gl = renderer.gl; 38 | document.body.appendChild(gl.canvas); 39 | gl.clearColor(1, 1, 1, 1); 40 | 41 | const camera = new Camera(gl, { fov: 45 }); 42 | camera.position.set(-2, 1, -3); 43 | 44 | const controls = new Orbit(camera, { ease: 1 }); 45 | 46 | function resize() { 47 | renderer.setSize(window.innerWidth, window.innerHeight); 48 | camera.perspective({ aspect: gl.canvas.width / gl.canvas.height }); 49 | } 50 | window.addEventListener('resize', resize, false); 51 | resize(); 52 | 53 | const scene = new Transform(); 54 | 55 | // Create an empty texture using the gl.TEXTURE_CUBE_MAP target 56 | const texture = new Texture(gl, { 57 | target: gl.TEXTURE_CUBE_MAP, 58 | }); 59 | 60 | loadImages(); 61 | async function loadImages() { 62 | function loadImage(src) { 63 | return new Promise((res) => { 64 | const img = new Image(); 65 | img.onload = () => res(img); 66 | img.src = src; 67 | }); 68 | } 69 | 70 | // Must be in this order (it's a WebGL thing) 71 | // gl.TEXTURE_CUBE_MAP_POSITIVE_X Right 72 | // gl.TEXTURE_CUBE_MAP_NEGATIVE_X Left 73 | // gl.TEXTURE_CUBE_MAP_POSITIVE_Y Top 74 | // gl.TEXTURE_CUBE_MAP_NEGATIVE_Y Bottom 75 | // gl.TEXTURE_CUBE_MAP_POSITIVE_Z Back 76 | // gl.TEXTURE_CUBE_MAP_NEGATIVE_Z Front 77 | 78 | const images = await Promise.all([ 79 | loadImage('../../assets/cube/posx.jpg'), 80 | loadImage('../../assets/cube/negx.jpg'), 81 | loadImage('../../assets/cube/posy.jpg'), 82 | loadImage('../../assets/cube/negy.jpg'), 83 | loadImage('../../assets/cube/posz.jpg'), 84 | loadImage('../../assets/cube/negz.jpg'), 85 | ]); 86 | 87 | // Once all are loaded, we can update the texture image, which will upload them 88 | texture.image = images; 89 | } 90 | 91 | const geometry = new Box(gl); 92 | 93 | const program = new Program(gl, { 94 | vertex, 95 | fragment, 96 | uniforms: { 97 | tMap: { value: texture }, 98 | }, 99 | cullFace: null, 100 | }); 101 | 102 | const skybox = new Mesh(gl, { geometry, program }); 103 | skybox.setParent(scene); 104 | skybox.scale.set(20); 105 | 106 | const mesh = new Mesh(gl, { geometry, program }); 107 | mesh.setParent(scene); 108 | 109 | requestAnimationFrame(update); 110 | function update(t) { 111 | requestAnimationFrame(update); 112 | 113 | controls.update(); 114 | 115 | mesh.rotation.y += 0.003; 116 | renderer.render({ scene, camera }); 117 | } 118 | 119 | document.getElementsByClassName('Info')[0].innerHTML = 'Cube Map. Texture by Humus'; 120 | document.title = 'OGL • Cube Map'; 121 | -------------------------------------------------------------------------------- /examples/curves/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /examples/curves/index.ts: -------------------------------------------------------------------------------- 1 | import { Renderer, Camera, Transform, Program, Mesh, Sphere, Polyline, Orbit, Vec3, Color, Curve } from '../../src'; 2 | 3 | const vertex = /* glsl */ ` 4 | precision highp float; 5 | precision highp int; 6 | 7 | attribute vec3 position; 8 | attribute vec3 normal; 9 | 10 | uniform mat3 normalMatrix; 11 | uniform mat4 modelViewMatrix; 12 | uniform mat4 projectionMatrix; 13 | 14 | varying vec3 vNormal; 15 | 16 | void main() { 17 | vNormal = normalize(normalMatrix * normal); 18 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); 19 | } 20 | `; 21 | 22 | const fragment = /* glsl */ ` 23 | precision highp float; 24 | precision highp int; 25 | 26 | varying vec3 vNormal; 27 | 28 | void main() { 29 | gl_FragColor.rgb = normalize(vNormal); 30 | gl_FragColor.a = 1.0; 31 | } 32 | `; 33 | 34 | const renderer = new Renderer({ dpr: 2 }); 35 | const gl = renderer.gl; 36 | document.body.appendChild(gl.canvas); 37 | gl.clearColor(1, 1, 1, 1); 38 | 39 | const camera = new Camera(gl, { fov: 35 }); 40 | camera.position.set(0, 0, 5); 41 | 42 | // Create controls and pass parameters 43 | const controls = new Orbit(camera, { 44 | target: new Vec3(0, 0, 0), 45 | }); 46 | 47 | function resize() { 48 | renderer.setSize(window.innerWidth, window.innerHeight); 49 | camera.perspective({ aspect: gl.canvas.width / gl.canvas.height }); 50 | } 51 | window.addEventListener('resize', resize, false); 52 | resize(); 53 | 54 | const scene = new Transform(); 55 | 56 | const sphereGeometry = new Sphere(gl); 57 | 58 | const program = new Program(gl, { 59 | vertex, 60 | fragment, 61 | 62 | // Don't cull faces so that plane is double sided - default is gl.BACK 63 | cullFace: null, 64 | }); 65 | 66 | const sphere = new Mesh(gl, { geometry: sphereGeometry, program }); 67 | sphere.setParent(scene); 68 | 69 | const curve = new Curve({ 70 | points: [new Vec3(0, 0.5, 0), new Vec3(0, 1, 1), new Vec3(0, -1, 1), new Vec3(0, -0.5, 0)], 71 | type: Curve.CUBICBEZIER, 72 | }); 73 | const points = curve.getPoints(20); 74 | 75 | curve.type = Curve.CATMULLROM; 76 | const points2 = curve.getPoints(20); 77 | 78 | curve.type = Curve.QUADRATICBEZIER; 79 | const points3 = curve.getPoints(20); 80 | 81 | const polyline = new Polyline(gl, { 82 | points, 83 | uniforms: { 84 | uColor: { value: new Color('#f00') }, 85 | uThickness: { value: 3 }, 86 | }, 87 | }); 88 | 89 | const polyline2 = new Polyline(gl, { 90 | points: points2, 91 | uniforms: { 92 | uColor: { value: new Color('#00f') }, 93 | uThickness: { value: 2 }, 94 | }, 95 | }); 96 | 97 | const polyline3 = new Polyline(gl, { 98 | points: points3, 99 | uniforms: { 100 | uColor: { value: new Color('#0f0') }, 101 | uThickness: { value: 4 }, 102 | }, 103 | }); 104 | 105 | for (let i = 0; i <= 60; i++) { 106 | const p = [polyline, polyline2, polyline3][i % 3]; 107 | const mesh = new Mesh(gl, { geometry: p.geometry, program: p.program }); 108 | mesh.setParent(sphere); 109 | mesh.rotation.y = (i * Math.PI) / 60; 110 | } 111 | 112 | requestAnimationFrame(update); 113 | function update() { 114 | requestAnimationFrame(update); 115 | sphere.rotation.y -= 0.01; 116 | controls.update(); 117 | renderer.render({ scene, camera }); 118 | } 119 | 120 | document.getElementsByClassName('Info')[0].innerHTML = 'Curves'; 121 | document.title = 'OGL • Curves'; 122 | -------------------------------------------------------------------------------- /examples/draw-modes/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /examples/draw-modes/index.ts: -------------------------------------------------------------------------------- 1 | import { Renderer, Camera, Transform, Geometry, Program, Mesh } from '../../src'; 2 | 3 | const vertex = /* glsl */ ` 4 | precision highp float; 5 | precision highp int; 6 | 7 | attribute vec2 uv; 8 | attribute vec3 position; 9 | 10 | uniform mat4 modelViewMatrix; 11 | uniform mat4 projectionMatrix; 12 | 13 | varying vec2 vUv; 14 | 15 | void main() { 16 | vUv = uv; 17 | 18 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); 19 | 20 | // gl_PointSize only applicable for gl.POINTS draw mode 21 | gl_PointSize = 5.0; 22 | } 23 | `; 24 | 25 | const fragment = /* glsl */ ` 26 | precision highp float; 27 | precision highp int; 28 | 29 | uniform float uTime; 30 | 31 | varying vec2 vUv; 32 | 33 | void main() { 34 | gl_FragColor.rgb = 0.5 + 0.3 * sin(vUv.yxx + uTime) + vec3(0.2, 0.0, 0.1); 35 | gl_FragColor.a = 1.0; 36 | } 37 | `; 38 | 39 | const renderer = new Renderer(); 40 | const gl = renderer.gl; 41 | document.body.appendChild(gl.canvas); 42 | gl.clearColor(1, 1, 1, 1); 43 | 44 | const camera = new Camera(gl, { fov: 15 }); 45 | camera.position.z = 15; 46 | 47 | function resize() { 48 | renderer.setSize(window.innerWidth, window.innerHeight); 49 | camera.perspective({ aspect: gl.canvas.width / gl.canvas.height }); 50 | } 51 | window.addEventListener('resize', resize, false); 52 | resize(); 53 | 54 | const scene = new Transform(); 55 | 56 | // Geometry is an indexed square, comprised of 4 vertices. 57 | const geometry = new Geometry(gl, { 58 | position: { size: 3, data: new Float32Array([-0.5, 0.5, 0, -0.5, -0.5, 0, 0.5, 0.5, 0, 0.5, -0.5, 0]) }, 59 | uv: { size: 2, data: new Float32Array([0, 1, 1, 1, 0, 0, 1, 0]) }, 60 | index: { data: new Uint16Array([0, 1, 2, 1, 3, 2]) }, 61 | }); 62 | 63 | const program = new Program(gl, { 64 | vertex, 65 | fragment, 66 | uniforms: { 67 | uTime: { value: 0 }, 68 | }, 69 | }); 70 | 71 | // gl.POINTS: draws 4 points (actually draws 6, with 2 duplicates due to the geometry indices) 72 | const points = new Mesh(gl, { mode: gl.POINTS, geometry, program }); 73 | points.setParent(scene); 74 | points.position.set(-1, 1, 0); 75 | 76 | // gl.LINES: draws 3 lines - a line between each pair of vertices. 77 | // Ideal use for separated lines. 78 | const lineStrip = new Mesh(gl, { mode: gl.LINES, geometry, program }); 79 | lineStrip.setParent(scene); 80 | lineStrip.position.set(1, 1, 0); 81 | 82 | // gl.LINE_LOOP: draws 6 lines (1 unavoidable overlap for squares). 83 | const lineLoop = new Mesh(gl, { mode: gl.LINE_LOOP, geometry, program }); 84 | lineLoop.setParent(scene); 85 | lineLoop.position.set(-1, -1, 0); 86 | 87 | // gl.TRIANGLES: draws a triangle between each set of 3 vertices. 88 | // Used as the default draw mode, so doesn't really need to be passed in as a param. 89 | const triangles = new Mesh(gl, { mode: gl.TRIANGLES, geometry, program }); 90 | triangles.setParent(scene); 91 | triangles.position.set(1, -1, 0); 92 | 93 | // OTHER MODES NOT FEATURED: 94 | // gl.LINE_STRIP: Draws a straight line to the next vertex. Does not connect first and last vertices like gl.LINE_LOOP, 95 | 96 | // gl.TRIANGLE_STRIP: draws triangles in a criss-cross pattern. Ideal for ribbons. 97 | // For example, in order to draw a rectangle, only 4 vertices needed (unlike 6 with gl.TRIANGLES). 98 | // The 4 vertices should follow the below pattern. 99 | // 100 | // 0--2 101 | // | /| 102 | // |/ | 103 | // 1--3 104 | 105 | // gl.TRIANGLE_FAN: draws triangles in a fan pattern. Ideal for small polygons. 106 | // For the rectangle example, similarly to gl.TRIANGLE_STRIP, only 4 vertices needed. However the 107 | // pattern differs as per below. 108 | // 109 | // 1--2 110 | // | /| 111 | // |/ | 112 | // 0--3 113 | 114 | requestAnimationFrame(update); 115 | function update(t) { 116 | requestAnimationFrame(update); 117 | 118 | program.uniforms.uTime.value = t * 0.001; 119 | renderer.render({ scene, camera }); 120 | } 121 | 122 | document.getElementsByClassName('Info')[0].innerHTML = 'Draw Modes'; 123 | document.title = 'OGL • Draw Modes'; 124 | -------------------------------------------------------------------------------- /examples/flat-shading-matcap/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /examples/fog/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /examples/fresnel/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /examples/fresnel/index.ts: -------------------------------------------------------------------------------- 1 | import { Renderer, Camera, Transform, Program, Mesh, Sphere, Color } from '../../src'; 2 | 3 | const params = { 4 | backgroundColor: new Color('#B6D8F2'), 5 | baseColor: new Color('#B6D8F2'), 6 | fresnelColor: new Color('#F7F6CF'), 7 | fresnelFactor: 1.5, 8 | }; 9 | 10 | { 11 | const renderer = new Renderer({ dpr: 2 }); 12 | const gl = renderer.gl; 13 | document.body.appendChild(gl.canvas); 14 | gl.clearColor(...params.backgroundColor, 1); 15 | 16 | const camera = new Camera(gl, { fov: 35 }); 17 | camera.position.set(0, 1, 7); 18 | camera.lookAt([0, 0, 0]); 19 | 20 | function resize() { 21 | renderer.setSize(window.innerWidth, window.innerHeight); 22 | camera.perspective({ aspect: gl.canvas.width / gl.canvas.height }); 23 | } 24 | window.addEventListener('resize', resize, false); 25 | resize(); 26 | 27 | const scene = new Transform(); 28 | 29 | const sphereGeometry = new Sphere(gl, { 30 | radius: 1, 31 | widthSegments: 128, 32 | }); 33 | 34 | const vertex = /* glsl */ ` 35 | attribute vec3 position; 36 | attribute vec2 uv; 37 | attribute vec3 normal; 38 | 39 | uniform mat4 modelViewMatrix; 40 | uniform mat4 projectionMatrix; 41 | uniform mat3 normalMatrix; 42 | 43 | varying vec2 vUv; 44 | varying vec3 vNormal; 45 | varying vec3 vPosition; 46 | 47 | void main() { 48 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); 49 | 50 | vPosition = position; 51 | vUv = uv; 52 | vNormal = normalMatrix * normal; 53 | } 54 | `; 55 | 56 | const fragment = /* glsl */ ` 57 | precision highp float; 58 | 59 | varying vec2 vUv; 60 | varying vec3 vNormal; 61 | varying vec3 vPosition; 62 | 63 | uniform vec3 fresnelColor; 64 | uniform vec3 baseColor; 65 | uniform float powerOfFactor; 66 | uniform vec3 cameraPosition; 67 | 68 | void main() { 69 | vec3 viewDirection = normalize(cameraPosition - vPosition.xyz); 70 | float fresnelFactor = dot(viewDirection, normalize(vNormal)); 71 | 72 | float inversefresnelFactor = clamp(1. - fresnelFactor, 0., 1.); 73 | 74 | // Shaping function 75 | fresnelFactor = pow(fresnelFactor, powerOfFactor); 76 | inversefresnelFactor = pow(inversefresnelFactor, powerOfFactor); 77 | 78 | gl_FragColor = vec4(fresnelFactor * baseColor + inversefresnelFactor * fresnelColor, 1.); 79 | } 80 | `; 81 | 82 | const uniforms = { 83 | fresnelColor: { value: params.fresnelColor }, 84 | baseColor: { value: params.baseColor }, 85 | powerOfFactor: { value: params.fresnelFactor }, 86 | }; 87 | 88 | const program = new Program(gl, { 89 | vertex: vertex, 90 | fragment: fragment, 91 | uniforms: uniforms, 92 | }); 93 | 94 | const sphere = new Mesh(gl, { geometry: sphereGeometry, program }); 95 | sphere.setParent(scene); 96 | 97 | requestAnimationFrame(update); 98 | function update() { 99 | requestAnimationFrame(update); 100 | sphere.rotation.x += 0.01; 101 | sphere.rotation.z += 0.02; 102 | 103 | renderer.render({ scene, camera }); 104 | } 105 | } 106 | 107 | document.getElementsByClassName('Info')[0].innerHTML = 'Simple Fresnel Shader'; 108 | document.title = 'Frustum Culling. Model by Google Poly'; 109 | -------------------------------------------------------------------------------- /examples/frustum-culling/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /examples/gpgpu-particles/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /examples/high-mesh-count/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 |
26 |
27 |
28 | 29 | 36 |
37 |
38 | 39 | 40 | -------------------------------------------------------------------------------- /examples/high-mesh-count/index.ts: -------------------------------------------------------------------------------- 1 | import { Renderer, Camera, Transform, Program, Mesh, Box } from '../../src'; 2 | 3 | const vertex = /* glsl */ ` 4 | precision highp float; 5 | precision highp int; 6 | 7 | attribute vec3 position; 8 | attribute vec3 normal; 9 | 10 | uniform mat4 modelViewMatrix; 11 | uniform mat4 projectionMatrix; 12 | uniform mat3 normalMatrix; 13 | 14 | varying vec3 vNormal; 15 | 16 | void main() { 17 | vNormal = normalize(normalMatrix * normal); 18 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); 19 | } 20 | `; 21 | 22 | const fragment = /* glsl */ ` 23 | precision highp float; 24 | precision highp int; 25 | 26 | varying vec3 vNormal; 27 | 28 | void main() { 29 | vec3 normal = normalize(vNormal); 30 | float lighting = dot(normal, normalize(vec3(-0.3, 0.8, 0.6))); 31 | gl_FragColor.rgb = vec3(0.2, 0.8, 1.0) + lighting * 0.1; 32 | gl_FragColor.a = 1.0; 33 | } 34 | `; 35 | 36 | const renderer = new Renderer({}); 37 | const gl = renderer.gl; 38 | document.body.appendChild(gl.canvas); 39 | gl.clearColor(1, 1, 1, 1); 40 | 41 | const camera = new Camera(gl, { fov: 35, far: 3000 }); 42 | 43 | function resize() { 44 | renderer.setSize(window.innerWidth, window.innerHeight); 45 | camera.perspective({ aspect: gl.canvas.width / gl.canvas.height }); 46 | } 47 | window.addEventListener('resize', resize, false); 48 | resize(); 49 | 50 | const scene = new Transform(); 51 | 52 | // Create base geometry 53 | const cubeGeometry = new Box(gl); 54 | 55 | // Using the shader from the base primitive example 56 | const program = new Program(gl, { 57 | vertex, 58 | fragment, 59 | }); 60 | 61 | // mesh container 62 | let meshes = []; 63 | 64 | function setMeshCount(count) { 65 | // sanitize input 66 | count = parseInt(count) || 1000; 67 | 68 | // remove old meshes 69 | for (let i = 0; i < meshes.length; ++i) scene.removeChild(meshes[i]); 70 | meshes = []; 71 | 72 | // create our meshes according to input 73 | for (let i = 0; i < count; ++i) { 74 | let mesh = new Mesh(gl, { geometry: cubeGeometry, program }); 75 | 76 | // position meshes in a random position between -100 / +100 in each dimension 77 | mesh.position.set(-100 + Math.random() * 200, -100 + Math.random() * 200, -100 + Math.random() * 200); 78 | mesh.rotation.set(Math.random() * 3, Math.random() * 3, Math.random() * 3); 79 | scene.addChild(mesh); 80 | meshes.push(mesh); 81 | } 82 | 83 | // set input counter value to make sure 84 | (document.getElementById('meshCountInput') as any).value = count; 85 | } 86 | 87 | (window as any).setMeshCount = setMeshCount; 88 | 89 | setMeshCount(1000); 90 | 91 | requestAnimationFrame(update); 92 | function update() { 93 | requestAnimationFrame(update); 94 | 95 | // rotate camera 96 | let time = performance.now() / 30000; 97 | camera.position.set(Math.sin(time) * 180, 80, Math.cos(time) * 180); 98 | camera.lookAt([0, 0, 0]); 99 | 100 | // rotate meshes 101 | for (let i = 0; i < meshes.length; ++i) { 102 | meshes[i].rotation.x += 0.01; 103 | meshes[i].rotation.y += 0.01; 104 | } 105 | 106 | renderer.render({ scene, camera }); 107 | } 108 | 109 | document.getElementsByClassName('Info')[0].innerHTML = 'Performance - High mesh count'; 110 | document.title = 'OGL • Performance - High mesh count'; 111 | -------------------------------------------------------------------------------- /examples/indexed-vs-non-indexed/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /examples/indexed-vs-non-indexed/index.ts: -------------------------------------------------------------------------------- 1 | import { Renderer, Camera, Transform, Geometry, Program, Mesh } from '../../src'; 2 | 3 | const vertex = /* glsl */ ` 4 | precision highp float; 5 | precision highp int; 6 | 7 | attribute vec2 uv; 8 | attribute vec3 position; 9 | 10 | uniform mat4 modelViewMatrix; 11 | uniform mat4 projectionMatrix; 12 | 13 | varying vec2 vUv; 14 | 15 | void main() { 16 | vUv = uv; 17 | 18 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); 19 | } 20 | `; 21 | 22 | const fragment = /* glsl */ ` 23 | precision highp float; 24 | precision highp int; 25 | 26 | uniform float uTime; 27 | 28 | varying vec2 vUv; 29 | 30 | void main() { 31 | gl_FragColor.rgb = 0.5 + 0.3 * sin(vUv.yxx + uTime) + vec3(0.2, 0.0, 0.1); 32 | gl_FragColor.a = 1.0; 33 | } 34 | `; 35 | 36 | const renderer = new Renderer(); 37 | const gl = renderer.gl; 38 | document.body.appendChild(gl.canvas); 39 | gl.clearColor(1, 1, 1, 1); 40 | 41 | const camera = new Camera(gl, { fov: 15 }); 42 | camera.position.z = 15; 43 | 44 | function resize() { 45 | renderer.setSize(window.innerWidth, window.innerHeight); 46 | camera.perspective({ aspect: gl.canvas.width / gl.canvas.height }); 47 | } 48 | window.addEventListener('resize', resize, false); 49 | resize(); 50 | 51 | const scene = new Transform(); 52 | 53 | // 4 vertices, using 6 indices to designate 2 triangles using the default gl.TRIANGLES draw mode 54 | const indexedGeometry = new Geometry(gl, { 55 | position: { 56 | size: 3, 57 | data: new Float32Array([ 58 | -0.5, 59 | 0.5, 60 | 0, // vertex 0 61 | -0.5, 62 | -0.5, 63 | 0, // vertex 1 64 | 0.5, 65 | 0.5, 66 | 0, // vertex 2 67 | 0.5, 68 | -0.5, 69 | 0, // vertex 3 70 | ]), 71 | }, 72 | uv: { 73 | size: 2, 74 | data: new Float32Array([ 75 | 0, 76 | 1, // vertex 0 77 | 1, 78 | 1, // vertex 1 79 | 0, 80 | 0, // vertex 2 81 | 1, 82 | 0, // vertex 3 83 | ]), 84 | }, 85 | 86 | // the indices attribute must use the name 'index' to be treated as an ELEMENT_ARRAY_BUFFER 87 | index: { data: new Uint16Array([0, 1, 2, 1, 3, 2]) }, 88 | }); 89 | 90 | // To recreate the same square using non-indexed geometry, 6 vertices need to be passed in, including 2 duplicates. 91 | // If only the original 4 vertices were supplied, only one triangle would be drawn. 92 | const nonIndexedGeometry = new Geometry(gl, { 93 | position: { 94 | size: 3, 95 | data: new Float32Array([ 96 | -0.5, 97 | 0.5, 98 | 0, // vertex 0 99 | -0.5, 100 | -0.5, 101 | 0, // vertex 1 102 | 0.5, 103 | 0.5, 104 | 0, // vertex 2 105 | -0.5, 106 | -0.5, 107 | 0, // vertex 1 108 | 0.5, 109 | -0.5, 110 | 0, // vertex 3 111 | 0.5, 112 | 0.5, 113 | 0, // vertex 2 114 | ]), 115 | }, 116 | uv: { 117 | size: 2, 118 | data: new Float32Array([ 119 | 0, 120 | 1, // vertex 0 121 | 1, 122 | 1, // vertex 1 123 | 0, 124 | 0, // vertex 2 125 | 1, 126 | 1, // vertex 1 127 | 1, 128 | 0, // vertex 3 129 | 0, 130 | 0, // vertex 2 131 | ]), 132 | }, 133 | }); 134 | 135 | const program = new Program(gl, { 136 | vertex, 137 | fragment, 138 | uniforms: { 139 | uTime: { value: 0 }, 140 | }, 141 | }); 142 | 143 | const indexedMesh = new Mesh(gl, { geometry: indexedGeometry, program }); 144 | indexedMesh.setParent(scene); 145 | indexedMesh.position.y = 0.9; 146 | 147 | const nonIndexedMesh = new Mesh(gl, { geometry: nonIndexedGeometry, program }); 148 | nonIndexedMesh.setParent(scene); 149 | nonIndexedMesh.position.y = -0.9; 150 | 151 | requestAnimationFrame(update); 152 | function update(t) { 153 | requestAnimationFrame(update); 154 | 155 | program.uniforms.uTime.value = t * 0.001; 156 | renderer.render({ scene, camera }); 157 | } 158 | 159 | document.getElementsByClassName('Info')[0].innerHTML = 'Indexed vs Non-Indexed'; 160 | document.title = 'OGL • Indexed vs Non-Indexed'; 161 | -------------------------------------------------------------------------------- /examples/instancing-gpu-picking/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /examples/instancing/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /examples/load-gltf/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /examples/load-json/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /examples/load-json/index.ts: -------------------------------------------------------------------------------- 1 | import { Renderer, Camera, Transform, Texture, Program, Geometry, Mesh } from '../../src'; 2 | 3 | const vertex = /* glsl */ ` 4 | precision highp float; 5 | precision highp int; 6 | 7 | attribute vec2 uv; 8 | attribute vec3 position; 9 | attribute vec3 normal; 10 | 11 | uniform mat4 modelViewMatrix; 12 | uniform mat4 projectionMatrix; 13 | uniform mat3 normalMatrix; 14 | 15 | varying vec2 vUv; 16 | varying vec3 vNormal; 17 | 18 | void main() { 19 | vUv = uv; 20 | vNormal = normalize(normalMatrix * normal); 21 | 22 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); 23 | } 24 | `; 25 | 26 | const fragment = /* glsl */ ` 27 | precision highp float; 28 | precision highp int; 29 | 30 | uniform float uTime; 31 | uniform sampler2D tMap; 32 | 33 | varying vec2 vUv; 34 | varying vec3 vNormal; 35 | 36 | void main() { 37 | vec3 normal = normalize(vNormal); 38 | vec3 tex = texture2D(tMap, vUv).rgb; 39 | 40 | vec3 light = normalize(vec3(0.5, 1.0, -0.3)); 41 | float shading = dot(normal, light) * 0.15; 42 | gl_FragColor.rgb = tex + shading; 43 | gl_FragColor.a = 1.0; 44 | } 45 | `; 46 | 47 | const renderer = new Renderer({ dpr: 2 }); 48 | const gl = renderer.gl; 49 | document.body.appendChild(gl.canvas); 50 | gl.clearColor(1, 1, 1, 1); 51 | 52 | const camera = new Camera(gl, { fov: 35 }); 53 | camera.position.set(8, 5, 15); 54 | camera.lookAt([0, 1.5, 0]); 55 | 56 | function resize() { 57 | renderer.setSize(window.innerWidth, window.innerHeight); 58 | camera.perspective({ aspect: gl.canvas.width / gl.canvas.height }); 59 | } 60 | window.addEventListener('resize', resize, false); 61 | resize(); 62 | 63 | const scene = new Transform(); 64 | 65 | const texture = new Texture(gl); 66 | const img = new Image(); 67 | img.onload = () => (texture.image = img); 68 | img.src = '../../assets/fox.jpg'; 69 | 70 | const program = new Program(gl, { 71 | vertex, 72 | fragment, 73 | uniforms: { 74 | tMap: { value: texture }, 75 | }, 76 | }); 77 | 78 | let mesh; 79 | loadModel(); 80 | async function loadModel() { 81 | const data = await (await fetch(`../../assets/fox.json`)).json(); 82 | 83 | const geometry = new Geometry(gl, { 84 | position: { size: 3, data: new Float32Array(data.position) }, 85 | uv: { size: 2, data: new Float32Array(data.uv) }, 86 | normal: { size: 3, data: new Float32Array(data.normal) }, 87 | }); 88 | 89 | mesh = new Mesh(gl, { geometry, program }); 90 | mesh.setParent(scene); 91 | } 92 | 93 | requestAnimationFrame(update); 94 | function update() { 95 | requestAnimationFrame(update); 96 | 97 | if (mesh) mesh.rotation.y -= 0.005; 98 | renderer.render({ scene, camera }); 99 | } 100 | 101 | document.getElementsByClassName('Info')[0].innerHTML = 'Load JSON (Javascript Object Notation). Model by Google Poly'; 102 | document.title = 'OGL • Load JSON'; 103 | -------------------------------------------------------------------------------- /examples/mouse-flowmap/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /examples/mrt/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /examples/msdf-text/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /examples/normal-maps/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /examples/orbit-controls/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /examples/orbit-controls/index.ts: -------------------------------------------------------------------------------- 1 | import { Renderer, Camera, Transform, Texture, Program, Geometry, Mesh, Vec3, Orbit } from '../../src'; 2 | 3 | const vertex = /* glsl */ ` 4 | precision highp float; 5 | precision highp int; 6 | 7 | attribute vec2 uv; 8 | attribute vec3 position; 9 | attribute vec3 normal; 10 | 11 | uniform mat4 modelViewMatrix; 12 | uniform mat4 projectionMatrix; 13 | uniform mat3 normalMatrix; 14 | 15 | varying vec2 vUv; 16 | varying vec3 vNormal; 17 | 18 | void main() { 19 | vUv = uv; 20 | vNormal = normalize(normalMatrix * normal); 21 | 22 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); 23 | } 24 | `; 25 | 26 | const fragment = /* glsl */ ` 27 | precision highp float; 28 | precision highp int; 29 | 30 | uniform float uTime; 31 | uniform sampler2D tMap; 32 | 33 | varying vec2 vUv; 34 | varying vec3 vNormal; 35 | 36 | void main() { 37 | vec3 normal = normalize(vNormal); 38 | vec3 tex = texture2D(tMap, vUv).rgb; 39 | 40 | vec3 light = normalize(vec3(0.5, 1.0, -0.3)); 41 | float shading = dot(normal, light) * 0.15; 42 | gl_FragColor.rgb = tex + shading; 43 | gl_FragColor.a = 1.0; 44 | } 45 | `; 46 | 47 | const renderer = new Renderer({ dpr: 2 }); 48 | const gl = renderer.gl; 49 | document.body.appendChild(gl.canvas); 50 | gl.clearColor(1, 1, 1, 1); 51 | 52 | const camera = new Camera(gl, { fov: 45 }); 53 | camera.position.set(-2, 1, 2); 54 | 55 | // Create controls and pass parameters 56 | const controls = new Orbit(camera, { 57 | target: new Vec3(0, 0.7, 0), 58 | }); 59 | 60 | function resize() { 61 | renderer.setSize(window.innerWidth, window.innerHeight); 62 | camera.perspective({ aspect: gl.canvas.width / gl.canvas.height }); 63 | } 64 | window.addEventListener('resize', resize, false); 65 | resize(); 66 | 67 | const scene = new Transform(); 68 | 69 | const texture = new Texture(gl); 70 | const img = new Image(); 71 | img.onload = () => (texture.image = img); 72 | img.src = '../../assets/macaw.jpg'; 73 | 74 | const program = new Program(gl, { 75 | vertex, 76 | fragment, 77 | uniforms: { 78 | tMap: { value: texture }, 79 | }, 80 | cullFace: null, 81 | }); 82 | 83 | loadModel(); 84 | async function loadModel() { 85 | const data = await (await fetch(`../../assets/macaw.json`)).json(); 86 | 87 | const geometry = new Geometry(gl, { 88 | position: { size: 3, data: new Float32Array(data.position) }, 89 | uv: { size: 2, data: new Float32Array(data.uv) }, 90 | normal: { size: 3, data: new Float32Array(data.normal) }, 91 | }); 92 | 93 | let mesh = new Mesh(gl, { geometry, program }); 94 | mesh.setParent(scene); 95 | } 96 | 97 | requestAnimationFrame(update); 98 | function update() { 99 | requestAnimationFrame(update); 100 | 101 | // Need to update controls every frame 102 | controls.update(); 103 | renderer.render({ scene, camera }); 104 | } 105 | 106 | document.getElementsByClassName('Info')[0].innerHTML = 'Orbit Controls. Model by Google Poly.'; 107 | document.title = 'OGL • Orbit Controls'; 108 | -------------------------------------------------------------------------------- /examples/particles/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /examples/particles/index.ts: -------------------------------------------------------------------------------- 1 | import { Renderer, Camera, Geometry, Program, Mesh } from '../../src'; 2 | 3 | const vertex = /* glsl */ ` 4 | precision highp float; 5 | precision highp int; 6 | 7 | attribute vec3 position; 8 | attribute vec4 random; 9 | 10 | uniform mat4 modelMatrix; 11 | uniform mat4 viewMatrix; 12 | uniform mat4 projectionMatrix; 13 | uniform float uTime; 14 | 15 | varying vec4 vRandom; 16 | 17 | void main() { 18 | vRandom = random; 19 | 20 | // positions are 0->1, so make -1->1 21 | vec3 pos = position * 2.0 - 1.0; 22 | 23 | // Scale towards camera to be more interesting 24 | pos.z *= 10.0; 25 | 26 | // modelMatrix is one of the automatically attached uniforms when using the Mesh class 27 | vec4 mPos = modelMatrix * vec4(pos, 1.0); 28 | 29 | // add some movement in world space 30 | float t = uTime * 0.6; 31 | mPos.x += sin(t * random.z + 6.28 * random.w) * mix(0.1, 1.5, random.x); 32 | mPos.y += sin(t * random.y + 6.28 * random.x) * mix(0.1, 1.5, random.w); 33 | mPos.z += sin(t * random.w + 6.28 * random.y) * mix(0.1, 1.5, random.z); 34 | 35 | // get the model view position so that we can scale the points off into the distance 36 | vec4 mvPos = viewMatrix * mPos; 37 | gl_PointSize = 300.0 / length(mvPos.xyz) * (random.x + 0.1); 38 | gl_Position = projectionMatrix * mvPos; 39 | } 40 | `; 41 | 42 | const fragment = /* glsl */ ` 43 | precision highp float; 44 | precision highp int; 45 | 46 | uniform float uTime; 47 | 48 | varying vec4 vRandom; 49 | 50 | void main() { 51 | vec2 uv = gl_PointCoord.xy; 52 | 53 | float circle = smoothstep(0.5, 0.4, length(uv - 0.5)) * 0.8; 54 | 55 | gl_FragColor.rgb = 0.8 + 0.2 * sin(uv.yxx + uTime + vRandom.y * 6.28) + vec3(0.1, 0.0, 0.3); 56 | gl_FragColor.a = circle; 57 | } 58 | `; 59 | 60 | const renderer = new Renderer({ depth: false }); 61 | const gl = renderer.gl; 62 | document.body.appendChild(gl.canvas); 63 | gl.clearColor(1, 1, 1, 1); 64 | 65 | const camera = new Camera(gl, { fov: 15 }); 66 | camera.position.z = 15; 67 | 68 | function resize() { 69 | renderer.setSize(window.innerWidth, window.innerHeight); 70 | camera.perspective({ aspect: gl.canvas.width / gl.canvas.height }); 71 | } 72 | window.addEventListener('resize', resize, false); 73 | resize(); 74 | 75 | const num = 100; 76 | const position = new Float32Array(num * 3); 77 | const random = new Float32Array(num * 4); 78 | 79 | for (let i = 0; i < num; i++) { 80 | position.set([Math.random(), Math.random(), Math.random()], i * 3); 81 | random.set([Math.random(), Math.random(), Math.random(), Math.random()], i * 4); 82 | } 83 | 84 | const geometry = new Geometry(gl, { 85 | position: { size: 3, data: position }, 86 | random: { size: 4, data: random }, 87 | }); 88 | 89 | const program = new Program(gl, { 90 | vertex, 91 | fragment, 92 | uniforms: { 93 | uTime: { value: 0 }, 94 | }, 95 | transparent: true, 96 | depthTest: false, 97 | }); 98 | 99 | // Make sure mode is gl.POINTS 100 | const particles = new Mesh(gl, { mode: gl.POINTS, geometry, program }); 101 | 102 | requestAnimationFrame(update); 103 | function update(t) { 104 | requestAnimationFrame(update); 105 | 106 | // add some slight overall movement to be more interesting 107 | particles.rotation.x = Math.sin(t * 0.0002) * 0.1; 108 | particles.rotation.y = Math.cos(t * 0.0005) * 0.15; 109 | particles.rotation.z += 0.01; 110 | 111 | program.uniforms.uTime.value = t * 0.001; 112 | renderer.render({ scene: particles, camera }); 113 | } 114 | 115 | document.getElementsByClassName('Info')[0].innerHTML = 'Particles'; 116 | document.title = 'OGL • Particles'; 117 | -------------------------------------------------------------------------------- /examples/pbr/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /examples/point-lighting/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /examples/polylines/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /examples/post-bloom/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /examples/post-fluid-distortion/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /examples/post-fxaa/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /examples/post-fxaa/index.ts: -------------------------------------------------------------------------------- 1 | import { Renderer, Camera, Program, Mesh, Vec2, Post, Box, NormalProgram } from '../../src'; 2 | 3 | const fragment = /* glsl */ ` 4 | precision highp float; 5 | 6 | // Default uniform for previous pass is 'tMap'. 7 | // Can change this using the 'textureUniform' property 8 | // when adding a pass. 9 | uniform sampler2D tMap; 10 | 11 | uniform vec2 uResolution; 12 | 13 | varying vec2 vUv; 14 | 15 | vec4 fxaa(sampler2D tex, vec2 uv, vec2 resolution) { 16 | vec2 pixel = vec2(1) / resolution; 17 | 18 | vec3 l = vec3(0.299, 0.587, 0.114); 19 | float lNW = dot(texture2D(tex, uv + vec2(-1, -1) * pixel).rgb, l); 20 | float lNE = dot(texture2D(tex, uv + vec2( 1, -1) * pixel).rgb, l); 21 | float lSW = dot(texture2D(tex, uv + vec2(-1, 1) * pixel).rgb, l); 22 | float lSE = dot(texture2D(tex, uv + vec2( 1, 1) * pixel).rgb, l); 23 | float lM = dot(texture2D(tex, uv).rgb, l); 24 | float lMin = min(lM, min(min(lNW, lNE), min(lSW, lSE))); 25 | float lMax = max(lM, max(max(lNW, lNE), max(lSW, lSE))); 26 | 27 | vec2 dir = vec2( 28 | -((lNW + lNE) - (lSW + lSE)), 29 | ((lNW + lSW) - (lNE + lSE)) 30 | ); 31 | 32 | float dirReduce = max((lNW + lNE + lSW + lSE) * 0.03125, 0.0078125); 33 | float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce); 34 | dir = min(vec2(8, 8), max(vec2(-8, -8), dir * rcpDirMin)) * pixel; 35 | 36 | vec3 rgbA = 0.5 * ( 37 | texture2D(tex, uv + dir * (1.0 / 3.0 - 0.5)).rgb + 38 | texture2D(tex, uv + dir * (2.0 / 3.0 - 0.5)).rgb); 39 | 40 | vec3 rgbB = rgbA * 0.5 + 0.25 * ( 41 | texture2D(tex, uv + dir * -0.5).rgb + 42 | texture2D(tex, uv + dir * 0.5).rgb); 43 | 44 | float lB = dot(rgbB, l); 45 | 46 | return mix( 47 | vec4(rgbB, 1), 48 | vec4(rgbA, 1), 49 | max(sign(lB - lMin), 0.0) * max(sign(lB - lMax), 0.0) 50 | ); 51 | } 52 | 53 | void main() { 54 | vec4 raw = texture2D(tMap, vUv); 55 | vec4 aa = fxaa(tMap, vUv, uResolution); 56 | 57 | // Split screen in half to show side-by-side comparison 58 | gl_FragColor = mix(raw, aa, step(0.5, vUv.x)); 59 | 60 | // Darken left side a tad for clarity 61 | gl_FragColor -= step(vUv.x, 0.5) * 0.1; 62 | } 63 | `; 64 | 65 | const renderer = new Renderer({ dpr: 1 }); 66 | const gl = renderer.gl; 67 | document.body.appendChild(gl.canvas); 68 | gl.clearColor(1, 1, 1, 1); 69 | 70 | const camera = new Camera(gl, { fov: 35 }); 71 | camera.position.set(0, 1, 5); 72 | camera.lookAt([0, 0, 0]); 73 | 74 | // Post copies the current renderer values (width, height, dpr) if none are passed in 75 | const post = new Post(gl); 76 | 77 | // Create uniform for pass 78 | const resolution = { value: new Vec2() }; 79 | 80 | function resize() { 81 | renderer.setSize(window.innerWidth, window.innerHeight); 82 | camera.perspective({ aspect: gl.canvas.width / gl.canvas.height }); 83 | 84 | // Need to resize post as render targets need to be re-created 85 | post.resize(); 86 | resolution.value.set(gl.canvas.width, gl.canvas.height); 87 | } 88 | window.addEventListener('resize', resize, false); 89 | resize(); 90 | 91 | const geometry = new Box(gl); 92 | const mesh = new Mesh(gl, { geometry, program: NormalProgram(gl) }); 93 | 94 | // Add pass like you're creating a Program. Then use the 'enabled' 95 | // property to toggle the pass. 96 | const pass = post.addPass({ 97 | // If not passed in, pass will use the default vertex/fragment 98 | // shaders found within the class. 99 | fragment, 100 | uniforms: { 101 | uResolution: resolution, 102 | }, 103 | }); 104 | 105 | requestAnimationFrame(update); 106 | function update() { 107 | requestAnimationFrame(update); 108 | 109 | mesh.rotation.y -= 0.005; 110 | mesh.rotation.x -= 0.01; 111 | 112 | // Replace Renderer.render with post.render. Use the same arguments. 113 | post.render({ scene: mesh, camera }); 114 | } 115 | 116 | document.getElementsByClassName('Info')[0].innerHTML = 'Post FXAA (Fast Approximate Anti-Aliasing)'; 117 | document.title = 'OGL • Post FXAA (Fast Approximate Anti-Aliasing)'; 118 | -------------------------------------------------------------------------------- /examples/raycasting/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /examples/render-to-texture/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /examples/render-to-texture/index.ts: -------------------------------------------------------------------------------- 1 | import { Renderer, Camera, Transform, Texture, RenderTarget, Program, Mesh, Box } from '../../src'; 2 | 3 | const vertex = /* glsl */ ` 4 | precision highp float; 5 | precision highp int; 6 | 7 | attribute vec3 position; 8 | attribute vec3 normal; 9 | attribute vec2 uv; 10 | 11 | uniform mat4 modelViewMatrix; 12 | uniform mat4 projectionMatrix; 13 | uniform mat3 normalMatrix; 14 | 15 | varying vec2 vUv; 16 | varying vec3 vNormal; 17 | 18 | void main() { 19 | vUv = uv; 20 | vNormal = normalize(normalMatrix * normal); 21 | 22 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); 23 | } 24 | `; 25 | 26 | const fragment = /* glsl */ ` 27 | precision highp float; 28 | precision highp int; 29 | 30 | uniform sampler2D tMap; 31 | 32 | varying vec2 vUv; 33 | varying vec3 vNormal; 34 | 35 | void main() { 36 | vec3 normal = normalize(vNormal); 37 | float lighting = 0.2 * dot(normal, normalize(vec3(-0.3, 0.8, 0.6))); 38 | vec3 tex = texture2D(tMap, vUv).rgb; 39 | gl_FragColor.rgb = tex + lighting + vec3(vUv - 0.5, 0.0) * 0.1; 40 | gl_FragColor.a = 1.0; 41 | } 42 | `; 43 | 44 | const renderer = new Renderer({ dpr: 2 }); 45 | const gl = renderer.gl; 46 | document.body.appendChild(gl.canvas); 47 | 48 | const camera = new Camera(gl, { fov: 35 }); 49 | camera.position.set(0, 1, 5); 50 | camera.lookAt([0, 0, 0]); 51 | 52 | const targetCamera = new Camera(gl, { fov: 35 }); 53 | targetCamera.position.set(0, 1, 5); 54 | targetCamera.lookAt([0, 0, 0]); 55 | 56 | function resize() { 57 | renderer.setSize(window.innerWidth, window.innerHeight); 58 | 59 | // Only update aspect of target camera, as first scene will be drawn to a square render target 60 | targetCamera.perspective({ aspect: gl.canvas.width / gl.canvas.height }); 61 | } 62 | window.addEventListener('resize', resize, false); 63 | resize(); 64 | 65 | const geometry = new Box(gl); 66 | 67 | // A little data texture with 4 colors just to keep things interesting 68 | const texture = new Texture(gl, { 69 | image: new Uint8Array([191, 25, 54, 255, 96, 18, 54, 255, 96, 18, 54, 255, 37, 13, 53, 255]), 70 | width: 2, 71 | height: 2, 72 | magFilter: gl.NEAREST, 73 | }); 74 | 75 | const program = new Program(gl, { 76 | vertex, 77 | fragment, 78 | uniforms: { 79 | tMap: { value: texture }, 80 | }, 81 | }); 82 | 83 | // Create render target framebuffer. 84 | // Uses canvas size by default and doesn't automatically resize. 85 | // To resize, re-create target 86 | const target = new RenderTarget(gl, { 87 | width: 512, 88 | height: 512, 89 | }); 90 | 91 | const targetProgram = new Program(gl, { 92 | vertex, 93 | fragment, 94 | uniforms: { 95 | tMap: { value: target.texture }, 96 | }, 97 | }); 98 | 99 | const mesh = new Mesh(gl, { geometry, program }); 100 | const targetMesh = new Mesh(gl, { geometry, program: targetProgram }); 101 | 102 | requestAnimationFrame(update); 103 | function update() { 104 | requestAnimationFrame(update); 105 | 106 | mesh.rotation.y -= 0.02; 107 | targetMesh.rotation.y -= 0.005; 108 | targetMesh.rotation.x -= 0.01; 109 | 110 | // Set background for first render to target 111 | gl.clearColor(0.15, 0.05, 0.2, 1); 112 | 113 | // Add target property to render call 114 | renderer.render({ scene: mesh, camera, target }); 115 | 116 | // Change to final background 117 | gl.clearColor(1, 1, 1, 1); 118 | 119 | // Omit target to render to canvas 120 | renderer.render({ scene: targetMesh, camera: targetCamera }); 121 | } 122 | 123 | document.getElementsByClassName('Info')[0].innerHTML = 'Render to texture'; 124 | document.title = 'OGL • Render to texture'; 125 | -------------------------------------------------------------------------------- /examples/scene-graph/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /examples/scene-graph/index.ts: -------------------------------------------------------------------------------- 1 | import { Renderer, Camera, Program, Transform, Mesh, Box, Sphere } from '../../src'; 2 | 3 | type SpeedMesh = { speed?: number } & Mesh; 4 | 5 | const vertex = /* glsl */ ` 6 | precision highp float; 7 | precision highp int; 8 | 9 | attribute vec3 position; 10 | attribute vec3 normal; 11 | 12 | uniform mat4 modelViewMatrix; 13 | uniform mat4 projectionMatrix; 14 | uniform mat3 normalMatrix; 15 | 16 | varying vec3 vNormal; 17 | varying vec4 vMVPos; 18 | 19 | void main() { 20 | vNormal = normalize(normalMatrix * normal); 21 | 22 | vMVPos = modelViewMatrix * vec4(position, 1.0); 23 | gl_Position = projectionMatrix * vMVPos; 24 | } 25 | `; 26 | 27 | const fragment = /* glsl */ ` 28 | precision highp float; 29 | precision highp int; 30 | 31 | varying vec3 vNormal; 32 | varying vec4 vMVPos; 33 | 34 | void main() { 35 | vec3 normal = normalize(vNormal); 36 | float lighting = dot(normal, normalize(vec3(-0.3, 0.8, 0.6))); 37 | vec3 color = vec3(1.0, 0.5, 0.2) * (1.0 - 0.5 * lighting) + vMVPos.xzy * 0.1; 38 | 39 | float dist = length(vMVPos); 40 | float fog = smoothstep(4.0, 10.0, dist); 41 | color = mix(color, vec3(1.0), fog); 42 | 43 | gl_FragColor.rgb = color; 44 | gl_FragColor.a = 1.0; 45 | } 46 | `; 47 | 48 | const renderer = new Renderer({ dpr: 2 }); 49 | const gl = renderer.gl; 50 | document.body.appendChild(gl.canvas); 51 | gl.clearColor(1, 1, 1, 1); 52 | 53 | // The Camera class extends Transform. See Below for more on Transform. 54 | const camera = new Camera(gl, { fov: 35 }); 55 | camera.position.set(0, 1, 7); 56 | camera.lookAt([0, 0, 0]); 57 | 58 | function resize() { 59 | renderer.setSize(window.innerWidth, window.innerHeight); 60 | camera.perspective({ aspect: gl.canvas.width / gl.canvas.height }); 61 | } 62 | window.addEventListener('resize', resize, false); 63 | resize(); 64 | 65 | const sphereGeometry = new Sphere(gl, { radius: 0.15 }); 66 | const cubeGeometry = new Box(gl, { width: 0.3, height: 0.3, depth: 0.3 }); 67 | 68 | const program = new Program(gl, { 69 | vertex, 70 | fragment, 71 | }); 72 | 73 | // The scene hierarchy is controlled by the Transform class 74 | // To create scenes, groups, null pointers etc, use Transform 75 | const scene = new Transform(); 76 | 77 | // The Mesh class extends the Transform class, and so shares the scene graph functionality 78 | const sphere: SpeedMesh = new Mesh(gl, { geometry: sphereGeometry, program }) as SpeedMesh; 79 | sphere.speed = -0.5; 80 | 81 | // Use .setParent to add transform as a child of another transform 82 | sphere.setParent(scene); 83 | // scene.addChild(sphere); // also works 84 | 85 | const shapes = [sphere]; 86 | 87 | // Create random array of shapes 88 | for (let i = 0; i < 50; i++) { 89 | const geometry = Math.random() > 0.5 ? cubeGeometry : sphereGeometry; 90 | const shape: SpeedMesh = new Mesh(gl, { geometry, program }); 91 | shape.scale.set(Math.random() * 0.3 + 0.7); 92 | shape.position.set((Math.random() - 0.5) * 3, (Math.random() - 0.5) * 3, (Math.random() - 0.5) * 3); 93 | shape.speed = (Math.random() - 0.5) * 0.7; 94 | 95 | // Attach them to a random, previously created shape 96 | shape.setParent(shapes[Math.floor(Math.random() * shapes.length)]); 97 | shapes.push(shape); 98 | } 99 | 100 | requestAnimationFrame(update); 101 | function update(t) { 102 | requestAnimationFrame(update); 103 | 104 | shapes.forEach((shape) => { 105 | shape.rotation.y += 0.03 * shape.speed; 106 | shape.rotation.x += 0.04 * shape.speed; 107 | shape.rotation.z += 0.01 * shape.speed; 108 | }); 109 | 110 | // The 'scene' property in the render call expects any Transform. 111 | // Therefore can be a Mesh instance as well. 112 | renderer.render({ scene, camera }); 113 | } 114 | 115 | document.getElementsByClassName('Info')[0].innerHTML = 'Scene Graph Hierarchy'; 116 | document.title = 'OGL • Scene Graph Hierarchy'; 117 | -------------------------------------------------------------------------------- /examples/shadow-maps/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /examples/skinning/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /examples/skydome/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /examples/skydome/index.ts: -------------------------------------------------------------------------------- 1 | import { Renderer, Geometry, Program, Mesh, Camera, Transform, Texture } from '../../src'; 2 | import { Sphere, Orbit } from '../../src'; 3 | 4 | const vertex = /* glsl */ ` 5 | precision highp float; 6 | precision highp int; 7 | 8 | attribute vec2 uv; 9 | attribute vec3 position; 10 | attribute vec3 normal; 11 | 12 | uniform mat4 modelViewMatrix; 13 | uniform mat4 projectionMatrix; 14 | 15 | varying vec2 vUv; 16 | 17 | void main() { 18 | vUv = uv; 19 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); 20 | } 21 | `; 22 | 23 | const fragment = /* glsl */ ` 24 | precision highp float; 25 | precision highp int; 26 | 27 | uniform sampler2D tMap; 28 | 29 | varying vec2 vUv; 30 | 31 | void main() { 32 | vec3 tex = texture2D(tMap, vUv).rgb; 33 | 34 | gl_FragColor.rgb = tex; 35 | gl_FragColor.a = 1.0; 36 | } 37 | `; 38 | 39 | const renderer = new Renderer({ dpr: 2 }); 40 | const gl = renderer.gl; 41 | document.body.appendChild(gl.canvas); 42 | gl.clearColor(1, 1, 1, 1); 43 | 44 | const camera = new Camera(gl, { fov: 45 }); 45 | camera.position.set(0, 0, 8); 46 | 47 | const controls = new Orbit(camera, { 48 | enablePan: false, 49 | enableZoom: false, 50 | }); 51 | 52 | function resize() { 53 | renderer.setSize(window.innerWidth, window.innerHeight); 54 | camera.perspective({ aspect: gl.canvas.width / gl.canvas.height }); 55 | } 56 | window.addEventListener('resize', resize, false); 57 | resize(); 58 | 59 | const scene = new Transform(); 60 | 61 | // Texture is equirectangular 62 | const texture = new Texture(gl); 63 | const img = new Image(); 64 | img.onload = () => (texture.image = img); 65 | img.src = '../../assets/sky.jpg'; 66 | 67 | // Use Sphere geometry to render equirectangular textures 68 | const geometry = new Sphere(gl, { radius: 1, widthSegments: 64 }); 69 | 70 | const program = new Program(gl, { 71 | vertex, 72 | fragment, 73 | uniforms: { 74 | tMap: { value: texture }, 75 | }, 76 | 77 | // Need inside of sphere to be visible 78 | cullFace: null, 79 | }); 80 | 81 | // A smaller sphere in the center just to help illustrate 82 | const mesh = new Mesh(gl, { geometry, program }); 83 | mesh.setParent(scene); 84 | 85 | // Camera will dwell inside skybox 86 | const skybox = new Mesh(gl, { geometry, program }); 87 | skybox.scale.set(10); 88 | skybox.setParent(scene); 89 | 90 | requestAnimationFrame(update); 91 | function update() { 92 | requestAnimationFrame(update); 93 | 94 | controls.update(); 95 | renderer.render({ scene, camera }); 96 | } 97 | 98 | document.getElementsByClassName('Info')[0].innerHTML = 99 | 'Skydome. Image credit charlesashaw.'; 100 | document.title = 'OGL • Skydome'; 101 | -------------------------------------------------------------------------------- /examples/sort-transparency/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /examples/sort-transparency/index.ts: -------------------------------------------------------------------------------- 1 | import { Renderer, Camera, Transform, Texture, Program, Mesh, Color, Plane } from '../../src'; 2 | 3 | type SpeedMesh = Mesh & { speed?: number }; 4 | 5 | const vertex = /* glsl */ ` 6 | precision highp float; 7 | precision highp int; 8 | 9 | attribute vec2 uv; 10 | attribute vec3 position; 11 | 12 | uniform mat4 modelViewMatrix; 13 | uniform mat4 projectionMatrix; 14 | 15 | varying vec2 vUv; 16 | varying vec4 vMVPos; 17 | 18 | void main() { 19 | vUv = uv; 20 | vec3 pos = position; 21 | float dist = pow(length(vUv - 0.5), 2.0) - 0.25; 22 | pos.z += dist * 0.5; 23 | vMVPos = modelViewMatrix * vec4(pos, 1.0); 24 | gl_Position = projectionMatrix * vMVPos; 25 | } 26 | `; 27 | 28 | const fragment = /* glsl */ ` 29 | precision highp float; 30 | precision highp int; 31 | 32 | uniform sampler2D tMap; 33 | uniform vec3 uColor; 34 | 35 | varying vec2 vUv; 36 | varying vec4 vMVPos; 37 | 38 | void main() { 39 | float alpha = texture2D(tMap, vUv).g; 40 | 41 | vec3 color = uColor + vMVPos.xzy * 0.05; 42 | 43 | float dist = length(vMVPos); 44 | float fog = smoothstep(5.0, 10.0, dist); 45 | color = mix(color, vec3(1.0), fog); 46 | 47 | gl_FragColor.rgb = color; 48 | gl_FragColor.a = alpha; 49 | if (alpha < 0.01) discard; 50 | } 51 | `; 52 | 53 | // This demonstrates the default geometry sorting before rendering. 54 | // It does not include sorting between faces/points within a single geometry. 55 | 56 | const renderer = new Renderer({ dpr: 2 }); 57 | const gl = renderer.gl; 58 | document.body.appendChild(gl.canvas); 59 | gl.clearColor(1, 1, 1, 1); 60 | 61 | const camera = new Camera(gl, { fov: 35 }); 62 | camera.position.set(0, 0, 7); 63 | camera.rotation.z = -0.3; 64 | 65 | function resize() { 66 | renderer.setSize(window.innerWidth, window.innerHeight); 67 | camera.perspective({ aspect: gl.canvas.width / gl.canvas.height }); 68 | } 69 | window.addEventListener('resize', resize, false); 70 | resize(); 71 | 72 | const scene = new Transform(); 73 | 74 | const geometry = new Plane(gl, { 75 | widthSegments: 10, 76 | heightSegments: 10, 77 | }); 78 | 79 | const texture = new Texture(gl); 80 | const img = new Image(); 81 | img.onload = () => (texture.image = img); 82 | img.src = '../../assets/leaf.jpg'; 83 | 84 | const program = new Program(gl, { 85 | vertex, 86 | fragment, 87 | uniforms: { 88 | tMap: { value: texture }, 89 | uColor: { value: new Color('#ffc219') }, 90 | }, 91 | transparent: true, 92 | cullFace: null, 93 | }); 94 | 95 | const meshes = []; 96 | 97 | for (let i = 0; i < 50; i++) { 98 | const mesh: SpeedMesh = new Mesh(gl, { geometry, program }); 99 | mesh.position.set((Math.random() - 0.5) * 3, (Math.random() - 0.5) * 6, (Math.random() - 0.5) * 3); 100 | mesh.rotation.set(0, (Math.random() - 0.5) * 6.28, (Math.random() - 0.5) * 6.28); 101 | mesh.scale.set(Math.random() * 0.5 + 0.2); 102 | mesh.speed = Math.random() * 1.5 + 0.2; 103 | mesh.setParent(scene); 104 | meshes.push(mesh); 105 | } 106 | 107 | requestAnimationFrame(update); 108 | function update(t) { 109 | requestAnimationFrame(update); 110 | 111 | meshes.forEach((mesh) => { 112 | mesh.rotation.y += 0.05; 113 | mesh.rotation.z += 0.05; 114 | // mesh.position.y -= 0.02 * mesh.speed; 115 | if (mesh.position.y < -3) mesh.position.y += 6; 116 | }); 117 | 118 | scene.rotation.y += 0.015; 119 | 120 | // Objects are automatically sorted if renderer.sort === true 121 | renderer.render({ scene, camera }); 122 | } 123 | 124 | document.getElementsByClassName('Info')[0].innerHTML = 'Sort Transparency'; 125 | document.title = 'OGL • Sort Transparency'; 126 | -------------------------------------------------------------------------------- /examples/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 |
25 |
26 | 27 | 28 | -------------------------------------------------------------------------------- /examples/textures/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /examples/torus/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /examples/torus/index.ts: -------------------------------------------------------------------------------- 1 | import { Renderer, Camera, Orbit, Transform, Program, Torus, Mesh } from '../../src'; 2 | 3 | const vertex = /* glsl */ ` 4 | precision highp float; 5 | precision highp int; 6 | 7 | attribute vec3 position; 8 | attribute vec3 normal; 9 | 10 | uniform mat3 normalMatrix; 11 | uniform mat4 modelViewMatrix; 12 | uniform mat4 projectionMatrix; 13 | 14 | varying vec3 vNormal; 15 | 16 | void main() { 17 | vNormal = normalize(normalMatrix * normal); 18 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); 19 | } 20 | `; 21 | 22 | const fragment = /* glsl */ ` 23 | precision highp float; 24 | precision highp int; 25 | 26 | varying vec3 vNormal; 27 | 28 | void main() { 29 | gl_FragColor.rgb = normalize(vNormal); 30 | gl_FragColor.a = 1.0; 31 | } 32 | `; 33 | 34 | { 35 | const renderer = new Renderer({ dpr: 2 }); 36 | const gl = renderer.gl; 37 | document.body.appendChild(gl.canvas); 38 | gl.clearColor(1, 1, 1, 1); 39 | 40 | const camera = new Camera(gl, { fov: 35 }); 41 | camera.position.set(5, 3, 6); 42 | camera.lookAt([0, 0, 0]); 43 | 44 | function resize() { 45 | renderer.setSize(window.innerWidth, window.innerHeight); 46 | camera.perspective({ aspect: gl.canvas.width / gl.canvas.height }); 47 | } 48 | window.addEventListener('resize', resize, false); 49 | resize(); 50 | 51 | const scene = new Transform(); 52 | const controls = new Orbit(camera); 53 | 54 | const program = new Program(gl, { 55 | vertex, 56 | fragment, 57 | }); 58 | 59 | const torusGeometry = new Torus(gl, { 60 | radius: 1, 61 | tube: 0.4, 62 | radialSegments: 16, 63 | tubularSegments: 32, 64 | }); 65 | 66 | const torus = new Mesh(gl, { geometry: torusGeometry, program }); 67 | torus.setParent(scene); 68 | 69 | requestAnimationFrame(update); 70 | function update() { 71 | requestAnimationFrame(update); 72 | torus.rotation.x += 0.001; 73 | torus.rotation.y += 0.005; 74 | torus.rotation.z += 0.003; 75 | 76 | controls.update(); 77 | 78 | renderer.render({ scene, camera }); 79 | } 80 | } 81 | 82 | document.getElementsByClassName('Info')[0].innerHTML = 'Torus'; 83 | document.title = 'OGL • Torus'; 84 | -------------------------------------------------------------------------------- /examples/triangle-screen-shader/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /examples/triangle-screen-shader/index.ts: -------------------------------------------------------------------------------- 1 | import { Renderer, Triangle, Program, Color, Mesh } from '../../src'; 2 | 3 | const vertex = /* glsl */ ` 4 | attribute vec2 uv; 5 | attribute vec2 position; 6 | 7 | varying vec2 vUv; 8 | 9 | void main() { 10 | vUv = uv; 11 | gl_Position = vec4(position, 0, 1); 12 | } 13 | `; 14 | 15 | const fragment = /* glsl */ ` 16 | precision highp float; 17 | 18 | uniform float uTime; 19 | uniform vec3 uColor; 20 | 21 | varying vec2 vUv; 22 | 23 | void main() { 24 | gl_FragColor.rgb = 0.5 + 0.3 * cos(vUv.xyx + uTime) + uColor; 25 | gl_FragColor.a = 1.0; 26 | } 27 | `; 28 | 29 | const renderer = new Renderer(); 30 | const gl = renderer.gl; 31 | document.body.appendChild(gl.canvas); 32 | gl.clearColor(1, 1, 1, 1); 33 | 34 | function resize() { 35 | renderer.setSize(window.innerWidth, window.innerHeight); 36 | } 37 | window.addEventListener('resize', resize, false); 38 | resize(); 39 | 40 | // Rather than using a plane (two triangles) to cover the viewport here is a 41 | // triangle that includes -1 to 1 range for 'position', and 0 to 1 range for 'uv'. 42 | // Excess will be out of the viewport. 43 | 44 | // position uv 45 | // (-1, 3) (0, 2) 46 | // |\ |\ 47 | // |__\(1, 1) |__\(1, 1) 48 | // |__|_\ |__|_\ 49 | // (-1, -1) (3, -1) (0, 0) (2, 0) 50 | 51 | const geometry = new Triangle(gl); 52 | 53 | const program = new Program(gl, { 54 | vertex, 55 | fragment, 56 | uniforms: { 57 | uTime: { value: 0 }, 58 | uColor: { value: new Color(0.3, 0.2, 0.5) }, 59 | }, 60 | }); 61 | 62 | const mesh = new Mesh(gl, { geometry, program }); 63 | 64 | requestAnimationFrame(update); 65 | function update(t) { 66 | requestAnimationFrame(update); 67 | 68 | program.uniforms.uTime.value = t * 0.001; 69 | 70 | // Don't need a camera if camera uniforms aren't required 71 | renderer.render({ scene: mesh }); 72 | } 73 | 74 | document.getElementsByClassName('Info')[0].innerHTML = 'Triangle Screen Shader'; 75 | document.title = 'OGL • Triangle Screen Shader'; 76 | -------------------------------------------------------------------------------- /examples/wireframe-shader/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /examples/wireframe/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ogl-typescript", 3 | "version": "0.1.40", 4 | "description": "TypeScript port of OGL WebGL Library", 5 | "module": "lib/index.js", 6 | "types": "lib/index.d.ts", 7 | "sideEffects": false, 8 | "directories": { 9 | "example": "examples" 10 | }, 11 | "files": [ 12 | "lib" 13 | ], 14 | "publishConfig": { 15 | "access": "public" 16 | }, 17 | "scripts": { 18 | "dev": "vite", 19 | "build": "tsc", 20 | "build:examples": "vite build" 21 | }, 22 | "repository": { 23 | "type": "git", 24 | "url": "git+https://github.com/nshen/ogl-typescript" 25 | }, 26 | "keywords": [ 27 | "webgl", 28 | "ogl", 29 | "ogl-typescript" 30 | ], 31 | "author": { 32 | "name": "Nathan Gordon", 33 | "email": "gordonnl@gmail.com", 34 | "url": "https://twitter.com/gordonnl" 35 | }, 36 | "license": "MIT", 37 | "bugs": { 38 | "url": "https://github.com/nshen/ogl-typescript/issues" 39 | }, 40 | "homepage": "https://github.com/nshen/ogl-typescript", 41 | "devDependencies": { 42 | "prettier": "^2.2.1", 43 | "rimraf": "^3.0.2", 44 | "typescript": "^4.2.4", 45 | "vite": "^2.1.5" 46 | }, 47 | "prettier": { 48 | "arrowParens": "always", 49 | "bracketSpacing": true, 50 | "endOfLine": "lf", 51 | "htmlWhitespaceSensitivity": "css", 52 | "printWidth": 150, 53 | "quoteProps": "as-needed", 54 | "semi": true, 55 | "singleQuote": true, 56 | "tabWidth": 4, 57 | "trailingComma": "es5", 58 | "useTabs": false 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /public/assets/acorn.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/acorn.jpg -------------------------------------------------------------------------------- /public/assets/airplane.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/airplane.jpg -------------------------------------------------------------------------------- /public/assets/anim-format.json: -------------------------------------------------------------------------------- 1 | { 2 | frames: [ 3 | { 4 | position: [bone1X, bone1Y, bone1Z, bone2X, bone2Y, bone2Z], 5 | quaternion: [2, 3, 1, 5, 6, 2], 6 | scale: [2, 3, 1, 5, 6, 2], 7 | }, 8 | { 9 | position: [2, 3, 1, 5, 6, 2], 10 | quaternion: [2, 3, 1, 5, 6, 2], 11 | scale: [2, 3, 1, 5, 6, 2], 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /public/assets/compressed/astc-m-y.ktx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/compressed/astc-m-y.ktx -------------------------------------------------------------------------------- /public/assets/compressed/etc-m-y.ktx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/compressed/etc-m-y.ktx -------------------------------------------------------------------------------- /public/assets/compressed/etc1-m-y.ktx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/compressed/etc1-m-y.ktx -------------------------------------------------------------------------------- /public/assets/compressed/pvrtc-m-y.ktx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/compressed/pvrtc-m-y.ktx -------------------------------------------------------------------------------- /public/assets/compressed/s3tc-m-y.ktx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/compressed/s3tc-m-y.ktx -------------------------------------------------------------------------------- /public/assets/compressed/uv.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/compressed/uv.jpg -------------------------------------------------------------------------------- /public/assets/croissant.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/croissant.jpg -------------------------------------------------------------------------------- /public/assets/cube/negx.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/cube/negx.jpg -------------------------------------------------------------------------------- /public/assets/cube/negy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/cube/negy.jpg -------------------------------------------------------------------------------- /public/assets/cube/negz.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/cube/negz.jpg -------------------------------------------------------------------------------- /public/assets/cube/posx.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/cube/posx.jpg -------------------------------------------------------------------------------- /public/assets/cube/posy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/cube/posy.jpg -------------------------------------------------------------------------------- /public/assets/cube/posz.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/cube/posz.jpg -------------------------------------------------------------------------------- /public/assets/earth.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/earth.jpg -------------------------------------------------------------------------------- /public/assets/earth_cloud.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/earth_cloud.jpg -------------------------------------------------------------------------------- /public/assets/earth_specular.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/earth_specular.jpg -------------------------------------------------------------------------------- /public/assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/favicon.png -------------------------------------------------------------------------------- /public/assets/fonts/FiraSans-Bold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/fonts/FiraSans-Bold.png -------------------------------------------------------------------------------- /public/assets/fonts/FiraSans-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/fonts/FiraSans-Bold.ttf -------------------------------------------------------------------------------- /public/assets/fonts/raleway-bold-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/fonts/raleway-bold-webfont.woff -------------------------------------------------------------------------------- /public/assets/fonts/raleway-bold-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/fonts/raleway-bold-webfont.woff2 -------------------------------------------------------------------------------- /public/assets/fonts/raleway-regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/fonts/raleway-regular-webfont.woff -------------------------------------------------------------------------------- /public/assets/fonts/raleway-regular-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/fonts/raleway-regular-webfont.woff2 -------------------------------------------------------------------------------- /public/assets/forest.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/forest.jpg -------------------------------------------------------------------------------- /public/assets/fox.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/fox.jpg -------------------------------------------------------------------------------- /public/assets/gltf/hershel.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/gltf/hershel.glb -------------------------------------------------------------------------------- /public/assets/goat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/goat.jpg -------------------------------------------------------------------------------- /public/assets/granite-diffuse.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/granite-diffuse.jpg -------------------------------------------------------------------------------- /public/assets/granite-normal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/granite-normal.jpg -------------------------------------------------------------------------------- /public/assets/grid.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/grid.jpg -------------------------------------------------------------------------------- /public/assets/laputa.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/laputa.mp4 -------------------------------------------------------------------------------- /public/assets/leaf.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/leaf.jpg -------------------------------------------------------------------------------- /public/assets/macaw.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/macaw.jpg -------------------------------------------------------------------------------- /public/assets/main.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | } 4 | 5 | body, 6 | html { 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | overflow-x: hidden; 10 | height: 100%; 11 | overscroll-behavior-y: contain; 12 | } 13 | 14 | body, 15 | button, 16 | p, 17 | ul { 18 | margin: 0; 19 | padding: 0; 20 | } 21 | 22 | button, 23 | select { 24 | border: none; 25 | outline: none; 26 | background: none; 27 | font-family: inherit; 28 | } 29 | 30 | a, 31 | button, 32 | input, 33 | select, 34 | textarea { 35 | -webkit-tap-highlight-color: transparent; 36 | } 37 | 38 | a { 39 | color: inherit; /* blue colors for links too */ 40 | text-decoration: inherit; /* no underline */ 41 | } 42 | 43 | :root { 44 | overflow-x: hidden; 45 | height: 100%; 46 | } 47 | 48 | @font-face { 49 | font-family: 'Raleway'; 50 | src: url('fonts/raleway-regular-webfont.woff2') format('woff2'), 51 | url('fonts/raleway-regular-webfont.woff') format('woff'); 52 | font-weight: 400; 53 | font-style: normal; 54 | } 55 | 56 | @font-face { 57 | font-family: 'Raleway'; 58 | src: url('fonts/raleway-bold-webfont.woff2') format('woff2'), 59 | url('fonts/raleway-bold-webfont.woff') format('woff'); 60 | font-weight: 700; 61 | font-style: normal; 62 | } 63 | 64 | body { 65 | font-family: 'Raleway', sans-serif; 66 | font-weight: 700; 67 | font-size: 14px; 68 | } 69 | 70 | .Side { 71 | position: relative; 72 | width: 300px; 73 | height: 100%; 74 | overflow: auto; 75 | padding-bottom: 100px; 76 | padding-left: 20px; 77 | padding-right: 40px; 78 | background: #fff; 79 | } 80 | .Iframe { 81 | position: absolute; 82 | left: 300px; 83 | right: 0; 84 | top: 0; 85 | bottom: 0; 86 | height: 100%; 87 | width: calc(100% - 300px); 88 | border: none; 89 | margin: 0; 90 | } 91 | .Title { 92 | font-size: 18px; 93 | margin: 20px 0; 94 | display: block; 95 | } 96 | .SubTitle { 97 | font-weight: 400; 98 | font-size: 17px; 99 | margin: 20px 0 50px; 100 | } 101 | .Section { 102 | font-weight: 400; 103 | margin: 40px 0 20px; 104 | padding-left: 15px; 105 | } 106 | .Example { 107 | margin: 10px 0; 108 | padding-left: 30px; 109 | display: block; 110 | } 111 | div.Example { 112 | opacity: 0.2; 113 | } 114 | 115 | .SideIcon, 116 | .CodeIcon { 117 | position: absolute; 118 | display: block; 119 | z-index: 1; 120 | margin: 30px; 121 | width: 50px; 122 | height: 50px; 123 | line-height: 50px; 124 | text-align: center; 125 | background: #fff; 126 | border-radius: 100%; 127 | font-size: 19px; 128 | letter-spacing: -0.1em; 129 | box-shadow: 1px 1px 6px rgba(0, 0, 0, 0.1); 130 | } 131 | .CodeIcon { 132 | bottom: 0; 133 | right: 0; 134 | } 135 | .SideIcon { 136 | top: 20px; 137 | left: 245px; 138 | display: none; 139 | } 140 | 141 | @media (max-width: 600px) { 142 | .Side { 143 | z-index: 1; 144 | } 145 | .Iframe { 146 | left: 0; 147 | width: 100%; 148 | } 149 | .SideIcon { 150 | display: block; 151 | } 152 | [data-hideSidebar] .Side { 153 | transform: translateX(-100%); 154 | } 155 | 156 | [data-hideSidebar] .SideIcon { 157 | left: 0; 158 | transform: rotate(180deg); 159 | } 160 | } 161 | 162 | .Info { 163 | padding: 20px; 164 | position: relative; 165 | z-index: 1; 166 | } 167 | .Info a { 168 | color: rgb(39, 124, 235); 169 | } 170 | canvas { 171 | position: absolute; 172 | width: 100%; 173 | height: 100%; 174 | top: 0; 175 | left: 0; 176 | } 177 | -------------------------------------------------------------------------------- /public/assets/matcap.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/matcap.jpg -------------------------------------------------------------------------------- /public/assets/octopus.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/octopus.jpg -------------------------------------------------------------------------------- /public/assets/ogl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/ogl.png -------------------------------------------------------------------------------- /public/assets/pbr/black.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/pbr/black.jpg -------------------------------------------------------------------------------- /public/assets/pbr/car-ext-color.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/pbr/car-ext-color.jpg -------------------------------------------------------------------------------- /public/assets/pbr/car-ext-emissive.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/pbr/car-ext-emissive.jpg -------------------------------------------------------------------------------- /public/assets/pbr/car-ext-normal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/pbr/car-ext-normal.jpg -------------------------------------------------------------------------------- /public/assets/pbr/car-ext-opacity.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/pbr/car-ext-opacity.jpg -------------------------------------------------------------------------------- /public/assets/pbr/car-ext-rmo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/pbr/car-ext-rmo.jpg -------------------------------------------------------------------------------- /public/assets/pbr/car-int-color.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/pbr/car-int-color.jpg -------------------------------------------------------------------------------- /public/assets/pbr/car-int-normal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/pbr/car-int-normal.jpg -------------------------------------------------------------------------------- /public/assets/pbr/car-int-rmo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/pbr/car-int-rmo.jpg -------------------------------------------------------------------------------- /public/assets/pbr/car-shadow.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/pbr/car-shadow.jpg -------------------------------------------------------------------------------- /public/assets/pbr/car-shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/pbr/car-shadow.png -------------------------------------------------------------------------------- /public/assets/pbr/lut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/pbr/lut.png -------------------------------------------------------------------------------- /public/assets/pbr/waterfall-diffuse-RGBM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/pbr/waterfall-diffuse-RGBM.png -------------------------------------------------------------------------------- /public/assets/pbr/waterfall-specular-RGBM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/pbr/waterfall-specular-RGBM.png -------------------------------------------------------------------------------- /public/assets/pbr/white.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/pbr/white.jpg -------------------------------------------------------------------------------- /public/assets/plane.json: -------------------------------------------------------------------------------- 1 | { 2 | "position": [0.0, -0.149, -1.543, -0.002, -0.288, 0.649, 0.242, 0.149, 0.589, 0.242, 0.149, 0.589, 0.681, -0.055, 0.618, 0.0, -0.149, -1.543, -0.221, 0.149, 0.589, -0.002, -0.288, 0.649, 0.0, -0.149, -1.543, -0.681, -0.055, 0.618, -0.221, 0.149, 0.589, 0.0, -0.149, -1.543], 3 | "normal": [-0.87, 0.491, 0.03, -0.87, 0.491, 0.03, -0.87, 0.491, 0.03, 0.425, 0.889, -0.172, 0.425, 0.889, -0.172, 0.425, 0.889, -0.172, 0.892, 0.451, 0.03, 0.892, 0.451, 0.03, 0.892, 0.451, 0.03, -0.409, 0.897, -0.168, -0.409, 0.897, -0.168, -0.409, 0.897, -0.168], 4 | "uv": [0.5, 1.0, 0.5, 0.0, 0.698, 0.0, 0.698, 0.0, 1.0, 0.0, 0.5, 1.0, 0.312, 0.0, 0.5, 0.0, 0.5, 1.0, 0.0, 0.0, 0.312, 0.0, 0.5, 1.0] 5 | } -------------------------------------------------------------------------------- /public/assets/saddle.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/saddle.jpg -------------------------------------------------------------------------------- /public/assets/sky.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/sky.jpg -------------------------------------------------------------------------------- /public/assets/snout-shadow.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/snout-shadow.jpg -------------------------------------------------------------------------------- /public/assets/snout.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/snout.jpg -------------------------------------------------------------------------------- /public/assets/sunset-diffuse-RGBM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/sunset-diffuse-RGBM.png -------------------------------------------------------------------------------- /public/assets/sunset-specular-RGBM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/sunset-specular-RGBM.png -------------------------------------------------------------------------------- /public/assets/sunset.exr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/sunset.exr -------------------------------------------------------------------------------- /public/assets/uv.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/uv.jpg -------------------------------------------------------------------------------- /public/assets/water.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/water.jpg -------------------------------------------------------------------------------- /public/assets/windmill.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/windmill.jpg -------------------------------------------------------------------------------- /src/Guards.ts: -------------------------------------------------------------------------------- 1 | import { Transform } from './core/Transform'; 2 | import { Mesh } from './core/Mesh'; 3 | import { OGLRenderingContext } from './core/Renderer'; 4 | 5 | export const isArrayLike = (term: any): term is ArrayLike => { 6 | if (term.length) return true; 7 | return false; 8 | }; 9 | 10 | export const isMesh = (node: Transform | Mesh): node is Mesh => { 11 | return !!(node as any).draw; 12 | }; 13 | 14 | export const isWebGl2 = (gl: OGLRenderingContext): gl is OGLRenderingContext & WebGL2RenderingContext => { 15 | return gl.renderer.isWebgl2; 16 | }; 17 | -------------------------------------------------------------------------------- /src/core/Mesh.ts: -------------------------------------------------------------------------------- 1 | import { Transform } from './Transform'; 2 | import { Mat3 } from '../math/Mat3'; 3 | import { Mat4 } from '../math/Mat4'; 4 | import { Geometry } from './Geometry'; 5 | import { Program } from './Program'; 6 | import { OGLRenderingContext } from './Renderer'; 7 | import { Camera } from './Camera'; 8 | import { Vec3 } from '../math/Vec3'; 9 | import { Vec2 } from '../math/Vec2'; 10 | 11 | let ID = 0; 12 | 13 | export interface MeshOptions { 14 | geometry: Geometry; 15 | program: Program; 16 | mode: GLenum; 17 | frustumCulled: boolean; 18 | renderOrder: number; 19 | } 20 | 21 | export interface DrawOptions { 22 | camera: Camera; 23 | } 24 | 25 | export class Mesh extends Transform { 26 | name: string; 27 | numInstances; 28 | 29 | gl: OGLRenderingContext; 30 | id: number; 31 | geometry: Geometry; 32 | program: Program; 33 | mode: GLenum; 34 | frustumCulled: boolean; 35 | renderOrder: number; 36 | 37 | modelViewMatrix: Mat4; 38 | normalMatrix: Mat3; 39 | 40 | beforeRenderCallbacks: Array; 41 | afterRenderCallbacks: Array; 42 | 43 | // raycast.ts 44 | hit: Partial<{ 45 | localPoint: Vec3; 46 | distance: number; 47 | point: Vec3; 48 | faceNormal: Vec3; 49 | localFaceNormal: Vec3; 50 | uv: Vec2; 51 | localNormal: Vec3; 52 | normal: Vec3; 53 | }> = null; 54 | 55 | constructor( 56 | gl: OGLRenderingContext, 57 | { geometry, program, mode = gl.TRIANGLES, frustumCulled = true, renderOrder = 0 }: Partial = {} 58 | ) { 59 | super(); 60 | if (!gl.canvas) console.error('gl not passed as first argument to Mesh'); 61 | this.gl = gl; 62 | this.id = ID++; 63 | this.geometry = geometry; 64 | this.program = program; 65 | this.mode = mode; 66 | 67 | // Used to skip frustum culling 68 | this.frustumCulled = frustumCulled; 69 | 70 | // Override sorting to force an order 71 | this.renderOrder = renderOrder; 72 | this.modelViewMatrix = new Mat4(); 73 | this.normalMatrix = new Mat3(); 74 | this.beforeRenderCallbacks = []; 75 | this.afterRenderCallbacks = []; 76 | } 77 | 78 | onBeforeRender(f) { 79 | this.beforeRenderCallbacks.push(f); 80 | return this; 81 | } 82 | 83 | onAfterRender(f) { 84 | this.afterRenderCallbacks.push(f); 85 | return this; 86 | } 87 | 88 | draw({ camera }: Partial = {}) { 89 | this.beforeRenderCallbacks.forEach((f) => f && f({ mesh: this, camera })); 90 | 91 | // Set the matrix uniforms 92 | if (camera) { 93 | // Add empty matrix uniforms to program if unset 94 | if (!this.program.uniforms.modelMatrix) { 95 | Object.assign(this.program.uniforms, { 96 | modelMatrix: { value: null }, 97 | viewMatrix: { value: null }, 98 | modelViewMatrix: { value: null }, 99 | normalMatrix: { value: null }, 100 | projectionMatrix: { value: null }, 101 | cameraPosition: { value: null }, 102 | }); 103 | } 104 | 105 | // Set the matrix uniforms 106 | this.program.uniforms.projectionMatrix.value = camera.projectionMatrix; 107 | this.program.uniforms.cameraPosition.value = camera.worldPosition; 108 | this.program.uniforms.viewMatrix.value = camera.viewMatrix; 109 | this.modelViewMatrix.multiply(camera.viewMatrix, this.worldMatrix); 110 | this.normalMatrix.getNormalMatrix(this.modelViewMatrix); 111 | this.program.uniforms.modelMatrix.value = this.worldMatrix; 112 | this.program.uniforms.modelViewMatrix.value = this.modelViewMatrix; 113 | this.program.uniforms.normalMatrix.value = this.normalMatrix; 114 | } 115 | 116 | // determine if faces need to be flipped - when mesh scaled negatively 117 | let flipFaces = this.program.cullFace && this.worldMatrix.determinant() < 0; 118 | 119 | this.program.use({ flipFaces }); 120 | this.geometry.draw({ mode: this.mode, program: this.program }); 121 | 122 | this.afterRenderCallbacks.forEach((f) => f && f({ mesh: this, camera })); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/core/Transform.ts: -------------------------------------------------------------------------------- 1 | import { Vec3 } from '../math/Vec3'; 2 | import { Quat } from '../math/Quat'; 3 | import { Mat4 } from '../math/Mat4'; 4 | import { Euler } from '../math/Euler'; 5 | 6 | export class Transform { 7 | parent: Transform; 8 | children: Transform[]; 9 | visible: boolean; 10 | 11 | matrix: Mat4; 12 | worldMatrix: Mat4; 13 | matrixAutoUpdate: boolean; 14 | worldMatrixNeedsUpdate: boolean; 15 | 16 | position: Vec3; 17 | scale: Vec3; 18 | up: Vec3; 19 | quaternion: Quat; 20 | rotation: Euler; 21 | 22 | constructor() { 23 | this.parent = null; 24 | this.children = []; 25 | this.visible = true; 26 | 27 | this.matrix = new Mat4(); 28 | this.worldMatrix = new Mat4(); 29 | this.matrixAutoUpdate = true; 30 | 31 | this.up = new Vec3(0, 1, 0); 32 | this.position = new Vec3(); 33 | this.scale = new Vec3(1); 34 | 35 | this.quaternion = new Quat(); 36 | this.rotation = new Euler(); 37 | 38 | this.rotation.onChange = () => this.quaternion.fromEuler(this.rotation); 39 | this.quaternion.onChange = () => this.rotation.fromQuaternion(this.quaternion); 40 | } 41 | 42 | setParent(parent, notifyParent = true) { 43 | if (this.parent && parent !== this.parent) this.parent.removeChild(this, false); 44 | this.parent = parent; 45 | if (notifyParent && parent) parent.addChild(this, false); 46 | } 47 | 48 | addChild(child: Transform, notifyChild: boolean = true) { 49 | if (!~this.children.indexOf(child)) this.children.push(child); 50 | if (notifyChild) child.setParent(this, false); 51 | } 52 | 53 | removeChild(child: Transform, notifyChild: boolean = true) { 54 | if (!!~this.children.indexOf(child)) this.children.splice(this.children.indexOf(child), 1); 55 | if (notifyChild) child.setParent(null, false); 56 | } 57 | 58 | updateMatrixWorld(force?: boolean) { 59 | if (this.matrixAutoUpdate) this.updateMatrix(); 60 | if (this.worldMatrixNeedsUpdate || force) { 61 | if (this.parent === null) this.worldMatrix.copy(this.matrix); 62 | else this.worldMatrix.multiply(this.parent.worldMatrix, this.matrix); 63 | this.worldMatrixNeedsUpdate = false; 64 | force = true; 65 | } 66 | 67 | for (let i = 0, l = this.children.length; i < l; i++) { 68 | this.children[i].updateMatrixWorld(force); 69 | } 70 | } 71 | 72 | updateMatrix() { 73 | this.matrix.compose(this.quaternion, this.position, this.scale); 74 | this.worldMatrixNeedsUpdate = true; 75 | } 76 | 77 | traverse(callback: (node: Transform) => boolean | void) { 78 | // Return true in callback to stop traversing children 79 | if (callback(this)) return; 80 | for (let i = 0, l = this.children.length; i < l; i++) { 81 | this.children[i].traverse(callback); 82 | } 83 | } 84 | 85 | decompose() { 86 | this.matrix.getTranslation(this.position); 87 | this.matrix.getRotation(this.quaternion); 88 | this.matrix.getScaling(this.scale); 89 | this.rotation.fromQuaternion(this.quaternion); 90 | } 91 | 92 | lookAt(target: T, invert = false) { 93 | if (invert) this.matrix.lookAt(this.position, target, this.up); 94 | else this.matrix.lookAt(target, this.position, this.up); 95 | this.matrix.getRotation(this.quaternion); 96 | this.rotation.fromQuaternion(this.quaternion); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/extras/Animation.ts: -------------------------------------------------------------------------------- 1 | import { Vec3 } from '../math/Vec3'; 2 | import { Quat } from '../math/Quat'; 3 | import { BoneTransform } from './Skin'; 4 | 5 | const prevPos = new Vec3(); 6 | const prevRot = new Quat(); 7 | const prevScl = new Vec3(); 8 | 9 | const nextPos = new Vec3(); 10 | const nextRot = new Quat(); 11 | const nextScl = new Vec3(); 12 | 13 | export interface AnimationOptions { 14 | objects: BoneTransform[]; 15 | data: any; 16 | } 17 | 18 | export class Animation { 19 | objects: BoneTransform[]; 20 | data: any; 21 | elapsed: number; 22 | weight: number; 23 | duration: number; 24 | 25 | constructor({ objects, data }: AnimationOptions) { 26 | this.objects = objects; 27 | this.data = data; 28 | this.elapsed = 0; 29 | this.weight = 1; 30 | this.duration = data.frames.length - 1; 31 | } 32 | 33 | update(totalWeight = 1, isSet) { 34 | const weight = isSet ? 1 : this.weight / totalWeight; 35 | const elapsed = this.elapsed % this.duration; 36 | 37 | const floorFrame = Math.floor(elapsed); 38 | const blend = elapsed - floorFrame; 39 | const prevKey = this.data.frames[floorFrame]; 40 | const nextKey = this.data.frames[(floorFrame + 1) % this.duration]; 41 | 42 | this.objects.forEach((object, i) => { 43 | prevPos.fromArray(prevKey.position, i * 3); 44 | prevRot.fromArray(prevKey.quaternion, i * 4); 45 | prevScl.fromArray(prevKey.scale, i * 3); 46 | 47 | nextPos.fromArray(nextKey.position, i * 3); 48 | nextRot.fromArray(nextKey.quaternion, i * 4); 49 | nextScl.fromArray(nextKey.scale, i * 3); 50 | 51 | prevPos.lerp(nextPos, blend); 52 | prevRot.slerp(nextRot, blend); 53 | prevScl.lerp(nextScl, blend); 54 | 55 | object.position.lerp(prevPos, weight); 56 | object.quaternion.slerp(prevRot, weight); 57 | object.scale.lerp(prevScl, weight); 58 | }); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/extras/Box.ts: -------------------------------------------------------------------------------- 1 | import { Geometry, AttributeMap } from '../core/Geometry'; 2 | import { Plane } from './Plane'; 3 | import { OGLRenderingContext } from '../core/Renderer'; 4 | 5 | export type BoxOptions = { 6 | width: number; 7 | height: number; 8 | depth: number; 9 | widthSegments: number; 10 | heightSegments: number; 11 | depthSegments: number; 12 | attributes: AttributeMap; 13 | }; 14 | 15 | export class Box extends Geometry { 16 | constructor( 17 | gl: OGLRenderingContext, 18 | { width = 1, height = 1, depth = 1, widthSegments = 1, heightSegments = 1, depthSegments = 1, attributes = {} }: Partial = {} 19 | ) { 20 | const wSegs = widthSegments; 21 | const hSegs = heightSegments; 22 | const dSegs = depthSegments; 23 | 24 | const num = (wSegs + 1) * (hSegs + 1) * 2 + (wSegs + 1) * (dSegs + 1) * 2 + (hSegs + 1) * (dSegs + 1) * 2; 25 | const numIndices = (wSegs * hSegs * 2 + wSegs * dSegs * 2 + hSegs * dSegs * 2) * 6; 26 | 27 | const position = new Float32Array(num * 3); 28 | const normal = new Float32Array(num * 3); 29 | const uv = new Float32Array(num * 2); 30 | const index = num > 65536 ? new Uint32Array(numIndices) : new Uint16Array(numIndices); 31 | 32 | let i = 0; 33 | let ii = 0; 34 | 35 | // left, right 36 | Plane.buildPlane(position, normal, uv, index, depth, height, width, dSegs, hSegs, 2, 1, 0, -1, -1, i, ii); 37 | i += (dSegs + 1) * (hSegs + 1); 38 | ii += dSegs * hSegs; 39 | 40 | Plane.buildPlane(position, normal, uv, index, depth, height, -width, dSegs, hSegs, 2, 1, 0, 1, -1, i, ii); 41 | i += (dSegs + 1) * (hSegs + 1); 42 | ii += dSegs * hSegs; 43 | 44 | // top, bottom 45 | Plane.buildPlane(position, normal, uv, index, width, depth, height, dSegs, wSegs, 0, 2, 1, 1, 1, i, ii); 46 | i += (wSegs + 1) * (dSegs + 1); 47 | ii += wSegs * dSegs; 48 | 49 | Plane.buildPlane(position, normal, uv, index, width, depth, -height, dSegs, wSegs, 0, 2, 1, 1, -1, i, ii); 50 | i += (wSegs + 1) * (dSegs + 1); 51 | ii += wSegs * dSegs; 52 | 53 | // front, back 54 | Plane.buildPlane(position, normal, uv, index, width, height, -depth, wSegs, hSegs, 0, 1, 2, -1, -1, i, ii); 55 | i += (wSegs + 1) * (hSegs + 1); 56 | ii += wSegs * hSegs; 57 | 58 | Plane.buildPlane(position, normal, uv, index, width, height, depth, wSegs, hSegs, 0, 1, 2, 1, -1, i, ii); 59 | 60 | Object.assign(attributes, { 61 | position: { size: 3, data: position }, 62 | normal: { size: 3, data: normal }, 63 | uv: { size: 2, data: uv }, 64 | index: { data: index }, 65 | }); 66 | 67 | super(gl, attributes); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/extras/GLTFAnimation.ts: -------------------------------------------------------------------------------- 1 | import { Vec3 } from '../math/Vec3'; 2 | import { Quat } from '../math/Quat'; 3 | 4 | const tmpVec3A = new Vec3(); 5 | const tmpVec3B = new Vec3(); 6 | const tmpVec3C = new Vec3(); 7 | const tmpVec3D = new Vec3(); 8 | 9 | const tmpQuatA = new Quat(); 10 | const tmpQuatB = new Quat(); 11 | const tmpQuatC = new Quat(); 12 | const tmpQuatD = new Quat(); 13 | 14 | export class GLTFAnimation { 15 | private data; 16 | private elapsed: number; 17 | private weight: number; 18 | private loop: boolean; 19 | private duration: number; 20 | private startTime: number; 21 | private endTime: number; 22 | 23 | constructor(data, weight = 1) { 24 | this.data = data; 25 | this.elapsed = 0; 26 | this.weight = weight; 27 | 28 | // Set to false to not apply modulo to elapsed against duration 29 | this.loop = true; 30 | 31 | // Find starting time as exports from blender (perhaps others too) don't always start from 0 32 | this.startTime = data.reduce((a, { times }) => Math.min(a, times[0]), Infinity); 33 | // Get largest final time in all channels to calculate duration 34 | this.endTime = data.reduce((a, { times }) => Math.max(a, times[times.length - 1]), 0); 35 | this.duration = this.endTime - this.startTime; 36 | } 37 | 38 | update(totalWeight = 1, isSet) { 39 | const weight = isSet ? 1 : this.weight / totalWeight; 40 | const elapsed = (this.loop ? this.elapsed % this.duration : Math.min(this.elapsed, this.duration - 0.001)) + this.startTime; 41 | 42 | this.data.forEach(({ node, transform, interpolation, times, values }) => { 43 | // Get index of two time values elapsed is between 44 | const prevIndex = 45 | Math.max( 46 | 1, 47 | times.findIndex((t) => t > elapsed) 48 | ) - 1; 49 | const nextIndex = prevIndex + 1; 50 | 51 | // Get linear blend/alpha between the two 52 | let alpha = (elapsed - times[prevIndex]) / (times[nextIndex] - times[prevIndex]); 53 | if (interpolation === 'STEP') alpha = 0; 54 | 55 | let prevVal: any = tmpVec3A; 56 | let prevTan: any = tmpVec3B; 57 | let nextTan: any = tmpVec3C; 58 | let nextVal: any = tmpVec3D; 59 | let size = 3; 60 | 61 | if (transform === 'quaternion') { 62 | prevVal = tmpQuatA; 63 | prevTan = tmpQuatB; 64 | nextTan = tmpQuatC; 65 | nextVal = tmpQuatD; 66 | size = 4; 67 | } 68 | 69 | if (interpolation === 'CUBICSPLINE') { 70 | // Get the prev and next values from the indices 71 | prevVal.fromArray(values, prevIndex * size * 3 + size * 1); 72 | prevTan.fromArray(values, prevIndex * size * 3 + size * 2); 73 | nextTan.fromArray(values, nextIndex * size * 3 + size * 0); 74 | nextVal.fromArray(values, nextIndex * size * 3 + size * 1); 75 | 76 | // interpolate for final value 77 | prevVal = this.cubicSplineInterpolate(alpha, prevVal, prevTan, nextTan, nextVal); 78 | if (size === 4) prevVal.normalize(); 79 | } else { 80 | // Get the prev and next values from the indices 81 | prevVal.fromArray(values, prevIndex * size); 82 | nextVal.fromArray(values, nextIndex * size); 83 | 84 | // interpolate for final value 85 | if (size === 4) prevVal.slerp(nextVal, alpha); 86 | else prevVal.lerp(nextVal, alpha); 87 | } 88 | 89 | // interpolate between multiple possible animations 90 | if (size === 4) node[transform].slerp(prevVal, weight); 91 | else node[transform].lerp(prevVal, weight); 92 | }); 93 | } 94 | 95 | cubicSplineInterpolate(t, prevVal, prevTan, nextTan, nextVal) { 96 | const t2 = t * t; 97 | const t3 = t2 * t; 98 | 99 | const s2 = 3 * t2 - 2 * t3; 100 | const s3 = t3 - t2; 101 | const s0 = 1 - s2; 102 | const s1 = s3 - t2 + t; 103 | 104 | for (let i = 0; i < prevVal.length; i++) { 105 | prevVal[i] = s0 * prevVal[i] + s1 * (1 - t) * prevTan[i] + s2 * nextVal[i] + s3 * t * nextTan[i]; 106 | } 107 | 108 | return prevVal; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/extras/GLTFSkin.ts: -------------------------------------------------------------------------------- 1 | import { Mesh } from '../core/Mesh'; 2 | import { Mat4 } from '../math/Mat4'; 3 | import { Texture } from '../core/Texture'; 4 | import { Camera } from '../core/Camera'; 5 | import { isWebGl2 } from '../Guards'; 6 | 7 | const tempMat4 = new Mat4(); 8 | const identity = new Mat4(); 9 | 10 | export interface GLTFSkinOptions { 11 | skeleton; 12 | geometry; 13 | program; 14 | mode; 15 | } 16 | 17 | export class GLTFSkin extends Mesh { 18 | skeleton; 19 | animations; 20 | boneMatrices: Float32Array; 21 | boneTextureSize: number; 22 | boneTexture: Texture; 23 | 24 | constructor(gl, { skeleton, geometry, program, mode = gl.TRIANGLES }: Partial = {}) { 25 | super(gl, { geometry, program, mode }); 26 | this.skeleton = skeleton; 27 | this.program = program; 28 | this.createBoneTexture(); 29 | this.animations = []; 30 | } 31 | 32 | createBoneTexture() { 33 | if (!this.skeleton.joints.length) return; 34 | const size = Math.max(4, Math.pow(2, Math.ceil(Math.log(Math.sqrt(this.skeleton.joints.length * 4)) / Math.LN2))); 35 | this.boneMatrices = new Float32Array(size * size * 4); 36 | this.boneTextureSize = size; 37 | this.boneTexture = new Texture(this.gl, { 38 | image: this.boneMatrices, 39 | generateMipmaps: false, 40 | type: this.gl.FLOAT, 41 | internalFormat: isWebGl2(this.gl) ? this.gl.RGBA32F : this.gl.RGBA, 42 | // internalFormat: this.gl.renderer.isWebgl2 ? this.gl.RGBA32F : this.gl.RGBA, 43 | minFilter: this.gl.NEAREST, 44 | magFilter: this.gl.NEAREST, 45 | flipY: false, 46 | width: size, 47 | }); 48 | } 49 | 50 | // addAnimation(data) { 51 | // const animation = new Animation({ objects: this.bones, data }); 52 | // this.animations.push(animation); 53 | // return animation; 54 | // } 55 | 56 | // updateAnimations() { 57 | // // Calculate combined animation weight 58 | // let total = 0; 59 | // this.animations.forEach((animation) => (total += animation.weight)); 60 | 61 | // this.animations.forEach((animation, i) => { 62 | // // force first animation to set in order to reset frame 63 | // animation.update(total, i === 0); 64 | // }); 65 | // } 66 | 67 | updateUniforms() { 68 | // Update bone texture 69 | this.skeleton.joints.forEach((bone, i) => { 70 | // Find difference between current and bind pose 71 | tempMat4.multiply(bone.worldMatrix, bone.bindInverse); 72 | this.boneMatrices.set(tempMat4, i * 16); 73 | }); 74 | if (this.boneTexture) this.boneTexture.needsUpdate = true; 75 | } 76 | 77 | draw({ camera }: { camera?: Camera } = {}) { 78 | if (!this.program.uniforms.boneTexture) { 79 | Object.assign(this.program.uniforms, { 80 | boneTexture: { value: this.boneTexture }, 81 | boneTextureSize: { value: this.boneTextureSize }, 82 | }); 83 | } 84 | 85 | this.updateUniforms(); 86 | 87 | // Switch the world matrix with identity to ignore any transforms 88 | // on the mesh itself - only use skeleton's transforms 89 | const _worldMatrix = this.worldMatrix; 90 | this.worldMatrix = identity; 91 | 92 | super.draw({ camera }); 93 | 94 | // Switch back to leave identity untouched 95 | this.worldMatrix = _worldMatrix; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/extras/KTXTexture.ts: -------------------------------------------------------------------------------- 1 | import { Texture, CompressedImage } from '../core/Texture'; 2 | 3 | // TODO: Support cubemaps 4 | // Generate textures using https://github.com/TimvanScherpenzeel/texture-compressor 5 | 6 | export interface KTXTextureOptions { 7 | buffer: ArrayBuffer; 8 | src: string; 9 | wrapS: number; 10 | wrapT: number; 11 | anisotropy: number; 12 | minFilter: number; 13 | magFilter: number; 14 | } 15 | 16 | export class KTXTexture extends Texture { 17 | constructor( 18 | gl, 19 | { buffer, wrapS = gl.CLAMP_TO_EDGE, wrapT = gl.CLAMP_TO_EDGE, anisotropy = 0, minFilter, magFilter }: Partial = {} 20 | ) { 21 | super(gl, { 22 | generateMipmaps: false, 23 | wrapS, 24 | wrapT, 25 | anisotropy, 26 | minFilter, 27 | magFilter, 28 | }); 29 | 30 | if (buffer) this.parseBuffer(buffer); 31 | } 32 | 33 | parseBuffer(buffer: ArrayBuffer) { 34 | const ktx = new KhronosTextureContainer(buffer); 35 | ktx.mipmaps.isCompressedTexture = true; 36 | 37 | // Update texture 38 | this.image = ktx.mipmaps; 39 | this.internalFormat = ktx.glInternalFormat; 40 | if (ktx.numberOfMipmapLevels > 1) { 41 | if (this.minFilter === this.gl.LINEAR) this.minFilter = this.gl.NEAREST_MIPMAP_LINEAR; 42 | } else { 43 | if (this.minFilter === this.gl.NEAREST_MIPMAP_LINEAR) this.minFilter = this.gl.LINEAR; 44 | } 45 | 46 | // TODO: support cube maps 47 | // ktx.numberOfFaces 48 | } 49 | } 50 | 51 | class KhronosTextureContainer { 52 | glInternalFormat: number; 53 | numberOfFaces: number; 54 | numberOfMipmapLevels: number; 55 | mipmaps: CompressedImage; 56 | isCompressedTexture: boolean; 57 | 58 | constructor(buffer) { 59 | const idCheck = [0xab, 0x4b, 0x54, 0x58, 0x20, 0x31, 0x31, 0xbb, 0x0d, 0x0a, 0x1a, 0x0a]; 60 | const id = new Uint8Array(buffer, 0, 12); 61 | for (let i = 0; i < id.length; i++) 62 | if (id[i] !== idCheck[i]) { 63 | console.error('File missing KTX identifier'); 64 | return; 65 | } 66 | 67 | // TODO: Is this always 4? Tested: [android, macos] 68 | const size = Uint32Array.BYTES_PER_ELEMENT; 69 | const head = new DataView(buffer, 12, 13 * size); 70 | const littleEndian = head.getUint32(0, true) === 0x04030201; 71 | const glType = head.getUint32(1 * size, littleEndian); 72 | if (glType !== 0) { 73 | console.warn('only compressed formats currently supported'); 74 | return; 75 | } 76 | this.glInternalFormat = head.getUint32(4 * size, littleEndian); 77 | let width = head.getUint32(6 * size, littleEndian); 78 | let height = head.getUint32(7 * size, littleEndian); 79 | this.numberOfFaces = head.getUint32(10 * size, littleEndian); 80 | this.numberOfMipmapLevels = Math.max(1, head.getUint32(11 * size, littleEndian)); 81 | const bytesOfKeyValueData = head.getUint32(12 * size, littleEndian); 82 | 83 | this.mipmaps = []; 84 | let offset = 12 + 13 * 4 + bytesOfKeyValueData; 85 | for (let level = 0; level < this.numberOfMipmapLevels; level++) { 86 | const levelSize = new Int32Array(buffer, offset, 1)[0]; // size per face, since not supporting array cubemaps 87 | offset += 4; // levelSize field 88 | for (let face = 0; face < this.numberOfFaces; face++) { 89 | const data = new Uint8Array(buffer, offset, levelSize); 90 | this.mipmaps.push({ data, width, height }); 91 | offset += levelSize; 92 | offset += 3 - ((levelSize + 3) % 4); // add padding for odd sized image 93 | } 94 | width = width >> 1; 95 | height = height >> 1; 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/extras/NormalProgram.ts: -------------------------------------------------------------------------------- 1 | import { Program, ProgramOptions } from '../core/Program'; 2 | import { OGLRenderingContext } from '../core/Renderer'; 3 | 4 | const vertex = /* glsl */ ` 5 | precision highp float; 6 | precision highp int; 7 | 8 | attribute vec3 position; 9 | attribute vec3 normal; 10 | 11 | uniform mat3 normalMatrix; 12 | uniform mat4 modelViewMatrix; 13 | uniform mat4 projectionMatrix; 14 | 15 | varying vec3 vNormal; 16 | 17 | void main() { 18 | vNormal = normalize(normalMatrix * normal); 19 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); 20 | } 21 | `; 22 | 23 | const fragment = /* glsl */ ` 24 | precision highp float; 25 | precision highp int; 26 | 27 | varying vec3 vNormal; 28 | 29 | void main() { 30 | gl_FragColor.rgb = normalize(vNormal); 31 | gl_FragColor.a = 1.0; 32 | } 33 | `; 34 | 35 | export function NormalProgram(gl) { 36 | return new Program(gl, { 37 | vertex: vertex, 38 | fragment: fragment, 39 | cullFace: null, 40 | }); 41 | } 42 | 43 | // export class NormalProgram extends Program { 44 | 45 | // public gltfMaterial; 46 | 47 | // constructor(gl: OGLRenderingContext, options: Partial = {}) { 48 | // super(gl, options); 49 | // } 50 | // } 51 | -------------------------------------------------------------------------------- /src/extras/Plane.ts: -------------------------------------------------------------------------------- 1 | import { Geometry, Attribute, AttributeMap } from '../core/Geometry'; 2 | import { OGLRenderingContext } from '../core/Renderer'; 3 | 4 | export type PlaneOptions = { 5 | width: number; 6 | height: number; 7 | widthSegments: number; 8 | heightSegments: number; 9 | attributes: AttributeMap; 10 | }; 11 | 12 | export class Plane extends Geometry { 13 | constructor( 14 | gl: OGLRenderingContext, 15 | { width = 1, height = 1, widthSegments = 1, heightSegments = 1, attributes = {} }: Partial = {} 16 | ) { 17 | const wSegs = widthSegments; 18 | const hSegs = heightSegments; 19 | 20 | // Determine length of arrays 21 | const num = (wSegs + 1) * (hSegs + 1); 22 | const numIndices = wSegs * hSegs * 6; 23 | 24 | // Generate empty arrays once 25 | const position = new Float32Array(num * 3); 26 | const normal = new Float32Array(num * 3); 27 | const uv = new Float32Array(num * 2); 28 | const index = num > 65536 ? new Uint32Array(numIndices) : new Uint16Array(numIndices); 29 | 30 | Plane.buildPlane(position, normal, uv, index, width, height, 0, wSegs, hSegs); 31 | 32 | Object.assign(attributes, { 33 | position: { size: 3, data: position }, 34 | normal: { size: 3, data: normal }, 35 | uv: { size: 2, data: uv }, 36 | index: { data: index }, 37 | }); 38 | 39 | super(gl, attributes); 40 | } 41 | 42 | static buildPlane( 43 | position: Float32Array, 44 | normal: Float32Array, 45 | uv: Float32Array, 46 | index: Uint32Array | Uint16Array, 47 | width: number, 48 | height: number, 49 | depth: number, 50 | wSegs: number, 51 | hSegs: number, 52 | u = 0, 53 | v = 1, 54 | w = 2, 55 | uDir = 1, 56 | vDir = -1, 57 | i = 0, 58 | ii = 0 59 | ) { 60 | const io = i; 61 | const segW = width / wSegs; 62 | const segH = height / hSegs; 63 | 64 | for (let iy = 0; iy <= hSegs; iy++) { 65 | let y = iy * segH - height / 2; 66 | for (let ix = 0; ix <= wSegs; ix++, i++) { 67 | let x = ix * segW - width / 2; 68 | 69 | position[i * 3 + u] = x * uDir; 70 | position[i * 3 + v] = y * vDir; 71 | position[i * 3 + w] = depth / 2; 72 | 73 | normal[i * 3 + u] = 0; 74 | normal[i * 3 + v] = 0; 75 | normal[i * 3 + w] = depth >= 0 ? 1 : -1; 76 | 77 | uv[i * 2] = ix / wSegs; 78 | uv[i * 2 + 1] = 1 - iy / hSegs; 79 | 80 | if (iy === hSegs || ix === wSegs) continue; 81 | let a = io + ix + iy * (wSegs + 1); 82 | let b = io + ix + (iy + 1) * (wSegs + 1); 83 | let c = io + ix + (iy + 1) * (wSegs + 1) + 1; 84 | let d = io + ix + iy * (wSegs + 1) + 1; 85 | 86 | index[ii * 6] = a; 87 | index[ii * 6 + 1] = b; 88 | index[ii * 6 + 2] = d; 89 | index[ii * 6 + 3] = b; 90 | index[ii * 6 + 4] = c; 91 | index[ii * 6 + 5] = d; 92 | ii++; 93 | } 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/extras/Shadow.ts: -------------------------------------------------------------------------------- 1 | import { Camera } from '../core/Camera'; 2 | import { Program } from '../core/Program'; 3 | import { RenderTarget, RenderTargetOptions } from '../core/RenderTarget'; 4 | import { OGLRenderingContext } from '../core/Renderer'; 5 | import { Mesh } from '../core/Mesh'; 6 | 7 | export class Shadow { 8 | gl: OGLRenderingContext; 9 | light: Camera; 10 | target: RenderTarget; 11 | depthProgram: Program; 12 | castMeshes: Mesh[]; 13 | 14 | constructor(gl: OGLRenderingContext, { light = new Camera(gl), width = 1024, height = width }) { 15 | this.gl = gl; 16 | 17 | this.light = light; 18 | 19 | this.target = new RenderTarget(gl, { width, height }); 20 | 21 | this.depthProgram = new Program(gl, { 22 | vertex: defaultVertex, 23 | fragment: defaultFragment, 24 | cullFace: null, 25 | }); 26 | 27 | this.castMeshes = []; 28 | } 29 | 30 | add({ 31 | mesh, 32 | receive = true, 33 | cast = true, 34 | vertex = defaultVertex, 35 | fragment = defaultFragment, 36 | uniformProjection = 'shadowProjectionMatrix', 37 | uniformView = 'shadowViewMatrix', 38 | uniformTexture = 'tShadow', 39 | }) { 40 | // Add uniforms to existing program 41 | if (receive && !mesh.program.uniforms[uniformProjection]) { 42 | mesh.program.uniforms[uniformProjection] = { value: this.light.projectionMatrix }; 43 | mesh.program.uniforms[uniformView] = { value: this.light.viewMatrix }; 44 | mesh.program.uniforms[uniformTexture] = { value: this.target.texture }; 45 | } 46 | 47 | if (!cast) return; 48 | this.castMeshes.push(mesh); 49 | 50 | // Store program for when switching between depth override 51 | mesh.colorProgram = mesh.program; 52 | 53 | // Check if depth program already attached 54 | if (mesh.depthProgram) return; 55 | 56 | // Use global depth override if nothing custom passed in 57 | if (vertex === defaultVertex && fragment === defaultFragment) { 58 | mesh.depthProgram = this.depthProgram; 59 | return; 60 | } 61 | 62 | // Create custom override program 63 | mesh.depthProgram = new Program(this.gl, { 64 | vertex, 65 | fragment, 66 | cullFace: null, 67 | }); 68 | } 69 | 70 | render({ scene }) { 71 | // For depth render, replace program with depth override. 72 | // Hide meshes not casting shadows. 73 | scene.traverse((node) => { 74 | if (!node.draw) return; 75 | if (!!~this.castMeshes.indexOf(node)) { 76 | node.program = node.depthProgram; 77 | } else { 78 | node.isForceVisibility = node.visible; 79 | node.visible = false; 80 | } 81 | }); 82 | 83 | // Render the depth shadow map using the light as the camera 84 | this.gl.renderer.render({ 85 | scene, 86 | camera: this.light, 87 | target: this.target, 88 | }); 89 | 90 | // Then switch the program back to the normal one 91 | scene.traverse((node) => { 92 | if (!node.draw) return; 93 | if (!!~this.castMeshes.indexOf(node)) { 94 | node.program = node.colorProgram; 95 | } else { 96 | node.visible = node.isForceVisibility; 97 | } 98 | }); 99 | } 100 | } 101 | 102 | const defaultVertex = /* glsl */ ` 103 | attribute vec3 position; 104 | attribute vec2 uv; 105 | 106 | uniform mat4 modelViewMatrix; 107 | uniform mat4 projectionMatrix; 108 | 109 | void main() { 110 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); 111 | } 112 | `; 113 | 114 | const defaultFragment = /* glsl */ ` 115 | precision highp float; 116 | 117 | vec4 packRGBA (float v) { 118 | vec4 pack = fract(vec4(1.0, 255.0, 65025.0, 16581375.0) * v); 119 | pack -= pack.yzww * vec2(1.0 / 255.0, 0.0).xxxy; 120 | return pack; 121 | } 122 | 123 | void main() { 124 | gl_FragColor = packRGBA(gl_FragCoord.z); 125 | } 126 | `; 127 | -------------------------------------------------------------------------------- /src/extras/Sphere.ts: -------------------------------------------------------------------------------- 1 | import { Geometry, AttributeMap } from '../core/Geometry'; 2 | import { Vec3 } from '../math/Vec3'; 3 | import { OGLRenderingContext } from '../core/Renderer'; 4 | 5 | export type SphereOptions = { 6 | radius: number; 7 | widthSegments: number; 8 | heightSegments: number; 9 | phiStart: number; 10 | phiLength: number; 11 | thetaStart: number; 12 | thetaLength: number; 13 | attributes: AttributeMap; 14 | }; 15 | 16 | export class Sphere extends Geometry { 17 | constructor( 18 | gl: OGLRenderingContext, 19 | { 20 | radius = 0.5, 21 | widthSegments = 16, 22 | heightSegments = Math.ceil(widthSegments * 0.5), 23 | phiStart = 0, 24 | phiLength = Math.PI * 2, 25 | thetaStart = 0, 26 | thetaLength = Math.PI, 27 | attributes = {}, 28 | }: Partial = {} 29 | ) { 30 | const wSegs = widthSegments; 31 | const hSegs = heightSegments; 32 | const pStart = phiStart; 33 | const pLength = phiLength; 34 | const tStart = thetaStart; 35 | const tLength = thetaLength; 36 | 37 | const num = (wSegs + 1) * (hSegs + 1); 38 | const numIndices = wSegs * hSegs * 6; 39 | 40 | const position = new Float32Array(num * 3); 41 | const normal = new Float32Array(num * 3); 42 | const uv = new Float32Array(num * 2); 43 | const index = num > 65536 ? new Uint32Array(numIndices) : new Uint16Array(numIndices); 44 | 45 | let i = 0; 46 | let iv = 0; 47 | let ii = 0; 48 | let te = tStart + tLength; 49 | const grid = []; 50 | 51 | let n = new Vec3(); 52 | 53 | for (let iy = 0; iy <= hSegs; iy++) { 54 | let vRow = []; 55 | let v = iy / hSegs; 56 | for (let ix = 0; ix <= wSegs; ix++, i++) { 57 | let u = ix / wSegs; 58 | let x = -radius * Math.cos(pStart + u * pLength) * Math.sin(tStart + v * tLength); 59 | let y = radius * Math.cos(tStart + v * tLength); 60 | let z = radius * Math.sin(pStart + u * pLength) * Math.sin(tStart + v * tLength); 61 | 62 | position[i * 3] = x; 63 | position[i * 3 + 1] = y; 64 | position[i * 3 + 2] = z; 65 | 66 | n.set(x, y, z).normalize(); 67 | normal[i * 3] = n.x; 68 | normal[i * 3 + 1] = n.y; 69 | normal[i * 3 + 2] = n.z; 70 | 71 | uv[i * 2] = u; 72 | uv[i * 2 + 1] = 1 - v; 73 | 74 | vRow.push(iv++); 75 | } 76 | 77 | grid.push(vRow); 78 | } 79 | 80 | for (let iy = 0; iy < hSegs; iy++) { 81 | for (let ix = 0; ix < wSegs; ix++) { 82 | let a = grid[iy][ix + 1]; 83 | let b = grid[iy][ix]; 84 | let c = grid[iy + 1][ix]; 85 | let d = grid[iy + 1][ix + 1]; 86 | 87 | if (iy !== 0 || tStart > 0) { 88 | index[ii * 3] = a; 89 | index[ii * 3 + 1] = b; 90 | index[ii * 3 + 2] = d; 91 | ii++; 92 | } 93 | if (iy !== hSegs - 1 || te < Math.PI) { 94 | index[ii * 3] = b; 95 | index[ii * 3 + 1] = c; 96 | index[ii * 3 + 2] = d; 97 | ii++; 98 | } 99 | } 100 | } 101 | 102 | Object.assign(attributes, { 103 | position: { size: 3, data: position }, 104 | normal: { size: 3, data: normal }, 105 | uv: { size: 2, data: uv }, 106 | index: { data: index }, 107 | }); 108 | 109 | super(gl, attributes); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/extras/Torus.ts: -------------------------------------------------------------------------------- 1 | // https://github.com/mrdoob/three.js/blob/master/src/geometries/TorusGeometry.js 2 | 3 | import { Geometry } from '../core/Geometry'; 4 | import { Vec3 } from '../math/Vec3'; 5 | 6 | export class Torus extends Geometry { 7 | constructor(gl, { radius = 0.5, tube = 0.2, radialSegments = 8, tubularSegments = 6, arc = Math.PI * 2, attributes = {} } = {}) { 8 | const num = (radialSegments + 1) * (tubularSegments + 1); 9 | const numIndices = radialSegments * tubularSegments * 6; 10 | 11 | const vertices = new Float32Array(num * 3); 12 | const normals = new Float32Array(num * 3); 13 | const uvs = new Float32Array(num * 2); 14 | const indices = num > 65536 ? new Uint32Array(numIndices) : new Uint16Array(numIndices); 15 | 16 | const center = new Vec3(); 17 | const vertex = new Vec3(); 18 | const normal = new Vec3(); 19 | 20 | // generate vertices, normals and uvs 21 | 22 | let idx = 0; 23 | for (let j = 0; j <= radialSegments; j++) { 24 | for (let i = 0; i <= tubularSegments; i++, idx++) { 25 | const u = (i / tubularSegments) * arc; 26 | const v = (j / radialSegments) * Math.PI * 2; 27 | 28 | // vertex 29 | 30 | vertex.x = (radius + tube * Math.cos(v)) * Math.cos(u); 31 | vertex.y = (radius + tube * Math.cos(v)) * Math.sin(u); 32 | vertex.z = tube * Math.sin(v); 33 | 34 | vertices.set([vertex.x, vertex.y, vertex.z], idx * 3); 35 | 36 | // normal 37 | 38 | center.x = radius * Math.cos(u); 39 | center.y = radius * Math.sin(u); 40 | normal.sub(vertex, center).normalize(); 41 | 42 | normals.set([normal.x, normal.y, normal.z], idx * 3); 43 | 44 | // uv 45 | 46 | uvs.set([i / tubularSegments, j / radialSegments], idx * 2); 47 | } 48 | } 49 | 50 | // generate indices 51 | 52 | idx = 0; 53 | for (let j = 1; j <= radialSegments; j++) { 54 | for (let i = 1; i <= tubularSegments; i++, idx++) { 55 | // indices 56 | 57 | const a = (tubularSegments + 1) * j + i - 1; 58 | const b = (tubularSegments + 1) * (j - 1) + i - 1; 59 | const c = (tubularSegments + 1) * (j - 1) + i; 60 | const d = (tubularSegments + 1) * j + i; 61 | 62 | // faces 63 | 64 | indices.set([a, b, d, b, c, d], idx * 6); 65 | } 66 | } 67 | 68 | Object.assign(attributes, { 69 | position: { size: 3, data: vertices }, 70 | normal: { size: 3, data: normals }, 71 | uv: { size: 2, data: uvs }, 72 | index: { data: indices }, 73 | }); 74 | 75 | super(gl, attributes); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/extras/Triangle.ts: -------------------------------------------------------------------------------- 1 | import { Geometry } from '../core/Geometry'; 2 | 3 | export class Triangle extends Geometry { 4 | constructor(gl, { attributes = {} } = {}) { 5 | // position uv 6 | // (-1, 3) (0, 2) 7 | // |\ |\ 8 | // |__\(1, 1) |__\(1, 1) 9 | // |__|_\ |__|_\ 10 | // (-1, -1) (3, -1) (0, 0) (2, 0) 11 | 12 | Object.assign(attributes, { 13 | position: { size: 2, data: new Float32Array([-1, -1, 3, -1, -1, 3]) }, 14 | uv: { size: 2, data: new Float32Array([0, 0, 2, 0, 0, 2]) }, 15 | }); 16 | 17 | super(gl, attributes); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | // Core 2 | export * from './core/Geometry'; 3 | export * from './core/Program'; 4 | export * from './core/Renderer'; 5 | export * from './core/Camera'; 6 | export * from './core/Transform'; 7 | export * from './core/Mesh'; 8 | export * from './core/Texture'; 9 | export * from './core/RenderTarget'; 10 | 11 | // Maths 12 | export * from './math/Color'; 13 | export * from './math/Euler'; 14 | export * from './math/Mat3'; 15 | export * from './math/Mat4'; 16 | export * from './math/Quat'; 17 | export * from './math/Vec2'; 18 | export * from './math/Vec3'; 19 | export * from './math/Vec4'; 20 | 21 | // Extras 22 | export * from './extras/Plane'; 23 | export * from './extras/Box'; 24 | export * from './extras/Sphere'; 25 | export * from './extras/Cylinder'; 26 | export * from './extras/Triangle'; 27 | export * from './extras/Torus'; 28 | 29 | export * from './extras/Orbit'; 30 | export * from './extras/Raycast'; 31 | export * from './extras/Curve'; 32 | export * from './extras/Post'; 33 | export * from './extras/Skin'; 34 | export * from './extras/Animation'; 35 | export * from './extras/Text'; 36 | export * from './extras/NormalProgram'; 37 | export * from './extras/Flowmap'; 38 | export * from './extras/GPGPU'; 39 | export * from './extras/Polyline'; 40 | export * from './extras/Shadow'; 41 | export * from './extras/KTXTexture'; 42 | export * from './extras/TextureLoader'; 43 | export * from './extras/GLTFLoader'; 44 | export * from './extras/GLTFAnimation'; 45 | export * from './extras/GLTFSkin'; 46 | -------------------------------------------------------------------------------- /src/math/Color.ts: -------------------------------------------------------------------------------- 1 | import * as ColorFunc from './functions/ColorFunc'; 2 | 3 | // Color stored as an array of RGB decimal values (between 0 > 1) 4 | // Constructor and set method accept following formats: 5 | // new Color() - Empty (defaults to black) 6 | // new Color([0.2, 0.4, 1.0]) - Decimal Array (or another Color instance) 7 | // new Color(0.7, 0.0, 0.1) - Decimal RGB values 8 | // new Color('#ff0000') - Hex string 9 | // new Color('#ccc') - Short-hand Hex string 10 | // new Color(0x4f27e8) - Number 11 | // new Color('red') - Color name string (short list in ColorFunc.js) 12 | 13 | export class Color extends Array { 14 | constructor(); 15 | constructor(color: [number, number, number]); 16 | constructor(color: number, g: number, b: number); 17 | constructor(color: string); 18 | constructor(color: ColorFunc.ColorNames); 19 | constructor(color: number); 20 | constructor(color: any = null) { 21 | if (Array.isArray(color)) { 22 | super(...color); 23 | } else { 24 | super(...ColorFunc.parseColor(...arguments)); 25 | } 26 | } 27 | 28 | get r() { 29 | return this[0]; 30 | } 31 | 32 | get g() { 33 | return this[1]; 34 | } 35 | 36 | get b() { 37 | return this[2]; 38 | } 39 | 40 | set r(v) { 41 | this[0] = v; 42 | } 43 | 44 | set g(v) { 45 | this[1] = v; 46 | } 47 | 48 | set b(v) { 49 | this[2] = v; 50 | } 51 | 52 | set(color) { 53 | if (Array.isArray(color)) return this.copy(color); 54 | return this.copy(ColorFunc.parseColor(...arguments)); 55 | } 56 | 57 | copy(v) { 58 | this[0] = v[0]; 59 | this[1] = v[1]; 60 | this[2] = v[2]; 61 | return this; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/math/Euler.ts: -------------------------------------------------------------------------------- 1 | import * as EulerFunc from './functions/EulerFunc'; 2 | import { Mat4 } from './Mat4'; 3 | 4 | const tmpMat4 = new Mat4(); 5 | 6 | export class Euler extends Array { 7 | onChange: () => void; 8 | order: string; 9 | constructor(x = 0, y = x, z = x, order = 'YXZ') { 10 | super(x, y, z); 11 | this.order = order; 12 | this.onChange = () => {}; 13 | return this; 14 | } 15 | 16 | get x() { 17 | return this[0]; 18 | } 19 | 20 | get y() { 21 | return this[1]; 22 | } 23 | 24 | get z() { 25 | return this[2]; 26 | } 27 | 28 | set x(v) { 29 | this[0] = v; 30 | this.onChange(); 31 | } 32 | 33 | set y(v) { 34 | this[1] = v; 35 | this.onChange(); 36 | } 37 | 38 | set z(v) { 39 | this[2] = v; 40 | this.onChange(); 41 | } 42 | 43 | set(x, y = x, z = x) { 44 | if (x.length) return this.copy(x); 45 | this[0] = x; 46 | this[1] = y; 47 | this[2] = z; 48 | this.onChange(); 49 | return this; 50 | } 51 | 52 | copy(v) { 53 | this[0] = v[0]; 54 | this[1] = v[1]; 55 | this[2] = v[2]; 56 | this.onChange(); 57 | return this; 58 | } 59 | 60 | reorder(order) { 61 | this.order = order; 62 | this.onChange(); 63 | return this; 64 | } 65 | 66 | fromRotationMatrix(m, order = this.order) { 67 | EulerFunc.fromRotationMatrix(this, m, order); 68 | return this; 69 | } 70 | 71 | fromQuaternion(q, order = this.order) { 72 | tmpMat4.fromQuaternion(q); 73 | return this.fromRotationMatrix(tmpMat4, order); 74 | } 75 | 76 | toArray(a = [], o = 0) { 77 | a[o] = this[0]; 78 | a[o + 1] = this[1]; 79 | a[o + 2] = this[2]; 80 | return a; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/math/Mat3.ts: -------------------------------------------------------------------------------- 1 | import * as Mat3Func from './functions/Mat3Func'; 2 | 3 | export class Mat3 extends Array { 4 | constructor(m00 = 1, m01 = 0, m02 = 0, m10 = 0, m11 = 1, m12 = 0, m20 = 0, m21 = 0, m22 = 1) { 5 | super(m00, m01, m02, m10, m11, m12, m20, m21, m22); 6 | return this; 7 | } 8 | 9 | set(m00, m01, m02, m10, m11, m12, m20, m21, m22) { 10 | if (m00.length) return this.copy(m00); 11 | Mat3Func.set(this, m00, m01, m02, m10, m11, m12, m20, m21, m22); 12 | return this; 13 | } 14 | 15 | translate(v, m = this) { 16 | Mat3Func.translate(this, m, v); 17 | return this; 18 | } 19 | 20 | rotate(v, m = this) { 21 | Mat3Func.rotate(this, m, v); 22 | return this; 23 | } 24 | 25 | scale(v, m = this) { 26 | Mat3Func.scale(this, m, v); 27 | return this; 28 | } 29 | 30 | multiply(ma, mb) { 31 | if (mb) { 32 | Mat3Func.multiply(this, ma, mb); 33 | } else { 34 | Mat3Func.multiply(this, this, ma); 35 | } 36 | return this; 37 | } 38 | 39 | identity() { 40 | Mat3Func.identity(this); 41 | return this; 42 | } 43 | 44 | copy(m) { 45 | Mat3Func.copy(this, m); 46 | return this; 47 | } 48 | 49 | fromMatrix4(m) { 50 | Mat3Func.fromMat4(this, m); 51 | return this; 52 | } 53 | 54 | fromQuaternion(q) { 55 | Mat3Func.fromQuat(this, q); 56 | return this; 57 | } 58 | 59 | fromBasis(vec3a, vec3b, vec3c) { 60 | this.set(vec3a[0], vec3a[1], vec3a[2], vec3b[0], vec3b[1], vec3b[2], vec3c[0], vec3c[1], vec3c[2]); 61 | return this; 62 | } 63 | 64 | inverse(m = this) { 65 | Mat3Func.invert(this, m); 66 | return this; 67 | } 68 | 69 | getNormalMatrix(m) { 70 | Mat3Func.normalFromMat4(this, m); 71 | return this; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/math/Quat.ts: -------------------------------------------------------------------------------- 1 | import * as QuatFunc from './functions/QuatFunc'; 2 | 3 | export class Quat extends Array { 4 | onChange: () => void; 5 | constructor(x = 0, y = 0, z = 0, w = 1) { 6 | super(x, y, z, w); 7 | this.onChange = () => {}; 8 | return this; 9 | } 10 | 11 | get x() { 12 | return this[0]; 13 | } 14 | 15 | get y() { 16 | return this[1]; 17 | } 18 | 19 | get z() { 20 | return this[2]; 21 | } 22 | 23 | get w() { 24 | return this[3]; 25 | } 26 | 27 | set x(v) { 28 | this[0] = v; 29 | this.onChange(); 30 | } 31 | 32 | set y(v) { 33 | this[1] = v; 34 | this.onChange(); 35 | } 36 | 37 | set z(v) { 38 | this[2] = v; 39 | this.onChange(); 40 | } 41 | 42 | set w(v) { 43 | this[3] = v; 44 | this.onChange(); 45 | } 46 | 47 | identity() { 48 | QuatFunc.identity(this); 49 | this.onChange(); 50 | return this; 51 | } 52 | 53 | set(x, y, z, w) { 54 | if (x.length) return this.copy(x); 55 | QuatFunc.set(this, x, y, z, w); 56 | this.onChange(); 57 | return this; 58 | } 59 | 60 | rotateX(a) { 61 | QuatFunc.rotateX(this, this, a); 62 | this.onChange(); 63 | return this; 64 | } 65 | 66 | rotateY(a) { 67 | QuatFunc.rotateY(this, this, a); 68 | this.onChange(); 69 | return this; 70 | } 71 | 72 | rotateZ(a) { 73 | QuatFunc.rotateZ(this, this, a); 74 | this.onChange(); 75 | return this; 76 | } 77 | 78 | inverse(q = this) { 79 | QuatFunc.invert(this, q); 80 | this.onChange(); 81 | return this; 82 | } 83 | 84 | conjugate(q = this) { 85 | QuatFunc.conjugate(this, q); 86 | this.onChange(); 87 | return this; 88 | } 89 | 90 | copy(q) { 91 | QuatFunc.copy(this, q); 92 | this.onChange(); 93 | return this; 94 | } 95 | 96 | normalize(q = this) { 97 | QuatFunc.normalize(this, q); 98 | this.onChange(); 99 | return this; 100 | } 101 | 102 | multiply(qA, qB) { 103 | if (qB) { 104 | QuatFunc.multiply(this, qA, qB); 105 | } else { 106 | QuatFunc.multiply(this, this, qA); 107 | } 108 | this.onChange(); 109 | return this; 110 | } 111 | 112 | dot(v) { 113 | return QuatFunc.dot(this, v); 114 | } 115 | 116 | fromMatrix3(matrix3) { 117 | QuatFunc.fromMat3(this, matrix3); 118 | this.onChange(); 119 | return this; 120 | } 121 | 122 | fromEuler(euler) { 123 | QuatFunc.fromEuler(this, euler, euler.order); 124 | return this; 125 | } 126 | 127 | fromAxisAngle(axis, a) { 128 | QuatFunc.setAxisAngle(this, axis, a); 129 | return this; 130 | } 131 | 132 | slerp(q, t) { 133 | QuatFunc.slerp(this, this, q, t); 134 | return this; 135 | } 136 | 137 | fromArray(a, o = 0) { 138 | this[0] = a[o]; 139 | this[1] = a[o + 1]; 140 | this[2] = a[o + 2]; 141 | this[3] = a[o + 3]; 142 | return this; 143 | } 144 | 145 | toArray(a = [], o = 0) { 146 | a[o] = this[0]; 147 | a[o + 1] = this[1]; 148 | a[o + 2] = this[2]; 149 | a[o + 3] = this[3]; 150 | return a; 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/math/Vec2.ts: -------------------------------------------------------------------------------- 1 | import * as Vec2Func from './functions/Vec2Func'; 2 | 3 | export class Vec2 extends Array { 4 | constructor(x = 0, y = x) { 5 | super(x, y); 6 | return this; 7 | } 8 | 9 | get x() { 10 | return this[0]; 11 | } 12 | 13 | get y() { 14 | return this[1]; 15 | } 16 | 17 | set x(v) { 18 | this[0] = v; 19 | } 20 | 21 | set y(v) { 22 | this[1] = v; 23 | } 24 | 25 | set(x, y = x) { 26 | if (x.length) return this.copy(x); 27 | Vec2Func.set(this, x, y); 28 | return this; 29 | } 30 | 31 | copy(v) { 32 | Vec2Func.copy(this, v); 33 | return this; 34 | } 35 | 36 | add(va, vb) { 37 | if (vb) Vec2Func.add(this, va, vb); 38 | else Vec2Func.add(this, this, va); 39 | return this; 40 | } 41 | 42 | sub(va, vb) { 43 | if (vb) Vec2Func.subtract(this, va, vb); 44 | else Vec2Func.subtract(this, this, va); 45 | return this; 46 | } 47 | 48 | multiply(v) { 49 | if (v.length) Vec2Func.multiply(this, this, v); 50 | else Vec2Func.scale(this, this, v); 51 | return this; 52 | } 53 | 54 | divide(v) { 55 | if (v.length) Vec2Func.divide(this, this, v); 56 | else Vec2Func.scale(this, this, 1 / v); 57 | return this; 58 | } 59 | 60 | inverse(v = this) { 61 | Vec2Func.inverse(this, v); 62 | return this; 63 | } 64 | 65 | // Can't use 'length' as Array.prototype uses it 66 | len() { 67 | return Vec2Func.length(this); 68 | } 69 | 70 | distance(v) { 71 | if (v) return Vec2Func.distance(this, v); 72 | else return Vec2Func.length(this); 73 | } 74 | 75 | squaredLen() { 76 | return this.squaredDistance(); 77 | } 78 | 79 | squaredDistance(v?) { 80 | if (v) return Vec2Func.squaredDistance(this, v); 81 | else return Vec2Func.squaredLength(this); 82 | } 83 | 84 | negate(v = this) { 85 | Vec2Func.negate(this, v); 86 | return this; 87 | } 88 | 89 | cross(va, vb) { 90 | if (vb) return Vec2Func.cross(va, vb); 91 | return Vec2Func.cross(this, va); 92 | } 93 | 94 | scale(v) { 95 | Vec2Func.scale(this, this, v); 96 | return this; 97 | } 98 | 99 | normalize() { 100 | Vec2Func.normalize(this, this); 101 | return this; 102 | } 103 | 104 | dot(v) { 105 | return Vec2Func.dot(this, v); 106 | } 107 | 108 | equals(v) { 109 | return Vec2Func.exactEquals(this, v); 110 | } 111 | 112 | applyMatrix3(mat3) { 113 | Vec2Func.transformMat3(this, this, mat3); 114 | return this; 115 | } 116 | 117 | applyMatrix4(mat4) { 118 | Vec2Func.transformMat4(this, this, mat4); 119 | return this; 120 | } 121 | 122 | lerp(v, a) { 123 | Vec2Func.lerp(this, this, v, a); 124 | } 125 | 126 | clone() { 127 | return new Vec2(this[0], this[1]); 128 | } 129 | 130 | fromArray(a, o = 0) { 131 | this[0] = a[o]; 132 | this[1] = a[o + 1]; 133 | return this; 134 | } 135 | 136 | toArray(a = [], o = 0) { 137 | a[o] = this[0]; 138 | a[o + 1] = this[1]; 139 | return a; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/math/Vec3.ts: -------------------------------------------------------------------------------- 1 | import * as Vec3Func from './functions/Vec3Func'; 2 | import { isArrayLike } from '../Guards'; 3 | export class Vec3 extends Array { 4 | constant: number; // TODO: only be used in Camera class 5 | constructor(x = 0, y = x, z = x) { 6 | super(x, y, z); 7 | return this; 8 | } 9 | 10 | get x() { 11 | return this[0]; 12 | } 13 | 14 | get y() { 15 | return this[1]; 16 | } 17 | 18 | get z() { 19 | return this[2]; 20 | } 21 | 22 | set x(v) { 23 | this[0] = v; 24 | } 25 | 26 | set y(v) { 27 | this[1] = v; 28 | } 29 | 30 | set z(v) { 31 | this[2] = v; 32 | } 33 | 34 | set(x: number | Array, y = x, z = x) { 35 | if (isArrayLike(x)) return this.copy(x); 36 | Vec3Func.set(this, x, y, z); 37 | return this; 38 | } 39 | 40 | copy(v) { 41 | Vec3Func.copy(this, v); 42 | return this; 43 | } 44 | 45 | add(va, vb?: Vec3) { 46 | if (vb) Vec3Func.add(this, va, vb); 47 | else Vec3Func.add(this, this, va); 48 | return this; 49 | } 50 | 51 | sub(va, vb?: Vec3) { 52 | if (vb) Vec3Func.subtract(this, va, vb); 53 | else Vec3Func.subtract(this, this, va); 54 | return this; 55 | } 56 | 57 | multiply(v) { 58 | if (v.length) Vec3Func.multiply(this, this, v); 59 | else Vec3Func.scale(this, this, v); 60 | return this; 61 | } 62 | 63 | divide(v) { 64 | if (v.length) Vec3Func.divide(this, this, v); 65 | else Vec3Func.scale(this, this, 1 / v); 66 | return this; 67 | } 68 | 69 | inverse(v = this) { 70 | Vec3Func.inverse(this, v); 71 | return this; 72 | } 73 | 74 | // Can't use 'length' as Array.prototype uses it 75 | len() { 76 | return Vec3Func.length(this); 77 | } 78 | 79 | distance(v?: Vec3) { 80 | if (v) return Vec3Func.distance(this, v); 81 | else return Vec3Func.length(this); 82 | } 83 | 84 | squaredLen() { 85 | return Vec3Func.squaredLength(this); 86 | } 87 | 88 | squaredDistance(v?: Vec3) { 89 | if (v) return Vec3Func.squaredDistance(this, v); 90 | else return Vec3Func.squaredLength(this); 91 | } 92 | 93 | negate(v = this) { 94 | Vec3Func.negate(this, v); 95 | return this; 96 | } 97 | 98 | cross(va, vb?) { 99 | if (vb) Vec3Func.cross(this, va, vb); 100 | else Vec3Func.cross(this, this, va); 101 | return this; 102 | } 103 | 104 | scale(v) { 105 | Vec3Func.scale(this, this, v); 106 | return this; 107 | } 108 | 109 | normalize() { 110 | Vec3Func.normalize(this, this); 111 | return this; 112 | } 113 | 114 | dot(v) { 115 | return Vec3Func.dot(this, v); 116 | } 117 | 118 | equals(v) { 119 | return Vec3Func.exactEquals(this, v); 120 | } 121 | 122 | applyMatrix4(mat4) { 123 | Vec3Func.transformMat4(this, this, mat4); 124 | return this; 125 | } 126 | 127 | scaleRotateMatrix4(mat4) { 128 | Vec3Func.scaleRotateMat4(this, this, mat4); 129 | return this; 130 | } 131 | 132 | applyQuaternion(q) { 133 | Vec3Func.transformQuat(this, this, q); 134 | return this; 135 | } 136 | 137 | angle(v) { 138 | return Vec3Func.angle(this, v); 139 | } 140 | 141 | lerp(v, t) { 142 | Vec3Func.lerp(this, this, v, t); 143 | return this; 144 | } 145 | 146 | clone() { 147 | return new Vec3(this[0], this[1], this[2]); 148 | } 149 | 150 | fromArray(a, o = 0) { 151 | this[0] = a[o]; 152 | this[1] = a[o + 1]; 153 | this[2] = a[o + 2]; 154 | return this; 155 | } 156 | 157 | toArray(a: Float32Array | Array = [], o = 0) { 158 | a[o] = this[0]; 159 | a[o + 1] = this[1]; 160 | a[o + 2] = this[2]; 161 | return a; 162 | } 163 | 164 | transformDirection(mat4) { 165 | const x = this[0]; 166 | const y = this[1]; 167 | const z = this[2]; 168 | 169 | this[0] = mat4[0] * x + mat4[4] * y + mat4[8] * z; 170 | this[1] = mat4[1] * x + mat4[5] * y + mat4[9] * z; 171 | this[2] = mat4[2] * x + mat4[6] * y + mat4[10] * z; 172 | 173 | return this.normalize(); 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/math/Vec4.ts: -------------------------------------------------------------------------------- 1 | import * as Vec4Func from './functions/Vec4Func'; 2 | 3 | export class Vec4 extends Array { 4 | constructor(x = 0, y = x, z = x, w = x) { 5 | super(x, y, z, w); 6 | return this; 7 | } 8 | 9 | get x() { 10 | return this[0]; 11 | } 12 | 13 | get y() { 14 | return this[1]; 15 | } 16 | 17 | get z() { 18 | return this[2]; 19 | } 20 | 21 | get w() { 22 | return this[3]; 23 | } 24 | 25 | set x(v) { 26 | this[0] = v; 27 | } 28 | 29 | set y(v) { 30 | this[1] = v; 31 | } 32 | 33 | set z(v) { 34 | this[2] = v; 35 | } 36 | 37 | set w(v) { 38 | this[3] = v; 39 | } 40 | 41 | set(x, y, z, w) { 42 | if (x.length) return this.copy(x); 43 | Vec4Func.set(this, x, y, z, w); 44 | return this; 45 | } 46 | 47 | copy(v) { 48 | Vec4Func.copy(this, v); 49 | return this; 50 | } 51 | 52 | normalize() { 53 | Vec4Func.normalize(this, this); 54 | return this; 55 | } 56 | 57 | fromArray(a, o = 0) { 58 | this[0] = a[o]; 59 | this[1] = a[o + 1]; 60 | this[2] = a[o + 2]; 61 | this[3] = a[o + 3]; 62 | return this; 63 | } 64 | 65 | toArray(a = [], o = 0) { 66 | a[o] = this[0]; 67 | a[o + 1] = this[1]; 68 | a[o + 2] = this[2]; 69 | a[o + 3] = this[3]; 70 | return a; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/math/functions/ColorFunc.ts: -------------------------------------------------------------------------------- 1 | const NAMES = { 2 | black: '#000000', 3 | white: '#ffffff', 4 | red: '#ff0000', 5 | green: '#00ff00', 6 | blue: '#0000ff', 7 | fuchsia: '#ff00ff', 8 | cyan: '#00ffff', 9 | yellow: '#ffff00', 10 | orange: '#ff8000', 11 | }; 12 | 13 | export type ColorNames = keyof typeof NAMES; 14 | 15 | export function hexToRGB(hex) { 16 | if (hex.length === 4) hex = hex[0] + hex[1] + hex[1] + hex[2] + hex[2] + hex[3] + hex[3]; 17 | const rgb = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); 18 | if (!rgb) console.warn(`Unable to convert hex string ${hex} to rgb values`); 19 | return [parseInt(rgb[1], 16) / 255, parseInt(rgb[2], 16) / 255, parseInt(rgb[3], 16) / 255]; 20 | } 21 | 22 | export function numberToRGB(num) { 23 | num = parseInt(num); 24 | return [((num >> 16) & 255) / 255, ((num >> 8) & 255) / 255, (num & 255) / 255]; 25 | } 26 | 27 | export function parseColor(color: any = null) { 28 | // Empty 29 | if (!color) return [0, 0, 0]; 30 | 31 | // Decimal 32 | if (arguments.length === 3) return arguments; 33 | 34 | // Number 35 | if (!isNaN(color)) return numberToRGB(color); 36 | 37 | // Hex 38 | if (color[0] === '#') return hexToRGB(color); 39 | 40 | // Names 41 | if (NAMES[color.toLowerCase()]) return hexToRGB(NAMES[color.toLowerCase()]); 42 | 43 | console.warn('Color format not recognised'); 44 | return [0, 0, 0]; 45 | } 46 | -------------------------------------------------------------------------------- /src/math/functions/EulerFunc.ts: -------------------------------------------------------------------------------- 1 | // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) 2 | export function fromRotationMatrix(out, m, order = 'YXZ') { 3 | if (order === 'XYZ') { 4 | out[1] = Math.asin(Math.min(Math.max(m[8], -1), 1)); 5 | if (Math.abs(m[8]) < 0.99999) { 6 | out[0] = Math.atan2(-m[9], m[10]); 7 | out[2] = Math.atan2(-m[4], m[0]); 8 | } else { 9 | out[0] = Math.atan2(m[6], m[5]); 10 | out[2] = 0; 11 | } 12 | } else if (order === 'YXZ') { 13 | out[0] = Math.asin(-Math.min(Math.max(m[9], -1), 1)); 14 | if (Math.abs(m[9]) < 0.99999) { 15 | out[1] = Math.atan2(m[8], m[10]); 16 | out[2] = Math.atan2(m[1], m[5]); 17 | } else { 18 | out[1] = Math.atan2(-m[2], m[0]); 19 | out[2] = 0; 20 | } 21 | } else if (order === 'ZXY') { 22 | out[0] = Math.asin(Math.min(Math.max(m[6], -1), 1)); 23 | if (Math.abs(m[6]) < 0.99999) { 24 | out[1] = Math.atan2(-m[2], m[10]); 25 | out[2] = Math.atan2(-m[4], m[5]); 26 | } else { 27 | out[1] = 0; 28 | out[2] = Math.atan2(m[1], m[0]); 29 | } 30 | } else if (order === 'ZYX') { 31 | out[1] = Math.asin(-Math.min(Math.max(m[2], -1), 1)); 32 | if (Math.abs(m[2]) < 0.99999) { 33 | out[0] = Math.atan2(m[6], m[10]); 34 | out[2] = Math.atan2(m[1], m[0]); 35 | } else { 36 | out[0] = 0; 37 | out[2] = Math.atan2(-m[4], m[5]); 38 | } 39 | } else if (order === 'YZX') { 40 | out[2] = Math.asin(Math.min(Math.max(m[1], -1), 1)); 41 | if (Math.abs(m[1]) < 0.99999) { 42 | out[0] = Math.atan2(-m[9], m[5]); 43 | out[1] = Math.atan2(-m[2], m[0]); 44 | } else { 45 | out[0] = 0; 46 | out[1] = Math.atan2(m[8], m[10]); 47 | } 48 | } else if (order === 'XZY') { 49 | out[2] = Math.asin(-Math.min(Math.max(m[4], -1), 1)); 50 | if (Math.abs(m[4]) < 0.99999) { 51 | out[0] = Math.atan2(m[6], m[5]); 52 | out[1] = Math.atan2(m[8], m[0]); 53 | } else { 54 | out[0] = Math.atan2(-m[9], m[10]); 55 | out[1] = 0; 56 | } 57 | } 58 | 59 | return out; 60 | } 61 | -------------------------------------------------------------------------------- /src/math/functions/Vec4Func.ts: -------------------------------------------------------------------------------- 1 | const EPSILON = 0.000001; 2 | 3 | /** 4 | * Copy the values from one vec4 to another 5 | * 6 | * @param {vec4} out the receiving vector 7 | * @param {vec4} a the source vector 8 | * @returns {vec4} out 9 | */ 10 | export function copy(out, a) { 11 | out[0] = a[0]; 12 | out[1] = a[1]; 13 | out[2] = a[2]; 14 | out[3] = a[3]; 15 | return out; 16 | } 17 | 18 | /** 19 | * Set the components of a vec4 to the given values 20 | * 21 | * @param {vec4} out the receiving vector 22 | * @param {Number} x X component 23 | * @param {Number} y Y component 24 | * @param {Number} z Z component 25 | * @param {Number} w W component 26 | * @returns {vec4} out 27 | */ 28 | export function set(out, x, y, z, w) { 29 | out[0] = x; 30 | out[1] = y; 31 | out[2] = z; 32 | out[3] = w; 33 | return out; 34 | } 35 | 36 | /** 37 | * Adds two vec4's 38 | * 39 | * @param {vec4} out the receiving vector 40 | * @param {vec4} a the first operand 41 | * @param {vec4} b the second operand 42 | * @returns {vec4} out 43 | */ 44 | export function add(out, a, b) { 45 | out[0] = a[0] + b[0]; 46 | out[1] = a[1] + b[1]; 47 | out[2] = a[2] + b[2]; 48 | out[3] = a[3] + b[3]; 49 | return out; 50 | } 51 | 52 | /** 53 | * Scales a vec4 by a scalar number 54 | * 55 | * @param {vec4} out the receiving vector 56 | * @param {vec4} a the vector to scale 57 | * @param {Number} b amount to scale the vector by 58 | * @returns {vec4} out 59 | */ 60 | export function scale(out, a, b) { 61 | out[0] = a[0] * b; 62 | out[1] = a[1] * b; 63 | out[2] = a[2] * b; 64 | out[3] = a[3] * b; 65 | return out; 66 | } 67 | 68 | /** 69 | * Calculates the length of a vec4 70 | * 71 | * @param {vec4} a vector to calculate length of 72 | * @returns {Number} length of a 73 | */ 74 | export function length(a) { 75 | let x = a[0]; 76 | let y = a[1]; 77 | let z = a[2]; 78 | let w = a[3]; 79 | return Math.sqrt(x * x + y * y + z * z + w * w); 80 | } 81 | 82 | /** 83 | * Normalize a vec4 84 | * 85 | * @param {vec4} out the receiving vector 86 | * @param {vec4} a vector to normalize 87 | * @returns {vec4} out 88 | */ 89 | export function normalize(out, a) { 90 | let x = a[0]; 91 | let y = a[1]; 92 | let z = a[2]; 93 | let w = a[3]; 94 | let len = x * x + y * y + z * z + w * w; 95 | if (len > 0) { 96 | len = 1 / Math.sqrt(len); 97 | } 98 | out[0] = x * len; 99 | out[1] = y * len; 100 | out[2] = z * len; 101 | out[3] = w * len; 102 | return out; 103 | } 104 | 105 | /** 106 | * Calculates the dot product of two vec4's 107 | * 108 | * @param {vec4} a the first operand 109 | * @param {vec4} b the second operand 110 | * @returns {Number} dot product of a and b 111 | */ 112 | export function dot(a, b) { 113 | return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]; 114 | } 115 | 116 | /** 117 | * Performs a linear interpolation between two vec4's 118 | * 119 | * @param {vec4} out the receiving vector 120 | * @param {vec4} a the first operand 121 | * @param {vec4} b the second operand 122 | * @param {Number} t interpolation amount between the two inputs 123 | * @returns {vec4} out 124 | */ 125 | export function lerp(out, a, b, t) { 126 | let ax = a[0]; 127 | let ay = a[1]; 128 | let az = a[2]; 129 | let aw = a[3]; 130 | out[0] = ax + t * (b[0] - ax); 131 | out[1] = ay + t * (b[1] - ay); 132 | out[2] = az + t * (b[2] - az); 133 | out[3] = aw + t * (b[3] - aw); 134 | return out; 135 | } 136 | -------------------------------------------------------------------------------- /tea.yaml: -------------------------------------------------------------------------------- 1 | # https://tea.xyz/what-is-this-file 2 | --- 3 | version: 1.0.0 4 | codeOwners: 5 | - '0x5763B690102Cd5167Ad247a598a13018B84fF77f' 6 | quorum: 1 7 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | const { resolve } = require('path'); 3 | const fs = require('fs'); 4 | 5 | export default ({ command, mode }) => { 6 | if (command === 'build') { 7 | return { 8 | // https://github.com/vitejs/vite/issues/3025 9 | base: 'http://github.nshen.net/ogl-typescript/dist/', 10 | build: { 11 | minify: false, 12 | emptyOutDir: true, 13 | assetsDir: 'chunks', 14 | rollupOptions: { 15 | input: getInput([ 16 | // Geometry 17 | 'triangle-screen-shader', 18 | 'draw-modes', 19 | 'indexed-vs-non-indexed', 20 | 'load-json', 21 | 'wireframe', 22 | 'base-primitives', 23 | 'particles', 24 | 'instancing', 25 | 'instancing-gpu-picking', 26 | 'polylines', 27 | 'curves', 28 | 'torus', 29 | 'load-gltf', 30 | 'compute-vertex-normal', 31 | 32 | // Scenes 33 | 'scene-graph', 34 | 'sort-transparency', 35 | 'frustum-culling', 36 | 37 | // Interaction 38 | 'orbit-controls', 39 | 'raycasting', 40 | 'mouse-flowmap', 41 | 42 | //Shading 43 | 'fog', 44 | 'textures', 45 | 'anisotropic', 46 | 'skydome', 47 | 'cube-map', 48 | 'normal-maps', 49 | 'flat-shading-matcap', 50 | 'wireframe-shader', 51 | 'msdf-text', 52 | 'point-lighting', 53 | 'pbr', 54 | 'compressed-textures', 55 | 56 | // Frame Buffer 57 | 'render-to-texture', 58 | 'post-fxaa', 59 | 'post-bloom', 60 | 'mrt', 61 | 'shadow-maps', 62 | 'post-fluid-distortion', 63 | 'gpgpu-particles', 64 | 65 | // Animation 66 | 'skinning', 67 | 68 | // Performance 69 | 'high-mesh-count', 70 | ]), 71 | 72 | output: { 73 | // https://github.com/vitejs/vite/issues/378#issuecomment-716717258 74 | // entryFileNames: `examples/[name]/index.js`, // works 75 | // chunkFileNames: `chunks/[name].js`, 76 | // assetFileNames: `assets/[name].[ext]` 77 | }, 78 | }, 79 | }, 80 | plugins: [copyIndexPlugin()], 81 | }; 82 | } else { 83 | return { 84 | // dev specific config 85 | }; 86 | } 87 | }; 88 | 89 | function copyIndexPlugin() { 90 | return { 91 | name: 'copyIndexPlugin', 92 | async closeBundle() { 93 | fs.copyFileSync('./index.html', './dist/index.html'); 94 | }, 95 | }; 96 | } 97 | 98 | function getInput(arr) { 99 | let input = {}; 100 | arr.forEach((example) => { 101 | input[example] = resolve(__dirname, `examples/${example}/index.html`); 102 | }); 103 | return input; 104 | } 105 | --------------------------------------------------------------------------------