aesthetics: improve bloom

This commit is contained in:
FMS-Cat
2021-03-26 02:49:03 +09:00
parent 76981445be
commit ccfb896923
12 changed files with 283 additions and 149 deletions

View File

@@ -2,9 +2,10 @@ export const
RTINSPECTOR_MULTIPLE = false,
// RTINSPECTOR_MULTIPLE = true,
RTINSPECTOR_CAPTURE_NAME: string | null = null,
// RTINSPECTOR_CAPTURE_NAME: string | null = 'Bloom/swap0',
// RTINSPECTOR_CAPTURE_NAME: string | null = 'Bloom/swap1',
// RTINSPECTOR_CAPTURE_NAME: string | null = 'main/postSwap0',
// RTINSPECTOR_CAPTURE_NAME: string | null = 'CameraEntity/cameraTarget',
RTINSPECTOR_CAPTURE_INDEX = 0,
COMPONENT_UPDATE_BREAKPOINT: string | null = null,
// COMPONENT_UPDATE_BREAKPOINT: string | null = 'Bloom/blitDup1',
// COMPONENT_UPDATE_BREAKPOINT: string | null = 'Bloom/quadUp1',
COMPONENT_DRAW_BREAKPOINT: string | null = null;

44
src/entities/Antialias.ts Normal file
View File

@@ -0,0 +1,44 @@
import { Entity } from '../heck/Entity';
import { Material } from '../heck/Material';
import { Quad } from '../heck/components/Quad';
import { RenderTarget } from '../heck/RenderTarget';
import fxaaFrag from '../shaders/fxaa.frag';
import quadVert from '../shaders/quad.vert';
import { BufferRenderTarget } from '../heck/BufferRenderTarget';
import { quadGeometry } from '../globals/quadGeometry';
import { dummyRenderTarget } from '../globals/dummyRenderTarget';
export interface PostOptions {
input: BufferRenderTarget;
target: RenderTarget;
}
export class Antialias extends Entity {
public constructor( options: PostOptions ) {
super();
this.visible = false;
// -- post -------------------------------------------------------------------------------------
const material = new Material(
quadVert,
fxaaFrag,
{ initOptions: { geometry: quadGeometry, target: dummyRenderTarget } },
);
material.addUniformTexture( 'sampler0', options.input.texture );
if ( process.env.DEV ) {
if ( module.hot ) {
module.hot.accept( '../shaders/fxaa.frag', () => {
material.replaceShader( quadVert, fxaaFrag );
} );
}
}
this.components.push( new Quad( {
target: options.target,
material,
name: process.env.DEV && 'Antialias/quad',
} ) );
}
}

View File

@@ -5,13 +5,12 @@ import { Quad } from '../heck/components/Quad';
import { RenderTarget } from '../heck/RenderTarget';
import { Swap } from '@fms-cat/experimental';
import quadVert from '../shaders/quad.vert';
import bloomPreFrag from '../shaders/bloom-pre.frag';
import bloomBlurFrag from '../shaders/bloom-blur.frag';
import bloomPostFrag from '../shaders/bloom-post.frag';
import bloomUpFrag from '../shaders/bloom-up.frag';
import bloomDownFrag from '../shaders/bloom-down.frag';
import { quadGeometry } from '../globals/quadGeometry';
import { dummyRenderTarget } from '../globals/dummyRenderTarget';
import { Blit } from '../heck/components/Blit';
import { gl } from '../globals/canvas';
import { Blit } from '../heck/components/Blit';
export interface BloomOptions {
input: BufferRenderTarget;
@@ -37,68 +36,76 @@ export class Bloom extends Entity {
} ),
);
// -- pre ----------------------------------------------------------------------------------------
const materialBloomPre = new Material(
quadVert,
bloomPreFrag,
{ initOptions: { target: dummyRenderTarget, geometry: quadGeometry } },
);
materialBloomPre.addUniformTexture( 'sampler0', options.input.texture );
this.components.push( new Quad( {
target: swap.o,
material: materialBloomPre,
range: [ -1.0, -1.0, 0.0, 0.0 ],
name: process.env.DEV && 'Bloom/quadPre',
// -- dry --------------------------------------------------------------------------------------
this.components.push( new Blit( {
src: options.input,
dst: options.target,
name: 'Glitch/blitDry',
} ) );
swap.swap();
// -- dup ----------------------------------------------------------------------------------------
// -- down -------------------------------------------------------------------------------------
for ( let i = 0; i < 6; i ++ ) {
this.components.push( new Blit( {
src: swap.i,
dst: swap.o,
dstRect: i === 0 ? null : [ width / 2, height / 2, width, height ],
name: `Bloom/blitDup${ i }`,
filter: gl.LINEAR,
} ) );
const isFirst = i === 0;
swap.swap();
}
// -- blur ---------------------------------------------------------------------------------------
for ( let i = 0; i < 2; i ++ ) {
const material = new Material(
quadVert,
bloomBlurFrag,
bloomDownFrag,
{ initOptions: { target: dummyRenderTarget, geometry: quadGeometry } },
);
material.addUniform( 'isVert', '1i', i );
material.addUniformTexture( 'sampler0', swap.i.texture );
material.addUniform( 'level', '1f', i );
material.addUniformTexture(
'sampler0',
isFirst ? options.input.texture : swap.i.texture,
);
const p = 2.0 * Math.pow( 0.5, i );
const range: [ number, number, number, number ] = isFirst
? [ -1.0, -1.0, 0.0, 0.0 ]
: [ 1.0 - p, 1.0 - p, 1.0 - 0.5 * p, 1.0 - 0.5 * p ];
this.components.push( new Quad( {
target: swap.o,
material,
name: process.env.DEV && `Bloom/quadBlur${ i }`,
range,
name: `Bloom/quadDown${ i }`,
} ) );
swap.swap();
}
// -- post ---------------------------------------------------------------------------------------
const materialBloomPost = new Material(
quadVert,
bloomPostFrag,
{ initOptions: { target: dummyRenderTarget, geometry: quadGeometry } },
);
materialBloomPost.addUniformTexture( 'samplerDry', options.input.texture );
materialBloomPost.addUniformTexture( 'samplerWet', swap.i.texture );
// -- up ---------------------------------------------------------------------------------------
for ( let i = 5; i >= 0; i -- ) {
const isLast = i === 0;
this.components.push( new Quad( {
target: options.target,
material: materialBloomPost,
name: process.env.DEV && 'Bloom/quadPost',
} ) );
const material = new Material(
quadVert,
bloomUpFrag,
{
initOptions: { target: dummyRenderTarget, geometry: quadGeometry },
blend: [ gl.ONE, gl.ONE ],
},
);
material.addUniform( 'level', '1f', i );
material.addUniformTexture(
'sampler0',
swap.i.texture,
);
const p = 4.0 * Math.pow( 0.5, i );
const range: [ number, number, number, number ] = isLast
? [ -1.0, -1.0, 1.0, 1.0 ]
: [ 1.0 - p, 1.0 - p, 1.0 - 0.5 * p, 1.0 - 0.5 * p ];
this.components.push( new Quad( {
target: isLast ? options.target : swap.o,
material,
range,
name: `Bloom/quadUp${ i }`,
} ) );
swap.swap();
}
}
}

View File

@@ -78,13 +78,14 @@ export class Material {
);
}
public blend: [ GLenum, GLenum ] = [ gl.ONE, gl.ZERO ];
public blend: [ GLenum, GLenum ];
public constructor(
vert: string,
frag: string,
{ defines, linkOptions, initOptions }: {
{ defines, blend, linkOptions, initOptions }: {
defines?: { [ key: string ]: ( string | undefined ) },
blend?: [ GLenum, GLenum ],
linkOptions?: GLCatProgramLinkOptions,
initOptions?: MaterialInitOptions,
} = {},
@@ -93,6 +94,7 @@ export class Material {
this.__frag = frag;
this.__linkOptions = linkOptions ?? {};
this.__defines = defines ?? {};
this.blend = blend ?? [ gl.ONE, gl.ZERO ];
if ( initOptions ) {
this.d3dSucks( initOptions );

View File

@@ -1,4 +1,5 @@
import { Swap, Vector3 } from '@fms-cat/experimental';
import { Antialias } from './entities/Antialias';
import { Bloom } from './entities/Bloom';
import { CameraEntity } from './entities/CameraEntity';
import { Condition } from './entities/Condition';
@@ -283,6 +284,13 @@ camera.components.unshift( new Lambda( {
} ) );
dog.root.children.push( camera );
swap.swap();
const antialias = new Antialias( {
input: swap.i,
target: swap.o
} );
dog.root.children.push( antialias );
swap.swap();
const bloom = new Bloom( {
input: swap.i,

View File

@@ -1,47 +0,0 @@
#version 300 es
precision highp float;
const float OFFSET1 = 1.411764705882353;
const float OFFSET2 = 3.2941176470588234;
const float OFFSET3 = 5.176470588235294;
const float CONTRIBUTION0 = 0.1964825501511404;
const float CONTRIBUTION1 = 0.2969069646728344;
const float CONTRIBUTION2 = 0.09447039785044732;
const float CONTRIBUTION3 = 0.010381362401148057;
in vec2 vUv;
out vec4 fragColor;
uniform bool isVert;
uniform vec2 resolution;
uniform sampler2D sampler0;
void main() {
vec2 halfTexel = 1.0 / resolution; // * 0.5;
float lv = ceil( -log( 1.0 - min( vUv.x, vUv.y ) ) / log( 2.0 ) );
float p = pow( 0.5, lv );
vec2 clampMin = floor( vUv / p ) * p + halfTexel;
vec2 clampMax = clampMin + p - 2.0 * halfTexel;
vec2 bv = halfTexel * ( isVert ? vec2( 0.0, 1.0 ) : vec2( 1.0, 0.0 ) );
vec4 sum = vec4( 0.0 );
sum += CONTRIBUTION0 * texture( sampler0, vUv );
vec2 suv = clamp( vUv - bv * OFFSET1, clampMin, clampMax );
sum += CONTRIBUTION1 * texture( sampler0, suv );
suv = clamp( vUv + bv * OFFSET1, clampMin, clampMax );
sum += CONTRIBUTION1 * texture( sampler0, suv );
suv = clamp( vUv - bv * OFFSET2, clampMin, clampMax );
sum += CONTRIBUTION2 * texture( sampler0, suv );
suv = clamp( vUv + bv * OFFSET2, clampMin, clampMax );
sum += CONTRIBUTION2 * texture( sampler0, suv );
suv = clamp( vUv - bv * OFFSET3, clampMin, clampMax );
sum += CONTRIBUTION3 * texture( sampler0, suv );
suv = clamp( vUv + bv * OFFSET3, clampMin, clampMax );
sum += CONTRIBUTION3 * texture( sampler0, suv );
fragColor = vec4( sum.xyz, 1.0 );
}

View File

@@ -0,0 +1,58 @@
#version 300 es
precision highp float;
const float WEIGHT_1 = 1.0;
const float WEIGHT_2 = 2.0;
const float WEIGHT_4 = 4.0;
const vec3 LUMA = vec3( 0.299, 0.587, 0.114 );
in vec2 vUv;
out vec4 fragColor;
uniform float level;
uniform vec2 resolution;
uniform sampler2D sampler0;
vec4 fetchWithWeight( vec2 uv ) {
vec4 tex = texture( sampler0, uv );
float luma = dot( LUMA, tex.xyz );
return vec4( tex.xyz, 1.0 + luma );
}
void main() {
float p = 2.0 * pow( 0.5, level ); // 2.0, 1.0, 0.5, 0.25...
vec2 deltaTexel = 1.0 / resolution;
vec2 uv0 = step( 0.5, level ) * vec2( 1.0 - p );
vec2 uv1 = vec2( 1.0 - step( 0.5, level ) * 0.5 * p );
vec2 uv = mix( uv0, uv1, vUv );
uv = clamp( uv, uv0 + deltaTexel, uv1 - deltaTexel );
// http://www.iryoku.com/next-generation-post-processing-in-call-of-duty-advanced-warfare
vec4 tex = WEIGHT_1 * fetchWithWeight( uv - deltaTexel * vec2( -1.0, -1.0 ) );
tex += WEIGHT_2 * fetchWithWeight( uv - deltaTexel * vec2( 0.0, -1.0 ) );
tex += WEIGHT_1 * fetchWithWeight( uv - deltaTexel * vec2( 1.0, -1.0 ) );
tex += WEIGHT_4 * fetchWithWeight( uv - deltaTexel * vec2( -0.5, -0.5 ) );
tex += WEIGHT_4 * fetchWithWeight( uv - deltaTexel * vec2( 0.5, -0.5 ) );
tex += WEIGHT_2 * fetchWithWeight( uv - deltaTexel * vec2( -1.0, 0.0 ) );
tex += WEIGHT_4 * fetchWithWeight( uv - deltaTexel * vec2( 0.0, 0.0 ) );
tex += WEIGHT_2 * fetchWithWeight( uv - deltaTexel * vec2( 1.0, 0.0 ) );
tex += WEIGHT_4 * fetchWithWeight( uv - deltaTexel * vec2( -0.5, 0.5 ) );
tex += WEIGHT_4 * fetchWithWeight( uv - deltaTexel * vec2( 0.5, 0.5 ) );
tex += WEIGHT_1 * fetchWithWeight( uv - deltaTexel * vec2( -1.0, 1.0 ) );
tex += WEIGHT_2 * fetchWithWeight( uv - deltaTexel * vec2( 0.0, 1.0 ) );
tex += WEIGHT_1 * fetchWithWeight( uv - deltaTexel * vec2( 1.0, 1.0 ) );
vec3 col = tex.rgb / tex.w;
if ( level == 0.0 ) {
float brightness = dot( LUMA, col );
vec3 normalized = brightness < 1E-4 ? vec3( brightness ) : col / brightness;
col = max( 0.0, brightness - 0.5 ) * normalized;
}
fragColor = vec4( col, 1.0 );
}

View File

@@ -1,29 +0,0 @@
#version 300 es
precision highp float;
in vec2 vUv;
out vec4 fragColor;
uniform vec2 resolution;
uniform sampler2D samplerDry;
uniform sampler2D samplerWet;
vec4 sampleLOD( vec2 uv, float lv ) {
float p = pow( 0.5, float( lv ) );
vec2 halfPixel = 0.5 / resolution;
vec2 uv00 = vec2( 1.0 - p );
vec2 uv11 = vec2( 1.0 - 0.5 * p );
vec2 uvt = mix( uv00, uv11, uv );
uvt = clamp( uvt, uv00 + halfPixel, uv11 - halfPixel );
return texture( samplerWet, uvt );
}
void main() {
fragColor = texture( samplerDry, vUv );
for ( int i = 0; i < 5; i ++ ) {
fragColor += sampleLOD( vUv, float( i ) );
}
fragColor.xyz = max( vec3( 0.0 ), fragColor.xyz );
}

View File

@@ -1,20 +0,0 @@
#version 300 es
precision highp float;
in vec2 vUv;
out vec4 fragColor;
uniform sampler2D sampler0;
void main() {
vec3 tex = texture( sampler0, vUv ).xyz;
float brightness = dot( tex, vec3( 0.299, 0.587, 0.114 ) );
vec3 normalized = brightness < 0.01 ? vec3( brightness ) : tex / brightness;
fragColor = vec4(
max( 0.0, brightness - 1.0 ) * normalized,
1.0
);
}

41
src/shaders/bloom-up.frag Normal file
View File

@@ -0,0 +1,41 @@
#version 300 es
precision highp float;
const float WEIGHT_1 = 1.0 / 16.0;
const float WEIGHT_2 = 2.0 / 16.0;
const float WEIGHT_4 = 4.0 / 16.0;
in vec2 vUv;
out vec4 fragColor;
uniform float level;
uniform vec2 resolution;
uniform sampler2D sampler0;
void main() {
float p = pow( 0.5, level ); // 1.0, 0.5, 0.25...
vec2 deltaTexel = 1.0 / resolution;
vec2 uv0 = vec2( 1.0 - p );
vec2 uv1 = vec2( 1.0 - 0.5 * p );
vec2 uv = mix( uv0, uv1, vUv );
uv = clamp( uv, uv0 + 1.5 * deltaTexel, uv1 - 1.5 * deltaTexel );
// http://www.iryoku.com/next-generation-post-processing-in-call-of-duty-advanced-warfare
vec4 tex = WEIGHT_1 * texture( sampler0, uv - deltaTexel * vec2( -1.0, -1.0 ) );
tex += WEIGHT_2 * texture( sampler0, uv - deltaTexel * vec2( 0.0, -1.0 ) );
tex += WEIGHT_1 * texture( sampler0, uv - deltaTexel * vec2( 1.0, -1.0 ) );
tex += WEIGHT_2 * texture( sampler0, uv - deltaTexel * vec2( -1.0, 0.0 ) );
tex += WEIGHT_4 * texture( sampler0, uv - deltaTexel * vec2( 0.0, 0.0 ) );
tex += WEIGHT_2 * texture( sampler0, uv - deltaTexel * vec2( 1.0, 0.0 ) );
tex += WEIGHT_1 * texture( sampler0, uv - deltaTexel * vec2( -1.0, 1.0 ) );
tex += WEIGHT_2 * texture( sampler0, uv - deltaTexel * vec2( 0.0, 1.0 ) );
tex += WEIGHT_1 * texture( sampler0, uv - deltaTexel * vec2( 1.0, 1.0 ) );
vec3 col = tex.rgb;
fragColor = vec4( col, 1.0 );
}

69
src/shaders/fxaa.frag Normal file
View File

@@ -0,0 +1,69 @@
#version 300 es
precision highp float;
const float FXAA_REDUCE_MIN = 1.0 / 128.0;
const float FXAA_REDUCE_MUL = 1.0 / 8.0;
const float FXAA_SPAN_MAX = 16.0;
in vec2 vUv;
out vec4 fragColor;
uniform vec2 resolution;
uniform sampler2D sampler0;
void main() {
vec2 uv = vUv;
vec4 neighbor = vec4( -1.0, -1.0, 1.0, 1.0 ) / resolution.xyxy;
vec3 rgb11 = texture( sampler0, vUv ).rgb;
vec3 rgb00 = texture( sampler0, vUv + neighbor.xy ).rgb;
vec3 rgb02 = texture( sampler0, vUv + neighbor.xw ).rgb;
vec3 rgb20 = texture( sampler0, vUv + neighbor.zy ).rgb;
vec3 rgb22 = texture( sampler0, vUv + neighbor.zw ).rgb;
vec3 luma = vec3( 0.299, 0.587, 0.114 );
float luma11 = dot( luma, rgb11 );
float luma00 = dot( luma, rgb00 );
float luma02 = dot( luma, rgb02 );
float luma20 = dot( luma, rgb20 );
float luma22 = dot( luma, rgb22 );
float lumaMin = min( luma00, min( min( luma00, luma02 ), min( luma20, luma22 ) ) );
float lumaMax = max( luma00, max( max( luma00, luma02 ), max( luma20, luma22 ) ) );
vec2 dir = vec2(
-( ( luma00 + luma20 ) - ( luma02 + luma22 ) ),
( ( luma00 + luma02 ) - ( luma20 + luma22 ) )
);
float dirReduce = max(
( luma00 + luma02 + luma20 + luma22 ) * 0.25 * FXAA_REDUCE_MUL,
FXAA_REDUCE_MIN
);
float rcpDirMin = 1.0 / ( min( abs( dir.x ), abs( dir.y ) ) + dirReduce );
dir = min(
vec2( FXAA_SPAN_MAX ),
max(
vec2( -FXAA_SPAN_MAX ),
dir * rcpDirMin
)
) / resolution;
vec3 rgbA = 0.5 * (
texture( sampler0, uv + dir * ( 1.0 / 3.0 - 0.5 ) ).xyz +
texture( sampler0, uv + dir * ( 2.0 / 3.0 - 0.5 ) ).xyz
);
vec3 rgbB = rgbA * 0.5 + 0.25 * (
texture( sampler0, uv - dir * 0.5 ).xyz +
texture( sampler0, uv + dir * 0.5 ).xyz
);
float lumaB = dot( rgbB, luma );
fragColor = (
( ( lumaB < lumaMin ) || ( lumaMax < lumaB ) ) ?
vec4( rgbA, 1.0 ) :
vec4( rgbB, 1.0 )
);
}