mirror of
https://github.com/PavelDoGreat/WebGL-Fluid-Simulation.git
synced 2025-10-03 17:31:53 +02:00
best update
This commit is contained in:
362
script.js
362
script.js
@@ -49,7 +49,10 @@ let config = {
|
|||||||
BLOOM_RESOLUTION: 256,
|
BLOOM_RESOLUTION: 256,
|
||||||
BLOOM_INTENSITY: 0.8,
|
BLOOM_INTENSITY: 0.8,
|
||||||
BLOOM_THRESHOLD: 0.6,
|
BLOOM_THRESHOLD: 0.6,
|
||||||
BLOOM_SOFT_KNEE: 0.7
|
BLOOM_SOFT_KNEE: 0.7,
|
||||||
|
SUNRAYS: true,
|
||||||
|
SUNRAYS_RESOLUTION: 196,
|
||||||
|
SUNRAYS_WEIGHT: 0.3,
|
||||||
}
|
}
|
||||||
|
|
||||||
function pointerPrototype () {
|
function pointerPrototype () {
|
||||||
@@ -79,6 +82,7 @@ if (!ext.supportLinearFiltering) {
|
|||||||
config.DYE_RESOLUTION = 512;
|
config.DYE_RESOLUTION = 512;
|
||||||
config.SHADING = false;
|
config.SHADING = false;
|
||||||
config.BLOOM = false;
|
config.BLOOM = false;
|
||||||
|
config.SUNRAYS = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
startGUI();
|
startGUI();
|
||||||
@@ -182,7 +186,7 @@ function startGUI () {
|
|||||||
gui.add(config, 'PRESSURE', 0.0, 1.0).name('pressure');
|
gui.add(config, 'PRESSURE', 0.0, 1.0).name('pressure');
|
||||||
gui.add(config, 'CURL', 0, 50).name('vorticity').step(1);
|
gui.add(config, 'CURL', 0, 50).name('vorticity').step(1);
|
||||||
gui.add(config, 'SPLAT_RADIUS', 0.01, 1.0).name('splat radius');
|
gui.add(config, 'SPLAT_RADIUS', 0.01, 1.0).name('splat radius');
|
||||||
gui.add(config, 'SHADING').name('shading');
|
gui.add(config, 'SHADING').name('shading').onFinishChange(updateKeywords);
|
||||||
gui.add(config, 'COLORFUL').name('colorful');
|
gui.add(config, 'COLORFUL').name('colorful');
|
||||||
gui.add(config, 'PAUSED').name('paused').listen();
|
gui.add(config, 'PAUSED').name('paused').listen();
|
||||||
|
|
||||||
@@ -191,10 +195,14 @@ function startGUI () {
|
|||||||
} }, 'fun').name('Random splats');
|
} }, 'fun').name('Random splats');
|
||||||
|
|
||||||
let bloomFolder = gui.addFolder('Bloom');
|
let bloomFolder = gui.addFolder('Bloom');
|
||||||
bloomFolder.add(config, 'BLOOM').name('enabled');
|
bloomFolder.add(config, 'BLOOM').name('enabled').onFinishChange(updateKeywords);
|
||||||
bloomFolder.add(config, 'BLOOM_INTENSITY', 0.1, 2.0).name('intensity');
|
bloomFolder.add(config, 'BLOOM_INTENSITY', 0.1, 2.0).name('intensity');
|
||||||
bloomFolder.add(config, 'BLOOM_THRESHOLD', 0.0, 1.0).name('threshold');
|
bloomFolder.add(config, 'BLOOM_THRESHOLD', 0.0, 1.0).name('threshold');
|
||||||
|
|
||||||
|
let sunraysFolder = gui.addFolder('Sunrays');
|
||||||
|
sunraysFolder.add(config, 'SUNRAYS').name('enabled').onFinishChange(updateKeywords);
|
||||||
|
sunraysFolder.add(config, 'SUNRAYS_WEIGHT', 0.3, 1.0).name('weight');
|
||||||
|
|
||||||
let captureFolder = gui.addFolder('Capture');
|
let captureFolder = gui.addFolder('Capture');
|
||||||
captureFolder.addColor(config, 'BACK_COLOR').name('background color');
|
captureFolder.addColor(config, 'BACK_COLOR').name('background color');
|
||||||
captureFolder.add(config, 'TRANSPARENT').name('transparent');
|
captureFolder.add(config, 'TRANSPARENT').name('transparent');
|
||||||
@@ -312,23 +320,44 @@ function downloadURI (filename, uri) {
|
|||||||
document.body.removeChild(link);
|
document.body.removeChild(link);
|
||||||
}
|
}
|
||||||
|
|
||||||
class GLProgram {
|
class Material {
|
||||||
|
constructor (vertexShader, fragmentShaderSource) {
|
||||||
|
this.vertexShader = vertexShader;
|
||||||
|
this.fragmentShaderSource = fragmentShaderSource;
|
||||||
|
this.programs = [];
|
||||||
|
this.activeProgram = null;
|
||||||
|
this.uniforms = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
setKeywords (keywords) {
|
||||||
|
let hash = 0;
|
||||||
|
for (let i = 0; i < keywords.length; i++)
|
||||||
|
hash += hashCode(keywords[i]);
|
||||||
|
|
||||||
|
let program = this.programs[hash];
|
||||||
|
if (program == null)
|
||||||
|
{
|
||||||
|
let fragmentShader = compileShader(gl.FRAGMENT_SHADER, this.fragmentShaderSource, keywords);
|
||||||
|
program = createProgram(this.vertexShader, fragmentShader);
|
||||||
|
this.programs[hash] = program;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (program == this.activeProgram) return;
|
||||||
|
|
||||||
|
this.uniforms = getUniforms(program);
|
||||||
|
this.activeProgram = program;
|
||||||
|
}
|
||||||
|
|
||||||
|
bind () {
|
||||||
|
gl.useProgram(this.activeProgram);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Program {
|
||||||
constructor (vertexShader, fragmentShader) {
|
constructor (vertexShader, fragmentShader) {
|
||||||
this.uniforms = {};
|
this.uniforms = {};
|
||||||
this.program = gl.createProgram();
|
this.program = createProgram(vertexShader, fragmentShader);
|
||||||
|
this.uniforms = getUniforms(this.program);
|
||||||
gl.attachShader(this.program, vertexShader);
|
|
||||||
gl.attachShader(this.program, fragmentShader);
|
|
||||||
gl.linkProgram(this.program);
|
|
||||||
|
|
||||||
if (!gl.getProgramParameter(this.program, gl.LINK_STATUS))
|
|
||||||
throw gl.getProgramInfoLog(this.program);
|
|
||||||
|
|
||||||
const uniformCount = gl.getProgramParameter(this.program, gl.ACTIVE_UNIFORMS);
|
|
||||||
for (let i = 0; i < uniformCount; i++) {
|
|
||||||
const uniformName = gl.getActiveUniform(this.program, i).name;
|
|
||||||
this.uniforms[uniformName] = gl.getUniformLocation(this.program, uniformName);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bind () {
|
bind () {
|
||||||
@@ -336,14 +365,30 @@ class GLProgram {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function compileShader (type, source, keywords) {
|
function createProgram (vertexShader, fragmentShader) {
|
||||||
if (keywords != null) {
|
let program = gl.createProgram();
|
||||||
let keywordsString = '';
|
gl.attachShader(program, vertexShader);
|
||||||
keywords.forEach(keyword => {
|
gl.attachShader(program, fragmentShader);
|
||||||
keywordsString += '#define ' + keyword + '\n';
|
gl.linkProgram(program);
|
||||||
});
|
|
||||||
source = keywordsString + source;
|
if (!gl.getProgramParameter(program, gl.LINK_STATUS))
|
||||||
|
throw gl.getProgramInfoLog(program);
|
||||||
|
|
||||||
|
return program;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUniforms (program) {
|
||||||
|
let uniforms = [];
|
||||||
|
let uniformCount = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
|
||||||
|
for (let i = 0; i < uniformCount; i++) {
|
||||||
|
let uniformName = gl.getActiveUniform(program, i).name;
|
||||||
|
uniforms[uniformName] = gl.getUniformLocation(program, uniformName);
|
||||||
}
|
}
|
||||||
|
return uniforms;
|
||||||
|
}
|
||||||
|
|
||||||
|
function compileShader (type, source, keywords) {
|
||||||
|
source = addKeywords(source, keywords);
|
||||||
|
|
||||||
const shader = gl.createShader(type);
|
const shader = gl.createShader(type);
|
||||||
gl.shaderSource(shader, source);
|
gl.shaderSource(shader, source);
|
||||||
@@ -355,6 +400,15 @@ function compileShader (type, source, keywords) {
|
|||||||
return shader;
|
return shader;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function addKeywords (source, keywords) {
|
||||||
|
if (keywords == null) return source;
|
||||||
|
let keywordsString = '';
|
||||||
|
keywords.forEach(keyword => {
|
||||||
|
keywordsString += '#define ' + keyword + '\n';
|
||||||
|
});
|
||||||
|
return keywordsString + source;
|
||||||
|
}
|
||||||
|
|
||||||
const baseVertexShader = compileShader(gl.VERTEX_SHADER, `
|
const baseVertexShader = compileShader(gl.VERTEX_SHADER, `
|
||||||
precision highp float;
|
precision highp float;
|
||||||
|
|
||||||
@@ -376,6 +430,41 @@ const baseVertexShader = compileShader(gl.VERTEX_SHADER, `
|
|||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
|
|
||||||
|
const blurVertexShader = compileShader(gl.VERTEX_SHADER, `
|
||||||
|
precision highp float;
|
||||||
|
|
||||||
|
attribute vec2 aPosition;
|
||||||
|
varying vec2 vUv;
|
||||||
|
varying vec2 vL;
|
||||||
|
varying vec2 vR;
|
||||||
|
uniform vec2 texelSize;
|
||||||
|
|
||||||
|
void main () {
|
||||||
|
vUv = aPosition * 0.5 + 0.5;
|
||||||
|
float offset = 1.33333333;
|
||||||
|
vL = vUv - texelSize * offset;
|
||||||
|
vR = vUv + texelSize * offset;
|
||||||
|
gl_Position = vec4(aPosition, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
const blurShader = compileShader(gl.FRAGMENT_SHADER, `
|
||||||
|
precision mediump float;
|
||||||
|
precision mediump sampler2D;
|
||||||
|
|
||||||
|
varying vec2 vUv;
|
||||||
|
varying vec2 vL;
|
||||||
|
varying vec2 vR;
|
||||||
|
uniform sampler2D uTexture;
|
||||||
|
|
||||||
|
void main () {
|
||||||
|
vec4 sum = texture2D(uTexture, vUv) * 0.29411764;
|
||||||
|
sum += texture2D(uTexture, vL) * 0.35294117;
|
||||||
|
sum += texture2D(uTexture, vR) * 0.35294117;
|
||||||
|
gl_FragColor = sum;
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
const copyShader = compileShader(gl.FRAGMENT_SHADER, `
|
const copyShader = compileShader(gl.FRAGMENT_SHADER, `
|
||||||
precision mediump float;
|
precision mediump float;
|
||||||
precision mediump sampler2D;
|
precision mediump sampler2D;
|
||||||
@@ -440,6 +529,7 @@ const displayShaderSource = `
|
|||||||
varying vec2 vB;
|
varying vec2 vB;
|
||||||
uniform sampler2D uTexture;
|
uniform sampler2D uTexture;
|
||||||
uniform sampler2D uBloom;
|
uniform sampler2D uBloom;
|
||||||
|
uniform sampler2D uSunrays;
|
||||||
uniform sampler2D uDithering;
|
uniform sampler2D uDithering;
|
||||||
uniform vec2 ditherScale;
|
uniform vec2 ditherScale;
|
||||||
uniform vec2 texelSize;
|
uniform vec2 texelSize;
|
||||||
@@ -470,6 +560,17 @@ const displayShaderSource = `
|
|||||||
|
|
||||||
#ifdef BLOOM
|
#ifdef BLOOM
|
||||||
vec3 bloom = texture2D(uBloom, vUv).rgb;
|
vec3 bloom = texture2D(uBloom, vUv).rgb;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef SUNRAYS
|
||||||
|
float sunrays = texture2D(uSunrays, vUv).r;
|
||||||
|
c *= sunrays;
|
||||||
|
#ifdef BLOOM
|
||||||
|
bloom *= sunrays;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef BLOOM
|
||||||
float noise = texture2D(uDithering, vUv * ditherScale).r;
|
float noise = texture2D(uDithering, vUv * ditherScale).r;
|
||||||
noise = noise * 2.0 - 1.0;
|
noise = noise * 2.0 - 1.0;
|
||||||
bloom += noise / 255.0;
|
bloom += noise / 255.0;
|
||||||
@@ -482,11 +583,6 @@ const displayShaderSource = `
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const displayShader = compileShader(gl.FRAGMENT_SHADER, displayShaderSource);
|
|
||||||
const displayBloomShader = compileShader(gl.FRAGMENT_SHADER, displayShaderSource, ['BLOOM']);
|
|
||||||
const displayShadingShader = compileShader(gl.FRAGMENT_SHADER, displayShaderSource, ['SHADING']);
|
|
||||||
const displayBloomShadingShader = compileShader(gl.FRAGMENT_SHADER, displayShaderSource, ['BLOOM', 'SHADING']);
|
|
||||||
|
|
||||||
const bloomPrefilterShader = compileShader(gl.FRAGMENT_SHADER, `
|
const bloomPrefilterShader = compileShader(gl.FRAGMENT_SHADER, `
|
||||||
precision mediump float;
|
precision mediump float;
|
||||||
precision mediump sampler2D;
|
precision mediump sampler2D;
|
||||||
@@ -549,6 +645,56 @@ const bloomFinalShader = compileShader(gl.FRAGMENT_SHADER, `
|
|||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
|
|
||||||
|
const sunraysMaskShader = compileShader(gl.FRAGMENT_SHADER, `
|
||||||
|
precision highp float;
|
||||||
|
precision highp sampler2D;
|
||||||
|
|
||||||
|
varying vec2 vUv;
|
||||||
|
uniform sampler2D uTexture;
|
||||||
|
|
||||||
|
void main () {
|
||||||
|
vec4 c = texture2D(uTexture, vUv);
|
||||||
|
float br = max(c.r, max(c.g, c.b));
|
||||||
|
c.a = 1.0 - min(max(br * 20.0, 0.0), 0.8);
|
||||||
|
gl_FragColor = c;
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
const sunraysShader = compileShader(gl.FRAGMENT_SHADER, `
|
||||||
|
precision highp float;
|
||||||
|
precision highp sampler2D;
|
||||||
|
|
||||||
|
varying vec2 vUv;
|
||||||
|
uniform sampler2D uTexture;
|
||||||
|
uniform float weight;
|
||||||
|
|
||||||
|
#define ITERATIONS 16
|
||||||
|
|
||||||
|
void main () {
|
||||||
|
float Density = 0.3;
|
||||||
|
float Decay = 0.95;
|
||||||
|
float Exposure = 0.7;
|
||||||
|
|
||||||
|
vec2 coord = vUv;
|
||||||
|
vec2 dir = vUv - 0.5;
|
||||||
|
|
||||||
|
dir *= 1.0 / float(ITERATIONS) * Density;
|
||||||
|
float illuminationDecay = 1.0;
|
||||||
|
|
||||||
|
float color = texture2D(uTexture, vUv).a;
|
||||||
|
|
||||||
|
for (int i = 0; i < ITERATIONS; i++)
|
||||||
|
{
|
||||||
|
coord -= dir;
|
||||||
|
float col = texture2D(uTexture, coord).a;
|
||||||
|
color += col * illuminationDecay * weight;
|
||||||
|
illuminationDecay *= Decay;
|
||||||
|
}
|
||||||
|
|
||||||
|
gl_FragColor = vec4(color * Exposure, 0.0, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
const splatShader = compileShader(gl.FRAGMENT_SHADER, `
|
const splatShader = compileShader(gl.FRAGMENT_SHADER, `
|
||||||
precision highp float;
|
precision highp float;
|
||||||
precision highp sampler2D;
|
precision highp sampler2D;
|
||||||
@@ -756,27 +902,30 @@ let divergence;
|
|||||||
let curl;
|
let curl;
|
||||||
let pressure;
|
let pressure;
|
||||||
let bloom;
|
let bloom;
|
||||||
|
let sunrays;
|
||||||
|
let sunraysTemp;
|
||||||
|
|
||||||
let ditheringTexture = createTextureAsync('LDR_LLL1_0.png');
|
let ditheringTexture = createTextureAsync('LDR_LLL1_0.png');
|
||||||
|
|
||||||
const copyProgram = new GLProgram(baseVertexShader, copyShader);
|
const blurProgram = new Program(blurVertexShader, blurShader);
|
||||||
const clearProgram = new GLProgram(baseVertexShader, clearShader);
|
const copyProgram = new Program(baseVertexShader, copyShader);
|
||||||
const colorProgram = new GLProgram(baseVertexShader, colorShader);
|
const clearProgram = new Program(baseVertexShader, clearShader);
|
||||||
const checkerboardProgram = new GLProgram(baseVertexShader, checkerboardShader);
|
const colorProgram = new Program(baseVertexShader, colorShader);
|
||||||
const displayProgram = new GLProgram(baseVertexShader, displayShader);
|
const checkerboardProgram = new Program(baseVertexShader, checkerboardShader);
|
||||||
const displayBloomProgram = new GLProgram(baseVertexShader, displayBloomShader);
|
const bloomPrefilterProgram = new Program(baseVertexShader, bloomPrefilterShader);
|
||||||
const displayShadingProgram = new GLProgram(baseVertexShader, displayShadingShader);
|
const bloomBlurProgram = new Program(baseVertexShader, bloomBlurShader);
|
||||||
const displayBloomShadingProgram = new GLProgram(baseVertexShader, displayBloomShadingShader);
|
const bloomFinalProgram = new Program(baseVertexShader, bloomFinalShader);
|
||||||
const bloomPrefilterProgram = new GLProgram(baseVertexShader, bloomPrefilterShader);
|
const sunraysMaskProgram = new Program(baseVertexShader, sunraysMaskShader);
|
||||||
const bloomBlurProgram = new GLProgram(baseVertexShader, bloomBlurShader);
|
const sunraysProgram = new Program(baseVertexShader, sunraysShader);
|
||||||
const bloomFinalProgram = new GLProgram(baseVertexShader, bloomFinalShader);
|
const splatProgram = new Program(baseVertexShader, splatShader);
|
||||||
const splatProgram = new GLProgram(baseVertexShader, splatShader);
|
const advectionProgram = new Program(baseVertexShader, advectionShader);
|
||||||
const advectionProgram = new GLProgram(baseVertexShader, advectionShader);
|
const divergenceProgram = new Program(baseVertexShader, divergenceShader);
|
||||||
const divergenceProgram = new GLProgram(baseVertexShader, divergenceShader);
|
const curlProgram = new Program(baseVertexShader, curlShader);
|
||||||
const curlProgram = new GLProgram(baseVertexShader, curlShader);
|
const vorticityProgram = new Program(baseVertexShader, vorticityShader);
|
||||||
const vorticityProgram = new GLProgram(baseVertexShader, vorticityShader);
|
const pressureProgram = new Program(baseVertexShader, pressureShader);
|
||||||
const pressureProgram = new GLProgram(baseVertexShader, pressureShader);
|
const gradienSubtractProgram = new Program(baseVertexShader, gradientSubtractShader);
|
||||||
const gradienSubtractProgram = new GLProgram(baseVertexShader, gradientSubtractShader);
|
|
||||||
|
const displayMaterial = new Material(baseVertexShader, displayShaderSource);
|
||||||
|
|
||||||
function initFramebuffers () {
|
function initFramebuffers () {
|
||||||
let simRes = getResolution(config.SIM_RESOLUTION);
|
let simRes = getResolution(config.SIM_RESOLUTION);
|
||||||
@@ -803,6 +952,7 @@ function initFramebuffers () {
|
|||||||
pressure = createDoubleFBO(simRes.width, simRes.height, r.internalFormat, r.format, texType, gl.NEAREST);
|
pressure = createDoubleFBO(simRes.width, simRes.height, r.internalFormat, r.format, texType, gl.NEAREST);
|
||||||
|
|
||||||
initBloomFramebuffers();
|
initBloomFramebuffers();
|
||||||
|
initSunraysFramebuffers();
|
||||||
}
|
}
|
||||||
|
|
||||||
function initBloomFramebuffers () {
|
function initBloomFramebuffers () {
|
||||||
@@ -827,6 +977,17 @@ function initBloomFramebuffers () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function initSunraysFramebuffers () {
|
||||||
|
let res = getResolution(config.SUNRAYS_RESOLUTION);
|
||||||
|
|
||||||
|
const texType = ext.halfFloatTexType;
|
||||||
|
const r = ext.formatR;
|
||||||
|
const filtering = ext.supportLinearFiltering ? gl.LINEAR : gl.NEAREST;
|
||||||
|
|
||||||
|
sunrays = createFBO(res.width, res.height, r.internalFormat, r.format, texType, filtering);
|
||||||
|
sunraysTemp = createFBO(res.width, res.height, r.internalFormat, r.format, texType, filtering);
|
||||||
|
}
|
||||||
|
|
||||||
function createFBO (w, h, internalFormat, format, type, param) {
|
function createFBO (w, h, internalFormat, format, type, param) {
|
||||||
gl.activeTexture(gl.TEXTURE0);
|
gl.activeTexture(gl.TEXTURE0);
|
||||||
let texture = gl.createTexture();
|
let texture = gl.createTexture();
|
||||||
@@ -843,11 +1004,16 @@ function createFBO (w, h, internalFormat, format, type, param) {
|
|||||||
gl.viewport(0, 0, w, h);
|
gl.viewport(0, 0, w, h);
|
||||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
|
let texelSizeX = 1.0 / w;
|
||||||
|
let texelSizeY = 1.0 / h;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
texture,
|
texture,
|
||||||
fbo,
|
fbo,
|
||||||
width: w,
|
width: w,
|
||||||
height: h,
|
height: h,
|
||||||
|
texelSizeX,
|
||||||
|
texelSizeY,
|
||||||
attach (id) {
|
attach (id) {
|
||||||
gl.activeTexture(gl.TEXTURE0 + id);
|
gl.activeTexture(gl.TEXTURE0 + id);
|
||||||
gl.bindTexture(gl.TEXTURE_2D, texture);
|
gl.bindTexture(gl.TEXTURE_2D, texture);
|
||||||
@@ -860,14 +1026,11 @@ function createDoubleFBO (w, h, internalFormat, format, type, param) {
|
|||||||
let fbo1 = createFBO(w, h, internalFormat, format, type, param);
|
let fbo1 = createFBO(w, h, internalFormat, format, type, param);
|
||||||
let fbo2 = createFBO(w, h, internalFormat, format, type, param);
|
let fbo2 = createFBO(w, h, internalFormat, format, type, param);
|
||||||
|
|
||||||
let texelSizeX = 1.0 / w;
|
|
||||||
let texelSizeY = 1.0 / h;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
width: w,
|
width: w,
|
||||||
height: h,
|
height: h,
|
||||||
texelSizeX,
|
texelSizeX: fbo1.texelSizeX,
|
||||||
texelSizeY,
|
texelSizeY: fbo1.texelSizeY,
|
||||||
get read () {
|
get read () {
|
||||||
return fbo1;
|
return fbo1;
|
||||||
},
|
},
|
||||||
@@ -940,6 +1103,15 @@ function createTextureAsync (url) {
|
|||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateKeywords () {
|
||||||
|
let displayKeywords = [];
|
||||||
|
if (config.SHADING) displayKeywords.push("SHADING");
|
||||||
|
if (config.BLOOM) displayKeywords.push("BLOOM");
|
||||||
|
if (config.SUNRAYS) displayKeywords.push("SUNRAYS");
|
||||||
|
displayMaterial.setKeywords(displayKeywords);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateKeywords();
|
||||||
initFramebuffers();
|
initFramebuffers();
|
||||||
multipleSplats(parseInt(Math.random() * 20) + 5);
|
multipleSplats(parseInt(Math.random() * 20) + 5);
|
||||||
|
|
||||||
@@ -1073,6 +1245,10 @@ function step (dt) {
|
|||||||
function render (target) {
|
function render (target) {
|
||||||
if (config.BLOOM)
|
if (config.BLOOM)
|
||||||
applyBloom(dye.read, bloom);
|
applyBloom(dye.read, bloom);
|
||||||
|
if (config.SUNRAYS) {
|
||||||
|
applySunrays(dye.read, dye.write, sunrays);
|
||||||
|
blur(sunrays, sunraysTemp, 1);
|
||||||
|
}
|
||||||
|
|
||||||
if (target == null || !config.TRANSPARENT) {
|
if (target == null || !config.TRANSPARENT) {
|
||||||
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
|
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
|
||||||
@@ -1107,25 +1283,21 @@ function drawCheckerboard (fbo) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function drawDisplay (fbo, width, height) {
|
function drawDisplay (fbo, width, height) {
|
||||||
let program = pickDisplayProgram();
|
displayMaterial.bind();
|
||||||
program.bind();
|
|
||||||
if (config.SHADING)
|
if (config.SHADING)
|
||||||
gl.uniform2f(program.uniforms.texelSize, 1.0 / width, 1.0 / height);
|
gl.uniform2f(displayMaterial.uniforms.texelSize, 1.0 / width, 1.0 / height);
|
||||||
gl.uniform1i(program.uniforms.uTexture, dye.read.attach(0));
|
gl.uniform1i(displayMaterial.uniforms.uTexture, dye.read.attach(0));
|
||||||
if (config.BLOOM) {
|
if (config.BLOOM) {
|
||||||
gl.uniform1i(program.uniforms.uBloom, bloom.attach(1));
|
gl.uniform1i(displayMaterial.uniforms.uBloom, bloom.attach(1));
|
||||||
gl.uniform1i(program.uniforms.uDithering, ditheringTexture.attach(2));
|
gl.uniform1i(displayMaterial.uniforms.uDithering, ditheringTexture.attach(2));
|
||||||
let scale = getTextureScale(ditheringTexture, width, height);
|
let scale = getTextureScale(ditheringTexture, width, height);
|
||||||
gl.uniform2f(program.uniforms.ditherScale, scale.x, scale.y);
|
gl.uniform2f(displayMaterial.uniforms.ditherScale, scale.x, scale.y);
|
||||||
}
|
}
|
||||||
|
if (config.SUNRAYS)
|
||||||
|
gl.uniform1i(displayMaterial.uniforms.uSunrays, sunrays.attach(3));
|
||||||
blit(fbo);
|
blit(fbo);
|
||||||
}
|
}
|
||||||
|
|
||||||
function pickDisplayProgram () {
|
|
||||||
if (config.SHADING) return config.BLOOM ? displayBloomShadingProgram : displayShadingProgram;
|
|
||||||
return config.BLOOM ? displayBloomProgram : displayProgram;
|
|
||||||
}
|
|
||||||
|
|
||||||
function applyBloom (source, destination) {
|
function applyBloom (source, destination) {
|
||||||
if (bloomFramebuffers.length < 2)
|
if (bloomFramebuffers.length < 2)
|
||||||
return;
|
return;
|
||||||
@@ -1147,7 +1319,7 @@ function applyBloom (source, destination) {
|
|||||||
bloomBlurProgram.bind();
|
bloomBlurProgram.bind();
|
||||||
for (let i = 0; i < bloomFramebuffers.length; i++) {
|
for (let i = 0; i < bloomFramebuffers.length; i++) {
|
||||||
let dest = bloomFramebuffers[i];
|
let dest = bloomFramebuffers[i];
|
||||||
gl.uniform2f(bloomBlurProgram.uniforms.texelSize, 1.0 / last.width, 1.0 / last.height);
|
gl.uniform2f(bloomBlurProgram.uniforms.texelSize, last.texelSizeX, last.texelSizeY);
|
||||||
gl.uniform1i(bloomBlurProgram.uniforms.uTexture, last.attach(0));
|
gl.uniform1i(bloomBlurProgram.uniforms.uTexture, last.attach(0));
|
||||||
gl.viewport(0, 0, dest.width, dest.height);
|
gl.viewport(0, 0, dest.width, dest.height);
|
||||||
blit(dest.fbo);
|
blit(dest.fbo);
|
||||||
@@ -1159,7 +1331,7 @@ function applyBloom (source, destination) {
|
|||||||
|
|
||||||
for (let i = bloomFramebuffers.length - 2; i >= 0; i--) {
|
for (let i = bloomFramebuffers.length - 2; i >= 0; i--) {
|
||||||
let baseTex = bloomFramebuffers[i];
|
let baseTex = bloomFramebuffers[i];
|
||||||
gl.uniform2f(bloomBlurProgram.uniforms.texelSize, 1.0 / last.width, 1.0 / last.height);
|
gl.uniform2f(bloomBlurProgram.uniforms.texelSize, last.texelSizeX, last.texelSizeY);
|
||||||
gl.uniform1i(bloomBlurProgram.uniforms.uTexture, last.attach(0));
|
gl.uniform1i(bloomBlurProgram.uniforms.uTexture, last.attach(0));
|
||||||
gl.viewport(0, 0, baseTex.width, baseTex.height);
|
gl.viewport(0, 0, baseTex.width, baseTex.height);
|
||||||
blit(baseTex.fbo);
|
blit(baseTex.fbo);
|
||||||
@@ -1168,13 +1340,40 @@ function applyBloom (source, destination) {
|
|||||||
|
|
||||||
gl.disable(gl.BLEND);
|
gl.disable(gl.BLEND);
|
||||||
bloomFinalProgram.bind();
|
bloomFinalProgram.bind();
|
||||||
gl.uniform2f(bloomFinalProgram.uniforms.texelSize, 1.0 / last.width, 1.0 / last.height);
|
gl.uniform2f(bloomFinalProgram.uniforms.texelSize, last.texelSizeX, last.texelSizeY);
|
||||||
gl.uniform1i(bloomFinalProgram.uniforms.uTexture, last.attach(0));
|
gl.uniform1i(bloomFinalProgram.uniforms.uTexture, last.attach(0));
|
||||||
gl.uniform1f(bloomFinalProgram.uniforms.intensity, config.BLOOM_INTENSITY);
|
gl.uniform1f(bloomFinalProgram.uniforms.intensity, config.BLOOM_INTENSITY);
|
||||||
gl.viewport(0, 0, destination.width, destination.height);
|
gl.viewport(0, 0, destination.width, destination.height);
|
||||||
blit(destination.fbo);
|
blit(destination.fbo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function applySunrays (source, mask, destination) {
|
||||||
|
gl.disable(gl.BLEND);
|
||||||
|
sunraysMaskProgram.bind();
|
||||||
|
gl.uniform1i(sunraysMaskProgram.uniforms.uTexture, source.attach(0));
|
||||||
|
gl.viewport(0, 0, mask.width, mask.height);
|
||||||
|
blit(mask.fbo);
|
||||||
|
|
||||||
|
sunraysProgram.bind();
|
||||||
|
gl.uniform1f(sunraysProgram.uniforms.weight, config.SUNRAYS_WEIGHT);
|
||||||
|
gl.uniform1i(sunraysProgram.uniforms.uTexture, mask.attach(0));
|
||||||
|
gl.viewport(0, 0, destination.width, destination.height);
|
||||||
|
blit(destination.fbo);
|
||||||
|
}
|
||||||
|
|
||||||
|
function blur (target, temp, iterations) {
|
||||||
|
blurProgram.bind();
|
||||||
|
for (let i = 0; i < iterations; i++) {
|
||||||
|
gl.uniform2f(blurProgram.uniforms.texelSize, target.texelSizeX, 0.0);
|
||||||
|
gl.uniform1i(blurProgram.uniforms.uTexture, target.attach(0));
|
||||||
|
blit(temp.fbo);
|
||||||
|
|
||||||
|
gl.uniform2f(blurProgram.uniforms.texelSize, 0.0, target.texelSizeY);
|
||||||
|
gl.uniform1i(blurProgram.uniforms.uTexture, temp.attach(0));
|
||||||
|
blit(target.fbo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function splatPointer (pointer) {
|
function splatPointer (pointer) {
|
||||||
let dx = pointer.deltaX * config.SPLAT_FORCE;
|
let dx = pointer.deltaX * config.SPLAT_FORCE;
|
||||||
let dy = pointer.deltaY * config.SPLAT_FORCE;
|
let dy = pointer.deltaY * config.SPLAT_FORCE;
|
||||||
@@ -1223,7 +1422,8 @@ function correctRadius (radius) {
|
|||||||
canvas.addEventListener('mousedown', e => {
|
canvas.addEventListener('mousedown', e => {
|
||||||
let posX = scaleByPixelRatio(e.offsetX);
|
let posX = scaleByPixelRatio(e.offsetX);
|
||||||
let posY = scaleByPixelRatio(e.offsetY);
|
let posY = scaleByPixelRatio(e.offsetY);
|
||||||
updatePointerDownData(pointers[0], -1, posX, posY);
|
let pointer = pointers.find(p => p.id == -1);
|
||||||
|
updatePointerDownData(pointer, -1, posX, posY);
|
||||||
});
|
});
|
||||||
|
|
||||||
canvas.addEventListener('mousemove', e => {
|
canvas.addEventListener('mousemove', e => {
|
||||||
@@ -1240,11 +1440,13 @@ canvas.addEventListener('touchstart', e => {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const touches = e.targetTouches;
|
const touches = e.targetTouches;
|
||||||
for (let i = 0; i < touches.length; i++) {
|
for (let i = 0; i < touches.length; i++) {
|
||||||
if (i >= pointers.length)
|
let touch = touches[i];
|
||||||
pointers.push(new pointerPrototype());
|
let pointer = pointers.find(p => p.id == touch.identifier);
|
||||||
let posX = scaleByPixelRatio(touches[i].pageX);
|
if (pointer == null)
|
||||||
let posY = scaleByPixelRatio(touches[i].pageY);
|
pointer = new pointerPrototype();
|
||||||
updatePointerDownData(pointers[i], touches[i].identifier, posX, posY);
|
let posX = scaleByPixelRatio(touch.pageX);
|
||||||
|
let posY = scaleByPixelRatio(touch.pageY);
|
||||||
|
updatePointerDownData(pointer, touch.identifier, posX, posY);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1383,4 +1585,14 @@ function getTextureScale (texture, width, height) {
|
|||||||
function scaleByPixelRatio (input) {
|
function scaleByPixelRatio (input) {
|
||||||
let pixelRatio = window.devicePixelRatio || 1;
|
let pixelRatio = window.devicePixelRatio || 1;
|
||||||
return Math.floor(input * pixelRatio);
|
return Math.floor(input * pixelRatio);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hashCode (s) {
|
||||||
|
if (s.length == 0) return 0;
|
||||||
|
let hash = 0;
|
||||||
|
for (let i = 0; i < s.length; i++) {
|
||||||
|
hash = (hash << 5) - hash + s.charCodeAt(i);
|
||||||
|
hash |= 0; // Convert to 32bit integer
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
};
|
Reference in New Issue
Block a user