Some improvements

This commit is contained in:
schmelczerandras 2020-09-11 21:06:40 +02:00
parent 23a6b473ae
commit b1b3ca685a
20 changed files with 259 additions and 217 deletions

View file

@ -1,4 +1,4 @@
import { waitWhileFalse } from '../../../helper/wait-for'; import { waitWhileFalse } from '../../../helper/wait-while-false';
import { tryEnableExtension } from '../helper/enable-extension'; import { tryEnableExtension } from '../helper/enable-extension';
import { checkProgram } from './check-program'; import { checkProgram } from './check-program';
import { createShader } from './create-shader'; import { createShader } from './create-shader';

View file

@ -4,16 +4,16 @@ export class DefaultFrameBuffer extends FrameBuffer {
constructor(gl: WebGL2RenderingContext) { constructor(gl: WebGL2RenderingContext) {
super(gl); super(gl);
this.frameBuffer = null; this.frameBuffer = null;
this.setSize(); this.setSize();
} }
public setSize() { public setSize(): boolean {
super.setSize(); const hasChanged = super.setSize();
if (hasChanged) {
if (this.gl.canvas.width !== this.size.x || this.gl.canvas.height !== this.size.y) {
this.gl.canvas.width = this.size.x; this.gl.canvas.width = this.size.x;
this.gl.canvas.height = this.size.y; this.gl.canvas.height = this.size.y;
} }
return hasChanged;
} }
} }

View file

@ -1,12 +1,11 @@
import { vec2 } from 'gl-matrix'; import { vec2 } from 'gl-matrix';
import { settings } from '../../settings';
export abstract class FrameBuffer { export abstract class FrameBuffer {
public renderScale = 1; public renderScale = 1;
public enableHighDpiRendering = settings.enableHighDpiRendering;
public enableHighDpiRendering = false; protected size = vec2.create();
protected size: vec2;
protected frameBuffer: WebGLFramebuffer; protected frameBuffer: WebGLFramebuffer;
constructor(protected gl: WebGL2RenderingContext) {} constructor(protected gl: WebGL2RenderingContext) {}
@ -23,15 +22,20 @@ export abstract class FrameBuffer {
this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT); this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
} }
public setSize() { public setSize(): boolean {
const realToCssPixels = window.devicePixelRatio * this.renderScale; const realToCssPixels =
(this.enableHighDpiRendering ? window.devicePixelRatio : 1) * this.renderScale;
const canvasWidth = (this.gl.canvas as HTMLCanvasElement).clientWidth; const canvasWidth = (this.gl.canvas as HTMLCanvasElement).clientWidth;
const canvasHeight = (this.gl.canvas as HTMLCanvasElement).clientHeight; const canvasHeight = (this.gl.canvas as HTMLCanvasElement).clientHeight;
const displayWidth = Math.floor(canvasWidth * realToCssPixels); const displayWidth = Math.floor(canvasWidth * realToCssPixels);
const displayHeight = Math.floor(canvasHeight * realToCssPixels); const displayHeight = Math.floor(canvasHeight * realToCssPixels);
const oldSize = vec2.clone(this.getSize());
this.size = vec2.fromValues(displayWidth, displayHeight); this.size = vec2.fromValues(displayWidth, displayHeight);
return this.size.x != oldSize.x || this.size.y != oldSize.y;
} }
public getSize(): vec2 { public getSize(): vec2 {

View file

@ -1,10 +1,9 @@
import { FrameBuffer } from './frame-buffer';
import { enableExtension } from '../helper/enable-extension'; import { enableExtension } from '../helper/enable-extension';
import { FrameBuffer } from './frame-buffer';
export class IntermediateFrameBuffer extends FrameBuffer { export class IntermediateFrameBuffer extends FrameBuffer {
private frameTexture: WebGLTexture; private frameTexture: WebGLTexture;
private floatLinearEnabled = false;
private floatLinearEnabled = true;
constructor(gl: WebGL2RenderingContext) { constructor(gl: WebGL2RenderingContext) {
super(gl); super(gl);
@ -13,8 +12,9 @@ export class IntermediateFrameBuffer extends FrameBuffer {
try { try {
enableExtension(gl, 'OES_texture_float_linear'); enableExtension(gl, 'OES_texture_float_linear');
this.floatLinearEnabled = true;
} catch { } catch {
this.floatLinearEnabled = false; // it's okay
} }
this.frameTexture = this.gl.createTexture(); this.frameTexture = this.gl.createTexture();
@ -30,22 +30,25 @@ export class IntermediateFrameBuffer extends FrameBuffer {
return this.frameTexture; return this.frameTexture;
} }
public setSize() { public setSize(): boolean {
super.setSize(); const hasChanged = super.setSize();
if (hasChanged) {
this.gl.bindTexture(this.gl.TEXTURE_2D, this.frameTexture);
this.gl.bindTexture(this.gl.TEXTURE_2D, this.frameTexture); this.gl.texImage2D(
this.gl.TEXTURE_2D,
0,
this.gl.RG16F,
this.size.x,
this.size.y,
0,
this.gl.RG,
this.gl.FLOAT,
null
);
}
this.gl.texImage2D( return hasChanged;
this.gl.TEXTURE_2D,
0,
this.gl.RG16F,
this.size.x,
this.size.y,
0,
this.gl.RG,
this.gl.FLOAT,
null
);
} }
private configureTexture() { private configureTexture() {

View file

@ -1,5 +1,15 @@
import { InfoText } from '../../../objects/types/info-text';
const extensions: Map<string, any> = new Map(); const extensions: Map<string, any> = new Map();
const printExtensions = () => {
const values = {};
for (const [k, v] of extensions.entries()) {
values[k] = v !== null;
}
InfoText.modifyRecord('extensions', values);
};
export const tryEnableExtension = ( export const tryEnableExtension = (
gl: WebGL2RenderingContext, gl: WebGL2RenderingContext,
name: string name: string
@ -15,6 +25,8 @@ export const tryEnableExtension = (
extensions.set(name, extension); extensions.set(name, extension);
printExtensions();
return extension; return extension;
}; };

View file

@ -5,11 +5,8 @@ import { enableExtension } from './enable-extension';
export class WebGlStopwatch { export class WebGlStopwatch {
private timerExtension: any; private timerExtension: any;
private timerQuery: WebGLQuery; private timerQuery: WebGLQuery;
private isReady = true; private isReady = true;
private resultsInNanoSeconds: number; private resultsInNanoSeconds: number;
constructor(private gl: WebGL2RenderingContext) { constructor(private gl: WebGL2RenderingContext) {

View file

@ -2,7 +2,7 @@ import { vec2 } from 'gl-matrix';
export interface IProgram { export interface IProgram {
initialize(): Promise<void>; initialize(): Promise<void>;
setDrawingRectangle(bottomLeft: vec2, size: vec2): void; setDrawingRectangleUV(bottomLeft: vec2, size: vec2): void;
bindAndSetUniforms(values: { [name: string]: any }): void; bindAndSetUniforms(values: { [name: string]: any }): void;
draw(): void; draw(): void;
delete(): void; delete(): void;

View file

@ -8,7 +8,7 @@ export default abstract class Program implements IProgram {
protected program: WebGLProgram; protected program: WebGLProgram;
private programPromise: Promise<WebGLProgram>; private programPromise: Promise<WebGLProgram>;
private modelTransform = mat2d.identity(mat2d.create()); private modelTransform = mat2d.identity(mat2d.create());
private readonly ndcToUv: mat2d; private readonly ndcToUv = mat2d.fromValues(0.5, 0, 0, 0.5, 0.5, 0.5);
private uniforms: Array<{ private uniforms: Array<{
name: Array<string>; name: Array<string>;
location: WebGLUniformLocation; location: WebGLUniformLocation;
@ -28,9 +28,6 @@ export default abstract class Program implements IProgram {
fragmentShaderSource, fragmentShaderSource,
substitutions substitutions
); );
this.ndcToUv = mat2d.fromScaling(mat2d.create(), vec2.fromValues(0.5, 0.5));
mat2d.translate(this.ndcToUv, this.ndcToUv, vec2.fromValues(1, 1));
} }
public async initialize(): Promise<void> { public async initialize(): Promise<void> {
@ -43,7 +40,7 @@ export default abstract class Program implements IProgram {
this.setUniforms({ modelTransform: this.modelTransform, ...values }); this.setUniforms({ modelTransform: this.modelTransform, ...values });
} }
public setDrawingRectangle(bottomLeft: vec2, size: vec2) { public setDrawingRectangleUV(bottomLeft: vec2, size: vec2) {
mat2d.invert(this.modelTransform, this.ndcToUv); mat2d.invert(this.modelTransform, this.ndcToUv);
mat2d.translate(this.modelTransform, this.modelTransform, bottomLeft); mat2d.translate(this.modelTransform, this.modelTransform, bottomLeft);
mat2d.scale(this.modelTransform, this.modelTransform, size); mat2d.scale(this.modelTransform, this.modelTransform, size);
@ -93,9 +90,11 @@ export default abstract class Program implements IProgram {
this.uniforms.map((u1) => { this.uniforms.map((u1) => {
const isSingle = const isSingle =
this.uniforms.filter((u2) => u2.name.includes(u1.name[0])).length == 1; this.uniforms.filter((u2) => u2.name.includes(u1.name[0])).length == 1;
if (u1.name.includes('0') && isSingle) { if (u1.name.includes('0') && isSingle) {
u1.name = u1.name.slice(0, -1); u1.name = u1.name.slice(0, -1);
} }
return u1; return u1;
}); });
} }

View file

@ -12,7 +12,7 @@ export class UniformArrayAutoScalingProgram implements IProgram {
}> = []; }> = [];
private current: FragmentShaderOnlyProgram; private current: FragmentShaderOnlyProgram;
private drawingRectangleTopLeft = vec2.fromValues(0, 0); private drawingRectangleBottomLeft = vec2.fromValues(0, 0);
private drawingRectangleSize = vec2.fromValues(1, 1); private drawingRectangleSize = vec2.fromValues(1, 1);
constructor( constructor(
@ -50,15 +50,15 @@ export class UniformArrayAutoScalingProgram implements IProgram {
}); });
} }
this.current.setDrawingRectangle( this.current.setDrawingRectangleUV(
this.drawingRectangleTopLeft, this.drawingRectangleBottomLeft,
this.drawingRectangleSize this.drawingRectangleSize
); );
this.current.bindAndSetUniforms(uniforms); this.current.bindAndSetUniforms(uniforms);
} }
public setDrawingRectangle(topLeft: vec2, size: vec2) { public setDrawingRectangleUV(bottomLeft: vec2, size: vec2) {
this.drawingRectangleTopLeft = topLeft; this.drawingRectangleBottomLeft = bottomLeft;
this.drawingRectangleSize = size; this.drawingRectangleSize = size;
} }

View file

@ -1,19 +1,15 @@
import { Autoscaler } from '../../helper/autoscaler'; import { Autoscaler as AutoScaler } from '../../helper/autoscaler';
import { settings } from '../settings';
import { exponentialDecay } from '../../helper/exponential-decay'; import { exponentialDecay } from '../../helper/exponential-decay';
import { InfoText } from '../../objects/types/info-text'; import { settings } from '../settings';
import { FrameBuffer } from '../graphics-library/frame-buffer/frame-buffer';
import { toPercent } from '../../helper/to-percent';
export class FpsAutoscaler extends Autoscaler { export class FpsAutoscaler extends AutoScaler {
private timeSinceLastAdjusment = 0; private timeSinceLastAdjusment = 0;
private exponentialDecayedDeltaTime = 0.0; private exponentialDecayedDeltaTime = 0.0;
constructor(private frameBuffers: Array<FrameBuffer>) { constructor(setters: { [key: string]: (value: number | boolean) => void }) {
super( super(
frameBuffers.map((f) => (v) => (f.renderScale = v)), setters,
settings.qualityScaling.scaleTargets, settings.qualityScaling.qualityTargets,
settings.qualityScaling.startingTargetIndex, settings.qualityScaling.startingTargetIndex,
settings.qualityScaling.scalingOptions settings.qualityScaling.scalingOptions
); );
@ -45,10 +41,5 @@ export class FpsAutoscaler extends Autoscaler {
this.decrease(); this.decrease();
} }
} }
InfoText.modifyRecord(
'quality',
this.frameBuffers.map((f) => toPercent(f.renderScale)).join(', ')
);
} }
} }

View file

@ -1,5 +1,4 @@
import { mat2d, vec2 } from 'gl-matrix'; import { mat2d, vec2 } from 'gl-matrix';
import { InfoText } from '../../objects/types/info-text';
import { IDrawable } from '../drawables/i-drawable'; import { IDrawable } from '../drawables/i-drawable';
import { IDrawableDescriptor } from '../drawables/i-drawable-descriptor'; import { IDrawableDescriptor } from '../drawables/i-drawable-descriptor';
import { FrameBuffer } from '../graphics-library/frame-buffer/frame-buffer'; import { FrameBuffer } from '../graphics-library/frame-buffer/frame-buffer';
@ -8,13 +7,12 @@ import { settings } from '../settings';
export class RenderingPass { export class RenderingPass {
private drawables: Array<IDrawable> = []; private drawables: Array<IDrawable> = [];
private program: UniformArrayAutoScalingProgram; private program: UniformArrayAutoScalingProgram;
constructor( constructor(
gl: WebGL2RenderingContext, gl: WebGL2RenderingContext,
shaderSources: [string, string], shaderSources: [string, string],
private drawableDescriptors: Array<IDrawableDescriptor>, drawableDescriptors: Array<IDrawableDescriptor>,
private frame: FrameBuffer private frame: FrameBuffer
) { ) {
this.program = new UniformArrayAutoScalingProgram( this.program = new UniformArrayAutoScalingProgram(
@ -39,38 +37,34 @@ export class RenderingPass {
inputTexture?: WebGLTexture inputTexture?: WebGLTexture
) { ) {
this.frame.bindAndClear(inputTexture); this.frame.bindAndClear(inputTexture);
const q = 1 / settings.tileMultiplier;
const tileUvSize = vec2.fromValues(q, q);
const origin = vec2.transformMat2d( const stepsInUV = 1 / settings.tileMultiplier;
vec2.create(),
vec2.fromValues(0, 0),
commonUniforms.uvToWorld
);
const firstCenter = vec2.transformMat2d( const worldR =
vec2.create(), 0.5 *
vec2.fromValues(q, q), vec2.length(vec2.scale(vec2.create(), commonUniforms.worldAreaInView, stepsInUV));
commonUniforms.uvToWorld
);
vec2.subtract(firstCenter, firstCenter, origin); const stepsInNDC = 2 * stepsInUV;
const worldR = vec2.length(firstCenter); for (let x = -1; x < 1; x += stepsInNDC) {
for (let y = -1; y < 1; y += stepsInNDC) {
let sumLineCount = 0;
for (let x = 0; x < 1; x += q) {
for (let y = 0; y < 1; y += q) {
const uniforms = { ...commonUniforms }; const uniforms = { ...commonUniforms };
uniforms.maxMinDistance = 2 * worldR * scale; uniforms.maxMinDistance = 2 * worldR * scale;
const uvBottomLeft = vec2.fromValues(x, y); const ndcBottomLeft = vec2.fromValues(x, y);
this.program.setDrawingRectangle(uvBottomLeft, tileUvSize);
this.program.setDrawingRectangleUV(
[(ndcBottomLeft.x + 1) / 2, (ndcBottomLeft.y + 1) / 2],
vec2.fromValues(stepsInUV, stepsInUV)
);
const tileCenterWorldCoordinates = vec2.transformMat2d( const tileCenterWorldCoordinates = vec2.transformMat2d(
vec2.create(), vec2.create(),
vec2.add(vec2.create(), uvBottomLeft, vec2.fromValues(q, q)), vec2.add(
vec2.create(),
[(ndcBottomLeft.x + 1) / 2, (ndcBottomLeft.y + 1) / 2],
vec2.fromValues(stepsInUV / 2, stepsInUV / 2)
),
uniforms.uvToWorld uniforms.uvToWorld
); );
@ -78,8 +72,6 @@ export class RenderingPass {
(d) => d.distance(tileCenterWorldCoordinates) < 2 * worldR (d) => d.distance(tileCenterWorldCoordinates) < 2 * worldR
); );
sumLineCount += primitivesNearTile.length;
primitivesNearTile.forEach((p) => primitivesNearTile.forEach((p) =>
p.serializeToUniforms(uniforms, scale, transform) p.serializeToUniforms(uniforms, scale, transform)
); );
@ -89,16 +81,6 @@ export class RenderingPass {
} }
} }
InfoText.modifyRecord(
`nearby ${this.drawableDescriptors[0].countMacroName}`,
this.drawables.length.toFixed(2)
);
InfoText.modifyRecord(
`drawn ${this.drawableDescriptors[0].countMacroName}`,
(sumLineCount / settings.tileMultiplier / settings.tileMultiplier).toFixed(2)
);
this.drawables = []; this.drawables = [];
} }
} }

View file

@ -22,12 +22,13 @@ export class WebGl2Renderer implements IRenderer {
private gl: WebGL2RenderingContext; private gl: WebGL2RenderingContext;
private stopwatch?: WebGlStopwatch; private stopwatch?: WebGlStopwatch;
private upscaleTransform = mat2d.create(); private scaleWorldToNDC = mat2d.create();
private linearUpscale = 1; private scaleWorldAreaInViewToNDC = 1;
private viewBoxBottomLeft = vec2.create(); private viewAreaBottomLeft = vec2.create();
private viewBoxSize = vec2.create(); private worldAreaInView = vec2.create();
private viewAreaScale: vec2; private squareToAspectRatio: vec2;
private uvToWorld: mat2d;
private cursorPosition = vec2.create(); private cursorPosition = vec2.create();
private distanceFieldFrameBuffer: IntermediateFrameBuffer; private distanceFieldFrameBuffer: IntermediateFrameBuffer;
@ -38,12 +39,7 @@ export class WebGl2Renderer implements IRenderer {
private initializePromise: Promise<[void, void]> = null; private initializePromise: Promise<[void, void]> = null;
private matrices: { private softShadowsEnabled: boolean;
distanceScreenToWorld?: mat2d;
worldToDistanceUV?: mat2d;
cursorPosition?: mat2d;
uvToWorld?: mat2d;
} = {};
constructor(private canvas: HTMLCanvasElement, private overlay: HTMLElement) { constructor(private canvas: HTMLCanvasElement, private overlay: HTMLElement) {
this.gl = getWebGl2Context(canvas); this.gl = getWebGl2Context(canvas);
@ -70,10 +66,12 @@ export class WebGl2Renderer implements IRenderer {
this.lightingPass.initialize(), this.lightingPass.initialize(),
]); ]);
this.autoscaler = new FpsAutoscaler([ this.autoscaler = new FpsAutoscaler({
this.lightingFrameBuffer, distanceRenderScale: (v) =>
this.distanceFieldFrameBuffer, (this.distanceFieldFrameBuffer.renderScale = v as number),
]); finalRenderScale: (v) => (this.lightingFrameBuffer.renderScale = v as number),
softShadowsEnabled: (v) => (this.softShadowsEnabled = v as boolean),
});
try { try {
this.stopwatch = new WebGlStopwatch(this.gl); this.stopwatch = new WebGlStopwatch(this.gl);
@ -106,11 +104,15 @@ export class WebGl2Renderer implements IRenderer {
public finishFrame() { public finishFrame() {
this.calculateMatrices(); this.calculateMatrices();
this.distancePass.render(this.uniforms, this.linearUpscale, this.upscaleTransform); this.distancePass.render(
this.uniforms,
this.scaleWorldAreaInViewToNDC,
this.scaleWorldToNDC
);
this.lightingPass.render( this.lightingPass.render(
this.uniforms, this.uniforms,
this.linearUpscale, this.scaleWorldAreaInViewToNDC,
this.upscaleTransform, this.scaleWorldToNDC,
this.distanceFieldFrameBuffer.colorTexture this.distanceFieldFrameBuffer.colorTexture
); );
@ -118,46 +120,37 @@ export class WebGl2Renderer implements IRenderer {
} }
private get uniforms(): any { private get uniforms(): any {
const cursorPosition = this.screenUvToWorldCoordinate(this.cursorPosition); const cursorPosition = this.uvToWorldCoordinate(this.cursorPosition);
return { return {
...this.matrices,
cursorPosition, cursorPosition,
viewAreaScale: this.viewAreaScale, pixelSize:
(4.5 * this.scaleWorldAreaInViewToNDC) /
this.distanceFieldFrameBuffer.renderScale,
uvToWorld: this.uvToWorld,
worldAreaInView: this.worldAreaInView,
squareToAspectRatio: this.squareToAspectRatio,
softShadowsEnabled: this.softShadowsEnabled,
}; };
} }
private calculateMatrices() { private calculateMatrices() {
this.matrices.uvToWorld = mat2d.fromTranslation( this.uvToWorld = mat2d.fromTranslation(mat2d.create(), this.viewAreaBottomLeft);
mat2d.create(), mat2d.scale(this.uvToWorld, this.uvToWorld, this.worldAreaInView);
this.viewBoxBottomLeft
);
mat2d.scale(this.matrices.uvToWorld, this.matrices.uvToWorld, this.viewBoxSize);
this.matrices.distanceScreenToWorld = this.getScreenToWorldTransform(
this.distanceFieldFrameBuffer.getSize()
);
this.matrices.worldToDistanceUV = mat2d.scale(
mat2d.create(),
this.matrices.distanceScreenToWorld,
this.distanceFieldFrameBuffer.getSize()
);
mat2d.invert(this.matrices.worldToDistanceUV, this.matrices.worldToDistanceUV);
} }
private getScreenToWorldTransform(screenSize: vec2) { private getScreenToWorldTransform(screenSize: vec2) {
const transform = mat2d.fromTranslation(mat2d.create(), this.viewBoxBottomLeft); const transform = mat2d.fromTranslation(mat2d.create(), this.viewAreaBottomLeft);
mat2d.scale( mat2d.scale(
transform, transform,
transform, transform,
vec2.divide(vec2.create(), this.viewBoxSize, screenSize) vec2.divide(vec2.create(), this.worldAreaInView, screenSize)
); );
mat2d.translate(transform, transform, vec2.fromValues(0.5, 0.5)); mat2d.translate(transform, transform, vec2.fromValues(0.5, 0.5));
return transform; return transform;
} }
public screenUvToWorldCoordinate(screenUvPosition: vec2): vec2 { public uvToWorldCoordinate(screenUvPosition: vec2): vec2 {
const resolution = vec2.fromValues(this.canvas.width, this.canvas.height); const resolution = vec2.fromValues(this.canvas.width, this.canvas.height);
return vec2.transformMat2d( return vec2.transformMat2d(
@ -172,31 +165,36 @@ export class WebGl2Renderer implements IRenderer {
} }
public setViewArea(viewArea: BoundingBoxBase) { public setViewArea(viewArea: BoundingBoxBase) {
const targetRange = 2; this.worldAreaInView = viewArea.size;
this.viewBoxSize = viewArea.size; // world coordinates
this.viewBoxBottomLeft = vec2.add( this.viewAreaBottomLeft = vec2.add(
vec2.create(), vec2.create(),
viewArea.topLeft, viewArea.topLeft,
vec2.fromValues(0, -viewArea.size.y) vec2.fromValues(0, -viewArea.size.y)
); );
this.linearUpscale = targetRange / Math.max(this.viewBoxSize.x, this.viewBoxSize.y); const scaleLongerEdgeTo1 =
1 / Math.max(this.worldAreaInView.x, this.worldAreaInView.y);
this.viewAreaScale = vec2.fromValues( this.squareToAspectRatio = vec2.fromValues(
(this.viewBoxSize.x * this.linearUpscale) / 2, this.worldAreaInView.x * scaleLongerEdgeTo1,
(this.viewBoxSize.y * this.linearUpscale) / 2 this.worldAreaInView.y * scaleLongerEdgeTo1
); );
this.scaleWorldAreaInViewToNDC = scaleLongerEdgeTo1 * 2;
mat2d.fromScaling( mat2d.fromScaling(
this.upscaleTransform, this.scaleWorldToNDC,
vec2.fromValues(this.linearUpscale, this.linearUpscale) vec2.fromValues(this.scaleWorldAreaInViewToNDC, this.scaleWorldAreaInViewToNDC)
); );
const translate = vec2.scale(vec2.create(), this.viewAreaBottomLeft, -1);
const translate = vec2.scale(vec2.create(), this.viewBoxBottomLeft, -1); vec2.subtract(
vec2.subtract(translate, translate, vec2.scale(vec2.create(), this.viewBoxSize, 0.5)); translate,
translate,
mat2d.translate(this.upscaleTransform, this.upscaleTransform, translate); vec2.scale(vec2.create(), this.worldAreaInView, 0.5)
);
mat2d.translate(this.scaleWorldToNDC, this.scaleWorldToNDC, translate);
} }
public setCursorPosition(position: vec2): void { public setCursorPosition(position: vec2): void {

View file

@ -1,18 +1,51 @@
export const settings = { export const settings = {
enableHighDpiRendering: true,
qualityScaling: { qualityScaling: {
targetDeltaTimeInMilliseconds: 30, targetDeltaTimeInMilliseconds: 20,
deltaTimeError: 2, deltaTimeError: 2,
deltaTimeResponsiveness: 1 / 16, deltaTimeResponsiveness: 1 / 16,
adjusmentRateInMilliseconds: 300, adjusmentRateInMilliseconds: 300,
scaleTargets: [ qualityTargets: [
[0.2, 0.1], {
[0.6, 0.1], distanceRenderScale: 0.1,
[1, 1], finalRenderScale: 0.2,
/*[1.25, 0.75], softShadowsEnabled: false,
[1.5, 1], },
[1.75, 1.25], {
//[1.75, 1.75], distanceRenderScale: 0.1,
//[2, 2],*/ finalRenderScale: 0.6,
softShadowsEnabled: false,
},
{
distanceRenderScale: 0.2,
finalRenderScale: 1.0,
softShadowsEnabled: false,
},
/* {
distanceRenderScale: 0.3,
finalRenderScale: 1.0,
softShadowsEnabled: true,
},
{
distanceRenderScale: 1.0,
finalRenderScale: 1.25,
softShadowsEnabled: true,
},
{
distanceRenderScale: 1.0,
finalRenderScale: 1.5,
softShadowsEnabled: true,
},
{
distanceRenderScale: 1.25,
finalRenderScale: 1.75,
softShadowsEnabled: true,
},
{
distanceRenderScale: 2,
finalRenderScale: 2,
softShadowsEnabled: true,
},*/
], ],
startingTargetIndex: 2, startingTargetIndex: 2,
scalingOptions: { scalingOptions: {

View file

@ -1,15 +1,15 @@
#version 300 es #version 300 es
precision mediump float; precision lowp float;
#define LINE_COUNT {lineCount} #define LINE_COUNT {lineCount}
#define BLOB_COUNT {blobCount} #define BLOB_COUNT {blobCount}
uniform float maxMinDistance; uniform float maxMinDistance;
uniform float pixelSize;
in vec2 position; in vec2 position;
#if LINE_COUNT > 0 #if LINE_COUNT > 0
uniform struct { uniform struct {
vec2 from; vec2 from;
@ -19,7 +19,7 @@ in vec2 position;
}[LINE_COUNT] lines; }[LINE_COUNT] lines;
void lineMinDistance(inout float minDistance, inout float color) { void lineMinDistance(inout float minDistance, inout float color) {
float myMinDistance = 10.0; float myMinDistance = maxMinDistance;
for (int i = 0; i < LINE_COUNT; i++) { for (int i = 0; i < LINE_COUNT; i++) {
vec2 targetFromDelta = position - lines[i].from; vec2 targetFromDelta = position - lines[i].from;
vec2 toFromDelta = lines[i].toFromDelta; vec2 toFromDelta = lines[i].toFromDelta;
@ -35,10 +35,11 @@ in vec2 position;
targetFromDelta, toFromDelta * h targetFromDelta, toFromDelta * h
); );
color = mix(1.0, color, step(0.0, lineDistance));
myMinDistance = min(myMinDistance, lineDistance); myMinDistance = min(myMinDistance, lineDistance);
} }
color = mix(0.0, color, step(pixelSize, -myMinDistance));
minDistance = -myMinDistance; minDistance = -myMinDistance;
} }
#endif #endif
@ -67,7 +68,7 @@ in vec2 position;
res += exp2(-blobs[i].k * circleMinDistance(blobs[i].rightFootCenter, blobs[i].footRadius)); res += exp2(-blobs[i].k * circleMinDistance(blobs[i].rightFootCenter, blobs[i].footRadius));
res = -log2(res) / blobs[i].k; res = -log2(res) / blobs[i].k;
color = mix(2.0, color, step(0.0, res)); color = mix(1.0, color, step(pixelSize, res));
minDistance = min(minDistance, res); minDistance = min(minDistance, res);
} }
@ -77,7 +78,7 @@ in vec2 position;
out vec2 fragmentColor; out vec2 fragmentColor;
void main() { void main() {
float minDistance = -10.0; float minDistance = -maxMinDistance;
float color = 0.0; float color = 0.0;
#if LINE_COUNT > 0 #if LINE_COUNT > 0
@ -86,6 +87,8 @@ void main() {
#if BLOB_COUNT > 0 #if BLOB_COUNT > 0
blobMinDistance(minDistance, color); blobMinDistance(minDistance, color);
//fragmentColor = vec2(-1.0, 2.0);
//return;
#endif #endif
fragmentColor = vec2(minDistance, color); fragmentColor = vec2(minDistance, color);

View file

@ -1,28 +1,28 @@
#version 300 es #version 300 es
precision mediump float; precision lowp float;
#define INFINITY 1000.0 #define INFINITY 1000.0
#define LIGHT_DROP 0.25 #define LIGHT_DROP 0.25
#define AMBIENT_LIGHT vec3(0.3) #define SOFT_SHADOWS_QUALITY 2.0
#define AMBIENT_LIGHT vec3(0.75)
#define CIRCLE_LIGHT_COUNT {circleLightCount} #define CIRCLE_LIGHT_COUNT {circleLightCount}
#define POINT_LIGHT_COUNT {pointLightCount} #define POINT_LIGHT_COUNT {pointLightCount}
#define SOFT_SHADOWS_ENABLED 1 uniform bool softShadowsEnabled;
#define HARD_SHADOWS_QUALITY 1.5
#define SOFT_SHADOWS_QUALITY 2.0 #define AIR_COLOR vec3(0.4)
uniform sampler2D distanceTexture; uniform sampler2D distanceTexture;
vec3[4] colors = vec3[4]( vec3[3] colors = vec3[](
vec3(0.2), vec3(0.1, 0.05, 0.15),
vec3(1.0, 1.0, 1.0),
vec3(0.0, 1.0, 0.0), vec3(0.0, 1.0, 0.0),
vec3(0.0, 0.0, 1.0) vec3(0.0, 0.0, 1.0)
); );
float getDistance(in vec2 target, out vec3 color) { float getDistance(in vec2 target, out vec3 color, out float nearness) {
vec4 values = texture(distanceTexture, target); vec4 values = texture(distanceTexture, target);
color = colors[int(values[1])]; color = colors[int(values[1])];
@ -56,13 +56,21 @@ float getDistance(in vec2 target) {
in vec2 position; in vec2 position;
in vec2 uvCoordinates; in vec2 uvCoordinates;
uniform vec2 viewAreaScale; uniform vec2 squareToAspectRatio;
out vec4 fragmentColor; out vec4 fragmentColor;
void main() { void main() {
vec3 colorAtPosition; vec3 colorAtPosition;
float startingDistance = getDistance(uvCoordinates, colorAtPosition); float nearness;
float startingDistance = getDistance(uvCoordinates, colorAtPosition, nearness);
if (startingDistance < 0.0) {
fragmentColor = vec4(colorAtPosition, 1.0);
return;
}
colorAtPosition = AIR_COLOR;
vec3 lighting = AMBIENT_LIGHT; vec3 lighting = AMBIENT_LIGHT;
#if CIRCLE_LIGHT_COUNT > 0 #if CIRCLE_LIGHT_COUNT > 0
@ -71,18 +79,14 @@ void main() {
float lightDistance = lightCenterDistance - circleLights[i].radius; float lightDistance = lightCenterDistance - circleLights[i].radius;
float traceDistance = lightCenterDistance - circleLights[i].traceRadius; float traceDistance = lightCenterDistance - circleLights[i].traceRadius;
if (traceDistance < 0.0) {
lighting = vec3(1.0, 1.0, 0.0);
}
vec3 lightColorAtPosition = circleLights[i].value / pow( vec3 lightColorAtPosition = circleLights[i].value / pow(
lightDistance / LIGHT_DROP + 1.0, 2.0 lightDistance / LIGHT_DROP + 1.0, 2.0
); );
#if SOFT_SHADOWS_ENABLED if (softShadowsEnabled) {
float q = INFINITY; float q = INFINITY;
float rayLength = startingDistance / SOFT_SHADOWS_QUALITY; float rayLength = startingDistance / SOFT_SHADOWS_QUALITY;
vec2 direction = normalize(circleLightDirections[i]) / viewAreaScale / 2.0; vec2 direction = normalize(circleLightDirections[i]) / squareToAspectRatio / 2.05;
for (int j = 0; j < 48 * int(ceil(SOFT_SHADOWS_QUALITY)); j++) { for (int j = 0; j < 48 * int(ceil(SOFT_SHADOWS_QUALITY)); j++) {
if (rayLength >= traceDistance) { if (rayLength >= traceDistance) {
lighting += lightColorAtPosition * clamp( lighting += lightColorAtPosition * clamp(
@ -96,20 +100,17 @@ void main() {
q = min(q, minDistance / rayLength); q = min(q, minDistance / rayLength);
rayLength += minDistance / SOFT_SHADOWS_QUALITY; rayLength += minDistance / SOFT_SHADOWS_QUALITY;
} }
#else } else {
float rayLength = startingDistance / 2.0; float rayLength = startingDistance;
vec2 direction = normalize(circleLightDirections[i]) / viewAreaScale / 2.0; vec2 direction = normalize(circleLightDirections[i]) / squareToAspectRatio / 2.05;
for (int j = 0; j < 24 * int(ceil(HARD_SHADOWS_QUALITY)); j++) { for (int j = 0; j < 24; j++) {
float minDistance = getDistance(uvCoordinates + direction * rayLength); float currentDistance = getDistance(uvCoordinates + direction * rayLength);
if (minDistance < 0.0) { rayLength += currentDistance;
break;
}
rayLength += minDistance / HARD_SHADOWS_QUALITY;
} }
if (rayLength >= traceDistance) { if (rayLength >= traceDistance) {
lighting += lightColorAtPosition * step(0.0, startingDistance); lighting += lightColorAtPosition * step(0.0, startingDistance);
} }
#endif }
} }
#endif #endif
@ -143,15 +144,5 @@ void main() {
}*/ }*/
#endif #endif
float d = startingDistance;
vec3 col = (d<0.0) ? vec3(0.6,0.8,1.0) : vec3(0.9,0.6,0.3);
col *= 1.0 - exp(-9.0*abs(d));
col *= 1.0 + 0.2*cos(128.0*abs(d));
col = mix( col, vec3(1.0), 1.0-smoothstep(0.0,0.015,abs(d)) );
fragmentColor = vec4(col, 1.0);
fragmentColor = vec4(colorAtPosition * lighting, 1.0); fragmentColor = vec4(colorAtPosition * lighting, 1.0);
fragmentColor = vec4(mix(colorAtPosition * lighting, col, 0.2), 1.0);
//fragmentColor = vec4(vec3(startingDistance), 1.0);
} }

View file

@ -1,9 +1,9 @@
#version 300 es #version 300 es
precision mediump float; precision lowp float;
uniform mat3 modelTransform; uniform mat3 modelTransform;
uniform vec2 viewAreaScale; uniform vec2 squareToAspectRatio;
in vec4 vertexPosition; in vec4 vertexPosition;
out vec2 position; out vec2 position;
@ -11,5 +11,5 @@ out vec2 position;
void main() { void main() {
vec3 vertexPosition2D = vec3(vertexPosition.xy, 1.0) * modelTransform; vec3 vertexPosition2D = vec3(vertexPosition.xy, 1.0) * modelTransform;
gl_Position = vec4(vertexPosition2D.xy, 0.0, 1.0); gl_Position = vec4(vertexPosition2D.xy, 0.0, 1.0);
position = vertexPosition2D.xy * viewAreaScale; position = vertexPosition2D.xy * squareToAspectRatio;
} }

View file

@ -1,6 +1,6 @@
#version 300 es #version 300 es
precision mediump float; precision lowp float;
#define CIRCLE_LIGHT_COUNT {circleLightCount} #define CIRCLE_LIGHT_COUNT {circleLightCount}
#define POINT_LIGHT_COUNT {pointLightCount} #define POINT_LIGHT_COUNT {pointLightCount}
@ -11,7 +11,7 @@ in vec4 vertexPosition;
out vec2 position; out vec2 position;
out vec2 uvCoordinates; out vec2 uvCoordinates;
uniform vec2 viewAreaScale; uniform vec2 squareToAspectRatio;
#if CIRCLE_LIGHT_COUNT > 0 #if CIRCLE_LIGHT_COUNT > 0
uniform struct { uniform struct {
@ -37,7 +37,7 @@ uniform vec2 viewAreaScale;
void main() { void main() {
vec3 vertexPosition2D = vec3(vertexPosition.xy, 1.0) * modelTransform; vec3 vertexPosition2D = vec3(vertexPosition.xy, 1.0) * modelTransform;
gl_Position = vec4(vertexPosition2D.xy, 0.0, 1.0); gl_Position = vec4(vertexPosition2D.xy, 0.0, 1.0);
position = vertexPosition2D.xy * viewAreaScale; position = vertexPosition2D.xy * squareToAspectRatio;
uvCoordinates = (vertexPosition2D * mat3( uvCoordinates = (vertexPosition2D * mat3(
0.5, 0.0, 0.5, 0.5, 0.0, 0.5,

View file

@ -1,13 +1,14 @@
import { mix } from './mix'; import { InfoText } from '../objects/types/info-text';
import { clamp } from './clamp'; import { clamp } from './clamp';
import { mix } from './mix';
export class Autoscaler { export class Autoscaler {
// can have fractions // can have fractions
private index: number; private index: number;
constructor( constructor(
private setters: Array<(value: number) => void>, private setters: { [key: string]: (value: number | boolean) => void },
private targets: Array<Array<number>>, private targets: Array<{ [key: string]: number | boolean }>,
startingIndex: number, startingIndex: number,
private scalingOptions: { private scalingOptions: {
additiveIncrease: number; additiveIncrease: number;
@ -38,8 +39,22 @@ export class Autoscaler {
const nextTarget = const nextTarget =
floor + 1 == this.targets.length ? previousTarget : this.targets[floor + 1]; floor + 1 == this.targets.length ? previousTarget : this.targets[floor + 1];
this.setters.forEach((setter, i) => const result = {};
setter(mix(previousTarget[i], nextTarget[i], fract)) for (const key in this.setters) {
); const previous = previousTarget[key];
const next = nextTarget[key];
let current: number | boolean;
if (typeof previous == 'number') {
current = mix(previous, next as number, fract);
} else {
current = next;
}
result[key] = current;
this.setters[key](current);
}
InfoText.modifyRecord('quality', result);
} }
} }

View file

@ -2,6 +2,8 @@ import { RenderCommand } from '../../drawing/commands/render';
import { GameObject } from '../game-object'; import { GameObject } from '../game-object';
export class InfoText extends GameObject { export class InfoText extends GameObject {
private static MinRowLength = 60;
constructor() { constructor() {
super(); super();
@ -10,13 +12,25 @@ export class InfoText extends GameObject {
private static records: Map<string, string> = new Map(); private static records: Map<string, string> = new Map();
public static modifyRecord(key: string, value: string) { public static modifyRecord(key: string, value: string | any) {
if (typeof value == 'string') {
value = ' ' + value;
} else {
value = JSON.stringify(
value,
(_, v) => (v.toFixed ? Number(v.toFixed(2)) : v),
' '
);
}
InfoText.records.set(key, value); InfoText.records.set(key, value);
} }
private draw(e: RenderCommand) { private draw(e: RenderCommand) {
let text = ''; let text = '';
InfoText.records.forEach((v, k) => (text += `${k}\n\t${v}\n`)); InfoText.records.forEach(
(v, k) => (text += `${k}\n${v.padEnd(InfoText.MinRowLength)}\n`)
);
e.renderer.drawInfoText(text); e.renderer.drawInfoText(text);
} }
} }