Add more options

This commit is contained in:
schmelczerandras 2020-09-17 17:51:45 +02:00
parent d0b53678e2
commit 2481cfc3e6
14 changed files with 102 additions and 117 deletions

View file

@ -1,5 +1,2 @@
.*
**/tsconfig.json
**/webpack.config.js
node_modules node_modules
src src

View file

@ -1,8 +1,8 @@
import { FrameBuffer } from './frame-buffer'; import { FrameBuffer } from './frame-buffer';
export class DefaultFrameBuffer extends FrameBuffer { export class DefaultFrameBuffer extends FrameBuffer {
constructor(gl: WebGL2RenderingContext, enableHighDpiRendering: boolean) { constructor(gl: WebGL2RenderingContext) {
super(gl, enableHighDpiRendering); super(gl);
this.frameBuffer = null; this.frameBuffer = null;
this.setSize(); this.setSize();
} }

View file

@ -2,16 +2,14 @@ import { vec2 } from 'gl-matrix';
export abstract class FrameBuffer { export abstract class FrameBuffer {
public renderScale = 1; public renderScale = 1;
public enableHighDpiRendering = false;
protected size = vec2.create(); protected size = vec2.create();
// null means the default framebuffer // null means the default framebuffer
protected frameBuffer: WebGLFramebuffer | null = null; protected frameBuffer: WebGLFramebuffer | null = null;
constructor( constructor(protected gl: WebGL2RenderingContext) {}
protected gl: WebGL2RenderingContext,
private enableHighDpiRendering: boolean
) {}
public bindAndClear(colorInput?: WebGLTexture) { public bindAndClear(colorInput?: WebGLTexture) {
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.frameBuffer); this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.frameBuffer);

View file

@ -5,8 +5,8 @@ export class IntermediateFrameBuffer extends FrameBuffer {
private frameTexture: WebGLTexture; private frameTexture: WebGLTexture;
private floatLinearEnabled = false; private floatLinearEnabled = false;
constructor(gl: WebGL2RenderingContext, enableHighDpiRendering: boolean) { constructor(gl: WebGL2RenderingContext) {
super(gl, enableHighDpiRendering); super(gl);
enableExtension(gl, 'EXT_color_buffer_float'); enableExtension(gl, 'EXT_color_buffer_float');

View file

@ -1,12 +1,14 @@
import { vec2 } from 'gl-matrix'; import { vec2 } from 'gl-matrix';
import { Drawable } from '../../drawables/drawable'; import { Drawable } from '../../drawables/drawable';
import { RuntimeSettings } from './runtime-settings';
export interface Renderer { export interface Renderer {
readonly canvasSize: vec2; readonly canvasSize: vec2;
readonly viewAreaSize: vec2;
readonly insights: any; readonly insights: any;
setViewArea(topLeft: vec2, size: vec2): void; setViewArea(topLeft: vec2, size: vec2): void;
setCursorPosition(position: vec2): void; setRuntimeSettings(overrides: Partial<RuntimeSettings>): void;
addDrawable(drawable: Drawable): void; addDrawable(drawable: Drawable): void;
autoscaleQuality(deltaTime: DOMHighResTimeStamp): void; autoscaleQuality(deltaTime: DOMHighResTimeStamp): void;
renderDrawables(): void; renderDrawables(): void;

View file

@ -5,21 +5,20 @@ import { FrameBuffer } from '../graphics-library/frame-buffer/frame-buffer';
import { UniformArrayAutoScalingProgram } from '../graphics-library/program/uniform-array-autoscaling-program'; import { UniformArrayAutoScalingProgram } from '../graphics-library/program/uniform-array-autoscaling-program';
export class RenderingPass { export class RenderingPass {
public tileMultiplier = 8;
public isWorldInverted = false;
private drawables: Array<Drawable> = []; private drawables: Array<Drawable> = [];
private program: UniformArrayAutoScalingProgram; private program: UniformArrayAutoScalingProgram;
constructor( constructor(gl: WebGL2RenderingContext, private frame: FrameBuffer) {
gl: WebGL2RenderingContext,
private frame: FrameBuffer,
private tileMultiplier: number
) {
this.program = new UniformArrayAutoScalingProgram(gl); this.program = new UniformArrayAutoScalingProgram(gl);
} }
public async initialize( public async initialize(
shaderSources: [string, string], shaderSources: [string, string],
descriptors: Array<DrawableDescriptor>, descriptors: Array<DrawableDescriptor>,
substitutions: { [name: string]: any } substitutions: { [name: string]: any } = {}
): Promise<void> { ): Promise<void> {
await this.program.initialize(shaderSources, descriptors, substitutions); await this.program.initialize(shaderSources, descriptors, substitutions);
} }
@ -43,7 +42,10 @@ export class RenderingPass {
for (let x = -1; x < 1; x += stepsInNDC) { for (let x = -1; x < 1; x += stepsInNDC) {
for (let y = -1; y < 1; y += stepsInNDC) { for (let y = -1; y < 1; y += stepsInNDC) {
const uniforms = { ...commonUniforms, maxMinDistance: radiusInNDC }; const uniforms = {
...commonUniforms,
maxMinDistance: radiusInNDC * (this.isWorldInverted ? -1 : 1),
};
const ndcBottomLeft = vec2.fromValues(x, y); const ndcBottomLeft = vec2.fromValues(x, y);

View file

@ -0,0 +1,8 @@
import { vec3 } from 'gl-matrix';
export interface RuntimeSettings {
enableHighDpiRendering: boolean;
tileMultiplier: number;
isWorldInverted: boolean;
ambientLight: vec3;
}

View file

@ -4,7 +4,6 @@ precision lowp float;
#define INFINITY 1000.0 #define INFINITY 1000.0
#define INTENSITY_INSIDE_RATIO 0.5 #define INTENSITY_INSIDE_RATIO 0.5
#define AMBIENT_LIGHT vec3(0.25, 0.15, 0.25)
#define SHADOW_HARDNESS 150.0 #define SHADOW_HARDNESS 150.0
#define HARD_SHADOW_TRACE_COUNT {hardShadowTraceCount} #define HARD_SHADOW_TRACE_COUNT {hardShadowTraceCount}
#define SOFT_SHADOW_TRACE_COUNT {softShadowTraceCount} #define SOFT_SHADOW_TRACE_COUNT {softShadowTraceCount}
@ -19,6 +18,8 @@ uniform sampler2D distanceTexture;
in vec2 position; in vec2 position;
in vec2 uvCoordinates; in vec2 uvCoordinates;
uniform vec3 ambientLight;
vec3[3] colors = vec3[]( vec3[3] colors = vec3[](
{palette} {palette}
); );
@ -127,7 +128,7 @@ float shadowTransparency(float startingDistance, float lightCenterDistance, vec2
out vec4 fragmentColor; out vec4 fragmentColor;
void main() { void main() {
vec3 lighting = AMBIENT_LIGHT; vec3 lighting = ambientLight;
vec3 colorAtPosition; vec3 colorAtPosition;
float startingDistance = getDistance(uvCoordinates, colorAtPosition); float startingDistance = getDistance(uvCoordinates, colorAtPosition);

View file

@ -0,0 +1,5 @@
export interface StartupSettings {
enableStopwatch: boolean;
softShadowTraceCount: string;
hardShadowTraceCount: string;
}

View file

@ -1,6 +1,8 @@
import { mat2d, vec2 } from 'gl-matrix'; import { mat2d, vec2, vec3 } from 'gl-matrix';
export class UniformsProvider { export class UniformsProvider {
public ambientLight = vec3.fromValues(0.25, 0.15, 0.25);
private scaleWorldLengthToNDC = 1; private scaleWorldLengthToNDC = 1;
private transformWorldToNDC = mat2d.create(); private transformWorldToNDC = mat2d.create();
@ -8,17 +10,15 @@ export class UniformsProvider {
private worldAreaInView = vec2.create(); private worldAreaInView = vec2.create();
private squareToAspectRatio = vec2.create(); private squareToAspectRatio = vec2.create();
private uvToWorld = mat2d.create(); private uvToWorld = mat2d.create();
private cursorPosition = vec2.create();
public softShadowsEnabled?: boolean; public softShadowsEnabled?: boolean;
public constructor(private gl: WebGL2RenderingContext) {} public constructor(private gl: WebGL2RenderingContext) {}
public getUniforms(uniforms: any): any { public getUniforms(uniforms: any): any {
const cursorPosition = this.uvToWorldCoordinate(this.cursorPosition);
return { return {
...uniforms, ...uniforms,
cursorPosition, ambientLight: this.ambientLight,
uvToWorld: this.uvToWorld, uvToWorld: this.uvToWorld,
worldAreaInView: this.worldAreaInView, worldAreaInView: this.worldAreaInView,
squareToAspectRatio: this.squareToAspectRatio, squareToAspectRatio: this.squareToAspectRatio,
@ -51,6 +51,10 @@ export class UniformsProvider {
); );
} }
public getViewArea(): vec2 {
return this.worldAreaInView;
}
public setViewArea(topLeft: vec2, size: vec2) { public setViewArea(topLeft: vec2, size: vec2) {
this.worldAreaInView = size; this.worldAreaInView = size;
@ -87,8 +91,4 @@ export class UniformsProvider {
this.uvToWorld = mat2d.fromTranslation(mat2d.create(), this.viewAreaBottomLeft); this.uvToWorld = mat2d.fromTranslation(mat2d.create(), this.viewAreaBottomLeft);
mat2d.scale(this.uvToWorld, this.uvToWorld, this.worldAreaInView); mat2d.scale(this.uvToWorld, this.uvToWorld, this.worldAreaInView);
} }
public setCursorPosition(position: vec2): void {
this.cursorPosition = position;
}
} }

View file

@ -1,11 +0,0 @@
export interface WebGl2RendererSettings {
enableHighDpiRendering: boolean;
enableStopwatch: boolean;
tileMultiplier: number;
shaderMacros: {
softShadowTraceCount: string;
hardShadowTraceCount: string;
// ambientColor: vec3;
// defaultIsInside: boolean;
};
}

View file

@ -11,12 +11,13 @@ import { Insights } from './insights';
import { Renderer } from './renderer'; import { Renderer } from './renderer';
import { RenderingPass } from './rendering-pass'; import { RenderingPass } from './rendering-pass';
import { RenderingPassName } from './rendering-pass-name'; import { RenderingPassName } from './rendering-pass-name';
import { RuntimeSettings } from './runtime-settings';
import distanceFragmentShader from './shaders/distance-fs.glsl'; import distanceFragmentShader from './shaders/distance-fs.glsl';
import distanceVertexShader from './shaders/distance-vs.glsl'; import distanceVertexShader from './shaders/distance-vs.glsl';
import lightsFragmentShader from './shaders/shading-fs.glsl'; import lightsFragmentShader from './shaders/shading-fs.glsl';
import lightsVertexShader from './shaders/shading-vs.glsl'; import lightsVertexShader from './shaders/shading-vs.glsl';
import { StartupSettings } from './startup-settings';
import { UniformsProvider } from './uniforms-provider'; import { UniformsProvider } from './uniforms-provider';
import { WebGl2RendererSettings } from './webgl2-renderer-settings';
type Passes = { [key in keyof typeof RenderingPassName]: RenderingPass }; type Passes = { [key in keyof typeof RenderingPassName]: RenderingPass };
@ -28,38 +29,49 @@ export class WebGl2Renderer implements Renderer {
private lightingFrameBuffer: DefaultFrameBuffer; private lightingFrameBuffer: DefaultFrameBuffer;
private passes: Passes; private passes: Passes;
private autoscaler: FpsAutoscaler; private autoscaler: FpsAutoscaler;
private settings: WebGl2RendererSettings;
private static defaultSettings: WebGl2RendererSettings = { private applyRuntimeSettings: {
enableHighDpiRendering: true, [key: string]: (value: any) => void;
enableStopwatch: true, } = {
tileMultiplier: 8, enableHighDpiRendering: (v) => {
shaderMacros: { this.distanceFieldFrameBuffer.enableHighDpiRendering = v;
softShadowTraceCount: '128', this.lightingFrameBuffer.enableHighDpiRendering = v;
hardShadowTraceCount: '32', },
tileMultiplier: (v) => {
this.passes[RenderingPassName.distance].tileMultiplier = v;
this.passes[RenderingPassName.pixel].tileMultiplier = v;
},
isWorldInverted: (v) => {
this.passes[RenderingPassName.distance].isWorldInverted = v;
this.passes[RenderingPassName.pixel].isWorldInverted = v;
},
ambientLight: (v) => {
this.uniformsProvider.ambientLight = v;
}, },
}; };
private static defaultStartupSettings: StartupSettings = {
enableStopwatch: true,
softShadowTraceCount: '128',
hardShadowTraceCount: '32',
};
setRuntimeSettings(overrides: Partial<RuntimeSettings>): void {
Object.entries(overrides).forEach((k, v) => {
this.applyRuntimeSettings[(k as unknown) as string](v);
});
}
constructor( constructor(
private canvas: HTMLCanvasElement, private canvas: HTMLCanvasElement,
private descriptors: Array<DrawableDescriptor>, private descriptors: Array<DrawableDescriptor>
private palette: Array<vec3>,
settingsOverrides: Partial<WebGl2RendererSettings>
) { ) {
this.settings = { ...WebGl2Renderer.defaultSettings, ...settingsOverrides };
this.gl = getWebGl2Context(canvas); this.gl = getWebGl2Context(canvas);
ParallelCompiler.initialize(this.gl); ParallelCompiler.initialize(this.gl);
this.distanceFieldFrameBuffer = new IntermediateFrameBuffer( this.distanceFieldFrameBuffer = new IntermediateFrameBuffer(this.gl);
this.gl, this.lightingFrameBuffer = new DefaultFrameBuffer(this.gl);
this.settings.enableHighDpiRendering
);
this.lightingFrameBuffer = new DefaultFrameBuffer(
this.gl,
this.settings.enableHighDpiRendering
);
this.passes = this.createPasses(); this.passes = this.createPasses();
@ -72,14 +84,6 @@ export class WebGl2Renderer implements Renderer {
softShadowsEnabled: (v) => softShadowsEnabled: (v) =>
(this.uniformsProvider.softShadowsEnabled = v as boolean), (this.uniformsProvider.softShadowsEnabled = v as boolean),
}); });
if (this.settings.enableStopwatch) {
try {
this.stopwatch = new WebGlStopwatch(this.gl);
} catch {
// no problem
}
}
} }
@Insights.measure('create render passes') @Insights.measure('create render passes')
@ -87,26 +91,25 @@ export class WebGl2Renderer implements Renderer {
return { return {
[RenderingPassName.distance]: new RenderingPass( [RenderingPassName.distance]: new RenderingPass(
this.gl, this.gl,
this.distanceFieldFrameBuffer, this.distanceFieldFrameBuffer
this.settings.tileMultiplier
),
[RenderingPassName.pixel]: new RenderingPass(
this.gl,
this.lightingFrameBuffer,
this.settings.tileMultiplier
), ),
[RenderingPassName.pixel]: new RenderingPass(this.gl, this.lightingFrameBuffer),
}; };
} }
@Insights.measure('initialize render passes') @Insights.measure('initialize render passes')
public async initialize(): Promise<void> { public async initialize(
palette: Array<vec3>,
settingsOverrides: Partial<StartupSettings>
): Promise<void> {
const settings = { ...WebGl2Renderer.defaultStartupSettings, ...settingsOverrides };
const promises: Array<Promise<void>> = []; const promises: Array<Promise<void>> = [];
promises.push( promises.push(
this.passes[RenderingPassName.distance].initialize( this.passes[RenderingPassName.distance].initialize(
[distanceVertexShader, distanceFragmentShader], [distanceVertexShader, distanceFragmentShader],
this.descriptors.filter(WebGl2Renderer.hasSdf), this.descriptors.filter(WebGl2Renderer.hasSdf)
this.settings.shaderMacros
) )
); );
@ -115,8 +118,9 @@ export class WebGl2Renderer implements Renderer {
[lightsVertexShader, lightsFragmentShader], [lightsVertexShader, lightsFragmentShader],
this.descriptors.filter((d) => !WebGl2Renderer.hasSdf(d)), this.descriptors.filter((d) => !WebGl2Renderer.hasSdf(d)),
{ {
palette: this.generatePaletteCode(this.palette), palette: this.generatePaletteCode(palette),
...this.settings.shaderMacros, hardShadowTraceCount: settings.hardShadowTraceCount,
softShadowTraceCount: settings.softShadowTraceCount,
} }
) )
); );
@ -124,6 +128,14 @@ export class WebGl2Renderer implements Renderer {
ParallelCompiler.compilePrograms(); ParallelCompiler.compilePrograms();
await Promise.all(promises); await Promise.all(promises);
if (settings.enableStopwatch) {
try {
this.stopwatch = new WebGlStopwatch(this.gl);
} catch {
// no problem
}
}
} }
public get insights(): any { public get insights(): any {
@ -191,8 +203,8 @@ export class WebGl2Renderer implements Renderer {
this.uniformsProvider.setViewArea(topLeft, size); this.uniformsProvider.setViewArea(topLeft, size);
} }
public setCursorPosition(position: vec2) { public get viewAreaSize(): vec2 {
this.uniformsProvider.setCursorPosition(position); return this.uniformsProvider.getViewArea();
} }
public get canvasSize(): vec2 { public get canvasSize(): vec2 {

View file

@ -1,29 +0,0 @@
export const waitWhileFalse = (body: () => boolean): Promise<void> => {
let resolveOnDone: () => void;
let rejectOnDone: (e: Error) => void;
const onDone = new Promise<void>((resolve, reject) => {
resolveOnDone = resolve;
rejectOnDone = reject;
});
const waiter = () => {
let success: boolean;
try {
success = body();
} catch (e) {
rejectOnDone(e);
return;
}
if (success) {
resolveOnDone();
} else {
requestAnimationFrame(waiter);
}
};
waiter();
return onDone;
};

View file

@ -1,8 +1,8 @@
import { vec3 } from 'gl-matrix'; import { vec3 } from 'gl-matrix';
import { DrawableDescriptor } from './drawables/drawable-descriptor'; import { DrawableDescriptor } from './drawables/drawable-descriptor';
import { Renderer } from './graphics/rendering/renderer'; import { Renderer } from './graphics/rendering/renderer';
import { StartupSettings } from './graphics/rendering/startup-settings';
import { WebGl2Renderer } from './graphics/rendering/webgl2-renderer'; import { WebGl2Renderer } from './graphics/rendering/webgl2-renderer';
import { WebGl2RendererSettings } from './graphics/rendering/webgl2-renderer-settings';
import { applyArrayPlugins } from './helper/array'; import { applyArrayPlugins } from './helper/array';
export { Drawable } from './drawables/drawable'; export { Drawable } from './drawables/drawable';
@ -30,11 +30,11 @@ export const compile = async (
canvas: HTMLCanvasElement, canvas: HTMLCanvasElement,
descriptors: Array<DrawableDescriptor>, descriptors: Array<DrawableDescriptor>,
palette: Array<vec3>, palette: Array<vec3>,
settings: Partial<WebGl2RendererSettings> = {} settings: Partial<StartupSettings> = {}
): Promise<Renderer> => { ): Promise<Renderer> => {
applyArrayPlugins(); applyArrayPlugins();
const renderer = new WebGl2Renderer(canvas, descriptors, palette, settings); const renderer = new WebGl2Renderer(canvas, descriptors);
await renderer.initialize(); await renderer.initialize(palette, settings);
return renderer; return renderer;
}; };