feature: improve multiple light + improve condition

This commit is contained in:
FMS-Cat
2021-03-28 19:44:09 +09:00
parent ee7300b226
commit 892260d8cd
10 changed files with 236 additions and 209 deletions

File diff suppressed because one or more lines are too long

View File

@@ -91,121 +91,122 @@ export class CameraEntity extends Entity {
name: process.env.DEV && 'CameraEntity/ao/quad',
} );
const shadingMaterials = options.lights.map( ( light, iLight ) => {
const shadingMaterial = new Material(
quadVert,
shadingFrag,
{
defines: iLight === 0 ? [ 'IS_FIRST_LIGHT' ] : [],
initOptions: { geometry: quadGeometry, target: dummyRenderTarget },
},
);
const shadingMaterial = new Material(
quadVert,
shadingFrag,
{
initOptions: { geometry: quadGeometry, target: dummyRenderTarget },
},
);
const shadingQuad = new Quad( {
material: shadingMaterial,
target: options.target,
name: process.env.DEV && 'CameraEntity/shading/quad',
} );
shadingQuad.clear = iLight === 0 ? [] : false;
const lambda = new Lambda( {
onUpdate: ( { frameCount } ) => {
const lightHasUpdated = frameCount === light.lastUpdateFrame;
shadingQuad.active = lightHasUpdated;
if ( !lightHasUpdated ) {
return;
}
const cameraView = this.transform.matrix.inverse!;
shadingMaterial.addUniformMatrixVector(
'cameraView',
'Matrix4fv',
cameraView.elements
);
shadingMaterial.addUniformMatrixVector(
'cameraPV',
'Matrix4fv',
this.camera.projectionMatrix.multiply(
cameraView
).elements
);
shadingMaterial.addUniform(
'lightNearFar',
'2f',
light.camera.near,
light.camera.far
);
shadingMaterial.addUniform(
'cameraNearFar',
'2f',
this.camera.near,
this.camera.far
);
shadingMaterial.addUniform(
'cameraPos',
'3f',
...this.transform.position.elements
);
shadingMaterial.addUniform(
'lightPos',
'3f',
...light.transform.position.elements
);
shadingMaterial.addUniform(
'lightColor',
'3f',
...light.color
);
shadingMaterial.addUniformMatrixVector(
'lightPV',
'Matrix4fv',
light.camera.projectionMatrix.multiply(
light.transform.matrix.inverse!
).elements
);
},
name: process.env.DEV && 'CameraEntity/shading/setCameraUniforms',
} );
for ( let i = 0; i < 4; i ++ ) {
shadingMaterial.addUniformTexture(
'sampler' + i,
cameraTarget.getTexture( gl.COLOR_ATTACHMENT0 + i )
);
}
shadingMaterial.blend = [ gl.ONE, gl.ONE ];
shadingMaterial.addUniformTexture( 'samplerAo', aoTarget.texture );
shadingMaterial.addUniformTexture( 'samplerShadow', light.shadowMap.texture );
shadingMaterial.addUniformTexture( 'samplerIBLLUT', options.textureIBLLUT );
shadingMaterial.addUniformTexture( 'samplerEnv', options.textureEnv );
shadingMaterial.addUniformTexture( 'samplerRandom', randomTexture.texture );
this.components.push(
this.camera,
lambdaAoSetCameraUniforms,
aoQuad,
lambda,
shadingQuad,
);
return shadingMaterial;
const shadingQuad = new Quad( {
material: shadingMaterial,
target: options.target,
name: process.env.DEV && 'CameraEntity/shading/quad',
} );
shadingQuad.clear = [];
const lambda = new Lambda( {
onUpdate: ( { frameCount } ) => {
const lights = options.lights.filter( ( light ) => (
frameCount === light.lastUpdateFrame
) );
const cameraView = this.transform.matrix.inverse!;
shadingMaterial.addUniform(
'lightCount',
'1i',
lights.length,
);
shadingMaterial.addUniformMatrixVector(
'cameraView',
'Matrix4fv',
cameraView.elements
);
shadingMaterial.addUniformMatrixVector(
'cameraPV',
'Matrix4fv',
this.camera.projectionMatrix.multiply(
cameraView
).elements
);
shadingMaterial.addUniform(
'cameraNearFar',
'2f',
this.camera.near,
this.camera.far
);
shadingMaterial.addUniform(
'cameraPos',
'3f',
...this.transform.position.elements
);
shadingMaterial.addUniformVector(
'lightNearFar',
'2fv',
lights.map( ( light ) => [ light.camera.near, light.camera.far ] ).flat(),
);
shadingMaterial.addUniformVector(
'lightPos',
'3fv',
lights.map( ( light ) => light.globalTransformCache.position.elements ).flat(),
);
shadingMaterial.addUniformVector(
'lightColor',
'3fv',
lights.map( ( light ) => light.color ).flat(),
);
shadingMaterial.addUniformMatrixVector(
'lightPV',
'Matrix4fv',
lights.map( ( light ) => (
light.camera.projectionMatrix.multiply(
light.globalTransformCache.matrix.inverse!
).elements
) ).flat(),
);
shadingMaterial.addUniformTextureArray(
'samplerShadow',
lights.map( ( light ) => light.shadowMap.texture ),
);
},
name: process.env.DEV && 'CameraEntity/shading/setCameraUniforms',
} );
for ( let i = 0; i < 4; i ++ ) {
shadingMaterial.addUniformTexture(
'sampler' + i,
cameraTarget.getTexture( gl.COLOR_ATTACHMENT0 + i )
);
}
shadingMaterial.addUniformTexture( 'samplerAo', aoTarget.texture );
shadingMaterial.addUniformTexture( 'samplerIBLLUT', options.textureIBLLUT );
shadingMaterial.addUniformTexture( 'samplerEnv', options.textureEnv );
shadingMaterial.addUniformTexture( 'samplerRandom', randomTexture.texture );
this.components.push(
this.camera,
lambdaAoSetCameraUniforms,
aoQuad,
lambda,
shadingQuad,
);
if ( process.env.DEV ) {
if ( module.hot ) {
module.hot.accept( '../shaders/shading.frag', () => {
shadingMaterials.forEach( ( material ) => {
material.replaceShader( quadVert, shadingFrag );
} );
shadingMaterial.replaceShader( quadVert, shadingFrag );
} );
}
}

View File

@@ -139,7 +139,11 @@ export class Condition extends Entity {
} );
auto( 'Sync/first/clap', ( { value } ) => {
material.addUniform( 'phaseOffset', '1f', 0.2 * value );
material.addUniform( 'phaseOffset', '1f', value );
} );
auto( 'Condition/hahaRatio', ( { value } ) => {
material.addUniform( 'hahaRatio', '1f', value );
} );
} );

View File

@@ -27,6 +27,7 @@ export interface EntityDrawEvent {
export class Entity {
public readonly transform = new Transform();
public globalTransformCache = new Transform();
public lastUpdateFrame = 0;
@@ -67,14 +68,14 @@ export class Entity {
public draw( event: EntityDrawEvent ): void {
if ( !this.visible ) { return; }
const globalTransform = event.globalTransform.multiply( this.transform );
this.globalTransformCache = event.globalTransform.multiply( this.transform );
this.components.forEach( ( component ) => {
component.draw( {
frameCount: event.frameCount,
time: event.time,
renderTarget: event.renderTarget,
globalTransform,
globalTransform: this.globalTransformCache,
camera: event.camera,
cameraTransform: event.cameraTransform,
viewMatrix: event.viewMatrix,
@@ -89,7 +90,7 @@ export class Entity {
frameCount: event.frameCount,
time: event.time,
renderTarget: event.renderTarget,
globalTransform,
globalTransform: this.globalTransformCache,
viewMatrix: event.viewMatrix,
projectionMatrix: event.projectionMatrix,
camera: event.camera,

View File

@@ -50,6 +50,12 @@ export class Material {
};
} = {};
protected __uniformTextureArrays: {
[ name: string ]: {
textures: GLCatTexture[];
};
} = {};
protected __uniformCubemaps: {
[ name: string ]: {
texture: GLCatTextureCubemap | null;
@@ -135,6 +141,10 @@ export class Material {
this.__uniformTextures[ name ] = { texture };
}
public addUniformTextureArray( name: string, textures: GLCatTexture[] ): void {
this.__uniformTextureArrays[ name ] = { textures };
}
public addUniformCubemap( name: string, texture: GLCatTextureCubemap | null ): void {
this.__uniformCubemaps[ name ] = { texture };
}
@@ -160,6 +170,10 @@ export class Material {
program.uniformTexture( name, texture );
} );
Object.entries( this.__uniformTextureArrays ).forEach( ( [ name, { textures } ] ) => {
program.uniformTextures( name, textures );
} );
Object.entries( this.__uniformCubemaps ).forEach( ( [ name, { texture } ] ) => {
program.uniformCubemap( name, texture );
} );

View File

@@ -195,8 +195,8 @@ const replacerLightFirst = new EntityReplacer( () => {
shadowMapFar: 20.0,
namePrefix: process.env.DEV && 'lightFirst',
} );
light.color = [ 40.0, 40.0, 40.0 ];
light.transform.lookAt( new Vector3( [ -1.0, 2.0, 8.0 ] ) );
light.color = [ 100.0, 100.0, 100.0 ];
light.transform.lookAt( new Vector3( [ 4.0, 4.0, 4.0 ] ) );
return light;
}, 'LightFirst' );
const lightFirst = replacerLightFirst.current;
@@ -210,7 +210,7 @@ const replacerLightPink = new EntityReplacer( () => {
namePrefix: process.env.DEV && 'lightPink',
} );
light.color = [ 120.0, 2.0, 10.0 ];
light.transform.lookAt( new Vector3( [ -1.0, 4.0, 4.0 ] ) );
light.transform.lookAt( new Vector3( [ -1.0, 2.0, 2.0 ] ) );
return light;
}, 'LightPink' );
const lightPink = replacerLightPink.current;

View File

@@ -11,6 +11,7 @@ const int MTL_UNLIT = 1;
in float vScale;
in float vPhase;
in float vFade;
in vec3 vNormal;
in vec4 vPosition;
in vec4 vHuh;
@@ -45,6 +46,7 @@ void main() {
if ( lenFromCenter < RADIUS_CULL_SPHERE ) { discard; }
float colorPhase = exp( -4.0 * ( lenFromCenter - RADIUS_CULL_SPHERE ) );
colorPhase = max( colorPhase, vFade );
vec3 color = mix(
vec3( 2.0 ),
vec3( 0.0 ),

View File

@@ -9,11 +9,13 @@ layout (location = 1) in vec4 huh;
out float vPhase;
out float vScale;
out float vFade;
out vec3 vNormal;
out vec4 vPosition;
out vec4 vHuh;
uniform float time;
uniform float hahaRatio;
uniform vec2 resolution;
uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
@@ -27,10 +29,17 @@ void main() {
vPhase = what.x;
vHuh = huh;
vec3 randomPos = fs( vec3( 5.0, 8.6, 1.9 ) + huh.z + 2.56 * huh.y ) - 0.5;
randomPos.z = mod( randomPos.z + 0.1 * time, 1.0 ) - 0.5;
randomPos *= vec3( 5.0, 5.0, 20.0 );
vFade = smoothstep( 9.0, 10.0, abs( randomPos.z ) );
float haha = hahaRatio * smoothstep( 1.0, 2.0, randomPos.z );
vec4 tex = texture( samplerSvg, vec2( what.x, huh.x ) );
vPosition = vec4( tex.xy, 0.0, 1.0 );
// vPosition.x += huh.y;
mat3 basis = orthBasis( vec3( tex.zw, 0.0 ) );
float theta = what.y / 3.0 * TAU;
@@ -38,17 +47,15 @@ void main() {
vNormal = ( normalMatrix * vec4( tube, 1.0 ) ).xyz;
vScale = exp( 2.0 * fs( huh.z + 2.56 * huh.y ) );
vPosition.xyz *= vScale;
vPosition.xyz *= mix( vScale, 1.0, haha );
vPosition.x += mix( 0.0, huh.y, haha );
vPosition.xyz += tube;
vPosition = modelMatrix * vPosition;
vec3 randomPos = fs( vec3( 5.0, 8.6, 1.9 ) + huh.z + 2.56 * huh.y ) - 0.5;
randomPos.z = mod( randomPos.z + 0.1 * time, 1.0 ) - 0.5;
vPosition.xyz += vec3( 5.0, 5.0, 20.0 ) * randomPos;
// vPosition.z += 6.0 - 1.2 * huh.z;
vPosition.xyz += mix( randomPos, vec3( 0.0, 0.0, 3.0 ), haha );
vec4 outPos = projectionMatrix * viewMatrix * vPosition;
outPos.x *= resolution.y / resolution.x;

View File

@@ -23,13 +23,13 @@ uniform float time;
void main() {
#ifdef FORWARD
fragColor = vec4( 8.0 * vec3( 0.9, 0.02, 0.1 ), 1.0 );
fragColor = vec4( 8.0 * vec3( 0.1, 0.9, 0.4 ), 1.0 );
#endif
#ifdef DEFERRED
fragPosition = vPosition;
fragNormal = vec4( normalize( vNormal ), 1.0 );
fragColor = vec4( 0.9, 0.02, 0.1, 1.0 );
fragColor = vec4( 0.1, 0.9, 0.4, 1.0 );
fragWTF = vec4( vec3( 0.2, 0.2, 8.0 ), MTL_PBR );
#endif
}

View File

@@ -6,7 +6,6 @@ const int MTL_NONE = 0;
const int MTL_UNLIT = 1;
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.9375;
const float AO_BIAS = 0.0;
@@ -27,19 +26,20 @@ in vec2 vUv;
out vec4 fragColor;
uniform vec2 lightNearFar;
uniform int lightCount;
uniform vec2 lightNearFar[8];
uniform vec2 cameraNearFar;
uniform vec3 cameraPos;
uniform vec3 lightPos;
uniform vec3 lightColor;
uniform mat4 lightPV;
uniform vec3 lightPos[8];
uniform vec3 lightColor[8];
uniform mat4 lightPV[8];
uniform mat4 cameraView;
uniform mat4 cameraPV;
uniform sampler2D sampler0; // position.xyz, depth
uniform sampler2D sampler1; // normal.xyz (yes, this is not good)
uniform sampler2D sampler2; // color.rgba (what is a though????)
uniform sampler2D sampler3; // materialParams.xyz, materialId
uniform sampler2D samplerShadow;
uniform sampler2D samplerShadow[8];
uniform sampler2D samplerIBLLUT;
uniform sampler2D samplerEnv;
uniform sampler2D samplerAo;
@@ -106,46 +106,42 @@ struct Isect {
vec3 materialParams;
};
struct AngularInfo {
vec3 V;
vec3 L;
vec3 H;
float lenL;
float lenV;
float dotNV;
float dotNL;
float dotNH;
float dotVH;
};
AngularInfo genAngularInfo( Isect isect ) {
AngularInfo aI;
aI.V = cameraPos - isect.position;
aI.lenV = length( aI.V );
aI.V = normalize( aI.V );
aI.L = lightPos - isect.position;
aI.lenL = length( aI.L );
aI.L = normalize( aI.L );
aI.H = normalize( aI.V + aI.L );
aI.dotNV = clamp( dot( isect.normal, aI.V ), EPSILON, 1.0 );
aI.dotNL = clamp( dot( isect.normal, aI.L ), EPSILON, 1.0 );
aI.dotNH = clamp( dot( isect.normal, aI.H ), EPSILON, 1.0 );
aI.dotVH = clamp( dot( aI.V, aI.H ), EPSILON, 1.0 );
return aI;
// == this is BAD ==================================================================================
vec4 fetchShadowMap( int iLight, vec2 uv ) {
if ( iLight == 0 ) {
return texture( samplerShadow[ 0 ], uv );
} else if ( iLight == 1 ) {
return texture( samplerShadow[ 1 ], uv );
} else if ( iLight == 2 ) {
return texture( samplerShadow[ 2 ], uv );
} else if ( iLight == 3 ) {
return texture( samplerShadow[ 3 ], uv );
} else if ( iLight == 4 ) {
return texture( samplerShadow[ 4 ], uv );
} else if ( iLight == 5 ) {
return texture( samplerShadow[ 5 ], uv );
} else if ( iLight == 6 ) {
return texture( samplerShadow[ 6 ], uv );
} else if ( iLight == 7 ) {
return texture( samplerShadow[ 7 ], uv );
}
}
// == features =====================================================================================
float castShadow( Isect isect, AngularInfo aI ) {
float depth = linearstep( lightNearFar.x, lightNearFar.y, length( isect.position - lightPos ) );
float bias = 0.0001 + 0.0001 * ( 1.0 - aI.dotNL );
float castShadow( int iLight, Isect isect, float NdotL ) {
float depth = linearstep(
lightNearFar[ iLight ].x,
lightNearFar[ iLight ].y,
length( isect.position - lightPos[ iLight ] )
);
float bias = 0.0001 + 0.0001 * ( 1.0 - NdotL );
depth -= bias;
vec4 proj = lightPV * vec4( isect.position, 1.0 );
vec4 proj = lightPV[ iLight ] * vec4( isect.position, 1.0 );
vec2 uv = proj.xy / proj.w * 0.5 + 0.5;
vec4 tex = texture( samplerShadow, uv );
vec4 tex = fetchShadowMap( iLight, uv );
float edgeClip = smoothstep( 0.4, 0.5, max( abs( uv.x - 0.5 ), abs( uv.y - 0.5 ) ) );
@@ -170,32 +166,57 @@ float calcDepth( vec3 pos ) {
}
// == shading functions ============================================================================
vec3 shadePBR( Isect isect, AngularInfo aI ) {
vec3 shadePBR( Isect isect ) {
// ref: https://github.com/KhronosGroup/glTF-Sample-Viewer/blob/master/src/shaders/metallic-roughness.frag
// from isect
vec3 V = cameraPos - isect.position;
float lenV = length( V );
V = normalize( V );
float NdotV = clamp( dot( isect.normal, V ), EPSILON, 1.0 );
float roughness = isect.materialParams.x;
float metallic = isect.materialParams.y;
float emissive = isect.materialParams.z;
float shadow = castShadow( isect, aI );
shadow = mix( 1.0, shadow, 0.8 );
float ao = texture( samplerAo, isect.screenUv ).x;
shadow *= ao;
float decayL = 1.0 / ( aI.lenL * aI.lenL );
// calc material stuff
vec3 albedo = mix( isect.color * ONE_SUB_DIELECTRIC_SPECULAR, vec3( 0.0 ), metallic );
vec3 f0 = mix( DIELECTRIC_SPECULAR, isect.color, metallic );
vec3 diffuse = brdfLambert( f0, albedo, aI.dotVH );
vec3 spec = brdfSpecularGGX( f0, roughness, aI.dotVH, aI.dotNL, aI.dotNV, aI.dotNH );
float ao = texture( samplerAo, isect.screenUv ).x;
vec3 shade = PI * lightColor * decayL * shadow * aI.dotNL * ( diffuse + spec );
// begin lighting
vec3 color = vec3( 0.0 );
vec3 color = shade;
// for each lights
for ( int iLight = 0; iLight < 8; iLight ++ ) {
if ( iLight >= lightCount ) { break; }
#ifdef IS_FIRST_LIGHT
// calc vectors
vec3 L = lightPos[ iLight ] - isect.position;
float lenL = length( L );
L = normalize( L );
vec3 H = normalize( V + L );
float NdotL = clamp( dot( isect.normal, L ), EPSILON, 1.0 );
float NdotH = clamp( dot( isect.normal, H ), EPSILON, 1.0 );
float VdotH = clamp( dot( V, H ), EPSILON, 1.0 );
float decayL = 1.0 / ( lenL * lenL );
// fetch shadowmap
float shadow = castShadow( iLight, isect, NdotL );
shadow = mix( 0.5, 1.0, shadow );
// do shading
vec3 diffuse = brdfLambert( f0, albedo, VdotH );
vec3 spec = brdfSpecularGGX( f0, roughness, VdotH, NdotL, NdotV, NdotH );
vec3 shade = PI * lightColor[ iLight ] * decayL * ao * shadow * NdotL * ( diffuse + spec );
color += shade;
}
// cheat the texture seam using noise!
vec3 nEnvDiffuse = importanceSampleGGX( vec2( prng( seed ), prng( seed ) * 0.05 ), 2.0, isect.normal );
@@ -209,34 +230,25 @@ vec3 shadePBR( Isect isect, AngularInfo aI ) {
color += ao * texEnvDiffuse * albedo;
// reflective ibl
vec3 reflEnvReflective = reflect( aI.V, isect.normal );
vec3 reflEnvReflective = reflect( 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;
vec2 brdfEnvReflective = texture( samplerIBLLUT, vec2( NdotV, roughness ) ).xy;
vec3 texEnvReflective = sampleEnvLinear( uvEnvReflective, 3.0 * roughness ).rgb;
color += ao * texEnvReflective * ( brdfEnvReflective.x * f0 + brdfEnvReflective.y );
// emissive
color += emissive * aI.dotNV * isect.color;
#endif // IS_FIRST_LIGHT
color += emissive * NdotV * isect.color;
return color;
}
vec3 shadeGradient( Isect isect ) {
vec3 ret;
#ifdef IS_FIRST_LIGHT
float shade = isect.normal.y;
ret = blurpleGradient( 0.5 + 0.5 * shade );
#else // IS_FIRST_LIGHT
ret = vec3( 0.0 );
#endif // IS_FIRST_LIGHT
return ret;
return blurpleGradient( 0.5 + 0.5 * shade );
}
// == main procedure ===============================================================================
@@ -260,41 +272,27 @@ void main() {
vec3 color = vec3( 0.0 );
AngularInfo aI = genAngularInfo( isect );
if ( isect.materialId == MTL_NONE ) {
// do nothing
} else if ( isect.materialId == MTL_UNLIT ) {
#ifdef IS_FIRST_LIGHT
color = isect.color;
#endif
} else if ( isect.materialId == MTL_PBR ) {
color = shadePBR( isect, aI );
color = shadePBR( isect );
} else if ( isect.materialId == MTL_GRADIENT ) {
color = shadeGradient( isect );
} else if ( isect.materialId == MTL_IRIDESCENT ) {
isect.color *= mix(
vec3( 1.0 ),
catColor( isect.materialParams.x * aI.dotNV ),
isect.materialParams.y
);
isect.materialParams = vec3( 0.1, isect.materialParams.z, 0.0 );
color = shadePBR( isect, aI );
}
color *= exp( -0.4 * max( aI.lenV - 3.0, 0.0 ) );
float lenV = length( cameraPos - isect.position );
color *= exp( -0.4 * max( lenV - 3.0, 0.0 ) );
#ifdef IS_FIRST_LIGHT
// color = 0.5 + 0.5 * isect.normal;
// color = vec3( calcDepth( tex0.xyz ) );
// color = vec3( 0.5, 0.9, 0.6 ) * ( 1.0 - texture( samplerAo, isect.screenUv ).xyz );
// color = vec3( 0.5, 0.9, 0.6 ) * ( texture( samplerAo, isect.screenUv ).xyz );
#endif
// xfdA = shadeGradient( isect );
fragColor = vec4( color, 1.0 );