Add uniform for lights
This commit is contained in:
parent
e6782a9a98
commit
d336bee1ba
14 changed files with 218 additions and 116 deletions
|
|
@ -1,12 +1,14 @@
|
||||||
import { vec2 } from 'gl-matrix';
|
import { vec2 } from 'gl-matrix';
|
||||||
|
|
||||||
export interface Drawer {
|
export interface Drawer {
|
||||||
startFrame(): void;
|
startFrame(deltaTime: DOMHighResTimeStamp): void;
|
||||||
finishFrame(): void;
|
finishFrame(): void;
|
||||||
giveUniforms(uniforms: any): void;
|
giveUniforms(uniforms: any): void;
|
||||||
|
appendToUniformList(listName: string, ...values: Array<any>): void;
|
||||||
setCameraPosition(position: vec2): void;
|
setCameraPosition(position: vec2): void;
|
||||||
setCursorPosition(position: vec2): void;
|
setCursorPosition(position: vec2): void;
|
||||||
setInViewArea(size: number): vec2;
|
setInViewArea(size: number): vec2;
|
||||||
|
screenUvToWorldCoordinate(mousePosition: vec2): vec2;
|
||||||
drawInfoText(text: string): void;
|
drawInfoText(text: string): void;
|
||||||
isOnScreen(position: vec2): boolean;
|
isOnScreen(position: vec2): boolean;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { mat3, vec2 } from 'gl-matrix';
|
import { mat3, ReadonlyVec3, vec2, vec3 } from 'gl-matrix';
|
||||||
|
|
||||||
const loaderMat3 = mat3.create();
|
const loaderMat3 = mat3.create();
|
||||||
|
|
||||||
|
|
@ -18,10 +18,15 @@ export const loadUniform = (
|
||||||
> = new Map();
|
> = new Map();
|
||||||
{
|
{
|
||||||
converters.set(WebGL2RenderingContext.FLOAT, (gl, v, l) => {
|
converters.set(WebGL2RenderingContext.FLOAT, (gl, v, l) => {
|
||||||
|
if (v instanceof Array) {
|
||||||
if (v.length == 0) {
|
if (v.length == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
gl.uniform1fv(l, new Float32Array(v));
|
gl.uniform1fv(l, new Float32Array(v));
|
||||||
|
} else {
|
||||||
|
gl.uniform1f(l, v);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
converters.set(
|
converters.set(
|
||||||
|
|
@ -39,13 +44,36 @@ export const loadUniform = (
|
||||||
result[2 * i + 1] = (v[i] as Array<number>).y;
|
result[2 * i + 1] = (v[i] as Array<number>).y;
|
||||||
}
|
}
|
||||||
|
|
||||||
gl.uniform2fv(l, new Float32Array(result));
|
gl.uniform2fv(l, result);
|
||||||
} else {
|
} else {
|
||||||
gl.uniform2fv(l, v as vec2);
|
gl.uniform2fv(l, v as vec2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
converters.set(
|
||||||
|
WebGL2RenderingContext.FLOAT_VEC3,
|
||||||
|
(gl, v: ReadonlyVec3 | Array<vec3>, l) => {
|
||||||
|
if (v.length == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (v[0] instanceof Array) {
|
||||||
|
const result = new Float32Array(v.length * 3);
|
||||||
|
|
||||||
|
for (let i = 0; i < v.length; i++) {
|
||||||
|
result[3 * i] = (v[i] as Array<number>)[0];
|
||||||
|
result[3 * i + 1] = (v[i] as Array<number>)[1];
|
||||||
|
result[3 * i + 2] = (v[i] as Array<number>)[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
gl.uniform3fv(l, result);
|
||||||
|
} else {
|
||||||
|
gl.uniform3fv(l, v as vec3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
converters.set(WebGL2RenderingContext.FLOAT_MAT3, (gl, v, l) => {
|
converters.set(WebGL2RenderingContext.FLOAT_MAT3, (gl, v, l) => {
|
||||||
gl.uniformMatrix3fv(l, true, mat3.fromMat2d(loaderMat3, v));
|
gl.uniformMatrix3fv(l, true, mat3.fromMat2d(loaderMat3, v));
|
||||||
});
|
});
|
||||||
|
|
@ -55,7 +83,7 @@ export const loadUniform = (
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!converters.has(type)) {
|
if (!converters.has(type)) {
|
||||||
throw new Error('Unimplemented webgl type');
|
throw new Error(`Unimplemented webgl type: ${type}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
converters.get(type)(gl, value, location);
|
converters.get(type)(gl, value, location);
|
||||||
|
|
|
||||||
|
|
@ -34,15 +34,15 @@ export class WebGl2Renderer implements Drawer {
|
||||||
new FragmentShaderOnlyProgram(this.gl, ...shaderSources[1]),
|
new FragmentShaderOnlyProgram(this.gl, ...shaderSources[1]),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
this.distanceFieldFrameBuffer.renderScale = 0.5;
|
this.distanceFieldFrameBuffer.renderScale = 0.1;
|
||||||
this.lightingFrameBuffer.renderScale = 1;
|
this.lightingFrameBuffer.renderScale = 0.2;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.stopwatch = new WebGlStopwatch(this.gl);
|
this.stopwatch = new WebGlStopwatch(this.gl);
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
public startFrame(): void {
|
public startFrame(deltaTime: DOMHighResTimeStamp): void {
|
||||||
this.stopwatch?.start();
|
this.stopwatch?.start();
|
||||||
this.uniforms = {};
|
this.uniforms = {};
|
||||||
this.distanceFieldFrameBuffer.setSize();
|
this.distanceFieldFrameBuffer.setSize();
|
||||||
|
|
@ -76,8 +76,6 @@ export class WebGl2Renderer implements Drawer {
|
||||||
mat2d.scale(ndcToWorld, ndcToWorld, vec2.fromValues(0.5, 0.5));
|
mat2d.scale(ndcToWorld, ndcToWorld, vec2.fromValues(0.5, 0.5));
|
||||||
mat2d.translate(ndcToWorld, ndcToWorld, vec2.fromValues(1, 1));
|
mat2d.translate(ndcToWorld, ndcToWorld, vec2.fromValues(1, 1));
|
||||||
|
|
||||||
const screenToWorld = this.getScreenToWorldTransform(resolution);
|
|
||||||
|
|
||||||
const worldToDistanceUV = mat2d.scale(
|
const worldToDistanceUV = mat2d.scale(
|
||||||
mat2d.create(),
|
mat2d.create(),
|
||||||
distanceScreenToWorld,
|
distanceScreenToWorld,
|
||||||
|
|
@ -85,11 +83,7 @@ export class WebGl2Renderer implements Drawer {
|
||||||
);
|
);
|
||||||
mat2d.invert(worldToDistanceUV, worldToDistanceUV);
|
mat2d.invert(worldToDistanceUV, worldToDistanceUV);
|
||||||
|
|
||||||
const cursorPosition = vec2.transformMat2d(
|
const cursorPosition = this.screenUvToWorldCoordinate(this.cursorPosition);
|
||||||
vec2.create(),
|
|
||||||
vec2.multiply(vec2.create(), this.cursorPosition, resolution),
|
|
||||||
screenToWorld
|
|
||||||
);
|
|
||||||
|
|
||||||
this.giveUniforms({
|
this.giveUniforms({
|
||||||
distanceScreenToWorld,
|
distanceScreenToWorld,
|
||||||
|
|
@ -115,6 +109,16 @@ export class WebGl2Renderer implements Drawer {
|
||||||
return transform;
|
return transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public screenUvToWorldCoordinate(screenUvPosition: vec2): vec2 {
|
||||||
|
const resolution = vec2.fromValues(this.canvas.width, this.canvas.height);
|
||||||
|
|
||||||
|
return vec2.transformMat2d(
|
||||||
|
vec2.create(),
|
||||||
|
vec2.multiply(vec2.create(), screenUvPosition, resolution),
|
||||||
|
this.getScreenToWorldTransform(resolution)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public setCameraPosition(position: vec2) {
|
public setCameraPosition(position: vec2) {
|
||||||
this.viewBox.topLeft = position;
|
this.viewBox.topLeft = position;
|
||||||
}
|
}
|
||||||
|
|
@ -123,6 +127,16 @@ export class WebGl2Renderer implements Drawer {
|
||||||
this.cursorPosition = position;
|
this.cursorPosition = position;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public appendToUniformList(listName: string, ...values: any[]): void {
|
||||||
|
if (!this.uniforms.hasOwnProperty(listName)) {
|
||||||
|
this.uniforms[listName] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let value of values) {
|
||||||
|
this.uniforms[listName].push(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public giveUniforms(uniforms: any): void {
|
public giveUniforms(uniforms: any): void {
|
||||||
this.uniforms = { ...this.uniforms, ...uniforms };
|
this.uniforms = { ...this.uniforms, ...uniforms };
|
||||||
}
|
}
|
||||||
|
|
@ -144,7 +158,7 @@ export class WebGl2Renderer implements Drawer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isOnScreen(position: vec2): boolean {
|
public isOnScreen(position: vec2): boolean {
|
||||||
return this.viewBox.isInside(position);
|
return this.viewBox.isInside(position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,9 @@ import { KeyboardListener } from './input/keyboard-listener';
|
||||||
import { MouseListener } from './input/mouse-listener';
|
import { MouseListener } from './input/mouse-listener';
|
||||||
import { TouchListener } from './input/touch-listener';
|
import { TouchListener } from './input/touch-listener';
|
||||||
import { ObjectContainer } from './objects/object-container';
|
import { ObjectContainer } from './objects/object-container';
|
||||||
import { Character } from './objects/types/character';
|
|
||||||
import { Dungeon } from './objects/types/dungeon';
|
|
||||||
import { InfoText } from './objects/types/info-text';
|
import { InfoText } from './objects/types/info-text';
|
||||||
|
import { createCharacter } from './objects/world/create-character';
|
||||||
|
import { createDungeon } from './objects/world/create-dungeon';
|
||||||
|
|
||||||
export class Game {
|
export class Game {
|
||||||
private previousTime: DOMHighResTimeStamp = 0;
|
private previousTime: DOMHighResTimeStamp = 0;
|
||||||
|
|
@ -46,9 +46,9 @@ export class Game {
|
||||||
}
|
}
|
||||||
|
|
||||||
private initializeScene() {
|
private initializeScene() {
|
||||||
this.objects.addObject(new Character(this.objects));
|
|
||||||
this.objects.addObject(new InfoText());
|
this.objects.addObject(new InfoText());
|
||||||
this.objects.addObject(new Dungeon());
|
createCharacter(this.objects);
|
||||||
|
createDungeon(this.objects);
|
||||||
}
|
}
|
||||||
|
|
||||||
@timeIt()
|
@timeIt()
|
||||||
|
|
@ -59,7 +59,7 @@ export class Game {
|
||||||
|
|
||||||
this.objects.sendCommand(new StepCommand(deltaTime));
|
this.objects.sendCommand(new StepCommand(deltaTime));
|
||||||
|
|
||||||
this.renderer.startFrame();
|
this.renderer.startFrame(deltaTime);
|
||||||
this.objects.sendCommand(new BeforeDrawCommand(this.renderer));
|
this.objects.sendCommand(new BeforeDrawCommand(this.renderer));
|
||||||
this.objects.sendCommand(new DrawCommand(this.renderer));
|
this.objects.sendCommand(new DrawCommand(this.renderer));
|
||||||
this.renderer.finishFrame();
|
this.renderer.finishFrame();
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,10 @@ export abstract class GameObject extends Typed implements CommandReceiver {
|
||||||
this.commandExecutors[commandType.name] = handler;
|
this.commandExecutors[commandType.name] = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public reactsToCommand<T extends Command>(commandType: new () => T): boolean {
|
||||||
|
return this.commandExecutors.hasOwnProperty(commandType.name);
|
||||||
|
}
|
||||||
|
|
||||||
public sendCommand(command: Command) {
|
public sendCommand(command: Command) {
|
||||||
const commandType = command.constructor.name;
|
const commandType = command.constructor.name;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,19 +5,16 @@ import { MoveToCommand } from '../../commands/types/move-to';
|
||||||
import { StepCommand } from '../../commands/types/step';
|
import { StepCommand } from '../../commands/types/step';
|
||||||
import { SwipeCommand } from '../../commands/types/swipe';
|
import { SwipeCommand } from '../../commands/types/swipe';
|
||||||
import { GameObject } from '../game-object';
|
import { GameObject } from '../game-object';
|
||||||
import { ObjectContainer } from '../object-container';
|
|
||||||
import { Camera } from './camera';
|
import { Camera } from './camera';
|
||||||
|
|
||||||
export class Character extends GameObject {
|
export class Character extends GameObject {
|
||||||
private keysDown: Set<string> = new Set();
|
private keysDown: Set<string> = new Set();
|
||||||
private camera = new Camera();
|
|
||||||
|
|
||||||
private static speed = 0.5;
|
private static speed = 0.5;
|
||||||
|
|
||||||
constructor(objects: ObjectContainer) {
|
constructor(private camera: Camera) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
objects.addObject(this.camera);
|
|
||||||
this.addCommandExecutor(StepCommand, this.stepHandler.bind(this));
|
this.addCommandExecutor(StepCommand, this.stepHandler.bind(this));
|
||||||
this.addCommandExecutor(KeyDownCommand, (c) => this.keysDown.add(c.key));
|
this.addCommandExecutor(KeyDownCommand, (c) => this.keysDown.add(c.key));
|
||||||
this.addCommandExecutor(KeyUpCommand, (c) => this.keysDown.delete(c.key));
|
this.addCommandExecutor(KeyUpCommand, (c) => this.keysDown.delete(c.key));
|
||||||
|
|
|
||||||
25
frontend/src/scripts/objects/types/circle-light.ts
Normal file
25
frontend/src/scripts/objects/types/circle-light.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { vec2, vec3 } from 'gl-matrix';
|
||||||
|
import { DrawCommand } from '../../commands/types/draw';
|
||||||
|
import { GameObject } from '../game-object';
|
||||||
|
|
||||||
|
export class CircleLight extends GameObject {
|
||||||
|
constructor(
|
||||||
|
private center: vec2,
|
||||||
|
private radius: number,
|
||||||
|
private value: vec3
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.addCommandExecutor(DrawCommand, this.draw.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
private draw(c: DrawCommand) {
|
||||||
|
if (c.drawer.isOnScreen(this.center)) {
|
||||||
|
c.drawer.appendToUniformList('lights', {
|
||||||
|
center: this.center,
|
||||||
|
radius: this.radius,
|
||||||
|
value: this.value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
30
frontend/src/scripts/objects/types/cursor-light.ts
Normal file
30
frontend/src/scripts/objects/types/cursor-light.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
import { vec2, vec3 } from 'gl-matrix';
|
||||||
|
import { CursorMoveCommand } from '../../commands/types/cursor-move-command';
|
||||||
|
import { DrawCommand } from '../../commands/types/draw';
|
||||||
|
import { GameObject } from '../game-object';
|
||||||
|
|
||||||
|
export class CursorLight extends GameObject {
|
||||||
|
private mousePosition = vec2.create();
|
||||||
|
|
||||||
|
constructor(private radius: number, private value: vec3) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.addCommandExecutor(DrawCommand, this.draw.bind(this));
|
||||||
|
this.addCommandExecutor(CursorMoveCommand, this.setPosition.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
private draw(c: DrawCommand) {
|
||||||
|
const center = c.drawer.screenUvToWorldCoordinate(this.mousePosition);
|
||||||
|
if (c.drawer.isOnScreen(center)) {
|
||||||
|
c.drawer.appendToUniformList('lights', {
|
||||||
|
center,
|
||||||
|
radius: this.radius,
|
||||||
|
value: this.value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private setPosition(c: CursorMoveCommand) {
|
||||||
|
this.mousePosition = c.position;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
import { vec2 } from 'gl-matrix';
|
|
||||||
import { DrawCommand } from '../../commands/types/draw';
|
|
||||||
import { GameObject } from '../game-object';
|
|
||||||
|
|
||||||
export interface Line {
|
|
||||||
start: vec2;
|
|
||||||
end: vec2;
|
|
||||||
radiusFrom: number;
|
|
||||||
radiusTo: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Dungeon extends GameObject {
|
|
||||||
private lines: Array<Line> = [];
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
|
|
||||||
this.addCommandExecutor(DrawCommand, this.draw.bind(this));
|
|
||||||
|
|
||||||
let previousRadius = 350;
|
|
||||||
let previousEnd = vec2.create();
|
|
||||||
|
|
||||||
for (let i = 0; i < 500000; i += 500) {
|
|
||||||
const height = previousEnd.y + (Math.random() - 0.5) * 2000;
|
|
||||||
const currentEnd = vec2.fromValues(i, height);
|
|
||||||
const currentToRadius = Math.random() * 300 + 150;
|
|
||||||
|
|
||||||
this.lines.push({
|
|
||||||
start: previousEnd,
|
|
||||||
end: currentEnd,
|
|
||||||
radiusFrom: previousRadius,
|
|
||||||
radiusTo: currentToRadius,
|
|
||||||
});
|
|
||||||
|
|
||||||
previousEnd = currentEnd;
|
|
||||||
previousRadius = currentToRadius;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private draw(c: DrawCommand) {
|
|
||||||
const lines: Array<vec2> = [];
|
|
||||||
const radii: Array<number> = [];
|
|
||||||
|
|
||||||
for (let line of this.lines) {
|
|
||||||
if (c.drawer.isOnScreen(line.start) || c.drawer.isOnScreen(line.end)) {
|
|
||||||
lines.push(line.start);
|
|
||||||
lines.push(line.end);
|
|
||||||
radii.push(line.radiusFrom);
|
|
||||||
radii.push(line.radiusTo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
c.drawer.giveUniforms({ lines, radii });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
25
frontend/src/scripts/objects/types/tunnel.ts
Normal file
25
frontend/src/scripts/objects/types/tunnel.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { vec2 } from 'gl-matrix';
|
||||||
|
import { DrawCommand } from '../../commands/types/draw';
|
||||||
|
import { GameObject } from '../game-object';
|
||||||
|
|
||||||
|
export interface Line {}
|
||||||
|
|
||||||
|
export class Tunnel extends GameObject {
|
||||||
|
constructor(
|
||||||
|
private from: vec2,
|
||||||
|
private to: vec2,
|
||||||
|
private radiusFrom: number,
|
||||||
|
private radiusTo: number
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.addCommandExecutor(DrawCommand, this.draw.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
private draw(c: DrawCommand) {
|
||||||
|
if (c.drawer.isOnScreen(this.from) || c.drawer.isOnScreen(this.to)) {
|
||||||
|
c.drawer.appendToUniformList('lines', this.from, this.to);
|
||||||
|
c.drawer.appendToUniformList('radii', this.radiusFrom, this.radiusTo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
12
frontend/src/scripts/objects/world/create-character.ts
Normal file
12
frontend/src/scripts/objects/world/create-character.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { vec3 } from 'gl-matrix';
|
||||||
|
import { ObjectContainer } from '../object-container';
|
||||||
|
import { Camera } from '../types/camera';
|
||||||
|
import { Character } from '../types/character';
|
||||||
|
import { CursorLight } from '../types/cursor-light';
|
||||||
|
|
||||||
|
export const createCharacter = (objects: ObjectContainer) => {
|
||||||
|
const camera = new Camera();
|
||||||
|
objects.addObject(camera);
|
||||||
|
objects.addObject(new Character(camera));
|
||||||
|
objects.addObject(new CursorLight(40, vec3.fromValues(0.67, 0.67, 0.33)));
|
||||||
|
};
|
||||||
37
frontend/src/scripts/objects/world/create-dungeon.ts
Normal file
37
frontend/src/scripts/objects/world/create-dungeon.ts
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
import { vec2, vec3 } from 'gl-matrix';
|
||||||
|
import { ObjectContainer } from '../object-container';
|
||||||
|
import { CircleLight } from '../types/circle-light';
|
||||||
|
import { Tunnel } from '../types/tunnel';
|
||||||
|
|
||||||
|
export const createDungeon = (objects: ObjectContainer) => {
|
||||||
|
let previousRadius = 350;
|
||||||
|
let previousEnd = vec2.create();
|
||||||
|
|
||||||
|
for (let i = 0; i < 500000; i += 500) {
|
||||||
|
const deltaHeight = (Math.random() - 0.5) * 2000;
|
||||||
|
const height = previousEnd.y + deltaHeight;
|
||||||
|
const currentEnd = vec2.fromValues(i, height);
|
||||||
|
const currentToRadius = Math.random() * 300 + 150;
|
||||||
|
|
||||||
|
objects.addObject(
|
||||||
|
new Tunnel(previousEnd, currentEnd, previousRadius, currentToRadius)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (deltaHeight > 0) {
|
||||||
|
objects.addObject(
|
||||||
|
new CircleLight(
|
||||||
|
currentEnd,
|
||||||
|
Math.random() * 20 + 30,
|
||||||
|
vec3.scale(
|
||||||
|
vec3.create(),
|
||||||
|
vec3.normalize(vec3.create(), vec3.random(vec3.create())),
|
||||||
|
Math.random() * 0.5 + 0.5
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
previousEnd = currentEnd;
|
||||||
|
previousRadius = currentToRadius;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -1,12 +1,10 @@
|
||||||
#version 300 es
|
#version 300 es
|
||||||
|
|
||||||
precision lowp float;
|
precision mediump float;
|
||||||
|
|
||||||
|
#define INFINITY 200.0
|
||||||
#define INFINITY 10000.0
|
|
||||||
#define LINE_COUNT 50
|
#define LINE_COUNT 50
|
||||||
|
|
||||||
|
|
||||||
uniform vec2[LINE_COUNT * 2] lines;
|
uniform vec2[LINE_COUNT * 2] lines;
|
||||||
uniform float[LINE_COUNT * 2] radii;
|
uniform float[LINE_COUNT * 2] radii;
|
||||||
|
|
||||||
|
|
@ -18,8 +16,9 @@ float lineDistance(
|
||||||
in float radiusTo
|
in float radiusTo
|
||||||
) {
|
) {
|
||||||
vec2 pa = target - start, ba = end - start;
|
vec2 pa = target - start, ba = end - start;
|
||||||
float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);
|
float baLength = length(ba);
|
||||||
return distance(pa, ba * h) - mix(radiusFrom, radiusTo, h);
|
float h = clamp(dot(pa / baLength, ba / baLength), 0.0, 1.0);
|
||||||
|
return length(pa - ba * h) - mix(radiusFrom, radiusTo, h);
|
||||||
}
|
}
|
||||||
|
|
||||||
float getDistance(in vec2 target) {
|
float getDistance(in vec2 target) {
|
||||||
|
|
|
||||||
|
|
@ -3,20 +3,18 @@
|
||||||
precision mediump float;
|
precision mediump float;
|
||||||
|
|
||||||
#define INFINITY 10000.0
|
#define INFINITY 10000.0
|
||||||
#define LIGHT_COUNT 3
|
#define LIGHT_COUNT 5
|
||||||
#define AMBIENT_LIGHT vec3(0.05)
|
#define AMBIENT_LIGHT vec3(0.05)
|
||||||
#define LIGHT_DROP 800.0
|
#define LIGHT_DROP 800.0
|
||||||
#define SHADOW_BIAS 0.01
|
#define SHADOW_BIAS 0.01
|
||||||
|
|
||||||
struct Light {
|
uniform struct Light {
|
||||||
vec2 center;
|
vec2 center;
|
||||||
float radius;
|
float radius;
|
||||||
vec3 value;
|
vec3 value;
|
||||||
}[LIGHT_COUNT] lights;
|
}[LIGHT_COUNT] lights;
|
||||||
|
|
||||||
uniform sampler2D distanceTexture;
|
uniform sampler2D distanceTexture;
|
||||||
uniform mat3 worldToDistanceUV;
|
|
||||||
uniform vec2 cursorPosition;
|
|
||||||
uniform vec2 viewBoxSize;
|
uniform vec2 viewBoxSize;
|
||||||
|
|
||||||
float square(in float a) {
|
float square(in float a) {
|
||||||
|
|
@ -24,26 +22,13 @@ float square(in float a) {
|
||||||
}
|
}
|
||||||
|
|
||||||
float getDistance(in vec2 target, out vec3 color) {
|
float getDistance(in vec2 target, out vec3 color) {
|
||||||
// should avoid this matrix multiplication
|
vec4 values = texture(distanceTexture, target);
|
||||||
vec2 targetUV = (vec3(target.xy, 1.0) * worldToDistanceUV).xy;
|
|
||||||
|
|
||||||
vec4 values = texture(distanceTexture, targetUV);
|
|
||||||
color = values.rgb;
|
color = values.rgb;
|
||||||
return values.w * 32.0;
|
return values.w * 32.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
float getDistance(in vec2 target) {
|
float getDistance(in vec2 target) {
|
||||||
// should avoid this matrix multiplication
|
return texture(distanceTexture, target).w * 32.0;
|
||||||
vec2 targetUV = (vec3(target.xy, 1.0) * worldToDistanceUV).xy;
|
|
||||||
|
|
||||||
vec4 values = texture(distanceTexture, targetUV);
|
|
||||||
return values.w * 32.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void createWorld() {
|
|
||||||
//lights[0] = Light(vec2(600, 700), 40.5, normalize(vec3(1.0)) * 2.0);
|
|
||||||
//lights[1] = Light(vec2(100.0, 350.0), 52.5, normalize(vec3(2.0, 1.0, 0.25)) * 0.5);
|
|
||||||
lights[2] = Light(cursorPosition, 52.5, normalize(vec3(0.93, 0.25, 0.5)) * 1.0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float getFractionOfLightArriving(
|
float getFractionOfLightArriving(
|
||||||
|
|
@ -53,23 +38,25 @@ float getFractionOfLightArriving(
|
||||||
in float lightDistance,
|
in float lightDistance,
|
||||||
in float lightRadius
|
in float lightRadius
|
||||||
) {
|
) {
|
||||||
float q = INFINITY;
|
float q = 1.0;
|
||||||
float rayLength = 0.0;
|
float rayLength = startingDistance;
|
||||||
|
|
||||||
float movingAverageMeanDistance = startingDistance;
|
float movingAverageMeanDistance = startingDistance;
|
||||||
|
|
||||||
|
direction /= viewBoxSize;
|
||||||
|
|
||||||
for (int j = 0; j < 64; j++) {
|
for (int j = 0; j < 64; j++) {
|
||||||
float minDistance = getDistance(target + direction * rayLength);
|
float minDistance = getDistance(target + direction * rayLength);
|
||||||
movingAverageMeanDistance = movingAverageMeanDistance / 2.0 + minDistance / 2.0;
|
movingAverageMeanDistance = movingAverageMeanDistance / 2.0 + minDistance / 2.0;
|
||||||
q = min(q, movingAverageMeanDistance / rayLength);
|
q = min(q, movingAverageMeanDistance / rayLength);
|
||||||
rayLength = min(lightDistance, rayLength + max(0.0001, minDistance));
|
rayLength = min(lightDistance, rayLength + max(1.0, minDistance));
|
||||||
}
|
}
|
||||||
|
|
||||||
return smoothstep(0.0, 1.0, (q - SHADOW_BIAS) * (lightDistance + lightRadius) / lightRadius);
|
return smoothstep(0.0, 1.0, (q - SHADOW_BIAS) * (lightDistance + lightRadius) / lightRadius);
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 getPixelColor(in vec2 worldCoordinates, in vec2 uvCoordinates) {
|
vec3 getPixelColor(in vec2 worldCoordinates, in vec2 uvCoordinates) {
|
||||||
vec3 colorAtPosition;
|
vec3 colorAtPosition;
|
||||||
float startingDistance = getDistance(worldCoordinates, colorAtPosition);
|
float startingDistance = getDistance(uvCoordinates, colorAtPosition);
|
||||||
|
|
||||||
vec3 result = colorAtPosition * AMBIENT_LIGHT;
|
vec3 result = colorAtPosition * AMBIENT_LIGHT;
|
||||||
|
|
||||||
|
|
@ -81,7 +68,7 @@ vec3 getPixelColor(in vec2 worldCoordinates, in vec2 uvCoordinates) {
|
||||||
vec2 lightDirection = normalize(light.center - worldCoordinates);
|
vec2 lightDirection = normalize(light.center - worldCoordinates);
|
||||||
|
|
||||||
float fractionOfLightArriving = getFractionOfLightArriving(
|
float fractionOfLightArriving = getFractionOfLightArriving(
|
||||||
worldCoordinates, lightDirection, startingDistance,
|
uvCoordinates, lightDirection, startingDistance,
|
||||||
max(0.0, lightDistance), light.radius
|
max(0.0, lightDistance), light.radius
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -91,13 +78,10 @@ vec3 getPixelColor(in vec2 worldCoordinates, in vec2 uvCoordinates) {
|
||||||
return clamp(result, 0.0, 1.0);
|
return clamp(result, 0.0, 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
in vec2 worldCoordinates;
|
in vec2 worldCoordinates;
|
||||||
in vec2 uvCoordinates;
|
in vec2 uvCoordinates;
|
||||||
out vec4 fragmentColor;
|
out vec4 fragmentColor;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
createWorld();
|
|
||||||
|
|
||||||
fragmentColor = vec4(getPixelColor(worldCoordinates, uvCoordinates), 1.0);
|
fragmentColor = vec4(getPixelColor(worldCoordinates, uvCoordinates), 1.0);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue