Skip to content

Commit

Permalink
Smoothest operator
Browse files Browse the repository at this point in the history
- Implement RogueLogix's normal-dependent smooth lighting function
- Uses a lot of fetches, so I imagine occupancy is kinda bad
  • Loading branch information
Jozufozu committed Jul 13, 2024
1 parent 22aefa3 commit 39237e1
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
// TODO: Add config for light smoothness. Should work at a compile flag level

/// Get the light at the given world position from the given normal.
/// This may be interpolated for smooth lighting.
bool flw_light(vec3 worldPos, vec3 normal, out vec2 light);

/// Get the light at the given world position.
/// This may be interpolated for smooth lighting.
bool flw_light(vec3 worldPos, out vec2 light);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,110 @@ bool flw_light(vec3 worldPos, out vec2 lightCoord) {
return true;
}

uint _flw_lightIndex(in uvec3 p) {
return p.x + p.z * 3u + p.y * 9u;
}

/// Premtively collect all light in a 3x3x3 area centered on our block.
/// Depending on the normal, we won't use all the data, but fetching on demand will have many duplicated fetches.
vec2[27] _flw_lightFetch3x3x3(uint sectionOffset, ivec3 blockInSectionPos) {
vec2[27] lights;

for (int y = -1; y <= 1; y++) {
for (int z = -1; z <= 1; z++) {
for (int x = -1; x <= 1; x++) {
lights[_flw_lightIndex(uvec3(x + 1, y + 1, z + 1))] = _flw_lightAt(sectionOffset, uvec3(blockInSectionPos + ivec3(x, y, z)));
}
}
}

return lights;
}

/// Calculate the light for a direction by averaging the light at the corners of the block.
///
/// To make this reusable across directions, c00..c11 choose what values relative to each corner to use.
/// e.g. (0, 0, 0) (0, 0, 1) (0, 1, 0) (0, 1, 1) would give you the light coming from -x at each corner.
/// In general, to get the light for a particular direction, you fix the x, y, or z coordinate of the c values, and permutate 0 and 1 for the other two.
/// Fixing the x coordinate to 0 gives you the light from -x, 1 gives you the light from +x.
///
/// @param lights The light data for the 3x3x3 area.
/// @param interpolant The position within the center block.
/// @param c00..c11 4 offsets to determine which "direction" we are averaging.
vec2 _flw_lightForDirection(in vec2[27] lights, in vec3 interpolant, in uvec3 c00, in uvec3 c01, in uvec3 c10, in uvec3 c11) {

vec2 light000 = lights[_flw_lightIndex(c00 + uvec3(0u, 0u, 0u))] + lights[_flw_lightIndex(c01 + uvec3(0u, 0u, 0u))] + lights[_flw_lightIndex(c10 + uvec3(0u, 0u, 0u))] + lights[_flw_lightIndex(c11 + uvec3(0u, 0u, 0u))];
vec2 light001 = lights[_flw_lightIndex(c00 + uvec3(0u, 0u, 1u))] + lights[_flw_lightIndex(c01 + uvec3(0u, 0u, 1u))] + lights[_flw_lightIndex(c10 + uvec3(0u, 0u, 1u))] + lights[_flw_lightIndex(c11 + uvec3(0u, 0u, 1u))];
vec2 light010 = lights[_flw_lightIndex(c00 + uvec3(0u, 1u, 0u))] + lights[_flw_lightIndex(c01 + uvec3(0u, 1u, 0u))] + lights[_flw_lightIndex(c10 + uvec3(0u, 1u, 0u))] + lights[_flw_lightIndex(c11 + uvec3(0u, 1u, 0u))];
vec2 light011 = lights[_flw_lightIndex(c00 + uvec3(0u, 1u, 1u))] + lights[_flw_lightIndex(c01 + uvec3(0u, 1u, 1u))] + lights[_flw_lightIndex(c10 + uvec3(0u, 1u, 1u))] + lights[_flw_lightIndex(c11 + uvec3(0u, 1u, 1u))];
vec2 light100 = lights[_flw_lightIndex(c00 + uvec3(1u, 0u, 0u))] + lights[_flw_lightIndex(c01 + uvec3(1u, 0u, 0u))] + lights[_flw_lightIndex(c10 + uvec3(1u, 0u, 0u))] + lights[_flw_lightIndex(c11 + uvec3(1u, 0u, 0u))];
vec2 light101 = lights[_flw_lightIndex(c00 + uvec3(1u, 0u, 1u))] + lights[_flw_lightIndex(c01 + uvec3(1u, 0u, 1u))] + lights[_flw_lightIndex(c10 + uvec3(1u, 0u, 1u))] + lights[_flw_lightIndex(c11 + uvec3(1u, 0u, 1u))];
vec2 light110 = lights[_flw_lightIndex(c00 + uvec3(1u, 1u, 0u))] + lights[_flw_lightIndex(c01 + uvec3(1u, 1u, 0u))] + lights[_flw_lightIndex(c10 + uvec3(1u, 1u, 0u))] + lights[_flw_lightIndex(c11 + uvec3(1u, 1u, 0u))];
vec2 light111 = lights[_flw_lightIndex(c00 + uvec3(1u, 1u, 1u))] + lights[_flw_lightIndex(c01 + uvec3(1u, 1u, 1u))] + lights[_flw_lightIndex(c10 + uvec3(1u, 1u, 1u))] + lights[_flw_lightIndex(c11 + uvec3(1u, 1u, 1u))];

vec2 light00 = mix(light000, light001, interpolant.z);
vec2 light01 = mix(light010, light011, interpolant.z);
vec2 light10 = mix(light100, light101, interpolant.z);
vec2 light11 = mix(light110, light111, interpolant.z);

vec2 light0 = mix(light00, light01, interpolant.y);
vec2 light1 = mix(light10, light11, interpolant.y);

// Divide by 60 (15 * 4) to normalize.
return mix(light0, light1, interpolant.x) / 60.;
}

bool flw_light(vec3 worldPos, vec3 normal, out vec2 lightCoord) {
// Always use the section of the block we are contained in to ensure accuracy.
// We don't want to interpolate between sections, but also we might not be able
// to rely on the existence neighboring sections, so don't do any extra rounding here.
ivec3 blockPos = ivec3(floor(worldPos));

uint lightSectionIndex;
if (_flw_chunkCoordToSectionIndex(blockPos >> 4, lightSectionIndex)) {
return false;
}
// The offset of the section in the light buffer.
uint sectionOffset = lightSectionIndex * _FLW_LIGHT_SECTION_SIZE_INTS;

// The block's position in the section adjusted into 18x18x18 space
ivec3 blockInSectionPos = (blockPos & 0xF) + 1;

// Fetch everything in a 3x3x3 area centered around the block.
vec2[27] lights = _flw_lightFetch3x3x3(sectionOffset, blockInSectionPos);

vec3 interpolant = fract(worldPos);

vec2 lightX;
if (normal.x > 0) {
lightX = _flw_lightForDirection(lights, interpolant, uvec3(1u, 0u, 0u), uvec3(1u, 0u, 1u), uvec3(1u, 1u, 0u), uvec3(1u, 1u, 1u));
} else if (normal.x < 0) {
lightX = _flw_lightForDirection(lights, interpolant, uvec3(0u, 0u, 0u), uvec3(0u, 0u, 1u), uvec3(0u, 1u, 0u), uvec3(0u, 1u, 1u));
} else {
lightX = vec2(0.);
}

vec2 lightZ;
if (normal.z > 0) {
lightZ = _flw_lightForDirection(lights, interpolant, uvec3(0u, 0u, 1u), uvec3(0u, 1u, 1u), uvec3(1u, 0u, 1u), uvec3(1u, 1u, 1u));
} else if (normal.z < 0) {
lightZ = _flw_lightForDirection(lights, interpolant, uvec3(0u, 0u, 0u), uvec3(0u, 1u, 0u), uvec3(1u, 0u, 0u), uvec3(1u, 1u, 0u));
} else {
lightZ = vec2(0.);
}

vec2 lightY;
// Average the light in relevant directions at each corner.
if (normal.y > 0.) {
lightY = _flw_lightForDirection(lights, interpolant, uvec3(0u, 1u, 0u), uvec3(0u, 1u, 1u), uvec3(1u, 1u, 0u), uvec3(1u, 1u, 1u));
} else if (normal.y < 0.) {
lightY = _flw_lightForDirection(lights, interpolant, uvec3(0u, 0u, 0u), uvec3(0u, 0u, 1u), uvec3(1u, 0u, 0u), uvec3(1u, 0u, 1u));
} else {
lightY = vec2(0.);
}

lightCoord = (lightX * abs(normal.x) + lightY * abs(normal.y) + lightZ * abs(normal.z));

return true;
}

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
void flw_materialFragment() {
#ifdef FLW_EMBEDDED
vec2 embeddedLight;
if (flw_light(flw_vertexPos.xyz, embeddedLight)) {
if (flw_light(flw_vertexPos.xyz, flw_vertexNormal, embeddedLight)) {
flw_fragLight = max(flw_fragLight, embeddedLight);
}
#endif
Expand Down

0 comments on commit 39237e1

Please sign in to comment.