Add lighting shader
This commit is contained in:
parent
e88b4589d2
commit
0cd383794c
11 changed files with 144 additions and 295 deletions
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { Command } from '../command';
|
||||||
|
import { vec2 } from 'gl-matrix';
|
||||||
|
|
||||||
|
export class CursorMoveCommand extends Command {
|
||||||
|
public constructor(public readonly position?: vec2) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,15 +6,17 @@ import { Rectangle } from '../math/rectangle';
|
||||||
import { IntermediateFrameBuffer } from './graphics-library/intermediate-frame-buffer';
|
import { IntermediateFrameBuffer } from './graphics-library/intermediate-frame-buffer';
|
||||||
import { FrameBuffer } from './graphics-library/frame-buffer';
|
import { FrameBuffer } from './graphics-library/frame-buffer';
|
||||||
import { DefaultFrameBuffer } from './graphics-library/default-frame-buffer';
|
import { DefaultFrameBuffer } from './graphics-library/default-frame-buffer';
|
||||||
|
import { translate } from 'gl-matrix/src/gl-matrix/mat2d';
|
||||||
|
|
||||||
export class WebGl2Renderer implements Drawer {
|
export class WebGl2Renderer implements Drawer {
|
||||||
private gl: WebGL2RenderingContext;
|
private gl: WebGL2RenderingContext;
|
||||||
private stopwatch: WebGlStopwatch;
|
private stopwatch: WebGlStopwatch;
|
||||||
|
|
||||||
private viewBox: Rectangle = new Rectangle();
|
private viewBox: Rectangle = new Rectangle();
|
||||||
private nextFrameUniforms: any;
|
private uniforms: any;
|
||||||
private cursorPosition = vec2.create();
|
private cursorPosition = vec2.create();
|
||||||
private frameBuffers: Array<FrameBuffer> = [];
|
private distanceFieldFrameBuffer: IntermediateFrameBuffer;
|
||||||
|
private lightingFrameBuffer: DefaultFrameBuffer;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private canvas: HTMLCanvasElement,
|
private canvas: HTMLCanvasElement,
|
||||||
|
|
@ -26,20 +28,16 @@ export class WebGl2Renderer implements Drawer {
|
||||||
throw new Error('WebGl2 is not supported');
|
throw new Error('WebGl2 is not supported');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.frameBuffers.push(
|
this.distanceFieldFrameBuffer = new IntermediateFrameBuffer(this.gl, [
|
||||||
new IntermediateFrameBuffer(this.gl, [
|
new FragmentShaderOnlyProgram(this.gl, shaderSources[0]),
|
||||||
new FragmentShaderOnlyProgram(this.gl, shaderSources[0]),
|
]);
|
||||||
])
|
|
||||||
);
|
|
||||||
|
|
||||||
this.frameBuffers.push(
|
this.lightingFrameBuffer = new DefaultFrameBuffer(this.gl, [
|
||||||
new DefaultFrameBuffer(this.gl, [
|
new FragmentShaderOnlyProgram(this.gl, shaderSources[1]),
|
||||||
new FragmentShaderOnlyProgram(this.gl, shaderSources[1]),
|
]);
|
||||||
])
|
|
||||||
);
|
|
||||||
|
|
||||||
this.frameBuffers[0].renderScale = 1;
|
this.distanceFieldFrameBuffer.renderScale = 0.2;
|
||||||
this.frameBuffers[1].renderScale = 1;
|
this.lightingFrameBuffer.renderScale = 1;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.stopwatch = new WebGlStopwatch(this.gl);
|
this.stopwatch = new WebGlStopwatch(this.gl);
|
||||||
|
|
@ -48,13 +46,54 @@ export class WebGl2Renderer implements Drawer {
|
||||||
|
|
||||||
startFrame(): void {
|
startFrame(): void {
|
||||||
this.stopwatch?.start();
|
this.stopwatch?.start();
|
||||||
this.nextFrameUniforms = {};
|
this.uniforms = {};
|
||||||
this.frameBuffers.forEach((f) => f.setSize());
|
this.distanceFieldFrameBuffer.setSize();
|
||||||
|
this.lightingFrameBuffer.setSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
public finishFrame() {
|
public finishFrame() {
|
||||||
const resolution = vec2.fromValues(this.canvas.width, this.canvas.height);
|
const resolution = vec2.fromValues(this.canvas.width, this.canvas.height);
|
||||||
|
|
||||||
|
const distanceScreenToWorld = this.getScreenToWorldTransform(
|
||||||
|
this.distanceFieldFrameBuffer.getSize()
|
||||||
|
);
|
||||||
|
|
||||||
|
const lightingScreenToWorld = this.getScreenToWorldTransform(
|
||||||
|
this.lightingFrameBuffer.getSize()
|
||||||
|
);
|
||||||
|
|
||||||
|
const screenToWorld = this.getScreenToWorldTransform(resolution);
|
||||||
|
|
||||||
|
const worldToDistanceUV = mat2d.scale(
|
||||||
|
mat2d.create(),
|
||||||
|
distanceScreenToWorld,
|
||||||
|
this.distanceFieldFrameBuffer.getSize()
|
||||||
|
);
|
||||||
|
mat2d.invert(worldToDistanceUV, worldToDistanceUV);
|
||||||
|
|
||||||
|
const cursorPosition = vec2.transformMat2d(
|
||||||
|
vec2.create(),
|
||||||
|
vec2.multiply(vec2.create(), this.cursorPosition, resolution),
|
||||||
|
screenToWorld
|
||||||
|
);
|
||||||
|
|
||||||
|
this.giveUniforms({
|
||||||
|
distanceScreenToWorld,
|
||||||
|
lightingScreenToWorld,
|
||||||
|
worldToDistanceUV,
|
||||||
|
cursorPosition,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.distanceFieldFrameBuffer.render(this.uniforms);
|
||||||
|
this.lightingFrameBuffer.render(
|
||||||
|
this.uniforms,
|
||||||
|
this.distanceFieldFrameBuffer.texture
|
||||||
|
);
|
||||||
|
|
||||||
|
this.stopwatch?.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
private getScreenToWorldTransform(screenSize: vec2) {
|
||||||
const transform = mat2d.fromTranslation(
|
const transform = mat2d.fromTranslation(
|
||||||
mat2d.create(),
|
mat2d.create(),
|
||||||
this.viewBox.topLeft
|
this.viewBox.topLeft
|
||||||
|
|
@ -62,29 +101,11 @@ export class WebGl2Renderer implements Drawer {
|
||||||
mat2d.scale(
|
mat2d.scale(
|
||||||
transform,
|
transform,
|
||||||
transform,
|
transform,
|
||||||
vec2.divide(
|
vec2.divide(vec2.create(), this.viewBox.size, screenSize)
|
||||||
vec2.create(),
|
|
||||||
this.viewBox.size,
|
|
||||||
this.frameBuffers[0].getSize()
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
mat2d.translate(transform, transform, vec2.fromValues(0.5, 0.5));
|
mat2d.translate(transform, transform, vec2.fromValues(0.5, 0.5));
|
||||||
this.nextFrameUniforms.transform = transform;
|
|
||||||
|
|
||||||
const transformUV = mat2d.fromScaling(
|
return transform;
|
||||||
mat2d.create(),
|
|
||||||
vec2.divide(vec2.create(), vec2.fromValues(1, 1), resolution)
|
|
||||||
);
|
|
||||||
mat2d.translate(transformUV, transformUV, vec2.fromValues(0.5, 0.5));
|
|
||||||
this.nextFrameUniforms.transformUV = transformUV;
|
|
||||||
|
|
||||||
this.frameBuffers[0].render(this.nextFrameUniforms);
|
|
||||||
this.frameBuffers[1].render(
|
|
||||||
this.nextFrameUniforms,
|
|
||||||
(this.frameBuffers[0] as IntermediateFrameBuffer).texture
|
|
||||||
);
|
|
||||||
|
|
||||||
this.stopwatch?.stop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public setCameraPosition(position: vec2) {
|
public setCameraPosition(position: vec2) {
|
||||||
|
|
@ -96,7 +117,7 @@ export class WebGl2Renderer implements Drawer {
|
||||||
}
|
}
|
||||||
|
|
||||||
public giveUniforms(uniforms: any): void {
|
public giveUniforms(uniforms: any): void {
|
||||||
this.nextFrameUniforms = { ...this.nextFrameUniforms, ...uniforms };
|
this.uniforms = { ...this.uniforms, ...uniforms };
|
||||||
}
|
}
|
||||||
|
|
||||||
public setInViewArea(size: number): vec2 {
|
public setInViewArea(size: number): vec2 {
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,8 @@ import { InfoText } from './objects/types/info-text';
|
||||||
import { timeIt } from './helper/timing';
|
import { timeIt } from './helper/timing';
|
||||||
|
|
||||||
import caveFragmentShader from '../shaders/cave-distance-fs.glsl';
|
import caveFragmentShader from '../shaders/cave-distance-fs.glsl';
|
||||||
import lightsShader from '../shaders/rainbow-shading-fs.glsl';
|
// import lightsShader from '../shaders/rainbow-shading-fs.glsl';
|
||||||
//import lightsShader from '../shaders/lights-shading-fs.glsl';
|
import lightsShader from '../shaders/lights-shading-fs.glsl';
|
||||||
import { Dungeon } from './objects/types/dungeon';
|
import { Dungeon } from './objects/types/dungeon';
|
||||||
import { BeforeDrawCommand } from './commands/types/before-draw';
|
import { BeforeDrawCommand } from './commands/types/before-draw';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { SwipeCommand } from '../commands/types/swipe';
|
||||||
import { ZoomCommand } from '../commands/types/zoom';
|
import { ZoomCommand } from '../commands/types/zoom';
|
||||||
import { vec2 } from 'gl-matrix';
|
import { vec2 } from 'gl-matrix';
|
||||||
import { clamp01 } from '../helper/clamp';
|
import { clamp01 } from '../helper/clamp';
|
||||||
|
import { CursorMoveCommand } from '../commands/types/cursor-move-command';
|
||||||
|
|
||||||
export class MouseListener extends CommandGenerator {
|
export class MouseListener extends CommandGenerator {
|
||||||
private previousPosition = vec2.create();
|
private previousPosition = vec2.create();
|
||||||
|
|
@ -25,8 +26,9 @@ export class MouseListener extends CommandGenerator {
|
||||||
});
|
});
|
||||||
|
|
||||||
target.addEventListener('mousemove', (event: MouseEvent) => {
|
target.addEventListener('mousemove', (event: MouseEvent) => {
|
||||||
|
const position = this.positionFromEvent(event);
|
||||||
|
|
||||||
if (this.isMouseDown) {
|
if (this.isMouseDown) {
|
||||||
const position = this.positionFromEvent(event);
|
|
||||||
this.sendCommand(
|
this.sendCommand(
|
||||||
new SwipeCommand(
|
new SwipeCommand(
|
||||||
vec2.subtract(vec2.create(), this.previousPosition, position)
|
vec2.subtract(vec2.create(), this.previousPosition, position)
|
||||||
|
|
@ -34,6 +36,8 @@ export class MouseListener extends CommandGenerator {
|
||||||
);
|
);
|
||||||
this.previousPosition = position;
|
this.previousPosition = position;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.sendCommand(new CursorMoveCommand(position));
|
||||||
});
|
});
|
||||||
|
|
||||||
target.addEventListener('mouseup', (event: MouseEvent) => {
|
target.addEventListener('mouseup', (event: MouseEvent) => {
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { ZoomCommand } from '../../commands/types/zoom';
|
||||||
import { BeforeDrawCommand } from '../../commands/types/before-draw';
|
import { BeforeDrawCommand } from '../../commands/types/before-draw';
|
||||||
import { PrimaryActionCommand } from '../../commands/types/primary-action';
|
import { PrimaryActionCommand } from '../../commands/types/primary-action';
|
||||||
import { vec2 } from 'gl-matrix';
|
import { vec2 } from 'gl-matrix';
|
||||||
|
import { CursorMoveCommand } from '../../commands/types/cursor-move-command';
|
||||||
|
|
||||||
export class Camera extends GameObject {
|
export class Camera extends GameObject {
|
||||||
private inViewArea = 1920 * 1080;
|
private inViewArea = 1920 * 1080;
|
||||||
|
|
@ -14,11 +15,11 @@ export class Camera extends GameObject {
|
||||||
|
|
||||||
this.addCommandExecutor(BeforeDrawCommand, this.draw.bind(this));
|
this.addCommandExecutor(BeforeDrawCommand, this.draw.bind(this));
|
||||||
this.addCommandExecutor(MoveToCommand, this.moveTo.bind(this));
|
this.addCommandExecutor(MoveToCommand, this.moveTo.bind(this));
|
||||||
this.addCommandExecutor(ZoomCommand, this.zoom.bind(this));
|
|
||||||
this.addCommandExecutor(
|
this.addCommandExecutor(
|
||||||
PrimaryActionCommand,
|
CursorMoveCommand,
|
||||||
this.setCursorPosition.bind(this)
|
this.setCursorPosition.bind(this)
|
||||||
);
|
);
|
||||||
|
this.addCommandExecutor(ZoomCommand, this.zoom.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
private draw(c: BeforeDrawCommand) {
|
private draw(c: BeforeDrawCommand) {
|
||||||
|
|
@ -35,7 +36,7 @@ export class Camera extends GameObject {
|
||||||
this.inViewArea *= c.factor;
|
this.inViewArea *= c.factor;
|
||||||
}
|
}
|
||||||
|
|
||||||
private setCursorPosition(c: PrimaryActionCommand) {
|
private setCursorPosition(c: CursorMoveCommand) {
|
||||||
this.cursorPosition = c.position;
|
this.cursorPosition = c.position;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,10 +20,10 @@ export class Dungeon extends GameObject {
|
||||||
let previousRadius = 0;
|
let previousRadius = 0;
|
||||||
let previousEnd = vec2.create();
|
let previousEnd = vec2.create();
|
||||||
|
|
||||||
for (let i = 0; i < 5000; i += 50) {
|
for (let i = 0; i < 500000; i += 500) {
|
||||||
const height = previousEnd.y + (Math.random() - 0.5) * 200;
|
const height = previousEnd.y + (Math.random() - 0.5) * 2000;
|
||||||
const currentEnd = vec2.fromValues(i, height);
|
const currentEnd = vec2.fromValues(i, height);
|
||||||
const currentToRadius = Math.random() * 10 + 30;
|
const currentToRadius = Math.random() * 10 + 300;
|
||||||
|
|
||||||
this.lines.push({
|
this.lines.push({
|
||||||
start: previousEnd,
|
start: previousEnd,
|
||||||
|
|
|
||||||
|
|
@ -36,11 +36,11 @@ float getDistance(in vec2 target) {
|
||||||
return -minDistance;
|
return -minDistance;
|
||||||
}
|
}
|
||||||
|
|
||||||
uniform mat3 transform;
|
uniform mat3 distanceScreenToWorld;
|
||||||
out vec4 fragmentColor;
|
out vec4 fragmentColor;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec2 position = (vec3(gl_FragCoord.xy, 1.0) * transform).xy;
|
vec2 position = (vec3(gl_FragCoord.xy, 1.0) * distanceScreenToWorld).xy;
|
||||||
float distance = getDistance(position);
|
float distance = getDistance(position);
|
||||||
fragmentColor = vec4(vec3(0.0), distance / 256.0 + 0.5);
|
fragmentColor = vec4(vec3(0.0), distance / 256.0 + 0.5);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,75 +0,0 @@
|
||||||
#version 300 es
|
|
||||||
|
|
||||||
precision mediump float;
|
|
||||||
|
|
||||||
#define SMOOTHING 10.0
|
|
||||||
#define INFINITY 10000.0;
|
|
||||||
#define LINE_COUNT 100
|
|
||||||
|
|
||||||
|
|
||||||
float interpolate(float from, float to, float quotient) {
|
|
||||||
return from + (to - from) * smoothstep(0.0, 1.0, quotient);
|
|
||||||
}
|
|
||||||
|
|
||||||
vec2 rotate90deg(in vec2 vector) {
|
|
||||||
return vec2(-vector.y, vector.x);
|
|
||||||
}
|
|
||||||
|
|
||||||
uniform struct Line {
|
|
||||||
vec2 from;
|
|
||||||
vec2 to;
|
|
||||||
vec2 normal;
|
|
||||||
bool isLineEnd;
|
|
||||||
}[LINE_COUNT] lines;
|
|
||||||
|
|
||||||
float lineDistance(in vec2 position, in Line line, out float h) {
|
|
||||||
vec2 pa = position - line.from, ba = line.to - line.from;
|
|
||||||
h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);
|
|
||||||
vec2 delta = pa - ba*h;
|
|
||||||
// sign can return 0, double sign prevents this
|
|
||||||
float side = sign(sign(dot(delta, line.normal)) - 0.5);
|
|
||||||
return length(delta) * side;
|
|
||||||
}
|
|
||||||
|
|
||||||
float getDistance(in vec2 target) {
|
|
||||||
float positiveMinDistance = INFINITY;
|
|
||||||
float negativeMaxDistance = INFINITY;
|
|
||||||
|
|
||||||
float leftJoinAcuteness = 0.0;
|
|
||||||
vec2 splitterLineNormalStart = vec2(-1.0, 0.0);
|
|
||||||
for (int i = 0; i < LINE_COUNT - 1; i++) {
|
|
||||||
vec2 splitterLineNormalEnd = rotate90deg(normalize(lines[i].normal + lines[i + 1].normal));
|
|
||||||
|
|
||||||
float h;
|
|
||||||
float distanceToCurrent = lineDistance(target, lines[i], h);
|
|
||||||
float rightJoinAcuteness = dot(lines[i + 1].to - lines[i].from, lines[i + 1].normal - lines[i].normal);
|
|
||||||
distanceToCurrent -= interpolate(
|
|
||||||
sign(leftJoinAcuteness) * SMOOTHING,
|
|
||||||
sign(rightJoinAcuteness) * SMOOTHING, h
|
|
||||||
);
|
|
||||||
leftJoinAcuteness = rightJoinAcuteness;
|
|
||||||
|
|
||||||
if (
|
|
||||||
!(
|
|
||||||
dot(target - lines[i].from, splitterLineNormalStart * -sign(dot(lines[i].to - lines[i].from, splitterLineNormalStart))) > 0.0
|
|
||||||
|| dot(target - lines[i].to, splitterLineNormalEnd * sign(dot(lines[i].from - lines[i].to, splitterLineNormalEnd))) <= 0.0
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
float distanceToCurrentSign = sign(distanceToCurrent) / 2.0;
|
|
||||||
positiveMinDistance = min(positiveMinDistance, 1.0 / (0.5 + distanceToCurrentSign) * abs(distanceToCurrent));
|
|
||||||
negativeMaxDistance = min(negativeMaxDistance, 1.0 / (0.5 - distanceToCurrentSign) * abs(distanceToCurrent));
|
|
||||||
}
|
|
||||||
splitterLineNormalStart = splitterLineNormalEnd;
|
|
||||||
}
|
|
||||||
|
|
||||||
return positiveMinDistance < negativeMaxDistance ? positiveMinDistance : -negativeMaxDistance;
|
|
||||||
}
|
|
||||||
|
|
||||||
uniform mat3 transform;
|
|
||||||
out vec4 fragmentColor;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec2 position = (vec3(gl_FragCoord.xy, 1.0) * transform).xy;
|
|
||||||
fragmentColor = vec4(vec3(1.0) * clamp(0.0, 1.0, getDistance(position)), 1.0);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -2,21 +2,13 @@
|
||||||
|
|
||||||
precision mediump float;
|
precision mediump float;
|
||||||
|
|
||||||
#define INFINITY 1.0 / 0.0
|
#define INFINITY 10000.0
|
||||||
|
|
||||||
#define WORLD_SIZE 4
|
#define LIGHTS_SIZE 3
|
||||||
#define LIGHTS_SIZE 2
|
|
||||||
|
|
||||||
#define LIGHT_PENETRATION 0.95
|
#define LIGHT_PENETRATION 0.95
|
||||||
#define ANTIALIASING_RADIUS 1.0
|
#define ANTIALIASING_RADIUS 1.0
|
||||||
|
|
||||||
uniform vec2 resolution;
|
|
||||||
uniform vec2 mouse;
|
|
||||||
uniform sampler2D distanceTexture;
|
|
||||||
uniform mat3 transformUV;
|
|
||||||
|
|
||||||
out vec4 fragmentColor;
|
|
||||||
|
|
||||||
struct Light {
|
struct Light {
|
||||||
vec2 center;
|
vec2 center;
|
||||||
float radius;
|
float radius;
|
||||||
|
|
@ -24,62 +16,38 @@ struct Light {
|
||||||
float intensity;
|
float intensity;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Circle {
|
uniform sampler2D distanceTexture;
|
||||||
vec2 center;
|
uniform mat3 worldToDistanceUV;
|
||||||
float radius;
|
uniform mat3 lightingScreenToWorld;
|
||||||
vec3 color;
|
uniform vec2 cursorPosition;
|
||||||
};
|
|
||||||
|
|
||||||
Light lights[LIGHTS_SIZE];
|
Light lights[LIGHTS_SIZE];
|
||||||
Circle world[WORLD_SIZE];
|
|
||||||
|
|
||||||
vec3 red = vec3(5.0, 0.0, 2.0);
|
|
||||||
vec3 blue = vec3(0.0, 0.0, 3.0);
|
|
||||||
|
|
||||||
float circleDistance(in vec2 position, in Circle circle)
|
|
||||||
{
|
|
||||||
return length(position - circle.center) - circle.radius;
|
|
||||||
}
|
|
||||||
|
|
||||||
float circleDistance(in vec2 position, in Light circle)
|
float circleDistance(in vec2 position, in Light circle)
|
||||||
{
|
{
|
||||||
return length(position - circle.center) - circle.radius;
|
return length(position - circle.center) - circle.radius;
|
||||||
}
|
}
|
||||||
|
|
||||||
float getDistance(in vec2 target) {
|
float getDistance(in vec2 target, out vec3 color) {
|
||||||
float distance = INFINITY;
|
vec2 targetUV = (vec3(target.xy, 1.0) * worldToDistanceUV).xy;
|
||||||
for (int i = 0; i < WORLD_SIZE; i++) {
|
vec4 values = texture(distanceTexture, targetUV);
|
||||||
distance = min(distance, circleDistance(target, world[i]));
|
color = values.rgb;
|
||||||
}
|
return (values.a - 0.5) * 256.0;
|
||||||
return distance;
|
|
||||||
}
|
|
||||||
|
|
||||||
float getDistance(in vec2 target, out Circle nearest) {
|
|
||||||
float distance = INFINITY;
|
|
||||||
for (int i = 0; i < WORLD_SIZE; i++) {
|
|
||||||
float distanceToCurrent = circleDistance(target, world[i]);
|
|
||||||
if (distanceToCurrent < distance) {
|
|
||||||
distance = distanceToCurrent;
|
|
||||||
nearest = world[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return distance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void createWorld() {
|
void createWorld() {
|
||||||
lights[0] = Light(mouse, 40.5, vec3(1.0), 25.0);
|
lights[0] = Light(vec2(600, 700), 40.5, vec3(1.0), 25.0);
|
||||||
lights[1] = Light(vec2(100.0, 350.0), 52.5,vec3(2.0, 1.0, 0.25), 20.5);
|
lights[1] = Light(vec2(100.0, 350.0), 52.5,vec3(2.0, 1.0, 0.25), 20.5);
|
||||||
|
lights[2] = Light(cursorPosition, 52.5,vec3(0.63, 0.07, 0.19), 200.5);
|
||||||
world[0] = Circle(vec2(250.0, 100.0), 12.5, blue);
|
|
||||||
world[1] = Circle(vec2(150.0, 50.0), 32.5, red);
|
|
||||||
world[2] = Circle(vec2(300.0, 350.0), 52.5, blue);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float escapeFromObject(inout vec2 position, in vec2 direction) {
|
float escapeFromObject(inout vec2 position, in vec2 direction) {
|
||||||
float fractionOfLightPenetrating = 1.0;
|
float fractionOfLightPenetrating = 1.0;
|
||||||
float rayLength = 0.0;
|
float rayLength = 0.0;
|
||||||
for (int i = 0; i < 64; i++) {
|
for (int i = 0; i < 64; i++) {
|
||||||
float minDistance = getDistance(position);
|
vec3 color;
|
||||||
|
float minDistance = getDistance(position, color);
|
||||||
if (minDistance >= 0.0) {
|
if (minDistance >= 0.0) {
|
||||||
return fractionOfLightPenetrating;
|
return fractionOfLightPenetrating;
|
||||||
}
|
}
|
||||||
|
|
@ -94,10 +62,11 @@ float escapeFromObject(inout vec2 position, in vec2 direction) {
|
||||||
|
|
||||||
float getFractionOfLightArriving(in vec2 position, in vec2 direction, in float lightDistance, in float lightRadius) {
|
float getFractionOfLightArriving(in vec2 position, in vec2 direction, in float lightDistance, in float lightRadius) {
|
||||||
float fractionOfLightArriving = 1.0;
|
float fractionOfLightArriving = 1.0;
|
||||||
|
vec3 color;
|
||||||
|
|
||||||
float rayLength = 0.0;
|
float rayLength = 0.0;
|
||||||
for (int j = 0; j < 64; j++) {
|
for (int j = 0; j < 64; j++) {
|
||||||
float minDistance = getDistance(position + direction * rayLength);
|
float minDistance = getDistance(position + direction * rayLength, color);
|
||||||
fractionOfLightArriving = min(fractionOfLightArriving, minDistance / rayLength);
|
fractionOfLightArriving = min(fractionOfLightArriving, minDistance / rayLength);
|
||||||
rayLength += max(1.0, abs(minDistance));
|
rayLength += max(1.0, abs(minDistance));
|
||||||
|
|
||||||
|
|
@ -110,25 +79,27 @@ float getFractionOfLightArriving(in vec2 position, in vec2 direction, in float l
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 getPixelColor(in vec2 position, in bool startsInside, in vec3 colorBias) {
|
vec3 getPixelColor(in vec2 targetLighting, in bool startsInside, in vec3 colorBias) {
|
||||||
vec3 result = vec3(0.0);
|
vec3 result = vec3(0.0);
|
||||||
|
|
||||||
for (int i = 0; i < LIGHTS_SIZE; i++) {
|
for (int i = 0; i < LIGHTS_SIZE; i++) {
|
||||||
Light light = lights[i];
|
Light light = lights[i];
|
||||||
|
|
||||||
float lightDistance = circleDistance(position, light);
|
float lightDistance = circleDistance(targetLighting, light);
|
||||||
vec3 lightColor = normalize(light.color) * light.intensity / mix(1.0, lightDistance, clamp(lightDistance, 0.0, 1.0));
|
vec3 lightColor = normalize(light.color) * light.intensity
|
||||||
|
/ mix(1.0, lightDistance, clamp(lightDistance, 0.0, 1.0));
|
||||||
|
|
||||||
if (lightDistance < 0.0) {
|
if (lightDistance < 0.0) {
|
||||||
return lightColor;
|
return lightColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
vec2 lightDirection = normalize(light.center - position);
|
vec2 lightDirection = normalize(light.center - targetLighting);
|
||||||
vec2 rayStart = position;
|
vec2 rayStart = targetLighting;
|
||||||
|
|
||||||
|
|
||||||
float fractionOfLightPenetrating = 1.0;
|
float fractionOfLightPenetrating = 1.0;
|
||||||
if (startsInside) {
|
if (startsInside) {
|
||||||
fractionOfLightPenetrating = escapeFromObject(rayStart, lightDirection);
|
fractionOfLightPenetrating = escapeFromObject(rayStart, lightDirection);
|
||||||
lightColor *= colorBias;
|
lightColor *= colorBias;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -139,7 +110,8 @@ vec3 getPixelColor(in vec2 position, in bool startsInside, in vec3 colorBias) {
|
||||||
return clamp(result, 0.0, 1.0);
|
return clamp(result, 0.0, 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 getPixelColorAntialiased(in vec2 position) {
|
|
||||||
|
/*vec3 getPixelColorAntialiased(in vec2 position) {
|
||||||
Circle nearest;
|
Circle nearest;
|
||||||
float minDistance = getDistance(position, nearest);
|
float minDistance = getDistance(position, nearest);
|
||||||
if (0.0 < minDistance && minDistance < 1.0) {
|
if (0.0 < minDistance && minDistance < 1.0) {
|
||||||
|
|
@ -148,13 +120,23 @@ vec3 getPixelColorAntialiased(in vec2 position) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return getPixelColor(position, minDistance < 0.0, minDistance < 0.0 ? nearest.color : vec3(1.0));
|
return getPixelColor(position, minDistance < 0.0, minDistance < 0.0 ? nearest.color : vec3(1.0));
|
||||||
}
|
}*/
|
||||||
|
|
||||||
|
out vec4 fragmentColor;
|
||||||
|
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
createWorld();
|
createWorld();
|
||||||
|
|
||||||
vec2 position = gl_FragCoord.xy + vec2(0.5);
|
vec2 pixelWorldCoordinates = (vec3(gl_FragCoord.xy, 1.0) * lightingScreenToWorld).xy;
|
||||||
vec3 color = getPixelColorAntialiased(position);
|
|
||||||
|
vec3 color;
|
||||||
|
float minDistance = getDistance(pixelWorldCoordinates, color);
|
||||||
|
color = getPixelColor(pixelWorldCoordinates, minDistance < 0.0, minDistance < 0.0 ? color : vec3(1.0));
|
||||||
|
|
||||||
fragmentColor = vec4(color, 1.0);
|
fragmentColor = vec4(color, 1.0);
|
||||||
|
|
||||||
|
if (distance(cursorPosition, pixelWorldCoordinates) < 50.0) {
|
||||||
|
fragmentColor = vec4(vec3(1.0, 1.0, 0.0), 1.0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,12 +22,28 @@ vec4 smoothRainbow(float x) {
|
||||||
|
|
||||||
|
|
||||||
uniform sampler2D distanceTexture;
|
uniform sampler2D distanceTexture;
|
||||||
uniform mat3 transformUV;
|
uniform mat3 worldToDistanceUV;
|
||||||
|
uniform mat3 lightingScreenToWorld;
|
||||||
out vec4 fragmentColor;
|
out vec4 fragmentColor;
|
||||||
|
uniform vec2 cursorPosition;
|
||||||
|
|
||||||
|
|
||||||
|
float getDistance(in vec2 targetUV, out vec3 color) {
|
||||||
|
vec4 values = texture(distanceTexture, targetUV);
|
||||||
|
color = values.rgb;
|
||||||
|
return values.a;
|
||||||
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec2 position = (vec3(gl_FragCoord.xy, 1.0) * transformUV).xy;
|
vec2 targetUV = (vec3(gl_FragCoord.xy, 1.0) * worldToDistanceUV).xy;
|
||||||
vec4 previous = texture(distanceTexture, position);
|
vec2 targetLighting = (vec3(gl_FragCoord.xy, 1.0) * lightingScreenToWorld).xy;
|
||||||
|
|
||||||
|
vec4 previous = texture(distanceTexture, targetUV);
|
||||||
|
|
||||||
//fragmentColor = smoothRainbow(previous.a);
|
//fragmentColor = smoothRainbow(previous.a);
|
||||||
fragmentColor = previous.a > 0.5 ? vec4(1.0, 1.0, 1.0, 1.0) : vec4(0.0, 0.0, 0.0, 1.0);
|
fragmentColor = previous.a > 0.5 ? vec4(1.0, 1.0, 1.0, 1.0) : vec4(0.0, 0.0, 0.0, 1.0);
|
||||||
|
|
||||||
|
if (distance(targetLighting, cursorPosition) < 0.01) {
|
||||||
|
fragmentColor = vec4(1.0, 1.0, 0.0, 1.0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
precision mediump float;
|
precision mediump float;
|
||||||
|
|
||||||
const float smoothing = 10.0;
|
|
||||||
const float inf = 1000000.0;
|
const float inf = 1000000.0;
|
||||||
const float pi = atan(1.0) * 4.0;
|
const float pi = atan(1.0) * 4.0;
|
||||||
|
|
||||||
|
|
@ -11,16 +10,6 @@ float interpolate(float from, float to, float quotient) {
|
||||||
return from + (to - from) * clamp(steppedQ, 0.0, 1.0);
|
return from + (to - from) * clamp(steppedQ, 0.0, 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
vec2 rotate90deg(in vec2 vector) {
|
|
||||||
return vec2(-vector.y, vector.x);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Line {
|
|
||||||
vec2 a;
|
|
||||||
vec2 b;
|
|
||||||
vec2 normal;
|
|
||||||
bool isLineEnd;
|
|
||||||
}[16] lines;
|
|
||||||
|
|
||||||
float noise(float x){
|
float noise(float x){
|
||||||
return fract(sin(x) * 43758.5453123);
|
return fract(sin(x) * 43758.5453123);
|
||||||
|
|
@ -57,101 +46,4 @@ float noise(vec2 st) {
|
||||||
dot( random2(i + vec2(1.0,0.0) ), f - vec2(1.0,0.0) ), u.x),
|
dot( random2(i + vec2(1.0,0.0) ), f - vec2(1.0,0.0) ), u.x),
|
||||||
mix( dot( random2(i + vec2(0.0,1.0) ), f - vec2(0.0,1.0) ),
|
mix( dot( random2(i + vec2(0.0,1.0) ), f - vec2(0.0,1.0) ),
|
||||||
dot( random2(i + vec2(1.0,1.0) ), f - vec2(1.0,1.0) ), u.x), u.y);
|
dot( random2(i + vec2(1.0,1.0) ), f - vec2(1.0,1.0) ), u.x), u.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
float lineDistance(in vec2 position, in Line line, out float h) {
|
|
||||||
vec2 pa = position - line.a, ba = line.b - line.a;
|
|
||||||
h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);
|
|
||||||
vec2 delta = pa - ba*h;
|
|
||||||
// sign can return 0, double sign prevents this
|
|
||||||
float side = sign(sign(dot(delta, line.normal)) - 0.5);
|
|
||||||
return length(delta) * side; //+ terrain(length(ba * h));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Line endDummyLineFromLine(Line line) {
|
|
||||||
return Line(line.b, line.b + rotate90deg(line.normal), line.normal, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
float getDistance(in vec2 target) {
|
|
||||||
float minDistance = inf;
|
|
||||||
|
|
||||||
float leftJoinAcuteness = 0.0;
|
|
||||||
vec2 splitterLineNormalStart = vec2(-1.0, 0.0);
|
|
||||||
bool skipDistanceToPrevious = true;
|
|
||||||
for (int i = 0; i < lines.length(); i++) {
|
|
||||||
Line current = lines[i];
|
|
||||||
|
|
||||||
Line next;
|
|
||||||
if (current.isLineEnd || i + 1 == lines.length()) {
|
|
||||||
next = endDummyLineFromLine(current);
|
|
||||||
} else {
|
|
||||||
next = lines[i + 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
vec2 splitterLineNormalEnd = rotate90deg(normalize(current.normal + next.normal));
|
|
||||||
|
|
||||||
float h;
|
|
||||||
float distanceToCurrent = lineDistance(target, current, h);
|
|
||||||
float rightJoinAcuteness = dot(next.b - current.a, next.normal - current.normal);
|
|
||||||
distanceToCurrent -= interpolate(
|
|
||||||
sign(leftJoinAcuteness) * smoothing,
|
|
||||||
sign(rightJoinAcuteness) * smoothing, h
|
|
||||||
);
|
|
||||||
leftJoinAcuteness = rightJoinAcuteness;
|
|
||||||
|
|
||||||
if (
|
|
||||||
!(
|
|
||||||
dot(target - current.a, splitterLineNormalStart * -sign(dot(current.b - current.a, splitterLineNormalStart))) > 0.0
|
|
||||||
|| dot(target - current.b, splitterLineNormalEnd * sign(dot(current.a - current.b, splitterLineNormalEnd))) <= 0.0
|
|
||||||
) && abs(distanceToCurrent) < abs(minDistance)
|
|
||||||
) {
|
|
||||||
minDistance = distanceToCurrent;
|
|
||||||
}
|
|
||||||
splitterLineNormalStart = splitterLineNormalEnd;
|
|
||||||
}
|
|
||||||
|
|
||||||
return minDistance;
|
|
||||||
}
|
|
||||||
|
|
||||||
void createWorld() {
|
|
||||||
lines[0] = Line(vec2(0.0, 300.0), vec2(550.0, 140.0), vec2(1.0), false);
|
|
||||||
lines[1] = Line(vec2(550.0, 140.0), vec2(750.0, 130.0), vec2(1.0), false);
|
|
||||||
lines[2] = Line(vec2(750.0, 130.0), vec2(650.0, 230.0), vec2(1.0), false);
|
|
||||||
lines[3] = Line(vec2(650.0, 230.0), vec2(850.0, 230.0), vec2(1.0), false);
|
|
||||||
lines[4] = Line(vec2(850.0, 230.0), vec2(800.0, 150.0), vec2(1.0), false);
|
|
||||||
lines[5] = Line(vec2(800.0, 150.0), vec2(1000.0, 120.0), vec2(1.0), false);
|
|
||||||
lines[6] = Line(vec2(1000.0, 120.0), vec2(1150, 120.0), vec2(1.0), false);
|
|
||||||
lines[7] = Line(vec2(1150, 120.0), vec2(10200, 350.0), vec2(1.0), true);
|
|
||||||
lines[8] = Line(vec2(0.0, 600.0), vec2(550.0, 440.0), vec2(-1.0), false);
|
|
||||||
lines[9] = Line(vec2(550.0, 440.0), vec2(750.0, 430.0), vec2(-1.0), false);
|
|
||||||
lines[10] = Line(vec2(750.0, 430.0), vec2(650.0, 530.0), vec2(-1.0), false);
|
|
||||||
lines[11] = Line(vec2(650.0, 530.0), vec2(850.0, 530.0), vec2(-1.0), false);
|
|
||||||
lines[12] = Line(vec2(850.0, 530.0), vec2(820.0, 450.0), vec2(-1.0), false);
|
|
||||||
lines[13] = Line(vec2(820.0, 450.0), vec2(1000.0, 420.0), vec2(-1.0), false);
|
|
||||||
lines[14] = Line(vec2(1000.0, 420.0), vec2(1150, 420.0), vec2(-1.0), false);
|
|
||||||
lines[15] = Line(vec2(1150, 420.0), vec2(10200, 650.0), vec2(-1.0), true);
|
|
||||||
|
|
||||||
for (int i = 0; i < lines.length(); i++) {
|
|
||||||
vec2 tangent = lines[i].b - lines[i].a;
|
|
||||||
lines[i].normal = normalize(
|
|
||||||
vec2(-lines[i].normal.x * tangent.y, lines[i].normal.x * tangent.x)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
uniform vec2 cameraPosition;
|
|
||||||
uniform vec2 viewBoxSize;
|
|
||||||
uniform vec2 resolution;
|
|
||||||
|
|
||||||
out vec4 fragmentColor;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
createWorld();
|
|
||||||
|
|
||||||
vec2 pixelPosition = gl_FragCoord.xy + vec2(0.5);
|
|
||||||
vec2 position = pixelPosition / resolution * viewBoxSize + cameraPosition;
|
|
||||||
|
|
||||||
fragmentColor = vec4(vec3(1.0) * clamp(0.0, 1.0, getDistance(position)), 1.0);
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue