Add more options
This commit is contained in:
parent
d0b53678e2
commit
2481cfc3e6
14 changed files with 102 additions and 117 deletions
|
|
@ -1,5 +1,2 @@
|
|||
.*
|
||||
**/tsconfig.json
|
||||
**/webpack.config.js
|
||||
node_modules
|
||||
src
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { FrameBuffer } from './frame-buffer';
|
||||
|
||||
export class DefaultFrameBuffer extends FrameBuffer {
|
||||
constructor(gl: WebGL2RenderingContext, enableHighDpiRendering: boolean) {
|
||||
super(gl, enableHighDpiRendering);
|
||||
constructor(gl: WebGL2RenderingContext) {
|
||||
super(gl);
|
||||
this.frameBuffer = null;
|
||||
this.setSize();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,16 +2,14 @@ import { vec2 } from 'gl-matrix';
|
|||
|
||||
export abstract class FrameBuffer {
|
||||
public renderScale = 1;
|
||||
public enableHighDpiRendering = false;
|
||||
|
||||
protected size = vec2.create();
|
||||
|
||||
// null means the default framebuffer
|
||||
protected frameBuffer: WebGLFramebuffer | null = null;
|
||||
|
||||
constructor(
|
||||
protected gl: WebGL2RenderingContext,
|
||||
private enableHighDpiRendering: boolean
|
||||
) {}
|
||||
constructor(protected gl: WebGL2RenderingContext) {}
|
||||
|
||||
public bindAndClear(colorInput?: WebGLTexture) {
|
||||
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.frameBuffer);
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ export class IntermediateFrameBuffer extends FrameBuffer {
|
|||
private frameTexture: WebGLTexture;
|
||||
private floatLinearEnabled = false;
|
||||
|
||||
constructor(gl: WebGL2RenderingContext, enableHighDpiRendering: boolean) {
|
||||
super(gl, enableHighDpiRendering);
|
||||
constructor(gl: WebGL2RenderingContext) {
|
||||
super(gl);
|
||||
|
||||
enableExtension(gl, 'EXT_color_buffer_float');
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
import { vec2 } from 'gl-matrix';
|
||||
import { Drawable } from '../../drawables/drawable';
|
||||
import { RuntimeSettings } from './runtime-settings';
|
||||
|
||||
export interface Renderer {
|
||||
readonly canvasSize: vec2;
|
||||
readonly viewAreaSize: vec2;
|
||||
readonly insights: any;
|
||||
|
||||
setViewArea(topLeft: vec2, size: vec2): void;
|
||||
setCursorPosition(position: vec2): void;
|
||||
setRuntimeSettings(overrides: Partial<RuntimeSettings>): void;
|
||||
addDrawable(drawable: Drawable): void;
|
||||
autoscaleQuality(deltaTime: DOMHighResTimeStamp): void;
|
||||
renderDrawables(): void;
|
||||
|
|
|
|||
|
|
@ -5,21 +5,20 @@ import { FrameBuffer } from '../graphics-library/frame-buffer/frame-buffer';
|
|||
import { UniformArrayAutoScalingProgram } from '../graphics-library/program/uniform-array-autoscaling-program';
|
||||
|
||||
export class RenderingPass {
|
||||
public tileMultiplier = 8;
|
||||
public isWorldInverted = false;
|
||||
|
||||
private drawables: Array<Drawable> = [];
|
||||
private program: UniformArrayAutoScalingProgram;
|
||||
|
||||
constructor(
|
||||
gl: WebGL2RenderingContext,
|
||||
private frame: FrameBuffer,
|
||||
private tileMultiplier: number
|
||||
) {
|
||||
constructor(gl: WebGL2RenderingContext, private frame: FrameBuffer) {
|
||||
this.program = new UniformArrayAutoScalingProgram(gl);
|
||||
}
|
||||
|
||||
public async initialize(
|
||||
shaderSources: [string, string],
|
||||
descriptors: Array<DrawableDescriptor>,
|
||||
substitutions: { [name: string]: any }
|
||||
substitutions: { [name: string]: any } = {}
|
||||
): Promise<void> {
|
||||
await this.program.initialize(shaderSources, descriptors, substitutions);
|
||||
}
|
||||
|
|
@ -43,7 +42,10 @@ export class RenderingPass {
|
|||
|
||||
for (let x = -1; x < 1; x += 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);
|
||||
|
||||
|
|
|
|||
8
src/graphics/rendering/runtime-settings.ts
Normal file
8
src/graphics/rendering/runtime-settings.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
import { vec3 } from 'gl-matrix';
|
||||
|
||||
export interface RuntimeSettings {
|
||||
enableHighDpiRendering: boolean;
|
||||
tileMultiplier: number;
|
||||
isWorldInverted: boolean;
|
||||
ambientLight: vec3;
|
||||
}
|
||||
|
|
@ -4,7 +4,6 @@ precision lowp float;
|
|||
|
||||
#define INFINITY 1000.0
|
||||
#define INTENSITY_INSIDE_RATIO 0.5
|
||||
#define AMBIENT_LIGHT vec3(0.25, 0.15, 0.25)
|
||||
#define SHADOW_HARDNESS 150.0
|
||||
#define HARD_SHADOW_TRACE_COUNT {hardShadowTraceCount}
|
||||
#define SOFT_SHADOW_TRACE_COUNT {softShadowTraceCount}
|
||||
|
|
@ -19,6 +18,8 @@ uniform sampler2D distanceTexture;
|
|||
in vec2 position;
|
||||
in vec2 uvCoordinates;
|
||||
|
||||
uniform vec3 ambientLight;
|
||||
|
||||
vec3[3] colors = vec3[](
|
||||
{palette}
|
||||
);
|
||||
|
|
@ -127,7 +128,7 @@ float shadowTransparency(float startingDistance, float lightCenterDistance, vec2
|
|||
|
||||
out vec4 fragmentColor;
|
||||
void main() {
|
||||
vec3 lighting = AMBIENT_LIGHT;
|
||||
vec3 lighting = ambientLight;
|
||||
|
||||
vec3 colorAtPosition;
|
||||
float startingDistance = getDistance(uvCoordinates, colorAtPosition);
|
||||
|
|
|
|||
5
src/graphics/rendering/startup-settings.ts
Normal file
5
src/graphics/rendering/startup-settings.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
export interface StartupSettings {
|
||||
enableStopwatch: boolean;
|
||||
softShadowTraceCount: string;
|
||||
hardShadowTraceCount: string;
|
||||
}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
import { mat2d, vec2 } from 'gl-matrix';
|
||||
import { mat2d, vec2, vec3 } from 'gl-matrix';
|
||||
|
||||
export class UniformsProvider {
|
||||
public ambientLight = vec3.fromValues(0.25, 0.15, 0.25);
|
||||
|
||||
private scaleWorldLengthToNDC = 1;
|
||||
private transformWorldToNDC = mat2d.create();
|
||||
|
||||
|
|
@ -8,17 +10,15 @@ export class UniformsProvider {
|
|||
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,
|
||||
ambientLight: this.ambientLight,
|
||||
uvToWorld: this.uvToWorld,
|
||||
worldAreaInView: this.worldAreaInView,
|
||||
squareToAspectRatio: this.squareToAspectRatio,
|
||||
|
|
@ -51,6 +51,10 @@ export class UniformsProvider {
|
|||
);
|
||||
}
|
||||
|
||||
public getViewArea(): vec2 {
|
||||
return this.worldAreaInView;
|
||||
}
|
||||
|
||||
public setViewArea(topLeft: vec2, size: vec2) {
|
||||
this.worldAreaInView = size;
|
||||
|
||||
|
|
@ -87,8 +91,4 @@ export class UniformsProvider {
|
|||
this.uvToWorld = mat2d.fromTranslation(mat2d.create(), this.viewAreaBottomLeft);
|
||||
mat2d.scale(this.uvToWorld, this.uvToWorld, this.worldAreaInView);
|
||||
}
|
||||
|
||||
public setCursorPosition(position: vec2): void {
|
||||
this.cursorPosition = position;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
export interface WebGl2RendererSettings {
|
||||
enableHighDpiRendering: boolean;
|
||||
enableStopwatch: boolean;
|
||||
tileMultiplier: number;
|
||||
shaderMacros: {
|
||||
softShadowTraceCount: string;
|
||||
hardShadowTraceCount: string;
|
||||
// ambientColor: vec3;
|
||||
// defaultIsInside: boolean;
|
||||
};
|
||||
}
|
||||
|
|
@ -11,12 +11,13 @@ import { Insights } from './insights';
|
|||
import { Renderer } from './renderer';
|
||||
import { RenderingPass } from './rendering-pass';
|
||||
import { RenderingPassName } from './rendering-pass-name';
|
||||
import { RuntimeSettings } from './runtime-settings';
|
||||
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 { StartupSettings } from './startup-settings';
|
||||
import { UniformsProvider } from './uniforms-provider';
|
||||
import { WebGl2RendererSettings } from './webgl2-renderer-settings';
|
||||
|
||||
type Passes = { [key in keyof typeof RenderingPassName]: RenderingPass };
|
||||
|
||||
|
|
@ -28,38 +29,49 @@ export class WebGl2Renderer implements Renderer {
|
|||
private lightingFrameBuffer: DefaultFrameBuffer;
|
||||
private passes: Passes;
|
||||
private autoscaler: FpsAutoscaler;
|
||||
private settings: WebGl2RendererSettings;
|
||||
|
||||
private static defaultSettings: WebGl2RendererSettings = {
|
||||
enableHighDpiRendering: true,
|
||||
enableStopwatch: true,
|
||||
tileMultiplier: 8,
|
||||
shaderMacros: {
|
||||
softShadowTraceCount: '128',
|
||||
hardShadowTraceCount: '32',
|
||||
private applyRuntimeSettings: {
|
||||
[key: string]: (value: any) => void;
|
||||
} = {
|
||||
enableHighDpiRendering: (v) => {
|
||||
this.distanceFieldFrameBuffer.enableHighDpiRendering = v;
|
||||
this.lightingFrameBuffer.enableHighDpiRendering = v;
|
||||
},
|
||||
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(
|
||||
private canvas: HTMLCanvasElement,
|
||||
private descriptors: Array<DrawableDescriptor>,
|
||||
private palette: Array<vec3>,
|
||||
settingsOverrides: Partial<WebGl2RendererSettings>
|
||||
private descriptors: Array<DrawableDescriptor>
|
||||
) {
|
||||
this.settings = { ...WebGl2Renderer.defaultSettings, ...settingsOverrides };
|
||||
|
||||
this.gl = getWebGl2Context(canvas);
|
||||
|
||||
ParallelCompiler.initialize(this.gl);
|
||||
|
||||
this.distanceFieldFrameBuffer = new IntermediateFrameBuffer(
|
||||
this.gl,
|
||||
this.settings.enableHighDpiRendering
|
||||
);
|
||||
this.lightingFrameBuffer = new DefaultFrameBuffer(
|
||||
this.gl,
|
||||
this.settings.enableHighDpiRendering
|
||||
);
|
||||
this.distanceFieldFrameBuffer = new IntermediateFrameBuffer(this.gl);
|
||||
this.lightingFrameBuffer = new DefaultFrameBuffer(this.gl);
|
||||
|
||||
this.passes = this.createPasses();
|
||||
|
||||
|
|
@ -72,14 +84,6 @@ export class WebGl2Renderer implements Renderer {
|
|||
softShadowsEnabled: (v) =>
|
||||
(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')
|
||||
|
|
@ -87,26 +91,25 @@ export class WebGl2Renderer implements Renderer {
|
|||
return {
|
||||
[RenderingPassName.distance]: new RenderingPass(
|
||||
this.gl,
|
||||
this.distanceFieldFrameBuffer,
|
||||
this.settings.tileMultiplier
|
||||
),
|
||||
[RenderingPassName.pixel]: new RenderingPass(
|
||||
this.gl,
|
||||
this.lightingFrameBuffer,
|
||||
this.settings.tileMultiplier
|
||||
this.distanceFieldFrameBuffer
|
||||
),
|
||||
[RenderingPassName.pixel]: new RenderingPass(this.gl, this.lightingFrameBuffer),
|
||||
};
|
||||
}
|
||||
|
||||
@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>> = [];
|
||||
|
||||
promises.push(
|
||||
this.passes[RenderingPassName.distance].initialize(
|
||||
[distanceVertexShader, distanceFragmentShader],
|
||||
this.descriptors.filter(WebGl2Renderer.hasSdf),
|
||||
this.settings.shaderMacros
|
||||
this.descriptors.filter(WebGl2Renderer.hasSdf)
|
||||
)
|
||||
);
|
||||
|
||||
|
|
@ -115,8 +118,9 @@ export class WebGl2Renderer implements Renderer {
|
|||
[lightsVertexShader, lightsFragmentShader],
|
||||
this.descriptors.filter((d) => !WebGl2Renderer.hasSdf(d)),
|
||||
{
|
||||
palette: this.generatePaletteCode(this.palette),
|
||||
...this.settings.shaderMacros,
|
||||
palette: this.generatePaletteCode(palette),
|
||||
hardShadowTraceCount: settings.hardShadowTraceCount,
|
||||
softShadowTraceCount: settings.softShadowTraceCount,
|
||||
}
|
||||
)
|
||||
);
|
||||
|
|
@ -124,6 +128,14 @@ export class WebGl2Renderer implements Renderer {
|
|||
ParallelCompiler.compilePrograms();
|
||||
|
||||
await Promise.all(promises);
|
||||
|
||||
if (settings.enableStopwatch) {
|
||||
try {
|
||||
this.stopwatch = new WebGlStopwatch(this.gl);
|
||||
} catch {
|
||||
// no problem
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public get insights(): any {
|
||||
|
|
@ -191,8 +203,8 @@ export class WebGl2Renderer implements Renderer {
|
|||
this.uniformsProvider.setViewArea(topLeft, size);
|
||||
}
|
||||
|
||||
public setCursorPosition(position: vec2) {
|
||||
this.uniformsProvider.setCursorPosition(position);
|
||||
public get viewAreaSize(): vec2 {
|
||||
return this.uniformsProvider.getViewArea();
|
||||
}
|
||||
|
||||
public get canvasSize(): vec2 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
import { vec3 } from 'gl-matrix';
|
||||
import { DrawableDescriptor } from './drawables/drawable-descriptor';
|
||||
import { Renderer } from './graphics/rendering/renderer';
|
||||
import { StartupSettings } from './graphics/rendering/startup-settings';
|
||||
import { WebGl2Renderer } from './graphics/rendering/webgl2-renderer';
|
||||
import { WebGl2RendererSettings } from './graphics/rendering/webgl2-renderer-settings';
|
||||
import { applyArrayPlugins } from './helper/array';
|
||||
|
||||
export { Drawable } from './drawables/drawable';
|
||||
|
|
@ -30,11 +30,11 @@ export const compile = async (
|
|||
canvas: HTMLCanvasElement,
|
||||
descriptors: Array<DrawableDescriptor>,
|
||||
palette: Array<vec3>,
|
||||
settings: Partial<WebGl2RendererSettings> = {}
|
||||
settings: Partial<StartupSettings> = {}
|
||||
): Promise<Renderer> => {
|
||||
applyArrayPlugins();
|
||||
|
||||
const renderer = new WebGl2Renderer(canvas, descriptors, palette, settings);
|
||||
await renderer.initialize();
|
||||
const renderer = new WebGl2Renderer(canvas, descriptors);
|
||||
await renderer.initialize(palette, settings);
|
||||
return renderer;
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue