Refactor rendering
This commit is contained in:
parent
9b47d56d8f
commit
c892ca2d01
38 changed files with 511 additions and 429 deletions
|
|
@ -13,11 +13,12 @@ A good-looking 2D adventure.
|
||||||
|
|
||||||
## Todo
|
## Todo
|
||||||
|
|
||||||
- host script
|
|
||||||
- https://github.com/ottomatica/slim
|
|
||||||
- rancher os
|
|
||||||
- Frontend nginx disable logging
|
- Frontend nginx disable logging
|
||||||
- procedural piano
|
- procedural piano
|
||||||
|
- subtract cameraPosition
|
||||||
|
- cross browser testing
|
||||||
|
- nginx log monitor
|
||||||
|
- pass befejezése
|
||||||
- lightweight object storage
|
- lightweight object storage
|
||||||
- local dev env
|
- local dev env
|
||||||
- CI pipeline:
|
- CI pipeline:
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { Command } from '../command';
|
import { Command } from '../command';
|
||||||
import { IRenderer } from '../../drawing/rendering/i-renderer';
|
import { IRenderer } from '../../drawing/i-renderer';
|
||||||
|
|
||||||
export class BeforeRenderCommand extends Command {
|
export class BeforeRenderCommand extends Command {
|
||||||
public constructor(public readonly renderer?: IRenderer) {
|
public constructor(public readonly renderer?: IRenderer) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { Command } from '../command';
|
import { Command } from '../command';
|
||||||
import { IRenderer } from '../../drawing/rendering/i-renderer';
|
import { IRenderer } from '../../drawing/i-renderer';
|
||||||
|
|
||||||
export class RenderCommand extends Command {
|
export class RenderCommand extends Command {
|
||||||
public constructor(public readonly renderer?: IRenderer) {
|
public constructor(public readonly renderer?: IRenderer) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
export interface IDrawableDescriptor {
|
||||||
|
uniformName: string;
|
||||||
|
countMacroName: string;
|
||||||
|
shaderCombinationSteps: Array<number>;
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { vec2 } from 'gl-matrix';
|
import { vec2 } from 'gl-matrix';
|
||||||
|
|
||||||
export interface IPrimitive {
|
export interface IDrawable {
|
||||||
serializeToUniforms(uniforms: any): void;
|
serializeToUniforms(uniforms: any): void;
|
||||||
distance(target: vec2): number;
|
distance(target: vec2): number;
|
||||||
minimumDistance(target: vec2): number;
|
minimumDistance(target: vec2): number;
|
||||||
|
|
@ -1,8 +1,14 @@
|
||||||
import { ILight } from './i-light';
|
import { ILight } from './i-light';
|
||||||
import { vec2, vec3 } from 'gl-matrix';
|
import { vec2, vec3 } from 'gl-matrix';
|
||||||
|
import { settings } from '../../settings';
|
||||||
|
import { IDrawableDescriptor } from '../i-drawable-descriptor';
|
||||||
|
|
||||||
export class CircleLight implements ILight {
|
export class CircleLight implements ILight {
|
||||||
public static uniformName = 'lights';
|
public static descriptor: IDrawableDescriptor = {
|
||||||
|
uniformName: 'lights',
|
||||||
|
countMacroName: 'lightCount',
|
||||||
|
shaderCombinationSteps: settings.shaderCombinations.circleLightSteps,
|
||||||
|
};
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public center: vec2,
|
public center: vec2,
|
||||||
|
|
@ -11,8 +17,16 @@ export class CircleLight implements ILight {
|
||||||
public lightness: number
|
public lightness: number
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
distance(target: vec2): number {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
minimumDistance(target: vec2): number {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
serializeToUniforms(uniforms: any): void {
|
serializeToUniforms(uniforms: any): void {
|
||||||
const listName = CircleLight.uniformName;
|
const listName = CircleLight.descriptor.uniformName;
|
||||||
|
|
||||||
if (!uniforms.hasOwnProperty(listName)) {
|
if (!uniforms.hasOwnProperty(listName)) {
|
||||||
uniforms[listName] = [];
|
uniforms[listName] = [];
|
||||||
3
frontend/src/scripts/drawing/drawables/lights/i-light.ts
Normal file
3
frontend/src/scripts/drawing/drawables/lights/i-light.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
import { IDrawable } from '../i-drawable';
|
||||||
|
|
||||||
|
export interface ILight extends IDrawable {}
|
||||||
|
|
@ -9,6 +9,12 @@ export class PointLight implements ILight {
|
||||||
public color: vec3,
|
public color: vec3,
|
||||||
public lightness: number
|
public lightness: number
|
||||||
) {}
|
) {}
|
||||||
|
distance(target: vec2): number {
|
||||||
|
throw new Error('Method not implemented.');
|
||||||
|
}
|
||||||
|
minimumDistance(target: vec2): number {
|
||||||
|
throw new Error('Method not implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
serializeToUniforms(uniforms: any): void {
|
serializeToUniforms(uniforms: any): void {
|
||||||
const listName = PointLight.uniformName;
|
const listName = PointLight.uniformName;
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
import { IDrawable } from '../i-drawable';
|
||||||
|
|
||||||
|
export interface IPrimitive extends IDrawable {}
|
||||||
|
|
@ -1,12 +1,17 @@
|
||||||
import { vec2 } from 'gl-matrix';
|
import { vec2 } from 'gl-matrix';
|
||||||
import { clamp01 } from '../../helper/clamp';
|
import { clamp01 } from '../../../helper/clamp';
|
||||||
import { mix } from '../../helper/mix';
|
import { mix } from '../../../helper/mix';
|
||||||
import { Circle } from './circle';
|
import { Circle } from './circle';
|
||||||
import { IPrimitive } from './i-primitive';
|
import { IPrimitive } from './i-primitive';
|
||||||
|
import { IDrawableDescriptor } from '../i-drawable-descriptor';
|
||||||
|
import { settings } from '../../settings';
|
||||||
|
|
||||||
export class TunnelShape implements IPrimitive {
|
export class TunnelShape implements IPrimitive {
|
||||||
public static uniformNameLines = 'lines';
|
public static descriptor: IDrawableDescriptor = {
|
||||||
public static uniformNameRadii = 'radii';
|
uniformName: 'lines',
|
||||||
|
countMacroName: 'lineCount',
|
||||||
|
shaderCombinationSteps: settings.shaderCombinations.lineSteps,
|
||||||
|
};
|
||||||
|
|
||||||
public readonly toFromDelta: vec2;
|
public readonly toFromDelta: vec2;
|
||||||
private toFromDeltaLength: number;
|
private toFromDeltaLength: number;
|
||||||
|
|
@ -29,18 +34,16 @@ export class TunnelShape implements IPrimitive {
|
||||||
}
|
}
|
||||||
|
|
||||||
serializeToUniforms(uniforms: any): void {
|
serializeToUniforms(uniforms: any): void {
|
||||||
if (!uniforms.hasOwnProperty(TunnelShape.uniformNameLines)) {
|
if (!uniforms.hasOwnProperty(TunnelShape.descriptor.uniformName)) {
|
||||||
uniforms[TunnelShape.uniformNameLines] = [];
|
uniforms[TunnelShape.descriptor.uniformName] = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!uniforms.hasOwnProperty(TunnelShape.uniformNameRadii)) {
|
uniforms[TunnelShape.descriptor.uniformName].push({
|
||||||
uniforms[TunnelShape.uniformNameRadii] = [];
|
from: this.from,
|
||||||
}
|
toFromDelta: this.toFromDelta,
|
||||||
|
fromRadius: this.fromRadius,
|
||||||
uniforms[TunnelShape.uniformNameLines].push(this.from);
|
toRadius: this.toRadius,
|
||||||
uniforms[TunnelShape.uniformNameLines].push(this.toFromDelta);
|
});
|
||||||
uniforms[TunnelShape.uniformNameRadii].push(this.fromRadius);
|
|
||||||
uniforms[TunnelShape.uniformNameRadii].push(this.toRadius);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public distance(target: vec2): number {
|
public distance(target: vec2): number {
|
||||||
|
|
@ -1,15 +1,16 @@
|
||||||
|
import { settings } from '../../settings';
|
||||||
|
|
||||||
export const createShader = (
|
export const createShader = (
|
||||||
gl: WebGL2RenderingContext,
|
gl: WebGL2RenderingContext,
|
||||||
type: GLenum,
|
type: GLenum,
|
||||||
source: string,
|
source: string,
|
||||||
substitutions: { [name: string]: string }
|
substitutions: { [name: string]: string }
|
||||||
): WebGLShader => {
|
): WebGLShader => {
|
||||||
|
const allSubstitutions = { ...settings.shaderMacros, ...substitutions };
|
||||||
|
|
||||||
source = source.replace(/{(.+)}/gm, (_, name: string): string => {
|
source = source.replace(/{(.+)}/gm, (_, name: string): string => {
|
||||||
const value = substitutions[name];
|
const value = allSubstitutions[name];
|
||||||
if (Number.isInteger(value)) {
|
return Number.isInteger(value) ? `${value}.0` : value;
|
||||||
return `${value}.0`;
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const shader = gl.createShader(type);
|
const shader = gl.createShader(type);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
export const getWebGl2Context = (
|
||||||
|
canvas: HTMLCanvasElement
|
||||||
|
): WebGL2RenderingContext => {
|
||||||
|
const gl = canvas.getContext('webgl2');
|
||||||
|
|
||||||
|
if (!gl) {
|
||||||
|
throw new Error('WebGl2 is not supported');
|
||||||
|
}
|
||||||
|
|
||||||
|
return gl;
|
||||||
|
};
|
||||||
|
|
@ -5,11 +5,10 @@ export class FragmentShaderOnlyProgram extends Program {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
gl: WebGL2RenderingContext,
|
gl: WebGL2RenderingContext,
|
||||||
vertexShaderSource: string,
|
shaderSources: [string, string],
|
||||||
fragmentShaderSource: string,
|
|
||||||
substitutions: { [name: string]: string }
|
substitutions: { [name: string]: string }
|
||||||
) {
|
) {
|
||||||
super(gl, vertexShaderSource, fragmentShaderSource, substitutions);
|
super(gl, shaderSources, substitutions);
|
||||||
this.prepareScreenQuad('vertexPosition');
|
this.prepareScreenQuad('vertexPosition');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { vec2 } from 'gl-matrix';
|
import { vec2 } from 'gl-matrix';
|
||||||
|
|
||||||
export interface IProgram {
|
export interface IProgram {
|
||||||
bindAndSetUniforms(values: { [name: string]: any }): void;
|
|
||||||
setDrawingRectangle(topLeft: vec2, size: vec2): void;
|
setDrawingRectangle(topLeft: vec2, size: vec2): void;
|
||||||
|
bindAndSetUniforms(values: { [name: string]: any }): void;
|
||||||
draw(): void;
|
draw(): void;
|
||||||
delete(): void;
|
delete(): void;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,7 @@ export abstract class Program implements IProgram {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected gl: WebGL2RenderingContext,
|
protected gl: WebGL2RenderingContext,
|
||||||
vertexShaderSource: string,
|
[vertexShaderSource, fragmentShaderSource]: [string, string],
|
||||||
fragmentShaderSource: string,
|
|
||||||
substitutions: { [name: string]: string }
|
substitutions: { [name: string]: string }
|
||||||
) {
|
) {
|
||||||
this.createProgram(vertexShaderSource, fragmentShaderSource, substitutions);
|
this.createProgram(vertexShaderSource, fragmentShaderSource, substitutions);
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
import { vec2 } from 'gl-matrix';
|
import { vec2 } from 'gl-matrix';
|
||||||
import { FragmentShaderOnlyProgram } from './fragment-shader-only-program';
|
import { FragmentShaderOnlyProgram } from './fragment-shader-only-program';
|
||||||
import { IProgram } from './i-program';
|
import { IProgram } from './i-program';
|
||||||
|
import { last } from '../../../helper/last';
|
||||||
|
import { getCombinations } from '../../../helper/get-combinations';
|
||||||
|
import { IDrawableDescriptor } from '../../drawables/i-drawable-descriptor';
|
||||||
|
|
||||||
export class UniformArrayAutoScalingProgram implements IProgram {
|
export class UniformArrayAutoScalingProgram implements IProgram {
|
||||||
private programs: Array<{
|
private programs: Array<{
|
||||||
program: FragmentShaderOnlyProgram;
|
program: FragmentShaderOnlyProgram;
|
||||||
value: number;
|
values: Array<number>;
|
||||||
}> = [];
|
}> = [];
|
||||||
private current: FragmentShaderOnlyProgram;
|
private current: FragmentShaderOnlyProgram;
|
||||||
|
|
||||||
|
|
@ -14,45 +17,33 @@ export class UniformArrayAutoScalingProgram implements IProgram {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private gl: WebGL2RenderingContext,
|
private gl: WebGL2RenderingContext,
|
||||||
private vertexShaderSource: string,
|
shaderSources: [string, string],
|
||||||
private fragmentShaderSource: string,
|
private options: Array<IDrawableDescriptor>
|
||||||
private substitutions: { [name: string]: any },
|
|
||||||
private options: {
|
|
||||||
getValueFromUniforms: (values: { [name: string]: any }) => number;
|
|
||||||
uniformArraySizeName: string;
|
|
||||||
enablingMacro: string;
|
|
||||||
startingValue: number;
|
|
||||||
steps: number;
|
|
||||||
maximumValue: number;
|
|
||||||
}
|
|
||||||
) {
|
) {
|
||||||
for (
|
const names = options.map((o) => o.countMacroName);
|
||||||
let i = options.startingValue;
|
for (let combination of getCombinations(
|
||||||
i < options.maximumValue;
|
options.map((o) => o.shaderCombinationSteps)
|
||||||
i += options.steps
|
)) {
|
||||||
) {
|
this.createProgram(names, combination, shaderSources);
|
||||||
this.createProgram(i);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bindAndSetUniforms(values: { [name: string]: any }): void {
|
public bindAndSetUniforms(uniforms: { [name: string]: any }): void {
|
||||||
let value = this.options.getValueFromUniforms(values);
|
let values = this.options.map((o) =>
|
||||||
value = Math.min(this.options.maximumValue, value);
|
uniforms[o.uniformName] ? uniforms[o.uniformName].length : 0
|
||||||
|
|
||||||
const closest = this.programs.find(
|
|
||||||
(p) => value < p.value && value + this.options.steps >= p.value
|
|
||||||
);
|
);
|
||||||
if (closest) {
|
|
||||||
this.current = closest.program;
|
const closest = this.programs.find((p) =>
|
||||||
} else {
|
p.values.every((v, i) => v > values[i])
|
||||||
this.current = this.createProgram(value + this.options.steps);
|
);
|
||||||
}
|
|
||||||
|
this.current = closest ? closest.program : last(this.programs).program;
|
||||||
|
|
||||||
this.current.setDrawingRectangle(
|
this.current.setDrawingRectangle(
|
||||||
this.drawingRectangleTopLeft,
|
this.drawingRectangleTopLeft,
|
||||||
this.drawingRectangleSize
|
this.drawingRectangleSize
|
||||||
);
|
);
|
||||||
this.current.bindAndSetUniforms(values);
|
this.current.bindAndSetUniforms(uniforms);
|
||||||
}
|
}
|
||||||
|
|
||||||
public setDrawingRectangle(topLeft: vec2, size: vec2) {
|
public setDrawingRectangle(topLeft: vec2, size: vec2) {
|
||||||
|
|
@ -68,21 +59,23 @@ export class UniformArrayAutoScalingProgram implements IProgram {
|
||||||
this.programs.forEach((p) => p.program.delete());
|
this.programs.forEach((p) => p.program.delete());
|
||||||
}
|
}
|
||||||
|
|
||||||
private createProgram(arraySize: number): FragmentShaderOnlyProgram {
|
private createProgram(
|
||||||
|
names: Array<string>,
|
||||||
|
combination: Array<number>,
|
||||||
|
shaderSources: [string, string]
|
||||||
|
): FragmentShaderOnlyProgram {
|
||||||
|
const substitutions = {};
|
||||||
|
names.forEach((v, i) => (substitutions[v] = combination[i].toString()));
|
||||||
|
|
||||||
const program = new FragmentShaderOnlyProgram(
|
const program = new FragmentShaderOnlyProgram(
|
||||||
this.gl,
|
this.gl,
|
||||||
this.vertexShaderSource,
|
shaderSources,
|
||||||
this.fragmentShaderSource,
|
substitutions
|
||||||
{
|
|
||||||
[this.options.uniformArraySizeName]: Math.floor(arraySize).toString(),
|
|
||||||
[this.options.enablingMacro]: arraySize > 0 ? '1' : '0',
|
|
||||||
...this.substitutions,
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
this.programs.push({
|
this.programs.push({
|
||||||
program,
|
program,
|
||||||
value: arraySize,
|
values: combination,
|
||||||
});
|
});
|
||||||
|
|
||||||
return program;
|
return program;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { vec2 } from 'gl-matrix';
|
import { vec2 } from 'gl-matrix';
|
||||||
import { IPrimitive } from '../primitives/i-primitive';
|
import { IPrimitive } from './drawables/primitives/i-primitive';
|
||||||
import { ILight } from '../lights/i-light';
|
import { ILight } from './drawables/lights/i-light';
|
||||||
|
|
||||||
export interface IRenderer {
|
export interface IRenderer {
|
||||||
startFrame(deltaTime: DOMHighResTimeStamp): void;
|
startFrame(deltaTime: DOMHighResTimeStamp): void;
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
export interface ILight {
|
|
||||||
serializeToUniforms(uniforms: any): void;
|
|
||||||
}
|
|
||||||
54
frontend/src/scripts/drawing/rendering/fps-autoscaler.ts
Normal file
54
frontend/src/scripts/drawing/rendering/fps-autoscaler.ts
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
import { Autoscaler } from '../../helper/autoscaler';
|
||||||
|
import { settings } from '../settings';
|
||||||
|
import { exponentialDecay } from '../../helper/exponential-decay';
|
||||||
|
import { InfoText } from '../../objects/types/info-text';
|
||||||
|
import { FrameBuffer } from '../graphics-library/frame-buffer/frame-buffer';
|
||||||
|
import { toPercent } from '../../helper/to-percent';
|
||||||
|
|
||||||
|
export class FpsAutoscaler extends Autoscaler {
|
||||||
|
private timeSinceLastAdjusment = 0;
|
||||||
|
private exponentialDecayedDeltaTime = 0.0;
|
||||||
|
|
||||||
|
constructor(private frameBuffers: Array<FrameBuffer>) {
|
||||||
|
super(
|
||||||
|
frameBuffers.map((f) => (v) => (f.renderScale = v)),
|
||||||
|
settings.qualityScaling.scaleTargets,
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InfoText.modifyRecord(
|
||||||
|
'quality',
|
||||||
|
this.frameBuffers.map((f) => toPercent(f.renderScale)).join(', ')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,105 @@
|
||||||
import { IProgram } from '../graphics-library/program/i-program';
|
|
||||||
import { FrameBuffer } from '../graphics-library/frame-buffer/frame-buffer';
|
import { FrameBuffer } from '../graphics-library/frame-buffer/frame-buffer';
|
||||||
|
import { IDrawable } from '../drawables/i-drawable';
|
||||||
|
import { settings } from '../settings';
|
||||||
|
import { vec2 } from 'gl-matrix';
|
||||||
|
import { Circle } from '../drawables/primitives/circle';
|
||||||
|
import { InfoText } from '../../objects/types/info-text';
|
||||||
|
import { UniformArrayAutoScalingProgram } from '../graphics-library/program/uniform-array-autoscaling-program';
|
||||||
|
import { IDrawableDescriptor } from '../drawables/i-drawable-descriptor';
|
||||||
|
|
||||||
export class RenderingPass {
|
export class RenderingPass {
|
||||||
constructor(private program: IProgram, private frame: FrameBuffer) {}
|
private drawables: Array<IDrawable> = [];
|
||||||
|
private program: UniformArrayAutoScalingProgram;
|
||||||
|
|
||||||
public render(uniforms: any, inputTexture?: WebGLTexture) {}
|
constructor(
|
||||||
|
gl: WebGL2RenderingContext,
|
||||||
|
shaderSources: [string, string],
|
||||||
|
private drawableDescriptors: Array<IDrawableDescriptor>,
|
||||||
|
private frame: FrameBuffer
|
||||||
|
) {
|
||||||
|
this.program = new UniformArrayAutoScalingProgram(
|
||||||
|
gl,
|
||||||
|
shaderSources,
|
||||||
|
drawableDescriptors
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public addDrawable(drawable: IDrawable) {
|
||||||
|
this.drawables.push(drawable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public render(
|
||||||
|
commonUniforms: any,
|
||||||
|
viewCircle: Circle,
|
||||||
|
inputTexture?: WebGLTexture
|
||||||
|
) {
|
||||||
|
this.frame.bindAndClear(inputTexture);
|
||||||
|
const q = 1 / settings.tileMultiplier;
|
||||||
|
const tileUvSize = vec2.fromValues(q, q);
|
||||||
|
|
||||||
|
const possiblyOnScreenDrawables = this.drawables.filter(
|
||||||
|
(p) => p.minimumDistance(viewCircle.center) < viewCircle.radius
|
||||||
|
);
|
||||||
|
|
||||||
|
const origin = vec2.transformMat2d(
|
||||||
|
vec2.create(),
|
||||||
|
vec2.fromValues(0, 0),
|
||||||
|
commonUniforms.uvToWorld
|
||||||
|
);
|
||||||
|
|
||||||
|
const firstCenter = vec2.transformMat2d(
|
||||||
|
vec2.create(),
|
||||||
|
vec2.fromValues(q, q),
|
||||||
|
commonUniforms.uvToWorld
|
||||||
|
);
|
||||||
|
|
||||||
|
vec2.subtract(firstCenter, firstCenter, origin);
|
||||||
|
|
||||||
|
const worldR = vec2.length(firstCenter);
|
||||||
|
|
||||||
|
let sumLineCount = 0;
|
||||||
|
|
||||||
|
for (let x = 0; x < 1; x += q) {
|
||||||
|
for (let y = 0; y < 1; y += q) {
|
||||||
|
const uniforms = { ...commonUniforms };
|
||||||
|
uniforms.maxMinDistance = 2 * worldR;
|
||||||
|
|
||||||
|
const uvBottomLeft = vec2.fromValues(x, y);
|
||||||
|
this.program.setDrawingRectangle(uvBottomLeft, tileUvSize);
|
||||||
|
|
||||||
|
const tileCenterWorldCoordinates = vec2.transformMat2d(
|
||||||
|
vec2.create(),
|
||||||
|
vec2.add(vec2.create(), uvBottomLeft, vec2.fromValues(q, q)),
|
||||||
|
uniforms.uvToWorld
|
||||||
|
);
|
||||||
|
|
||||||
|
const primitivesNearTile = possiblyOnScreenDrawables.filter(
|
||||||
|
(p) => p.distance(tileCenterWorldCoordinates) < 2 * worldR
|
||||||
|
);
|
||||||
|
|
||||||
|
sumLineCount += primitivesNearTile.length;
|
||||||
|
|
||||||
|
primitivesNearTile.forEach((p) => p.serializeToUniforms(uniforms));
|
||||||
|
|
||||||
|
this.program.bindAndSetUniforms(uniforms);
|
||||||
|
this.program.draw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.drawables = [];
|
||||||
|
|
||||||
|
InfoText.modifyRecord(
|
||||||
|
'nearby ' + this.drawableDescriptors[0].countMacroName,
|
||||||
|
possiblyOnScreenDrawables.length.toFixed(2)
|
||||||
|
);
|
||||||
|
|
||||||
|
InfoText.modifyRecord(
|
||||||
|
'drawn ' + this.drawableDescriptors[0].countMacroName,
|
||||||
|
(
|
||||||
|
sumLineCount /
|
||||||
|
settings.tileMultiplier /
|
||||||
|
settings.tileMultiplier
|
||||||
|
).toFixed(2)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,28 @@
|
||||||
import { mat2d, vec2 } from 'gl-matrix';
|
import { mat2d, vec2 } from 'gl-matrix';
|
||||||
import { InfoText } from '../../objects/types/info-text';
|
import caveFragmentShader from '../shaders/cave-distance-fs.glsl';
|
||||||
|
import lightsFragmentShader from '../shaders/lights-shading-fs.glsl';
|
||||||
|
import caveVertexShader from '../shaders/passthrough-distance-vs.glsl';
|
||||||
|
import lightsVertexShader from '../shaders/passthrough-shading-vs.glsl';
|
||||||
|
// import lightsShader from '../shaders/rainbow-shading-fs.glsl';
|
||||||
import { DefaultFrameBuffer } from '../graphics-library/frame-buffer/default-frame-buffer';
|
import { DefaultFrameBuffer } from '../graphics-library/frame-buffer/default-frame-buffer';
|
||||||
import { IntermediateFrameBuffer } from '../graphics-library/frame-buffer/intermediate-frame-buffer';
|
import { IntermediateFrameBuffer } from '../graphics-library/frame-buffer/intermediate-frame-buffer';
|
||||||
import { WebGlStopwatch } from '../graphics-library/helper/stopwatch';
|
import { WebGlStopwatch } from '../graphics-library/helper/stopwatch';
|
||||||
import { IProgram } from '../graphics-library/program/i-program';
|
import { IProgram } from '../graphics-library/program/i-program';
|
||||||
import { UniformArrayAutoScalingProgram } from '../graphics-library/program/uniform-array-autoscaling-program';
|
import { UniformArrayAutoScalingProgram } from '../graphics-library/program/uniform-array-autoscaling-program';
|
||||||
import { IRenderer } from './i-renderer';
|
import { IRenderer } from '../i-renderer';
|
||||||
import { Circle } from '../primitives/circle';
|
import { Circle } from '../drawables/primitives/circle';
|
||||||
import { IPrimitive } from '../primitives/i-primitive';
|
import { IPrimitive } from '../drawables/primitives/i-primitive';
|
||||||
import { Rectangle } from '../primitives/rectangle';
|
import { Rectangle } from '../drawables/primitives/rectangle';
|
||||||
import { TunnelShape } from '../primitives/tunnel-shape';
|
import { ILight } from '../drawables/lights/i-light';
|
||||||
import { Autoscaler } from './autoscaler';
|
import { settings } from '../settings';
|
||||||
import { ILight } from '../lights/i-light';
|
import { FpsAutoscaler } from './fps-autoscaler';
|
||||||
import { toPercent } from '../../helper/to-percent';
|
import { getWebGl2Context } from '../graphics-library/helper/get-webgl2-context';
|
||||||
import { exponentialDecay } from '../../helper/exponential-decay';
|
import { RenderingPass } from './rendering-pass';
|
||||||
import { settings } from './settings';
|
import { TunnelShape } from '../drawables/primitives/tunnel-shape';
|
||||||
|
import { CircleLight } from '../drawables/lights/circle-light';
|
||||||
|
|
||||||
export class WebGl2Renderer implements IRenderer {
|
export class WebGl2Renderer implements IRenderer {
|
||||||
private gl: WebGL2RenderingContext;
|
private gl: WebGL2RenderingContext;
|
||||||
private qualityScaler: Autoscaler;
|
|
||||||
private stopwatch?: WebGlStopwatch;
|
private stopwatch?: WebGlStopwatch;
|
||||||
|
|
||||||
private viewBox: Rectangle = new Rectangle();
|
private viewBox: Rectangle = new Rectangle();
|
||||||
|
|
@ -28,131 +32,51 @@ export class WebGl2Renderer implements IRenderer {
|
||||||
|
|
||||||
private distanceFieldFrameBuffer: IntermediateFrameBuffer;
|
private distanceFieldFrameBuffer: IntermediateFrameBuffer;
|
||||||
private lightingFrameBuffer: DefaultFrameBuffer;
|
private lightingFrameBuffer: DefaultFrameBuffer;
|
||||||
private distanceProgram: IProgram;
|
private distancePass: RenderingPass;
|
||||||
private lightingProgram: IProgram;
|
private lightingPass: RenderingPass;
|
||||||
|
|
||||||
private primitives: Array<IPrimitive>;
|
private autoscaler: FpsAutoscaler;
|
||||||
private lights: Array<ILight>;
|
|
||||||
|
|
||||||
constructor(
|
constructor(private canvas: HTMLCanvasElement, private overlay: HTMLElement) {
|
||||||
private canvas: HTMLCanvasElement,
|
this.gl = getWebGl2Context(canvas);
|
||||||
private overlay: HTMLElement,
|
|
||||||
shaderSources: Array<[string, string]>
|
this.distanceFieldFrameBuffer = new IntermediateFrameBuffer(this.gl);
|
||||||
) {
|
this.lightingFrameBuffer = new DefaultFrameBuffer(this.gl);
|
||||||
this.getContext();
|
|
||||||
this.createPipeline(shaderSources);
|
this.distancePass = new RenderingPass(
|
||||||
this.setupAutoscaling();
|
this.gl,
|
||||||
|
[caveVertexShader, caveFragmentShader],
|
||||||
|
[TunnelShape.descriptor],
|
||||||
|
this.distanceFieldFrameBuffer
|
||||||
|
);
|
||||||
|
|
||||||
|
this.lightingPass = new RenderingPass(
|
||||||
|
this.gl,
|
||||||
|
[lightsVertexShader, lightsFragmentShader],
|
||||||
|
[CircleLight.descriptor],
|
||||||
|
this.lightingFrameBuffer
|
||||||
|
);
|
||||||
|
|
||||||
|
this.autoscaler = new FpsAutoscaler([
|
||||||
|
this.lightingFrameBuffer,
|
||||||
|
this.distanceFieldFrameBuffer,
|
||||||
|
]);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.stopwatch = new WebGlStopwatch(this.gl);
|
this.stopwatch = new WebGlStopwatch(this.gl);
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
private getContext() {
|
|
||||||
this.gl = this.canvas.getContext('webgl2');
|
|
||||||
if (!this.gl) {
|
|
||||||
throw new Error('WebGl2 is not supported');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private createPipeline(shaderSources: Array<[string, string]>) {
|
|
||||||
const distanceScale = settings.shaderUniforms.distanceScale;
|
|
||||||
|
|
||||||
this.distanceFieldFrameBuffer = new IntermediateFrameBuffer(this.gl);
|
|
||||||
this.distanceProgram = new UniformArrayAutoScalingProgram(
|
|
||||||
this.gl,
|
|
||||||
shaderSources[0][0],
|
|
||||||
shaderSources[0][1],
|
|
||||||
{ ...settings.shaderUniforms },
|
|
||||||
{
|
|
||||||
getValueFromUniforms: (v) => (v.lines ? v.lines.length / 2 : 0),
|
|
||||||
uniformArraySizeName: 'lineCount',
|
|
||||||
startingValue: 0,
|
|
||||||
enablingMacro: 'linesEnabled',
|
|
||||||
steps: 1,
|
|
||||||
maximumValue: 15,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
this.lightingFrameBuffer = new DefaultFrameBuffer(this.gl);
|
|
||||||
this.lightingProgram = new UniformArrayAutoScalingProgram(
|
|
||||||
this.gl,
|
|
||||||
shaderSources[1][0],
|
|
||||||
shaderSources[1][1],
|
|
||||||
{ ...settings.shaderUniforms },
|
|
||||||
{
|
|
||||||
getValueFromUniforms: (v) => (v.lights ? v.lights.length : 0),
|
|
||||||
uniformArraySizeName: 'lightCount',
|
|
||||||
startingValue: 1,
|
|
||||||
enablingMacro: null,
|
|
||||||
steps: 1,
|
|
||||||
maximumValue: 8,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private setupAutoscaling() {
|
|
||||||
this.qualityScaler = new Autoscaler(
|
|
||||||
[
|
|
||||||
(v) => (this.lightingFrameBuffer.renderScale = v),
|
|
||||||
(v) => (this.distanceFieldFrameBuffer.renderScale = v),
|
|
||||||
],
|
|
||||||
settings.qualityScaling.scaleTargets,
|
|
||||||
settings.qualityScaling.startingTargetIndex,
|
|
||||||
settings.qualityScaling.scalingOptions
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private timeSinceLastAdjusment = 0;
|
|
||||||
private exponentialDecayedDeltaTime = 0.0;
|
|
||||||
private configureQuality(deltaTime: DOMHighResTimeStamp) {
|
|
||||||
this.timeSinceLastAdjusment += deltaTime;
|
|
||||||
if (
|
|
||||||
this.timeSinceLastAdjusment >=
|
|
||||||
settings.qualityScaling.adjusmentRateInMilliseconds
|
|
||||||
) {
|
|
||||||
this.timeSinceLastAdjusment = 0;
|
|
||||||
this.exponentialDecayedDeltaTime = exponentialDecay(
|
|
||||||
this.exponentialDecayedDeltaTime,
|
|
||||||
deltaTime,
|
|
||||||
settings.qualityScaling.deltaTimeResponsiveness
|
|
||||||
);
|
|
||||||
|
|
||||||
if (
|
|
||||||
this.exponentialDecayedDeltaTime <=
|
|
||||||
settings.qualityScaling.targetDeltaTimeInMilliseconds -
|
|
||||||
settings.qualityScaling.deltaTimeError
|
|
||||||
) {
|
|
||||||
this.qualityScaler.increase();
|
|
||||||
} else if (
|
|
||||||
this.exponentialDecayedDeltaTime >
|
|
||||||
settings.qualityScaling.targetDeltaTimeInMilliseconds +
|
|
||||||
settings.qualityScaling.deltaTimeError
|
|
||||||
) {
|
|
||||||
this.qualityScaler.decrease();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
InfoText.modifyRecord(
|
|
||||||
'quality',
|
|
||||||
`${toPercent(this.distanceFieldFrameBuffer.renderScale)}, ${toPercent(
|
|
||||||
this.lightingFrameBuffer.renderScale
|
|
||||||
)}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public drawPrimitive(primitive: IPrimitive) {
|
public drawPrimitive(primitive: IPrimitive) {
|
||||||
this.primitives.push(primitive);
|
this.distancePass.addDrawable(primitive);
|
||||||
}
|
}
|
||||||
|
|
||||||
public drawLight(light: ILight) {
|
public drawLight(light: ILight) {
|
||||||
this.lights.push(light);
|
this.lightingPass.addDrawable(light);
|
||||||
}
|
}
|
||||||
|
|
||||||
public startFrame(deltaTime: DOMHighResTimeStamp): void {
|
public startFrame(deltaTime: DOMHighResTimeStamp): void {
|
||||||
this.configureQuality(deltaTime);
|
this.autoscaler.autoscale(deltaTime);
|
||||||
this.primitives = [];
|
|
||||||
this.lights = [];
|
|
||||||
|
|
||||||
this.stopwatch?.start();
|
this.stopwatch?.start();
|
||||||
this.distanceFieldFrameBuffer.setSize();
|
this.distanceFieldFrameBuffer.setSize();
|
||||||
|
|
@ -160,84 +84,13 @@ export class WebGl2Renderer implements IRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
public finishFrame() {
|
public finishFrame() {
|
||||||
const uniforms: any = this.calculateOwnUniforms();
|
const uniforms = this.calculateOwnUniforms();
|
||||||
|
this.distancePass.render(uniforms, this.viewCircle);
|
||||||
this.lights.forEach((l) => l.serializeToUniforms(uniforms));
|
this.lightingPass.render(
|
||||||
|
uniforms,
|
||||||
this.distanceFieldFrameBuffer.bindAndClear();
|
this.viewCircle,
|
||||||
const q = 1 / settings.tileMultiplier;
|
|
||||||
const uvSize = vec2.fromValues(q, q);
|
|
||||||
|
|
||||||
const possiblyOnScreenPrimitives = this.primitives.filter(
|
|
||||||
(p) => p.minimumDistance(this.viewCircle.center) < this.viewCircle.radius
|
|
||||||
) as Array<TunnelShape>;
|
|
||||||
|
|
||||||
InfoText.modifyRecord(
|
|
||||||
'nearby lines',
|
|
||||||
possiblyOnScreenPrimitives.length.toString()
|
|
||||||
);
|
|
||||||
|
|
||||||
const origin = vec2.transformMat2d(
|
|
||||||
vec2.create(),
|
|
||||||
vec2.fromValues(0, 0),
|
|
||||||
uniforms.uvToWorld
|
|
||||||
);
|
|
||||||
|
|
||||||
const firstCenter = vec2.transformMat2d(
|
|
||||||
vec2.create(),
|
|
||||||
vec2.fromValues(q, q),
|
|
||||||
uniforms.uvToWorld
|
|
||||||
);
|
|
||||||
|
|
||||||
vec2.subtract(firstCenter, firstCenter, origin);
|
|
||||||
|
|
||||||
const worldR = vec2.length(firstCenter);
|
|
||||||
uniforms.maxMinDistance = 2 * worldR;
|
|
||||||
|
|
||||||
let sumLineCount = 0;
|
|
||||||
|
|
||||||
for (let x = 0; x < 1; x += q) {
|
|
||||||
for (let y = 0; y < 1; y += q) {
|
|
||||||
const uvBottomLeft = vec2.fromValues(x, y);
|
|
||||||
this.distanceProgram.setDrawingRectangle(uvBottomLeft, uvSize);
|
|
||||||
|
|
||||||
const tileCenterWorldCoordinates = vec2.transformMat2d(
|
|
||||||
vec2.create(),
|
|
||||||
vec2.add(vec2.create(), uvBottomLeft, vec2.fromValues(q, q)),
|
|
||||||
uniforms.uvToWorld
|
|
||||||
);
|
|
||||||
|
|
||||||
const primitivesNearTile = possiblyOnScreenPrimitives.filter(
|
|
||||||
(p) => p.distance(tileCenterWorldCoordinates) < 2 * worldR
|
|
||||||
);
|
|
||||||
|
|
||||||
sumLineCount += primitivesNearTile.length;
|
|
||||||
|
|
||||||
uniforms.lines = [];
|
|
||||||
uniforms.radii = [];
|
|
||||||
|
|
||||||
primitivesNearTile.forEach((p) => p.serializeToUniforms(uniforms));
|
|
||||||
|
|
||||||
this.distanceProgram.bindAndSetUniforms(uniforms);
|
|
||||||
this.distanceProgram.draw();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
InfoText.modifyRecord(
|
|
||||||
'lines',
|
|
||||||
(
|
|
||||||
sumLineCount /
|
|
||||||
settings.tileMultiplier /
|
|
||||||
settings.tileMultiplier
|
|
||||||
).toFixed(2)
|
|
||||||
);
|
|
||||||
|
|
||||||
this.lightingFrameBuffer.bindAndClear(
|
|
||||||
this.distanceFieldFrameBuffer.colorTexture
|
this.distanceFieldFrameBuffer.colorTexture
|
||||||
);
|
);
|
||||||
this.lightingProgram.bindAndSetUniforms(uniforms);
|
|
||||||
this.lightingProgram.draw();
|
|
||||||
|
|
||||||
this.stopwatch?.stop();
|
this.stopwatch?.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,12 @@ export const settings = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
tileMultiplier: 5,
|
tileMultiplier: 5,
|
||||||
shaderUniforms: {
|
shaderMacros: {
|
||||||
distanceScale: 64,
|
distanceScale: 64,
|
||||||
edgeSmoothing: 10,
|
edgeSmoothing: 10,
|
||||||
},
|
},
|
||||||
|
shaderCombinations: {
|
||||||
|
lineSteps: [0, 1, 2, 3, 4, 8, 16, 32],
|
||||||
|
circleLightSteps: [0, 1, 2, 3],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
precision mediump float;
|
precision mediump float;
|
||||||
|
|
||||||
#define LINES_ENABLED {linesEnabled}
|
|
||||||
#define LINE_COUNT {lineCount}
|
#define LINE_COUNT {lineCount}
|
||||||
#define CAVE_COLOR vec3(0.36, 0.38, 0.76)
|
#define CAVE_COLOR vec3(0.36, 0.38, 0.76)
|
||||||
#define AIR_COLOR vec3(0.7)
|
#define AIR_COLOR vec3(0.7)
|
||||||
|
|
@ -11,27 +10,30 @@ precision mediump float;
|
||||||
|
|
||||||
uniform float maxMinDistance;
|
uniform float maxMinDistance;
|
||||||
|
|
||||||
#if LINES_ENABLED
|
#if LINE_COUNT > 0
|
||||||
// start, end - start
|
uniform struct Line {
|
||||||
uniform vec2[LINE_COUNT * 2] lines;
|
vec2 from;
|
||||||
// startRadius, endRadius
|
vec2 toFromDelta;
|
||||||
uniform float[LINE_COUNT * 2] radii;
|
float fromRadius;
|
||||||
|
float toRadius;
|
||||||
|
}[LINE_COUNT] lines;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
in vec2 worldCoordinates;
|
in vec2 worldCoordinates;
|
||||||
out vec4 fragmentColor;
|
out vec4 fragmentColor;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
#if LINES_ENABLED
|
#if LINE_COUNT > 0
|
||||||
float minDistance = maxMinDistance;
|
float minDistance = maxMinDistance;
|
||||||
|
|
||||||
for (int i = 0; i < LINE_COUNT; i++) {
|
for (int i = 0; i < LINE_COUNT; i++) {
|
||||||
vec2 pa = worldCoordinates - lines[2 * i];
|
Line line = lines[i];
|
||||||
vec2 ba = lines[2 * i + 1];
|
vec2 pa = worldCoordinates - line.from;
|
||||||
|
vec2 ba = line.toFromDelta;
|
||||||
float baLength = length(ba);
|
float baLength = length(ba);
|
||||||
// todo: do we really want this dot(pa / baLength, ba / baLength)
|
// todo: do we really want this dot(pa / baLength, ba / baLength)
|
||||||
float h = clamp(dot(pa / baLength, ba / baLength), 0.0, 1.0);
|
float h = clamp(dot(pa / baLength, ba / baLength), 0.0, 1.0);
|
||||||
float lineDistance = distance(pa, ba * h) - mix(radii[2 * i], radii[2 * i + 1], h);
|
float lineDistance = distance(pa, ba * h) - mix(line.fromRadius, line.toRadius, h);
|
||||||
|
|
||||||
minDistance = min(minDistance, lineDistance);
|
minDistance = min(minDistance, lineDistance);
|
||||||
}
|
}
|
||||||
100
frontend/src/scripts/drawing/shaders/lights-shading-fs.glsl
Normal file
100
frontend/src/scripts/drawing/shaders/lights-shading-fs.glsl
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
#version 300 es
|
||||||
|
|
||||||
|
precision mediump float;
|
||||||
|
|
||||||
|
#define INFINITY 1000.0
|
||||||
|
#define LIGHT_DROP 500.0
|
||||||
|
#define MIN_STEP 1.0
|
||||||
|
#define AMBIENT_LIGHT vec3(0.15)
|
||||||
|
|
||||||
|
#define LIGHT_COUNT {lightCount}
|
||||||
|
#define DISTANCE_SCALE {distanceScale}
|
||||||
|
#define EDGE_SMOOTHING {edgeSmoothing}
|
||||||
|
|
||||||
|
#if LIGHT_COUNT > 0
|
||||||
|
uniform struct Light {
|
||||||
|
vec2 center;
|
||||||
|
float radius;
|
||||||
|
vec3 value;
|
||||||
|
}[LIGHT_COUNT] lights;
|
||||||
|
|
||||||
|
uniform sampler2D distanceTexture;
|
||||||
|
uniform vec2 viewBoxSize;
|
||||||
|
|
||||||
|
in vec2[LIGHT_COUNT] directions;
|
||||||
|
|
||||||
|
float getDistance(in vec2 target, out vec3 color) {
|
||||||
|
vec4 values = texture(distanceTexture, target);
|
||||||
|
color = values.rgb;
|
||||||
|
return values.w * DISTANCE_SCALE;
|
||||||
|
}
|
||||||
|
|
||||||
|
float getDistance(in vec2 target) {
|
||||||
|
return texture(distanceTexture, target).w * DISTANCE_SCALE;
|
||||||
|
}
|
||||||
|
|
||||||
|
float getFractionOfLightArriving(
|
||||||
|
in vec2 target,
|
||||||
|
in vec2 direction,
|
||||||
|
in float startingDistance,
|
||||||
|
in float lightDistance,
|
||||||
|
in float lightRadius
|
||||||
|
) {
|
||||||
|
float q = 1.0;
|
||||||
|
float rayLength = startingDistance;
|
||||||
|
float movingAverageMeanDistance = startingDistance;
|
||||||
|
|
||||||
|
direction /= viewBoxSize;
|
||||||
|
|
||||||
|
for (int j = 0; j < 48; j++) {
|
||||||
|
float minDistance = getDistance(target + direction * rayLength);
|
||||||
|
movingAverageMeanDistance = movingAverageMeanDistance / 2.0 + minDistance / 2.0;
|
||||||
|
q = min(q, movingAverageMeanDistance / rayLength);
|
||||||
|
|
||||||
|
rayLength += max(MIN_STEP, minDistance);
|
||||||
|
if (rayLength > lightDistance) {
|
||||||
|
return clamp(q * (lightDistance + lightRadius) / lightRadius, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
in vec2 worldCoordinates;
|
||||||
|
in vec2 uvCoordinates;
|
||||||
|
out vec4 fragmentColor;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
#if LIGHT_COUNT > 0
|
||||||
|
|
||||||
|
vec3 colorAtPosition;
|
||||||
|
float startingDistance = getDistance(uvCoordinates, colorAtPosition);
|
||||||
|
|
||||||
|
vec3 ligthing = vec3(0.0);
|
||||||
|
|
||||||
|
for (int i = 0; i < LIGHT_COUNT; i++) {
|
||||||
|
Light light = lights[i];
|
||||||
|
float lightDistance = distance(light.center, worldCoordinates);
|
||||||
|
|
||||||
|
float r = (lightDistance + light.radius) / LIGHT_DROP + 1.0;
|
||||||
|
vec3 lightColorAtPosition = light.value / (r * r);
|
||||||
|
|
||||||
|
float fractionOfLightArriving = getFractionOfLightArriving(
|
||||||
|
uvCoordinates, normalize(directions[i]), startingDistance,
|
||||||
|
lightDistance, light.radius
|
||||||
|
);
|
||||||
|
|
||||||
|
ligthing += lightColorAtPosition * fractionOfLightArriving;
|
||||||
|
}
|
||||||
|
|
||||||
|
fragmentColor = vec4(
|
||||||
|
colorAtPosition * (AMBIENT_LIGHT +
|
||||||
|
step(EDGE_SMOOTHING / 2.0, clamp(startingDistance, 0.0, EDGE_SMOOTHING)) * ligthing),
|
||||||
|
1.0
|
||||||
|
);
|
||||||
|
|
||||||
|
#else
|
||||||
|
fragmentColor = vec4(1.0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
#version 300 es
|
||||||
|
|
||||||
|
precision mediump float;
|
||||||
|
|
||||||
|
#define LIGHT_COUNT {lightCount}
|
||||||
|
|
||||||
|
uniform mat3 modelTransform;
|
||||||
|
uniform mat3 ndcToUv;
|
||||||
|
uniform mat3 uvToWorld;
|
||||||
|
|
||||||
|
in vec4 vertexPosition;
|
||||||
|
|
||||||
|
out vec2 worldCoordinates;
|
||||||
|
out vec2 uvCoordinates;
|
||||||
|
|
||||||
|
#if LIGHT_COUNT > 0
|
||||||
|
uniform struct Light {
|
||||||
|
vec2 center;
|
||||||
|
float radius;
|
||||||
|
vec3 value;
|
||||||
|
}[LIGHT_COUNT] lights;
|
||||||
|
|
||||||
|
out vec2[LIGHT_COUNT] directions;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec3 vertexPosition2D = vec3(vertexPosition.xy, 1.0) * modelTransform;
|
||||||
|
vec3 uvCoordinates1 = vertexPosition2D * ndcToUv;
|
||||||
|
worldCoordinates = (uvCoordinates1 * uvToWorld).xy;
|
||||||
|
uvCoordinates = (uvCoordinates1).xy;
|
||||||
|
|
||||||
|
#if LIGHT_COUNT > 0
|
||||||
|
for (int i = 0; i < LIGHT_COUNT; i++) {
|
||||||
|
directions[i] = lights[i].center - worldCoordinates;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
gl_Position = vec4(vertexPosition2D.xy, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
precision mediump float;
|
precision mediump float;
|
||||||
|
|
||||||
|
|
||||||
vec3 rainbow(float level) {
|
vec3 rainbow(float level) {
|
||||||
float r = float(level <= 2.0) + float(level > 4.0) * 0.5;
|
float r = float(level <= 2.0) + float(level > 4.0) * 0.5;
|
||||||
float g = max(1.0 - abs(level - 2.0) * 0.5, 0.0);
|
float g = max(1.0 - abs(level - 2.0) * 0.5, 0.0);
|
||||||
|
|
@ -1,8 +1,3 @@
|
||||||
import caveFragmentShader from '../shaders/cave-distance-fs.glsl';
|
|
||||||
import lightsShader from '../shaders/lights-shading-fs.glsl';
|
|
||||||
import caveVertexShader from '../shaders/passthrough-distance-vs.glsl';
|
|
||||||
import lightsVertexShader from '../shaders/passthrough-shading-vs.glsl';
|
|
||||||
// import lightsShader from '../shaders/rainbow-shading-fs.glsl';
|
|
||||||
import { CommandBroadcaster } from './commands/command-broadcaster';
|
import { CommandBroadcaster } from './commands/command-broadcaster';
|
||||||
import { BeforeRenderCommand } from './commands/types/before-render';
|
import { BeforeRenderCommand } from './commands/types/before-render';
|
||||||
import { RenderCommand } from './commands/types/draw';
|
import { RenderCommand } from './commands/types/draw';
|
||||||
|
|
@ -41,10 +36,7 @@ export class Game {
|
||||||
[this.objects]
|
[this.objects]
|
||||||
);
|
);
|
||||||
|
|
||||||
this.renderer = new WebGl2Renderer(canvas, overlay, [
|
this.renderer = new WebGl2Renderer(canvas, overlay);
|
||||||
[caveVertexShader, caveFragmentShader],
|
|
||||||
[lightsVertexShader, lightsShader],
|
|
||||||
]);
|
|
||||||
|
|
||||||
this.initializeScene();
|
this.initializeScene();
|
||||||
requestAnimationFrame(this.gameLoop.bind(this));
|
requestAnimationFrame(this.gameLoop.bind(this));
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { mix } from '../../helper/mix';
|
import { mix } from './mix';
|
||||||
import { clamp } from '../../helper/clamp';
|
import { clamp } from './clamp';
|
||||||
|
|
||||||
export class Autoscaler {
|
export class Autoscaler {
|
||||||
// can have fractions
|
// can have fractions
|
||||||
31
frontend/src/scripts/helper/get-combinations.ts
Normal file
31
frontend/src/scripts/helper/get-combinations.ts
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
export const getCombinations = (
|
||||||
|
values: Array<Array<number>>
|
||||||
|
): Array<Array<number>> => {
|
||||||
|
if (!values.every((a) => a.length > 0)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const result: Array<Array<number>> = [];
|
||||||
|
const counters = values.map((_) => 0);
|
||||||
|
|
||||||
|
const increaseCounter = (i: number) => {
|
||||||
|
if (i >= counters.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
counters[i]++;
|
||||||
|
|
||||||
|
if (counters[i] >= values[i].length) {
|
||||||
|
counters[i] = 0;
|
||||||
|
return increaseCounter(i + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
do {
|
||||||
|
result.push(values.map((v, i) => v[counters[i]]));
|
||||||
|
} while (increaseCounter(0));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
@ -2,7 +2,7 @@ import { vec2, vec3 } from 'gl-matrix';
|
||||||
import { RenderCommand } from '../../commands/types/draw';
|
import { RenderCommand } from '../../commands/types/draw';
|
||||||
import { MoveToCommand } from '../../commands/types/move-to';
|
import { MoveToCommand } from '../../commands/types/move-to';
|
||||||
import { GameObject } from '../game-object';
|
import { GameObject } from '../game-object';
|
||||||
import { CircleLight } from '../../drawing/lights/circle-light';
|
import { CircleLight } from '../../drawing/drawables/lights/circle-light';
|
||||||
|
|
||||||
const range = 2000;
|
const range = 2000;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { vec2 } from 'gl-matrix';
|
import { vec2 } from 'gl-matrix';
|
||||||
import { RenderCommand } from '../../commands/types/draw';
|
import { RenderCommand } from '../../commands/types/draw';
|
||||||
import { TunnelShape } from '../../drawing/primitives/tunnel-shape';
|
import { TunnelShape } from '../../drawing/drawables/primitives/tunnel-shape';
|
||||||
import { GameObject } from '../game-object';
|
import { GameObject } from '../game-object';
|
||||||
|
|
||||||
export interface Line {}
|
export interface Line {}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ export const createDungeon = (objects: ObjectContainer) => {
|
||||||
new Tunnel(previousEnd, currentEnd, previousRadius, currentToRadius)
|
new Tunnel(previousEnd, currentEnd, previousRadius, currentToRadius)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (deltaHeight > 0 && Math.random() > 0.8) {
|
/*if (deltaHeight > 0 && Math.random() > 0.8) {
|
||||||
objects.addObject(
|
objects.addObject(
|
||||||
new Lamp(
|
new Lamp(
|
||||||
currentEnd,
|
currentEnd,
|
||||||
|
|
@ -30,7 +30,7 @@ export const createDungeon = (objects: ObjectContainer) => {
|
||||||
1
|
1
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}*/
|
||||||
|
|
||||||
previousEnd = currentEnd;
|
previousEnd = currentEnd;
|
||||||
previousRadius = currentToRadius;
|
previousRadius = currentToRadius;
|
||||||
|
|
|
||||||
|
|
@ -1,91 +0,0 @@
|
||||||
#version 300 es
|
|
||||||
|
|
||||||
precision mediump float;
|
|
||||||
|
|
||||||
#define INFINITY 1000.0
|
|
||||||
#define AMBIENT_LIGHT vec3(0.15)
|
|
||||||
#define LIGHT_DROP 500.0
|
|
||||||
#define MIN_STEP 1.0
|
|
||||||
#define LIGHT_COUNT 1
|
|
||||||
#define DISTANCE_SCALE {distanceScale}
|
|
||||||
#define EDGE_SMOOTHING {edgeSmoothing}
|
|
||||||
|
|
||||||
uniform struct Light {
|
|
||||||
vec2 center;
|
|
||||||
float radius;
|
|
||||||
vec3 value;
|
|
||||||
}[LIGHT_COUNT] lights;
|
|
||||||
|
|
||||||
uniform sampler2D distanceTexture;
|
|
||||||
uniform vec2 viewBoxSize;
|
|
||||||
|
|
||||||
in vec2[LIGHT_COUNT] directions;
|
|
||||||
|
|
||||||
float getDistance(in vec2 target, out vec3 color) {
|
|
||||||
vec4 values = texture(distanceTexture, target);
|
|
||||||
color = values.rgb;
|
|
||||||
return values.w * DISTANCE_SCALE;
|
|
||||||
}
|
|
||||||
|
|
||||||
float getDistance(in vec2 target) {
|
|
||||||
return texture(distanceTexture, target).w * DISTANCE_SCALE;
|
|
||||||
}
|
|
||||||
|
|
||||||
float getFractionOfLightArriving(
|
|
||||||
in vec2 target,
|
|
||||||
in vec2 direction,
|
|
||||||
in float startingDistance,
|
|
||||||
in float lightDistance,
|
|
||||||
in float lightRadius
|
|
||||||
) {
|
|
||||||
float q = 1.0;
|
|
||||||
float rayLength = startingDistance;
|
|
||||||
float movingAverageMeanDistance = startingDistance;
|
|
||||||
|
|
||||||
direction /= viewBoxSize;
|
|
||||||
|
|
||||||
for (int j = 0; j < 32; j++) {
|
|
||||||
float minDistance = getDistance(target + direction * rayLength);
|
|
||||||
movingAverageMeanDistance = movingAverageMeanDistance / 2.0 + minDistance / 2.0;
|
|
||||||
q = min(q, movingAverageMeanDistance / rayLength);
|
|
||||||
|
|
||||||
rayLength += max(MIN_STEP, minDistance);
|
|
||||||
if (rayLength > lightDistance) {
|
|
||||||
return clamp(q * (lightDistance + lightRadius) / lightRadius, 0.0, 1.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
in vec2 worldCoordinates;
|
|
||||||
in vec2 uvCoordinates;
|
|
||||||
out vec4 fragmentColor;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec3 colorAtPosition;
|
|
||||||
float startingDistance = getDistance(uvCoordinates, colorAtPosition);
|
|
||||||
|
|
||||||
vec3 ligthing = vec3(0.0);
|
|
||||||
|
|
||||||
for (int i = 0; i < LIGHT_COUNT; i++) {
|
|
||||||
Light light = lights[i];
|
|
||||||
float lightDistance = distance(light.center, worldCoordinates);
|
|
||||||
|
|
||||||
float r = (lightDistance + light.radius) / LIGHT_DROP + 1.0;
|
|
||||||
vec3 lightColorAtPosition = light.value / (r * r);
|
|
||||||
|
|
||||||
float fractionOfLightArriving = getFractionOfLightArriving(
|
|
||||||
uvCoordinates, normalize(directions[i]), startingDistance,
|
|
||||||
lightDistance, light.radius
|
|
||||||
);
|
|
||||||
|
|
||||||
ligthing += lightColorAtPosition * fractionOfLightArriving;
|
|
||||||
}
|
|
||||||
|
|
||||||
fragmentColor = vec4(
|
|
||||||
colorAtPosition * (AMBIENT_LIGHT +
|
|
||||||
step(EDGE_SMOOTHING / 2.0, clamp(startingDistance, 0.0, EDGE_SMOOTHING)) * ligthing),
|
|
||||||
1.0
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
#version 300 es
|
|
||||||
|
|
||||||
precision mediump float;
|
|
||||||
|
|
||||||
#define LIGHT_COUNT 1
|
|
||||||
uniform mat3 ndcToUv;
|
|
||||||
uniform mat3 uvToWorld;
|
|
||||||
|
|
||||||
in vec4 vertexPosition;
|
|
||||||
|
|
||||||
out vec2 worldCoordinates;
|
|
||||||
out vec2 uvCoordinates;
|
|
||||||
|
|
||||||
uniform struct Light {
|
|
||||||
vec2 center;
|
|
||||||
float radius;
|
|
||||||
vec3 value;
|
|
||||||
}[LIGHT_COUNT] lights;
|
|
||||||
|
|
||||||
out vec2[LIGHT_COUNT] directions;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec3 vertexPosition2D = vec3(vertexPosition.xy, 1.0);
|
|
||||||
vec3 uvCoordinates1 = vertexPosition2D * ndcToUv;
|
|
||||||
worldCoordinates = (uvCoordinates1 * uvToWorld).xy;
|
|
||||||
uvCoordinates = (uvCoordinates1).xy;
|
|
||||||
|
|
||||||
for (int i = 0; i < LIGHT_COUNT; i++) {
|
|
||||||
directions[i] = lights[i].center - worldCoordinates;
|
|
||||||
}
|
|
||||||
|
|
||||||
gl_Position = vertexPosition;
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue