From e44be0c5eec9a1144bc1bce1fc50d495a8a64433 Mon Sep 17 00:00:00 2001 From: schmelczerandras Date: Sun, 20 Sep 2020 19:23:22 +0200 Subject: [PATCH] Fix lights --- README.md | 5 ++ package.json | 10 +++- src/drawables/drawable.ts | 2 +- src/drawables/lights/circle-light.ts | 22 ++----- src/drawables/lights/flashlight.ts | 26 ++++----- src/drawables/lights/light-drawable.ts | 26 +++++++++ src/drawables/shapes/circle.ts | 2 +- src/drawables/shapes/inverted-tunnel.ts | 2 +- src/drawables/shapes/tunnel.ts | 2 +- .../distance-render-pass.ts} | 47 +++++---------- .../render-pass/lights-render-pass.ts | 58 +++++++++++++++++++ .../rendering/render-pass/render-pass.ts | 26 +++++++++ src/graphics/rendering/rendering-pass-name.ts | 4 -- src/graphics/rendering/webgl2-renderer.ts | 54 ++++++----------- src/main.ts | 2 +- tsconfig.json | 2 +- webpack.config.js | 6 +- 17 files changed, 179 insertions(+), 117 deletions(-) create mode 100644 README.md create mode 100644 src/drawables/lights/light-drawable.ts rename src/graphics/rendering/{rendering-pass.ts => render-pass/distance-render-pass.ts} (58%) create mode 100644 src/graphics/rendering/render-pass/lights-render-pass.ts create mode 100644 src/graphics/rendering/render-pass/render-pass.ts delete mode 100644 src/graphics/rendering/rendering-pass-name.ts diff --git a/README.md b/README.md new file mode 100644 index 0000000..c0905f5 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +logo + +# SDF-2D library + +Documentation will be provided by the end of september 2020. diff --git a/package.json b/package.json index db7a524..f1f671f 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,10 @@ "url": "https://github.com/schmelczerandras/sdf-2d/issues", "email": "andras@schmelczer.dev" }, + "repository": { + "type": "git", + "url": "git://github.com/schmelczerandras/sdf-2d.git" + }, "license": "ISC", "scripts": { "start": "shx rm -rf lib/* && webpack --mode development -w", @@ -30,10 +34,11 @@ "lib" ], "sideEffects": false, - "optimization": { - "usedExports": true + "dependencies": { + "gl-matrix": "^3.3.0" }, "devDependencies": { + "@types/gl-matrix": "^2.4.5", "@typescript-eslint/eslint-plugin": "^3.10.1", "@typescript-eslint/parser": "^3.10.1", "eslint": "^7.9.0", @@ -41,7 +46,6 @@ "eslint-plugin-import": "^2.22.0", "eslint-plugin-prettier": "^3.1.4", "eslint-plugin-unused-imports": "^0.1.3", - "gl-matrix": "^3.3.0", "prettier": "^2.1.2", "raw-loader": "^4.0.1", "shx": "^0.3.2", diff --git a/src/drawables/drawable.ts b/src/drawables/drawable.ts index 300fc27..f7265fb 100644 --- a/src/drawables/drawable.ts +++ b/src/drawables/drawable.ts @@ -5,7 +5,7 @@ export abstract class Drawable { // This should be overriden by inherited classes public static readonly descriptor: DrawableDescriptor; - public abstract distance(target: vec2): number; + public abstract minDistance(target: vec2): number; protected abstract getObjectToSerialize(transform2d: mat2d, transform1d: number): any; diff --git a/src/drawables/lights/circle-light.ts b/src/drawables/lights/circle-light.ts index d2edff7..f468895 100644 --- a/src/drawables/lights/circle-light.ts +++ b/src/drawables/lights/circle-light.ts @@ -1,8 +1,8 @@ -import { mat2d, vec2, vec3 } from 'gl-matrix'; -import { Drawable } from '../drawable'; +import { vec2, vec3 } from 'gl-matrix'; import { DrawableDescriptor } from '../drawable-descriptor'; +import { LightDrawable } from './light-drawable'; -export class CircleLight extends Drawable { +export class CircleLight extends LightDrawable { public static readonly descriptor: DrawableDescriptor = { uniformName: 'circleLights', uniformCountMacroName: 'CIRCLE_LIGHT_COUNT', @@ -10,19 +10,7 @@ export class CircleLight extends Drawable { empty: new CircleLight(vec2.fromValues(0, 0), vec3.fromValues(0, 0, 0), 0), }; - constructor(public center: vec2, public color: vec3, public intensity: number) { - super(); - } - - public distance(target: vec2): number { - return vec2.dist(this.center, target); - } - - protected getObjectToSerialize(transform2d: mat2d, transform1d: number): any { - return { - center: vec2.transformMat2d(vec2.create(), this.center, transform2d), - color: this.color, - intensity: this.intensity, - }; + constructor(center: vec2, color: vec3, intensity: number) { + super(center, color, intensity); } } diff --git a/src/drawables/lights/flashlight.ts b/src/drawables/lights/flashlight.ts index 7ddb1ef..9d60c36 100644 --- a/src/drawables/lights/flashlight.ts +++ b/src/drawables/lights/flashlight.ts @@ -1,39 +1,33 @@ import { mat2d, vec2, vec3 } from 'gl-matrix'; -import { Drawable } from '../drawable'; import { DrawableDescriptor } from '../drawable-descriptor'; +import { LightDrawable } from './light-drawable'; -export class Flashlight extends Drawable { +export class Flashlight extends LightDrawable { public static readonly descriptor: DrawableDescriptor = { uniformName: 'flashlights', uniformCountMacroName: 'FLASHLIGHT_COUNT', shaderCombinationSteps: [0, 1, 2, 4], empty: new Flashlight( - vec2.fromValues(0, 0), vec2.fromValues(0, 0), vec3.fromValues(0, 0, 0), - 0 + 0, + vec2.fromValues(0, 0) ), }; public constructor( - public center: vec2, - public direction: vec2, - public color: vec3, - public intensity: number + center: vec2, + color: vec3, + intensity: number, + public direction: vec2 ) { - super(); - } - - public distance(_: vec2): number { - return 0; + super(center, color, intensity); } protected getObjectToSerialize(transform2d: mat2d, transform1d: number): any { return { - center: vec2.transformMat2d(vec2.create(), this.center, transform2d), + ...super.getObjectToSerialize(transform2d, transform1d), direction: this.direction, - intensity: this.intensity, - color: this.color, }; } } diff --git a/src/drawables/lights/light-drawable.ts b/src/drawables/lights/light-drawable.ts new file mode 100644 index 0000000..49e0aee --- /dev/null +++ b/src/drawables/lights/light-drawable.ts @@ -0,0 +1,26 @@ +import { mat2d, vec2, vec3 } from 'gl-matrix'; +import { Drawable } from '../../main'; + +export class LightDrawable extends Drawable { + protected lightnessRatio = 1; + + constructor(public center: vec2, public color: vec3, public intensity: number) { + super(); + } + + public setLightnessRatio(ratio: number) { + this.lightnessRatio = ratio; + } + + public minDistance(target: vec2): number { + return vec2.dist(this.center, target); + } + + protected getObjectToSerialize(transform2d: mat2d, transform1d: number): any { + return { + center: vec2.transformMat2d(vec2.create(), this.center, transform2d), + color: this.color, + intensity: this.intensity * this.lightnessRatio, + }; + } +} diff --git a/src/drawables/shapes/circle.ts b/src/drawables/shapes/circle.ts index 76c9bd5..051db2d 100644 --- a/src/drawables/shapes/circle.ts +++ b/src/drawables/shapes/circle.ts @@ -36,7 +36,7 @@ export class Circle extends Drawable { super(); } - public distance(target: vec2): number { + public minDistance(target: vec2): number { return vec2.dist(this.center, target) - this.radius; } diff --git a/src/drawables/shapes/inverted-tunnel.ts b/src/drawables/shapes/inverted-tunnel.ts index 0c278f1..3a02434 100644 --- a/src/drawables/shapes/inverted-tunnel.ts +++ b/src/drawables/shapes/inverted-tunnel.ts @@ -60,7 +60,7 @@ export class InvertedTunnel extends Drawable { super(); } - public distance(target: vec2): number { + public minDistance(target: vec2): number { const toFromDelta = vec2.subtract(vec2.create(), this.to, this.from); const targetFromDelta = vec2.subtract(vec2.create(), target, this.from); diff --git a/src/drawables/shapes/tunnel.ts b/src/drawables/shapes/tunnel.ts index f01b013..3fe837a 100644 --- a/src/drawables/shapes/tunnel.ts +++ b/src/drawables/shapes/tunnel.ts @@ -65,7 +65,7 @@ export class Tunnel extends Drawable { super(); } - public distance(target: vec2): number { + public minDistance(target: vec2): number { const toFromDelta = vec2.subtract(vec2.create(), this.to, this.from); const targetFromDelta = vec2.subtract(vec2.create(), target, this.from); diff --git a/src/graphics/rendering/rendering-pass.ts b/src/graphics/rendering/render-pass/distance-render-pass.ts similarity index 58% rename from src/graphics/rendering/rendering-pass.ts rename to src/graphics/rendering/render-pass/distance-render-pass.ts index 8866825..b48cf03 100644 --- a/src/graphics/rendering/rendering-pass.ts +++ b/src/graphics/rendering/render-pass/distance-render-pass.ts @@ -1,33 +1,13 @@ import { vec2 } from 'gl-matrix'; -import { Drawable } from '../../drawables/drawable'; -import { DrawableDescriptor } from '../../drawables/drawable-descriptor'; -import { FrameBuffer } from '../graphics-library/frame-buffer/frame-buffer'; -import { UniformArrayAutoScalingProgram } from '../graphics-library/program/uniform-array-autoscaling-program'; -import { Insights } from './insights'; -import { RenderingPassName } from './rendering-pass-name'; +import { Drawable } from '../../../drawables/drawable'; +import { Insights } from '../insights'; +import { RenderPass } from './render-pass'; -export class RenderingPass { +export class DistanceRenderPass extends RenderPass { public tileMultiplier = 8; public isWorldInverted = false; private drawables: Array = []; - private program: UniformArrayAutoScalingProgram; - - constructor( - gl: WebGL2RenderingContext, - private frame: FrameBuffer, - private name: RenderingPassName - ) { - this.program = new UniformArrayAutoScalingProgram(gl); - } - - public async initialize( - shaderSources: [string, string], - descriptors: Array, - substitutions: { [name: string]: any } = {} - ): Promise { - await this.program.initialize(shaderSources, descriptors, substitutions); - } public addDrawable(drawable: Drawable) { this.drawables.push(drawable); @@ -72,7 +52,7 @@ export class RenderingPass { ); const drawablesNearTile = this.drawables.filter( - (d) => d.distance(tileCenterWorldCoordinates) < 2 * worldR + (d) => d.minDistance(tileCenterWorldCoordinates) < 2 * worldR ); drawnDrawablesCount += drawablesNearTile.length; @@ -89,18 +69,19 @@ export class RenderingPass { } } - Insights.setValue(['render pass', this.name, 'all drawables'], this.drawables.length); - Insights.setValue(['render pass', this.name, 'tile count'], this.tileMultiplier ** 2); Insights.setValue( - ['render pass', this.name, 'rendered drawables'], + ['render pass', 'distance', 'all drawables'], + this.drawables.length + ); + Insights.setValue( + ['render pass', 'distance', 'tile count'], + this.tileMultiplier ** 2 + ); + Insights.setValue( + ['render pass', 'distance', 'rendered drawables'], drawnDrawablesCount / this.tileMultiplier ** 2 ); this.drawables = []; } - - public destroy(): void { - this.frame.destroy(); - this.program.destroy(); - } } diff --git a/src/graphics/rendering/render-pass/lights-render-pass.ts b/src/graphics/rendering/render-pass/lights-render-pass.ts new file mode 100644 index 0000000..1dd560f --- /dev/null +++ b/src/graphics/rendering/render-pass/lights-render-pass.ts @@ -0,0 +1,58 @@ +import { vec2 } from 'gl-matrix'; +import { LightDrawable } from '../../../drawables/lights/light-drawable'; +import { clamp01 } from '../../../helper/clamp'; +import { Insights } from '../insights'; +import { RenderPass } from './render-pass'; + +export class LightsRenderPass extends RenderPass { + public lightPhaseOutLength = 400; + private drawables: Array = []; + + public addDrawable(drawable: LightDrawable) { + this.drawables.push(drawable); + } + + public render(commonUniforms: any, inputTexture?: WebGLTexture) { + this.frame.bindAndClear(inputTexture); + + const tileCenterWorldCoordinates = vec2.transformMat2d( + vec2.create(), + vec2.fromValues(0.5, 0.5), + commonUniforms.uvToWorld + ); + + const drawablesNearTile = this.drawables.filter((l) => { + const d = vec2.subtract( + vec2.create(), + [ + Math.abs(l.center.x - tileCenterWorldCoordinates.x), + Math.abs(l.center.y - tileCenterWorldCoordinates.y), + ], + vec2.scale(vec2.create(), commonUniforms.worldAreaInView, 0.5) + ); + const distance = + vec2.length([Math.max(d.x, 0), Math.max(d.y, 0)]) + + Math.min(Math.max(d.x, d.y), 0.0); + l.setLightnessRatio(clamp01(1 - distance / this.lightPhaseOutLength)); + + return distance < this.lightPhaseOutLength; + }); + + drawablesNearTile.forEach((p) => + p.serializeToUniforms( + commonUniforms, + commonUniforms.transformWorldToNDC, + commonUniforms.scaleWorldLengthToNDC + ) + ); + + this.program.draw(commonUniforms); + + Insights.setValue( + ['render pass', 'lights', 'rendered drawables'], + drawablesNearTile.length + ); + + this.drawables = []; + } +} diff --git a/src/graphics/rendering/render-pass/render-pass.ts b/src/graphics/rendering/render-pass/render-pass.ts new file mode 100644 index 0000000..c252e9b --- /dev/null +++ b/src/graphics/rendering/render-pass/render-pass.ts @@ -0,0 +1,26 @@ +import { DrawableDescriptor } from '../../../drawables/drawable-descriptor'; +import { FrameBuffer } from '../../graphics-library/frame-buffer/frame-buffer'; +import { UniformArrayAutoScalingProgram } from '../../graphics-library/program/uniform-array-autoscaling-program'; + +export abstract class RenderPass { + protected program: UniformArrayAutoScalingProgram; + + constructor(gl: WebGL2RenderingContext, protected frame: FrameBuffer) { + this.program = new UniformArrayAutoScalingProgram(gl); + } + + public async initialize( + shaderSources: [string, string], + descriptors: Array, + substitutions: { [name: string]: any } = {} + ): Promise { + await this.program.initialize(shaderSources, descriptors, substitutions); + } + + public abstract render(commonUniforms: any, inputTexture?: WebGLTexture): void; + + public destroy(): void { + this.frame.destroy(); + this.program.destroy(); + } +} diff --git a/src/graphics/rendering/rendering-pass-name.ts b/src/graphics/rendering/rendering-pass-name.ts deleted file mode 100644 index 5d593c6..0000000 --- a/src/graphics/rendering/rendering-pass-name.ts +++ /dev/null @@ -1,4 +0,0 @@ -export enum RenderingPassName { - distance = 'distance', - pixel = 'pixel', -} diff --git a/src/graphics/rendering/webgl2-renderer.ts b/src/graphics/rendering/webgl2-renderer.ts index 5f04b1a..91c37cd 100644 --- a/src/graphics/rendering/webgl2-renderer.ts +++ b/src/graphics/rendering/webgl2-renderer.ts @@ -1,6 +1,7 @@ import { vec2, vec3 } from 'gl-matrix'; import { Drawable } from '../../drawables/drawable'; import { DrawableDescriptor } from '../../drawables/drawable-descriptor'; +import { LightDrawable } from '../../drawables/lights/light-drawable'; import { msToString } from '../../helper/ms-to-string'; import { DefaultFrameBuffer } from '../graphics-library/frame-buffer/default-frame-buffer'; import { IntermediateFrameBuffer } from '../graphics-library/frame-buffer/intermediate-frame-buffer'; @@ -9,9 +10,9 @@ import { WebGlStopwatch } from '../graphics-library/helper/stopwatch'; import { ParallelCompiler } from '../graphics-library/parallel-compiler'; import { FpsAutoscaler } from './fps-autoscaler'; import { Insights } from './insights'; +import { DistanceRenderPass } from './render-pass/distance-render-pass'; +import { LightsRenderPass } from './render-pass/lights-render-pass'; import { Renderer } from './renderer'; -import { RenderingPass } from './rendering-pass'; -import { RenderingPassName } from './rendering-pass-name'; import { RuntimeSettings } from './settings/runtime-settings'; import { StartupSettings } from './settings/startup-settings'; import distanceFragmentShader from './shaders/distance-fs.glsl'; @@ -20,15 +21,14 @@ import lightsFragmentShader from './shaders/shading-fs.glsl'; import lightsVertexShader from './shaders/shading-vs.glsl'; import { UniformsProvider } from './uniforms-provider'; -type Passes = { [key in keyof typeof RenderingPassName]: RenderingPass }; - export class WebGl2Renderer implements Renderer { private gl: WebGL2RenderingContext; private stopwatch?: WebGlStopwatch; private uniformsProvider: UniformsProvider; private distanceFieldFrameBuffer: IntermediateFrameBuffer; + private distancePass: DistanceRenderPass; + private lightsPass: LightsRenderPass; private lightingFrameBuffer: DefaultFrameBuffer; - private passes: Passes; private autoscaler: FpsAutoscaler; private applyRuntimeSettings: { @@ -39,12 +39,10 @@ export class WebGl2Renderer implements Renderer { this.lightingFrameBuffer.enableHighDpiRendering = v; }, tileMultiplier: (v) => { - this.passes[RenderingPassName.distance].tileMultiplier = v; - this.passes[RenderingPassName.pixel].tileMultiplier = 1; + this.distancePass.tileMultiplier = v; }, isWorldInverted: (v) => { - this.passes[RenderingPassName.distance].isWorldInverted = v; - this.passes[RenderingPassName.pixel].isWorldInverted = v; + this.distancePass.isWorldInverted = v; }, shadowLength: (v) => { this.uniformsProvider.shadowLength = v; @@ -75,9 +73,8 @@ export class WebGl2Renderer implements Renderer { this.distanceFieldFrameBuffer = new IntermediateFrameBuffer(this.gl); this.lightingFrameBuffer = new DefaultFrameBuffer(this.gl); - this.passes = this.createPasses(); - - this.passes.pixel.tileMultiplier = 1; + this.distancePass = new DistanceRenderPass(this.gl, this.distanceFieldFrameBuffer); + this.lightsPass = new LightsRenderPass(this.gl, this.lightingFrameBuffer); this.uniformsProvider = new UniformsProvider(this.gl); @@ -88,21 +85,6 @@ export class WebGl2Renderer implements Renderer { }); } - private createPasses(): Passes { - return { - [RenderingPassName.distance]: new RenderingPass( - this.gl, - this.distanceFieldFrameBuffer, - RenderingPassName.distance - ), - [RenderingPassName.pixel]: new RenderingPass( - this.gl, - this.lightingFrameBuffer, - RenderingPassName.pixel - ), - }; - } - public async initialize( palette: Array, settingsOverrides: Partial @@ -112,14 +94,14 @@ export class WebGl2Renderer implements Renderer { const promises: Array> = []; promises.push( - this.passes[RenderingPassName.distance].initialize( + this.distancePass.initialize( [distanceVertexShader, distanceFragmentShader], this.descriptors.filter(WebGl2Renderer.hasSdf) ) ); promises.push( - this.passes[RenderingPassName.pixel].initialize( + this.lightsPass.initialize( [lightsVertexShader, lightsFragmentShader], this.descriptors.filter((d) => !WebGl2Renderer.hasSdf(d)), { @@ -150,9 +132,9 @@ export class WebGl2Renderer implements Renderer { public addDrawable(drawable: Drawable): void { if (WebGl2Renderer.hasSdf((drawable.constructor as typeof Drawable).descriptor)) { - this.passes[RenderingPassName.distance].addDrawable(drawable); + this.distancePass.addDrawable(drawable); } else { - this.passes[RenderingPassName.pixel].addDrawable(drawable); + this.lightsPass.addDrawable(drawable as LightDrawable); } } @@ -193,10 +175,8 @@ export class WebGl2Renderer implements Renderer { shadingNdcPixelSize: 2 / Math.max(...this.distanceFieldFrameBuffer.getSize()), }; - this.passes[RenderingPassName.distance].render( - this.uniformsProvider.getUniforms(common) - ); - this.passes[RenderingPassName.pixel].render( + this.distancePass.render(this.uniformsProvider.getUniforms(common)); + this.lightsPass.render( this.uniformsProvider.getUniforms(common), this.distanceFieldFrameBuffer.colorTexture ); @@ -219,7 +199,7 @@ export class WebGl2Renderer implements Renderer { } public destroy(): void { - this.passes.distance.destroy(); - this.passes.pixel.destroy(); + this.distancePass.destroy(); + this.lightsPass.destroy(); } } diff --git a/src/main.ts b/src/main.ts index fb53bea..0154567 100644 --- a/src/main.ts +++ b/src/main.ts @@ -5,6 +5,7 @@ import { Renderer } from './graphics/rendering/renderer'; import { StartupSettings } from './graphics/rendering/settings/startup-settings'; import { WebGl2Renderer } from './graphics/rendering/webgl2-renderer'; import { applyArrayPlugins } from './helper/array'; + export { Drawable } from './drawables/drawable'; export { DrawableDescriptor } from './drawables/drawable-descriptor'; export { CircleLight } from './drawables/lights/circle-light'; @@ -13,7 +14,6 @@ export { Circle } from './drawables/shapes/circle'; export { InvertedTunnel } from './drawables/shapes/inverted-tunnel'; export { Tunnel } from './drawables/shapes/tunnel'; export { Renderer } from './graphics/rendering/renderer'; -export { RenderingPassName } from './graphics/rendering/rendering-pass-name'; declare global { interface Array { diff --git a/tsconfig.json b/tsconfig.json index 48b6aef..0323388 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,7 +4,7 @@ "sourceMap": true, "declaration": true, "declarationMap": true, - "target": "es6", + "target": "ES2017", "esModuleInterop": true, "strict": true, "experimentalDecorators": true, diff --git a/webpack.config.js b/webpack.config.js index 217c655..749d6de 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -14,7 +14,7 @@ module.exports = { output: { path: PATHS.bundles, filename: '[name].js', - library: 'SDF2D', + library: 'sdf-2d', libraryTarget: 'umd', umdNamedDefine: true, }, @@ -23,8 +23,12 @@ module.exports = { aggregateTimeout: 600, ignored: /node_modules/, }, + externals: { + 'gl-matrix': 'gl-matrix', + }, optimization: { minimize: true, + usedExports: true, minimizer: [ new TerserJSPlugin({ sourceMap: true,