mirror of
https://github.com/FMS-Cat/condition.git
synced 2025-08-31 00:59:52 +02:00
feature: IBL??
This commit is contained in:
@@ -17,6 +17,8 @@ export interface CameraEntityOptions {
|
|||||||
root: Entity;
|
root: Entity;
|
||||||
target: RenderTarget;
|
target: RenderTarget;
|
||||||
lights: LightEntity[];
|
lights: LightEntity[];
|
||||||
|
textureIBLLUT: GLCatTexture<WebGL2RenderingContext>;
|
||||||
|
textureEnv: GLCatTexture<WebGL2RenderingContext>;
|
||||||
textureRandom: GLCatTexture<WebGL2RenderingContext>;
|
textureRandom: GLCatTexture<WebGL2RenderingContext>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,6 +187,8 @@ export class CameraEntity {
|
|||||||
shadingMaterial.blend = [ gl.ONE, gl.ONE ];
|
shadingMaterial.blend = [ gl.ONE, gl.ONE ];
|
||||||
shadingMaterial.addUniformTexture( 'samplerAo', aoTarget.texture );
|
shadingMaterial.addUniformTexture( 'samplerAo', aoTarget.texture );
|
||||||
shadingMaterial.addUniformTexture( 'samplerShadow', light.shadowMap.texture );
|
shadingMaterial.addUniformTexture( 'samplerShadow', light.shadowMap.texture );
|
||||||
|
shadingMaterial.addUniformTexture( 'samplerIBLLUT', options.textureIBLLUT );
|
||||||
|
shadingMaterial.addUniformTexture( 'samplerEnv', options.textureEnv );
|
||||||
shadingMaterial.addUniformTexture( 'samplerRandom', options.textureRandom );
|
shadingMaterial.addUniformTexture( 'samplerRandom', options.textureRandom );
|
||||||
|
|
||||||
const shadingQuad = new Quad( {
|
const shadingQuad = new Quad( {
|
||||||
|
81
src/entities/EnvironmentMap.ts
Normal file
81
src/entities/EnvironmentMap.ts
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
import { Entity } from '../heck/Entity';
|
||||||
|
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 quadVert from '../shaders/quad.vert';
|
||||||
|
import { BufferRenderTarget } from '../heck/BufferRenderTarget';
|
||||||
|
import { Swap, Xorshift } from '@fms-cat/experimental';
|
||||||
|
import { Lambda } from '../heck/components/Lambda';
|
||||||
|
|
||||||
|
const WIDTH = 1024;
|
||||||
|
const HEIGHT = 512;
|
||||||
|
|
||||||
|
export class EnvironmentMap {
|
||||||
|
public entity: Entity;
|
||||||
|
|
||||||
|
public swap: Swap<BufferRenderTarget>;
|
||||||
|
|
||||||
|
public get texture(): GLCatTexture<WebGL2RenderingContext> {
|
||||||
|
return this.swap.o.texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
public constructor() {
|
||||||
|
this.entity = new Entity();
|
||||||
|
this.entity.visible = false;
|
||||||
|
|
||||||
|
const rng = new Xorshift( 114514 );
|
||||||
|
|
||||||
|
// -- swap -------------------------------------------------------------------------------------
|
||||||
|
this.swap = new Swap(
|
||||||
|
new BufferRenderTarget( {
|
||||||
|
width: WIDTH,
|
||||||
|
height: HEIGHT,
|
||||||
|
name: process.env.DEV && 'EnvironmentMap/swap0',
|
||||||
|
} ),
|
||||||
|
new BufferRenderTarget( {
|
||||||
|
width: WIDTH,
|
||||||
|
height: HEIGHT,
|
||||||
|
name: process.env.DEV && 'EnvironmentMap/swap1',
|
||||||
|
} ),
|
||||||
|
);
|
||||||
|
|
||||||
|
// -- post -------------------------------------------------------------------------------------
|
||||||
|
const material = new Material(
|
||||||
|
quadVert,
|
||||||
|
environmentMapFrag,
|
||||||
|
);
|
||||||
|
material.addUniform( 'uniformSeed', '4f', rng.gen(), rng.gen(), rng.gen(), rng.gen() );
|
||||||
|
material.addUniformTexture( 'sampler0', this.swap.i.texture );
|
||||||
|
|
||||||
|
if ( process.env.DEV ) {
|
||||||
|
if ( module.hot ) {
|
||||||
|
module.hot.accept( '../shaders/environment-map.frag', () => {
|
||||||
|
material.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( quad );
|
||||||
|
}
|
||||||
|
}
|
80
src/entities/IBLLUT.ts
Normal file
80
src/entities/IBLLUT.ts
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
import { Entity } from '../heck/Entity';
|
||||||
|
import { GLCatTexture } from '@fms-cat/glcat-ts';
|
||||||
|
import { Material } from '../heck/Material';
|
||||||
|
import { Quad } from '../heck/components/Quad';
|
||||||
|
import iblLutFrag from '../shaders/ibl-lut.frag';
|
||||||
|
import quadVert from '../shaders/quad.vert';
|
||||||
|
import { BufferRenderTarget } from '../heck/BufferRenderTarget';
|
||||||
|
import { Swap } from '@fms-cat/experimental';
|
||||||
|
import { Lambda } from '../heck/components/Lambda';
|
||||||
|
import { vdc } from '../utils/vdc';
|
||||||
|
|
||||||
|
const IBL_SIZE = 256;
|
||||||
|
|
||||||
|
export class IBLLUT {
|
||||||
|
public entity: Entity;
|
||||||
|
|
||||||
|
public swap: Swap<BufferRenderTarget>;
|
||||||
|
|
||||||
|
public get texture(): GLCatTexture<WebGL2RenderingContext> {
|
||||||
|
return this.swap.o.texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
public constructor() {
|
||||||
|
this.entity = new Entity();
|
||||||
|
this.entity.visible = false;
|
||||||
|
|
||||||
|
// -- swap -------------------------------------------------------------------------------------
|
||||||
|
this.swap = new Swap(
|
||||||
|
new BufferRenderTarget( {
|
||||||
|
width: IBL_SIZE,
|
||||||
|
height: IBL_SIZE,
|
||||||
|
name: process.env.DEV && 'IBLLUT/swap0',
|
||||||
|
} ),
|
||||||
|
new BufferRenderTarget( {
|
||||||
|
width: IBL_SIZE,
|
||||||
|
height: IBL_SIZE,
|
||||||
|
name: process.env.DEV && 'IBLLUT/swap1',
|
||||||
|
} ),
|
||||||
|
);
|
||||||
|
|
||||||
|
// -- post -------------------------------------------------------------------------------------
|
||||||
|
let samples = 0.0;
|
||||||
|
|
||||||
|
const material = new Material(
|
||||||
|
quadVert,
|
||||||
|
iblLutFrag,
|
||||||
|
);
|
||||||
|
material.addUniform( 'samples', '1f', samples );
|
||||||
|
material.addUniform( 'vdc', '1f', vdc( samples, 2.0 ) );
|
||||||
|
material.addUniformTexture( 'sampler0', this.swap.i.texture );
|
||||||
|
|
||||||
|
const quad = new Quad( {
|
||||||
|
target: this.swap.o,
|
||||||
|
material,
|
||||||
|
name: process.env.DEV && 'IBLLUT/quad',
|
||||||
|
} );
|
||||||
|
|
||||||
|
// -- swapper ----------------------------------------------------------------------------------
|
||||||
|
this.entity.components.push( new Lambda( {
|
||||||
|
onUpdate: () => {
|
||||||
|
samples ++;
|
||||||
|
this.swap.swap();
|
||||||
|
|
||||||
|
if ( samples > 1024 ) {
|
||||||
|
this.entity.active = false;
|
||||||
|
} else {
|
||||||
|
material.addUniform( 'samples', '1f', samples );
|
||||||
|
material.addUniform( 'vdc', '1f', vdc( samples, 2.0 ) );
|
||||||
|
material.addUniformTexture( 'sampler0', this.swap.i.texture );
|
||||||
|
|
||||||
|
quad.target = this.swap.o;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
visible: false,
|
||||||
|
name: process.env.DEV && 'IBLLUT/swapper',
|
||||||
|
} ) );
|
||||||
|
|
||||||
|
this.entity.components.push( quad );
|
||||||
|
}
|
||||||
|
}
|
37
src/main.ts
37
src/main.ts
@@ -24,6 +24,8 @@ import { RTInspector } from './entities/RTInspector';
|
|||||||
import { Component } from './heck/components/Component';
|
import { Component } from './heck/components/Component';
|
||||||
import { FlickyParticles } from './entities/FlickyParticles';
|
import { FlickyParticles } from './entities/FlickyParticles';
|
||||||
import { PixelSorter } from './entities/PixelSorter';
|
import { PixelSorter } from './entities/PixelSorter';
|
||||||
|
import { IBLLUT } from './entities/IBLLUT';
|
||||||
|
import { EnvironmentMap } from './entities/EnvironmentMap';
|
||||||
|
|
||||||
// == music ========================================================================================
|
// == music ========================================================================================
|
||||||
const audio = new AudioContext();
|
const audio = new AudioContext();
|
||||||
@@ -193,6 +195,13 @@ dog.root.components.push( new Lambda( {
|
|||||||
name: process.env.DEV && 'main/update',
|
name: process.env.DEV && 'main/update',
|
||||||
} ) );
|
} ) );
|
||||||
|
|
||||||
|
// -- bake -----------------------------------------------------------------------------------------
|
||||||
|
const ibllut = new IBLLUT();
|
||||||
|
dog.root.children.push( ibllut.entity );
|
||||||
|
|
||||||
|
const environmentMap = new EnvironmentMap();
|
||||||
|
dog.root.children.push( environmentMap.entity );
|
||||||
|
|
||||||
// -- "objects" ------------------------------------------------------------------------------------
|
// -- "objects" ------------------------------------------------------------------------------------
|
||||||
const sphereParticles = new SphereParticles( {
|
const sphereParticles = new SphereParticles( {
|
||||||
particlesSqrt: 256,
|
particlesSqrt: 256,
|
||||||
@@ -201,13 +210,13 @@ const sphereParticles = new SphereParticles( {
|
|||||||
} );
|
} );
|
||||||
dog.root.children.push( sphereParticles.entity );
|
dog.root.children.push( sphereParticles.entity );
|
||||||
|
|
||||||
const trails = new Trails( {
|
// const trails = new Trails( {
|
||||||
trails: 4096,
|
// trails: 4096,
|
||||||
trailLength: 64,
|
// trailLength: 64,
|
||||||
textureRandom: randomTexture.texture,
|
// textureRandom: randomTexture.texture,
|
||||||
textureRandomStatic: randomTextureStatic.texture
|
// textureRandomStatic: randomTextureStatic.texture
|
||||||
} );
|
// } );
|
||||||
dog.root.children.push( trails.entity );
|
// dog.root.children.push( trails.entity );
|
||||||
|
|
||||||
const rings = new Rings();
|
const rings = new Rings();
|
||||||
dog.root.children.push( rings.entity );
|
dog.root.children.push( rings.entity );
|
||||||
@@ -219,11 +228,11 @@ const flickyParticles = new FlickyParticles( {
|
|||||||
} );
|
} );
|
||||||
dog.root.children.push( flickyParticles.entity );
|
dog.root.children.push( flickyParticles.entity );
|
||||||
|
|
||||||
const raymarcher = new Raymarcher( {
|
// const raymarcher = new Raymarcher( {
|
||||||
textureRandom: randomTexture.texture,
|
// textureRandom: randomTexture.texture,
|
||||||
textureRandomStatic: randomTextureStatic.texture
|
// textureRandomStatic: randomTextureStatic.texture
|
||||||
} );
|
// } );
|
||||||
dog.root.children.push( raymarcher.entity );
|
// dog.root.children.push( raymarcher.entity );
|
||||||
|
|
||||||
// -- things that is not an "object" ---------------------------------------------------------------
|
// -- things that is not an "object" ---------------------------------------------------------------
|
||||||
const swapOptions = {
|
const swapOptions = {
|
||||||
@@ -249,7 +258,7 @@ const light = new LightEntity( {
|
|||||||
shadowMapFar: 20.0,
|
shadowMapFar: 20.0,
|
||||||
namePrefix: process.env.DEV && 'light1',
|
namePrefix: process.env.DEV && 'light1',
|
||||||
} );
|
} );
|
||||||
light.color = [ 60.0, 60.0, 60.0 ];
|
light.color = [ 40.0, 40.0, 40.0 ];
|
||||||
light.entity.transform.lookAt( new Vector3( [ -1.0, 2.0, 8.0 ] ) );
|
light.entity.transform.lookAt( new Vector3( [ -1.0, 2.0, 8.0 ] ) );
|
||||||
dog.root.children.push( light.entity );
|
dog.root.children.push( light.entity );
|
||||||
|
|
||||||
@@ -271,6 +280,8 @@ const camera = new CameraEntity( {
|
|||||||
light,
|
light,
|
||||||
// light2
|
// light2
|
||||||
],
|
],
|
||||||
|
textureIBLLUT: ibllut.texture,
|
||||||
|
textureEnv: environmentMap.texture,
|
||||||
textureRandom: randomTexture.texture
|
textureRandom: randomTexture.texture
|
||||||
} );
|
} );
|
||||||
camera.camera.clear = [ 0.0, 0.0, 0.0, 0.0 ];
|
camera.camera.clear = [ 0.0, 0.0, 0.0, 0.0 ];
|
||||||
|
@@ -9,12 +9,16 @@ out vec4 fragColor;
|
|||||||
uniform sampler2D samplerDry;
|
uniform sampler2D samplerDry;
|
||||||
uniform sampler2D samplerWet;
|
uniform sampler2D samplerWet;
|
||||||
|
|
||||||
|
vec4 sampleLOD( vec2 uv, float lv ) {
|
||||||
|
float p = pow( 0.5, float( lv ) );
|
||||||
|
vec2 uvt = mix( vec2( 1.0 - p ), vec2( 1.0 - 0.5 * p ), uv );
|
||||||
|
return texture( samplerWet, uvt );
|
||||||
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
fragColor = texture( samplerDry, vUv );
|
fragColor = texture( samplerDry, vUv );
|
||||||
for ( int i = 0; i < 5; i ++ ) {
|
for ( int i = 0; i < 5; i ++ ) {
|
||||||
float fuck = pow( 0.5, float( i ) );
|
fragColor += sampleLOD( vUv, float( i ) );
|
||||||
vec2 suv = mix( vec2( 1.0 - fuck ), vec2( 1.0 - 0.5 * fuck ), vUv );
|
|
||||||
fragColor += texture( samplerWet, suv );
|
|
||||||
}
|
}
|
||||||
fragColor.xyz = max( vec3( 0.0 ), fragColor.xyz );
|
fragColor.xyz = max( vec3( 0.0 ), fragColor.xyz );
|
||||||
}
|
}
|
||||||
|
119
src/shaders/environment-map.frag
Normal file
119
src/shaders/environment-map.frag
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
#version 300 es
|
||||||
|
|
||||||
|
// https://learnopengl.com/PBR/IBL/Specular-IBL
|
||||||
|
|
||||||
|
precision highp float;
|
||||||
|
|
||||||
|
#define saturate(x) clamp(x,0.,1.)
|
||||||
|
#define linearstep(a,b,x) saturate(((x)-(a))/((b)-(a)))
|
||||||
|
|
||||||
|
#pragma glslify: prng = require( ./-prng );
|
||||||
|
|
||||||
|
const int SAMPLES = 16;
|
||||||
|
const float UV_MARGIN = 0.9;
|
||||||
|
const float PI = 3.14159265;
|
||||||
|
const float TAU = 6.283185307;
|
||||||
|
|
||||||
|
in vec2 vUv;
|
||||||
|
|
||||||
|
out vec4 fragColor;
|
||||||
|
|
||||||
|
uniform float head;
|
||||||
|
uniform vec2 resolution;
|
||||||
|
uniform vec4 uniformSeed;
|
||||||
|
uniform sampler2D sampler0;
|
||||||
|
|
||||||
|
vec4 seed;
|
||||||
|
|
||||||
|
float vdc( float i, float base ) {
|
||||||
|
float r = 0.0;
|
||||||
|
float denom = 1.0;
|
||||||
|
|
||||||
|
for ( int j = 0; j < 32; j ++ ) {
|
||||||
|
denom *= base;
|
||||||
|
r += mod( i, base ) / denom;
|
||||||
|
i = floor( i / base );
|
||||||
|
|
||||||
|
if ( i <= 0.0 ) { break; }
|
||||||
|
}
|
||||||
|
|
||||||
|
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 ) {
|
||||||
|
bool circ = dot( L, normalize( vec3( 1.0, 3.0, 3.0 ) ) ) > 0.9;
|
||||||
|
vec3 c = circ ? 0.01 * vec3( 0.1, 0.1, 1.0 ) : vec3( 0.0 );
|
||||||
|
|
||||||
|
bool ring = abs( dot( L, vec3( 0.0, 1.0, 0.0 ) ) ) < 0.1;
|
||||||
|
c += ring ? 10.0 * vec3( 0.1, 1.0, 0.3 ) : vec3( 0.0 );
|
||||||
|
|
||||||
|
return c;
|
||||||
|
// return 0.5 + 0.5 * L;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 uv00 = floor( vUv / p ) * p;
|
||||||
|
vec2 uv11 = uv00 + p;
|
||||||
|
vec2 uv = clamp( vUv, uv00 + halfTexel, uv11 - halfTexel );
|
||||||
|
uv = linearstep( uv00, uv11, uv );
|
||||||
|
uv = ( uv - 0.5 ) / UV_MARGIN + 0.5;
|
||||||
|
|
||||||
|
vec3 tex = texture( sampler0, vUv ).xyz;
|
||||||
|
float roughness = lv * 0.2;
|
||||||
|
|
||||||
|
float a = TAU * uv.x;
|
||||||
|
float b = PI * ( uv.y - 0.5 );
|
||||||
|
vec3 N = vec3( sin( a ) * cos( b ), -sin( b ), -cos( a ) * cos( b ) );
|
||||||
|
vec3 R = N;
|
||||||
|
vec3 V = R;
|
||||||
|
|
||||||
|
seed = uniformSeed + N.xyzx;
|
||||||
|
|
||||||
|
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 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.xyz = col.w <= 0.001 ? vec3( 0.0 ) : ( col.xyz / col.w );
|
||||||
|
|
||||||
|
tex.xyz = mix( tex.xyz, col.xyz, 1.0 / 16.0 );
|
||||||
|
|
||||||
|
fragColor = vec4( tex, 1.0 );
|
||||||
|
}
|
90
src/shaders/ibl-lut.frag
Normal file
90
src/shaders/ibl-lut.frag
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
#version 300 es
|
||||||
|
|
||||||
|
precision highp float;
|
||||||
|
|
||||||
|
const float TAU = 6.283185307;
|
||||||
|
|
||||||
|
in vec2 vUv;
|
||||||
|
|
||||||
|
out vec4 fragColor;
|
||||||
|
|
||||||
|
uniform float samples;
|
||||||
|
uniform float vdc;
|
||||||
|
uniform sampler2D sampler0;
|
||||||
|
|
||||||
|
vec3 ImportanceSampleGGX( vec2 Xi, float roughness, vec3 N ) {
|
||||||
|
float a = roughness * roughness;
|
||||||
|
|
||||||
|
float phi = TAU * Xi.x;
|
||||||
|
float cosTheta = 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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
float GeometrySchlickGGX( float NdotV, float roughness ) {
|
||||||
|
float a = roughness;
|
||||||
|
float k = ( a * a ) / 2.0;
|
||||||
|
|
||||||
|
float nom = NdotV;
|
||||||
|
float denom = NdotV * ( 1.0 - k ) + k;
|
||||||
|
|
||||||
|
return nom / denom;
|
||||||
|
}
|
||||||
|
|
||||||
|
float GeometrySmith( float roughness, float NoV, float NoL ) {
|
||||||
|
float ggx2 = GeometrySchlickGGX( NoV, roughness );
|
||||||
|
float ggx1 = GeometrySchlickGGX( NoL, roughness );
|
||||||
|
|
||||||
|
return ggx1 * ggx2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/HectorMF/BRDFGenerator/blob/master/BRDFGenerator/BRDFGenerator.cpp
|
||||||
|
vec2 IntegrateBRDF( float NdotV, float roughness ) {
|
||||||
|
vec3 V = vec3( sqrt( 1.0 - NdotV * NdotV ), 0.0, NdotV );
|
||||||
|
vec3 N = vec3( 0.0, 0.0, 1.0 );
|
||||||
|
|
||||||
|
vec2 Xi = vec2( samples / 1024.0, vdc );
|
||||||
|
vec3 H = ImportanceSampleGGX( Xi, roughness, N );
|
||||||
|
vec3 L = normalize( 2.0 * dot( V, H ) * H - V );
|
||||||
|
|
||||||
|
float NoL = max( L.z, 0.0 );
|
||||||
|
float NoH = max( H.z, 0.0 );
|
||||||
|
float VoH = max( dot( V, H ), 0.0 );
|
||||||
|
float NoV = max( dot( N, V ), 0.0 );
|
||||||
|
|
||||||
|
if ( NoL > 0.0 ) {
|
||||||
|
float G = GeometrySmith( roughness, NoV, NoL );
|
||||||
|
|
||||||
|
float G_Vis = ( G * VoH ) / ( NoH * NoV );
|
||||||
|
float Fc = pow( 1.0 - VoH, 5.0 );
|
||||||
|
|
||||||
|
return vec2( ( 1.0 - Fc ) * G_Vis, Fc * G_Vis );
|
||||||
|
}
|
||||||
|
|
||||||
|
return vec2( 0.0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 tex = texture( sampler0, vUv ).xy;
|
||||||
|
|
||||||
|
float NdotV = vUv.y;
|
||||||
|
float roughness = vUv.x;
|
||||||
|
|
||||||
|
tex = mix( tex, IntegrateBRDF( NdotV, roughness ), 1.0 / samples );
|
||||||
|
|
||||||
|
fragColor = vec4( tex, 0.0, 1.0 );
|
||||||
|
}
|
@@ -70,7 +70,7 @@ void main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
vec3 normal = normalFunc( rayPos, 1E-4 );
|
vec3 normal = normalFunc( rayPos, 1E-4 );
|
||||||
vec4 color = vec4( 0.1, 0.2, 0.4, 1.0 );
|
vec4 color = vec4( 0.4, 0.7, 0.9, 1.0 );
|
||||||
|
|
||||||
vec4 projPos = projectionMatrix * viewMatrix * vec4( rayPos, 1.0 ); // terrible
|
vec4 projPos = projectionMatrix * viewMatrix * vec4( rayPos, 1.0 ); // terrible
|
||||||
float depth = projPos.z / projPos.w;
|
float depth = projPos.z / projPos.w;
|
||||||
@@ -79,5 +79,5 @@ void main() {
|
|||||||
fragPosition = vec4( rayPos, depth );
|
fragPosition = vec4( rayPos, depth );
|
||||||
fragNormal = vec4( normal, 1.0 );
|
fragNormal = vec4( normal, 1.0 );
|
||||||
fragColor = color;
|
fragColor = color;
|
||||||
fragWTF = vec4( vec3( 2.0, 0.9, 0.9 ), MTL_IRIDESCENT );
|
fragWTF = vec4( vec3( 0.8, 0.8, 0.0 ), MTL_PBR );
|
||||||
}
|
}
|
||||||
|
@@ -8,6 +8,7 @@ const int MTL_PBR = 2;
|
|||||||
const int MTL_GRADIENT = 3;
|
const int MTL_GRADIENT = 3;
|
||||||
const int MTL_IRIDESCENT = 4;
|
const int MTL_IRIDESCENT = 4;
|
||||||
const int AO_ITER = 8;
|
const int AO_ITER = 8;
|
||||||
|
const float ENV_UV_MARGIN = 0.9;
|
||||||
const float AO_BIAS = 0.0;
|
const float AO_BIAS = 0.0;
|
||||||
const float AO_RADIUS = 0.5;
|
const float AO_RADIUS = 0.5;
|
||||||
const float PI = 3.14159265359;
|
const float PI = 3.14159265359;
|
||||||
@@ -36,7 +37,7 @@ uniform sampler2D sampler1; // normal.xyz (yes, this is not good)
|
|||||||
uniform sampler2D sampler2; // color.rgba (what is a though????)
|
uniform sampler2D sampler2; // color.rgba (what is a though????)
|
||||||
uniform sampler2D sampler3; // materialParams.xyz, materialId
|
uniform sampler2D sampler3; // materialParams.xyz, materialId
|
||||||
uniform sampler2D samplerShadow;
|
uniform sampler2D samplerShadow;
|
||||||
uniform sampler2D samplerBRDFLUT;
|
uniform sampler2D samplerIBLLUT;
|
||||||
uniform sampler2D samplerEnv;
|
uniform sampler2D samplerEnv;
|
||||||
uniform sampler2D samplerAo;
|
uniform sampler2D samplerAo;
|
||||||
uniform sampler2D samplerRandom;
|
uniform sampler2D samplerRandom;
|
||||||
@@ -86,6 +87,21 @@ vec3 blurpleGradient( float t ) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vec4 sampleEnvNearest( vec2 uv, float lv ) {
|
||||||
|
float p = pow( 0.5, float( lv ) );
|
||||||
|
vec2 uvt = ENV_UV_MARGIN * ( uv - 0.5 ) + 0.5;
|
||||||
|
uvt = mix( vec2( 1.0 - p ), vec2( 1.0 - 0.5 * p ), uvt );
|
||||||
|
return texture( samplerEnv, uvt );
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 sampleEnvLinear( vec2 uv, float lv ) {
|
||||||
|
return mix(
|
||||||
|
sampleEnvNearest( uv, floor( lv ) ),
|
||||||
|
sampleEnvNearest( uv, floor( lv + 1.0 ) ),
|
||||||
|
fract( lv )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// == structs ======================================================================================
|
// == structs ======================================================================================
|
||||||
struct Isect {
|
struct Isect {
|
||||||
vec2 screenUv;
|
vec2 screenUv;
|
||||||
@@ -188,16 +204,18 @@ vec3 shadePBR( Isect isect, AngularInfo aI ) {
|
|||||||
vec3 color = shade;
|
vec3 color = shade;
|
||||||
|
|
||||||
#ifdef IS_FIRST_LIGHT
|
#ifdef IS_FIRST_LIGHT
|
||||||
// vec3 refl = reflect( aI.V, isect.normal );
|
vec3 refl = reflect( aI.V, isect.normal );
|
||||||
// vec2 envCoord = vec2(
|
vec2 envUv = vec2(
|
||||||
// 0.5 + atan( refl.z, refl.x ) / TAU,
|
0.5 + atan( refl.x, -refl.z ) / TAU,
|
||||||
// 0.5 + atan( refl.y, length( refl.zx ) ) / PI
|
0.5 + atan( refl.y, length( refl.zx ) ) / PI
|
||||||
// );
|
);
|
||||||
// vec2 brdf = texture( samplerBRDFLUT, vec2( aI.dotNV, 1.0 - roughness ) ).xy;
|
|
||||||
|
|
||||||
// vec3 texEnv = 0.2 * pow( texture( samplerEnv, envCoord ).rgb, vec3( 2.2 ) );
|
// reflective ibl
|
||||||
// color += PI * texEnv * ( brdf.x * F0 + brdf.y );
|
vec2 brdf = texture( samplerIBLLUT, vec2( aI.dotNV, 1.0 - roughness ) ).xy;
|
||||||
|
vec3 texEnv = 0.2 * sampleEnvLinear( envUv, 5.0 * roughness ).rgb;
|
||||||
|
color += PI * ao * texEnv * ( brdf.x * F0 + brdf.y );
|
||||||
|
|
||||||
|
// emissive
|
||||||
color += emissive * aI.dotNV * isect.albedo;
|
color += emissive * aI.dotNV * isect.albedo;
|
||||||
#endif // IS_FIRST_LIGHT
|
#endif // IS_FIRST_LIGHT
|
||||||
|
|
||||||
|
@@ -38,5 +38,5 @@ void main() {
|
|||||||
fragPosition = vPosition;
|
fragPosition = vPosition;
|
||||||
fragNormal = vec4( vNormal, 1.0 );
|
fragNormal = vec4( vNormal, 1.0 );
|
||||||
fragColor = vec4( vColor.xyz, 1.0 );
|
fragColor = vec4( vColor.xyz, 1.0 );
|
||||||
fragWTF = vec4( vec3( 0.8, 0.8, 0.0 ), MTL_PBR );
|
fragWTF = vec4( vec3( 0.4, 0.4, 0.0 ), MTL_PBR );
|
||||||
}
|
}
|
||||||
|
@@ -83,15 +83,15 @@ void main() {
|
|||||||
size *= sin( PI * saturate( vLife ) );
|
size *= sin( PI * saturate( vLife ) );
|
||||||
|
|
||||||
vec3 shape = position * size;
|
vec3 shape = position * size;
|
||||||
shape.yz = rotate2D( 7.0 * vPosition.x ) * shape.yz;
|
shape.yz = rotate2D( 7.0 * ( vPosition.x + vDice.z ) ) * shape.yz;
|
||||||
shape.zx = rotate2D( 7.0 * vPosition.y ) * shape.zx;
|
shape.zx = rotate2D( 7.0 * ( vPosition.y + vDice.w ) ) * shape.zx;
|
||||||
|
|
||||||
vPosition.xyz += shape;
|
vPosition.xyz += shape;
|
||||||
|
|
||||||
// == compute normals ============================================================================
|
// == compute normals ============================================================================
|
||||||
vNormal = ( normalMatrix * vec4( normal, 1.0 ) ).xyz;
|
vNormal = ( normalMatrix * vec4( normal, 1.0 ) ).xyz;
|
||||||
vNormal.yz = rotate2D( 7.0 * vPosition.x ) * vNormal.yz;
|
vNormal.yz = rotate2D( 7.0 * ( vPosition.x + vDice.z ) ) * vNormal.yz;
|
||||||
vNormal.zx = rotate2D( 7.0 * vPosition.y ) * vNormal.zx;
|
vNormal.zx = rotate2D( 7.0 * ( vPosition.y + vDice.w ) ) * vNormal.zx;
|
||||||
|
|
||||||
// == send the vertex position ===================================================================
|
// == send the vertex position ===================================================================
|
||||||
vPosition = modelMatrix * vPosition;
|
vPosition = modelMatrix * vPosition;
|
||||||
|
18
src/utils/vdc.ts
Normal file
18
src/utils/vdc.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* Generate a number using Van der Corput sequence.
|
||||||
|
* e.g. vdc(i, 2) = 1/2, 1/4, 3/4, 1/8, 5/8, 3/8, 7/8, 1/16, ...
|
||||||
|
* @param i Index of the sequence
|
||||||
|
* @param base Base of the sequence
|
||||||
|
*/
|
||||||
|
export function vdc( i: number, base: number ) {
|
||||||
|
let r = 0;
|
||||||
|
let denom = 1;
|
||||||
|
|
||||||
|
while ( 0 < i ) {
|
||||||
|
denom *= base;
|
||||||
|
r += ( i % base ) / denom;
|
||||||
|
i = Math.floor( i / base );
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
Reference in New Issue
Block a user