mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2025-01-27 13:27:55 +01:00
Lighting, for instance
- Sideport light lut stuffs to instancing engine - Move actual lookup logic to light_lut.glsl, and have backend mains provide functions to index the backing storages for sanity's sake - Standardize naming of lut and sections - Pull in pepper's loom fix, so I can build :lwe: - Allow specifying the internal format of texture buffers so light can be a simple uint array
This commit is contained in:
parent
253de1f343
commit
495c047968
13 changed files with 236 additions and 136 deletions
|
@ -8,4 +8,6 @@ public class Samplers {
|
|||
public static final GlTextureUnit LIGHT = GlTextureUnit.T2;
|
||||
public static final GlTextureUnit CRUMBLING = GlTextureUnit.T3;
|
||||
public static final GlTextureUnit INSTANCE_BUFFER = GlTextureUnit.T4;
|
||||
public static final GlTextureUnit LIGHT_LUT = GlTextureUnit.T5;
|
||||
public static final GlTextureUnit LIGHT_SECTIONS = GlTextureUnit.T6;
|
||||
}
|
||||
|
|
|
@ -11,7 +11,11 @@ public final class Pipelines {
|
|||
.vertexMain(Flywheel.rl("internal/instancing/main.vert"))
|
||||
.fragmentMain(Flywheel.rl("internal/instancing/main.frag"))
|
||||
.assembler(BufferTextureInstanceComponent::new)
|
||||
.onLink(program -> program.setSamplerBinding("_flw_instances", Samplers.INSTANCE_BUFFER))
|
||||
.onLink(program -> {
|
||||
program.setSamplerBinding("_flw_instances", Samplers.INSTANCE_BUFFER);
|
||||
program.setSamplerBinding("_flw_lightLut", Samplers.LIGHT_LUT);
|
||||
program.setSamplerBinding("_flw_lightSections", Samplers.LIGHT_SECTIONS);
|
||||
})
|
||||
.build();
|
||||
|
||||
public static final Pipeline INDIRECT = Pipeline.builder()
|
||||
|
|
|
@ -8,6 +8,7 @@ import dev.engine_room.flywheel.api.event.RenderContext;
|
|||
import dev.engine_room.flywheel.api.task.Plan;
|
||||
import dev.engine_room.flywheel.backend.engine.EnvironmentStorage;
|
||||
import dev.engine_room.flywheel.backend.engine.indirect.StagingBuffer;
|
||||
import dev.engine_room.flywheel.backend.gl.buffer.GlBuffer;
|
||||
import dev.engine_room.flywheel.lib.task.SimplePlan;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.longs.Long2IntMap;
|
||||
|
@ -205,10 +206,6 @@ public class LightStorage {
|
|||
arena.delete();
|
||||
}
|
||||
|
||||
public boolean hasChanges() {
|
||||
return !changed.isEmpty();
|
||||
}
|
||||
|
||||
public boolean checkNeedsLutRebuildAndClear() {
|
||||
var out = needsLutRebuild;
|
||||
needsLutRebuild = false;
|
||||
|
@ -222,6 +219,15 @@ public class LightStorage {
|
|||
changed.clear();
|
||||
}
|
||||
|
||||
public void upload(GlBuffer buffer) {
|
||||
if (changed.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
buffer.upload(arena.indexToPointer(0), arena.capacity() * SECTION_SIZE_BYTES);
|
||||
changed.clear();
|
||||
}
|
||||
|
||||
public IntArrayList createLut() {
|
||||
// TODO: incremental lut updates
|
||||
return LightLut.buildLut(section2ArenaIndex);
|
||||
|
|
|
@ -6,8 +6,8 @@ public class BufferBindings {
|
|||
public static final int MODEL_INDEX_BUFFER_BINDING = 2;
|
||||
public static final int MODEL_BUFFER_BINDING = 3;
|
||||
public static final int DRAW_BUFFER_BINDING = 4;
|
||||
public static final int EMBEDDING_LUT_BINDING = 5;
|
||||
public static final int EMBEDDING_LIGHT_BINDING = 6;
|
||||
public static final int LIGHT_LUT_BINDING = 5;
|
||||
public static final int LIGHT_SECTION_BINDING = 6;
|
||||
|
||||
private BufferBindings() {
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import org.lwjgl.system.MemoryUtil;
|
|||
import dev.engine_room.flywheel.backend.engine.embed.LightStorage;
|
||||
|
||||
public class LightBuffers {
|
||||
private final ResizableStorageArray lightArena = new ResizableStorageArray(LightStorage.SECTION_SIZE_BYTES);
|
||||
private final ResizableStorageArray sections = new ResizableStorageArray(LightStorage.SECTION_SIZE_BYTES);
|
||||
private final ResizableStorageArray lut = new ResizableStorageArray(4);
|
||||
|
||||
public LightBuffers() {
|
||||
|
@ -19,8 +19,8 @@ public class LightBuffers {
|
|||
return;
|
||||
}
|
||||
|
||||
lightArena.ensureCapacity(capacity);
|
||||
light.uploadChangedSections(staging, lightArena.handle());
|
||||
sections.ensureCapacity(capacity);
|
||||
light.uploadChangedSections(staging, sections.handle());
|
||||
|
||||
if (light.checkNeedsLutRebuildAndClear()) {
|
||||
var lut = light.createLut();
|
||||
|
@ -36,11 +36,11 @@ public class LightBuffers {
|
|||
}
|
||||
|
||||
public void bind() {
|
||||
if (lightArena.capacity() == 0) {
|
||||
if (sections.capacity() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
GL46.glBindBufferRange(GL46.GL_SHADER_STORAGE_BUFFER, BufferBindings.EMBEDDING_LUT_BINDING, lut.handle(), 0, lut.byteCapacity());
|
||||
GL46.glBindBufferRange(GL46.GL_SHADER_STORAGE_BUFFER, BufferBindings.EMBEDDING_LIGHT_BINDING, lightArena.handle(), 0, lightArena.byteCapacity());
|
||||
GL46.glBindBufferRange(GL46.GL_SHADER_STORAGE_BUFFER, BufferBindings.LIGHT_LUT_BINDING, lut.handle(), 0, lut.byteCapacity());
|
||||
GL46.glBindBufferRange(GL46.GL_SHADER_STORAGE_BUFFER, BufferBindings.LIGHT_SECTION_BINDING, sections.handle(), 0, sections.byteCapacity());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ public class InstancedDrawManager extends DrawManager<InstancedInstancer<?>> {
|
|||
private final MeshPool meshPool;
|
||||
private final GlVertexArray vao;
|
||||
private final TextureBuffer instanceTexture;
|
||||
private final InstancedLight light;
|
||||
|
||||
public InstancedDrawManager(InstancingPrograms programs) {
|
||||
programs.acquire();
|
||||
|
@ -51,6 +52,7 @@ public class InstancedDrawManager extends DrawManager<InstancedInstancer<?>> {
|
|||
meshPool = new MeshPool();
|
||||
vao = GlVertexArray.create();
|
||||
instanceTexture = new TextureBuffer();
|
||||
light = new InstancedLight();
|
||||
|
||||
meshPool.bind(vao);
|
||||
}
|
||||
|
@ -78,6 +80,8 @@ public class InstancedDrawManager extends DrawManager<InstancedInstancer<?>> {
|
|||
}
|
||||
|
||||
meshPool.flush();
|
||||
|
||||
light.flush(lightStorage);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -92,6 +96,7 @@ public class InstancedDrawManager extends DrawManager<InstancedInstancer<?>> {
|
|||
Uniforms.bindAll();
|
||||
vao.bindForDraw();
|
||||
TextureBinder.bindLightAndOverlay();
|
||||
light.bind();
|
||||
|
||||
drawSet.draw(instanceTexture, programs);
|
||||
|
||||
|
@ -114,6 +119,8 @@ public class InstancedDrawManager extends DrawManager<InstancedInstancer<?>> {
|
|||
programs.release();
|
||||
vao.delete();
|
||||
|
||||
light.delete();
|
||||
|
||||
super.delete();
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
package dev.engine_room.flywheel.backend.engine.instancing;
|
||||
|
||||
import org.lwjgl.opengl.GL32;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import dev.engine_room.flywheel.backend.Samplers;
|
||||
import dev.engine_room.flywheel.backend.engine.embed.LightStorage;
|
||||
import dev.engine_room.flywheel.backend.gl.TextureBuffer;
|
||||
import dev.engine_room.flywheel.backend.gl.buffer.GlBuffer;
|
||||
import dev.engine_room.flywheel.lib.memory.MemoryBlock;
|
||||
|
||||
public class InstancedLight {
|
||||
private final GlBuffer lut;
|
||||
private final GlBuffer sections;
|
||||
private final TextureBuffer lutTexture;
|
||||
private final TextureBuffer sectionsTexture;
|
||||
|
||||
public InstancedLight() {
|
||||
sections = new GlBuffer();
|
||||
lut = new GlBuffer();
|
||||
lutTexture = new TextureBuffer(GL32.GL_R32UI);
|
||||
sectionsTexture = new TextureBuffer(GL32.GL_R32UI);
|
||||
}
|
||||
|
||||
public void bind() {
|
||||
Samplers.LIGHT_LUT.makeActive();
|
||||
lutTexture.bind(lut.handle());
|
||||
Samplers.LIGHT_SECTIONS.makeActive();
|
||||
sectionsTexture.bind(sections.handle());
|
||||
}
|
||||
|
||||
public void flush(LightStorage light) {
|
||||
if (light.capacity() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
light.upload(sections);
|
||||
|
||||
if (light.checkNeedsLutRebuildAndClear()) {
|
||||
var lut = light.createLut();
|
||||
|
||||
var up = MemoryBlock.malloc((long) lut.size() * Integer.BYTES);
|
||||
|
||||
long ptr = up.ptr();
|
||||
|
||||
for (int i = 0; i < lut.size(); i++) {
|
||||
MemoryUtil.memPutInt(ptr + (long) Integer.BYTES * i, lut.getInt(i));
|
||||
}
|
||||
|
||||
this.lut.upload(up);
|
||||
|
||||
up.free();
|
||||
}
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
sections.delete();
|
||||
lut.delete();
|
||||
lutTexture.delete();
|
||||
sectionsTexture.delete();
|
||||
}
|
||||
}
|
|
@ -5,14 +5,20 @@ import org.lwjgl.opengl.GL32;
|
|||
public class TextureBuffer extends GlObject {
|
||||
public static final int MAX_TEXELS = GL32.glGetInteger(GL32.GL_MAX_TEXTURE_BUFFER_SIZE);
|
||||
public static final int MAX_BYTES = MAX_TEXELS * 16; // 4 channels * 4 bytes
|
||||
private final int format;
|
||||
|
||||
public TextureBuffer() {
|
||||
this(GL32.GL_RGBA32UI);
|
||||
}
|
||||
|
||||
public TextureBuffer(int format) {
|
||||
handle(GL32.glGenTextures());
|
||||
this.format = format;
|
||||
}
|
||||
|
||||
public void bind(int buffer) {
|
||||
GL32.glBindTexture(GL32.GL_TEXTURE_BUFFER, handle());
|
||||
GL32.glTexBuffer(GL32.GL_TEXTURE_BUFFER, GL32.GL_RGBA32UI, buffer);
|
||||
GL32.glTexBuffer(GL32.GL_TEXTURE_BUFFER, format, buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -13,9 +13,7 @@ uniform sampler2D _flw_crumblingTex;
|
|||
in vec2 _flw_crumblingTexCoord;
|
||||
#endif
|
||||
|
||||
#ifdef _FLW_EMBEDDED
|
||||
bool _flw_embeddedLight(vec3 worldPos, out vec2 lightCoord);
|
||||
#endif
|
||||
|
||||
flat in uint _flw_instanceID;
|
||||
|
||||
|
|
|
@ -3,5 +3,5 @@
|
|||
#define _FLW_MODEL_INDEX_BUFFER_BINDING 2
|
||||
#define _FLW_MODEL_BUFFER_BINDING 3
|
||||
#define _FLW_DRAW_BUFFER_BINDING 4
|
||||
#define _FLW_EMBEDDING_LUT_BINDING 5
|
||||
#define _FLW_EMBEDDING_LIGHT_BINDING 6
|
||||
#define _FLW_LIGHT_LUT_BINDING 5
|
||||
#define _FLW_LIGHT_SECTIONS_BINDING 6
|
||||
|
|
|
@ -1,131 +1,25 @@
|
|||
#include "flywheel:internal/common.frag"
|
||||
#include "flywheel:internal/light_lut.glsl"
|
||||
#include "flywheel:internal/indirect/buffer_bindings.glsl"
|
||||
|
||||
flat in uvec3 _flw_packedMaterial;
|
||||
|
||||
#ifdef _FLW_EMBEDDED
|
||||
|
||||
layout(std430, binding = _FLW_EMBEDDING_LUT_BINDING) restrict readonly buffer EmbeddingLut {
|
||||
uint _flw_embeddingLut[];
|
||||
layout(std430, binding = _FLW_LIGHT_LUT_BINDING) restrict readonly buffer LightLut {
|
||||
uint _flw_lightLut[];
|
||||
};
|
||||
|
||||
const uint _FLW_LIGHT_SECTION_SIZE_BYTES = 18 * 18 * 18;
|
||||
const uint _FLW_LIGHT_SECTION_SIZE_INTS = _FLW_LIGHT_SECTION_SIZE_BYTES / 4;
|
||||
|
||||
layout(std430, binding = _FLW_EMBEDDING_LIGHT_BINDING) restrict readonly buffer LightSections {
|
||||
layout(std430, binding = _FLW_LIGHT_SECTIONS_BINDING) restrict readonly buffer LightSections {
|
||||
uint _flw_lightSections[];
|
||||
};
|
||||
|
||||
/// Find the index for the next step in the LUT.
|
||||
/// @param base The base index in the LUT, should point to the start of a coordinate span.
|
||||
/// @param coord The coordinate to look for.
|
||||
/// @param next Output. The index of the next step in the LUT.
|
||||
/// @return true if the coordinate is not in the span.
|
||||
bool _flw_nextLut(uint base, int coord, out uint next) {
|
||||
// The base coordinate.
|
||||
int start = int(_flw_embeddingLut[base]);
|
||||
// The width of the coordinate span.
|
||||
uint size = _flw_embeddingLut[base + 1];
|
||||
|
||||
// Index of the coordinate in the span.
|
||||
int i = coord - start;
|
||||
|
||||
if (i < 0 || i >= size) {
|
||||
// We missed.
|
||||
return true;
|
||||
}
|
||||
|
||||
next = _flw_embeddingLut[base + 2 + i];
|
||||
|
||||
return false;
|
||||
uint _flw_indexLut(uint index) {
|
||||
return _flw_lightLut[index];
|
||||
}
|
||||
|
||||
bool _flw_chunkCoordToSectionIndex(ivec3 sectionPos, out uint index) {
|
||||
uint y;
|
||||
if (_flw_nextLut(0, sectionPos.x, y) || y == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
uint z;
|
||||
if (_flw_nextLut(y, sectionPos.y, z) || z == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
uint sectionIndex;
|
||||
if (_flw_nextLut(z, sectionPos.z, sectionIndex) || sectionIndex == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// The index is written as 1-based so we can properly detect missing sections.
|
||||
index = sectionIndex - 1;
|
||||
|
||||
return false;
|
||||
uint _flw_indexLight(uint index) {
|
||||
return _flw_lightSections[index];
|
||||
}
|
||||
|
||||
vec2 _flw_lightAt(uint sectionOffset, uvec3 blockInSectionPos) {
|
||||
uint byteOffset = blockInSectionPos.x + blockInSectionPos.z * 18u + blockInSectionPos.y * 18u * 18u;
|
||||
|
||||
uint uintOffset = byteOffset >> 2u;
|
||||
uint bitOffset = (byteOffset & 3u) << 3;
|
||||
|
||||
uint raw = _flw_lightSections[sectionOffset + uintOffset];
|
||||
uint block = (raw >> bitOffset) & 0xFu;
|
||||
uint sky = (raw >> (bitOffset + 4u)) & 0xFu;
|
||||
|
||||
return vec2(block, sky);
|
||||
}
|
||||
|
||||
bool _flw_embeddedLight(vec3 worldPos, 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)) {
|
||||
// TODO: useful debug mode for this.
|
||||
// flw_fragOverlay = ivec2(0, 3);
|
||||
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
|
||||
uvec3 blockInSectionPos = (blockPos & 0xF) + 1;
|
||||
|
||||
// The lowest corner of the 2x2x2 area we'll be trilinear interpolating.
|
||||
// The ugly bit on the end evaluates to -1 or 0 depending on which side of 0.5 we are.
|
||||
uvec3 lowestCorner = blockInSectionPos + ivec3(floor(fract(worldPos) - 0.5));
|
||||
|
||||
// The distance our fragment is from the center of the lowest corner.
|
||||
vec3 interpolant = fract(worldPos - 0.5);
|
||||
|
||||
// Fetch everything for trilinear interpolation
|
||||
// Hypothetically we could re-order these and do some calculations in-between fetches
|
||||
// to help with latency hiding, but the compiler should be able to do that for us.
|
||||
vec2 light000 = _flw_lightAt(sectionOffset, lowestCorner);
|
||||
vec2 light001 = _flw_lightAt(sectionOffset, lowestCorner + uvec3(0, 0, 1));
|
||||
vec2 light010 = _flw_lightAt(sectionOffset, lowestCorner + uvec3(0, 1, 0));
|
||||
vec2 light011 = _flw_lightAt(sectionOffset, lowestCorner + uvec3(0, 1, 1));
|
||||
vec2 light100 = _flw_lightAt(sectionOffset, lowestCorner + uvec3(1, 0, 0));
|
||||
vec2 light101 = _flw_lightAt(sectionOffset, lowestCorner + uvec3(1, 0, 1));
|
||||
vec2 light110 = _flw_lightAt(sectionOffset, lowestCorner + uvec3(1, 1, 0));
|
||||
vec2 light111 = _flw_lightAt(sectionOffset, lowestCorner + uvec3(1, 1, 1));
|
||||
|
||||
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);
|
||||
|
||||
lightCoord = mix(light0, light1, interpolant.x) / 15.;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void main() {
|
||||
_flw_uberMaterialFragmentIndex = _flw_packedMaterial.x;
|
||||
_flw_unpackUint2x16(_flw_packedMaterial.y, _flw_uberCutoutIndex, _flw_uberFogIndex);
|
||||
|
|
|
@ -1,12 +1,18 @@
|
|||
#include "flywheel:internal/common.frag"
|
||||
#include "flywheel:internal/light_lut.glsl"
|
||||
|
||||
uniform uvec4 _flw_packedMaterial;
|
||||
|
||||
#ifdef _FLW_EMBEDDED
|
||||
bool _flw_embeddedLight(vec3 worldPos, out vec2 lightCoord) {
|
||||
return true;
|
||||
uniform usamplerBuffer _flw_lightLut;
|
||||
uniform usamplerBuffer _flw_lightSections;
|
||||
|
||||
uint _flw_indexLut(uint index) {
|
||||
return texelFetch(_flw_lightLut, int(index)).r;
|
||||
}
|
||||
|
||||
uint _flw_indexLight(uint index) {
|
||||
return texelFetch(_flw_lightSections, int(index)).r;
|
||||
}
|
||||
#endif
|
||||
|
||||
void main() {
|
||||
_flw_uberMaterialFragmentIndex = _flw_packedMaterial.y;
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
const uint _FLW_LIGHT_SECTION_SIZE_BYTES = 18 * 18 * 18;
|
||||
const uint _FLW_LIGHT_SECTION_SIZE_INTS = _FLW_LIGHT_SECTION_SIZE_BYTES / 4;
|
||||
|
||||
uint _flw_indexLut(uint index);
|
||||
|
||||
uint _flw_indexLight(uint index);
|
||||
|
||||
/// Find the index for the next step in the LUT.
|
||||
/// @param base The base index in the LUT, should point to the start of a coordinate span.
|
||||
/// @param coord The coordinate to look for.
|
||||
/// @param next Output. The index of the next step in the LUT.
|
||||
/// @return true if the coordinate is not in the span.
|
||||
bool _flw_nextLut(uint base, int coord, out uint next) {
|
||||
// The base coordinate.
|
||||
int start = int(_flw_indexLut(base));
|
||||
// The width of the coordinate span.
|
||||
uint size = _flw_indexLut(base + 1);
|
||||
|
||||
// Index of the coordinate in the span.
|
||||
int i = coord - start;
|
||||
|
||||
if (i < 0 || i >= size) {
|
||||
// We missed.
|
||||
return true;
|
||||
}
|
||||
|
||||
next = _flw_indexLut(base + 2 + i);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool _flw_chunkCoordToSectionIndex(ivec3 sectionPos, out uint index) {
|
||||
uint y;
|
||||
if (_flw_nextLut(0, sectionPos.x, y) || y == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
uint z;
|
||||
if (_flw_nextLut(y, sectionPos.y, z) || z == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
uint sectionIndex;
|
||||
if (_flw_nextLut(z, sectionPos.z, sectionIndex) || sectionIndex == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// The index is written as 1-based so we can properly detect missing sections.
|
||||
index = sectionIndex - 1;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
vec2 _flw_lightAt(uint sectionOffset, uvec3 blockInSectionPos) {
|
||||
uint byteOffset = blockInSectionPos.x + blockInSectionPos.z * 18u + blockInSectionPos.y * 18u * 18u;
|
||||
|
||||
uint uintOffset = byteOffset >> 2u;
|
||||
uint bitOffset = (byteOffset & 3u) << 3;
|
||||
|
||||
uint raw = _flw_indexLight(sectionOffset + uintOffset);
|
||||
uint block = (raw >> bitOffset) & 0xFu;
|
||||
uint sky = (raw >> (bitOffset + 4u)) & 0xFu;
|
||||
|
||||
return vec2(block, sky);
|
||||
}
|
||||
|
||||
bool _flw_embeddedLight(vec3 worldPos, 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)) {
|
||||
// TODO: useful debug mode for this.
|
||||
// flw_fragOverlay = ivec2(0, 3);
|
||||
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
|
||||
uvec3 blockInSectionPos = (blockPos & 0xF) + 1;
|
||||
|
||||
// The lowest corner of the 2x2x2 area we'll be trilinear interpolating.
|
||||
// The ugly bit on the end evaluates to -1 or 0 depending on which side of 0.5 we are.
|
||||
uvec3 lowestCorner = blockInSectionPos + ivec3(floor(fract(worldPos) - 0.5));
|
||||
|
||||
// The distance our fragment is from the center of the lowest corner.
|
||||
vec3 interpolant = fract(worldPos - 0.5);
|
||||
|
||||
// Fetch everything for trilinear interpolation
|
||||
// Hypothetically we could re-order these and do some calculations in-between fetches
|
||||
// to help with latency hiding, but the compiler should be able to do that for us.
|
||||
vec2 light000 = _flw_lightAt(sectionOffset, lowestCorner);
|
||||
vec2 light001 = _flw_lightAt(sectionOffset, lowestCorner + uvec3(0, 0, 1));
|
||||
vec2 light010 = _flw_lightAt(sectionOffset, lowestCorner + uvec3(0, 1, 0));
|
||||
vec2 light011 = _flw_lightAt(sectionOffset, lowestCorner + uvec3(0, 1, 1));
|
||||
vec2 light100 = _flw_lightAt(sectionOffset, lowestCorner + uvec3(1, 0, 0));
|
||||
vec2 light101 = _flw_lightAt(sectionOffset, lowestCorner + uvec3(1, 0, 1));
|
||||
vec2 light110 = _flw_lightAt(sectionOffset, lowestCorner + uvec3(1, 1, 0));
|
||||
vec2 light111 = _flw_lightAt(sectionOffset, lowestCorner + uvec3(1, 1, 1));
|
||||
|
||||
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);
|
||||
|
||||
lightCoord = mix(light0, light1, interpolant.x) / 15.;
|
||||
return true;
|
||||
}
|
||||
|
Loading…
Reference in a new issue