Add files
This commit is contained in:
commit
77bde04db3
97 changed files with 10327 additions and 0 deletions
45
src/graphics/rendering/fps-autoscaler.ts
Normal file
45
src/graphics/rendering/fps-autoscaler.ts
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import { Autoscaler as AutoScaler } from '../../helper/autoscaler';
|
||||
import { exponentialDecay } from '../../helper/exponential-decay';
|
||||
import { settings } from '../settings';
|
||||
|
||||
export class FpsAutoscaler extends AutoScaler {
|
||||
private timeSinceLastAdjusment = 0;
|
||||
private exponentialDecayedDeltaTime = 0.0;
|
||||
|
||||
constructor(setters: { [key: string]: (value: number | boolean) => void }) {
|
||||
super(
|
||||
setters,
|
||||
settings.qualityScaling.qualityTargets,
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
86
src/graphics/rendering/rendering-pass.ts
Normal file
86
src/graphics/rendering/rendering-pass.ts
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
import { vec2 } from 'gl-matrix';
|
||||
import { IDrawable } from '../drawables/i-drawable';
|
||||
import { IDrawableDescriptor } from '../drawables/i-drawable-descriptor';
|
||||
import { FrameBuffer } from '../graphics-library/frame-buffer/frame-buffer';
|
||||
import { UniformArrayAutoScalingProgram } from '../graphics-library/program/uniform-array-autoscaling-program';
|
||||
import { settings } from '../settings';
|
||||
|
||||
export class RenderingPass {
|
||||
private drawables: Array<IDrawable> = [];
|
||||
private program: UniformArrayAutoScalingProgram;
|
||||
|
||||
constructor(
|
||||
gl: WebGL2RenderingContext,
|
||||
shaderSources: [string, string],
|
||||
drawableDescriptors: Array<IDrawableDescriptor>,
|
||||
private frame: FrameBuffer
|
||||
) {
|
||||
this.program = new UniformArrayAutoScalingProgram(
|
||||
gl,
|
||||
shaderSources,
|
||||
drawableDescriptors
|
||||
);
|
||||
}
|
||||
|
||||
public async initialize(): Promise<void> {
|
||||
await this.program.initialize();
|
||||
}
|
||||
|
||||
public addDrawable(drawable: IDrawable) {
|
||||
this.drawables.push(drawable);
|
||||
}
|
||||
|
||||
public render(commonUniforms: any, inputTexture?: WebGLTexture) {
|
||||
this.frame.bindAndClear(inputTexture);
|
||||
|
||||
const stepsInUV = 1 / settings.tileMultiplier;
|
||||
|
||||
const worldR =
|
||||
0.5 *
|
||||
vec2.length(vec2.scale(vec2.create(), commonUniforms.worldAreaInView, stepsInUV));
|
||||
|
||||
const radiusInNDC = worldR * commonUniforms.scaleWorldLengthToNDC;
|
||||
|
||||
const stepsInNDC = 2 * stepsInUV;
|
||||
|
||||
for (let x = -1; x < 1; x += stepsInNDC) {
|
||||
for (let y = -1; y < 1; y += stepsInNDC) {
|
||||
const uniforms = { ...commonUniforms, maxMinDistance: 0.0 };
|
||||
|
||||
const ndcBottomLeft = vec2.fromValues(x, y);
|
||||
|
||||
this.program.setDrawingRectangleUV(
|
||||
[(ndcBottomLeft.x + 1) / 2, (ndcBottomLeft.y + 1) / 2],
|
||||
vec2.fromValues(stepsInUV, stepsInUV)
|
||||
);
|
||||
|
||||
const tileCenterWorldCoordinates = vec2.transformMat2d(
|
||||
vec2.create(),
|
||||
vec2.add(
|
||||
vec2.create(),
|
||||
[(ndcBottomLeft.x + 1) / 2, (ndcBottomLeft.y + 1) / 2],
|
||||
vec2.fromValues(stepsInUV / 2, stepsInUV / 2)
|
||||
),
|
||||
uniforms.uvToWorld
|
||||
);
|
||||
|
||||
const primitivesNearTile = this.drawables.filter(
|
||||
(d) => d.distance(tileCenterWorldCoordinates) < 2 * worldR
|
||||
);
|
||||
|
||||
primitivesNearTile.forEach((p) =>
|
||||
p.serializeToUniforms(
|
||||
uniforms,
|
||||
uniforms.scaleWorldLengthToNDC,
|
||||
uniforms.transformWorldToNDC
|
||||
)
|
||||
);
|
||||
|
||||
this.program.bindAndSetUniforms(uniforms);
|
||||
this.program.draw();
|
||||
}
|
||||
}
|
||||
|
||||
this.drawables = [];
|
||||
}
|
||||
}
|
||||
94
src/graphics/rendering/uniforms-provider.ts
Normal file
94
src/graphics/rendering/uniforms-provider.ts
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
import { mat2d, vec2 } from 'gl-matrix';
|
||||
|
||||
export class UniformsProvider {
|
||||
private scaleWorldLengthToNDC = 1;
|
||||
private transformWorldToNDC = mat2d.create();
|
||||
|
||||
private viewAreaBottomLeft = vec2.create();
|
||||
private worldAreaInView = vec2.create();
|
||||
private squareToAspectRatio = vec2.create();
|
||||
private uvToWorld = mat2d.create();
|
||||
private cursorPosition = vec2.create();
|
||||
|
||||
public softShadowsEnabled?: boolean;
|
||||
|
||||
public constructor(private gl: WebGL2RenderingContext) {}
|
||||
|
||||
public getUniforms(uniforms: any): any {
|
||||
const cursorPosition = this.uvToWorldCoordinate(this.cursorPosition);
|
||||
return {
|
||||
...uniforms,
|
||||
cursorPosition,
|
||||
uvToWorld: this.uvToWorld,
|
||||
worldAreaInView: this.worldAreaInView,
|
||||
squareToAspectRatio: this.squareToAspectRatio,
|
||||
scaleWorldLengthToNDC: this.scaleWorldLengthToNDC,
|
||||
transformWorldToNDC: this.transformWorldToNDC,
|
||||
squareToAspectRatioTimes2: vec2.scale(vec2.create(), this.squareToAspectRatio, 2),
|
||||
softShadowsEnabled: this.softShadowsEnabled,
|
||||
};
|
||||
}
|
||||
|
||||
private getScreenToWorldTransform(screenSize: vec2) {
|
||||
const transform = mat2d.fromTranslation(mat2d.create(), this.viewAreaBottomLeft);
|
||||
mat2d.scale(
|
||||
transform,
|
||||
transform,
|
||||
vec2.divide(vec2.create(), this.worldAreaInView, screenSize)
|
||||
);
|
||||
mat2d.translate(transform, transform, vec2.fromValues(0.5, 0.5));
|
||||
|
||||
return transform;
|
||||
}
|
||||
|
||||
public uvToWorldCoordinate(screenUvPosition: vec2): vec2 {
|
||||
const resolution = vec2.fromValues(this.gl.canvas.width, this.gl.canvas.height);
|
||||
|
||||
return vec2.transformMat2d(
|
||||
vec2.create(),
|
||||
vec2.multiply(vec2.create(), screenUvPosition, resolution),
|
||||
this.getScreenToWorldTransform(resolution)
|
||||
);
|
||||
}
|
||||
|
||||
public setViewArea(topLeft: vec2, size: vec2) {
|
||||
this.worldAreaInView = size;
|
||||
|
||||
// world coordinates
|
||||
this.viewAreaBottomLeft = vec2.add(
|
||||
vec2.create(),
|
||||
topLeft,
|
||||
vec2.fromValues(0, -size.y)
|
||||
);
|
||||
|
||||
const scaleLongerEdgeTo1 =
|
||||
1 / Math.max(this.worldAreaInView.x, this.worldAreaInView.y);
|
||||
|
||||
this.squareToAspectRatio = vec2.fromValues(
|
||||
this.worldAreaInView.x * scaleLongerEdgeTo1,
|
||||
this.worldAreaInView.y * scaleLongerEdgeTo1
|
||||
);
|
||||
|
||||
this.scaleWorldLengthToNDC = scaleLongerEdgeTo1 * 2;
|
||||
|
||||
mat2d.fromScaling(
|
||||
this.transformWorldToNDC,
|
||||
vec2.fromValues(this.scaleWorldLengthToNDC, this.scaleWorldLengthToNDC)
|
||||
);
|
||||
mat2d.translate(
|
||||
this.transformWorldToNDC,
|
||||
this.transformWorldToNDC,
|
||||
vec2.fromValues(
|
||||
-this.viewAreaBottomLeft.x - 0.5 * this.worldAreaInView.x,
|
||||
-this.viewAreaBottomLeft.y - 0.5 * this.worldAreaInView.y
|
||||
)
|
||||
);
|
||||
|
||||
this.uvToWorld = mat2d.fromTranslation(mat2d.create(), this.viewAreaBottomLeft);
|
||||
mat2d.scale(this.uvToWorld, this.uvToWorld, this.worldAreaInView);
|
||||
}
|
||||
|
||||
public setCursorPosition(position: vec2): void {
|
||||
this.cursorPosition = position;
|
||||
}
|
||||
}
|
||||
126
src/graphics/rendering/webgl2-renderer.ts
Normal file
126
src/graphics/rendering/webgl2-renderer.ts
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
import { vec2 } from 'gl-matrix';
|
||||
import { IDrawable } from '../drawables/i-drawable';
|
||||
import { CircleLight } from '../drawables/lights/circle-light';
|
||||
import { Flashlight } from '../drawables/lights/flashlight';
|
||||
import { ILight } from '../drawables/lights/i-light';
|
||||
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';
|
||||
import { WebGlStopwatch } from '../graphics-library/helper/stopwatch';
|
||||
import { IRenderer } from '../i-renderer';
|
||||
import distanceFragmentShader from '../shaders/distance-fs.glsl';
|
||||
import distanceVertexShader from '../shaders/distance-vs.glsl';
|
||||
import lightsFragmentShader from '../shaders/shading-fs.glsl';
|
||||
import lightsVertexShader from '../shaders/shading-vs.glsl';
|
||||
import { FpsAutoscaler } from './fps-autoscaler';
|
||||
import { RenderingPass } from './rendering-pass';
|
||||
import { UniformsProvider } from './uniforms-provider';
|
||||
|
||||
export class WebGl2Renderer implements IRenderer {
|
||||
private gl: WebGL2RenderingContext;
|
||||
private stopwatch?: WebGlStopwatch;
|
||||
private uniformsProvider: UniformsProvider;
|
||||
private distanceFieldFrameBuffer: IntermediateFrameBuffer;
|
||||
private lightingFrameBuffer: DefaultFrameBuffer;
|
||||
private distancePass: RenderingPass;
|
||||
private lightingPass: RenderingPass;
|
||||
private autoscaler: FpsAutoscaler;
|
||||
|
||||
private initializePromise: Promise<[void, void]>;
|
||||
|
||||
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,
|
||||
[distanceVertexShader, distanceFragmentShader],
|
||||
[],
|
||||
this.distanceFieldFrameBuffer
|
||||
);
|
||||
|
||||
this.lightingPass = new RenderingPass(
|
||||
this.gl,
|
||||
[lightsVertexShader, lightsFragmentShader],
|
||||
[CircleLight.descriptor, Flashlight.descriptor],
|
||||
this.lightingFrameBuffer
|
||||
);
|
||||
|
||||
this.initializePromise = Promise.all([
|
||||
this.distancePass.initialize(),
|
||||
this.lightingPass.initialize(),
|
||||
]);
|
||||
|
||||
this.uniformsProvider = new UniformsProvider(this.gl);
|
||||
|
||||
this.autoscaler = new FpsAutoscaler({
|
||||
distanceRenderScale: (v) =>
|
||||
(this.distanceFieldFrameBuffer.renderScale = v as number),
|
||||
finalRenderScale: (v) => (this.lightingFrameBuffer.renderScale = v as number),
|
||||
softShadowsEnabled: (v) =>
|
||||
(this.uniformsProvider.softShadowsEnabled = v as boolean),
|
||||
});
|
||||
|
||||
try {
|
||||
this.stopwatch = new WebGlStopwatch(this.gl);
|
||||
} catch {
|
||||
// no problem
|
||||
}
|
||||
}
|
||||
|
||||
public async initialize(): Promise<void> {
|
||||
await this.initializePromise;
|
||||
}
|
||||
|
||||
public drawShape(shape: IDrawable) {
|
||||
this.distancePass.addDrawable(shape);
|
||||
}
|
||||
|
||||
public drawLight(light: ILight) {
|
||||
this.lightingPass.addDrawable(light);
|
||||
}
|
||||
|
||||
public startFrame(deltaTime: DOMHighResTimeStamp) {
|
||||
this.autoscaler.autoscale(deltaTime);
|
||||
|
||||
this.stopwatch?.start();
|
||||
|
||||
this.distanceFieldFrameBuffer.setSize();
|
||||
this.lightingFrameBuffer.setSize();
|
||||
}
|
||||
|
||||
public finishFrame() {
|
||||
const common = {
|
||||
distanceNdcPixelSize: 2 / Math.max(...this.distanceFieldFrameBuffer.getSize()),
|
||||
shadingNdcPixelSize: 2 / Math.max(...this.distanceFieldFrameBuffer.getSize()),
|
||||
};
|
||||
|
||||
this.distancePass.render(this.uniformsProvider.getUniforms(common));
|
||||
this.lightingPass.render(
|
||||
this.uniformsProvider.getUniforms(common),
|
||||
this.distanceFieldFrameBuffer.colorTexture
|
||||
);
|
||||
|
||||
this.stopwatch?.stop();
|
||||
}
|
||||
|
||||
public setViewArea(topLeft: vec2, size: vec2) {
|
||||
this.uniformsProvider.setViewArea(topLeft, size);
|
||||
}
|
||||
|
||||
public setCursorPosition(position: vec2) {
|
||||
this.uniformsProvider.setCursorPosition(position);
|
||||
}
|
||||
|
||||
public get canvasSize(): vec2 {
|
||||
return vec2.fromValues(this.canvas.clientWidth, this.canvas.clientHeight);
|
||||
}
|
||||
|
||||
public drawInfoText(text: string) {
|
||||
if (this.overlay.innerText != text) {
|
||||
this.overlay.innerText = text;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue