diff --git a/src/config-hot.ts b/src/config-hot.ts index 94e895d..85c2c0f 100644 --- a/src/config-hot.ts +++ b/src/config-hot.ts @@ -1,7 +1,8 @@ export const RTINSPECTOR_MULTIPLE = false, RTINSPECTOR_CAPTURE_NAME: string | null = null, - // RTINSPECTOR_CAPTURE_NAME: string | null = 'CameraEntity/cameraTarget', + // RTINSPECTOR_CAPTURE_NAME: string | null = 'light1/shadowMap', + // RTINSPECTOR_CAPTURE_NAME: string | null = 'EnvironmentMap/swap0', RTINSPECTOR_CAPTURE_INDEX = 0, COMPONENT_UPDATE_BREAKPOINT: string | null = null, // COMPONENT_UPDATE_BREAKPOINT: string | null = 'Bloom/quadDup3', diff --git a/src/config.ts b/src/config.ts index 482f91c..ac7c080 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,6 +1,7 @@ export const RANDOM_RESOLUTION = [ 64, 64 ], STATIC_RANDOM_RESOLUTION = [ 2048, 2048 ], + CUBEMAP_RESOLUTION = [ 512, 512 ], AO_RESOLUTION_RATIO = 1.0, RESOLUTION = [ 1280, 720 ], MUSIC_BPM = 180, diff --git a/src/entities/CameraEntity.ts b/src/entities/CameraEntity.ts index 2a2bca3..ef54a28 100644 --- a/src/entities/CameraEntity.ts +++ b/src/entities/CameraEntity.ts @@ -58,6 +58,7 @@ export class CameraEntity { near: 0.1, far: 20.0, name: 'CameraEntity/camera', + materialTag: 'deferred', } ); this.__entity.components.push( this.__camera ); diff --git a/src/entities/Cube.ts b/src/entities/Cube.ts index 23dcb1a..aa1b7b6 100644 --- a/src/entities/Cube.ts +++ b/src/entities/Cube.ts @@ -2,16 +2,17 @@ import { Quaternion, Vector3 } from '@fms-cat/experimental'; import { Mesh } from '../heck/components/Mesh'; import { Entity } from '../heck/Entity'; import { Geometry } from '../heck/Geometry'; -import { Material } from '../heck/Material'; +import { Material, MaterialMap } from '../heck/Material'; import cubeVert from '../shaders/cube.vert'; import cubeFrag from '../shaders/cube.frag'; +import depthFrag from '../shaders/depth.frag'; import { genCube } from '../geometries/genCube'; import { Lambda } from '../heck/components/Lambda'; export class Cube { public mesh: Mesh; public geometry: Geometry; - public material: Material; + public materials: MaterialMap<'deferred' | 'shadow'>; public entity: Entity; public constructor() { @@ -29,11 +30,11 @@ export class Cube { this.entity.transform.scale = this.entity.transform.scale.scale( 0.8 ); this.geometry = this.__createGeometry(); - this.material = this.__createMaterial(); + this.materials = this.__createMaterials(); this.mesh = new Mesh( { geometry: this.geometry, - material: this.material, + materials: this.materials, name: process.env.DEV && 'Cube/mesh', } ); this.entity.components.push( this.mesh ); @@ -42,6 +43,10 @@ export class Cube { onUpdate: ( { time } ) => { this.entity.transform.rotation = rot0.multiply( Quaternion.fromAxisAngle( new Vector3( [ 0.0, 1.0, 0.0 ] ), time ) + ).multiply( + Quaternion.fromAxisAngle( new Vector3( [ 1.0, 0.0, 0.0 ] ), 1.0 ) + ).multiply( + Quaternion.fromAxisAngle( new Vector3( [ 0.0, 0.0, 1.0 ] ), 1.0 ) ); }, visible: false, @@ -64,10 +69,14 @@ export class Cube { return geometry; } - private __createMaterial(): Material { - const material = new Material( cubeVert, cubeFrag ); + private __createMaterials(): MaterialMap<'deferred' | 'shadow'> { + const deferred = new Material( + cubeVert, + cubeFrag, + { defines: { 'DEFERRED': 'true' } }, + ); - material.addUniform( 'inflate', '1f', 0.01 ); + const shadow = new Material( cubeVert, depthFrag ); if ( process.env.DEV ) { if ( module.hot ) { @@ -77,12 +86,13 @@ export class Cube { '../shaders/cube.frag', ], () => { - material.replaceShader( cubeVert, cubeFrag ); + deferred.replaceShader( cubeVert, cubeFrag ); + shadow.replaceShader( cubeVert, depthFrag ); }, ); } } - return material; + return { deferred, shadow }; } } diff --git a/src/entities/CubemapCameraEntity.ts b/src/entities/CubemapCameraEntity.ts new file mode 100644 index 0000000..9fe8d5b --- /dev/null +++ b/src/entities/CubemapCameraEntity.ts @@ -0,0 +1,39 @@ +import { Entity } from '../heck/Entity'; +import { LightEntity } from './LightEntity'; +import { PerspectiveCamera } from '../heck/components/PerspectiveCamera'; +import { CubemapRenderTarget } from '../heck/CubemapRenderTarget'; +import { CubemapCamera } from '../heck/components/CubemapCamera'; +import { CUBEMAP_RESOLUTION } from '../config'; + +export interface CubemapCameraEntityOptions { + root: Entity; + lights: LightEntity[]; +} + +export class CubemapCameraEntity { + public root: Entity; + public camera: PerspectiveCamera; + public readonly target: CubemapRenderTarget; + public entity: Entity; + + public constructor( options: CubemapCameraEntityOptions ) { + this.root = options.root; + + this.entity = new Entity(); + + this.target = new CubemapRenderTarget( { + width: CUBEMAP_RESOLUTION[ 0 ], + height: CUBEMAP_RESOLUTION[ 1 ], + } ); + + this.camera = new CubemapCamera( { + scene: this.root, + renderTarget: this.target, + near: 0.1, + far: 20.0, + name: 'CubemapCameraEntity/camera', + materialTag: 'forward', + } ); + this.entity.components.push( this.camera ); + } +} diff --git a/src/entities/EnvironmentMap.ts b/src/entities/EnvironmentMap.ts index 9109b6c..e693a41 100644 --- a/src/entities/EnvironmentMap.ts +++ b/src/entities/EnvironmentMap.ts @@ -7,6 +7,7 @@ import quadVert from '../shaders/quad.vert'; import { BufferRenderTarget } from '../heck/BufferRenderTarget'; import { Swap, Xorshift } from '@fms-cat/experimental'; import { Lambda } from '../heck/components/Lambda'; +import { CubemapRenderTarget } from '../heck/CubemapRenderTarget'; const WIDTH = 1024; const HEIGHT = 512; @@ -20,7 +21,9 @@ export class EnvironmentMap { return this.swap.o.texture; } - public constructor() { + public constructor( { cubemap }: { + cubemap: CubemapRenderTarget; + } ) { this.entity = new Entity(); this.entity.visible = false; @@ -47,6 +50,7 @@ export class EnvironmentMap { ); material.addUniform( 'uniformSeed', '4f', rng.gen(), rng.gen(), rng.gen(), rng.gen() ); material.addUniformTexture( 'sampler0', this.swap.i.texture ); + material.addUniformCubemap( 'samplerCubemap', cubemap.texture ); if ( process.env.DEV ) { if ( module.hot ) { diff --git a/src/entities/FlickyParticles.ts b/src/entities/FlickyParticles.ts index 1d1270d..f178b18 100644 --- a/src/entities/FlickyParticles.ts +++ b/src/entities/FlickyParticles.ts @@ -2,7 +2,7 @@ import { Entity } from '../heck/Entity'; import { GPUParticles } from './GPUParticles'; import { Geometry } from '../heck/Geometry'; import { InstancedGeometry } from '../heck/InstancedGeometry'; -import { Material } from '../heck/Material'; +import { Material, MaterialMap } from '../heck/Material'; import quadVert from '../shaders/quad.vert'; import flickyParticleComputeFrag from '../shaders/flicky-particles-compute.frag'; import flickyParticleRenderFrag from '../shaders/flicky-particles-render.frag'; @@ -16,24 +16,16 @@ const PARTICLES = PARTICLES_SQRT * PARTICLES_SQRT; export class FlickyParticles { public get entity(): Entity { - return this.__gpuParticles.entity; + return this.gpuParticles.entity; } - private __gpuParticles: GPUParticles; - - public get materialCompute(): Material { - return this.__gpuParticles.materialCompute; - } - - public get materialRender(): Material { - return this.__gpuParticles.materialRender; - } + public gpuParticles: GPUParticles; public constructor() { - this.__gpuParticles = new GPUParticles( { + this.gpuParticles = new GPUParticles( { materialCompute: this.__createMaterialCompute(), geometryRender: this.__createGeometryRender(), - materialRender: this.__createMaterialRender(), + materialsRender: this.__createMaterialsRender(), computeWidth: PARTICLES_SQRT, computeHeight: PARTICLES_SQRT, computeNumBuffers: 1, @@ -99,36 +91,44 @@ export class FlickyParticles { return geometry; } - private __createMaterialRender(): Material { - const material = new Material( + private __createMaterialsRender(): MaterialMap { + const forward = new Material( flickyParticleRenderVert, flickyParticleRenderFrag, + { defines: { 'FORWARD': 'true' } }, ); - material.addUniform( 'colorVar', '1f', 0.1 ); - material.addUniformTexture( 'samplerRandomStatic', randomTextureStatic.texture ); + forward.addUniformTexture( 'samplerRandomStatic', randomTextureStatic.texture ); + + const deferred = new Material( + flickyParticleRenderVert, + flickyParticleRenderFrag, + { defines: { 'DEFERRED': 'true' } }, + ); + deferred.addUniformTexture( 'samplerRandomStatic', randomTextureStatic.texture ); + + const shadow = new Material( + flickyParticleRenderVert, + flickyParticleRenderFrag, + { defines: { 'SHADOW': 'true' } }, + ); + shadow.addUniformTexture( 'samplerRandomStatic', randomTextureStatic.texture ); if ( process.env.DEV ) { if ( module.hot ) { - module.hot.accept( '../shaders/flicky-particles-render.vert', () => { - material.replaceShader( - flickyParticleRenderVert, - flickyParticleRenderFrag, - ); - } ); + module.hot.accept( + [ + '../shaders/flicky-particles-render.vert', + '../shaders/flicky-particles-render.frag', + ], + () => { + forward.replaceShader( flickyParticleRenderVert, flickyParticleRenderFrag ); + deferred.replaceShader( flickyParticleRenderVert, flickyParticleRenderFrag ); + shadow.replaceShader( flickyParticleRenderVert, flickyParticleRenderFrag ); + } + ); } } - if ( process.env.DEV ) { - if ( module.hot ) { - module.hot.accept( '../shaders/flicky-particles-render.frag', () => { - material.replaceShader( - flickyParticleRenderVert, - flickyParticleRenderFrag, - ); - } ); - } - } - - return material; + return { forward, deferred, shadow }; } } diff --git a/src/entities/GPUParticles.ts b/src/entities/GPUParticles.ts index 2fcecab..7521ad5 100644 --- a/src/entities/GPUParticles.ts +++ b/src/entities/GPUParticles.ts @@ -2,7 +2,7 @@ import { BufferRenderTarget, BufferRenderTargetOptions } from '../heck/BufferRen import { Entity } from '../heck/Entity'; import { Geometry } from '../heck/Geometry'; import { Lambda } from '../heck/components/Lambda'; -import { Material } from '../heck/Material'; +import { Material, MaterialMap, MaterialTag } from '../heck/Material'; import { Mesh } from '../heck/components/Mesh'; import { Quad } from '../heck/components/Quad'; import { Swap } from '@fms-cat/experimental'; @@ -11,7 +11,7 @@ import { gl } from '../globals/canvas'; export interface GPUParticlesOptions { materialCompute: Material; geometryRender: Geometry; - materialRender: Material; + materialsRender: Partial>; computeWidth: number; computeHeight: number; computeNumBuffers: number; @@ -39,8 +39,8 @@ export class GPUParticles { return this.__quadCompute.material; } - public get materialRender(): Material { - return this.__meshRender.material; + public get materialsRender(): Partial> { + return this.__meshRender.materials; } public constructor( options: GPUParticlesOptions ) { @@ -75,10 +75,13 @@ export class GPUParticles { `samplerCompute${ i }`, this.__swapCompute.i.getTexture( attachment ) ); - this.materialRender.addUniformTexture( - `samplerCompute${ i }`, - this.__swapCompute.o.getTexture( attachment ) - ); + + for ( const material of Object.values( this.materialsRender ) ) { + material?.addUniformTexture( + `samplerCompute${ i }`, + this.__swapCompute.o.getTexture( attachment ) + ); + } } this.__quadCompute.target = this.__swapCompute.o; @@ -98,15 +101,19 @@ export class GPUParticles { // -- render ----------------------------------------------------------------------------------- this.__meshRender = new Mesh( { geometry: options.geometryRender, - material: options.materialRender, + materials: options.materialsRender, name: process.env.DEV && `${ options.namePrefix }/meshRender`, } ); - options.materialRender.addUniform( - 'resolutionCompute', - '2f', - options.computeWidth, - options.computeHeight - ); + + for ( const material of Object.values( options.materialsRender ) ) { + material?.addUniform( + 'resolutionCompute', + '2f', + options.computeWidth, + options.computeHeight + ); + } + this.__entity.components.push( this.__meshRender ); } } diff --git a/src/entities/LightEntity.ts b/src/entities/LightEntity.ts index efb407d..d33b4ce 100644 --- a/src/entities/LightEntity.ts +++ b/src/entities/LightEntity.ts @@ -1,11 +1,9 @@ import { BufferRenderTarget } from '../heck/BufferRenderTarget'; import { Entity } from '../heck/Entity'; -import { Lambda } from '../heck/components/Lambda'; import { Material } from '../heck/Material'; import { PerspectiveCamera } from '../heck/components/PerspectiveCamera'; import { Quad } from '../heck/components/Quad'; import { Swap } from '@fms-cat/experimental'; -import posToDepthFrag from '../shaders/pos-to-depth.frag'; import quadVert from '../shaders/quad.vert'; import shadowBlurFrag from '../shaders/shadow-blur.frag'; @@ -52,8 +50,8 @@ export class LightEntity { this.__entity = new Entity(); const swapOptions = { - width: options.shadowMapWidth || 1024, - height: options.shadowMapHeight || 1024 + width: options.shadowMapWidth ?? 1024, + height: options.shadowMapHeight ?? 1024 }; const swap = new Swap( @@ -68,9 +66,9 @@ export class LightEntity { ); // -- camera ----------------------------------------------------------------------------------- - const fov = options.shadowMapFov || 45.0; - const near = options.shadowMapNear || 0.1; - const far = options.shadowMapFar || 100.0; + const fov = options.shadowMapFov ?? 45.0; + const near = options.shadowMapNear ?? 0.1; + const far = options.shadowMapFar ?? 100.0; this.__shadowMapCamera = new PerspectiveCamera( { fov, @@ -79,44 +77,20 @@ export class LightEntity { renderTarget: swap.o, scene: this.__root, name: process.env.DEV && `${ options.namePrefix }/shadowMapCamera`, + materialTag: 'shadow', } ); this.__shadowMapCamera.clear = [ 1.0, 1.0, 1.0, 1.0 ]; this.__entity.components.push( this.__shadowMapCamera ); this.__shadowMap = new BufferRenderTarget( { - width: options.shadowMapWidth || 2048, - height: options.shadowMapHeight || 2048, + width: options.shadowMapWidth ?? 1024, + height: options.shadowMapHeight ?? 1024, name: process.env.DEV && `${ options.namePrefix }/shadowMap`, } ); swap.swap(); - // -- convert ---------------------------------------------------------------------------------- - const materialConvert = new Material( - quadVert, - posToDepthFrag - ); - - materialConvert.addUniformTexture( 'sampler0', swap.i.texture ); - - this.__entity.components.push( new Lambda( { - onUpdate: () => { - materialConvert.addUniform( 'cameraPos', '3f', ...this.entity.transform.position.elements ); - materialConvert.addUniform( 'cameraNearFar', '2f', this.camera.near, this.camera.far ); - }, - visible: false, - name: process.env.DEV && `${ options.namePrefix }/setCameraUniforms`, - } ) ); - - this.__entity.components.push( new Quad( { - target: swap.o, - material: materialConvert, - name: process.env.DEV && `${ options.namePrefix }/quadConvertPosToDepth`, - } ) ); - - swap.swap(); - - // -- blur --------------------------------------------------------------------------------------- + // -- blur ------------------------------------------------------------------------------------- for ( let i = 0; i < 2; i ++ ) { const material = new Material( quadVert, diff --git a/src/entities/Raymarcher.ts b/src/entities/Raymarcher.ts index 50dcd8c..f2a28ee 100644 --- a/src/entities/Raymarcher.ts +++ b/src/entities/Raymarcher.ts @@ -1,70 +1,63 @@ -import { GLCatTexture } from '@fms-cat/glcat-ts'; import { Mesh, MeshCull } from '../heck/components/Mesh'; import { TRIANGLE_STRIP_QUAD, Vector3 } from '@fms-cat/experimental'; import { gl, glCat } from '../globals/canvas'; import { Entity } from '../heck/Entity'; import { Geometry } from '../heck/Geometry'; -import { Material } from '../heck/Material'; +import { Material, MaterialMap } from '../heck/Material'; import quadVert from '../shaders/quad.vert'; import raymarcherFrag from '../shaders/raymarcher.frag'; import { Lambda } from '../heck/components/Lambda'; import { randomTexture, randomTextureStatic } from '../globals/randomTexture'; export class Raymarcher { - private __mesh: Mesh; - private __geometry: Geometry; - - private __material: Material; - - public get material(): Material { - return this.__material; - } - - private __entity: Entity; - - public get entity(): Entity { - return this.__entity; - } + public materials: MaterialMap<'deferred' | 'shadow'>; + public mesh: Mesh; + public geometry: Geometry; + public readonly entity: Entity; public constructor() { - this.__entity = new Entity(); - this.__entity.transform.position = new Vector3( [ 0.0, 0.0, 0.3 ] ); - this.__entity.transform.scale = new Vector3( [ 16.0, 9.0, 1.0 ] ).scale( 0.15 ); + this.entity = new Entity(); + this.entity.transform.position = new Vector3( [ 0.0, 0.0, 0.3 ] ); + this.entity.transform.scale = new Vector3( [ 16.0, 9.0, 1.0 ] ).scale( 0.15 ); - this.__geometry = this.__createGeoemtry(); - this.__material = this.__createMaterial(); + this.geometry = this.__createGeoemtry(); + this.materials = this.__createMaterials(); - this.__material.addUniform( 'range', '4f', -1.0, -1.0, 1.0, 1.0 ); + for ( const material of Object.values( this.materials ) ) { + material.addUniform( 'range', '4f', -1.0, -1.0, 1.0, 1.0 ); - this.__material.addUniformTexture( 'samplerRandom', randomTexture.texture ); - this.__material.addUniformTexture( 'samplerRandomStatic', randomTextureStatic.texture ); + material.addUniformTexture( 'samplerRandom', randomTexture.texture ); + material.addUniformTexture( 'samplerRandomStatic', randomTextureStatic.texture ); + } - this.__entity.components.push( new Lambda( { + this.entity.components.push( new Lambda( { onDraw: ( event ) => { - this.__material.addUniform( - 'cameraNearFar', - '2f', - event.camera.near, - event.camera.far - ); + for ( const material of Object.values( this.materials ) ) { + material.addUniform( + 'cameraNearFar', + '2f', + event.camera.near, + event.camera.far + ); - this.__material.addUniformVector( - 'inversePV', - 'Matrix4fv', - event.projectionMatrix.multiply( event.viewMatrix ).inverse!.elements - ); + material.addUniformVector( + 'inversePV', + 'Matrix4fv', + event.projectionMatrix.multiply( event.viewMatrix ).inverse!.elements + ); + } }, active: false, name: process.env.DEV && 'Raymarcher/setCameraUniforms', } ) ); - this.__mesh = new Mesh( { - geometry: this.__geometry, - material: this.__material, + this.mesh = new Mesh( { + geometry: this.geometry, + materials: this.materials, name: process.env.DEV && 'Raymarcher/mesh', } ); - this.__mesh.cull = MeshCull.None; - this.__entity.components.push( this.__mesh ); + this.mesh.cull = MeshCull.None; + this.entity.components.push( this.mesh ); } protected __createGeoemtry(): Geometry { @@ -84,17 +77,19 @@ export class Raymarcher { return geometry; } - protected __createMaterial(): Material { - const material = new Material( quadVert, raymarcherFrag ); + protected __createMaterials(): MaterialMap<'deferred' | 'shadow'> { + const deferred = new Material( quadVert, raymarcherFrag, { defines: { 'DEFERRED': 'true' } } ); + const shadow = new Material( quadVert, raymarcherFrag, { defines: { 'SHADOW': 'true' } } ); if ( process.env.DEV ) { if ( module.hot ) { module.hot.accept( '../shaders/raymarcher.frag', () => { - material.replaceShader( quadVert, raymarcherFrag ); + deferred.replaceShader( quadVert, raymarcherFrag ); + shadow.replaceShader( quadVert, raymarcherFrag ); } ); } } - return material; + return { deferred, shadow }; } } diff --git a/src/entities/Rings.ts b/src/entities/Rings.ts index 1b9bb14..75e8a96 100644 --- a/src/entities/Rings.ts +++ b/src/entities/Rings.ts @@ -4,39 +4,56 @@ import { Mesh } from '../heck/components/Mesh'; import { Entity } from '../heck/Entity'; import { Geometry } from '../heck/Geometry'; import { InstancedGeometry } from '../heck/InstancedGeometry'; -import { Material } from '../heck/Material'; +import { Material, MaterialMap } from '../heck/Material'; +import depthFrag from '../shaders/depth.frag'; import ringsVert from '../shaders/rings.vert'; import ringsFrag from '../shaders/rings.frag'; import { gl, glCat } from '../globals/canvas'; +import { Lambda } from '../heck/components/Lambda'; const PRIMCOUNT = 32; export class Rings { public mesh: Mesh; public geometry: Geometry; - public material: Material; + public materials: MaterialMap; public entity: Entity; public constructor() { this.entity = new Entity(); - this.entity.transform.rotation = Quaternion.fromAxisAngle( + const rot0 = Quaternion.fromAxisAngle( new Vector3( [ 1.0, 0.0, 0.0 ] ), 0.4, ).multiply( Quaternion.fromAxisAngle( new Vector3( [ 0.0, 0.0, 1.0 ] ), 0.4, ) ); + this.entity.transform.rotation = rot0; this.geometry = this.__createGeometry(); - this.material = this.__createMaterial(); + this.materials = this.__createMaterials(); this.mesh = new Mesh( { geometry: this.geometry, - material: this.material, + materials: this.materials, name: process.env.DEV && 'Rings/mesh', } ); this.entity.components.push( this.mesh ); + + this.entity.components.push( new Lambda( { + onUpdate: ( { time } ) => { + this.entity.transform.rotation = rot0.multiply( + Quaternion.fromAxisAngle( new Vector3( [ 0.0, 1.0, 0.0 ] ), time ) + ).multiply( + Quaternion.fromAxisAngle( new Vector3( [ 1.0, 0.0, 0.0 ] ), 1.0 ) + ).multiply( + Quaternion.fromAxisAngle( new Vector3( [ 0.0, 0.0, 1.0 ] ), 1.0 ) + ); + }, + visible: false, + name: process.env.DEV && 'Cube/speen', + } ) ); } private __createGeometry(): Geometry { @@ -66,10 +83,20 @@ export class Rings { return geometry; } - private __createMaterial(): Material { - const material = new Material( ringsVert, ringsFrag ); + private __createMaterials(): MaterialMap { + const forward = new Material( + ringsVert, + ringsFrag, + { defines: { 'FORWARD': 'true' } }, + ); - material.addUniform( 'inflate', '1f', 0.01 ); + const deferred = new Material( + ringsVert, + ringsFrag, + { defines: { 'DEFERRED': 'true' } }, + ); + + const shadow = new Material( ringsVert, depthFrag ); if ( process.env.DEV ) { if ( module.hot ) { @@ -79,12 +106,14 @@ export class Rings { '../shaders/rings.frag', ], () => { - material.replaceShader( ringsVert, ringsFrag ); + forward.replaceShader( ringsVert, ringsFrag ); + deferred.replaceShader( ringsVert, ringsFrag ); + shadow.replaceShader( ringsVert, depthFrag ); }, ); } } - return material; + return { forward, deferred, shadow }; } } diff --git a/src/entities/SphereParticles.ts b/src/entities/SphereParticles.ts index e4067a4..1cdd591 100644 --- a/src/entities/SphereParticles.ts +++ b/src/entities/SphereParticles.ts @@ -2,8 +2,10 @@ import { Entity } from '../heck/Entity'; import { GPUParticles } from './GPUParticles'; import { Geometry } from '../heck/Geometry'; import { InstancedGeometry } from '../heck/InstancedGeometry'; -import { Material } from '../heck/Material'; +import { Material, MaterialMap } from '../heck/Material'; import { genOctahedron } from '../geometries/genOctahedron'; +import depthFrag from '../shaders/depth.frag'; +import discardFrag from '../shaders/discard.frag'; import quadVert from '../shaders/quad.vert'; import sphereParticleComputeFrag from '../shaders/sphere-particles-compute.frag'; import sphereParticleRenderFrag from '../shaders/sphere-particles-render.frag'; @@ -16,24 +18,16 @@ const PARTICLES = PARTICLES_SQRT * PARTICLES_SQRT; export class SphereParticles { public get entity(): Entity { - return this.__gpuParticles.entity; + return this.gpuParticles.entity; } - private __gpuParticles: GPUParticles; - - public get materialCompute(): Material { - return this.__gpuParticles.materialCompute; - } - - public get materialRender(): Material { - return this.__gpuParticles.materialRender; - } + public gpuParticles: GPUParticles; public constructor() { - this.__gpuParticles = new GPUParticles( { + this.gpuParticles = new GPUParticles( { materialCompute: this.__createMaterialCompute(), geometryRender: this.__createGeometryRender(), - materialRender: this.__createMaterialRender(), + materialsRender: this.__createMaterialsRender(), computeWidth: PARTICLES_SQRT, computeHeight: PARTICLES_SQRT, computeNumBuffers: 2, @@ -59,7 +53,7 @@ export class SphereParticles { } private __createGeometryRender(): Geometry { - const octahedron = genOctahedron( { radius: 1.0, div: 1 } ); + const octahedron = genOctahedron( { radius: 1.0, div: 3 } ); const geometry = new InstancedGeometry(); @@ -96,42 +90,32 @@ export class SphereParticles { return geometry; } - private __createMaterialRender(): Material { - const material = new Material( + private __createMaterialsRender(): MaterialMap<'deferred' | 'shadow'> { + const deferred = new Material( sphereParticleRenderVert, sphereParticleRenderFrag, - { - defines: { - 'USE_CLIP': 'true', - 'USE_VERTEX_COLOR': 'true' - }, - }, + { defines: { 'DEFERRED': 'true' } }, ); - material.addUniform( 'colorVar', '1f', 0.1 ); - material.addUniformTexture( 'samplerRandomStatic', randomTextureStatic.texture ); + deferred.addUniformTexture( 'samplerRandomStatic', randomTextureStatic.texture ); + + const shadow = new Material( sphereParticleRenderVert, depthFrag ); + shadow.addUniformTexture( 'samplerRandomStatic', randomTextureStatic.texture ); if ( process.env.DEV ) { if ( module.hot ) { - module.hot.accept( '../shaders/sphere-particles-render.vert', () => { - material.replaceShader( - sphereParticleRenderVert, - sphereParticleRenderFrag, - ); - } ); + module.hot.accept( + [ + '../shaders/sphere-particles-render.vert', + '../shaders/sphere-particles-render.frag', + ], + () => { + deferred.replaceShader( sphereParticleRenderVert, sphereParticleRenderFrag ); + shadow.replaceShader( sphereParticleRenderVert, depthFrag ); + } + ); } } - if ( process.env.DEV ) { - if ( module.hot ) { - module.hot.accept( '../shaders/sphere-particles-render.frag', () => { - material.replaceShader( - sphereParticleRenderVert, - sphereParticleRenderFrag, - ); - } ); - } - } - - return material; + return { deferred, shadow }; } } diff --git a/src/entities/Trails.ts b/src/entities/Trails.ts index c6b2fc2..be224f6 100644 --- a/src/entities/Trails.ts +++ b/src/entities/Trails.ts @@ -2,8 +2,9 @@ import { Entity } from '../heck/Entity'; import { GPUParticles } from './GPUParticles'; import { Geometry } from '../heck/Geometry'; import { InstancedGeometry } from '../heck/InstancedGeometry'; -import { Material } from '../heck/Material'; +import { Material, MaterialMap } from '../heck/Material'; import quadVert from '../shaders/quad.vert'; +import depthFrag from '../shaders/depth.frag'; import trailsComputeFrag from '../shaders/trails-compute.frag'; import trailsRenderFrag from '../shaders/trails-render.frag'; import trailsRenderVert from '../shaders/trails-render.vert'; @@ -15,24 +16,16 @@ const TRAIL_LENGTH = 64; export class Trails { public get entity(): Entity { - return this.__gpuParticles.entity; + return this.gpuParticles.entity; } - private __gpuParticles: GPUParticles; - - public get materialCompute(): Material { - return this.__gpuParticles.materialCompute; - } - - public get materialRender(): Material { - return this.__gpuParticles.materialRender; - } + public gpuParticles: GPUParticles; public constructor() { - this.__gpuParticles = new GPUParticles( { + this.gpuParticles = new GPUParticles( { materialCompute: this.__createMaterialCompute(), geometryRender: this.__createGeometryRender(), - materialRender: this.__createMaterialRender(), + materialsRender: this.__createMaterialsRender(), computeWidth: TRAIL_LENGTH, computeHeight: TRAILS, computeNumBuffers: 2, @@ -140,33 +133,32 @@ export class Trails { return geometry; } - private __createMaterialRender(): Material { - const material = new Material( + private __createMaterialsRender(): MaterialMap<'deferred' | 'shadow'> { + const deferred = new Material( trailsRenderVert, trailsRenderFrag, + { defines: { 'DEFERRED': 'true' } }, ); - material.addUniformTexture( 'samplerRandomStatic', randomTextureStatic.texture ); + deferred.addUniformTexture( 'samplerRandomStatic', randomTextureStatic.texture ); + + const shadow = new Material( trailsRenderVert, depthFrag ); + shadow.addUniformTexture( 'samplerRandomStatic', randomTextureStatic.texture ); if ( process.env.DEV ) { if ( module.hot ) { - module.hot.accept( '../shaders/trails-render.vert', () => { - material.replaceShader( - trailsRenderVert, - trailsRenderFrag, - ); - } ); - } - - if ( module.hot ) { - module.hot.accept( '../shaders/trails-render.frag', () => { - material.replaceShader( - trailsRenderVert, - trailsRenderFrag, - ); - } ); + module.hot.accept( + [ + '../shaders/trails-render.vert', + '../shaders/trails-render.frag', + ], + () => { + deferred.replaceShader( trailsRenderVert, trailsRenderFrag ); + shadow.replaceShader( trailsRenderVert, depthFrag ); + } + ); } } - return material; + return { deferred, shadow }; } } diff --git a/src/heck/BufferRenderTarget.ts b/src/heck/BufferRenderTarget.ts index 4da83e1..30663fc 100644 --- a/src/heck/BufferRenderTarget.ts +++ b/src/heck/BufferRenderTarget.ts @@ -13,29 +13,10 @@ export interface BufferRenderTargetOptions { export class BufferRenderTarget extends RenderTarget { public static nameMap = new Map(); - private readonly __framebuffer: GLCatFramebuffer; - - public get framebuffer(): GLCatFramebuffer { - return this.__framebuffer; - } - - private __width: number; - - public get width(): number { - return this.__width; - } - - private __height: number; - - public get height(): number { - return this.__height; - } - - private __numBuffers: number; - - public get numBuffers(): number { - return this.__numBuffers; - } + public readonly framebuffer: GLCatFramebuffer; + public readonly width: number; + public readonly height: number; + public readonly numBuffers: number; private __name?: string; public get name(): string | undefined { @@ -67,18 +48,18 @@ export class BufferRenderTarget extends RenderTarget { public constructor( options: BufferRenderTargetOptions ) { super(); - this.__framebuffer = glCat.lazyDrawbuffers( + this.framebuffer = glCat.lazyDrawbuffers( options.width, options.height, - options.numBuffers || 1, + options.numBuffers ?? 1, { - isFloat: options.isFloat || true + isFloat: options.isFloat ?? true } ); - this.__width = options.width; - this.__height = options.height; - this.__numBuffers = options.numBuffers || 1; + this.width = options.width; + this.height = options.height; + this.numBuffers = options.numBuffers ?? 1; if ( process.env.DEV ) { this.name = options?.name; @@ -86,20 +67,20 @@ export class BufferRenderTarget extends RenderTarget { } public get texture(): GLCatTexture { - return this.__framebuffer.texture!; + return this.framebuffer.texture!; } public getTexture( attachment: number ): GLCatTexture | null { - return this.__framebuffer.getTexture( attachment ); + return this.framebuffer.getTexture( attachment ); } public bind(): void { - gl.bindFramebuffer( gl.FRAMEBUFFER, this.__framebuffer.raw ); - glCat.drawBuffers( this.__numBuffers ); + gl.bindFramebuffer( gl.FRAMEBUFFER, this.framebuffer.raw ); + glCat.drawBuffers( this.numBuffers ); gl.viewport( 0, 0, this.width, this.height ); } public dispose(): void { - this.__framebuffer.dispose(); + this.framebuffer.dispose(); } } diff --git a/src/heck/CubemapRenderTarget.ts b/src/heck/CubemapRenderTarget.ts new file mode 100644 index 0000000..e1de50d --- /dev/null +++ b/src/heck/CubemapRenderTarget.ts @@ -0,0 +1,43 @@ +import { GLCatFramebuffer, GLCatTextureCubemap } from '@fms-cat/glcat-ts'; +import { gl, glCat } from '../globals/canvas'; +import { RenderTarget } from './RenderTarget'; + +export interface CubemapRenderTargetOptions { + width: number; + height: number; + isFloat?: boolean; +} + +export class CubemapRenderTarget extends RenderTarget { + public readonly framebuffer: GLCatFramebuffer; + public readonly texture: GLCatTextureCubemap; + public readonly width: number; + public readonly height: number; + + public constructor( options: CubemapRenderTargetOptions ) { + super(); + + const { framebuffer, texture } = glCat.lazyCubemapFramebuffer( + options.width, + options.height, + { + isFloat: options.isFloat ?? true + }, + ); + this.framebuffer = framebuffer; + this.texture = texture; + + this.width = options.width; + this.height = options.height; + } + + public bind(): void { + gl.bindFramebuffer( gl.FRAMEBUFFER, this.framebuffer.raw ); + glCat.drawBuffers( 1 ); + gl.viewport( 0, 0, this.width, this.height ); + } + + public dispose(): void { + this.framebuffer.dispose(); + } +} diff --git a/src/heck/Entity.ts b/src/heck/Entity.ts index c3374a6..d75d891 100644 --- a/src/heck/Entity.ts +++ b/src/heck/Entity.ts @@ -3,6 +3,7 @@ import { Component } from './components/Component'; import { Matrix4 } from '@fms-cat/experimental'; import { RenderTarget } from './RenderTarget'; import { Transform } from './Transform'; +import { MaterialTag } from './Material'; export interface EntityUpdateEvent { frameCount: number; @@ -20,6 +21,8 @@ export interface EntityDrawEvent { viewMatrix: Matrix4; projectionMatrix: Matrix4; camera: Camera; + cameraTransform: Transform; + materialTag: MaterialTag; } export class Entity { @@ -69,9 +72,11 @@ export class Entity { renderTarget: event.renderTarget, globalTransform, camera: event.camera, + cameraTransform: event.cameraTransform, viewMatrix: event.viewMatrix, projectionMatrix: event.projectionMatrix, - entity: this + entity: this, + materialTag: event.materialTag, } ); } ); @@ -83,7 +88,9 @@ export class Entity { globalTransform, viewMatrix: event.viewMatrix, projectionMatrix: event.projectionMatrix, - camera: event.camera + camera: event.camera, + cameraTransform: event.cameraTransform, + materialTag: event.materialTag, } ); } ); } diff --git a/src/heck/Material.ts b/src/heck/Material.ts index 2f78ed3..3548663 100644 --- a/src/heck/Material.ts +++ b/src/heck/Material.ts @@ -1,7 +1,14 @@ -import { GLCatProgram, GLCatProgramLinkOptions, GLCatProgramUniformType, GLCatTexture } from '@fms-cat/glcat-ts'; +import { GLCatProgram, GLCatProgramLinkOptions, GLCatProgramUniformType, GLCatTexture, GLCatTextureCubemap } from '@fms-cat/glcat-ts'; import { gl } from '../globals/canvas'; import { SHADERPOOL } from './ShaderPool'; +export type MaterialTag = + | 'deferred' + | 'forward' + | 'shadow'; + +export type MaterialMap = { [ tag in T ]: Material }; + export class Material { protected __linkOptions: GLCatProgramLinkOptions; @@ -29,6 +36,12 @@ export class Material { }; } = {}; + protected __uniformCubemaps: { + [ name: string ]: { + texture: GLCatTextureCubemap | null; + }; + } = {}; + private __vert: string; public get vert(): string { @@ -89,6 +102,10 @@ export class Material { this.__uniformTextures[ name ] = { texture }; } + public addUniformCubemap( name: string, texture: GLCatTextureCubemap | null ): void { + this.__uniformCubemaps[ name ] = { texture }; + } + public setUniforms(): void { const program = this.program; @@ -103,6 +120,10 @@ export class Material { Object.entries( this.__uniformTextures ).forEach( ( [ name, { texture } ] ) => { program.uniformTexture( name, texture ); } ); + + Object.entries( this.__uniformCubemaps ).forEach( ( [ name, { texture } ] ) => { + program.uniformCubemap( name, texture ); + } ); } public setBlendMode(): void { diff --git a/src/heck/components/Camera.ts b/src/heck/components/Camera.ts index 433620a..3cdd1c6 100644 --- a/src/heck/components/Camera.ts +++ b/src/heck/components/Camera.ts @@ -4,20 +4,18 @@ import { Matrix4 } from '@fms-cat/experimental'; import { RenderTarget } from '../RenderTarget'; import { Transform } from '../Transform'; import { glCat } from '../../globals/canvas'; +import { MaterialTag } from '../Material'; export interface CameraOptions extends ComponentOptions { renderTarget?: RenderTarget; projectionMatrix: Matrix4; + materialTag: MaterialTag; scene?: Entity; clear?: Array | false; } export abstract class Camera extends Component { - protected __projectionMatrix: Matrix4; - - public get projectionMatrix(): Matrix4 { - return this.__projectionMatrix; - } + public projectionMatrix: Matrix4; public renderTarget?: RenderTarget; @@ -25,6 +23,8 @@ export abstract class Camera extends Component { public clear: Array | false = []; + public materialTag: MaterialTag; + public abstract get near(): number; public abstract get far(): number; @@ -36,7 +36,8 @@ export abstract class Camera extends Component { this.renderTarget = options.renderTarget; this.scene = options.scene; - this.__projectionMatrix = options.projectionMatrix; + this.projectionMatrix = options.projectionMatrix; + this.materialTag = options.materialTag; if ( options.clear !== undefined ) { this.clear = options.clear; } } @@ -63,10 +64,12 @@ export abstract class Camera extends Component { frameCount: event.frameCount, time: event.time, renderTarget: renderTarget, + cameraTransform: event.globalTransform, globalTransform: new Transform(), viewMatrix, - projectionMatrix: this.__projectionMatrix, - camera: this + projectionMatrix: this.projectionMatrix, + camera: this, + materialTag: this.materialTag, } ); if ( process.env.DEV ) { diff --git a/src/heck/components/Component.ts b/src/heck/components/Component.ts index dd776a9..1042690 100644 --- a/src/heck/components/Component.ts +++ b/src/heck/components/Component.ts @@ -6,6 +6,7 @@ import { Transform } from '../Transform'; import { COMPONENT_DRAW_BREAKPOINT, COMPONENT_UPDATE_BREAKPOINT } from '../../config-hot'; import { GPUTimer } from '../GPUTimer'; import { getDivComponentsDraw, getDivComponentsUpdate } from '../../globals/dom'; +import { MaterialTag } from '../Material'; export interface ComponentUpdateEvent { frameCount: number; @@ -19,6 +20,8 @@ export interface ComponentDrawEvent { frameCount: number; time: number; camera: Camera; + cameraTransform: Transform; + materialTag: MaterialTag; renderTarget: RenderTarget; globalTransform: Transform; viewMatrix: Matrix4; diff --git a/src/heck/components/CubemapCamera.ts b/src/heck/components/CubemapCamera.ts new file mode 100644 index 0000000..c5afdda --- /dev/null +++ b/src/heck/components/CubemapCamera.ts @@ -0,0 +1,81 @@ +import { Camera } from './Camera'; +import { Entity } from '../Entity'; +import { Matrix4, Quaternion, Vector3 } from '@fms-cat/experimental'; +import { ComponentOptions, ComponentUpdateEvent } from './Component'; +import { CubemapRenderTarget } from '../CubemapRenderTarget'; +import { gl } from '../../globals/canvas'; +import { Transform } from '../Transform'; +import { MaterialTag } from '../Material'; + +const INV_SQRT2 = 1.0 / Math.sqrt( 2.0 ); + +const CUBEMAP_ROTATIONS = [ // 🔥 + new Quaternion( [ 0.0, INV_SQRT2, 0.0, INV_SQRT2 ] ), // PX + new Quaternion( [ 0.0, -INV_SQRT2, 0.0, INV_SQRT2 ] ), // NX + new Quaternion( [ 0.0, INV_SQRT2, INV_SQRT2, 0.0 ] ), // PY + new Quaternion( [ 0.0, INV_SQRT2, -INV_SQRT2, 0.0 ] ), // NY + new Quaternion( [ 0.0, 1.0, 0.0, 0.0 ] ), // PZ + new Quaternion( [ 0.0, 0.0, 0.0, 1.0 ] ), // NZ +]; + +export interface CubemapCameraOptions extends ComponentOptions { + materialTag: MaterialTag; + renderTarget?: CubemapRenderTarget; + near?: number; + far?: number; + scene?: Entity; + clear?: Array | false; +} + +export class CubemapCamera extends Camera { + public renderTarget?: CubemapRenderTarget; + public readonly near: number; + public readonly far: number; + + public constructor( options: CubemapCameraOptions ) { + const projectionMatrix = Matrix4.perspective( + 90.0, + options.near ?? 0.1, + options.far ?? 20.0, + ); + + super( { + ...options, + projectionMatrix, + renderTarget: options.renderTarget, + scene: options.scene, + clear: options.clear, + materialTag: options.materialTag, + } ); + + this.near = options.near ?? 0.1; + this.far = options.far ?? 20.0; + } + + protected __updateImpl( event: ComponentUpdateEvent ): void { + const { renderTarget } = this; + + if ( !renderTarget ) { + throw new Error( process.env.DEV && 'You must assign a renderTarget to the Camera' ); + } + + for ( let i = 0; i < 6; i ++ ) { + renderTarget.framebuffer.attachTexture( + renderTarget.texture, + { textarget: gl.TEXTURE_CUBE_MAP_POSITIVE_X + i }, + ); + + const globalTransform = new Transform(); + globalTransform.matrix = Matrix4.compose( + event.globalTransform.position, + CUBEMAP_ROTATIONS[ i ], + new Vector3( [ 1.0, 1.0, 1.0 ] ), + ); + + super.__updateImpl( { + ...event, + globalTransform, + } ); + } + } +} diff --git a/src/heck/components/Mesh.ts b/src/heck/components/Mesh.ts index b7cf03a..6b2547d 100644 --- a/src/heck/components/Mesh.ts +++ b/src/heck/components/Mesh.ts @@ -1,6 +1,6 @@ import { Component, ComponentDrawEvent, ComponentOptions } from './Component'; import { Geometry } from '../Geometry'; -import { Material } from '../Material'; +import { MaterialMap, MaterialTag } from '../Material'; import { glCat } from '../../globals/canvas'; export enum MeshCull { @@ -18,12 +18,12 @@ const meshCullMap = { export interface MeshOptions extends ComponentOptions { geometry: Geometry; - material: Material; + materials: Partial>; } export class Mesh extends Component { public geometry: Geometry; - public material: Material; + public materials: Partial>; public cull: MeshCull = MeshCull.Back; @@ -33,16 +33,21 @@ export class Mesh extends Component { this.active = false; this.geometry = options.geometry; - this.material = options.material; + this.materials = options.materials; } protected __drawImpl( event: ComponentDrawEvent ): void { const gl = glCat.renderingContext; - const program = this.material.program; + const material = this.materials[ event.materialTag ]; + if ( material == null ) { + return; + } + + const program = material.program; glCat.useProgram( program ); - this.material.setBlendMode(); + material.setBlendMode(); if ( this.cull === MeshCull.None ) { gl.disable( gl.CULL_FACE ); @@ -51,13 +56,15 @@ export class Mesh extends Component { gl.cullFace( meshCullMap[ this.cull ] ); } - this.geometry.assignBuffers( this.material ); + this.geometry.assignBuffers( material ); - this.material.setUniforms(); + material.setUniforms(); program.uniform1f( 'time', event.time ); program.uniform1f( 'frameCount', event.frameCount ); program.uniform2f( 'resolution', event.renderTarget.width, event.renderTarget.height ); + program.uniform3f( 'cameraPos', ...event.cameraTransform.position.elements ); + program.uniform2f( 'cameraNearFar', event.camera.near, event.camera.far ); program.uniformMatrix4fv( 'normalMatrix', event.globalTransform.matrix.inverse!.transpose.elements ); program.uniformMatrix4fv( 'modelMatrix', event.globalTransform.matrix.elements ); diff --git a/src/heck/components/PerspectiveCamera.ts b/src/heck/components/PerspectiveCamera.ts index 69ef2f5..38684eb 100644 --- a/src/heck/components/PerspectiveCamera.ts +++ b/src/heck/components/PerspectiveCamera.ts @@ -3,8 +3,10 @@ import { Entity } from '../Entity'; import { Matrix4 } from '@fms-cat/experimental'; import { RenderTarget } from '../RenderTarget'; import { ComponentOptions } from './Component'; +import { MaterialTag } from '../Material'; export interface PerspectiveCameraOptions extends ComponentOptions { + materialTag: MaterialTag; renderTarget?: RenderTarget; near?: number; far?: number; @@ -14,17 +16,8 @@ export interface PerspectiveCameraOptions extends ComponentOptions { } export class PerspectiveCamera extends Camera { - private __near: number; - - public get near(): number { - return this.__near; - } - - private __far: number; - - public get far(): number { - return this.__far; - } + public readonly near: number; + public readonly far: number; public constructor( options: PerspectiveCameraOptions ) { const projectionMatrix = Matrix4.perspective( @@ -38,10 +31,10 @@ export class PerspectiveCamera extends Camera { projectionMatrix, renderTarget: options.renderTarget, scene: options.scene, - clear: options.clear + clear: options.clear, } ); - this.__near = options.near || 0.01; - this.__far = options.far || 100.0; + this.near = options.near || 0.01; + this.far = options.far || 100.0; } } diff --git a/src/scene.ts b/src/scene.ts index f86e7f8..58b8655 100644 --- a/src/scene.ts +++ b/src/scene.ts @@ -2,6 +2,7 @@ import { Swap, Vector3 } from '@fms-cat/experimental'; import { Bloom } from './entities/Bloom'; import { CameraEntity } from './entities/CameraEntity'; import { Cube } from './entities/Cube'; +import { CubemapCameraEntity } from './entities/CubemapCameraEntity'; import { EnvironmentMap } from './entities/EnvironmentMap'; import { FlickyParticles } from './entities/FlickyParticles'; import { Glitch } from './entities/Glitch'; @@ -69,23 +70,20 @@ class EntityReplacer { const ibllut = new IBLLUT(); dog.root.children.push( ibllut.entity ); -const environmentMap = new EnvironmentMap(); -dog.root.children.push( environmentMap.entity ); - // -- "objects" ------------------------------------------------------------------------------------ -const replacerSphereParticles = new EntityReplacer( () => new SphereParticles() ); -if ( process.env.DEV && module.hot ) { - module.hot.accept( './entities/SphereParticles', () => { - replacerSphereParticles.replace(); - } ); -} +// const replacerSphereParticles = new EntityReplacer( () => new SphereParticles() ); +// if ( process.env.DEV && module.hot ) { +// module.hot.accept( './entities/SphereParticles', () => { +// replacerSphereParticles.replace(); +// } ); +// } -const replacerTrails = new EntityReplacer( () => new Trails() ); -if ( process.env.DEV && module.hot ) { - module.hot.accept( './entities/Trails', () => { - replacerTrails.replace(); - } ); -} +// const replacerTrails = new EntityReplacer( () => new Trails() ); +// if ( process.env.DEV && module.hot ) { +// module.hot.accept( './entities/Trails', () => { +// replacerTrails.replace(); +// } ); +// } const replacerRings = new EntityReplacer( () => new Rings() ); if ( process.env.DEV && module.hot ) { @@ -94,12 +92,12 @@ if ( process.env.DEV && module.hot ) { } ); } -const replacerCube = new EntityReplacer( () => new Cube() ); -if ( process.env.DEV && module.hot ) { - module.hot.accept( './entities/Cube', () => { - replacerCube.replace(); - } ); -} +// const replacerCube = new EntityReplacer( () => new Cube() ); +// if ( process.env.DEV && module.hot ) { +// module.hot.accept( './entities/Cube', () => { +// replacerCube.replace(); +// } ); +// } const replacerFlickyParticles = new EntityReplacer( () => new FlickyParticles() ); if ( process.env.DEV && module.hot ) { @@ -139,7 +137,7 @@ const light = new LightEntity( { shadowMapFar: 20.0, namePrefix: process.env.DEV && 'light1', } ); -light.color = [ 40.0, 40.0, 40.0 ]; +light.color = [ 0.1, 0.1, 0.1 ]; light.entity.transform.lookAt( new Vector3( [ -1.0, 2.0, 8.0 ] ) ); dog.root.children.push( light.entity ); @@ -154,6 +152,20 @@ dog.root.children.push( light.entity ); // light2.entity.transform.lookAt( new Vector3( [ -4.0, -2.0, 6.0 ] ) ); // dog.root.children.push( light2.entity ); +const cubemapCamera = new CubemapCameraEntity( { + root: dog.root, + lights: [ + light, + // light2 + ], +} ); +dog.root.children.push( cubemapCamera.entity ); + +const environmentMap = new EnvironmentMap( { + cubemap: cubemapCamera.target, +} ); +dog.root.children.push( environmentMap.entity ); + const camera = new CameraEntity( { root: dog.root, target: swap.o, diff --git a/src/shaders/cube.frag b/src/shaders/cube.frag index 56a9650..37d3552 100644 --- a/src/shaders/cube.frag +++ b/src/shaders/cube.frag @@ -9,10 +9,12 @@ in vec3 vNormal; in vec4 vPosition; in vec4 vPositionWithoutModel; -layout (location = 0) out vec4 fragPosition; -layout (location = 1) out vec4 fragNormal; -layout (location = 2) out vec4 fragColor; -layout (location = 3) out vec4 fragWTF; +#ifdef DEFERRED + layout (location = 0) out vec4 fragPosition; + layout (location = 1) out vec4 fragNormal; + layout (location = 2) out vec4 fragColor; + layout (location = 3) out vec4 fragWTF; +#endif #pragma glslify: noise = require( ./-simplex4d ); @@ -29,8 +31,10 @@ float fbm( vec4 p ) { void main() { float rough = sin( 14.0 * fbm( vPositionWithoutModel ) ); - fragPosition = vPosition; - fragNormal = vec4( normalize( vNormal ), 1.0 ); - fragColor = vec4( vec3( 0.5 ), 1.0 ); - fragWTF = vec4( vec3( 0.2 + 0.03 * rough, 0.17, 0.0 ), MTL_PBR ); + #ifdef DEFERRED + fragPosition = vPosition; + fragNormal = vec4( normalize( vNormal ), 1.0 ); + fragColor = vec4( vec3( 0.02, 0.04, 0.9 ), 1.0 ); + fragWTF = vec4( vec3( 0.02 + 0.03 * rough, 0.87, 0.0 ), MTL_PBR ); + #endif } diff --git a/src/shaders/pos-to-depth.frag b/src/shaders/depth.frag similarity index 75% rename from src/shaders/pos-to-depth.frag rename to src/shaders/depth.frag index 049732d..c8b08ce 100644 --- a/src/shaders/pos-to-depth.frag +++ b/src/shaders/depth.frag @@ -5,20 +5,18 @@ precision highp float; #define saturate(x) clamp(x,0.,1.) #define linearstep(a,b,x) saturate(((x)-(a))/((b)-(a))) -in vec2 vUv; +in vec4 vPosition; out vec4 fragColor; uniform vec2 cameraNearFar; uniform vec3 cameraPos; -uniform sampler2D sampler0; void main() { - vec4 tex = texture( sampler0, vUv ); float depth = linearstep( cameraNearFar.x, cameraNearFar.y, - length( cameraPos - tex.xyz ) + length( cameraPos - vPosition.xyz ) ); fragColor = vec4( depth, depth * depth, depth, 1.0 ); } diff --git a/src/shaders/environment-map.frag b/src/shaders/environment-map.frag index bafe831..367da8a 100644 --- a/src/shaders/environment-map.frag +++ b/src/shaders/environment-map.frag @@ -22,6 +22,7 @@ uniform float head; uniform vec2 resolution; uniform vec4 uniformSeed; uniform sampler2D sampler0; +uniform samplerCube samplerCubemap; vec4 seed; @@ -66,14 +67,7 @@ vec3 ImportanceSampleGGX( vec2 Xi, float roughness, vec3 N ) { } vec3 haha( vec3 L ) { - bool circ = dot( L, normalize( vec3( 1.0, 3.0, 3.0 ) ) ) > 0.9; - vec3 c = circ ? 0.01 * vec3( 0.1, 0.1, 1.0 ) : vec3( 0.0 ); - - bool ring = abs( dot( L, vec3( -0.3, 1.0, 0.3 ) ) ) < 0.1; - c += ring ? 10.0 * vec3( 0.1, 1.0, 0.3 ) : vec3( 0.0 ); - - return c; - // return 0.5 + 0.5 * L; + return texture( samplerCubemap, L ).xyz; } void main() { @@ -92,11 +86,11 @@ void main() { float a = TAU * uv.x; float b = PI * ( uv.y - 0.5 ); - vec3 N = vec3( sin( a ) * cos( b ), -sin( b ), -cos( a ) * cos( b ) ); + vec3 N = vec3( -sin( a ) * cos( b ), sin( b ), -cos( a ) * cos( b ) ); vec3 R = N; vec3 V = R; - seed = uniformSeed + N.xyzx; + seed = uniformSeed + 500.0 * N.xyzx; vec4 col = vec4( 0.0 ); for ( int i = 0; i < SAMPLES; i ++ ) { @@ -113,7 +107,7 @@ void main() { col.xyz = col.w <= 0.001 ? vec3( 0.0 ) : ( col.xyz / col.w ); - tex.xyz = mix( tex.xyz, col.xyz, 1.0 / 16.0 ); + tex.xyz = mix( tex.xyz, col.xyz, 1.0 / 4.0 ); fragColor = vec4( tex, 1.0 ); } diff --git a/src/shaders/flicky-particles-render.frag b/src/shaders/flicky-particles-render.frag index 040d7ec..24e973e 100644 --- a/src/shaders/flicky-particles-render.frag +++ b/src/shaders/flicky-particles-render.frag @@ -29,14 +29,27 @@ in vec3 vNormal; in vec4 vPosition; in vec4 vDice; -layout (location = 0) out vec4 fragPosition; -layout (location = 1) out vec4 fragNormal; -layout (location = 2) out vec4 fragColor; -layout (location = 3) out vec4 fragWTF; - uniform float time; uniform sampler2D samplerRandomStatic; +#ifdef FORWARD + out vec4 fragColor; +#endif + +#ifdef DEFERRED + layout (location = 0) out vec4 fragPosition; + layout (location = 1) out vec4 fragNormal; + layout (location = 2) out vec4 fragColor; + layout (location = 3) out vec4 fragWTF; +#endif + +#ifdef SHADOW + out vec4 fragColor; + + uniform vec2 cameraNearFar; + uniform vec3 cameraPos; +#endif + // == utils ======================================================================================== mat2 rotate2D( float t ) { return mat2( cos( t ), sin( t ), -sin( t ), cos( t ) ); @@ -163,8 +176,23 @@ void main() { } - fragPosition = vPosition; - fragNormal = vec4( vNormal, 1.0 ); - fragColor = vec4( color, 1.0 ); - fragWTF = vec4( vec3( 0.0 ), MTL_UNLIT ); + #ifdef FORWARD + fragColor = vec4( 1.0 ); + #endif + + #ifdef DEFERRED + fragPosition = vPosition; + fragNormal = vec4( vNormal, 1.0 ); + fragColor = vec4( color, 1.0 ); + fragWTF = vec4( vec3( 0.0 ), MTL_UNLIT ); + #endif + + #ifdef SHADOW + float depth = linearstep( + cameraNearFar.x, + cameraNearFar.y, + length( cameraPos - vPosition.xyz ) + ); + fragColor = vec4( depth, depth * depth, depth, 1.0 ); + #endif } diff --git a/src/shaders/music.vert b/src/shaders/music.vert index ca0aa75..730d75d 100644 --- a/src/shaders/music.vert +++ b/src/shaders/music.vert @@ -82,9 +82,8 @@ vec2 filterSaw( vec2 time, float freq, float cutoff, float resonance ) { float kick( float t ) { if ( t < 0.0 ) { return 0.0; } - float phase = 50.0 * t - 12.0 * exp( -200.0 * t ) - 7.4 * exp( -40.0 * t ); - float fm = 0.7 * exp( -40.0 * t ) * sin( 2.0 * TAU * phase ); - return exp( -4.0 * t ) * sin( TAU * phase + fm ); + float phase = 50.0 * t - 15.0 * exp( -200.0 * t ) - 9.4 * exp( -30.0 * t ); + return exp( -4.0 * t ) * sin( TAU * phase ); } vec2 longsnare( float t ) { @@ -145,7 +144,7 @@ vec2 crash( float t ) { if ( t < 0.0 ) { return vec2( 0.0 ); } t = t + 0.01 * sin( 0.5 * exp( -40.0 * t ) + 3.0 ); - t = lofi( t, 0.00004 ); + t = lofi( 0.8 * t, 0.00004 ); float fmamp = -3.4 * exp( -1.0 * t ); vec2 fm = fmamp * sin( vec2( 38855.0, 38865.0 ) * t ); float amp = exp( -3.0 * t ); @@ -234,7 +233,7 @@ vec2 mainAudio( vec4 time ) { : time.x; float sidechain = smoothstep( 0.0, 0.7 * BEAT, tKick ); { - dest += 0.2 * kick( tKick ); + dest += 0.25 * kick( tKick ); } // -- snare -------------------------------------------------------------------------------------- @@ -262,7 +261,7 @@ vec2 mainAudio( vec4 time ) { } if ( - inRange( time.w, SECTION_PSY + 32.0 * BEAT, 1E9 ) + inRange( time.w, SECTION_PSY + 31.5 * BEAT, 1E9 ) ) { float t = mod( time.x - 0.5 * BEAT, BEAT ); dest += 0.1 * mix( 0.3, 1.0, sidechain ) * hihat( t, 20.0 ); @@ -299,7 +298,7 @@ vec2 mainAudio( vec4 time ) { // -- amen --------------------------------------------------------------------------------------- if ( - inRange( time.w, SECTION_PORTER_FUCKING_ROBINSON, SECTION_AAAA - 8.0 * BEAT ) && + inRange( time.w, SECTION_PORTER_FUCKING_ROBINSON, SECTION_AAAA - 8.0 * BEAT ) || inRange( time.w, SECTION_AAAA, SECTION_PSY ) ) { float chunk = floor( 6.0 * fs( lofi( time.z, 0.5 * BEAT ) ) ); @@ -336,6 +335,7 @@ vec2 mainAudio( vec4 time ) { // -- superbass ---------------------------------------------------------------------------------- if ( inRange( time.w, SECTION_PORTER_FUCKING_ROBINSON, SECTION_AAAA ) ) { float t = mod( time.z, 8.0 * BEAT ); + t += 1.0 * inRangeInteg( time.z, 28.0 * BEAT, 31.5 * BEAT, 50.0 ); float freq = n2f( float( chordsB[ progB ] ) ) * 0.125; float fadetime = max( 0.0, time.w - SECTION_AAAA + 8.0 * BEAT ); dest += 0.1 * exp( -1.0 * fadetime ) * mix( 0.1, 1.0, sidechain ) * superbass( t, freq, exp( -2.0 * fadetime ) ); @@ -401,7 +401,7 @@ vec2 mainAudio( vec4 time ) { sum += 0.3 * mix( 0.2, 1.0, sidechain ) * choir( t * rate * 0.5 ); } - dest += 0.1 * aSaturate( sum ); + dest += 0.12 * aSaturate( sum ); } // -- harp --------------------------------------------------------------------------------------- @@ -469,7 +469,7 @@ vec2 mainAudio( vec4 time ) { dest += 0.2 * kick( t ) * exp( -decay * t ); dest += 0.1 * inRangeFloat( time.w, SECTION_PSY - 32.0 * BEAT, 1E9 ) * clap( t ); - dest += 0.1 * ph * inRangeFloat( time.w, SECTION_PSY - 16.0 * BEAT, 1E9 ) * snare909( t ); + dest += 0.05 * ph * inRangeFloat( time.w, SECTION_PSY - 16.0 * BEAT, 1E9 ) * snare909( t ); } // -- fill, before psy --------------------------------------------------------------------------- diff --git a/src/shaders/raymarcher.frag b/src/shaders/raymarcher.frag index 3ee3244..c88ec98 100644 --- a/src/shaders/raymarcher.frag +++ b/src/shaders/raymarcher.frag @@ -13,14 +13,21 @@ const int MTL_IRIDESCENT = 4; in vec2 vUv; -layout (location = 0) out vec4 fragPosition; -layout (location = 1) out vec4 fragNormal; -layout (location = 2) out vec4 fragColor; -layout (location = 3) out vec4 fragWTF; +#ifdef DEFERRED + layout (location = 0) out vec4 fragPosition; + layout (location = 1) out vec4 fragNormal; + layout (location = 2) out vec4 fragColor; + layout (location = 3) out vec4 fragWTF; +#endif + +#ifdef SHADOW + out vec4 fragColor; +#endif uniform float time; uniform vec2 resolution; uniform vec2 cameraNearFar; +uniform vec3 cameraPos; uniform mat4 viewMatrix; uniform mat4 projectionMatrix; uniform mat4 inversePV; @@ -76,8 +83,19 @@ void main() { float depth = projPos.z / projPos.w; gl_FragDepth = 0.5 + 0.5 * depth; - fragPosition = vec4( rayPos, depth ); - fragNormal = vec4( normal, 1.0 ); - fragColor = color; - fragWTF = vec4( vec3( 0.8, 0.8, 0.0 ), MTL_PBR ); + #ifdef DEFERRED + fragPosition = vec4( rayPos, depth ); + fragNormal = vec4( normal, 1.0 ); + fragColor = color; + fragWTF = vec4( vec3( 0.2, 0.2, 0.0 ), MTL_PBR ); + #endif + + #ifdef SHADOW + float shadowDepth = linearstep( + cameraNearFar.x, + cameraNearFar.y, + length( cameraPos - rayPos ) + ); + fragColor = vec4( shadowDepth, shadowDepth * shadowDepth, shadowDepth, 1.0 ); + #endif } diff --git a/src/shaders/rings.frag b/src/shaders/rings.frag index defa278..f83f17b 100644 --- a/src/shaders/rings.frag +++ b/src/shaders/rings.frag @@ -8,16 +8,28 @@ in float vLife; in vec4 vPosition; in vec3 vNormal; -layout (location = 0) out vec4 fragPosition; -layout (location = 1) out vec4 fragNormal; -layout (location = 2) out vec4 fragColor; -layout (location = 3) out vec4 fragWTF; +#ifdef FORWARD + out vec4 fragColor; +#endif + +#ifdef DEFERRED + layout (location = 0) out vec4 fragPosition; + layout (location = 1) out vec4 fragNormal; + layout (location = 2) out vec4 fragColor; + layout (location = 3) out vec4 fragWTF; +#endif uniform float time; void main() { - fragPosition = vPosition; - fragNormal = vec4( normalize( vNormal ), 1.0 ); - fragColor = vec4( 0.2, 0.9, 0.5, 1.0 ); - fragWTF = vec4( vec3( 0.2, 0.2, 4.0 ), MTL_PBR ); + #ifdef FORWARD + fragColor = vec4( 28.0 * vec3( 0.2, 0.9, 0.5 ), 1.0 ); + #endif + + #ifdef DEFERRED + fragPosition = vPosition; + fragNormal = vec4( normalize( vNormal ), 1.0 ); + fragColor = vec4( 0.2, 0.9, 0.5, 1.0 ); + fragWTF = vec4( vec3( 0.2, 0.2, 4.0 ), MTL_PBR ); + #endif } diff --git a/src/shaders/sphere-particles-render.frag b/src/shaders/sphere-particles-render.frag index eaad76f..55e7b24 100644 --- a/src/shaders/sphere-particles-render.frag +++ b/src/shaders/sphere-particles-render.frag @@ -19,10 +19,12 @@ in vec3 vNormal; in vec4 vColor; in vec4 vRandom; -layout (location = 0) out vec4 fragPosition; -layout (location = 1) out vec4 fragNormal; -layout (location = 2) out vec4 fragColor; -layout (location = 3) out vec4 fragWTF; +#ifdef DEFERRED + layout (location = 0) out vec4 fragPosition; + layout (location = 1) out vec4 fragNormal; + layout (location = 2) out vec4 fragColor; + layout (location = 3) out vec4 fragWTF; +#endif uniform float time; @@ -35,8 +37,10 @@ mat2 rotate2D( float _t ) { void main() { if ( vColor.a < 0.0 ) { discard; } - fragPosition = vPosition; - fragNormal = vec4( vNormal, 1.0 ); - fragColor = vec4( vColor.xyz, 1.0 ); - fragWTF = vec4( vec3( 0.4, 0.4, 0.0 ), MTL_PBR ); + #ifdef DEFERRED + fragPosition = vPosition; + fragNormal = vec4( vNormal, 1.0 ); + fragColor = vec4( vColor.xyz, 1.0 ); + fragWTF = vec4( vec3( 0.1, 0.2, 0.0 ), MTL_PBR ); + #endif } diff --git a/src/utils/RandomTexture.ts b/src/utils/RandomTexture.ts index 7d5010f..593d64b 100644 --- a/src/utils/RandomTexture.ts +++ b/src/utils/RandomTexture.ts @@ -19,6 +19,7 @@ export class RandomTexture { this.__rng = new Xorshift(); this.__array = new Uint8Array( width * height * 4 ); this.__texture = glCat.createTexture()!; + this.__texture.textureFilter( gl.LINEAR ); this.__texture.textureWrap( gl.REPEAT ); }