From c892ca2d0124a1a7713533c065473b68af1d9da0 Mon Sep 17 00:00:00 2001 From: schmelczerandras Date: Thu, 30 Jul 2020 13:28:10 +0200 Subject: [PATCH] Refactor rendering --- README.md | 7 +- .../scripts/commands/types/before-render.ts | 2 +- frontend/src/scripts/commands/types/draw.ts | 2 +- .../drawables/i-drawable-descriptor.ts | 5 + .../i-drawable.ts} | 2 +- .../{ => drawables}/lights/circle-light.ts | 18 +- .../drawing/drawables/lights/i-light.ts | 3 + .../{ => drawables}/lights/point-light.ts | 6 + .../{ => drawables}/primitives/circle.ts | 0 .../drawables/primitives/i-primitive.ts | 3 + .../{ => drawables}/primitives/rectangle.ts | 0 .../primitives/tunnel-shape.ts | 31 ++- .../graphics-library/helper/create-shader.ts | 11 +- .../helper/get-webgl2-context.ts | 11 + .../program/fragment-shader-only-program.ts | 5 +- .../graphics-library/program/i-program.ts | 2 +- .../graphics-library/program/program.ts | 3 +- .../uniform-array-autoscaling-program.ts | 71 +++-- .../drawing/{rendering => }/i-renderer.ts | 4 +- .../src/scripts/drawing/lights/i-light.ts | 3 - .../drawing/rendering/fps-autoscaler.ts | 54 ++++ .../drawing/rendering/rendering-pass.ts | 103 +++++++- .../drawing/rendering/webgl2-renderer.ts | 249 ++++-------------- .../drawing/{rendering => }/settings.ts | 6 +- .../drawing}/shaders/cave-distance-fs.glsl | 22 +- .../drawing/shaders/lights-shading-fs.glsl | 100 +++++++ .../shaders/passthrough-distance-vs.glsl | 0 .../shaders/passthrough-shading-vs.glsl | 39 +++ .../drawing}/shaders/rainbow-shading-fs.glsl | 1 - .../{ => scripts/drawing}/shaders/random.frag | 0 frontend/src/scripts/game.ts | 10 +- .../rendering => helper}/autoscaler.ts | 4 +- .../src/scripts/helper/get-combinations.ts | 31 +++ frontend/src/scripts/objects/types/lamp.ts | 2 +- frontend/src/scripts/objects/types/tunnel.ts | 2 +- .../scripts/objects/world/create-dungeon.ts | 4 +- frontend/src/shaders/lights-shading-fs.glsl | 91 ------- .../src/shaders/passthrough-shading-vs.glsl | 33 --- 38 files changed, 511 insertions(+), 429 deletions(-) create mode 100644 frontend/src/scripts/drawing/drawables/i-drawable-descriptor.ts rename frontend/src/scripts/drawing/{primitives/i-primitive.ts => drawables/i-drawable.ts} (83%) rename frontend/src/scripts/drawing/{ => drawables}/lights/circle-light.ts (57%) create mode 100644 frontend/src/scripts/drawing/drawables/lights/i-light.ts rename frontend/src/scripts/drawing/{ => drawables}/lights/point-light.ts (78%) rename frontend/src/scripts/drawing/{ => drawables}/primitives/circle.ts (100%) create mode 100644 frontend/src/scripts/drawing/drawables/primitives/i-primitive.ts rename frontend/src/scripts/drawing/{ => drawables}/primitives/rectangle.ts (100%) rename frontend/src/scripts/drawing/{ => drawables}/primitives/tunnel-shape.ts (65%) create mode 100644 frontend/src/scripts/drawing/graphics-library/helper/get-webgl2-context.ts rename frontend/src/scripts/drawing/{rendering => }/i-renderer.ts (76%) delete mode 100644 frontend/src/scripts/drawing/lights/i-light.ts create mode 100644 frontend/src/scripts/drawing/rendering/fps-autoscaler.ts rename frontend/src/scripts/drawing/{rendering => }/settings.ts (81%) rename frontend/src/{ => scripts/drawing}/shaders/cave-distance-fs.glsl (70%) create mode 100644 frontend/src/scripts/drawing/shaders/lights-shading-fs.glsl rename frontend/src/{ => scripts/drawing}/shaders/passthrough-distance-vs.glsl (100%) create mode 100644 frontend/src/scripts/drawing/shaders/passthrough-shading-vs.glsl rename frontend/src/{ => scripts/drawing}/shaders/rainbow-shading-fs.glsl (99%) rename frontend/src/{ => scripts/drawing}/shaders/random.frag (100%) rename frontend/src/scripts/{drawing/rendering => helper}/autoscaler.ts (92%) create mode 100644 frontend/src/scripts/helper/get-combinations.ts delete mode 100644 frontend/src/shaders/lights-shading-fs.glsl delete mode 100644 frontend/src/shaders/passthrough-shading-vs.glsl diff --git a/README.md b/README.md index 071793b..6297b20 100644 --- a/README.md +++ b/README.md @@ -13,11 +13,12 @@ A good-looking 2D adventure. ## Todo -- host script - - https://github.com/ottomatica/slim - - rancher os - Frontend nginx disable logging - procedural piano +- subtract cameraPosition +- cross browser testing +- nginx log monitor +- pass befejezése - lightweight object storage - local dev env - CI pipeline: diff --git a/frontend/src/scripts/commands/types/before-render.ts b/frontend/src/scripts/commands/types/before-render.ts index d5ffadc..9069362 100644 --- a/frontend/src/scripts/commands/types/before-render.ts +++ b/frontend/src/scripts/commands/types/before-render.ts @@ -1,5 +1,5 @@ import { Command } from '../command'; -import { IRenderer } from '../../drawing/rendering/i-renderer'; +import { IRenderer } from '../../drawing/i-renderer'; export class BeforeRenderCommand extends Command { public constructor(public readonly renderer?: IRenderer) { diff --git a/frontend/src/scripts/commands/types/draw.ts b/frontend/src/scripts/commands/types/draw.ts index 6a3718e..e1262af 100644 --- a/frontend/src/scripts/commands/types/draw.ts +++ b/frontend/src/scripts/commands/types/draw.ts @@ -1,5 +1,5 @@ import { Command } from '../command'; -import { IRenderer } from '../../drawing/rendering/i-renderer'; +import { IRenderer } from '../../drawing/i-renderer'; export class RenderCommand extends Command { public constructor(public readonly renderer?: IRenderer) { diff --git a/frontend/src/scripts/drawing/drawables/i-drawable-descriptor.ts b/frontend/src/scripts/drawing/drawables/i-drawable-descriptor.ts new file mode 100644 index 0000000..bd85e4e --- /dev/null +++ b/frontend/src/scripts/drawing/drawables/i-drawable-descriptor.ts @@ -0,0 +1,5 @@ +export interface IDrawableDescriptor { + uniformName: string; + countMacroName: string; + shaderCombinationSteps: Array; +} diff --git a/frontend/src/scripts/drawing/primitives/i-primitive.ts b/frontend/src/scripts/drawing/drawables/i-drawable.ts similarity index 83% rename from frontend/src/scripts/drawing/primitives/i-primitive.ts rename to frontend/src/scripts/drawing/drawables/i-drawable.ts index 9b270a8..2e7d4d9 100644 --- a/frontend/src/scripts/drawing/primitives/i-primitive.ts +++ b/frontend/src/scripts/drawing/drawables/i-drawable.ts @@ -1,6 +1,6 @@ import { vec2 } from 'gl-matrix'; -export interface IPrimitive { +export interface IDrawable { serializeToUniforms(uniforms: any): void; distance(target: vec2): number; minimumDistance(target: vec2): number; diff --git a/frontend/src/scripts/drawing/lights/circle-light.ts b/frontend/src/scripts/drawing/drawables/lights/circle-light.ts similarity index 57% rename from frontend/src/scripts/drawing/lights/circle-light.ts rename to frontend/src/scripts/drawing/drawables/lights/circle-light.ts index 7e2e7ed..7468276 100644 --- a/frontend/src/scripts/drawing/lights/circle-light.ts +++ b/frontend/src/scripts/drawing/drawables/lights/circle-light.ts @@ -1,8 +1,14 @@ import { ILight } from './i-light'; import { vec2, vec3 } from 'gl-matrix'; +import { settings } from '../../settings'; +import { IDrawableDescriptor } from '../i-drawable-descriptor'; export class CircleLight implements ILight { - public static uniformName = 'lights'; + public static descriptor: IDrawableDescriptor = { + uniformName: 'lights', + countMacroName: 'lightCount', + shaderCombinationSteps: settings.shaderCombinations.circleLightSteps, + }; constructor( public center: vec2, @@ -11,8 +17,16 @@ export class CircleLight implements ILight { public lightness: number ) {} + distance(target: vec2): number { + return 0; + } + + minimumDistance(target: vec2): number { + return 0; + } + serializeToUniforms(uniforms: any): void { - const listName = CircleLight.uniformName; + const listName = CircleLight.descriptor.uniformName; if (!uniforms.hasOwnProperty(listName)) { uniforms[listName] = []; diff --git a/frontend/src/scripts/drawing/drawables/lights/i-light.ts b/frontend/src/scripts/drawing/drawables/lights/i-light.ts new file mode 100644 index 0000000..0ed88cd --- /dev/null +++ b/frontend/src/scripts/drawing/drawables/lights/i-light.ts @@ -0,0 +1,3 @@ +import { IDrawable } from '../i-drawable'; + +export interface ILight extends IDrawable {} diff --git a/frontend/src/scripts/drawing/lights/point-light.ts b/frontend/src/scripts/drawing/drawables/lights/point-light.ts similarity index 78% rename from frontend/src/scripts/drawing/lights/point-light.ts rename to frontend/src/scripts/drawing/drawables/lights/point-light.ts index c107805..0c18709 100644 --- a/frontend/src/scripts/drawing/lights/point-light.ts +++ b/frontend/src/scripts/drawing/drawables/lights/point-light.ts @@ -9,6 +9,12 @@ export class PointLight implements ILight { public color: vec3, public lightness: number ) {} + distance(target: vec2): number { + throw new Error('Method not implemented.'); + } + minimumDistance(target: vec2): number { + throw new Error('Method not implemented.'); + } serializeToUniforms(uniforms: any): void { const listName = PointLight.uniformName; diff --git a/frontend/src/scripts/drawing/primitives/circle.ts b/frontend/src/scripts/drawing/drawables/primitives/circle.ts similarity index 100% rename from frontend/src/scripts/drawing/primitives/circle.ts rename to frontend/src/scripts/drawing/drawables/primitives/circle.ts diff --git a/frontend/src/scripts/drawing/drawables/primitives/i-primitive.ts b/frontend/src/scripts/drawing/drawables/primitives/i-primitive.ts new file mode 100644 index 0000000..0f46971 --- /dev/null +++ b/frontend/src/scripts/drawing/drawables/primitives/i-primitive.ts @@ -0,0 +1,3 @@ +import { IDrawable } from '../i-drawable'; + +export interface IPrimitive extends IDrawable {} diff --git a/frontend/src/scripts/drawing/primitives/rectangle.ts b/frontend/src/scripts/drawing/drawables/primitives/rectangle.ts similarity index 100% rename from frontend/src/scripts/drawing/primitives/rectangle.ts rename to frontend/src/scripts/drawing/drawables/primitives/rectangle.ts diff --git a/frontend/src/scripts/drawing/primitives/tunnel-shape.ts b/frontend/src/scripts/drawing/drawables/primitives/tunnel-shape.ts similarity index 65% rename from frontend/src/scripts/drawing/primitives/tunnel-shape.ts rename to frontend/src/scripts/drawing/drawables/primitives/tunnel-shape.ts index 4cd4a91..5ab40ce 100644 --- a/frontend/src/scripts/drawing/primitives/tunnel-shape.ts +++ b/frontend/src/scripts/drawing/drawables/primitives/tunnel-shape.ts @@ -1,12 +1,17 @@ import { vec2 } from 'gl-matrix'; -import { clamp01 } from '../../helper/clamp'; -import { mix } from '../../helper/mix'; +import { clamp01 } from '../../../helper/clamp'; +import { mix } from '../../../helper/mix'; import { Circle } from './circle'; import { IPrimitive } from './i-primitive'; +import { IDrawableDescriptor } from '../i-drawable-descriptor'; +import { settings } from '../../settings'; export class TunnelShape implements IPrimitive { - public static uniformNameLines = 'lines'; - public static uniformNameRadii = 'radii'; + public static descriptor: IDrawableDescriptor = { + uniformName: 'lines', + countMacroName: 'lineCount', + shaderCombinationSteps: settings.shaderCombinations.lineSteps, + }; public readonly toFromDelta: vec2; private toFromDeltaLength: number; @@ -29,18 +34,16 @@ export class TunnelShape implements IPrimitive { } serializeToUniforms(uniforms: any): void { - if (!uniforms.hasOwnProperty(TunnelShape.uniformNameLines)) { - uniforms[TunnelShape.uniformNameLines] = []; + if (!uniforms.hasOwnProperty(TunnelShape.descriptor.uniformName)) { + uniforms[TunnelShape.descriptor.uniformName] = []; } - if (!uniforms.hasOwnProperty(TunnelShape.uniformNameRadii)) { - uniforms[TunnelShape.uniformNameRadii] = []; - } - - uniforms[TunnelShape.uniformNameLines].push(this.from); - uniforms[TunnelShape.uniformNameLines].push(this.toFromDelta); - uniforms[TunnelShape.uniformNameRadii].push(this.fromRadius); - uniforms[TunnelShape.uniformNameRadii].push(this.toRadius); + uniforms[TunnelShape.descriptor.uniformName].push({ + from: this.from, + toFromDelta: this.toFromDelta, + fromRadius: this.fromRadius, + toRadius: this.toRadius, + }); } public distance(target: vec2): number { diff --git a/frontend/src/scripts/drawing/graphics-library/helper/create-shader.ts b/frontend/src/scripts/drawing/graphics-library/helper/create-shader.ts index 0338fbe..0c6901f 100644 --- a/frontend/src/scripts/drawing/graphics-library/helper/create-shader.ts +++ b/frontend/src/scripts/drawing/graphics-library/helper/create-shader.ts @@ -1,15 +1,16 @@ +import { settings } from '../../settings'; + export const createShader = ( gl: WebGL2RenderingContext, type: GLenum, source: string, substitutions: { [name: string]: string } ): WebGLShader => { + const allSubstitutions = { ...settings.shaderMacros, ...substitutions }; + source = source.replace(/{(.+)}/gm, (_, name: string): string => { - const value = substitutions[name]; - if (Number.isInteger(value)) { - return `${value}.0`; - } - return value; + const value = allSubstitutions[name]; + return Number.isInteger(value) ? `${value}.0` : value; }); const shader = gl.createShader(type); diff --git a/frontend/src/scripts/drawing/graphics-library/helper/get-webgl2-context.ts b/frontend/src/scripts/drawing/graphics-library/helper/get-webgl2-context.ts new file mode 100644 index 0000000..426004c --- /dev/null +++ b/frontend/src/scripts/drawing/graphics-library/helper/get-webgl2-context.ts @@ -0,0 +1,11 @@ +export const getWebGl2Context = ( + canvas: HTMLCanvasElement +): WebGL2RenderingContext => { + const gl = canvas.getContext('webgl2'); + + if (!gl) { + throw new Error('WebGl2 is not supported'); + } + + return gl; +}; diff --git a/frontend/src/scripts/drawing/graphics-library/program/fragment-shader-only-program.ts b/frontend/src/scripts/drawing/graphics-library/program/fragment-shader-only-program.ts index 8bfd496..568c760 100644 --- a/frontend/src/scripts/drawing/graphics-library/program/fragment-shader-only-program.ts +++ b/frontend/src/scripts/drawing/graphics-library/program/fragment-shader-only-program.ts @@ -5,11 +5,10 @@ export class FragmentShaderOnlyProgram extends Program { constructor( gl: WebGL2RenderingContext, - vertexShaderSource: string, - fragmentShaderSource: string, + shaderSources: [string, string], substitutions: { [name: string]: string } ) { - super(gl, vertexShaderSource, fragmentShaderSource, substitutions); + super(gl, shaderSources, substitutions); this.prepareScreenQuad('vertexPosition'); } diff --git a/frontend/src/scripts/drawing/graphics-library/program/i-program.ts b/frontend/src/scripts/drawing/graphics-library/program/i-program.ts index 7bdb18c..27052f5 100644 --- a/frontend/src/scripts/drawing/graphics-library/program/i-program.ts +++ b/frontend/src/scripts/drawing/graphics-library/program/i-program.ts @@ -1,8 +1,8 @@ import { vec2 } from 'gl-matrix'; export interface IProgram { - bindAndSetUniforms(values: { [name: string]: any }): void; setDrawingRectangle(topLeft: vec2, size: vec2): void; + bindAndSetUniforms(values: { [name: string]: any }): void; draw(): void; delete(): void; } diff --git a/frontend/src/scripts/drawing/graphics-library/program/program.ts b/frontend/src/scripts/drawing/graphics-library/program/program.ts index 6c62d35..0440f5d 100644 --- a/frontend/src/scripts/drawing/graphics-library/program/program.ts +++ b/frontend/src/scripts/drawing/graphics-library/program/program.ts @@ -17,8 +17,7 @@ export abstract class Program implements IProgram { constructor( protected gl: WebGL2RenderingContext, - vertexShaderSource: string, - fragmentShaderSource: string, + [vertexShaderSource, fragmentShaderSource]: [string, string], substitutions: { [name: string]: string } ) { this.createProgram(vertexShaderSource, fragmentShaderSource, substitutions); diff --git a/frontend/src/scripts/drawing/graphics-library/program/uniform-array-autoscaling-program.ts b/frontend/src/scripts/drawing/graphics-library/program/uniform-array-autoscaling-program.ts index 1350c12..9b7a8dc 100644 --- a/frontend/src/scripts/drawing/graphics-library/program/uniform-array-autoscaling-program.ts +++ b/frontend/src/scripts/drawing/graphics-library/program/uniform-array-autoscaling-program.ts @@ -1,11 +1,14 @@ import { vec2 } from 'gl-matrix'; import { FragmentShaderOnlyProgram } from './fragment-shader-only-program'; import { IProgram } from './i-program'; +import { last } from '../../../helper/last'; +import { getCombinations } from '../../../helper/get-combinations'; +import { IDrawableDescriptor } from '../../drawables/i-drawable-descriptor'; export class UniformArrayAutoScalingProgram implements IProgram { private programs: Array<{ program: FragmentShaderOnlyProgram; - value: number; + values: Array; }> = []; private current: FragmentShaderOnlyProgram; @@ -14,45 +17,33 @@ export class UniformArrayAutoScalingProgram implements IProgram { constructor( private gl: WebGL2RenderingContext, - private vertexShaderSource: string, - private fragmentShaderSource: string, - private substitutions: { [name: string]: any }, - private options: { - getValueFromUniforms: (values: { [name: string]: any }) => number; - uniformArraySizeName: string; - enablingMacro: string; - startingValue: number; - steps: number; - maximumValue: number; - } + shaderSources: [string, string], + private options: Array ) { - for ( - let i = options.startingValue; - i < options.maximumValue; - i += options.steps - ) { - this.createProgram(i); + const names = options.map((o) => o.countMacroName); + for (let combination of getCombinations( + options.map((o) => o.shaderCombinationSteps) + )) { + this.createProgram(names, combination, shaderSources); } } - public bindAndSetUniforms(values: { [name: string]: any }): void { - let value = this.options.getValueFromUniforms(values); - value = Math.min(this.options.maximumValue, value); - - const closest = this.programs.find( - (p) => value < p.value && value + this.options.steps >= p.value + public bindAndSetUniforms(uniforms: { [name: string]: any }): void { + let values = this.options.map((o) => + uniforms[o.uniformName] ? uniforms[o.uniformName].length : 0 ); - if (closest) { - this.current = closest.program; - } else { - this.current = this.createProgram(value + this.options.steps); - } + + const closest = this.programs.find((p) => + p.values.every((v, i) => v > values[i]) + ); + + this.current = closest ? closest.program : last(this.programs).program; this.current.setDrawingRectangle( this.drawingRectangleTopLeft, this.drawingRectangleSize ); - this.current.bindAndSetUniforms(values); + this.current.bindAndSetUniforms(uniforms); } public setDrawingRectangle(topLeft: vec2, size: vec2) { @@ -68,21 +59,23 @@ export class UniformArrayAutoScalingProgram implements IProgram { this.programs.forEach((p) => p.program.delete()); } - private createProgram(arraySize: number): FragmentShaderOnlyProgram { + private createProgram( + names: Array, + combination: Array, + shaderSources: [string, string] + ): FragmentShaderOnlyProgram { + const substitutions = {}; + names.forEach((v, i) => (substitutions[v] = combination[i].toString())); + const program = new FragmentShaderOnlyProgram( this.gl, - this.vertexShaderSource, - this.fragmentShaderSource, - { - [this.options.uniformArraySizeName]: Math.floor(arraySize).toString(), - [this.options.enablingMacro]: arraySize > 0 ? '1' : '0', - ...this.substitutions, - } + shaderSources, + substitutions ); this.programs.push({ program, - value: arraySize, + values: combination, }); return program; diff --git a/frontend/src/scripts/drawing/rendering/i-renderer.ts b/frontend/src/scripts/drawing/i-renderer.ts similarity index 76% rename from frontend/src/scripts/drawing/rendering/i-renderer.ts rename to frontend/src/scripts/drawing/i-renderer.ts index 88c9afd..61ba749 100644 --- a/frontend/src/scripts/drawing/rendering/i-renderer.ts +++ b/frontend/src/scripts/drawing/i-renderer.ts @@ -1,6 +1,6 @@ import { vec2 } from 'gl-matrix'; -import { IPrimitive } from '../primitives/i-primitive'; -import { ILight } from '../lights/i-light'; +import { IPrimitive } from './drawables/primitives/i-primitive'; +import { ILight } from './drawables/lights/i-light'; export interface IRenderer { startFrame(deltaTime: DOMHighResTimeStamp): void; diff --git a/frontend/src/scripts/drawing/lights/i-light.ts b/frontend/src/scripts/drawing/lights/i-light.ts deleted file mode 100644 index 1e910dc..0000000 --- a/frontend/src/scripts/drawing/lights/i-light.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface ILight { - serializeToUniforms(uniforms: any): void; -} diff --git a/frontend/src/scripts/drawing/rendering/fps-autoscaler.ts b/frontend/src/scripts/drawing/rendering/fps-autoscaler.ts new file mode 100644 index 0000000..72fee3f --- /dev/null +++ b/frontend/src/scripts/drawing/rendering/fps-autoscaler.ts @@ -0,0 +1,54 @@ +import { Autoscaler } from '../../helper/autoscaler'; +import { settings } from '../settings'; +import { exponentialDecay } from '../../helper/exponential-decay'; +import { InfoText } from '../../objects/types/info-text'; +import { FrameBuffer } from '../graphics-library/frame-buffer/frame-buffer'; +import { toPercent } from '../../helper/to-percent'; + +export class FpsAutoscaler extends Autoscaler { + private timeSinceLastAdjusment = 0; + private exponentialDecayedDeltaTime = 0.0; + + constructor(private frameBuffers: Array) { + super( + frameBuffers.map((f) => (v) => (f.renderScale = v)), + settings.qualityScaling.scaleTargets, + settings.qualityScaling.startingTargetIndex, + settings.qualityScaling.scalingOptions + ); + } + + public autoscale(lastDeltaTime: DOMHighResTimeStamp) { + this.timeSinceLastAdjusment += lastDeltaTime; + if ( + this.timeSinceLastAdjusment >= + settings.qualityScaling.adjusmentRateInMilliseconds + ) { + this.timeSinceLastAdjusment = 0; + this.exponentialDecayedDeltaTime = exponentialDecay( + this.exponentialDecayedDeltaTime, + lastDeltaTime, + settings.qualityScaling.deltaTimeResponsiveness + ); + + if ( + this.exponentialDecayedDeltaTime <= + settings.qualityScaling.targetDeltaTimeInMilliseconds - + settings.qualityScaling.deltaTimeError + ) { + this.increase(); + } else if ( + this.exponentialDecayedDeltaTime > + settings.qualityScaling.targetDeltaTimeInMilliseconds + + settings.qualityScaling.deltaTimeError + ) { + this.decrease(); + } + } + + InfoText.modifyRecord( + 'quality', + this.frameBuffers.map((f) => toPercent(f.renderScale)).join(', ') + ); + } +} diff --git a/frontend/src/scripts/drawing/rendering/rendering-pass.ts b/frontend/src/scripts/drawing/rendering/rendering-pass.ts index 5a892b6..e20d8bb 100644 --- a/frontend/src/scripts/drawing/rendering/rendering-pass.ts +++ b/frontend/src/scripts/drawing/rendering/rendering-pass.ts @@ -1,8 +1,105 @@ -import { IProgram } from '../graphics-library/program/i-program'; import { FrameBuffer } from '../graphics-library/frame-buffer/frame-buffer'; +import { IDrawable } from '../drawables/i-drawable'; +import { settings } from '../settings'; +import { vec2 } from 'gl-matrix'; +import { Circle } from '../drawables/primitives/circle'; +import { InfoText } from '../../objects/types/info-text'; +import { UniformArrayAutoScalingProgram } from '../graphics-library/program/uniform-array-autoscaling-program'; +import { IDrawableDescriptor } from '../drawables/i-drawable-descriptor'; export class RenderingPass { - constructor(private program: IProgram, private frame: FrameBuffer) {} + private drawables: Array = []; + private program: UniformArrayAutoScalingProgram; - public render(uniforms: any, inputTexture?: WebGLTexture) {} + constructor( + gl: WebGL2RenderingContext, + shaderSources: [string, string], + private drawableDescriptors: Array, + private frame: FrameBuffer + ) { + this.program = new UniformArrayAutoScalingProgram( + gl, + shaderSources, + drawableDescriptors + ); + } + + public addDrawable(drawable: IDrawable) { + this.drawables.push(drawable); + } + + public render( + commonUniforms: any, + viewCircle: Circle, + inputTexture?: WebGLTexture + ) { + this.frame.bindAndClear(inputTexture); + const q = 1 / settings.tileMultiplier; + const tileUvSize = vec2.fromValues(q, q); + + const possiblyOnScreenDrawables = this.drawables.filter( + (p) => p.minimumDistance(viewCircle.center) < viewCircle.radius + ); + + const origin = vec2.transformMat2d( + vec2.create(), + vec2.fromValues(0, 0), + commonUniforms.uvToWorld + ); + + const firstCenter = vec2.transformMat2d( + vec2.create(), + vec2.fromValues(q, q), + commonUniforms.uvToWorld + ); + + vec2.subtract(firstCenter, firstCenter, origin); + + const worldR = vec2.length(firstCenter); + + let sumLineCount = 0; + + for (let x = 0; x < 1; x += q) { + for (let y = 0; y < 1; y += q) { + const uniforms = { ...commonUniforms }; + uniforms.maxMinDistance = 2 * worldR; + + const uvBottomLeft = vec2.fromValues(x, y); + this.program.setDrawingRectangle(uvBottomLeft, tileUvSize); + + const tileCenterWorldCoordinates = vec2.transformMat2d( + vec2.create(), + vec2.add(vec2.create(), uvBottomLeft, vec2.fromValues(q, q)), + uniforms.uvToWorld + ); + + const primitivesNearTile = possiblyOnScreenDrawables.filter( + (p) => p.distance(tileCenterWorldCoordinates) < 2 * worldR + ); + + sumLineCount += primitivesNearTile.length; + + primitivesNearTile.forEach((p) => p.serializeToUniforms(uniforms)); + + this.program.bindAndSetUniforms(uniforms); + this.program.draw(); + } + } + + this.drawables = []; + + InfoText.modifyRecord( + 'nearby ' + this.drawableDescriptors[0].countMacroName, + possiblyOnScreenDrawables.length.toFixed(2) + ); + + InfoText.modifyRecord( + 'drawn ' + this.drawableDescriptors[0].countMacroName, + ( + sumLineCount / + settings.tileMultiplier / + settings.tileMultiplier + ).toFixed(2) + ); + } } diff --git a/frontend/src/scripts/drawing/rendering/webgl2-renderer.ts b/frontend/src/scripts/drawing/rendering/webgl2-renderer.ts index e4df410..1e66c3f 100644 --- a/frontend/src/scripts/drawing/rendering/webgl2-renderer.ts +++ b/frontend/src/scripts/drawing/rendering/webgl2-renderer.ts @@ -1,24 +1,28 @@ import { mat2d, vec2 } from 'gl-matrix'; -import { InfoText } from '../../objects/types/info-text'; +import caveFragmentShader from '../shaders/cave-distance-fs.glsl'; +import lightsFragmentShader from '../shaders/lights-shading-fs.glsl'; +import caveVertexShader from '../shaders/passthrough-distance-vs.glsl'; +import lightsVertexShader from '../shaders/passthrough-shading-vs.glsl'; +// import lightsShader from '../shaders/rainbow-shading-fs.glsl'; import { DefaultFrameBuffer } from '../graphics-library/frame-buffer/default-frame-buffer'; import { IntermediateFrameBuffer } from '../graphics-library/frame-buffer/intermediate-frame-buffer'; import { WebGlStopwatch } from '../graphics-library/helper/stopwatch'; import { IProgram } from '../graphics-library/program/i-program'; import { UniformArrayAutoScalingProgram } from '../graphics-library/program/uniform-array-autoscaling-program'; -import { IRenderer } from './i-renderer'; -import { Circle } from '../primitives/circle'; -import { IPrimitive } from '../primitives/i-primitive'; -import { Rectangle } from '../primitives/rectangle'; -import { TunnelShape } from '../primitives/tunnel-shape'; -import { Autoscaler } from './autoscaler'; -import { ILight } from '../lights/i-light'; -import { toPercent } from '../../helper/to-percent'; -import { exponentialDecay } from '../../helper/exponential-decay'; -import { settings } from './settings'; +import { IRenderer } from '../i-renderer'; +import { Circle } from '../drawables/primitives/circle'; +import { IPrimitive } from '../drawables/primitives/i-primitive'; +import { Rectangle } from '../drawables/primitives/rectangle'; +import { ILight } from '../drawables/lights/i-light'; +import { settings } from '../settings'; +import { FpsAutoscaler } from './fps-autoscaler'; +import { getWebGl2Context } from '../graphics-library/helper/get-webgl2-context'; +import { RenderingPass } from './rendering-pass'; +import { TunnelShape } from '../drawables/primitives/tunnel-shape'; +import { CircleLight } from '../drawables/lights/circle-light'; export class WebGl2Renderer implements IRenderer { private gl: WebGL2RenderingContext; - private qualityScaler: Autoscaler; private stopwatch?: WebGlStopwatch; private viewBox: Rectangle = new Rectangle(); @@ -28,131 +32,51 @@ export class WebGl2Renderer implements IRenderer { private distanceFieldFrameBuffer: IntermediateFrameBuffer; private lightingFrameBuffer: DefaultFrameBuffer; - private distanceProgram: IProgram; - private lightingProgram: IProgram; + private distancePass: RenderingPass; + private lightingPass: RenderingPass; - private primitives: Array; - private lights: Array; + private autoscaler: FpsAutoscaler; - constructor( - private canvas: HTMLCanvasElement, - private overlay: HTMLElement, - shaderSources: Array<[string, string]> - ) { - this.getContext(); - this.createPipeline(shaderSources); - this.setupAutoscaling(); + constructor(private canvas: HTMLCanvasElement, private overlay: HTMLElement) { + this.gl = getWebGl2Context(canvas); + + this.distanceFieldFrameBuffer = new IntermediateFrameBuffer(this.gl); + this.lightingFrameBuffer = new DefaultFrameBuffer(this.gl); + + this.distancePass = new RenderingPass( + this.gl, + [caveVertexShader, caveFragmentShader], + [TunnelShape.descriptor], + this.distanceFieldFrameBuffer + ); + + this.lightingPass = new RenderingPass( + this.gl, + [lightsVertexShader, lightsFragmentShader], + [CircleLight.descriptor], + this.lightingFrameBuffer + ); + + this.autoscaler = new FpsAutoscaler([ + this.lightingFrameBuffer, + this.distanceFieldFrameBuffer, + ]); try { this.stopwatch = new WebGlStopwatch(this.gl); } catch {} } - private getContext() { - this.gl = this.canvas.getContext('webgl2'); - if (!this.gl) { - throw new Error('WebGl2 is not supported'); - } - } - - private createPipeline(shaderSources: Array<[string, string]>) { - const distanceScale = settings.shaderUniforms.distanceScale; - - this.distanceFieldFrameBuffer = new IntermediateFrameBuffer(this.gl); - this.distanceProgram = new UniformArrayAutoScalingProgram( - this.gl, - shaderSources[0][0], - shaderSources[0][1], - { ...settings.shaderUniforms }, - { - getValueFromUniforms: (v) => (v.lines ? v.lines.length / 2 : 0), - uniformArraySizeName: 'lineCount', - startingValue: 0, - enablingMacro: 'linesEnabled', - steps: 1, - maximumValue: 15, - } - ); - - this.lightingFrameBuffer = new DefaultFrameBuffer(this.gl); - this.lightingProgram = new UniformArrayAutoScalingProgram( - this.gl, - shaderSources[1][0], - shaderSources[1][1], - { ...settings.shaderUniforms }, - { - getValueFromUniforms: (v) => (v.lights ? v.lights.length : 0), - uniformArraySizeName: 'lightCount', - startingValue: 1, - enablingMacro: null, - steps: 1, - maximumValue: 8, - } - ); - } - - private setupAutoscaling() { - this.qualityScaler = new Autoscaler( - [ - (v) => (this.lightingFrameBuffer.renderScale = v), - (v) => (this.distanceFieldFrameBuffer.renderScale = v), - ], - settings.qualityScaling.scaleTargets, - settings.qualityScaling.startingTargetIndex, - settings.qualityScaling.scalingOptions - ); - } - - private timeSinceLastAdjusment = 0; - private exponentialDecayedDeltaTime = 0.0; - private configureQuality(deltaTime: DOMHighResTimeStamp) { - this.timeSinceLastAdjusment += deltaTime; - if ( - this.timeSinceLastAdjusment >= - settings.qualityScaling.adjusmentRateInMilliseconds - ) { - this.timeSinceLastAdjusment = 0; - this.exponentialDecayedDeltaTime = exponentialDecay( - this.exponentialDecayedDeltaTime, - deltaTime, - settings.qualityScaling.deltaTimeResponsiveness - ); - - if ( - this.exponentialDecayedDeltaTime <= - settings.qualityScaling.targetDeltaTimeInMilliseconds - - settings.qualityScaling.deltaTimeError - ) { - this.qualityScaler.increase(); - } else if ( - this.exponentialDecayedDeltaTime > - settings.qualityScaling.targetDeltaTimeInMilliseconds + - settings.qualityScaling.deltaTimeError - ) { - this.qualityScaler.decrease(); - } - } - - InfoText.modifyRecord( - 'quality', - `${toPercent(this.distanceFieldFrameBuffer.renderScale)}, ${toPercent( - this.lightingFrameBuffer.renderScale - )}` - ); - } - public drawPrimitive(primitive: IPrimitive) { - this.primitives.push(primitive); + this.distancePass.addDrawable(primitive); } public drawLight(light: ILight) { - this.lights.push(light); + this.lightingPass.addDrawable(light); } public startFrame(deltaTime: DOMHighResTimeStamp): void { - this.configureQuality(deltaTime); - this.primitives = []; - this.lights = []; + this.autoscaler.autoscale(deltaTime); this.stopwatch?.start(); this.distanceFieldFrameBuffer.setSize(); @@ -160,84 +84,13 @@ export class WebGl2Renderer implements IRenderer { } public finishFrame() { - const uniforms: any = this.calculateOwnUniforms(); - - this.lights.forEach((l) => l.serializeToUniforms(uniforms)); - - this.distanceFieldFrameBuffer.bindAndClear(); - const q = 1 / settings.tileMultiplier; - const uvSize = vec2.fromValues(q, q); - - const possiblyOnScreenPrimitives = this.primitives.filter( - (p) => p.minimumDistance(this.viewCircle.center) < this.viewCircle.radius - ) as Array; - - InfoText.modifyRecord( - 'nearby lines', - possiblyOnScreenPrimitives.length.toString() - ); - - const origin = vec2.transformMat2d( - vec2.create(), - vec2.fromValues(0, 0), - uniforms.uvToWorld - ); - - const firstCenter = vec2.transformMat2d( - vec2.create(), - vec2.fromValues(q, q), - uniforms.uvToWorld - ); - - vec2.subtract(firstCenter, firstCenter, origin); - - const worldR = vec2.length(firstCenter); - uniforms.maxMinDistance = 2 * worldR; - - let sumLineCount = 0; - - for (let x = 0; x < 1; x += q) { - for (let y = 0; y < 1; y += q) { - const uvBottomLeft = vec2.fromValues(x, y); - this.distanceProgram.setDrawingRectangle(uvBottomLeft, uvSize); - - const tileCenterWorldCoordinates = vec2.transformMat2d( - vec2.create(), - vec2.add(vec2.create(), uvBottomLeft, vec2.fromValues(q, q)), - uniforms.uvToWorld - ); - - const primitivesNearTile = possiblyOnScreenPrimitives.filter( - (p) => p.distance(tileCenterWorldCoordinates) < 2 * worldR - ); - - sumLineCount += primitivesNearTile.length; - - uniforms.lines = []; - uniforms.radii = []; - - primitivesNearTile.forEach((p) => p.serializeToUniforms(uniforms)); - - this.distanceProgram.bindAndSetUniforms(uniforms); - this.distanceProgram.draw(); - } - } - - InfoText.modifyRecord( - 'lines', - ( - sumLineCount / - settings.tileMultiplier / - settings.tileMultiplier - ).toFixed(2) - ); - - this.lightingFrameBuffer.bindAndClear( + const uniforms = this.calculateOwnUniforms(); + this.distancePass.render(uniforms, this.viewCircle); + this.lightingPass.render( + uniforms, + this.viewCircle, this.distanceFieldFrameBuffer.colorTexture ); - this.lightingProgram.bindAndSetUniforms(uniforms); - this.lightingProgram.draw(); - this.stopwatch?.stop(); } diff --git a/frontend/src/scripts/drawing/rendering/settings.ts b/frontend/src/scripts/drawing/settings.ts similarity index 81% rename from frontend/src/scripts/drawing/rendering/settings.ts rename to frontend/src/scripts/drawing/settings.ts index 4ce8c6e..ce3b1dc 100644 --- a/frontend/src/scripts/drawing/rendering/settings.ts +++ b/frontend/src/scripts/drawing/settings.ts @@ -20,8 +20,12 @@ export const settings = { }, }, tileMultiplier: 5, - shaderUniforms: { + shaderMacros: { distanceScale: 64, edgeSmoothing: 10, }, + shaderCombinations: { + lineSteps: [0, 1, 2, 3, 4, 8, 16, 32], + circleLightSteps: [0, 1, 2, 3], + }, }; diff --git a/frontend/src/shaders/cave-distance-fs.glsl b/frontend/src/scripts/drawing/shaders/cave-distance-fs.glsl similarity index 70% rename from frontend/src/shaders/cave-distance-fs.glsl rename to frontend/src/scripts/drawing/shaders/cave-distance-fs.glsl index 10c14aa..48fa77e 100644 --- a/frontend/src/shaders/cave-distance-fs.glsl +++ b/frontend/src/scripts/drawing/shaders/cave-distance-fs.glsl @@ -2,7 +2,6 @@ precision mediump float; -#define LINES_ENABLED {linesEnabled} #define LINE_COUNT {lineCount} #define CAVE_COLOR vec3(0.36, 0.38, 0.76) #define AIR_COLOR vec3(0.7) @@ -11,27 +10,30 @@ precision mediump float; uniform float maxMinDistance; -#if LINES_ENABLED - // start, end - start - uniform vec2[LINE_COUNT * 2] lines; - // startRadius, endRadius - uniform float[LINE_COUNT * 2] radii; +#if LINE_COUNT > 0 + uniform struct Line { + vec2 from; + vec2 toFromDelta; + float fromRadius; + float toRadius; + }[LINE_COUNT] lines; #endif in vec2 worldCoordinates; out vec4 fragmentColor; void main() { - #if LINES_ENABLED + #if LINE_COUNT > 0 float minDistance = maxMinDistance; for (int i = 0; i < LINE_COUNT; i++) { - vec2 pa = worldCoordinates - lines[2 * i]; - vec2 ba = lines[2 * i + 1]; + Line line = lines[i]; + vec2 pa = worldCoordinates - line.from; + vec2 ba = line.toFromDelta; float baLength = length(ba); // todo: do we really want this dot(pa / baLength, ba / baLength) float h = clamp(dot(pa / baLength, ba / baLength), 0.0, 1.0); - float lineDistance = distance(pa, ba * h) - mix(radii[2 * i], radii[2 * i + 1], h); + float lineDistance = distance(pa, ba * h) - mix(line.fromRadius, line.toRadius, h); minDistance = min(minDistance, lineDistance); } diff --git a/frontend/src/scripts/drawing/shaders/lights-shading-fs.glsl b/frontend/src/scripts/drawing/shaders/lights-shading-fs.glsl new file mode 100644 index 0000000..eff6fff --- /dev/null +++ b/frontend/src/scripts/drawing/shaders/lights-shading-fs.glsl @@ -0,0 +1,100 @@ +#version 300 es + +precision mediump float; + +#define INFINITY 1000.0 +#define LIGHT_DROP 500.0 +#define MIN_STEP 1.0 +#define AMBIENT_LIGHT vec3(0.15) + +#define LIGHT_COUNT {lightCount} +#define DISTANCE_SCALE {distanceScale} +#define EDGE_SMOOTHING {edgeSmoothing} + +#if LIGHT_COUNT > 0 + uniform struct Light { + vec2 center; + float radius; + vec3 value; + }[LIGHT_COUNT] lights; + + uniform sampler2D distanceTexture; + uniform vec2 viewBoxSize; + + in vec2[LIGHT_COUNT] directions; + + float getDistance(in vec2 target, out vec3 color) { + vec4 values = texture(distanceTexture, target); + color = values.rgb; + return values.w * DISTANCE_SCALE; + } + + float getDistance(in vec2 target) { + return texture(distanceTexture, target).w * DISTANCE_SCALE; + } + + float getFractionOfLightArriving( + in vec2 target, + in vec2 direction, + in float startingDistance, + in float lightDistance, + in float lightRadius + ) { + float q = 1.0; + float rayLength = startingDistance; + float movingAverageMeanDistance = startingDistance; + + direction /= viewBoxSize; + + for (int j = 0; j < 48; j++) { + float minDistance = getDistance(target + direction * rayLength); + movingAverageMeanDistance = movingAverageMeanDistance / 2.0 + minDistance / 2.0; + q = min(q, movingAverageMeanDistance / rayLength); + + rayLength += max(MIN_STEP, minDistance); + if (rayLength > lightDistance) { + return clamp(q * (lightDistance + lightRadius) / lightRadius, 0.0, 1.0); + } + } + + return 0.0; + } +#endif + +in vec2 worldCoordinates; +in vec2 uvCoordinates; +out vec4 fragmentColor; + +void main() { + #if LIGHT_COUNT > 0 + + vec3 colorAtPosition; + float startingDistance = getDistance(uvCoordinates, colorAtPosition); + + vec3 ligthing = vec3(0.0); + + for (int i = 0; i < LIGHT_COUNT; i++) { + Light light = lights[i]; + float lightDistance = distance(light.center, worldCoordinates); + + float r = (lightDistance + light.radius) / LIGHT_DROP + 1.0; + vec3 lightColorAtPosition = light.value / (r * r); + + float fractionOfLightArriving = getFractionOfLightArriving( + uvCoordinates, normalize(directions[i]), startingDistance, + lightDistance, light.radius + ); + + ligthing += lightColorAtPosition * fractionOfLightArriving; + } + + fragmentColor = vec4( + colorAtPosition * (AMBIENT_LIGHT + + step(EDGE_SMOOTHING / 2.0, clamp(startingDistance, 0.0, EDGE_SMOOTHING)) * ligthing), + 1.0 + ); + + #else + fragmentColor = vec4(1.0); + #endif +} diff --git a/frontend/src/shaders/passthrough-distance-vs.glsl b/frontend/src/scripts/drawing/shaders/passthrough-distance-vs.glsl similarity index 100% rename from frontend/src/shaders/passthrough-distance-vs.glsl rename to frontend/src/scripts/drawing/shaders/passthrough-distance-vs.glsl diff --git a/frontend/src/scripts/drawing/shaders/passthrough-shading-vs.glsl b/frontend/src/scripts/drawing/shaders/passthrough-shading-vs.glsl new file mode 100644 index 0000000..6a93194 --- /dev/null +++ b/frontend/src/scripts/drawing/shaders/passthrough-shading-vs.glsl @@ -0,0 +1,39 @@ +#version 300 es + +precision mediump float; + +#define LIGHT_COUNT {lightCount} + +uniform mat3 modelTransform; +uniform mat3 ndcToUv; +uniform mat3 uvToWorld; + +in vec4 vertexPosition; + +out vec2 worldCoordinates; +out vec2 uvCoordinates; + +#if LIGHT_COUNT > 0 + uniform struct Light { + vec2 center; + float radius; + vec3 value; + }[LIGHT_COUNT] lights; + + out vec2[LIGHT_COUNT] directions; +#endif + +void main() { + vec3 vertexPosition2D = vec3(vertexPosition.xy, 1.0) * modelTransform; + vec3 uvCoordinates1 = vertexPosition2D * ndcToUv; + worldCoordinates = (uvCoordinates1 * uvToWorld).xy; + uvCoordinates = (uvCoordinates1).xy; + + #if LIGHT_COUNT > 0 + for (int i = 0; i < LIGHT_COUNT; i++) { + directions[i] = lights[i].center - worldCoordinates; + } + #endif + + gl_Position = vec4(vertexPosition2D.xy, 0.0, 1.0); +} diff --git a/frontend/src/shaders/rainbow-shading-fs.glsl b/frontend/src/scripts/drawing/shaders/rainbow-shading-fs.glsl similarity index 99% rename from frontend/src/shaders/rainbow-shading-fs.glsl rename to frontend/src/scripts/drawing/shaders/rainbow-shading-fs.glsl index 9c9b0c6..8ef59b2 100644 --- a/frontend/src/shaders/rainbow-shading-fs.glsl +++ b/frontend/src/scripts/drawing/shaders/rainbow-shading-fs.glsl @@ -2,7 +2,6 @@ precision mediump float; - vec3 rainbow(float level) { float r = float(level <= 2.0) + float(level > 4.0) * 0.5; float g = max(1.0 - abs(level - 2.0) * 0.5, 0.0); diff --git a/frontend/src/shaders/random.frag b/frontend/src/scripts/drawing/shaders/random.frag similarity index 100% rename from frontend/src/shaders/random.frag rename to frontend/src/scripts/drawing/shaders/random.frag diff --git a/frontend/src/scripts/game.ts b/frontend/src/scripts/game.ts index ae7b490..d37bc36 100644 --- a/frontend/src/scripts/game.ts +++ b/frontend/src/scripts/game.ts @@ -1,8 +1,3 @@ -import caveFragmentShader from '../shaders/cave-distance-fs.glsl'; -import lightsShader from '../shaders/lights-shading-fs.glsl'; -import caveVertexShader from '../shaders/passthrough-distance-vs.glsl'; -import lightsVertexShader from '../shaders/passthrough-shading-vs.glsl'; -// import lightsShader from '../shaders/rainbow-shading-fs.glsl'; import { CommandBroadcaster } from './commands/command-broadcaster'; import { BeforeRenderCommand } from './commands/types/before-render'; import { RenderCommand } from './commands/types/draw'; @@ -41,10 +36,7 @@ export class Game { [this.objects] ); - this.renderer = new WebGl2Renderer(canvas, overlay, [ - [caveVertexShader, caveFragmentShader], - [lightsVertexShader, lightsShader], - ]); + this.renderer = new WebGl2Renderer(canvas, overlay); this.initializeScene(); requestAnimationFrame(this.gameLoop.bind(this)); diff --git a/frontend/src/scripts/drawing/rendering/autoscaler.ts b/frontend/src/scripts/helper/autoscaler.ts similarity index 92% rename from frontend/src/scripts/drawing/rendering/autoscaler.ts rename to frontend/src/scripts/helper/autoscaler.ts index e4437f2..e4d7557 100644 --- a/frontend/src/scripts/drawing/rendering/autoscaler.ts +++ b/frontend/src/scripts/helper/autoscaler.ts @@ -1,5 +1,5 @@ -import { mix } from '../../helper/mix'; -import { clamp } from '../../helper/clamp'; +import { mix } from './mix'; +import { clamp } from './clamp'; export class Autoscaler { // can have fractions diff --git a/frontend/src/scripts/helper/get-combinations.ts b/frontend/src/scripts/helper/get-combinations.ts new file mode 100644 index 0000000..4d298ab --- /dev/null +++ b/frontend/src/scripts/helper/get-combinations.ts @@ -0,0 +1,31 @@ +export const getCombinations = ( + values: Array> +): Array> => { + if (!values.every((a) => a.length > 0)) { + return []; + } + + const result: Array> = []; + const counters = values.map((_) => 0); + + const increaseCounter = (i: number) => { + if (i >= counters.length) { + return false; + } + + counters[i]++; + + if (counters[i] >= values[i].length) { + counters[i] = 0; + return increaseCounter(i + 1); + } + + return true; + }; + + do { + result.push(values.map((v, i) => v[counters[i]])); + } while (increaseCounter(0)); + + return result; +}; diff --git a/frontend/src/scripts/objects/types/lamp.ts b/frontend/src/scripts/objects/types/lamp.ts index 32ec497..da9f868 100644 --- a/frontend/src/scripts/objects/types/lamp.ts +++ b/frontend/src/scripts/objects/types/lamp.ts @@ -2,7 +2,7 @@ import { vec2, vec3 } from 'gl-matrix'; import { RenderCommand } from '../../commands/types/draw'; import { MoveToCommand } from '../../commands/types/move-to'; import { GameObject } from '../game-object'; -import { CircleLight } from '../../drawing/lights/circle-light'; +import { CircleLight } from '../../drawing/drawables/lights/circle-light'; const range = 2000; diff --git a/frontend/src/scripts/objects/types/tunnel.ts b/frontend/src/scripts/objects/types/tunnel.ts index f4ad078..9c647b0 100644 --- a/frontend/src/scripts/objects/types/tunnel.ts +++ b/frontend/src/scripts/objects/types/tunnel.ts @@ -1,6 +1,6 @@ import { vec2 } from 'gl-matrix'; import { RenderCommand } from '../../commands/types/draw'; -import { TunnelShape } from '../../drawing/primitives/tunnel-shape'; +import { TunnelShape } from '../../drawing/drawables/primitives/tunnel-shape'; import { GameObject } from '../game-object'; export interface Line {} diff --git a/frontend/src/scripts/objects/world/create-dungeon.ts b/frontend/src/scripts/objects/world/create-dungeon.ts index 8307ee1..91541d7 100644 --- a/frontend/src/scripts/objects/world/create-dungeon.ts +++ b/frontend/src/scripts/objects/world/create-dungeon.ts @@ -17,7 +17,7 @@ export const createDungeon = (objects: ObjectContainer) => { new Tunnel(previousEnd, currentEnd, previousRadius, currentToRadius) ); - if (deltaHeight > 0 && Math.random() > 0.8) { + /*if (deltaHeight > 0 && Math.random() > 0.8) { objects.addObject( new Lamp( currentEnd, @@ -30,7 +30,7 @@ export const createDungeon = (objects: ObjectContainer) => { 1 ) ); - } + }*/ previousEnd = currentEnd; previousRadius = currentToRadius; diff --git a/frontend/src/shaders/lights-shading-fs.glsl b/frontend/src/shaders/lights-shading-fs.glsl deleted file mode 100644 index bb6508d..0000000 --- a/frontend/src/shaders/lights-shading-fs.glsl +++ /dev/null @@ -1,91 +0,0 @@ -#version 300 es - -precision mediump float; - -#define INFINITY 1000.0 -#define AMBIENT_LIGHT vec3(0.15) -#define LIGHT_DROP 500.0 -#define MIN_STEP 1.0 -#define LIGHT_COUNT 1 -#define DISTANCE_SCALE {distanceScale} -#define EDGE_SMOOTHING {edgeSmoothing} - -uniform struct Light { - vec2 center; - float radius; - vec3 value; -}[LIGHT_COUNT] lights; - -uniform sampler2D distanceTexture; -uniform vec2 viewBoxSize; - -in vec2[LIGHT_COUNT] directions; - -float getDistance(in vec2 target, out vec3 color) { - vec4 values = texture(distanceTexture, target); - color = values.rgb; - return values.w * DISTANCE_SCALE; -} - -float getDistance(in vec2 target) { - return texture(distanceTexture, target).w * DISTANCE_SCALE; -} - -float getFractionOfLightArriving( - in vec2 target, - in vec2 direction, - in float startingDistance, - in float lightDistance, - in float lightRadius -) { - float q = 1.0; - float rayLength = startingDistance; - float movingAverageMeanDistance = startingDistance; - - direction /= viewBoxSize; - - for (int j = 0; j < 32; j++) { - float minDistance = getDistance(target + direction * rayLength); - movingAverageMeanDistance = movingAverageMeanDistance / 2.0 + minDistance / 2.0; - q = min(q, movingAverageMeanDistance / rayLength); - - rayLength += max(MIN_STEP, minDistance); - if (rayLength > lightDistance) { - return clamp(q * (lightDistance + lightRadius) / lightRadius, 0.0, 1.0); - } - } - - return 0.0; -} - -in vec2 worldCoordinates; -in vec2 uvCoordinates; -out vec4 fragmentColor; - -void main() { - vec3 colorAtPosition; - float startingDistance = getDistance(uvCoordinates, colorAtPosition); - - vec3 ligthing = vec3(0.0); - - for (int i = 0; i < LIGHT_COUNT; i++) { - Light light = lights[i]; - float lightDistance = distance(light.center, worldCoordinates); - - float r = (lightDistance + light.radius) / LIGHT_DROP + 1.0; - vec3 lightColorAtPosition = light.value / (r * r); - - float fractionOfLightArriving = getFractionOfLightArriving( - uvCoordinates, normalize(directions[i]), startingDistance, - lightDistance, light.radius - ); - - ligthing += lightColorAtPosition * fractionOfLightArriving; - } - - fragmentColor = vec4( - colorAtPosition * (AMBIENT_LIGHT + - step(EDGE_SMOOTHING / 2.0, clamp(startingDistance, 0.0, EDGE_SMOOTHING)) * ligthing), - 1.0 - ); -} diff --git a/frontend/src/shaders/passthrough-shading-vs.glsl b/frontend/src/shaders/passthrough-shading-vs.glsl deleted file mode 100644 index f3655e3..0000000 --- a/frontend/src/shaders/passthrough-shading-vs.glsl +++ /dev/null @@ -1,33 +0,0 @@ -#version 300 es - -precision mediump float; - -#define LIGHT_COUNT 1 -uniform mat3 ndcToUv; -uniform mat3 uvToWorld; - -in vec4 vertexPosition; - -out vec2 worldCoordinates; -out vec2 uvCoordinates; - -uniform struct Light { - vec2 center; - float radius; - vec3 value; -}[LIGHT_COUNT] lights; - -out vec2[LIGHT_COUNT] directions; - -void main() { - vec3 vertexPosition2D = vec3(vertexPosition.xy, 1.0); - vec3 uvCoordinates1 = vertexPosition2D * ndcToUv; - worldCoordinates = (uvCoordinates1 * uvToWorld).xy; - uvCoordinates = (uvCoordinates1).xy; - - for (int i = 0; i < LIGHT_COUNT; i++) { - directions[i] = lights[i].center - worldCoordinates; - } - - gl_Position = vertexPosition; -}