From 05682dd2d55869e5f2dc4677fd06aa0f7449052e Mon Sep 17 00:00:00 2001 From: schmelczerandras Date: Tue, 18 Aug 2020 22:36:30 +0200 Subject: [PATCH] Render in NDC --- .../drawing/drawables/drawable-blob.ts | 1 + .../drawing/drawables/drawable-tunnel.ts | 3 +- .../drawables/i-drawable-descriptor.ts | 3 ++ .../drawing/drawables/lights/circle-light.ts | 3 +- .../drawing/drawables/lights/point-light.ts | 1 + .../uniform-array-autoscaling-program.ts | 23 ++++++--- .../drawing/rendering/rendering-pass.ts | 14 ++--- .../drawing/rendering/webgl2-renderer.ts | 51 ++++++++++++++----- frontend/src/scripts/drawing/settings.ts | 7 ++- .../drawing/shaders/cave-distance-fs.glsl | 6 +-- .../drawing/shaders/lights-shading-fs.glsl | 18 +++---- .../shaders/passthrough-distance-vs.glsl | 7 ++- .../shaders/passthrough-shading-vs.glsl | 25 ++++----- frontend/src/scripts/input/mouse-listener.ts | 6 +-- .../src/scripts/objects/types/character.ts | 3 -- frontend/src/scripts/shapes/types/blob.ts | 17 ++----- 16 files changed, 106 insertions(+), 82 deletions(-) diff --git a/frontend/src/scripts/drawing/drawables/drawable-blob.ts b/frontend/src/scripts/drawing/drawables/drawable-blob.ts index 9a63575..31d3d0e 100644 --- a/frontend/src/scripts/drawing/drawables/drawable-blob.ts +++ b/frontend/src/scripts/drawing/drawables/drawable-blob.ts @@ -9,6 +9,7 @@ export class DrawableBlob extends Blob implements IDrawable { uniformName: 'blobs', countMacroName: 'blobCount', shaderCombinationSteps: settings.shaderCombinations.blobSteps, + empty: new DrawableBlob(vec2.fromValues(0, 0)), }; public serializeToUniforms(uniforms: any, scale: number, transform: mat2d): void { diff --git a/frontend/src/scripts/drawing/drawables/drawable-tunnel.ts b/frontend/src/scripts/drawing/drawables/drawable-tunnel.ts index ff734d7..7076c32 100644 --- a/frontend/src/scripts/drawing/drawables/drawable-tunnel.ts +++ b/frontend/src/scripts/drawing/drawables/drawable-tunnel.ts @@ -9,6 +9,7 @@ export class DrawableTunnel extends TunnelShape implements IDrawable { uniformName: 'lines', countMacroName: 'lineCount', shaderCombinationSteps: settings.shaderCombinations.lineSteps, + empty: new DrawableTunnel(vec2.fromValues(0, 0), vec2.fromValues(0, 0), 0, 0), }; public serializeToUniforms(uniforms: any, scale: number, transform: mat2d): void { @@ -19,7 +20,7 @@ export class DrawableTunnel extends TunnelShape implements IDrawable { uniforms[uniformName].push({ from: vec2.transformMat2d(vec2.create(), this.from, transform), - toFromDelta: vec2.transformMat2d(vec2.create(), this.toFromDelta, transform), + toFromDelta: vec2.scale(vec2.create(), this.toFromDelta, scale), fromRadius: this.fromRadius * scale, toRadius: this.toRadius * scale, }); diff --git a/frontend/src/scripts/drawing/drawables/i-drawable-descriptor.ts b/frontend/src/scripts/drawing/drawables/i-drawable-descriptor.ts index bd85e4e..5e669bf 100644 --- a/frontend/src/scripts/drawing/drawables/i-drawable-descriptor.ts +++ b/frontend/src/scripts/drawing/drawables/i-drawable-descriptor.ts @@ -1,5 +1,8 @@ +import { IDrawable } from './i-drawable'; + export interface IDrawableDescriptor { uniformName: string; countMacroName: string; shaderCombinationSteps: Array; + readonly empty: IDrawable; } diff --git a/frontend/src/scripts/drawing/drawables/lights/circle-light.ts b/frontend/src/scripts/drawing/drawables/lights/circle-light.ts index 3bb9674..0f1ecf2 100644 --- a/frontend/src/scripts/drawing/drawables/lights/circle-light.ts +++ b/frontend/src/scripts/drawing/drawables/lights/circle-light.ts @@ -8,6 +8,7 @@ export class CircleLight implements ILight { uniformName: 'circleLights', countMacroName: 'circleLightCount', shaderCombinationSteps: settings.shaderCombinations.circleLightSteps, + empty: new CircleLight(vec2.fromValues(0, 0), 0, vec3.fromValues(0, 0, 0), 0), }; constructor( @@ -17,7 +18,7 @@ export class CircleLight implements ILight { public lightness: number ) {} - public distance(target: vec2): number { + public distance(_: vec2): number { return 0; } diff --git a/frontend/src/scripts/drawing/drawables/lights/point-light.ts b/frontend/src/scripts/drawing/drawables/lights/point-light.ts index 71cb42e..d5cce8e 100644 --- a/frontend/src/scripts/drawing/drawables/lights/point-light.ts +++ b/frontend/src/scripts/drawing/drawables/lights/point-light.ts @@ -8,6 +8,7 @@ export class PointLight implements ILight { uniformName: 'pointLights', countMacroName: 'pointLightCount', shaderCombinationSteps: settings.shaderCombinations.pointLightSteps, + empty: new PointLight(vec2.fromValues(0, 0), 0, vec3.fromValues(0, 0, 0), 0), }; public constructor( 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 5fb2772..bbb6cb0 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,4 +1,4 @@ -import { vec2 } from 'gl-matrix'; +import { mat2d, vec2 } from 'gl-matrix'; import { getCombinations } from '../../../helper/get-combinations'; import { last } from '../../../helper/last'; import { IDrawableDescriptor } from '../../drawables/i-drawable-descriptor'; @@ -12,33 +12,40 @@ export class UniformArrayAutoScalingProgram implements IProgram { }> = []; private current: FragmentShaderOnlyProgram; - private drawingRectangleTopLeft = vec2.fromValues(0, 0); - private drawingRectangleSize = vec2.fromValues(1, 1); constructor( private gl: WebGL2RenderingContext, shaderSources: [string, string], - private options: Array + private descriptors: Array ) { - const names = options.map((o) => o.countMacroName); + const names = descriptors.map((o) => o.countMacroName); for (const combination of getCombinations( - options.map((o) => o.shaderCombinationSteps) + descriptors.map((o) => o.shaderCombinationSteps) )) { this.createProgram(names, combination, shaderSources); } } public bindAndSetUniforms(uniforms: { [name: string]: any }): void { - const values = this.options.map((o) => - uniforms[o.uniformName] ? uniforms[o.uniformName].length : 0 + const values = this.descriptors.map((d) => + uniforms[d.uniformName] ? uniforms[d.uniformName].length : 0 ); const closest = this.programs.find((p) => p.values.every((v, i) => v >= values[i])); this.current = closest ? closest.program : last(this.programs).program; + if (closest) { + this.descriptors.map((d, i) => { + const difference = closest.values[i] - values[i]; + for (let i = 0; i < difference; i++) { + d.empty.serializeToUniforms(uniforms, 0, mat2d.create()); + } + }); + } + this.current.setDrawingRectangle( this.drawingRectangleTopLeft, this.drawingRectangleSize diff --git a/frontend/src/scripts/drawing/rendering/rendering-pass.ts b/frontend/src/scripts/drawing/rendering/rendering-pass.ts index 6c44767..671059d 100644 --- a/frontend/src/scripts/drawing/rendering/rendering-pass.ts +++ b/frontend/src/scripts/drawing/rendering/rendering-pass.ts @@ -28,7 +28,12 @@ export class RenderingPass { this.drawables.push(drawable); } - public render(commonUniforms: any, inputTexture?: WebGLTexture) { + public render( + commonUniforms: any, + scale: number, + transform: mat2d, + inputTexture?: WebGLTexture + ) { this.frame.bindAndClear(inputTexture); const q = 1 / settings.tileMultiplier; const tileUvSize = vec2.fromValues(q, q); @@ -51,13 +56,10 @@ export class RenderingPass { let sumLineCount = 0; - const scale = 1; - const transform = mat2d.create(); - for (let x = 0; x < 1; x += q) { for (let y = 0; y < 1; y += q) { const uniforms = { ...commonUniforms }; - uniforms.maxMinDistance = 2 * worldR; + uniforms.maxMinDistance = 2 * worldR * scale; const uvBottomLeft = vec2.fromValues(x, y); this.program.setDrawingRectangle(uvBottomLeft, tileUvSize); @@ -69,7 +71,7 @@ export class RenderingPass { ); const primitivesNearTile = this.drawables.filter( - (p) => p.distance(tileCenterWorldCoordinates) < 2 * worldR + (d) => d.distance(tileCenterWorldCoordinates) < 2 * worldR ); sumLineCount += primitivesNearTile.length; diff --git a/frontend/src/scripts/drawing/rendering/webgl2-renderer.ts b/frontend/src/scripts/drawing/rendering/webgl2-renderer.ts index 38c6833..7f61d3a 100644 --- a/frontend/src/scripts/drawing/rendering/webgl2-renderer.ts +++ b/frontend/src/scripts/drawing/rendering/webgl2-renderer.ts @@ -6,7 +6,6 @@ import { IDrawable } from '../drawables/i-drawable'; import { CircleLight } from '../drawables/lights/circle-light'; import { ILight } from '../drawables/lights/i-light'; import { PointLight } from '../drawables/lights/point-light'; -// 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 { getWebGl2Context } from '../graphics-library/helper/get-webgl2-context'; @@ -21,32 +20,28 @@ import { RenderingPass } from './rendering-pass'; export class WebGl2Renderer implements IRenderer { private gl: WebGL2RenderingContext; - private stopwatch?: WebGlStopwatch; + private upscaleTransform = mat2d.create(); + private linearDownscale = 1; + private viewBoxBottomLeft = vec2.create(); - private viewBoxSize = vec2.create(); - + private viewAreaScale: vec2; private cursorPosition = vec2.create(); private distanceFieldFrameBuffer: IntermediateFrameBuffer; - private lightingFrameBuffer: DefaultFrameBuffer; - private distancePass: RenderingPass; - private lightingPass: RenderingPass; - private autoscaler: FpsAutoscaler; private matrices: { distanceScreenToWorld?: mat2d; worldToDistanceUV?: mat2d; cursorPosition?: mat2d; - ndcToUv?: mat2d; uvToWorld?: mat2d; - } = { ndcToUv: mat2d.fromValues(0.5, 0, 0, 0.5, 0.5, 0.5) }; + } = {}; constructor(private canvas: HTMLCanvasElement, private overlay: HTMLElement) { this.gl = getWebGl2Context(canvas); @@ -92,6 +87,7 @@ export class WebGl2Renderer implements IRenderer { this.autoscaler.autoscale(deltaTime); this.stopwatch?.start(); + this.distanceFieldFrameBuffer.setSize(); this.lightingFrameBuffer.setSize(); } @@ -99,16 +95,24 @@ export class WebGl2Renderer implements IRenderer { public finishFrame() { this.calculateMatrices(); - this.distancePass.render(this.uniforms); - - this.lightingPass.render(this.uniforms, this.distanceFieldFrameBuffer.colorTexture); + this.distancePass.render(this.uniforms, this.linearDownscale, this.upscaleTransform); + this.lightingPass.render( + this.uniforms, + this.linearDownscale, + this.upscaleTransform, + this.distanceFieldFrameBuffer.colorTexture + ); this.stopwatch?.stop(); } private get uniforms(): any { const cursorPosition = this.screenUvToWorldCoordinate(this.cursorPosition); - return { ...this.matrices, cursorPosition, viewBoxSize: this.viewBoxSize }; + return { + ...this.matrices, + cursorPosition, + viewAreaScale: this.viewAreaScale, + }; } private calculateMatrices() { @@ -157,12 +161,31 @@ export class WebGl2Renderer implements IRenderer { } public setViewArea(viewArea: BoundingBoxBase) { + const targetRange = 2 ** 7; + this.viewBoxSize = viewArea.size; this.viewBoxBottomLeft = vec2.add( vec2.create(), viewArea.topLeft, vec2.fromValues(0, -viewArea.size.y) ); + + this.linearDownscale = targetRange / Math.max(this.viewBoxSize.x, this.viewBoxSize.y); + + this.viewAreaScale = vec2.fromValues( + (this.viewBoxSize.x * this.linearDownscale) / 2, + (this.viewBoxSize.y * this.linearDownscale) / 2 + ); + + mat2d.fromScaling( + this.upscaleTransform, + vec2.fromValues(this.linearDownscale, this.linearDownscale) + ); + + const translate = vec2.scale(vec2.create(), this.viewBoxBottomLeft, -1); + vec2.subtract(translate, translate, vec2.scale(vec2.create(), this.viewBoxSize, 0.5)); + + mat2d.translate(this.upscaleTransform, this.upscaleTransform, translate); } public setCursorPosition(position: vec2): void { diff --git a/frontend/src/scripts/drawing/settings.ts b/frontend/src/scripts/drawing/settings.ts index 5ea8610..f9d7ef4 100644 --- a/frontend/src/scripts/drawing/settings.ts +++ b/frontend/src/scripts/drawing/settings.ts @@ -22,10 +22,9 @@ export const settings = { }, tileMultiplier: 8, shaderMacros: { - edgeSmoothing: 10, - headRadius: 5, - torsoRadius: 8, - footRadius: 2, + headRadius: 0.05, + torsoRadius: 0.08, + footRadius: 0.02, }, shaderCombinations: { lineSteps: [0, 1, 2, 4, 8, 16, 128], diff --git a/frontend/src/scripts/drawing/shaders/cave-distance-fs.glsl b/frontend/src/scripts/drawing/shaders/cave-distance-fs.glsl index 076d5cd..174819d 100644 --- a/frontend/src/scripts/drawing/shaders/cave-distance-fs.glsl +++ b/frontend/src/scripts/drawing/shaders/cave-distance-fs.glsl @@ -69,18 +69,18 @@ uniform float maxMinDistance; } #endif -in vec2 worldCoordinates; +in vec2 position; out vec2 fragmentColor; void main() { float minDistance = -maxMinDistance; #if LINE_COUNT > 0 - lineMinDistance(worldCoordinates, minDistance); + lineMinDistance(position, minDistance); #endif #if BLOB_COUNT > 0 - blobMinDistance(worldCoordinates, minDistance); + //blobMinDistance(position, minDistance); #endif fragmentColor = vec2(minDistance, 0.0); diff --git a/frontend/src/scripts/drawing/shaders/lights-shading-fs.glsl b/frontend/src/scripts/drawing/shaders/lights-shading-fs.glsl index be40f31..709b536 100644 --- a/frontend/src/scripts/drawing/shaders/lights-shading-fs.glsl +++ b/frontend/src/scripts/drawing/shaders/lights-shading-fs.glsl @@ -3,15 +3,13 @@ precision mediump float; #define INFINITY 1000.0 -#define LIGHT_DROP 500.0 +#define LIGHT_DROP 50.0 #define AMBIENT_LIGHT vec3(0.15) #define CIRCLE_LIGHT_COUNT {circleLightCount} #define POINT_LIGHT_COUNT {pointLightCount} -#define EDGE_SMOOTHING {edgeSmoothing} uniform sampler2D distanceTexture; -uniform vec2 viewBoxSize; vec3[4] colors = vec3[4]( vec3(0.5), @@ -50,8 +48,10 @@ float getDistance(in vec2 target) { in vec2[POINT_LIGHT_COUNT] pointLightDirections; #endif -in vec2 worldCoordinates; +in vec2 position; in vec2 uvCoordinates; +uniform vec2 viewAreaScale; + out vec4 fragmentColor; void main() { @@ -63,13 +63,13 @@ void main() { for (int i = 0; i < CIRCLE_LIGHT_COUNT; i++) { float lightCenterDistance = distance( circleLights[i].center, - worldCoordinates + position ); float lightDistance = lightCenterDistance - circleLights[i].radius; - /*if (lightDistance < 0.0) { + if (lightDistance < 0.0) { lighting = vec3(1.0, 1.0, 0.0); - }*/ + } vec3 lightColorAtPosition = circleLights[i].value / pow( lightDistance / LIGHT_DROP + 1.0, 2.0 @@ -77,8 +77,7 @@ void main() { float q = INFINITY; float rayLength = startingDistance; - vec2 direction = normalize(circleLightDirections[i]) / viewBoxSize; - + vec2 direction = normalize(circleLightDirections[i]) / viewAreaScale / 2.0; for (int j = 0; j < 48; j++) { if (rayLength >= lightDistance) { lighting += lightColorAtPosition * clamp( @@ -124,6 +123,7 @@ void main() { }*/ #endif + fragmentColor = vec4(vec3(startingDistance / 100.0), 1.0); fragmentColor = vec4( colorAtPosition * lighting * step(0.0, startingDistance), 1.0 diff --git a/frontend/src/scripts/drawing/shaders/passthrough-distance-vs.glsl b/frontend/src/scripts/drawing/shaders/passthrough-distance-vs.glsl index 5248949..c5bbeb5 100644 --- a/frontend/src/scripts/drawing/shaders/passthrough-distance-vs.glsl +++ b/frontend/src/scripts/drawing/shaders/passthrough-distance-vs.glsl @@ -3,14 +3,13 @@ precision mediump float; uniform mat3 modelTransform; -uniform mat3 uvToWorld; -uniform mat3 ndcToUv; +uniform vec2 viewAreaScale; in vec4 vertexPosition; -out vec2 worldCoordinates; +out vec2 position; void main() { vec3 vertexPosition2D = vec3(vertexPosition.xy, 1.0) * modelTransform; - worldCoordinates = (vertexPosition2D * ndcToUv * uvToWorld).xy; gl_Position = vec4(vertexPosition2D.xy, 0.0, 1.0); + position = vertexPosition2D.xy * viewAreaScale; } diff --git a/frontend/src/scripts/drawing/shaders/passthrough-shading-vs.glsl b/frontend/src/scripts/drawing/shaders/passthrough-shading-vs.glsl index dcb4baa..0c2c521 100644 --- a/frontend/src/scripts/drawing/shaders/passthrough-shading-vs.glsl +++ b/frontend/src/scripts/drawing/shaders/passthrough-shading-vs.glsl @@ -6,15 +6,13 @@ precision mediump float; #define POINT_LIGHT_COUNT {pointLightCount} uniform mat3 modelTransform; -uniform mat3 cameraTransform; -uniform mat3 ndcToUv; -uniform mat3 uvToWorld; - in vec4 vertexPosition; -out vec2 worldCoordinates; +out vec2 position; out vec2 uvCoordinates; +uniform vec2 viewAreaScale; + #if CIRCLE_LIGHT_COUNT > 0 uniform struct CircleLight { vec2 center; @@ -37,21 +35,24 @@ out vec2 uvCoordinates; void main() { vec3 vertexPosition2D = vec3(vertexPosition.xy, 1.0) * modelTransform; - vec3 uvCoordinates1 = vertexPosition2D * ndcToUv; - worldCoordinates = (uvCoordinates1 * uvToWorld).xy; - uvCoordinates = (uvCoordinates1).xy; + gl_Position = vec4(vertexPosition2D.xy, 0.0, 1.0); + position = vertexPosition2D.xy * viewAreaScale; + + uvCoordinates = (vertexPosition2D * mat3( + 0.5, 0.0, 0.5, + 0.0, 0.5, 0.5, + 0.0, 0.0, 1.0 + )).xy; #if CIRCLE_LIGHT_COUNT > 0 for (int i = 0; i < CIRCLE_LIGHT_COUNT; i++) { - circleLightDirections[i] = circleLights[i].center - worldCoordinates; + circleLightDirections[i] = circleLights[i].center - position; } #endif #if POINT_LIGHT_COUNT > 0 for (int i = 0; i < POINT_LIGHT_COUNT; i++) { - pointLightDirections[i] = pointLights[i].center - worldCoordinates; + pointLightDirections[i] = pointLights[i].center - position; } #endif - - gl_Position = vec4(vertexPosition2D.xy, 0.0, 1.0); } diff --git a/frontend/src/scripts/input/mouse-listener.ts b/frontend/src/scripts/input/mouse-listener.ts index 055ca6e..1120079 100644 --- a/frontend/src/scripts/input/mouse-listener.ts +++ b/frontend/src/scripts/input/mouse-listener.ts @@ -3,8 +3,8 @@ import { CommandGenerator } from '../commands/command-generator'; import { clamp01 } from '../helper/clamp'; import { CursorMoveCommand } from './commands/cursor-move-command'; import { PrimaryActionCommand } from './commands/primary-action'; -import { SwipeCommand } from './commands/swipe'; import { SecondaryActionCommand } from './commands/secondary-action'; +import { SwipeCommand } from './commands/swipe'; import { ZoomCommand } from './commands/zoom'; export class MouseListener extends CommandGenerator { @@ -39,11 +39,11 @@ export class MouseListener extends CommandGenerator { this.sendCommand(new CursorMoveCommand(position)); }); - target.addEventListener('mouseup', (event: MouseEvent) => { + target.addEventListener('mouseup', (_: MouseEvent) => { this.isMouseDown = false; }); - target.addEventListener('mouseleave', (event: MouseEvent) => { + target.addEventListener('mouseleave', (_: MouseEvent) => { this.isMouseDown = false; }); diff --git a/frontend/src/scripts/objects/types/character.ts b/frontend/src/scripts/objects/types/character.ts index 236c87f..e87e508 100644 --- a/frontend/src/scripts/objects/types/character.ts +++ b/frontend/src/scripts/objects/types/character.ts @@ -15,11 +15,8 @@ import { Lamp } from './lamp'; export class Character extends GameObject { private keysDown: Set = new Set(); - private light = new Lamp(vec2.create(), 40, vec3.fromValues(0.67, 0.0, 0.33), 2); - private shape = new DrawableBlob(vec2.create()); - private static speed = 1.5; constructor(private game: IGame) { diff --git a/frontend/src/scripts/shapes/types/blob.ts b/frontend/src/scripts/shapes/types/blob.ts index b586759..78dbe1d 100644 --- a/frontend/src/scripts/shapes/types/blob.ts +++ b/frontend/src/scripts/shapes/types/blob.ts @@ -1,33 +1,22 @@ import { vec2 } from 'gl-matrix'; -import { IShape } from '../i-shape'; -import { BoundingBox } from '../bounding-box'; -import { Circle } from './circle'; import { settings } from '../../drawing/settings'; import { GameObject } from '../../objects/game-object'; +import { BoundingBox } from '../bounding-box'; +import { IShape } from '../i-shape'; +import { Circle } from './circle'; export class Blob implements IShape { private static readonly boundingCircleRadius = 19; - private static readonly headOffset = vec2.fromValues(0, 15); - private static readonly torsoOffset = vec2.fromValues(0, 0); - private static readonly leftFootOffset = vec2.fromValues(-5, -10); - private static readonly rightFootOffset = vec2.fromValues(5, -10); - public readonly isInverted = false; - protected boundingCircle = new Circle(vec2.create(), Blob.boundingCircleRadius); - protected head = new Circle(vec2.create(), settings.shaderMacros.headRadius); - protected torso = new Circle(vec2.create(), settings.shaderMacros.torsoRadius); - protected leftFoot = new Circle(vec2.create(), settings.shaderMacros.footRadius); - protected rightFoot = new Circle(vec2.create(), settings.shaderMacros.footRadius); - public constructor(center: vec2, public readonly gameObject: GameObject = null) { this.position = center; }