From 864db7d0073c07942cae4a5a463b4dbb4ea304eb Mon Sep 17 00:00:00 2001 From: FMS-Cat Date: Fri, 19 Mar 2021 03:37:04 +0900 Subject: [PATCH] aesthetics: even better cubemap --- src/config-hot.ts | 4 +- src/entities/EnvironmentMap.ts | 90 +++++++++++++------- src/entities/RTInspector.ts | 2 + src/entities/SphereParticles.ts | 2 +- src/scene.ts | 14 +-- src/shaders/blur.frag | 46 ++++++++++ src/shaders/environment-map-merge.frag | 38 +++++++++ src/shaders/environment-map.frag | 48 +++-------- src/shaders/modules/brdfLambert.glsl | 14 +++ src/shaders/modules/brdfSpecularGGX.glsl | 26 ++++++ src/shaders/modules/importanceSampleGGX.glsl | 30 +++++++ src/shaders/raymarcher.frag | 2 +- src/shaders/rings.frag | 2 +- src/shaders/shading.frag | 75 ++++++++-------- src/shaders/sphere-particles-render.frag | 2 +- 15 files changed, 276 insertions(+), 119 deletions(-) create mode 100644 src/shaders/blur.frag create mode 100644 src/shaders/environment-map-merge.frag create mode 100644 src/shaders/modules/brdfLambert.glsl create mode 100644 src/shaders/modules/brdfSpecularGGX.glsl create mode 100644 src/shaders/modules/importanceSampleGGX.glsl diff --git a/src/config-hot.ts b/src/config-hot.ts index 85c2c0f..a2da13f 100644 --- a/src/config-hot.ts +++ b/src/config-hot.ts @@ -2,8 +2,8 @@ export const RTINSPECTOR_MULTIPLE = false, RTINSPECTOR_CAPTURE_NAME: string | null = null, // RTINSPECTOR_CAPTURE_NAME: string | null = 'light1/shadowMap', - // RTINSPECTOR_CAPTURE_NAME: string | null = 'EnvironmentMap/swap0', + // RTINSPECTOR_CAPTURE_NAME: string | null = 'EnvironmentMap/swap1', RTINSPECTOR_CAPTURE_INDEX = 0, COMPONENT_UPDATE_BREAKPOINT: string | null = null, - // COMPONENT_UPDATE_BREAKPOINT: string | null = 'Bloom/quadDup3', + // COMPONENT_UPDATE_BREAKPOINT: string | null = 'EnvironmentMap/quadIntegrate', COMPONENT_DRAW_BREAKPOINT: string | null = null; diff --git a/src/entities/EnvironmentMap.ts b/src/entities/EnvironmentMap.ts index e693a41..99ca2ae 100644 --- a/src/entities/EnvironmentMap.ts +++ b/src/entities/EnvironmentMap.ts @@ -3,11 +3,13 @@ import { GLCatTexture } from '@fms-cat/glcat-ts'; import { Material } from '../heck/Material'; import { Quad } from '../heck/components/Quad'; import environmentMapFrag from '../shaders/environment-map.frag'; +import environmentMapMergeFrag from '../shaders/environment-map-merge.frag'; import quadVert from '../shaders/quad.vert'; import { BufferRenderTarget } from '../heck/BufferRenderTarget'; import { Swap, Xorshift } from '@fms-cat/experimental'; import { Lambda } from '../heck/components/Lambda'; import { CubemapRenderTarget } from '../heck/CubemapRenderTarget'; +import { gl } from '../globals/canvas'; const WIDTH = 1024; const HEIGHT = 512; @@ -15,11 +17,7 @@ const HEIGHT = 512; export class EnvironmentMap { public entity: Entity; - public swap: Swap; - - public get texture(): GLCatTexture { - return this.swap.o.texture; - } + public readonly texture: GLCatTexture; public constructor( { cubemap }: { cubemap: CubemapRenderTarget; @@ -30,7 +28,7 @@ export class EnvironmentMap { const rng = new Xorshift( 114514 ); // -- swap ------------------------------------------------------------------------------------- - this.swap = new Swap( + const swap = new Swap( new BufferRenderTarget( { width: WIDTH, height: HEIGHT, @@ -42,44 +40,74 @@ export class EnvironmentMap { name: process.env.DEV && 'EnvironmentMap/swap1', } ), ); + swap.i.texture.textureWrap( gl.REPEAT ); + swap.o.texture.textureWrap( gl.REPEAT ); - // -- post ------------------------------------------------------------------------------------- - const material = new Material( + // -- integrate -------------------------------------------------------------------------------- + swap.swap(); + + const materialIntegrate = new Material( quadVert, environmentMapFrag, ); - material.addUniform( 'uniformSeed', '4f', rng.gen(), rng.gen(), rng.gen(), rng.gen() ); - material.addUniformTexture( 'sampler0', this.swap.i.texture ); - material.addUniformCubemap( 'samplerCubemap', cubemap.texture ); + materialIntegrate.addUniform( 'uniformSeed', '4f', rng.gen(), rng.gen(), rng.gen(), rng.gen() ); + materialIntegrate.addUniformTexture( 'sampler0', swap.i.texture ); + materialIntegrate.addUniformCubemap( 'samplerCubemap', cubemap.texture ); if ( process.env.DEV ) { if ( module.hot ) { module.hot.accept( '../shaders/environment-map.frag', () => { - material.replaceShader( quadVert, environmentMapFrag ); + materialIntegrate.replaceShader( quadVert, environmentMapFrag ); } ); } } - const quad = new Quad( { - target: this.swap.o, - material, - name: process.env.DEV && 'EnvironmentMap/quad', - } ); - - // -- swapper ---------------------------------------------------------------------------------- - this.entity.components.push( new Lambda( { - onUpdate: () => { - this.swap.swap(); - - material.addUniform( 'uniformSeed', '4f', rng.gen(), rng.gen(), rng.gen(), rng.gen() ); - material.addUniformTexture( 'sampler0', this.swap.i.texture ); - - quad.target = this.swap.o; - }, - visible: false, - name: process.env.DEV && 'EnvironmentMap/swapper', + this.entity.components.push( new Quad( { + target: swap.o, + material: materialIntegrate, + name: process.env.DEV && 'EnvironmentMap/quadIntegrate', } ) ); - this.entity.components.push( quad ); + // -- merge results ---------------------------------------------------------------------------- + swap.swap(); + + const materialMerge = new Material( + quadVert, + environmentMapMergeFrag, + ); + materialMerge.addUniformTexture( 'sampler0', swap.i.texture ); + + if ( process.env.DEV ) { + if ( module.hot ) { + module.hot.accept( '../shaders/environment-map-merge.frag', () => { + materialMerge.replaceShader( quadVert, environmentMapMergeFrag ); + } ); + } + } + + this.entity.components.push( new Quad( { + target: swap.o, + material: materialMerge, + name: process.env.DEV && 'EnvironmentMap/quadMerge', + } ) ); + + // -- this is the output ----------------------------------------------------------------------- + this.texture = swap.o.texture; + + // -- updater ---------------------------------------------------------------------------------- + this.entity.components.push( new Lambda( { + onUpdate: () => { + materialIntegrate.addUniform( + 'uniformSeed', + '4f', + rng.gen(), + rng.gen(), + rng.gen(), + rng.gen(), + ); + }, + visible: false, + name: process.env.DEV && 'EnvironmentMap/updater', + } ) ); } } diff --git a/src/entities/RTInspector.ts b/src/entities/RTInspector.ts index 874b4a7..38293c5 100644 --- a/src/entities/RTInspector.ts +++ b/src/entities/RTInspector.ts @@ -26,6 +26,7 @@ export class RTInspector { this.blitSingle = new Blit( { dst: options.target, name: process.env.DEV && 'RTInspector/blitSingle', + ignoreBreakpoints: true, } ); this.entitySingle.components.push( this.blitSingle ); @@ -64,6 +65,7 @@ export class RTInspector { attachment: gl.COLOR_ATTACHMENT0 + iAttachment, dstRect, name: `RTInspector/blitsMultiple/${ src.name }/${ iAttachment }`, + ignoreBreakpoints: true, } ); this.blitsMultiple.push( blit ); diff --git a/src/entities/SphereParticles.ts b/src/entities/SphereParticles.ts index 1cdd591..5f1a441 100644 --- a/src/entities/SphereParticles.ts +++ b/src/entities/SphereParticles.ts @@ -53,7 +53,7 @@ export class SphereParticles { } private __createGeometryRender(): Geometry { - const octahedron = genOctahedron( { radius: 1.0, div: 3 } ); + const octahedron = genOctahedron( { radius: 1.0, div: 1 } ); const geometry = new InstancedGeometry(); diff --git a/src/scene.ts b/src/scene.ts index 58b8655..f2e1e2d 100644 --- a/src/scene.ts +++ b/src/scene.ts @@ -71,12 +71,12 @@ const ibllut = new IBLLUT(); dog.root.children.push( ibllut.entity ); // -- "objects" ------------------------------------------------------------------------------------ -// const replacerSphereParticles = new EntityReplacer( () => new SphereParticles() ); -// if ( process.env.DEV && module.hot ) { -// module.hot.accept( './entities/SphereParticles', () => { -// replacerSphereParticles.replace(); -// } ); -// } +const replacerSphereParticles = new EntityReplacer( () => new SphereParticles() ); +if ( process.env.DEV && module.hot ) { + module.hot.accept( './entities/SphereParticles', () => { + replacerSphereParticles.replace(); + } ); +} // const replacerTrails = new EntityReplacer( () => new Trails() ); // if ( process.env.DEV && module.hot ) { @@ -137,7 +137,7 @@ const light = new LightEntity( { shadowMapFar: 20.0, namePrefix: process.env.DEV && 'light1', } ); -light.color = [ 0.1, 0.1, 0.1 ]; +light.color = [ 40.0, 40.0, 40.0 ]; light.entity.transform.lookAt( new Vector3( [ -1.0, 2.0, 8.0 ] ) ); dog.root.children.push( light.entity ); diff --git a/src/shaders/blur.frag b/src/shaders/blur.frag new file mode 100644 index 0000000..ee5664e --- /dev/null +++ b/src/shaders/blur.frag @@ -0,0 +1,46 @@ +#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 vec2 resolution; +uniform sampler2D sampler0; + +void main() { + vec2 bv; + +#ifdef IS_VERTICAL + bv = vec2( 0.0, 1.0 ) / resolution; +#else + bv = vec2( 1.0, 0.0 ) / resolution; +#endif + + vec4 sum = vec4( 0.0 ); + + sum += CONTRIBUTION0 * texture( sampler0, vUv ); + vec2 uvt = vUv - bv * OFFSET1; + sum += CONTRIBUTION1 * texture( sampler0, uvt ); + uvt = vUv + bv * OFFSET1; + sum += CONTRIBUTION1 * texture( sampler0, uvt ); + uvt = vUv - bv * OFFSET2; + sum += CONTRIBUTION2 * texture( sampler0, uvt ); + uvt = vUv + bv * OFFSET2; + sum += CONTRIBUTION2 * texture( sampler0, uvt ); + uvt = vUv - bv * OFFSET3; + sum += CONTRIBUTION3 * texture( sampler0, uvt ); + uvt = vUv + bv * OFFSET3; + sum += CONTRIBUTION3 * texture( sampler0, uvt ); + + fragColor = vec4( sum.xyz, 1.0 ); +} diff --git a/src/shaders/environment-map-merge.frag b/src/shaders/environment-map-merge.frag new file mode 100644 index 0000000..471ab7e --- /dev/null +++ b/src/shaders/environment-map-merge.frag @@ -0,0 +1,38 @@ +#version 300 es + +precision highp float; + +const float PI = 3.14159265; +const float TAU = 6.283185307; + +in vec2 vUv; + +out vec4 fragColor; + +uniform float head; +uniform vec2 resolution; +uniform sampler2D sampler0; + +void main() { + float lv = floor( -log( 1.0 - max( vUv.x, vUv.y ) ) / log( 2.0 ) ); + + if ( lv >= 5.0 ) { discard; } + + fragColor = texture( sampler0, vUv ); + + if ( lv != floor( -log( 1.0 - min( vUv.x, vUv.y ) ) / log( 2.0 ) ) ) { return; } + if ( lv < 1.0 ) { return; } + + float p = pow( 0.5, lv + 1.0 ); + vec2 uvt; + + for ( int i = 1; i < 31; i ++ ) { + uvt = vUv - p * vec2( i, 0.0 ); + if ( uvt.x < 0.0 ) { break; } + fragColor += texture( sampler0, uvt ); + uvt = vUv - p * vec2( 0.0, i ); + fragColor += texture( sampler0, uvt ); + } + + fragColor = fragColor / fragColor.w; +} diff --git a/src/shaders/environment-map.frag b/src/shaders/environment-map.frag index 367da8a..831cac2 100644 --- a/src/shaders/environment-map.frag +++ b/src/shaders/environment-map.frag @@ -1,7 +1,5 @@ #version 300 es -// https://learnopengl.com/PBR/IBL/Specular-IBL - precision highp float; #define saturate(x) clamp(x,0.,1.) @@ -10,7 +8,7 @@ precision highp float; #pragma glslify: prng = require( ./-prng ); const int SAMPLES = 16; -const float UV_MARGIN = 0.9; +const float UV_MARGIN = 0.9375; const float PI = 3.14159265; const float TAU = 6.283185307; @@ -41,40 +39,16 @@ float vdc( float i, float base ) { return r; } -vec3 ImportanceSampleGGX( vec2 Xi, float roughness, vec3 N ) { - float a = roughness * roughness; - - float phi = TAU * Xi.x; - float cosTheta = roughness < 0.0 // negative roughness to usa lambert ??? - ? asin( sqrt( Xi.y ) ) - : sqrt( ( 1.0 - Xi.y ) / ( 1.0 + ( a * a - 1.0 ) * Xi.y ) ); - float sinTheta = sqrt( 1.0 - cosTheta * cosTheta ); - - // from spherical coordinates to cartesian coordinates - vec3 H = vec3( - cos( phi ) * sinTheta, - sin( phi ) * sinTheta, - cosTheta - ); - - // from tangent-space vector to world-space sample vector - vec3 up = abs( N.y ) < 0.999 ? vec3( 0.0, 1.0, 0.0 ) : vec3( 1.0, 0.0, 0.0 ); - vec3 tangent = normalize( cross( up, N ) ); - vec3 bitangent = cross( N, tangent ); - - vec3 sampleVec = tangent * H.x + bitangent * H.y + N * H.z; - return normalize( sampleVec ); -} - -vec3 haha( vec3 L ) { - return texture( samplerCubemap, L ).xyz; -} +#pragma glslify: importanceSampleGGX = require( ./modules/importanceSampleGGX ); 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 ); + float lv = floor( -log( 1.0 - max( vUv.x, vUv.y ) ) / log( 2.0 ) ); + + if ( lv >= 5.0 ) { discard; } + + float p = pow( 0.5, lv + 1.0 ); vec2 uv00 = floor( vUv / p ) * p; vec2 uv11 = uv00 + p; vec2 uv = clamp( vUv, uv00 + halfTexel, uv11 - halfTexel ); @@ -82,7 +56,7 @@ void main() { uv = ( uv - 0.5 ) / UV_MARGIN + 0.5; vec3 tex = texture( sampler0, vUv ).xyz; - float roughness = lv * 0.2; + float roughness = lv * 0.333; float a = TAU * uv.x; float b = PI * ( uv.y - 0.5 ); @@ -90,18 +64,18 @@ void main() { vec3 R = N; vec3 V = R; - seed = uniformSeed + 500.0 * N.xyzx; + seed = uniformSeed + 500.0 * vec4( N.xy, uv00 ); vec4 col = vec4( 0.0 ); for ( int i = 0; i < SAMPLES; i ++ ) { vec2 Xi = vec2( prng( seed ), prng( seed ) ); - vec3 H = ImportanceSampleGGX( Xi, roughness, N ); + vec3 H = importanceSampleGGX( Xi, roughness, N ); vec3 L = normalize( 2.0 * dot( V, H ) * H - V ); float NoL = dot( N, L ); if ( NoL > 0.0 ) { - col += vec4( haha( L ), 1.0 ) * NoL; + col += texture( samplerCubemap, L ) * NoL; } } diff --git a/src/shaders/modules/brdfLambert.glsl b/src/shaders/modules/brdfLambert.glsl new file mode 100644 index 0000000..db5b212 --- /dev/null +++ b/src/shaders/modules/brdfLambert.glsl @@ -0,0 +1,14 @@ +// Ref: https://github.com/KhronosGroup/glTF-Sample-Viewer/blob/38b88e365728d7b2e28a78a57d84be2675fdb70d/source/Renderer/shaders/brdf.glsl + +const float PI = 3.14159265; +const vec3 DIELECTRIC_SPECULAR = vec3( 0.04 ); +const vec3 ONE_SUB_DIELECTRIC_SPECULAR = 1.0 - DIELECTRIC_SPECULAR; + +vec3 brdfLambert( vec3 f0, vec3 albedo, float VdotH ) { + // F_Schlick + vec3 F = f0 + ( 1.0 - f0 ) * pow( max( 0.0, 1.0 - VdotH ), 5.0 ); + + return ( 1.0 - F ) * albedo / PI; +} + +#pragma glslify: export(brdfLambert) diff --git a/src/shaders/modules/brdfSpecularGGX.glsl b/src/shaders/modules/brdfSpecularGGX.glsl new file mode 100644 index 0000000..96b8e8a --- /dev/null +++ b/src/shaders/modules/brdfSpecularGGX.glsl @@ -0,0 +1,26 @@ +// Ref: https://github.com/KhronosGroup/glTF-Sample-Viewer/blob/38b88e365728d7b2e28a78a57d84be2675fdb70d/source/Renderer/shaders/brdf.glsl + +const float PI = 3.14159265; +const vec3 DIELECTRIC_SPECULAR = vec3( 0.04 ); + +vec3 brdfSpecularGGX( vec3 f0, float roughness, float VdotH, float NdotL, float NdotV, float NdotH ) { + // F_Schlick + vec3 F = f0 + ( 1.0 - f0 ) * pow( max( 0.0, 1.0 - VdotH ), 5.0 ); + + // V_GGX + float roughnessSq = roughness * roughness; + + float GGXV = NdotL * sqrt( NdotV * NdotV * ( 1.0 - roughnessSq ) + roughnessSq ); + float GGXL = NdotV * sqrt( NdotL * NdotL * ( 1.0 - roughnessSq ) + roughnessSq ); + + float GGX = GGXV + GGXL; + float Vis = ( 0.0 < GGX ) ? ( 0.5 / GGX ) : 0.0; + + // D_GGX + float f = ( NdotH * NdotH ) * ( roughnessSq - 1.0 ) + 1.0; + float D = roughnessSq / ( PI * f * f ); + + return F * Vis * D; +} + +#pragma glslify: export(brdfSpecularGGX) diff --git a/src/shaders/modules/importanceSampleGGX.glsl b/src/shaders/modules/importanceSampleGGX.glsl new file mode 100644 index 0000000..5fdb252 --- /dev/null +++ b/src/shaders/modules/importanceSampleGGX.glsl @@ -0,0 +1,30 @@ +// Ref: https://learnopengl.com/PBR/IBL/Specular-IBL + +const float TAU = 6.28318530718; + +vec3 importanceSampleGGX( vec2 Xi, float roughness, vec3 N ) { + float a = roughness * roughness; + + float phi = TAU * Xi.x; + float cosTheta = roughness > 1.0 // use lambert ??? + ? cos( asin( sqrt( Xi.y ) ) ) + : sqrt( ( 1.0 - Xi.y ) / ( 1.0 + ( a * a - 1.0 ) * Xi.y ) ); + float sinTheta = sqrt( 1.0 - cosTheta * cosTheta ); + + // from spherical coordinates to cartesian coordinates + vec3 H = vec3( + cos( phi ) * sinTheta, + sin( phi ) * sinTheta, + cosTheta + ); + + // from tangent-space vector to world-space sample vector + vec3 up = abs( N.y ) < 0.999 ? vec3( 0.0, 1.0, 0.0 ) : vec3( 1.0, 0.0, 0.0 ); + vec3 tangent = normalize( cross( up, N ) ); + vec3 bitangent = cross( N, tangent ); + + vec3 sampleVec = tangent * H.x + bitangent * H.y + N * H.z; + return normalize( sampleVec ); +} + +#pragma glslify: export(importanceSampleGGX) diff --git a/src/shaders/raymarcher.frag b/src/shaders/raymarcher.frag index c88ec98..c9b4819 100644 --- a/src/shaders/raymarcher.frag +++ b/src/shaders/raymarcher.frag @@ -87,7 +87,7 @@ void main() { fragPosition = vec4( rayPos, depth ); fragNormal = vec4( normal, 1.0 ); fragColor = color; - fragWTF = vec4( vec3( 0.2, 0.2, 0.0 ), MTL_PBR ); + fragWTF = vec4( vec3( 0.2, 0.9, 0.0 ), MTL_PBR ); #endif #ifdef SHADOW diff --git a/src/shaders/rings.frag b/src/shaders/rings.frag index f83f17b..69bb340 100644 --- a/src/shaders/rings.frag +++ b/src/shaders/rings.frag @@ -23,7 +23,7 @@ uniform float time; void main() { #ifdef FORWARD - fragColor = vec4( 28.0 * vec3( 0.2, 0.9, 0.5 ), 1.0 ); + fragColor = vec4( 6.0 * vec3( 0.2, 0.9, 0.5 ), 1.0 ); #endif #ifdef DEFERRED diff --git a/src/shaders/shading.frag b/src/shaders/shading.frag index c327e65..cfe9897 100644 --- a/src/shaders/shading.frag +++ b/src/shaders/shading.frag @@ -8,7 +8,7 @@ const int MTL_PBR = 2; const int MTL_GRADIENT = 3; const int MTL_IRIDESCENT = 4; const int AO_ITER = 8; -const float ENV_UV_MARGIN = 0.9; +const float ENV_UV_MARGIN = 0.9375; const float AO_BIAS = 0.0; const float AO_RADIUS = 0.5; const float PI = 3.14159265359; @@ -16,10 +16,13 @@ const float TAU = 6.28318530718; const float EPSILON = 1E-3; const vec3 BLACK = vec3( 0.0 ); const vec3 DIELECTRIC_SPECULAR = vec3( 0.04 ); +const vec3 ONE_SUB_DIELECTRIC_SPECULAR = 1.0 - DIELECTRIC_SPECULAR; #define saturate(x) clamp(x,0.,1.) #define linearstep(a,b,x) saturate(((x)-(a))/((b)-(a))) +vec4 seed; + in vec2 vUv; out vec4 fragColor; @@ -44,6 +47,9 @@ uniform sampler2D samplerRandom; // == commons ====================================================================================== #pragma glslify: prng = require( ./-prng ); +#pragma glslify: brdfLambert = require( ./modules/brdfLambert.glsl ); +#pragma glslify: brdfSpecularGGX = require( ./modules/brdfSpecularGGX.glsl ); +#pragma glslify: importanceSampleGGX = require( ./modules/importanceSampleGGX.glsl ); vec3 catColor( float _p ) { return 0.5 + 0.5 * vec3( @@ -53,19 +59,6 @@ vec3 catColor( float _p ) { ); } -vec3 randomSphere( inout vec4 seed ) { - vec3 v; - for ( int i = 0; i < 10; i ++ ) { - v = vec3( - prng( seed ), - prng( seed ), - prng( seed ) - ) * 2.0 - 1.0; - if ( length( v ) < 1.0 ) { break; } - } - return v; -} - vec3 blurpleGradient( float t ) { vec3 colorA = vec3( 0.01, 0.04, 0.2 ); vec3 colorB = vec3( 0.02, 0.3, 0.9 ); @@ -105,7 +98,7 @@ vec4 sampleEnvLinear( vec2 uv, float lv ) { // == structs ====================================================================================== struct Isect { vec2 screenUv; - vec3 albedo; + vec3 color; vec3 position; float depth; vec3 normal; @@ -185,38 +178,41 @@ vec3 shadePBR( Isect isect, AngularInfo aI ) { float lenL = length( isect.position - lightPos ); float decay = 1.0 / ( lenL * lenL ); - vec3 F0 = mix( DIELECTRIC_SPECULAR, isect.albedo, metallic ); - vec3 F = F0 + ( 1.0 - F0 ) * pow( 1.0 - aI.dotVH, 5.0 ); + vec3 albedo = mix( isect.color * ONE_SUB_DIELECTRIC_SPECULAR, vec3( 0.0 ), metallic ); + vec3 f0 = mix( DIELECTRIC_SPECULAR, isect.color, metallic ); - float roughnessSq = roughness * roughness; - float GGXV = aI.dotNL * sqrt( aI.dotNV * aI.dotNV * ( 1.0 - roughnessSq ) + roughnessSq ); - float GGXL = aI.dotNV * sqrt( aI.dotNL * aI.dotNL * ( 1.0 - roughnessSq ) + roughnessSq ); - float GGX = GGXV + GGXL; - float Vis = ( 0.0 < GGX ) ? ( 0.5 / GGX ) : 0.0; + vec3 diffuse = brdfLambert( f0, albedo, aI.dotVH ); + vec3 spec = brdfSpecularGGX( f0, roughness, aI.dotVH, aI.dotNL, aI.dotNV, aI.dotNH ); - float f = ( aI.dotNH * roughnessSq - aI.dotNH ) * aI.dotNH + 1.0; - float D = roughnessSq / ( PI * f * f ); - - vec3 diffuse = max( vec3( 0.0 ), ( 1.0 - F ) * ( isect.albedo / PI ) ); - vec3 spec = max( vec3( 0.0 ), F * Vis * D ); vec3 shade = PI * lightColor * decay * shadow * aI.dotNL * ( diffuse + spec ); vec3 color = shade; #ifdef IS_FIRST_LIGHT - vec3 refl = reflect( aI.V, isect.normal ); - vec2 envUv = vec2( - 0.5 + atan( refl.x, -refl.z ) / TAU, - 0.5 + atan( refl.y, length( refl.zx ) ) / PI + + // cheat the texture seam using noise! + vec3 nEnvDiffuse = importanceSampleGGX( vec2( prng( seed ), prng( seed ) * 0.05 ), 2.0, isect.normal ); + + // diffuse ibl + vec2 uvEnvDiffuse = vec2( + 0.5 + atan( nEnvDiffuse.x, -nEnvDiffuse.z ) / TAU, + 0.5 + atan( nEnvDiffuse.y, length( nEnvDiffuse.zx ) ) / PI ); + vec3 texEnvDiffuse = sampleEnvNearest( uvEnvDiffuse, 4.0 ).rgb; + color += ao * texEnvDiffuse * albedo; // reflective ibl - vec2 brdf = texture( samplerIBLLUT, vec2( aI.dotNV, roughness ) ).xy; - vec3 texEnv = 0.2 * sampleEnvLinear( envUv, 5.0 * roughness ).rgb; - color += PI * ao * texEnv * ( brdf.x * F0 + brdf.y ); + vec3 reflEnvReflective = reflect( aI.V, isect.normal ); + vec2 uvEnvReflective = vec2( + 0.5 + atan( reflEnvReflective.x, -reflEnvReflective.z ) / TAU, + 0.5 + atan( reflEnvReflective.y, length( reflEnvReflective.zx ) ) / PI + ); + vec2 brdfEnvReflective = texture( samplerIBLLUT, vec2( aI.dotNV, roughness ) ).xy; + vec3 texEnvReflective = sampleEnvLinear( uvEnvReflective, 3.0 * roughness ).rgb; + color += ao * texEnvReflective * ( brdfEnvReflective.x * f0 + brdfEnvReflective.y ); // emissive - color += emissive * aI.dotNV * isect.albedo; + color += emissive * aI.dotNV * isect.color; #endif // IS_FIRST_LIGHT return color; @@ -243,12 +239,15 @@ void main() { vec4 tex2 = texture( sampler2, vUv ); vec4 tex3 = texture( sampler3, vUv ); + seed = texture( samplerRandom, vUv ) * 1919.810; + prng( seed ); + Isect isect; isect.screenUv = vUv; isect.position = tex0.xyz; isect.depth = tex0.w; isect.normal = tex1.xyz; - isect.albedo = tex2.rgb; + isect.color = tex2.rgb; isect.materialId = int( tex3.w + 0.5 ); isect.materialParams = tex3.xyz; @@ -259,7 +258,7 @@ void main() { } else if ( isect.materialId == MTL_UNLIT ) { #ifdef IS_FIRST_LIGHT - color = isect.albedo; + color = isect.color; #endif } else if ( isect.materialId == MTL_PBR ) { @@ -271,7 +270,7 @@ void main() { } else if ( isect.materialId == MTL_IRIDESCENT ) { AngularInfo aI = genAngularInfo( isect ); - isect.albedo *= mix( + isect.color *= mix( vec3( 1.0 ), catColor( isect.materialParams.x * aI.dotNV ), isect.materialParams.y diff --git a/src/shaders/sphere-particles-render.frag b/src/shaders/sphere-particles-render.frag index 55e7b24..035c692 100644 --- a/src/shaders/sphere-particles-render.frag +++ b/src/shaders/sphere-particles-render.frag @@ -41,6 +41,6 @@ void main() { fragPosition = vPosition; fragNormal = vec4( vNormal, 1.0 ); fragColor = vec4( vColor.xyz, 1.0 ); - fragWTF = vec4( vec3( 0.1, 0.2, 0.0 ), MTL_PBR ); + fragWTF = vec4( vec3( 0.9, 0.1, 0.0 ), MTL_PBR ); #endif }