Render in NDC
This commit is contained in:
parent
40a660b7cb
commit
05682dd2d5
16 changed files with 106 additions and 82 deletions
|
|
@ -9,6 +9,7 @@ export class DrawableBlob extends Blob implements IDrawable {
|
|||
uniformName: 'blobs',
|
||||
countMacroName: 'blobCount',
|
||||
shaderCombinationSteps: settings.shaderCombinations.blobSteps,
|
||||
empty: new DrawableBlob(vec2.fromValues(0, 0)),
|
||||
};
|
||||
|
||||
public serializeToUniforms(uniforms: any, scale: number, transform: mat2d): void {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ export class DrawableTunnel extends TunnelShape implements IDrawable {
|
|||
uniformName: 'lines',
|
||||
countMacroName: 'lineCount',
|
||||
shaderCombinationSteps: settings.shaderCombinations.lineSteps,
|
||||
empty: new DrawableTunnel(vec2.fromValues(0, 0), vec2.fromValues(0, 0), 0, 0),
|
||||
};
|
||||
|
||||
public serializeToUniforms(uniforms: any, scale: number, transform: mat2d): void {
|
||||
|
|
@ -19,7 +20,7 @@ export class DrawableTunnel extends TunnelShape implements IDrawable {
|
|||
|
||||
uniforms[uniformName].push({
|
||||
from: vec2.transformMat2d(vec2.create(), this.from, transform),
|
||||
toFromDelta: vec2.transformMat2d(vec2.create(), this.toFromDelta, transform),
|
||||
toFromDelta: vec2.scale(vec2.create(), this.toFromDelta, scale),
|
||||
fromRadius: this.fromRadius * scale,
|
||||
toRadius: this.toRadius * scale,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
import { IDrawable } from './i-drawable';
|
||||
|
||||
export interface IDrawableDescriptor {
|
||||
uniformName: string;
|
||||
countMacroName: string;
|
||||
shaderCombinationSteps: Array<number>;
|
||||
readonly empty: IDrawable;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ export class CircleLight implements ILight {
|
|||
uniformName: 'circleLights',
|
||||
countMacroName: 'circleLightCount',
|
||||
shaderCombinationSteps: settings.shaderCombinations.circleLightSteps,
|
||||
empty: new CircleLight(vec2.fromValues(0, 0), 0, vec3.fromValues(0, 0, 0), 0),
|
||||
};
|
||||
|
||||
constructor(
|
||||
|
|
@ -17,7 +18,7 @@ export class CircleLight implements ILight {
|
|||
public lightness: number
|
||||
) {}
|
||||
|
||||
public distance(target: vec2): number {
|
||||
public distance(_: vec2): number {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ export class PointLight implements ILight {
|
|||
uniformName: 'pointLights',
|
||||
countMacroName: 'pointLightCount',
|
||||
shaderCombinationSteps: settings.shaderCombinations.pointLightSteps,
|
||||
empty: new PointLight(vec2.fromValues(0, 0), 0, vec3.fromValues(0, 0, 0), 0),
|
||||
};
|
||||
|
||||
public constructor(
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { vec2 } from 'gl-matrix';
|
||||
import { mat2d, vec2 } from 'gl-matrix';
|
||||
import { getCombinations } from '../../../helper/get-combinations';
|
||||
import { last } from '../../../helper/last';
|
||||
import { IDrawableDescriptor } from '../../drawables/i-drawable-descriptor';
|
||||
|
|
@ -12,33 +12,40 @@ export class UniformArrayAutoScalingProgram implements IProgram {
|
|||
}> = [];
|
||||
|
||||
private current: FragmentShaderOnlyProgram;
|
||||
|
||||
private drawingRectangleTopLeft = vec2.fromValues(0, 0);
|
||||
|
||||
private drawingRectangleSize = vec2.fromValues(1, 1);
|
||||
|
||||
constructor(
|
||||
private gl: WebGL2RenderingContext,
|
||||
shaderSources: [string, string],
|
||||
private options: Array<IDrawableDescriptor>
|
||||
private descriptors: Array<IDrawableDescriptor>
|
||||
) {
|
||||
const names = options.map((o) => o.countMacroName);
|
||||
const names = descriptors.map((o) => o.countMacroName);
|
||||
for (const combination of getCombinations(
|
||||
options.map((o) => o.shaderCombinationSteps)
|
||||
descriptors.map((o) => o.shaderCombinationSteps)
|
||||
)) {
|
||||
this.createProgram(names, combination, shaderSources);
|
||||
}
|
||||
}
|
||||
|
||||
public bindAndSetUniforms(uniforms: { [name: string]: any }): void {
|
||||
const values = this.options.map((o) =>
|
||||
uniforms[o.uniformName] ? uniforms[o.uniformName].length : 0
|
||||
const values = this.descriptors.map((d) =>
|
||||
uniforms[d.uniformName] ? uniforms[d.uniformName].length : 0
|
||||
);
|
||||
|
||||
const closest = this.programs.find((p) => p.values.every((v, i) => v >= values[i]));
|
||||
|
||||
this.current = closest ? closest.program : last(this.programs).program;
|
||||
|
||||
if (closest) {
|
||||
this.descriptors.map((d, i) => {
|
||||
const difference = closest.values[i] - values[i];
|
||||
for (let i = 0; i < difference; i++) {
|
||||
d.empty.serializeToUniforms(uniforms, 0, mat2d.create());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.current.setDrawingRectangle(
|
||||
this.drawingRectangleTopLeft,
|
||||
this.drawingRectangleSize
|
||||
|
|
|
|||
|
|
@ -28,7 +28,12 @@ export class RenderingPass {
|
|||
this.drawables.push(drawable);
|
||||
}
|
||||
|
||||
public render(commonUniforms: any, inputTexture?: WebGLTexture) {
|
||||
public render(
|
||||
commonUniforms: any,
|
||||
scale: number,
|
||||
transform: mat2d,
|
||||
inputTexture?: WebGLTexture
|
||||
) {
|
||||
this.frame.bindAndClear(inputTexture);
|
||||
const q = 1 / settings.tileMultiplier;
|
||||
const tileUvSize = vec2.fromValues(q, q);
|
||||
|
|
@ -51,13 +56,10 @@ export class RenderingPass {
|
|||
|
||||
let sumLineCount = 0;
|
||||
|
||||
const scale = 1;
|
||||
const transform = mat2d.create();
|
||||
|
||||
for (let x = 0; x < 1; x += q) {
|
||||
for (let y = 0; y < 1; y += q) {
|
||||
const uniforms = { ...commonUniforms };
|
||||
uniforms.maxMinDistance = 2 * worldR;
|
||||
uniforms.maxMinDistance = 2 * worldR * scale;
|
||||
|
||||
const uvBottomLeft = vec2.fromValues(x, y);
|
||||
this.program.setDrawingRectangle(uvBottomLeft, tileUvSize);
|
||||
|
|
@ -69,7 +71,7 @@ export class RenderingPass {
|
|||
);
|
||||
|
||||
const primitivesNearTile = this.drawables.filter(
|
||||
(p) => p.distance(tileCenterWorldCoordinates) < 2 * worldR
|
||||
(d) => d.distance(tileCenterWorldCoordinates) < 2 * worldR
|
||||
);
|
||||
|
||||
sumLineCount += primitivesNearTile.length;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import { IDrawable } from '../drawables/i-drawable';
|
|||
import { CircleLight } from '../drawables/lights/circle-light';
|
||||
import { ILight } from '../drawables/lights/i-light';
|
||||
import { PointLight } from '../drawables/lights/point-light';
|
||||
// import lightsShader from '../shaders/rainbow-shading-fs.glsl';
|
||||
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';
|
||||
|
|
@ -21,32 +20,28 @@ import { RenderingPass } from './rendering-pass';
|
|||
|
||||
export class WebGl2Renderer implements IRenderer {
|
||||
private gl: WebGL2RenderingContext;
|
||||
|
||||
private stopwatch?: WebGlStopwatch;
|
||||
|
||||
private upscaleTransform = mat2d.create();
|
||||
private linearDownscale = 1;
|
||||
|
||||
private viewBoxBottomLeft = vec2.create();
|
||||
|
||||
private viewBoxSize = vec2.create();
|
||||
|
||||
private viewAreaScale: vec2;
|
||||
private cursorPosition = vec2.create();
|
||||
|
||||
private distanceFieldFrameBuffer: IntermediateFrameBuffer;
|
||||
|
||||
private lightingFrameBuffer: DefaultFrameBuffer;
|
||||
|
||||
private distancePass: RenderingPass;
|
||||
|
||||
private lightingPass: RenderingPass;
|
||||
|
||||
private autoscaler: FpsAutoscaler;
|
||||
|
||||
private matrices: {
|
||||
distanceScreenToWorld?: mat2d;
|
||||
worldToDistanceUV?: mat2d;
|
||||
cursorPosition?: mat2d;
|
||||
ndcToUv?: mat2d;
|
||||
uvToWorld?: mat2d;
|
||||
} = { ndcToUv: mat2d.fromValues(0.5, 0, 0, 0.5, 0.5, 0.5) };
|
||||
} = {};
|
||||
|
||||
constructor(private canvas: HTMLCanvasElement, private overlay: HTMLElement) {
|
||||
this.gl = getWebGl2Context(canvas);
|
||||
|
|
@ -92,6 +87,7 @@ export class WebGl2Renderer implements IRenderer {
|
|||
this.autoscaler.autoscale(deltaTime);
|
||||
|
||||
this.stopwatch?.start();
|
||||
|
||||
this.distanceFieldFrameBuffer.setSize();
|
||||
this.lightingFrameBuffer.setSize();
|
||||
}
|
||||
|
|
@ -99,16 +95,24 @@ export class WebGl2Renderer implements IRenderer {
|
|||
public finishFrame() {
|
||||
this.calculateMatrices();
|
||||
|
||||
this.distancePass.render(this.uniforms);
|
||||
|
||||
this.lightingPass.render(this.uniforms, this.distanceFieldFrameBuffer.colorTexture);
|
||||
this.distancePass.render(this.uniforms, this.linearDownscale, this.upscaleTransform);
|
||||
this.lightingPass.render(
|
||||
this.uniforms,
|
||||
this.linearDownscale,
|
||||
this.upscaleTransform,
|
||||
this.distanceFieldFrameBuffer.colorTexture
|
||||
);
|
||||
|
||||
this.stopwatch?.stop();
|
||||
}
|
||||
|
||||
private get uniforms(): any {
|
||||
const cursorPosition = this.screenUvToWorldCoordinate(this.cursorPosition);
|
||||
return { ...this.matrices, cursorPosition, viewBoxSize: this.viewBoxSize };
|
||||
return {
|
||||
...this.matrices,
|
||||
cursorPosition,
|
||||
viewAreaScale: this.viewAreaScale,
|
||||
};
|
||||
}
|
||||
|
||||
private calculateMatrices() {
|
||||
|
|
@ -157,12 +161,31 @@ export class WebGl2Renderer implements IRenderer {
|
|||
}
|
||||
|
||||
public setViewArea(viewArea: BoundingBoxBase) {
|
||||
const targetRange = 2 ** 7;
|
||||
|
||||
this.viewBoxSize = viewArea.size;
|
||||
this.viewBoxBottomLeft = vec2.add(
|
||||
vec2.create(),
|
||||
viewArea.topLeft,
|
||||
vec2.fromValues(0, -viewArea.size.y)
|
||||
);
|
||||
|
||||
this.linearDownscale = targetRange / Math.max(this.viewBoxSize.x, this.viewBoxSize.y);
|
||||
|
||||
this.viewAreaScale = vec2.fromValues(
|
||||
(this.viewBoxSize.x * this.linearDownscale) / 2,
|
||||
(this.viewBoxSize.y * this.linearDownscale) / 2
|
||||
);
|
||||
|
||||
mat2d.fromScaling(
|
||||
this.upscaleTransform,
|
||||
vec2.fromValues(this.linearDownscale, this.linearDownscale)
|
||||
);
|
||||
|
||||
const translate = vec2.scale(vec2.create(), this.viewBoxBottomLeft, -1);
|
||||
vec2.subtract(translate, translate, vec2.scale(vec2.create(), this.viewBoxSize, 0.5));
|
||||
|
||||
mat2d.translate(this.upscaleTransform, this.upscaleTransform, translate);
|
||||
}
|
||||
|
||||
public setCursorPosition(position: vec2): void {
|
||||
|
|
|
|||
|
|
@ -22,10 +22,9 @@ export const settings = {
|
|||
},
|
||||
tileMultiplier: 8,
|
||||
shaderMacros: {
|
||||
edgeSmoothing: 10,
|
||||
headRadius: 5,
|
||||
torsoRadius: 8,
|
||||
footRadius: 2,
|
||||
headRadius: 0.05,
|
||||
torsoRadius: 0.08,
|
||||
footRadius: 0.02,
|
||||
},
|
||||
shaderCombinations: {
|
||||
lineSteps: [0, 1, 2, 4, 8, 16, 128],
|
||||
|
|
|
|||
|
|
@ -69,18 +69,18 @@ uniform float maxMinDistance;
|
|||
}
|
||||
#endif
|
||||
|
||||
in vec2 worldCoordinates;
|
||||
in vec2 position;
|
||||
out vec2 fragmentColor;
|
||||
|
||||
void main() {
|
||||
float minDistance = -maxMinDistance;
|
||||
|
||||
#if LINE_COUNT > 0
|
||||
lineMinDistance(worldCoordinates, minDistance);
|
||||
lineMinDistance(position, minDistance);
|
||||
#endif
|
||||
|
||||
#if BLOB_COUNT > 0
|
||||
blobMinDistance(worldCoordinates, minDistance);
|
||||
//blobMinDistance(position, minDistance);
|
||||
#endif
|
||||
|
||||
fragmentColor = vec2(minDistance, 0.0);
|
||||
|
|
|
|||
|
|
@ -3,15 +3,13 @@
|
|||
precision mediump float;
|
||||
|
||||
#define INFINITY 1000.0
|
||||
#define LIGHT_DROP 500.0
|
||||
#define LIGHT_DROP 50.0
|
||||
#define AMBIENT_LIGHT vec3(0.15)
|
||||
|
||||
#define CIRCLE_LIGHT_COUNT {circleLightCount}
|
||||
#define POINT_LIGHT_COUNT {pointLightCount}
|
||||
#define EDGE_SMOOTHING {edgeSmoothing}
|
||||
|
||||
uniform sampler2D distanceTexture;
|
||||
uniform vec2 viewBoxSize;
|
||||
|
||||
vec3[4] colors = vec3[4](
|
||||
vec3(0.5),
|
||||
|
|
@ -50,8 +48,10 @@ float getDistance(in vec2 target) {
|
|||
in vec2[POINT_LIGHT_COUNT] pointLightDirections;
|
||||
#endif
|
||||
|
||||
in vec2 worldCoordinates;
|
||||
in vec2 position;
|
||||
in vec2 uvCoordinates;
|
||||
uniform vec2 viewAreaScale;
|
||||
|
||||
out vec4 fragmentColor;
|
||||
|
||||
void main() {
|
||||
|
|
@ -63,13 +63,13 @@ void main() {
|
|||
for (int i = 0; i < CIRCLE_LIGHT_COUNT; i++) {
|
||||
float lightCenterDistance = distance(
|
||||
circleLights[i].center,
|
||||
worldCoordinates
|
||||
position
|
||||
);
|
||||
float lightDistance = lightCenterDistance - circleLights[i].radius;
|
||||
|
||||
/*if (lightDistance < 0.0) {
|
||||
if (lightDistance < 0.0) {
|
||||
lighting = vec3(1.0, 1.0, 0.0);
|
||||
}*/
|
||||
}
|
||||
|
||||
vec3 lightColorAtPosition = circleLights[i].value / pow(
|
||||
lightDistance / LIGHT_DROP + 1.0, 2.0
|
||||
|
|
@ -77,8 +77,7 @@ void main() {
|
|||
|
||||
float q = INFINITY;
|
||||
float rayLength = startingDistance;
|
||||
vec2 direction = normalize(circleLightDirections[i]) / viewBoxSize;
|
||||
|
||||
vec2 direction = normalize(circleLightDirections[i]) / viewAreaScale / 2.0;
|
||||
for (int j = 0; j < 48; j++) {
|
||||
if (rayLength >= lightDistance) {
|
||||
lighting += lightColorAtPosition * clamp(
|
||||
|
|
@ -124,6 +123,7 @@ void main() {
|
|||
}*/
|
||||
#endif
|
||||
|
||||
fragmentColor = vec4(vec3(startingDistance / 100.0), 1.0);
|
||||
fragmentColor = vec4(
|
||||
colorAtPosition * lighting * step(0.0, startingDistance),
|
||||
1.0
|
||||
|
|
|
|||
|
|
@ -3,14 +3,13 @@
|
|||
precision mediump float;
|
||||
|
||||
uniform mat3 modelTransform;
|
||||
uniform mat3 uvToWorld;
|
||||
uniform mat3 ndcToUv;
|
||||
uniform vec2 viewAreaScale;
|
||||
|
||||
in vec4 vertexPosition;
|
||||
out vec2 worldCoordinates;
|
||||
out vec2 position;
|
||||
|
||||
void main() {
|
||||
vec3 vertexPosition2D = vec3(vertexPosition.xy, 1.0) * modelTransform;
|
||||
worldCoordinates = (vertexPosition2D * ndcToUv * uvToWorld).xy;
|
||||
gl_Position = vec4(vertexPosition2D.xy, 0.0, 1.0);
|
||||
position = vertexPosition2D.xy * viewAreaScale;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,15 +6,13 @@ precision mediump float;
|
|||
#define POINT_LIGHT_COUNT {pointLightCount}
|
||||
|
||||
uniform mat3 modelTransform;
|
||||
uniform mat3 cameraTransform;
|
||||
uniform mat3 ndcToUv;
|
||||
uniform mat3 uvToWorld;
|
||||
|
||||
in vec4 vertexPosition;
|
||||
|
||||
out vec2 worldCoordinates;
|
||||
out vec2 position;
|
||||
out vec2 uvCoordinates;
|
||||
|
||||
uniform vec2 viewAreaScale;
|
||||
|
||||
#if CIRCLE_LIGHT_COUNT > 0
|
||||
uniform struct CircleLight {
|
||||
vec2 center;
|
||||
|
|
@ -37,21 +35,24 @@ out vec2 uvCoordinates;
|
|||
|
||||
void main() {
|
||||
vec3 vertexPosition2D = vec3(vertexPosition.xy, 1.0) * modelTransform;
|
||||
vec3 uvCoordinates1 = vertexPosition2D * ndcToUv;
|
||||
worldCoordinates = (uvCoordinates1 * uvToWorld).xy;
|
||||
uvCoordinates = (uvCoordinates1).xy;
|
||||
gl_Position = vec4(vertexPosition2D.xy, 0.0, 1.0);
|
||||
position = vertexPosition2D.xy * viewAreaScale;
|
||||
|
||||
uvCoordinates = (vertexPosition2D * mat3(
|
||||
0.5, 0.0, 0.5,
|
||||
0.0, 0.5, 0.5,
|
||||
0.0, 0.0, 1.0
|
||||
)).xy;
|
||||
|
||||
#if CIRCLE_LIGHT_COUNT > 0
|
||||
for (int i = 0; i < CIRCLE_LIGHT_COUNT; i++) {
|
||||
circleLightDirections[i] = circleLights[i].center - worldCoordinates;
|
||||
circleLightDirections[i] = circleLights[i].center - position;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if POINT_LIGHT_COUNT > 0
|
||||
for (int i = 0; i < POINT_LIGHT_COUNT; i++) {
|
||||
pointLightDirections[i] = pointLights[i].center - worldCoordinates;
|
||||
pointLightDirections[i] = pointLights[i].center - position;
|
||||
}
|
||||
#endif
|
||||
|
||||
gl_Position = vec4(vertexPosition2D.xy, 0.0, 1.0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ import { CommandGenerator } from '../commands/command-generator';
|
|||
import { clamp01 } from '../helper/clamp';
|
||||
import { CursorMoveCommand } from './commands/cursor-move-command';
|
||||
import { PrimaryActionCommand } from './commands/primary-action';
|
||||
import { SwipeCommand } from './commands/swipe';
|
||||
import { SecondaryActionCommand } from './commands/secondary-action';
|
||||
import { SwipeCommand } from './commands/swipe';
|
||||
import { ZoomCommand } from './commands/zoom';
|
||||
|
||||
export class MouseListener extends CommandGenerator {
|
||||
|
|
@ -39,11 +39,11 @@ export class MouseListener extends CommandGenerator {
|
|||
this.sendCommand(new CursorMoveCommand(position));
|
||||
});
|
||||
|
||||
target.addEventListener('mouseup', (event: MouseEvent) => {
|
||||
target.addEventListener('mouseup', (_: MouseEvent) => {
|
||||
this.isMouseDown = false;
|
||||
});
|
||||
|
||||
target.addEventListener('mouseleave', (event: MouseEvent) => {
|
||||
target.addEventListener('mouseleave', (_: MouseEvent) => {
|
||||
this.isMouseDown = false;
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -15,11 +15,8 @@ import { Lamp } from './lamp';
|
|||
|
||||
export class Character extends GameObject {
|
||||
private keysDown: Set<string> = new Set();
|
||||
|
||||
private light = new Lamp(vec2.create(), 40, vec3.fromValues(0.67, 0.0, 0.33), 2);
|
||||
|
||||
private shape = new DrawableBlob(vec2.create());
|
||||
|
||||
private static speed = 1.5;
|
||||
|
||||
constructor(private game: IGame) {
|
||||
|
|
|
|||
|
|
@ -1,33 +1,22 @@
|
|||
import { vec2 } from 'gl-matrix';
|
||||
import { IShape } from '../i-shape';
|
||||
import { BoundingBox } from '../bounding-box';
|
||||
import { Circle } from './circle';
|
||||
import { settings } from '../../drawing/settings';
|
||||
import { GameObject } from '../../objects/game-object';
|
||||
import { BoundingBox } from '../bounding-box';
|
||||
import { IShape } from '../i-shape';
|
||||
import { Circle } from './circle';
|
||||
|
||||
export class Blob implements IShape {
|
||||
private static readonly boundingCircleRadius = 19;
|
||||
|
||||
private static readonly headOffset = vec2.fromValues(0, 15);
|
||||
|
||||
private static readonly torsoOffset = vec2.fromValues(0, 0);
|
||||
|
||||
private static readonly leftFootOffset = vec2.fromValues(-5, -10);
|
||||
|
||||
private static readonly rightFootOffset = vec2.fromValues(5, -10);
|
||||
|
||||
public readonly isInverted = false;
|
||||
|
||||
protected boundingCircle = new Circle(vec2.create(), Blob.boundingCircleRadius);
|
||||
|
||||
protected head = new Circle(vec2.create(), settings.shaderMacros.headRadius);
|
||||
|
||||
protected torso = new Circle(vec2.create(), settings.shaderMacros.torsoRadius);
|
||||
|
||||
protected leftFoot = new Circle(vec2.create(), settings.shaderMacros.footRadius);
|
||||
|
||||
protected rightFoot = new Circle(vec2.create(), settings.shaderMacros.footRadius);
|
||||
|
||||
public constructor(center: vec2, public readonly gameObject: GameObject = null) {
|
||||
this.position = center;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue