From f902f5be7414e8f538fa6b3d82012de0aee97892 Mon Sep 17 00:00:00 2001 From: Neddslayer <59270607+Neddslayer@users.noreply.github.com> Date: Mon, 27 Jul 2026 17:41:24 -0400 Subject: [PATCH 1/7] Add DDA occlusion for directional lights (#171) --- .../light/data/DirectionalLightData.java | 20 +++++++++++++++- .../light/DirectionalLightRenderer.java | 23 ++++++++++++++++++- .../shaders/program/light/directional.fsh | 9 ++++++++ .../shaders/program/light/directional.json | 6 ++++- 4 files changed, 55 insertions(+), 3 deletions(-) diff --git a/common/src/main/java/foundry/veil/api/client/render/light/data/DirectionalLightData.java b/common/src/main/java/foundry/veil/api/client/render/light/data/DirectionalLightData.java index 1b42a9cec..b92e5f821 100644 --- a/common/src/main/java/foundry/veil/api/client/render/light/data/DirectionalLightData.java +++ b/common/src/main/java/foundry/veil/api/client/render/light/data/DirectionalLightData.java @@ -4,6 +4,7 @@ import foundry.veil.api.client.editor.EditorAttributeProvider; import foundry.veil.api.client.registry.LightTypeRegistry; import foundry.veil.api.client.render.CullFrustum; +import foundry.veil.api.client.render.light.DDALightData; import imgui.ImGui; import net.minecraft.client.Camera; import org.joml.Vector3f; @@ -14,12 +15,14 @@ * * @since 2.0.0 */ -public class DirectionalLightData extends LightData implements EditorAttributeProvider { +public class DirectionalLightData extends LightData implements EditorAttributeProvider, DDALightData { protected final Vector3f direction; + protected boolean occlusionEnabled; public DirectionalLightData() { this.direction = new Vector3f(0.0F, -1.0F, 0.0F); + this.occlusionEnabled = false; } /** @@ -92,6 +95,12 @@ public DirectionalLightData setBrightness(float brightness) { return this; } + public DirectionalLightData setOcclusionEnabled(boolean occlusionEnabled) { + this.occlusionEnabled = occlusionEnabled; + this.markDirty(); + return this; + } + @Override public boolean isVisible(CullFrustum frustum) { return true; @@ -117,5 +126,14 @@ public void renderImGuiAttributes() { } ImGui.sameLine(0, ImGui.getStyle().getItemInnerSpacingX()); ImGui.text("direction"); + + if (ImGui.checkbox("Occluded", this.occlusionEnabled)) { + this.setOcclusionEnabled(!this.occlusionEnabled); + } + } + + @Override + public boolean isOcclusionEnabled() { + return this.occlusionEnabled; } } diff --git a/common/src/main/java/foundry/veil/impl/client/render/light/DirectionalLightRenderer.java b/common/src/main/java/foundry/veil/impl/client/render/light/DirectionalLightRenderer.java index eca98f5cf..0c79c1038 100644 --- a/common/src/main/java/foundry/veil/impl/client/render/light/DirectionalLightRenderer.java +++ b/common/src/main/java/foundry/veil/impl/client/render/light/DirectionalLightRenderer.java @@ -7,6 +7,7 @@ import foundry.veil.api.client.color.Colorc; import foundry.veil.api.client.render.CullFrustum; import foundry.veil.api.client.render.light.data.DirectionalLightData; +import foundry.veil.api.client.render.light.renderer.DDALightRenderer; import foundry.veil.api.client.render.light.renderer.LightRenderHandle; import foundry.veil.api.client.render.light.renderer.LightRenderer; import foundry.veil.api.client.render.light.renderer.LightTypeRenderer; @@ -17,13 +18,14 @@ import net.minecraft.resources.ResourceLocation; import org.jetbrains.annotations.ApiStatus; import org.joml.Vector3f; +import org.joml.Vector3fc; import java.util.Collection; import java.util.LinkedList; import java.util.List; @ApiStatus.Internal -public class DirectionalLightRenderer implements LightTypeRenderer { +public class DirectionalLightRenderer implements DDALightRenderer { private static final Vector3f DIRECTION = new Vector3f(); private static final ResourceLocation RENDER_TYPE = Veil.veilPath("light/directional"); @@ -115,6 +117,12 @@ private void render() { lightDirection.upload(); } + Uniform occluded = shader.getUniform("Occluded"); + if (occluded != null) { + occluded.set(light.isOcclusionEnabled() ? 1f : 0f); + occluded.upload(); + } + this.vertexArray.draw(); } } @@ -135,6 +143,19 @@ public void free() { this.freed = true; } + @Override + public void uploadVoxelGridUniforms(int voxelGridTexture, Vector3fc voxelGridOrigin) { + RenderType renderType = VeilRenderType.get(RENDER_TYPE); + if (renderType == null) { + return; + } + + ResourceLocation veilShaderId = VeilRenderType.getShards(renderType).veilShaderId(); + if (veilShaderId != null) { + DDALightRenderer.uploadVoxelGridUniforms(veilShaderId, voxelGridTexture, voxelGridOrigin); + } + } + private class LightHandle implements LightRenderHandle { private final DirectionalLightData data; diff --git a/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/directional.fsh b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/directional.fsh index f908bd76c..1c749e6c2 100644 --- a/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/directional.fsh +++ b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/directional.fsh @@ -1,14 +1,17 @@ #include veil:common #include veil:space_helper #include veil:color_utilities +#include veil:voxel_shadow in vec2 texCoord; uniform sampler2D AlbedoSampler; uniform sampler2D NormalSampler; +uniform sampler2D DepthSampler; uniform vec3 LightColor; uniform vec3 LightDirection; +uniform float Occluded; out vec4 fragColor; @@ -24,6 +27,12 @@ void main() { // lighting calculation float diffuse = clamp(smoothstep(-0.2, 0.2, -dot(normalVS, lightDirectionVS)), 0.0, 1.0); + if (Occluded > 0.5) { + vec3 normalWS = normalize((VeilCamera.IViewMat * vec4(normalVS, 0.0)).xyz); + vec3 pos = screenToWorldSpace(texCoord, texture(DepthSampler, texCoord).r).xyz; + diffuse *= voxelshadowVisibility(pos + normalWS * 0.01, pos - LightDirection * 50); + } + float reflectivity = 0.05; vec3 diffuseColor = diffuse * LightColor; fragColor = vec4(albedoColor.rgb * diffuseColor * (1.0 - reflectivity) + diffuseColor * reflectivity, 1.0); diff --git a/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/directional.json b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/directional.json index fd62cd5b2..71e75fb30 100644 --- a/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/directional.json +++ b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/directional.json @@ -3,6 +3,10 @@ "fragment": "veil:light/directional", "textures": { "AlbedoSampler": "veil:dynamic_buffer/albedo", - "NormalSampler": "veil:dynamic_buffer/normal" + "NormalSampler": "veil:dynamic_buffer/normal", + "DepthSampler": { + "type": "framebuffer", + "name": "minecraft:main:depth" + } } } From 929ebdd540b18cddd94ef33750776c66ff9e7322 Mon Sep 17 00:00:00 2001 From: Neddslayer <59270607+Neddslayer@users.noreply.github.com> Date: Mon, 27 Jul 2026 17:41:38 -0400 Subject: [PATCH 2/7] Add raymarched in-scattering as an option for deferred lights (#168) * Add initial inscattering implementation * Migrate to separate pass - Use separate, 1/4th scale framebuffer to maintain performance - Allow light renderers to have in-scattering as a separate option - Add depth linearization function to space_helper.glsl * Add light guides to the light editor * Correct depth testing * Change blur algorithm --- .../render/VeilLevelPerspectiveRenderer.java | 2 +- .../api/client/render/VeilRenderSystem.java | 7 +- .../render/framebuffer/VeilFramebuffers.java | 1 + .../render/light/LightGuideProvider.java | 18 +++ .../render/light/data/AreaLightData.java | 63 +++++++++- .../render/light/data/PointLightData.java | 59 +++++++++- .../renderer/InscatteringLightRenderer.java | 22 ++++ .../renderer/InstancedLightRenderer.java | 96 +++++++++++++-- .../render/light/renderer/LightRenderer.java | 13 +- .../render/light/AreaLightRenderer.java | 11 +- .../light/InstancedPointLightRenderer.java | 22 ++-- .../pipeline/VeilFirstPersonRenderer.java | 2 +- .../client/PipelineLevelRendererMixin.java | 2 +- .../framebuffers/light_inscattering.json | 10 ++ .../veil/pinwheel/rendertypes/light/area.json | 4 - .../rendertypes/light/inscattering/area.json | 26 ++++ .../rendertypes/light/inscattering/point.json | 26 ++++ .../shaders/include/space_helper.glsl | 5 + .../shaders/program/core/composite.fsh | 25 +++- .../shaders/program/core/composite.json | 4 + .../pinwheel/shaders/program/light/area.fsh | 10 +- .../pinwheel/shaders/program/light/area.vsh | 5 + .../program/light/inscattering/area.fsh | 111 ++++++++++++++++++ .../program/light/inscattering/area.json | 10 ++ .../program/light/inscattering/point.fsh | 75 ++++++++++++ .../program/light/inscattering/point.json | 10 ++ .../pinwheel/shaders/program/light/point.fsh | 5 +- .../pinwheel/shaders/program/light/point.vsh | 3 + 28 files changed, 599 insertions(+), 48 deletions(-) create mode 100644 common/src/main/java/foundry/veil/api/client/render/light/LightGuideProvider.java create mode 100644 common/src/main/java/foundry/veil/api/client/render/light/renderer/InscatteringLightRenderer.java create mode 100644 common/src/main/resources/assets/veil/pinwheel/framebuffers/light_inscattering.json create mode 100644 common/src/main/resources/assets/veil/pinwheel/rendertypes/light/inscattering/area.json create mode 100644 common/src/main/resources/assets/veil/pinwheel/rendertypes/light/inscattering/point.json create mode 100644 common/src/main/resources/assets/veil/pinwheel/shaders/program/light/inscattering/area.fsh create mode 100644 common/src/main/resources/assets/veil/pinwheel/shaders/program/light/inscattering/area.json create mode 100644 common/src/main/resources/assets/veil/pinwheel/shaders/program/light/inscattering/point.fsh create mode 100644 common/src/main/resources/assets/veil/pinwheel/shaders/program/light/inscattering/point.json diff --git a/common/src/main/java/foundry/veil/api/client/render/VeilLevelPerspectiveRenderer.java b/common/src/main/java/foundry/veil/api/client/render/VeilLevelPerspectiveRenderer.java index 6651ffb87..76c1b62e7 100644 --- a/common/src/main/java/foundry/veil/api/client/render/VeilLevelPerspectiveRenderer.java +++ b/common/src/main/java/foundry/veil/api/client/render/VeilLevelPerspectiveRenderer.java @@ -170,7 +170,7 @@ public static AdvancedFbo render(AdvancedFbo framebuffer, @Nullable Entity camer // Draw lights if (drawLights) { ProfilerFiller profiler = Minecraft.getInstance().getProfiler(); - if (VeilRenderSystem.drawLights(profiler, VeilRenderSystem.getCullingFrustum())) { + if (VeilRenderSystem.drawLights(profiler, VeilRenderSystem.getCullingFrustum(), true)) { VeilRenderSystem.compositeLights(profiler); } else { AdvancedFbo.unbind(); diff --git a/common/src/main/java/foundry/veil/api/client/render/VeilRenderSystem.java b/common/src/main/java/foundry/veil/api/client/render/VeilRenderSystem.java index 1ad4fd2c6..5bf93df4b 100644 --- a/common/src/main/java/foundry/veil/api/client/render/VeilRenderSystem.java +++ b/common/src/main/java/foundry/veil/api/client/render/VeilRenderSystem.java @@ -1278,10 +1278,11 @@ public static void setCameraBobOffset(Vector3fc offset) { } @ApiStatus.Internal - public static boolean drawLights(ProfilerFiller profiler, CullFrustum cullFrustum) { + public static boolean drawLights(ProfilerFiller profiler, CullFrustum cullFrustum, boolean renderInscattering) { FramebufferManager framebufferManager = renderer.getFramebufferManager(); AdvancedFbo lightFbo = framebufferManager.getFramebuffer(VeilFramebuffers.LIGHT); - if (lightFbo == null) { + AdvancedFbo lightInscatteringFbo = framebufferManager.getFramebuffer(VeilFramebuffers.LIGHT_INSCATTERING); + if (lightFbo == null || lightInscatteringFbo == null) { AdvancedFbo.unbind(); return false; } @@ -1302,7 +1303,7 @@ public static boolean drawLights(ProfilerFiller profiler, CullFrustum cullFrustu LightRenderer lightRenderer = renderer.getLightRenderer(); profiler.push("draw_lights"); - boolean rendered = lightRenderer.render(cullFrustum, lightFbo); + boolean rendered = lightRenderer.render(cullFrustum, lightFbo, lightInscatteringFbo, renderInscattering); profiler.pop(); renderProfiler.pop(); diff --git a/common/src/main/java/foundry/veil/api/client/render/framebuffer/VeilFramebuffers.java b/common/src/main/java/foundry/veil/api/client/render/framebuffer/VeilFramebuffers.java index c674d674c..ed25f8f26 100644 --- a/common/src/main/java/foundry/veil/api/client/render/framebuffer/VeilFramebuffers.java +++ b/common/src/main/java/foundry/veil/api/client/render/framebuffer/VeilFramebuffers.java @@ -17,6 +17,7 @@ private VeilFramebuffers() { public static final ResourceLocation FIRST_PERSON = buffer("first_person"); public static final ResourceLocation BLOOM = buffer("bloom"); public static final ResourceLocation LIGHT = buffer("light"); + public static final ResourceLocation LIGHT_INSCATTERING = buffer("light_inscattering"); public static final ResourceLocation POST = buffer("post"); public static final ResourceLocation TRANSLUCENT_TARGET = transparency("translucent"); diff --git a/common/src/main/java/foundry/veil/api/client/render/light/LightGuideProvider.java b/common/src/main/java/foundry/veil/api/client/render/light/LightGuideProvider.java new file mode 100644 index 000000000..455858658 --- /dev/null +++ b/common/src/main/java/foundry/veil/api/client/render/light/LightGuideProvider.java @@ -0,0 +1,18 @@ +package foundry.veil.api.client.render.light; + +import com.mojang.blaze3d.vertex.VertexConsumer; +import foundry.veil.api.client.render.MatrixStack; + +/** + * Provides extra information for given light data by rendering guides into the world. + * + * @author Neddslayer + */ +public interface LightGuideProvider { + + /** + * Render the light guides using the VertexConsumer. + */ + void renderLightGuide(MatrixStack stack, VertexConsumer consumer); + +} diff --git a/common/src/main/java/foundry/veil/api/client/render/light/data/AreaLightData.java b/common/src/main/java/foundry/veil/api/client/render/light/data/AreaLightData.java index 4dfa71c7d..1bece0ab0 100644 --- a/common/src/main/java/foundry/veil/api/client/render/light/data/AreaLightData.java +++ b/common/src/main/java/foundry/veil/api/client/render/light/data/AreaLightData.java @@ -1,11 +1,14 @@ package foundry.veil.api.client.render.light.data; +import com.mojang.blaze3d.vertex.VertexConsumer; import foundry.veil.api.client.color.Colorc; import foundry.veil.api.client.editor.EditorAttributeProvider; import foundry.veil.api.client.registry.LightTypeRegistry; import foundry.veil.api.client.render.CullFrustum; +import foundry.veil.api.client.render.MatrixStack; import foundry.veil.api.client.render.light.DDALightData; import foundry.veil.api.client.render.light.InstancedLightData; +import foundry.veil.api.client.render.light.LightGuideProvider; import imgui.ImGui; import net.minecraft.client.Camera; import net.minecraft.util.Mth; @@ -21,7 +24,7 @@ * * @since 2.0.0 */ -public class AreaLightData extends LightData implements InstancedLightData, DDALightData, EditorAttributeProvider { +public class AreaLightData extends LightData implements InstancedLightData, DDALightData, EditorAttributeProvider, LightGuideProvider { private static final float MAX_ANGLE_SIZE = (float) (65535.0 / 2.0 / Math.PI); @@ -34,6 +37,7 @@ public class AreaLightData extends LightData implements InstancedLightData, DDAL protected float angle; protected float distance; protected boolean occlusionEnabled; + protected float inscattering; public AreaLightData() { this.matrix = new Matrix4d(); @@ -45,6 +49,7 @@ public AreaLightData() { this.angle = (float) Math.toRadians(45); this.distance = 1.0F; this.occlusionEnabled = false; + this.inscattering = 0.0F; } /** @@ -136,6 +141,13 @@ public boolean isOcclusionEnabled() { return this.occlusionEnabled; } + /** + * @return The strength of the light's in-scattering effect. + */ + public float getInscatteringStrength() { + return this.inscattering; + } + /** * Sets the size of the light's surface * @@ -176,6 +188,12 @@ public AreaLightData setOcclusionEnabled(boolean occlusionEnabled) { return this; } + public AreaLightData setInscatteringStrength(float inscattering) { + this.inscattering = inscattering; + this.markDirty(); + return this; + } + @Override public AreaLightData setColor(Vector3fc color) { super.setColor(color); @@ -221,6 +239,7 @@ public void store(ByteBuffer buffer) { buffer.putShort((short) Mth.clamp((int) (this.angle * MAX_ANGLE_SIZE), 0, 65535)); buffer.putFloat(this.distance); buffer.putFloat(this.occlusionEnabled ? 1.0F : 0.0F); + buffer.putFloat(this.inscattering); } @Override @@ -261,6 +280,8 @@ public void renderImGuiAttributes() { float[] editAngle = new float[]{this.angle}; float[] editDistance = new float[]{this.distance}; + float[] editInscattering = new float[]{this.inscattering}; + if (ImGui.dragFloat2("size", editSize, 0.02F, 0.0001F)) { this.setSize(editSize[0], editSize[1]); } @@ -317,8 +338,44 @@ public void renderImGuiAttributes() { } if (ImGui.checkbox("Occluded", this.occlusionEnabled)) { - this.occlusionEnabled = !this.occlusionEnabled; - this.markDirty(); + this.setOcclusionEnabled(!this.occlusionEnabled); + } + + if (ImGui.dragScalar("In-scattering", editInscattering, 0.01F, 0.0F)) { + this.setInscatteringStrength(editInscattering[0]); } } + + @Override + public void renderLightGuide(MatrixStack stack, VertexConsumer consumer) { + stack.matrixPush(); + + stack.translate(this.position.x, this.position.y, this.position.z); + Vector3f rot = this.orientation.getEulerAnglesXYZ(new Vector3f()).mul(-1); + stack.rotate(new Quaternionf().rotateX(rot.x).rotateLocalY(rot.y).rotateLocalZ(rot.z)); + + Matrix4f pose = stack.position(); + + consumer.addVertex(pose, -this.size.x, -this.size.y, 0).setColor(this.color.red(), this.color.green(), this.color.blue(), this.color.alpha()); + consumer.addVertex(pose, -this.size.x, this.size.y, 0).setColor(this.color.red(), this.color.green(), this.color.blue(), this.color.alpha()); + consumer.addVertex(pose, this.size.x, this.size.y, 0).setColor(this.color.red(), this.color.green(), this.color.blue(), this.color.alpha()); + consumer.addVertex(pose, this.size.x, -this.size.y, 0).setColor(this.color.red(), this.color.green(), this.color.blue(), this.color.alpha()); + consumer.addVertex(pose, -this.size.x, -this.size.y, 0).setColor(this.color.red(), this.color.green(), this.color.blue(), this.color.alpha()); + + Vector3f endSize = new Vector3f(this.size.x + (this.distance * (float)Math.tan(this.angle * 0.5)), this.size.y + (this.distance * (float)Math.tan(this.angle * 0.5)), this.distance); + + consumer.addVertex(pose, -endSize.x, -endSize.y, this.distance).setColor(this.color.red(), this.color.green(), this.color.blue(), this.color.alpha()); + consumer.addVertex(pose, -endSize.x, endSize.y, this.distance).setColor(this.color.red(), this.color.green(), this.color.blue(), this.color.alpha()); + consumer.addVertex(pose, -this.size.x, this.size.y, 0).setColor(this.color.red(), this.color.green(), this.color.blue(), this.color.alpha()); + consumer.addVertex(pose, -endSize.x, endSize.y, this.distance).setColor(this.color.red(), this.color.green(), this.color.blue(), this.color.alpha()); + consumer.addVertex(pose, endSize.x, endSize.y, this.distance).setColor(this.color.red(), this.color.green(), this.color.blue(), this.color.alpha()); + consumer.addVertex(pose, this.size.x, this.size.y, 0).setColor(this.color.red(), this.color.green(), this.color.blue(), this.color.alpha()); + consumer.addVertex(pose, endSize.x, endSize.y, this.distance).setColor(this.color.red(), this.color.green(), this.color.blue(), this.color.alpha()); + consumer.addVertex(pose, endSize.x, -endSize.y, this.distance).setColor(this.color.red(), this.color.green(), this.color.blue(), this.color.alpha()); + consumer.addVertex(pose, this.size.x, -this.size.y, 0).setColor(this.color.red(), this.color.green(), this.color.blue(), this.color.alpha()); + consumer.addVertex(pose, endSize.x, -endSize.y, this.distance).setColor(this.color.red(), this.color.green(), this.color.blue(), this.color.alpha()); + consumer.addVertex(pose, -endSize.x, -endSize.y, this.distance).setColor(this.color.red(), this.color.green(), this.color.blue(), this.color.alpha()); + + stack.matrixPop(); + } } diff --git a/common/src/main/java/foundry/veil/api/client/render/light/data/PointLightData.java b/common/src/main/java/foundry/veil/api/client/render/light/data/PointLightData.java index 4da99f78d..9123d56c3 100644 --- a/common/src/main/java/foundry/veil/api/client/render/light/data/PointLightData.java +++ b/common/src/main/java/foundry/veil/api/client/render/light/data/PointLightData.java @@ -1,14 +1,19 @@ package foundry.veil.api.client.render.light.data; +import com.mojang.blaze3d.vertex.VertexConsumer; import foundry.veil.api.client.color.Colorc; import foundry.veil.api.client.editor.EditorAttributeProvider; import foundry.veil.api.client.registry.LightTypeRegistry; import foundry.veil.api.client.render.CullFrustum; +import foundry.veil.api.client.render.MatrixStack; import foundry.veil.api.client.render.light.DDALightData; import foundry.veil.api.client.render.light.IndirectLightData; +import foundry.veil.api.client.render.light.LightGuideProvider; import imgui.ImGui; import net.minecraft.client.Camera; +import net.minecraft.util.Mth; import net.minecraft.world.phys.Vec3; +import org.joml.Matrix4f; import org.joml.Vector3d; import org.joml.Vector3dc; import org.joml.Vector3fc; @@ -20,16 +25,18 @@ * * @since 2.0.0 */ -public class PointLightData extends LightData implements IndirectLightData, DDALightData, EditorAttributeProvider { +public class PointLightData extends LightData implements IndirectLightData, DDALightData, EditorAttributeProvider, LightGuideProvider { protected final Vector3d position; protected float radius; protected boolean occlusionEnabled; + protected float inscattering; public PointLightData() { this.position = new Vector3d(); this.radius = 1.0F; this.occlusionEnabled = false; + this.inscattering = 0.0F; } @Override @@ -58,6 +65,13 @@ public boolean isOcclusionEnabled() { return this.occlusionEnabled; } + /** + * @return The strength of the light's in-scattering effect. + */ + public float getInscatteringStrength() { + return this.inscattering; + } + public PointLightData setPosition(Vector3dc pos) { this.position.set(pos); this.markDirty(); @@ -82,6 +96,12 @@ public PointLightData setOcclusionEnabled(boolean occlusionEnabled) { return this; } + public PointLightData setInscatteringStrength(float inscattering) { + this.inscattering = inscattering; + this.markDirty(); + return this; + } + @Override public PointLightData setColor(Vector3fc color) { super.setColor(color); @@ -126,6 +146,7 @@ public void store(ByteBuffer buffer) { buffer.putFloat(this.color.blue() * this.brightness); buffer.putFloat(this.radius); buffer.putFloat(this.occlusionEnabled ? 1.0F : 0.0F); + buffer.putFloat(this.inscattering); } @Override @@ -149,6 +170,8 @@ public void renderImGuiAttributes() { float[] editRadius = new float[]{this.radius}; + float[] editInscattering = new float[]{this.inscattering}; + float totalWidth = ImGui.calcItemWidth(); ImGui.pushItemWidth(totalWidth / 3.0F - (ImGui.getStyle().getItemInnerSpacingX() * 0.58F)); if (ImGui.dragScalar("##x", editX, 0.02F)) { @@ -175,8 +198,38 @@ public void renderImGuiAttributes() { } if (ImGui.checkbox("Occluded", this.occlusionEnabled)) { - this.occlusionEnabled = !this.occlusionEnabled; - this.markDirty(); + this.setOcclusionEnabled(!this.occlusionEnabled); + } + + if (ImGui.dragScalar("In-scattering", editInscattering, 0.01F, 0.0F)) { + this.setInscatteringStrength(editInscattering[0]); + } + } + + @Override + public void renderLightGuide(MatrixStack stack, VertexConsumer consumer) { + stack.matrixPush(); + + stack.translate(this.position.x, this.position.y, this.position.z); + + Matrix4f pose = stack.position(); + + for (int i = -8; i < 25; i++) { + consumer.addVertex(pose, (float)Math.sin(i / 32.0f * Mth.TWO_PI) * this.radius, 0, (float)Math.cos(i / 32.0f * Mth.TWO_PI) * this.radius).setColor(this.color.red(), this.color.green(), this.color.blue(), this.color.alpha()); } + + for (int i = -8; i < 25; i++) { + consumer.addVertex(pose, (float)Math.sin(i / 32.0f * Mth.TWO_PI) * this.radius, (float)Math.cos(i / 32.0f * Mth.TWO_PI) * this.radius, 0).setColor(this.color.red(), this.color.green(), this.color.blue(), this.color.alpha()); + } + + for (int i = 25; i >= 16; i--) { + consumer.addVertex(pose, (float)Math.sin(i / 32.0f * Mth.TWO_PI) * this.radius, (float)Math.cos(i / 32.0f * Mth.TWO_PI) * this.radius, 0).setColor(this.color.red(), this.color.green(), this.color.blue(), this.color.alpha()); + } + + for (int i = -8; i < 25; i++) { + consumer.addVertex(pose, 0, (float)Math.sin(i / 32.0f * Mth.TWO_PI) * this.radius, (float)Math.cos(i / 32.0f * Mth.TWO_PI) * this.radius).setColor(this.color.red(), this.color.green(), this.color.blue(), this.color.alpha()); + } + + stack.matrixPop(); } } diff --git a/common/src/main/java/foundry/veil/api/client/render/light/renderer/InscatteringLightRenderer.java b/common/src/main/java/foundry/veil/api/client/render/light/renderer/InscatteringLightRenderer.java new file mode 100644 index 000000000..ecdec3077 --- /dev/null +++ b/common/src/main/java/foundry/veil/api/client/render/light/renderer/InscatteringLightRenderer.java @@ -0,0 +1,22 @@ +package foundry.veil.api.client.render.light.renderer; + +import foundry.veil.api.client.render.light.data.LightData; + +/** + * Renders in-scattering for deferred lights. + * + * @author Neddslayer + */ +public interface InscatteringLightRenderer extends LightTypeRenderer { + + /** + * Renders inscattering of all prepared lights with this renderer. + *
+ * Shaders, custom uniforms, and the way lights are rendered is up to the individual renderer. + * This function is not called for the first-person perspective. + * + * @param lightRenderer The light renderer instance + */ + void renderLightInscattering(LightRenderer lightRenderer); + +} diff --git a/common/src/main/java/foundry/veil/api/client/render/light/renderer/InstancedLightRenderer.java b/common/src/main/java/foundry/veil/api/client/render/light/renderer/InstancedLightRenderer.java index 4b78eed98..41f1e0e30 100644 --- a/common/src/main/java/foundry/veil/api/client/render/light/renderer/InstancedLightRenderer.java +++ b/common/src/main/java/foundry/veil/api/client/render/light/renderer/InstancedLightRenderer.java @@ -1,14 +1,23 @@ package foundry.veil.api.client.render.light.renderer; import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.vertex.MeshData; +import com.mojang.blaze3d.vertex.*; +import foundry.veil.Veil; import foundry.veil.api.client.render.CullFrustum; +import foundry.veil.api.client.render.MatrixStack; +import foundry.veil.api.client.render.VeilRenderBridge; +import foundry.veil.api.client.render.VeilRenderSystem; import foundry.veil.api.client.render.light.InstancedLightData; +import foundry.veil.api.client.render.light.LightGuideProvider; import foundry.veil.api.client.render.light.data.LightData; import foundry.veil.api.client.render.vertex.VertexArray; import foundry.veil.api.client.render.vertex.VertexArrayBuilder; +import foundry.veil.impl.client.editor.LightInspector; +import net.minecraft.client.Camera; +import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.RenderType; import org.jetbrains.annotations.Nullable; +import org.joml.Matrix4fStack; import org.lwjgl.system.MemoryStack; import java.nio.ByteBuffer; @@ -24,7 +33,7 @@ * @param The type of lights to render * @author Ocelot */ -public abstract class InstancedLightRenderer implements LightTypeRenderer { +public abstract class InstancedLightRenderer implements InscatteringLightRenderer { private static final int MAX_UPLOADS = 400; @@ -79,6 +88,14 @@ public InstancedLightRenderer(int lightSize) { */ protected abstract @Nullable RenderType getRenderType(List> lights); + /** + * Calculates the render type to use for the specified lights' in-scattering. + * + * @param lights All lights in the order they are in the instanced buffer + * @return The render type to use for in-scattering + */ + protected abstract @Nullable RenderType getInscatteringRenderType(List> lights); + private void updateAllLights() { try (MemoryStack stack = MemoryStack.stackPush()) { int pointer = 0; @@ -162,17 +179,7 @@ public void prepareLights(LightRenderer lightRenderer, CullFrustum frustum) { } } - @Override - public void renderLights(LightRenderer lightRenderer) { - if (this.visibleLights.isEmpty()) { - return; - } - - RenderType renderType = this.getRenderType(this.visibleLights); - if (renderType == null) { - return; - } - + private void bindRenderQuad() { RenderSystem.glBindBuffer(GL_ARRAY_BUFFER, this.instancedVbo); boolean resized = false; @@ -197,6 +204,69 @@ public void renderLights(LightRenderer lightRenderer) { } this.vertexArray.bind(); + } + + @Override + public void renderLights(LightRenderer lightRenderer) { + if (this.visibleLights.isEmpty()) { + return; + } + + RenderType renderType = this.getRenderType(this.visibleLights); + if (renderType == null) { + return; + } + + this.bindRenderQuad(); + this.vertexArray.drawInstancedWithRenderType(renderType, this.visibleLights.size()); + + // Render light guides + if (Veil.IMGUIMC) { + RenderSystem.setProjectionMatrix(VeilRenderSystem.renderer().getCameraMatrices().getProjectionMatrix(), VertexSorting.DISTANCE_TO_ORIGIN); + Matrix4fStack stack = RenderSystem.getModelViewStack(); + stack.pushMatrix(); + stack.set(VeilRenderSystem.renderer().getCameraMatrices().getViewMatrix()); + RenderSystem.applyModelViewMatrix(); + Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera(); + MatrixStack matrixStack = VeilRenderBridge.create(new PoseStack()); + matrixStack.matrixPush(); + matrixStack.translate(-camera.getPosition().x, -camera.getPosition().y, -camera.getPosition().z); + + for (LightHandle handle : this.lights) { + if (VeilRenderSystem.renderer().getEditorManager().isVisible(i -> i instanceof LightInspector) && handle.getLightData() instanceof LightGuideProvider) { + RenderType debugRenderType = RenderType.debugLineStrip(1); + ByteBufferBuilder bytebufferbuilder = new ByteBufferBuilder(debugRenderType.bufferSize()); + BufferBuilder builder = new BufferBuilder(bytebufferbuilder, debugRenderType.mode(), debugRenderType.format()); + ((LightGuideProvider) handle.getLightData()).renderLightGuide(matrixStack, builder); + MeshData meshData = builder.build(); + if (meshData != null) { + if (renderType.sortOnUpload()) { + ByteBufferBuilder sortBufferBuilder = new ByteBufferBuilder(renderType.bufferSize()); + meshData.sortQuads(sortBufferBuilder, RenderSystem.getVertexSorting()); + } + + debugRenderType.draw(meshData); + } + } + } + matrixStack.matrixPop(); + stack.popMatrix(); + RenderSystem.applyModelViewMatrix(); + } + } + + @Override + public void renderLightInscattering(LightRenderer lightRenderer) { + if (this.visibleLights.isEmpty()) { + return; + } + + RenderType renderType = this.getInscatteringRenderType(this.visibleLights); + if (renderType == null) { + return; + } + + this.bindRenderQuad(); this.vertexArray.drawInstancedWithRenderType(renderType, this.visibleLights.size()); } diff --git a/common/src/main/java/foundry/veil/api/client/render/light/renderer/LightRenderer.java b/common/src/main/java/foundry/veil/api/client/render/light/renderer/LightRenderer.java index 524338e68..fc03f029a 100644 --- a/common/src/main/java/foundry/veil/api/client/render/light/renderer/LightRenderer.java +++ b/common/src/main/java/foundry/veil/api/client/render/light/renderer/LightRenderer.java @@ -54,7 +54,7 @@ public LightRenderer() { * @return If any lights were actually rendered */ @ApiStatus.Internal - public boolean render(CullFrustum frustum, AdvancedFbo lightFbo) { + public boolean render(CullFrustum frustum, AdvancedFbo lightFbo, AdvancedFbo lightInscatteringFbo, boolean renderInscattering) { boolean hasRendered = false; boolean setupDDA = false; VeilRenderer renderer = VeilRenderSystem.renderer(); @@ -88,6 +88,17 @@ public boolean render(CullFrustum frustum, AdvancedFbo lightFbo) { ddalightRenderer.uploadVoxelGridUniforms(VoxelShadowGrid.getTextureId(), VoxelShadowGrid.getUniformGridPos()); } lightRenderer.renderLights(this); + + if (lightRenderer instanceof InscatteringLightRenderer inscatteringLightRenderer && renderInscattering) { + lightInscatteringFbo.bind(true); + AdvancedFbo.getMainFramebuffer().resolveToAdvancedFbo(lightInscatteringFbo, GL_DEPTH_BUFFER_BIT, GL_NEAREST); + inscatteringLightRenderer.renderLightInscattering(this); + AdvancedFbo.unbind(); + } + } + + if (hasRendered && !renderInscattering) { + lightInscatteringFbo.clear(GL_COLOR_BUFFER_BIT); } if (!hasRendered) { diff --git a/common/src/main/java/foundry/veil/impl/client/render/light/AreaLightRenderer.java b/common/src/main/java/foundry/veil/impl/client/render/light/AreaLightRenderer.java index 5621245d7..cc9e22638 100644 --- a/common/src/main/java/foundry/veil/impl/client/render/light/AreaLightRenderer.java +++ b/common/src/main/java/foundry/veil/impl/client/render/light/AreaLightRenderer.java @@ -25,9 +25,10 @@ public class AreaLightRenderer extends InstancedLightRenderer implements DDALightRenderer { private static final ResourceLocation RENDER_TYPE = Veil.veilPath("light/area"); + private static final ResourceLocation INSCATTERING_RENDER_TYPE = Veil.veilPath("light/inscattering/area"); public AreaLightRenderer() { - super(Float.BYTES * 23 + 2); + super(Float.BYTES * 24 + 2); } @Override @@ -47,7 +48,8 @@ protected void setupBufferState(VertexArrayBuilder builder) { builder.setVertexAttribute(6, 2, 2, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 19); // size builder.setVertexAttribute(7, 2, 1, VertexArrayBuilder.DataType.UNSIGNED_SHORT, true, Float.BYTES * 21); // angle builder.setVertexAttribute(8, 2, 1, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 21 + 2); // distance - builder.setVertexAttribute(9, 2, 1, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 21 + 2 + Float.BYTES); + builder.setVertexAttribute(9, 2, 1, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 21 + 2 + Float.BYTES); // Occlusion + builder.setVertexAttribute(10, 2, 1, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 21 + 2 + Float.BYTES * 2); // In-scattering } @Override @@ -55,6 +57,11 @@ protected void setupBufferState(VertexArrayBuilder builder) { return VeilRenderType.get(RENDER_TYPE); } + @Override + protected @Nullable RenderType getInscatteringRenderType(List> lights) { + return VeilRenderType.get(INSCATTERING_RENDER_TYPE); + } + @Override public void uploadVoxelGridUniforms(int voxelGridTexture, Vector3fc voxelGridOrigin) { RenderType renderType = VeilRenderType.get(RENDER_TYPE); diff --git a/common/src/main/java/foundry/veil/impl/client/render/light/InstancedPointLightRenderer.java b/common/src/main/java/foundry/veil/impl/client/render/light/InstancedPointLightRenderer.java index 4353ecf5b..5a78721db 100644 --- a/common/src/main/java/foundry/veil/impl/client/render/light/InstancedPointLightRenderer.java +++ b/common/src/main/java/foundry/veil/impl/client/render/light/InstancedPointLightRenderer.java @@ -7,10 +7,7 @@ import com.mojang.blaze3d.vertex.VertexFormat; import foundry.veil.Veil; import foundry.veil.api.client.render.light.data.PointLightData; -import foundry.veil.api.client.render.light.renderer.DDALightRenderer; -import foundry.veil.api.client.render.light.renderer.InstancedLightRenderer; -import foundry.veil.api.client.render.light.renderer.LightRenderHandle; -import foundry.veil.api.client.render.light.renderer.LightTypeRenderer; +import foundry.veil.api.client.render.light.renderer.*; import foundry.veil.api.client.render.rendertype.VeilRenderType; import foundry.veil.api.client.render.vertex.VertexArrayBuilder; import net.minecraft.client.renderer.RenderType; @@ -25,9 +22,10 @@ public class InstancedPointLightRenderer extends InstancedLightRenderer implements DDALightRenderer { private static final ResourceLocation RENDER_TYPE = Veil.veilPath("light/point"); + private static final ResourceLocation INSCATTERING_RENDER_TYPE = Veil.veilPath("light/inscattering/point"); public InstancedPointLightRenderer() { - super(Float.BYTES * 8); + super(Float.BYTES * 9); } @Override @@ -39,10 +37,11 @@ protected MeshData createMesh() { @Override protected void setupBufferState(VertexArrayBuilder builder) { - builder.setVertexAttribute(1, 2, 3, VertexArrayBuilder.DataType.FLOAT, false, 0); - builder.setVertexAttribute(2, 2, 3, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 3); - builder.setVertexAttribute(3, 2, 1, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 6); - builder.setVertexAttribute(4, 2, 1, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 7); + builder.setVertexAttribute(1, 2, 3, VertexArrayBuilder.DataType.FLOAT, false, 0); // LightPosition + builder.setVertexAttribute(2, 2, 3, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 3); // Color + builder.setVertexAttribute(3, 2, 1, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 6); // Distance + builder.setVertexAttribute(4, 2, 1, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 7); // Occluded + builder.setVertexAttribute(5, 2, 1, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 8); // In-scattering } @Override @@ -50,6 +49,11 @@ protected void setupBufferState(VertexArrayBuilder builder) { return VeilRenderType.get(RENDER_TYPE); } + @Override + protected @Nullable RenderType getInscatteringRenderType(List> lights) { + return VeilRenderType.get(INSCATTERING_RENDER_TYPE); + } + @Override public void uploadVoxelGridUniforms(int voxelGridTexture, Vector3fc voxelGridOrigin) { RenderType renderType = VeilRenderType.get(RENDER_TYPE); diff --git a/common/src/main/java/foundry/veil/impl/client/render/pipeline/VeilFirstPersonRenderer.java b/common/src/main/java/foundry/veil/impl/client/render/pipeline/VeilFirstPersonRenderer.java index 8f455076a..862938b7a 100644 --- a/common/src/main/java/foundry/veil/impl/client/render/pipeline/VeilFirstPersonRenderer.java +++ b/common/src/main/java/foundry/veil/impl/client/render/pipeline/VeilFirstPersonRenderer.java @@ -60,7 +60,7 @@ public static void bind(int mask) { public static void unbind() { // TODO update projection/modelview matrix ProfilerFiller profiler = Minecraft.getInstance().getProfiler(); - boolean rendered = VeilRenderSystem.drawLights(profiler, VeilRenderSystem.getCullingFrustum()); + boolean rendered = VeilRenderSystem.drawLights(profiler, VeilRenderSystem.getCullingFrustum(), false); ((RenderTargetExtension) Minecraft.getInstance().getMainRenderTarget()).veil$setWrapper(null); if (rendered) { diff --git a/common/src/main/java/foundry/veil/mixin/pipeline/client/PipelineLevelRendererMixin.java b/common/src/main/java/foundry/veil/mixin/pipeline/client/PipelineLevelRendererMixin.java index 8d0a2957e..f7e7dfb14 100644 --- a/common/src/main/java/foundry/veil/mixin/pipeline/client/PipelineLevelRendererMixin.java +++ b/common/src/main/java/foundry/veil/mixin/pipeline/client/PipelineLevelRendererMixin.java @@ -113,7 +113,7 @@ public void loadFramebuffer(CallbackInfo ci) { @Inject(method = "renderLevel", at = @At("TAIL")) public void blit(CallbackInfo ci, @Local ProfilerFiller profiler) { if (!VeilLevelPerspectiveRenderer.isRenderingPerspective()) { - if (VeilRenderSystem.drawLights(profiler, VeilRenderSystem.getCullingFrustum())) { + if (VeilRenderSystem.drawLights(profiler, VeilRenderSystem.getCullingFrustum(), true)) { VeilRenderSystem.compositeLights(profiler); } else { AdvancedFbo.unbind(); diff --git a/common/src/main/resources/assets/veil/pinwheel/framebuffers/light_inscattering.json b/common/src/main/resources/assets/veil/pinwheel/framebuffers/light_inscattering.json new file mode 100644 index 000000000..0eec263c8 --- /dev/null +++ b/common/src/main/resources/assets/veil/pinwheel/framebuffers/light_inscattering.json @@ -0,0 +1,10 @@ +{ + "width": "q.screen_width / 4.0", + "height": "q.screen_height / 4.0", + "depth": true, + "filter": { + "blur": true + }, + "autoClear": false, + "format": "RGB16F" +} \ No newline at end of file diff --git a/common/src/main/resources/assets/veil/pinwheel/rendertypes/light/area.json b/common/src/main/resources/assets/veil/pinwheel/rendertypes/light/area.json index bf260b5f3..924972919 100644 --- a/common/src/main/resources/assets/veil/pinwheel/rendertypes/light/area.json +++ b/common/src/main/resources/assets/veil/pinwheel/rendertypes/light/area.json @@ -18,10 +18,6 @@ "type": "minecraft:depth_test", "mode": "greater" }, - { - "type": "minecraft:cull", - "face": "back" - }, { "type": "minecraft:transparency", "mode": "additive" diff --git a/common/src/main/resources/assets/veil/pinwheel/rendertypes/light/inscattering/area.json b/common/src/main/resources/assets/veil/pinwheel/rendertypes/light/inscattering/area.json new file mode 100644 index 000000000..818f826e7 --- /dev/null +++ b/common/src/main/resources/assets/veil/pinwheel/rendertypes/light/inscattering/area.json @@ -0,0 +1,26 @@ +{ + "format": "POSITION_TEX", + "mode": "QUADS", + "bufferSize": "TRANSIENT", + "sort": false, + "affectsCrumbling": false, + "outline": false, + "layers": [ + { + "type": "veil:shader", + "name": "veil:light/inscattering/area" + }, + { + "type": "minecraft:write_mask", + "depth": false + }, + { + "type": "minecraft:depth_test", + "mode": "notequal" + }, + { + "type": "minecraft:transparency", + "mode": "additive" + } + ] +} \ No newline at end of file diff --git a/common/src/main/resources/assets/veil/pinwheel/rendertypes/light/inscattering/point.json b/common/src/main/resources/assets/veil/pinwheel/rendertypes/light/inscattering/point.json new file mode 100644 index 000000000..c841626df --- /dev/null +++ b/common/src/main/resources/assets/veil/pinwheel/rendertypes/light/inscattering/point.json @@ -0,0 +1,26 @@ +{ + "format": "POSITION_TEX", + "mode": "QUADS", + "bufferSize": "TRANSIENT", + "sort": false, + "affectsCrumbling": false, + "outline": false, + "layers": [ + { + "type": "veil:shader", + "name": "veil:light/inscattering/point" + }, + { + "type": "minecraft:write_mask", + "depth": false + }, + { + "type": "minecraft:depth_test", + "mode": "notequal" + }, + { + "type": "minecraft:transparency", + "mode": "additive" + } + ] +} \ No newline at end of file diff --git a/common/src/main/resources/assets/veil/pinwheel/shaders/include/space_helper.glsl b/common/src/main/resources/assets/veil/pinwheel/shaders/include/space_helper.glsl index 7bc8b79ea..dcb6557ff 100644 --- a/common/src/main/resources/assets/veil/pinwheel/shaders/include/space_helper.glsl +++ b/common/src/main/resources/assets/veil/pinwheel/shaders/include/space_helper.glsl @@ -142,3 +142,8 @@ vec4 screenToClipSpace(vec2 uv, float depth) { vec3 viewDirFromUv(vec2 uv) { return normalize(screenToLocalSpace(uv, 1.0).xyz); } + +float depthSampleToWorldDepth(float depthSample) { + float f = depthSample * 2.0 - 1.0; + return 2.0 * VeilCamera.NearPlane * VeilCamera.FarPlane / (VeilCamera.FarPlane + VeilCamera.NearPlane - f * (VeilCamera.FarPlane - VeilCamera.NearPlane)); +} \ No newline at end of file diff --git a/common/src/main/resources/assets/veil/pinwheel/shaders/program/core/composite.fsh b/common/src/main/resources/assets/veil/pinwheel/shaders/program/core/composite.fsh index b3b9d05fb..250d0c347 100644 --- a/common/src/main/resources/assets/veil/pinwheel/shaders/program/core/composite.fsh +++ b/common/src/main/resources/assets/veil/pinwheel/shaders/program/core/composite.fsh @@ -1,17 +1,40 @@ uniform sampler2D DiffuseSampler0; uniform sampler2D DiffuseDepthSampler; uniform sampler2D LightSampler; +uniform sampler2D LightInscatteringSampler; uniform vec4 ColorModulator; +uniform vec2 ScreenSize; in vec2 texCoord; out vec4 fragColor; +#define BOXRADIUS 3 + +vec3 boxBlur(vec2 uv, vec2 size) +{ + int kernel_window_size = BOXRADIUS * 2 + 1; + int samples = kernel_window_size * kernel_window_size; + + vec3 color = vec3(0); + + float wsum = 0.0; + for (int ry = -BOXRADIUS; ry <= BOXRADIUS; ++ry) + for (int rx = -BOXRADIUS; rx <= BOXRADIUS; ++rx) + { + float w = 1.0; + wsum += w; + color += texture(LightInscatteringSampler, uv + vec2(rx, ry) / size).rgb * w; + } + + return color/wsum; +} + void main() { vec4 main = texture(DiffuseSampler0, texCoord); float mainDepth = texture(DiffuseDepthSampler, texCoord).r; - vec3 light = texture(LightSampler, texCoord).rgb; + vec3 light = texture(LightSampler, texCoord).rgb + boxBlur(texCoord, ScreenSize / 4.0).rgb; fragColor = vec4(main.rgb + light, main.a); gl_FragDepth = mainDepth; } diff --git a/common/src/main/resources/assets/veil/pinwheel/shaders/program/core/composite.json b/common/src/main/resources/assets/veil/pinwheel/shaders/program/core/composite.json index 2e814cda6..9ea15e36b 100644 --- a/common/src/main/resources/assets/veil/pinwheel/shaders/program/core/composite.json +++ b/common/src/main/resources/assets/veil/pinwheel/shaders/program/core/composite.json @@ -5,6 +5,10 @@ "LightSampler": { "type": "framebuffer", "name": "veil:light" + }, + "LightInscatteringSampler": { + "type": "framebuffer", + "name": "veil:light_inscattering" } } } diff --git a/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/area.fsh b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/area.fsh index 079a01398..fa9b7fbae 100644 --- a/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/area.fsh +++ b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/area.fsh @@ -5,11 +5,13 @@ #include veil:voxel_shadow in mat4 lightMat; +in mat4 invLightMat; in vec3 lightColor; in vec2 size; in float maxAngle; in float maxDistance; in float occluded; +in float inscattering; uniform sampler2D AlbedoSampler; uniform sampler2D NormalSampler; @@ -29,9 +31,10 @@ float sacos(float x) } struct AreaLightResult { vec3 position; float angle; }; -AreaLightResult closestPointOnPlaneAndAngle(vec3 point, mat4 planeMatrix, vec2 planeSize) { +AreaLightResult closestPointOnPlaneAndAngle(vec3 point, mat4 planeMatrix, mat4 invPlaneMatrix, vec2 planeSize) { // no idea why i need to do this planeMatrix[3].xyz *= -1.0; + invPlaneMatrix[3].xyz *= -1.0; // transform the point to the plane's local space vec3 localSpacePoint = (planeMatrix * vec4(point, 1.0)).xyz; // clamp position @@ -42,7 +45,7 @@ AreaLightResult closestPointOnPlaneAndAngle(vec3 point, mat4 planeMatrix, vec2 p float angle = sacos(dot(direction, vec3(0.0, 0.0, 1.0))); // transform back to global space - return AreaLightResult((inverse(planeMatrix) * vec4(localSpacePointOnPlane, 1.0)).xyz, angle); + return AreaLightResult((invPlaneMatrix * vec4(localSpacePointOnPlane, 1.0)).xyz, angle); } void main() { @@ -52,13 +55,12 @@ void main() { if (albedoColor.a == 0) { discard; } - vec3 normalVS = texture(NormalSampler, screenUv).xyz; float depth = texture(DepthSampler, screenUv).r; vec3 pos = screenToWorldSpace(screenUv, depth).xyz; // lighting calculation - AreaLightResult areaLightInfo = closestPointOnPlaneAndAngle(pos, lightMat, size); + AreaLightResult areaLightInfo = closestPointOnPlaneAndAngle(pos, lightMat, invLightMat, size); vec3 lightPos = areaLightInfo.position; float angle = areaLightInfo.angle; diff --git a/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/area.vsh b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/area.vsh index e166c011a..8f4cddcca 100644 --- a/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/area.vsh +++ b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/area.vsh @@ -7,13 +7,16 @@ layout (location = 6) in vec2 Size; layout (location = 7) in float NormalizedAngle; layout (location = 8) in float Distance; layout (location = 9) in float Occluded; +layout (location = 10) in float Inscattering; out mat4 lightMat; +out mat4 invLightMat; out vec3 lightColor; out vec2 size; out float maxAngle; out float maxDistance; out float occluded; +out float inscattering; void main() { vec3 vertexPos = Position; @@ -32,9 +35,11 @@ void main() { gl_Position = VeilCamera.ProjMat * VeilCamera.ViewMat * vec4(vertexPos - VeilCamera.CameraPosition, 1.0); lightMat = LightMatrix; + invLightMat = inverse(LightMatrix); lightColor = Color; size = Size; maxAngle = Angle; maxDistance = Distance; occluded = Occluded; + inscattering = Inscattering; } diff --git a/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/inscattering/area.fsh b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/inscattering/area.fsh new file mode 100644 index 000000000..34fa7dbf7 --- /dev/null +++ b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/inscattering/area.fsh @@ -0,0 +1,111 @@ +#include veil:common +#include veil:space_helper +#include veil:color_utilities +#include veil:light +#include veil:voxel_shadow + +in mat4 lightMat; +in mat4 invLightMat; +in vec3 lightColor; +in vec2 size; +in float maxAngle; +in float maxDistance; +in float occluded; +in float inscattering; + +uniform sampler2D DepthSampler; + +uniform vec2 ScreenSize; + +out vec4 fragColor; + +// acos approximation +// faster and also doesn't flicker weirdly +float sacos(float x) +{ + float y = abs(clamp(x, -1.0, 1.0)); + float z = (-0.168577*y + 1.56723) * sqrt(1.0 - y); + return mix(0.5*3.1415927, z, sign(x)); +} + +struct AreaLightResult { vec3 position; float angle; }; +AreaLightResult closestPointOnPlaneAndAngle(vec3 point, mat4 planeMatrix, mat4 invPlaneMatrix, vec2 planeSize) { + // no idea why i need to do this + planeMatrix[3].xyz *= -1.0; + invPlaneMatrix[3].xyz *= -1.0; + // transform the point to the plane's local space + vec3 localSpacePoint = (planeMatrix * vec4(point, 1.0)).xyz; + // clamp position + vec3 localSpacePointOnPlane = vec3(clamp(localSpacePoint.xy, -planeSize, planeSize), 0); + + // calculate the angles + vec3 direction = normalize(localSpacePoint - localSpacePointOnPlane); + float angle = sacos(dot(direction, vec3(0.0, 0.0, 1.0))); + + // transform back to global space + return AreaLightResult((invPlaneMatrix * vec4(localSpacePointOnPlane, 1.0)).xyz, angle); +} + +//Fog density +#define DENSITY 2.6 +//Surface pass rate +#define PASSTHROUGH 0.5 + +#define STEPS 50.0 + +float sdSphere( vec3 p, float r ) +{ + return (length(p) - r) / DENSITY + PASSTHROUGH; +} + +vec3 ray(vec3 dir, vec3 pos, vec3 fragPos) { + //Output brightness + #define BRIGHTNESS 0.004 + + //Accumulative color + vec3 col = vec3(0.0); + float d = 0; + float fragDistance = distance(pos, fragPos); + + //Glow raymarch loop + for(float i = 0.0; i 0.0) { + float depth = texture(DepthSampler, screenUv).r; + volume = ray(viewDirFromUv(screenUv), VeilCamera.CameraPosition + VeilCamera.CameraBobOffset, screenToWorldSpace(screenUv, depth).xyz); + } + + fragColor = vec4(volume, 1.0); +} diff --git a/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/inscattering/area.json b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/inscattering/area.json new file mode 100644 index 000000000..1625fa8d5 --- /dev/null +++ b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/inscattering/area.json @@ -0,0 +1,10 @@ +{ + "vertex": "veil:light/area", + "fragment": "veil:light/inscattering/area", + "textures": { + "DepthSampler": { + "type": "framebuffer", + "name": "veil:light_inscattering:depth" + } + } +} diff --git a/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/inscattering/point.fsh b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/inscattering/point.fsh new file mode 100644 index 000000000..d4f2dd832 --- /dev/null +++ b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/inscattering/point.fsh @@ -0,0 +1,75 @@ +#include veil:common +#include veil:space_helper +#include veil:color_utilities +#include veil:light +#include veil:voxel_shadow + +in vec3 lightPos; +in vec3 lightColor; +in float radius; +in float occluded; +in float inscattering; + +uniform sampler2D DepthSampler; + +uniform vec2 ScreenSize; + +out vec4 fragColor; + +//Fog density +#define DENSITY 2.6 +//Surface pass rate +#define PASSTHROUGH 0.5 + +#define STEPS 50.0 + +float sdSphere( vec3 p, float r ) +{ + return (length(p - lightPos) - r) / DENSITY + PASSTHROUGH; +} + +vec3 ray(vec3 dir, vec3 pos, vec3 fragPos) { + //Output brightness + #define BRIGHTNESS 0.004 + + //Accumulative color + vec3 col = vec3(0.0); + float d = 0; + float fragDistance = distance(pos, fragPos); + + //Glow raymarch loop + for(float i = 0.0; i 0.0) { + float depth = texture(DepthSampler, screenUv).r; + volume = ray(viewDirFromUv(screenUv), VeilCamera.CameraPosition + VeilCamera.CameraBobOffset, screenToWorldSpace(screenUv, depth).xyz); + } + + fragColor = vec4(volume, 1.0); +} diff --git a/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/inscattering/point.json b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/inscattering/point.json new file mode 100644 index 000000000..21a5c297b --- /dev/null +++ b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/inscattering/point.json @@ -0,0 +1,10 @@ +{ + "vertex": "veil:light/point", + "fragment": "veil:light/inscattering/point", + "textures": { + "DepthSampler": { + "type": "framebuffer", + "name": "minecraft:main:depth" + } + } +} diff --git a/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/point.fsh b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/point.fsh index b76e65599..6769de4c3 100644 --- a/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/point.fsh +++ b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/point.fsh @@ -8,6 +8,7 @@ in vec3 lightPos; in vec3 lightColor; in float radius; in float occluded; +in float inscattering; uniform sampler2D AlbedoSampler; uniform sampler2D NormalSampler; @@ -21,21 +22,21 @@ void main() { vec2 screenUv = gl_FragCoord.xy / ScreenSize; vec4 albedoColor = texture(AlbedoSampler, screenUv); + if (albedoColor.a == 0) { discard; } - float depth = texture(DepthSampler, screenUv).r; vec3 pos = screenToWorldSpace(screenUv, depth).xyz; // lighting calculation vec3 offset = lightPos - pos; - vec3 normalVS = texture(NormalSampler, screenUv).xyz; vec3 lightDirection = normalize((VeilCamera.ViewMat * vec4(offset, 0.0)).xyz); float diffuse = clamp(dot(normalVS, lightDirection), 0.0, 1.0); diffuse = (diffuse + MINECRAFT_AMBIENT_LIGHT) / (1.0 + MINECRAFT_AMBIENT_LIGHT); diffuse *= attenuate_no_cusp(length(offset), radius); + if (occluded > 0.5) { vec3 normalWS = normalize((VeilCamera.IViewMat * vec4(normalVS, 0.0)).xyz); diffuse *= voxelshadowVisibility(pos + normalWS * 0.01, lightPos); diff --git a/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/point.vsh b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/point.vsh index 8f6b6831e..328f81536 100644 --- a/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/point.vsh +++ b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/point.vsh @@ -5,11 +5,13 @@ layout (location = 1) in vec3 LightPosition; layout (location = 2) in vec3 Color; layout (location = 3) in float Distance; layout (location = 4) in float Occluded; +layout (location = 5) in float Inscattering; out vec3 lightPos; out vec3 lightColor; out float radius; out float occluded; +out float inscattering; void main() { vec3 size = Position * Distance; @@ -22,4 +24,5 @@ void main() { lightColor = Color; radius = Distance; occluded = Occluded; + inscattering = Inscattering; } From 3f6515509e8b25865140890a3a31edf8ace6b354 Mon Sep 17 00:00:00 2001 From: Redcat_XVIII <124214835+redboi18@users.noreply.github.com> Date: Mon, 27 Jul 2026 14:41:13 -0700 Subject: [PATCH 3/7] Spot Light (#167) --- .../client/registry/LightTypeRegistry.java | 3 + .../render/light/data/SpotLightData.java | 312 ++++++++++++++++++ .../render/light/SpotLightRenderer.java | 70 ++++ .../veil/pinwheel/rendertypes/light/spot.json | 30 ++ .../pinwheel/shaders/program/light/spot.fsh | 78 +++++ .../pinwheel/shaders/program/light/spot.json | 12 + .../pinwheel/shaders/program/light/spot.vsh | 39 +++ 7 files changed, 544 insertions(+) create mode 100644 common/src/main/java/foundry/veil/api/client/render/light/data/SpotLightData.java create mode 100644 common/src/main/java/foundry/veil/impl/client/render/light/SpotLightRenderer.java create mode 100644 common/src/main/resources/assets/veil/pinwheel/rendertypes/light/spot.json create mode 100644 common/src/main/resources/assets/veil/pinwheel/shaders/program/light/spot.fsh create mode 100644 common/src/main/resources/assets/veil/pinwheel/shaders/program/light/spot.json create mode 100644 common/src/main/resources/assets/veil/pinwheel/shaders/program/light/spot.vsh diff --git a/common/src/main/java/foundry/veil/api/client/registry/LightTypeRegistry.java b/common/src/main/java/foundry/veil/api/client/registry/LightTypeRegistry.java index ab640442d..a444d35cb 100644 --- a/common/src/main/java/foundry/veil/api/client/registry/LightTypeRegistry.java +++ b/common/src/main/java/foundry/veil/api/client/registry/LightTypeRegistry.java @@ -2,12 +2,14 @@ import foundry.veil.Veil; import foundry.veil.api.client.render.light.data.AreaLightData; +import foundry.veil.api.client.render.light.data.SpotLightData; import foundry.veil.api.client.render.light.data.DirectionalLightData; import foundry.veil.api.client.render.light.data.LightData; import foundry.veil.api.client.render.light.data.PointLightData; import foundry.veil.api.client.render.light.renderer.LightTypeRenderer; import foundry.veil.impl.client.editor.LightInspector; import foundry.veil.impl.client.render.light.AreaLightRenderer; +import foundry.veil.impl.client.render.light.SpotLightRenderer; import foundry.veil.impl.client.render.light.DirectionalLightRenderer; import foundry.veil.impl.client.render.light.InstancedPointLightRenderer; import foundry.veil.platform.registry.RegistrationProvider; @@ -42,6 +44,7 @@ public final class LightTypeRegistry { return new InstancedPointLightRenderer(); }, (level, camera) -> new PointLightData().setTo(camera).setRadius(15.0F)); public static final Supplier> AREA = register("area", AreaLightRenderer::new, (level, camera) -> new AreaLightData().setDistance(15.0F).setTo(camera)); + public static final Supplier> SPOT = register("spot", SpotLightRenderer::new, (level, camera) -> new SpotLightData().setDistance(15.0F).setTo(camera)); private LightTypeRegistry() { } diff --git a/common/src/main/java/foundry/veil/api/client/render/light/data/SpotLightData.java b/common/src/main/java/foundry/veil/api/client/render/light/data/SpotLightData.java new file mode 100644 index 000000000..14bcc4eeb --- /dev/null +++ b/common/src/main/java/foundry/veil/api/client/render/light/data/SpotLightData.java @@ -0,0 +1,312 @@ +package foundry.veil.api.client.render.light.data; + +import foundry.veil.api.client.color.Colorc; +import foundry.veil.api.client.editor.EditorAttributeProvider; +import foundry.veil.api.client.registry.LightTypeRegistry; +import foundry.veil.api.client.render.CullFrustum; +import foundry.veil.api.client.render.light.DDALightData; +import foundry.veil.api.client.render.light.InstancedLightData; +import imgui.ImGui; +import net.minecraft.client.Camera; +import net.minecraft.util.Mth; +import net.minecraft.world.phys.Vec3; +import org.jetbrains.annotations.ApiStatus; +import org.joml.*; + +import java.lang.Math; +import java.nio.ByteBuffer; + +/** + * Represents a light emitting quad in the world. + * + * @since 2.0.0 + */ +public class SpotLightData extends LightData implements InstancedLightData, DDALightData, EditorAttributeProvider { + + private static final float MAX_ANGLE_SIZE = (float) (65535.0 / 2.0 / Math.PI); + + protected final Vector3d position; + protected final Quaternionf orientation; + private final Matrix4d matrix; + + protected float size; + + protected float angle; + protected float distance; + protected boolean occlusionEnabled; + + public SpotLightData() { + this.matrix = new Matrix4d(); + this.position = new Vector3d(); + this.orientation = new Quaternionf(); + + this.size = (float) Math.toRadians(45); + + this.angle = (float) Math.toRadians(45); + this.distance = 1.0F; + this.occlusionEnabled = false; + } + + /** + * @deprecated No longer used + */ + @ApiStatus.ScheduledForRemoval(inVersion = "5.0.0") + @Deprecated + protected void updateMatrix() { + Quaternionfc orientation = this.getOrientation(); + this.matrix.rotation(orientation).translate(this.position); + } + + @Override + public LightTypeRegistry.LightType getType() { + return LightTypeRegistry.SPOT.get(); + } + + /** + * @return The XYZ position of this light in the world + * @apiNote The return type will change to {@link Vector3dc} in 5.0.0. Use {@link #getPositionMutable()} to mutate this + */ + public Vector3d getPosition() { + return this.position; + } + + /** + * Allows the value to be safely modified. + * + * @return The XYZ position of this light in the world + * @since 4.3.0 + */ + public Vector3d getPositionMutable() { + this.markDirty(); + return this.position; + } + + /** + * @return The current orientation of the light + * @apiNote The return type will change to {@link Quaternionfc} in 5.0.0. Use {@link #getOrientationMutable()} to mutate this + */ + public Quaternionf getOrientation() { + return this.orientation; + } + + /** + * Allows the value to be safely modified. + * + * @return The current orientation of the light + * @since 4.3.0 + */ + public Quaternionf getOrientationMutable() { + return this.orientation; + } + + /** + * @return The size of the light's surface + * @apiNote The return type will change to {@link Vector2fc} in 5.0.0. + */ + public float getSize() { + return this.size; + } + + /** + * @return The maximum angle of the light from the plane's surface. + */ + public float getAngle() { + return this.angle; + } + + /** + * @return The maximum distance the light can travel + */ + public float getDistance() { + return this.distance; + } + + @Override + public boolean isOcclusionEnabled() { + return this.occlusionEnabled; + } + + /** + * Sets the size of the light's surface + * + * @param size The size, in blocks, of the light's surface. + */ + public SpotLightData setSize(float size) { + this.size = size; + this.markDirty(); + return this; + } + + /** + * Sets the maximum angle the light can influence. + * + * @param angle The maximum angle of the light's influence in radians + */ + public SpotLightData setAngle(float angle) { + this.angle = angle; + this.markDirty(); + return this; + } + + /** + * Sets the maximum distance the light can influence. + * + * @param distance The maximum area of influence for the light + */ + public SpotLightData setDistance(float distance) { + this.distance = distance; + this.markDirty(); + return this; + } + + public SpotLightData setOcclusionEnabled(boolean occlusionEnabled) { + this.occlusionEnabled = occlusionEnabled; + this.markDirty(); + return this; + } + + @Override + public SpotLightData setColor(Vector3fc color) { + super.setColor(color); + return this; + } + + @Override + public SpotLightData setColor(Colorc color) { + this.setColor(color.red(), color.green(), color.blue()); + return this; + } + + @Override + public SpotLightData setColor(float red, float green, float blue) { + super.setColor(red, green, blue); + return this; + } + + @Override + public SpotLightData setColor(int color) { + super.setColor(color); + return this; + } + + @Override + public SpotLightData setBrightness(float brightness) { + super.setBrightness(brightness); + return this; + } + + @Override + public void store(ByteBuffer buffer) { + this.matrix.identity().rotation(this.orientation).translate(this.position).getFloats(buffer.position(), buffer); + buffer.position(buffer.position() + Float.BYTES * 16); + + buffer.putFloat(this.color.red() * this.brightness); + buffer.putFloat(this.color.green() * this.brightness); + buffer.putFloat(this.color.blue() * this.brightness); + + buffer.putFloat(this.size); + + buffer.putShort((short) Mth.clamp((int) (this.angle * MAX_ANGLE_SIZE), 0, 65535)); + buffer.putFloat(this.distance); + buffer.putFloat(this.occlusionEnabled ? 1.0F : 0.0F); + } + + @Override + public boolean isVisible(CullFrustum frustum) { + float radius = this.distance; + return frustum.testAab( + this.position.x - radius, + this.position.y - radius, + this.position.z - radius, + this.position.x + radius, + this.position.y + radius, + this.position.z + radius); + } + + @Override + public LightData setTo(Camera camera) { + Vec3 pos = camera.getPosition(); + this.position.set(pos.x, pos.y, pos.z); + this.orientation.identity().lookAlong(camera.getLookVector().mul(-1), camera.getUpVector()); + this.markDirty(); + return this; + } + + @Override + public void renderImGuiAttributes() { + Vector3f orientationAngles = this.orientation.normalize().getEulerAnglesXYZ(new Vector3f()); + + float[] editSize = new float[]{this.size}; + + double[] editX = new double[]{this.position.x()}; + double[] editY = new double[]{this.position.y()}; + double[] editZ = new double[]{this.position.z()}; + + float[] editXRot = new float[]{orientationAngles.x()}; + float[] editYRot = new float[]{orientationAngles.y()}; + float[] editZRot = new float[]{orientationAngles.z()}; + + float[] editAngle = new float[]{this.angle}; + float[] editDistance = new float[]{this.distance}; + + if (ImGui.sliderAngle("size", editSize, 0.1F, 180.0F, "%.1f")) { + this.setSize(editSize[0]); + } + + float totalWidth = ImGui.calcItemWidth(); + ImGui.pushItemWidth(totalWidth / 3.0F - (ImGui.getStyle().getItemInnerSpacingX() * 0.58F)); + if (ImGui.dragScalar("##x", editX, 0.02F)) { + this.position.x = editX[0]; + this.markDirty(); + } + ImGui.sameLine(0, ImGui.getStyle().getItemInnerSpacingX()); + if (ImGui.dragScalar("##y", editY, 0.02F)) { + this.position.y = editY[0]; + this.markDirty(); + } + ImGui.sameLine(0, ImGui.getStyle().getItemInnerSpacingX()); + if (ImGui.dragScalar("##z", editZ, 0.02F)) { + this.position.z = editZ[0]; + this.markDirty(); + } + + ImGui.popItemWidth(); + ImGui.sameLine(0, ImGui.getStyle().getItemInnerSpacingX()); + ImGui.text("position"); + + ImGui.pushItemWidth(totalWidth / 3.0F - (ImGui.getStyle().getItemInnerSpacingX() * 0.58F)); + if (ImGui.sliderAngle("##xrot", editXRot)) { + this.orientation.identity().rotationXYZ(editXRot[0], orientationAngles.y(), orientationAngles.z()); + this.markDirty(); + } + ImGui.sameLine(0, ImGui.getStyle().getItemInnerSpacingX()); + if (ImGui.sliderAngle("##yrot", editYRot)) { + this.orientation.identity().rotationXYZ(orientationAngles.x(), editYRot[0], orientationAngles.z()); + this.markDirty(); + } + ImGui.sameLine(0, ImGui.getStyle().getItemInnerSpacingX()); + if (ImGui.sliderAngle("##zrot", editZRot)) { + this.orientation.identity().rotationXYZ(orientationAngles.x(), orientationAngles.y(), editZRot[0]); + this.markDirty(); + } + + ImGui.popItemWidth(); + ImGui.sameLine(0, ImGui.getStyle().getItemInnerSpacingX()); + ImGui.text("orientation"); + + if (ImGui.sliderAngle("##angle", editAngle, 0.1F, 180.0F, "%.1f")) { + this.setAngle(editAngle[0]); + } + ImGui.sameLine(0, ImGui.getStyle().getItemInnerSpacingX()); + ImGui.text("angle"); + + if (ImGui.dragScalar("distance", editDistance, 0.02F, 0.0F)) { + this.setDistance(editDistance[0]); + } + + if (ImGui.checkbox("Occluded", this.occlusionEnabled)) { + this.occlusionEnabled = !this.occlusionEnabled; + this.markDirty(); + } + } +} diff --git a/common/src/main/java/foundry/veil/impl/client/render/light/SpotLightRenderer.java b/common/src/main/java/foundry/veil/impl/client/render/light/SpotLightRenderer.java new file mode 100644 index 000000000..e984933b6 --- /dev/null +++ b/common/src/main/java/foundry/veil/impl/client/render/light/SpotLightRenderer.java @@ -0,0 +1,70 @@ +package foundry.veil.impl.client.render.light; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.BufferBuilder; +import com.mojang.blaze3d.vertex.DefaultVertexFormat; +import com.mojang.blaze3d.vertex.MeshData; +import com.mojang.blaze3d.vertex.VertexFormat; +import foundry.veil.Veil; +import foundry.veil.api.client.render.light.data.SpotLightData; +import foundry.veil.api.client.render.light.renderer.DDALightRenderer; +import foundry.veil.api.client.render.light.renderer.InstancedLightRenderer; +import foundry.veil.api.client.render.light.renderer.LightRenderHandle; +import foundry.veil.api.client.render.light.renderer.LightTypeRenderer; +import foundry.veil.api.client.render.rendertype.VeilRenderType; +import foundry.veil.api.client.render.vertex.VertexArrayBuilder; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.resources.ResourceLocation; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; +import org.joml.Vector3fc; + +import java.util.List; + +@ApiStatus.Internal +public class SpotLightRenderer extends InstancedLightRenderer implements DDALightRenderer { + + private static final ResourceLocation RENDER_TYPE = Veil.veilPath("light/spot"); + + public SpotLightRenderer() { + super(Float.BYTES * 22 + 2); + } + + @Override + protected MeshData createMesh() { + BufferBuilder builder = RenderSystem.renderThreadTesselator().begin(VertexFormat.Mode.TRIANGLE_STRIP, DefaultVertexFormat.POSITION); + LightTypeRenderer.createInvertedCube(builder); + return builder.buildOrThrow(); + } + + @Override + protected void setupBufferState(VertexArrayBuilder builder) { + builder.setVertexAttribute(1, 2, 4, VertexArrayBuilder.DataType.FLOAT, false, 0); + builder.setVertexAttribute(2, 2, 4, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 4); + builder.setVertexAttribute(3, 2, 4, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 8); + builder.setVertexAttribute(4, 2, 4, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 12); // matrix ! + builder.setVertexAttribute(5, 2, 3, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 16); // color + builder.setVertexAttribute(6, 2, 1, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 19); // size + builder.setVertexAttribute(7, 2, 1, VertexArrayBuilder.DataType.UNSIGNED_SHORT, true, Float.BYTES * 20); // angle + builder.setVertexAttribute(8, 2, 1, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 20 + 2); // distance + builder.setVertexAttribute(9, 2, 1, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 20 + 2 + Float.BYTES); // occluded + } + + @Override + protected @Nullable RenderType getRenderType(List> lights) { + return VeilRenderType.get(RENDER_TYPE); + } + + @Override + public void uploadVoxelGridUniforms(int voxelGridTexture, Vector3fc voxelGridOrigin) { + RenderType renderType = VeilRenderType.get(RENDER_TYPE); + if (renderType == null) { + return; + } + + ResourceLocation veilShaderId = VeilRenderType.getShards(renderType).veilShaderId(); + if (veilShaderId != null) { + DDALightRenderer.uploadVoxelGridUniforms(veilShaderId, voxelGridTexture, voxelGridOrigin); + } + } +} diff --git a/common/src/main/resources/assets/veil/pinwheel/rendertypes/light/spot.json b/common/src/main/resources/assets/veil/pinwheel/rendertypes/light/spot.json new file mode 100644 index 000000000..eb3821167 --- /dev/null +++ b/common/src/main/resources/assets/veil/pinwheel/rendertypes/light/spot.json @@ -0,0 +1,30 @@ +{ + "format": "POSITION_TEX", + "mode": "QUADS", + "bufferSize": "TRANSIENT", + "sort": false, + "affectsCrumbling": false, + "outline": false, + "layers": [ + { + "type": "veil:shader", + "name": "veil:light/spot" + }, + { + "type": "minecraft:write_mask", + "depth": false + }, + { + "type": "minecraft:depth_test", + "mode": "greater" + }, + { + "type": "minecraft:cull", + "face": "back" + }, + { + "type": "minecraft:transparency", + "mode": "additive" + } + ] +} \ No newline at end of file diff --git a/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/spot.fsh b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/spot.fsh new file mode 100644 index 000000000..17c87ca77 --- /dev/null +++ b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/spot.fsh @@ -0,0 +1,78 @@ +#include veil:common +#include veil:space_helper +#include veil:color_utilities +#include veil:light +#include veil:voxel_shadow + +in mat4 lightMat; +in vec3 lightColor; +in float size; +in float maxAngle; +in float maxDistance; +in float occluded; + +uniform sampler2D AlbedoSampler; +uniform sampler2D NormalSampler; +uniform sampler2D DepthSampler; + +uniform vec2 ScreenSize; + +out vec4 fragColor; + +// acos approximation +// faster and also doesn't flicker weirdly +float sacos(float x) +{ + float y = abs(clamp(x, -1.0, 1.0)); + float z = (-0.168577*y + 1.56723) * sqrt(1.0 - y); + return mix(0.5*3.1415927, z, sign(x)); +} + +struct SpotLightResult { vec3 position; float angle; }; +SpotLightResult spotLightPositionAndAngle(vec3 point, mat4 lightMatrix) { + // no idea why i need to do this + lightMatrix[3].xyz *= -1.0; + + vec3 localSpacePoint = (lightMatrix * vec4(point, 1.0)).xyz; + vec3 localDir = normalize(localSpacePoint); + float angle = sacos(localDir.z); + + vec3 worldPos = (inverse(lightMatrix) * vec4(0.0, 0.0, 0.0, 1.0)).xyz; + return SpotLightResult(worldPos, angle); +} + +void main() { + vec2 screenUv = gl_FragCoord.xy / ScreenSize; + + vec4 albedoColor = texture(AlbedoSampler, screenUv); + if (albedoColor.a == 0) { + discard; + } + + vec3 normalVS = texture(NormalSampler, screenUv).xyz; + float depth = texture(DepthSampler, screenUv).r; + vec3 pos = screenToWorldSpace(screenUv, depth).xyz; + + // lighting calculation + SpotLightResult spotLightInfo = spotLightPositionAndAngle(pos, lightMat); + vec3 lightPos = spotLightInfo.position; + + vec3 offset = lightPos - pos; + vec3 lightDirection = normalize((VeilCamera.ViewMat * vec4(offset, 0.0)).xyz); + float diffuse = (dot(normalVS, lightDirection) + 1.0) * 0.5; + diffuse = (diffuse + MINECRAFT_AMBIENT_LIGHT) / (1.0 + MINECRAFT_AMBIENT_LIGHT); + diffuse *= attenuate_no_cusp(length(offset), maxDistance); + + float angleFalloff = smoothstep(size, size - maxAngle, spotLightInfo.angle); + diffuse *= angleFalloff; + + if (occluded > 0.5) { + vec3 normalWS = normalize((VeilCamera.IViewMat * vec4(normalVS, 0.0)).xyz); + diffuse *= voxelshadowVisibility(pos + normalWS * 0.01, lightPos); + } + + float reflectivity = 0.05; + vec3 diffuseColor = diffuse * lightColor; + + fragColor = vec4(albedoColor.rgb * diffuseColor * (1.0 - reflectivity) + diffuseColor * reflectivity, 1.0); +} \ No newline at end of file diff --git a/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/spot.json b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/spot.json new file mode 100644 index 000000000..8a92782f3 --- /dev/null +++ b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/spot.json @@ -0,0 +1,12 @@ +{ + "vertex": "veil:light/spot", + "fragment": "veil:light/spot", + "textures": { + "AlbedoSampler": "veil:dynamic_buffer/albedo", + "NormalSampler": "veil:dynamic_buffer/normal", + "DepthSampler": { + "type": "framebuffer", + "name": "minecraft:main:depth" + } + } +} diff --git a/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/spot.vsh b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/spot.vsh new file mode 100644 index 000000000..8bcae8970 --- /dev/null +++ b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/spot.vsh @@ -0,0 +1,39 @@ +#veil:buffer veil:camera VeilCamera + +layout (location = 0) in vec3 Position; +layout (location = 1) in mat4 LightMatrix; +layout (location = 5) in vec3 Color; +layout (location = 6) in float Size; +layout (location = 7) in float NormalizedAngle; +layout (location = 8) in float Distance; +layout (location = 9) in float Occluded; + +out mat4 lightMat; +out vec3 lightColor; +out float size; +out float maxAngle; +out float maxDistance; +out float occluded; + +void main() { + vec3 vertexPos = Position; + vertexPos.z = clamp(vertexPos.z, min(cos(Size), 0), 1); + float angleTerm = sin(min(Size, 1.57079633)) * Distance; + vertexPos *= vec3(angleTerm, angleTerm, Distance); + + // awful fix but not sure why just multiplying the matrix doesnt work? it does what it should in + // all the second calculations. really weird! + vec3 lightPos = LightMatrix[3].xyz; + mat3 rotationMatrix = mat3(LightMatrix); + lightPos = inverse(rotationMatrix) * lightPos; + vertexPos = inverse(rotationMatrix) * vertexPos; + vertexPos += lightPos; + gl_Position = VeilCamera.ProjMat * VeilCamera.ViewMat * vec4(vertexPos - VeilCamera.CameraPosition, 1.0); + + lightMat = LightMatrix; + lightColor = Color; + size = Size; + maxAngle = NormalizedAngle * 6.28318530718; + maxDistance = Distance; + occluded = Occluded; +} \ No newline at end of file From 69822bfb543f5ec69e26cce4ba904671a2b209bf Mon Sep 17 00:00:00 2001 From: Ocelot Date: Mon, 27 Jul 2026 15:47:17 -0600 Subject: [PATCH 4/7] Add @since tags --- .../client/registry/LightTypeRegistry.java | 3 +++ .../render/framebuffer/VeilFramebuffers.java | 3 +++ .../render/light/LightGuideProvider.java | 2 +- .../render/light/data/AreaLightData.java | 5 ++++ .../light/data/DirectionalLightData.java | 3 +++ .../render/light/data/PointLightData.java | 12 ++++++---- .../render/light/data/SpotLightData.java | 23 ++++--------------- .../renderer/InscatteringLightRenderer.java | 2 +- 8 files changed, 28 insertions(+), 25 deletions(-) diff --git a/common/src/main/java/foundry/veil/api/client/registry/LightTypeRegistry.java b/common/src/main/java/foundry/veil/api/client/registry/LightTypeRegistry.java index a444d35cb..f3a02e5f8 100644 --- a/common/src/main/java/foundry/veil/api/client/registry/LightTypeRegistry.java +++ b/common/src/main/java/foundry/veil/api/client/registry/LightTypeRegistry.java @@ -44,6 +44,9 @@ public final class LightTypeRegistry { return new InstancedPointLightRenderer(); }, (level, camera) -> new PointLightData().setTo(camera).setRadius(15.0F)); public static final Supplier> AREA = register("area", AreaLightRenderer::new, (level, camera) -> new AreaLightData().setDistance(15.0F).setTo(camera)); + /** + * @since 4.4.0 + */ public static final Supplier> SPOT = register("spot", SpotLightRenderer::new, (level, camera) -> new SpotLightData().setDistance(15.0F).setTo(camera)); private LightTypeRegistry() { diff --git a/common/src/main/java/foundry/veil/api/client/render/framebuffer/VeilFramebuffers.java b/common/src/main/java/foundry/veil/api/client/render/framebuffer/VeilFramebuffers.java index ed25f8f26..44797f1cc 100644 --- a/common/src/main/java/foundry/veil/api/client/render/framebuffer/VeilFramebuffers.java +++ b/common/src/main/java/foundry/veil/api/client/render/framebuffer/VeilFramebuffers.java @@ -17,6 +17,9 @@ private VeilFramebuffers() { public static final ResourceLocation FIRST_PERSON = buffer("first_person"); public static final ResourceLocation BLOOM = buffer("bloom"); public static final ResourceLocation LIGHT = buffer("light"); + /** + * @since 4.4.0 + */ public static final ResourceLocation LIGHT_INSCATTERING = buffer("light_inscattering"); public static final ResourceLocation POST = buffer("post"); diff --git a/common/src/main/java/foundry/veil/api/client/render/light/LightGuideProvider.java b/common/src/main/java/foundry/veil/api/client/render/light/LightGuideProvider.java index 455858658..22424cf38 100644 --- a/common/src/main/java/foundry/veil/api/client/render/light/LightGuideProvider.java +++ b/common/src/main/java/foundry/veil/api/client/render/light/LightGuideProvider.java @@ -7,6 +7,7 @@ * Provides extra information for given light data by rendering guides into the world. * * @author Neddslayer + * @since 4.4.0 */ public interface LightGuideProvider { @@ -14,5 +15,4 @@ public interface LightGuideProvider { * Render the light guides using the VertexConsumer. */ void renderLightGuide(MatrixStack stack, VertexConsumer consumer); - } diff --git a/common/src/main/java/foundry/veil/api/client/render/light/data/AreaLightData.java b/common/src/main/java/foundry/veil/api/client/render/light/data/AreaLightData.java index 1bece0ab0..63450e52d 100644 --- a/common/src/main/java/foundry/veil/api/client/render/light/data/AreaLightData.java +++ b/common/src/main/java/foundry/veil/api/client/render/light/data/AreaLightData.java @@ -101,6 +101,7 @@ public Quaternionf getOrientation() { * @since 4.3.0 */ public Quaternionf getOrientationMutable() { + this.markDirty(); return this.orientation; } @@ -143,6 +144,7 @@ public boolean isOcclusionEnabled() { /** * @return The strength of the light's in-scattering effect. + * @since 4.4.0 */ public float getInscatteringStrength() { return this.inscattering; @@ -188,6 +190,9 @@ public AreaLightData setOcclusionEnabled(boolean occlusionEnabled) { return this; } + /** + * @since 4.4.0 + */ public AreaLightData setInscatteringStrength(float inscattering) { this.inscattering = inscattering; this.markDirty(); diff --git a/common/src/main/java/foundry/veil/api/client/render/light/data/DirectionalLightData.java b/common/src/main/java/foundry/veil/api/client/render/light/data/DirectionalLightData.java index b92e5f821..13a43cc7d 100644 --- a/common/src/main/java/foundry/veil/api/client/render/light/data/DirectionalLightData.java +++ b/common/src/main/java/foundry/veil/api/client/render/light/data/DirectionalLightData.java @@ -95,6 +95,9 @@ public DirectionalLightData setBrightness(float brightness) { return this; } + /** + * @since 4.4.0 + */ public DirectionalLightData setOcclusionEnabled(boolean occlusionEnabled) { this.occlusionEnabled = occlusionEnabled; this.markDirty(); diff --git a/common/src/main/java/foundry/veil/api/client/render/light/data/PointLightData.java b/common/src/main/java/foundry/veil/api/client/render/light/data/PointLightData.java index 9123d56c3..15f7a8156 100644 --- a/common/src/main/java/foundry/veil/api/client/render/light/data/PointLightData.java +++ b/common/src/main/java/foundry/veil/api/client/render/light/data/PointLightData.java @@ -67,6 +67,7 @@ public boolean isOcclusionEnabled() { /** * @return The strength of the light's in-scattering effect. + * @since 4.4.0 */ public float getInscatteringStrength() { return this.inscattering; @@ -96,6 +97,9 @@ public PointLightData setOcclusionEnabled(boolean occlusionEnabled) { return this; } + /** + * @since 4.4.0 + */ public PointLightData setInscatteringStrength(float inscattering) { this.inscattering = inscattering; this.markDirty(); @@ -215,19 +219,19 @@ public void renderLightGuide(MatrixStack stack, VertexConsumer consumer) { Matrix4f pose = stack.position(); for (int i = -8; i < 25; i++) { - consumer.addVertex(pose, (float)Math.sin(i / 32.0f * Mth.TWO_PI) * this.radius, 0, (float)Math.cos(i / 32.0f * Mth.TWO_PI) * this.radius).setColor(this.color.red(), this.color.green(), this.color.blue(), this.color.alpha()); + consumer.addVertex(pose, (float) Math.sin(i / 32.0f * Mth.TWO_PI) * this.radius, 0, (float) Math.cos(i / 32.0f * Mth.TWO_PI) * this.radius).setColor(this.color.red(), this.color.green(), this.color.blue(), this.color.alpha()); } for (int i = -8; i < 25; i++) { - consumer.addVertex(pose, (float)Math.sin(i / 32.0f * Mth.TWO_PI) * this.radius, (float)Math.cos(i / 32.0f * Mth.TWO_PI) * this.radius, 0).setColor(this.color.red(), this.color.green(), this.color.blue(), this.color.alpha()); + consumer.addVertex(pose, (float) Math.sin(i / 32.0f * Mth.TWO_PI) * this.radius, (float) Math.cos(i / 32.0f * Mth.TWO_PI) * this.radius, 0).setColor(this.color.red(), this.color.green(), this.color.blue(), this.color.alpha()); } for (int i = 25; i >= 16; i--) { - consumer.addVertex(pose, (float)Math.sin(i / 32.0f * Mth.TWO_PI) * this.radius, (float)Math.cos(i / 32.0f * Mth.TWO_PI) * this.radius, 0).setColor(this.color.red(), this.color.green(), this.color.blue(), this.color.alpha()); + consumer.addVertex(pose, (float) Math.sin(i / 32.0f * Mth.TWO_PI) * this.radius, (float) Math.cos(i / 32.0f * Mth.TWO_PI) * this.radius, 0).setColor(this.color.red(), this.color.green(), this.color.blue(), this.color.alpha()); } for (int i = -8; i < 25; i++) { - consumer.addVertex(pose, 0, (float)Math.sin(i / 32.0f * Mth.TWO_PI) * this.radius, (float)Math.cos(i / 32.0f * Mth.TWO_PI) * this.radius).setColor(this.color.red(), this.color.green(), this.color.blue(), this.color.alpha()); + consumer.addVertex(pose, 0, (float) Math.sin(i / 32.0f * Mth.TWO_PI) * this.radius, (float) Math.cos(i / 32.0f * Mth.TWO_PI) * this.radius).setColor(this.color.red(), this.color.green(), this.color.blue(), this.color.alpha()); } stack.matrixPop(); diff --git a/common/src/main/java/foundry/veil/api/client/render/light/data/SpotLightData.java b/common/src/main/java/foundry/veil/api/client/render/light/data/SpotLightData.java index 14bcc4eeb..9490da27c 100644 --- a/common/src/main/java/foundry/veil/api/client/render/light/data/SpotLightData.java +++ b/common/src/main/java/foundry/veil/api/client/render/light/data/SpotLightData.java @@ -10,7 +10,6 @@ import net.minecraft.client.Camera; import net.minecraft.util.Mth; import net.minecraft.world.phys.Vec3; -import org.jetbrains.annotations.ApiStatus; import org.joml.*; import java.lang.Math; @@ -19,7 +18,7 @@ /** * Represents a light emitting quad in the world. * - * @since 2.0.0 + * @since 4.4.0 */ public class SpotLightData extends LightData implements InstancedLightData, DDALightData, EditorAttributeProvider { @@ -47,16 +46,6 @@ public SpotLightData() { this.occlusionEnabled = false; } - /** - * @deprecated No longer used - */ - @ApiStatus.ScheduledForRemoval(inVersion = "5.0.0") - @Deprecated - protected void updateMatrix() { - Quaternionfc orientation = this.getOrientation(); - this.matrix.rotation(orientation).translate(this.position); - } - @Override public LightTypeRegistry.LightType getType() { return LightTypeRegistry.SPOT.get(); @@ -64,9 +53,8 @@ public LightTypeRegistry.LightType getType() { /** * @return The XYZ position of this light in the world - * @apiNote The return type will change to {@link Vector3dc} in 5.0.0. Use {@link #getPositionMutable()} to mutate this */ - public Vector3d getPosition() { + public Vector3dc getPosition() { return this.position; } @@ -74,7 +62,6 @@ public Vector3d getPosition() { * Allows the value to be safely modified. * * @return The XYZ position of this light in the world - * @since 4.3.0 */ public Vector3d getPositionMutable() { this.markDirty(); @@ -83,9 +70,8 @@ public Vector3d getPositionMutable() { /** * @return The current orientation of the light - * @apiNote The return type will change to {@link Quaternionfc} in 5.0.0. Use {@link #getOrientationMutable()} to mutate this */ - public Quaternionf getOrientation() { + public Quaternionfc getOrientation() { return this.orientation; } @@ -93,15 +79,14 @@ public Quaternionf getOrientation() { * Allows the value to be safely modified. * * @return The current orientation of the light - * @since 4.3.0 */ public Quaternionf getOrientationMutable() { + this.markDirty(); return this.orientation; } /** * @return The size of the light's surface - * @apiNote The return type will change to {@link Vector2fc} in 5.0.0. */ public float getSize() { return this.size; diff --git a/common/src/main/java/foundry/veil/api/client/render/light/renderer/InscatteringLightRenderer.java b/common/src/main/java/foundry/veil/api/client/render/light/renderer/InscatteringLightRenderer.java index ecdec3077..313e922a8 100644 --- a/common/src/main/java/foundry/veil/api/client/render/light/renderer/InscatteringLightRenderer.java +++ b/common/src/main/java/foundry/veil/api/client/render/light/renderer/InscatteringLightRenderer.java @@ -6,6 +6,7 @@ * Renders in-scattering for deferred lights. * * @author Neddslayer + * @since 4.4.0 */ public interface InscatteringLightRenderer extends LightTypeRenderer { @@ -18,5 +19,4 @@ public interface InscatteringLightRenderer extends LightTyp * @param lightRenderer The light renderer instance */ void renderLightInscattering(LightRenderer lightRenderer); - } From ef71a31089c006ca9300eea49ff6ccf60d7dbc11 Mon Sep 17 00:00:00 2001 From: Ocelot Date: Mon, 27 Jul 2026 18:00:37 -0600 Subject: [PATCH 5/7] Remove duplicate shaders --- .../renderer/InstancedLightRenderer.java | 9 +- .../shader/program/ProgramDefinition.java | 5 +- .../render/light/AreaLightRenderer.java | 19 ++- .../light/InstancedPointLightRenderer.java | 9 +- .../render/light/SpotLightRenderer.java | 24 ++-- .../veil/pinwheel/post/core/composite.json | 74 +++++++++++- .../shaders/include/inscattering.glsl | 11 ++ .../shaders/program/core/composite.fsh | 28 +---- .../pinwheel/shaders/program/light/area.fsh | 98 ++++++++++++++++ .../pinwheel/shaders/program/light/area.vsh | 35 +++++- .../program/light/inscattering/area.fsh | 111 ------------------ .../program/light/inscattering/area.json | 7 +- .../program/light/inscattering/point.fsh | 75 ------------ .../program/light/inscattering/point.json | 7 +- .../program/light/inscattering/spot.json | 14 +++ .../pinwheel/shaders/program/light/point.fsh | 52 ++++++++ .../pinwheel/shaders/program/light/point.vsh | 12 +- .../pinwheel/shaders/program/light/spot.fsh | 78 ------------ .../pinwheel/shaders/program/light/spot.json | 9 +- .../pinwheel/shaders/program/light/spot.vsh | 39 ------ gradle.properties | 2 +- 21 files changed, 341 insertions(+), 377 deletions(-) create mode 100644 common/src/main/resources/assets/veil/pinwheel/shaders/include/inscattering.glsl delete mode 100644 common/src/main/resources/assets/veil/pinwheel/shaders/program/light/inscattering/area.fsh delete mode 100644 common/src/main/resources/assets/veil/pinwheel/shaders/program/light/inscattering/point.fsh create mode 100644 common/src/main/resources/assets/veil/pinwheel/shaders/program/light/inscattering/spot.json delete mode 100644 common/src/main/resources/assets/veil/pinwheel/shaders/program/light/spot.fsh delete mode 100644 common/src/main/resources/assets/veil/pinwheel/shaders/program/light/spot.vsh diff --git a/common/src/main/java/foundry/veil/api/client/render/light/renderer/InstancedLightRenderer.java b/common/src/main/java/foundry/veil/api/client/render/light/renderer/InstancedLightRenderer.java index 41f1e0e30..74ac62cbf 100644 --- a/common/src/main/java/foundry/veil/api/client/render/light/renderer/InstancedLightRenderer.java +++ b/common/src/main/java/foundry/veil/api/client/render/light/renderer/InstancedLightRenderer.java @@ -179,7 +179,7 @@ public void prepareLights(LightRenderer lightRenderer, CullFrustum frustum) { } } - private void bindRenderQuad() { + private void updateInstancedData() { RenderSystem.glBindBuffer(GL_ARRAY_BUFFER, this.instancedVbo); boolean resized = false; @@ -217,7 +217,9 @@ public void renderLights(LightRenderer lightRenderer) { return; } - this.bindRenderQuad(); + RenderSystem.glBindBuffer(GL_ARRAY_BUFFER, this.instancedVbo); + this.updateInstancedData(); + this.vertexArray.bind(); this.vertexArray.drawInstancedWithRenderType(renderType, this.visibleLights.size()); // Render light guides @@ -266,7 +268,8 @@ public void renderLightInscattering(LightRenderer lightRenderer) { return; } - this.bindRenderQuad(); + RenderSystem.glBindBuffer(GL_ARRAY_BUFFER, this.instancedVbo); + this.vertexArray.bind(); this.vertexArray.drawInstancedWithRenderType(renderType, this.visibleLights.size()); } diff --git a/common/src/main/java/foundry/veil/api/client/render/shader/program/ProgramDefinition.java b/common/src/main/java/foundry/veil/api/client/render/shader/program/ProgramDefinition.java index 1a88eb11c..bf90fa0a9 100644 --- a/common/src/main/java/foundry/veil/api/client/render/shader/program/ProgramDefinition.java +++ b/common/src/main/java/foundry/veil/api/client/render/shader/program/ProgramDefinition.java @@ -63,10 +63,7 @@ public Map getMacros(Set dependencies, ShaderPreDefiniti if (definition != null) { macros.put(name.toUpperCase(Locale.ROOT), definition); } else { - String definitionDefault = this.definitionDefaults.get(name); - if (definitionDefault != null) { - macros.put(name.toUpperCase(Locale.ROOT), definitionDefault); - } + macros.put(name.toUpperCase(Locale.ROOT), this.definitionDefaults.getOrDefault(name, "1")); } dependencies.add(name); diff --git a/common/src/main/java/foundry/veil/impl/client/render/light/AreaLightRenderer.java b/common/src/main/java/foundry/veil/impl/client/render/light/AreaLightRenderer.java index cc9e22638..0b1b24b3d 100644 --- a/common/src/main/java/foundry/veil/impl/client/render/light/AreaLightRenderer.java +++ b/common/src/main/java/foundry/veil/impl/client/render/light/AreaLightRenderer.java @@ -12,6 +12,7 @@ import foundry.veil.api.client.render.light.renderer.LightRenderHandle; import foundry.veil.api.client.render.light.renderer.LightTypeRenderer; import foundry.veil.api.client.render.rendertype.VeilRenderType; +import foundry.veil.api.client.render.vertex.VertexArray; import foundry.veil.api.client.render.vertex.VertexArrayBuilder; import net.minecraft.client.renderer.RenderType; import net.minecraft.resources.ResourceLocation; @@ -40,16 +41,14 @@ protected MeshData createMesh() { @Override protected void setupBufferState(VertexArrayBuilder builder) { - builder.setVertexAttribute(1, 2, 4, VertexArrayBuilder.DataType.FLOAT, false, 0); - builder.setVertexAttribute(2, 2, 4, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 4); - builder.setVertexAttribute(3, 2, 4, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 8); - builder.setVertexAttribute(4, 2, 4, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 12); // matrix ! - builder.setVertexAttribute(5, 2, 3, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 16); // color - builder.setVertexAttribute(6, 2, 2, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 19); // size - builder.setVertexAttribute(7, 2, 1, VertexArrayBuilder.DataType.UNSIGNED_SHORT, true, Float.BYTES * 21); // angle - builder.setVertexAttribute(8, 2, 1, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 21 + 2); // distance - builder.setVertexAttribute(9, 2, 1, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 21 + 2 + Float.BYTES); // Occlusion - builder.setVertexAttribute(10, 2, 1, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 21 + 2 + Float.BYTES * 2); // In-scattering + builder.setVertexAttribute(1, VertexArray.INSTANCE_BUFFER, 4, VertexArrayBuilder.DataType.FLOAT, false, 0); + builder.setVertexAttribute(2, VertexArray.INSTANCE_BUFFER, 4, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 4); + builder.setVertexAttribute(3, VertexArray.INSTANCE_BUFFER, 4, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 8); + builder.setVertexAttribute(4, VertexArray.INSTANCE_BUFFER, 4, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 12); // matrix ! + builder.setVertexAttribute(5, VertexArray.INSTANCE_BUFFER, 3, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 16); // color + builder.setVertexAttribute(6, VertexArray.INSTANCE_BUFFER, 2, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 19); // size + builder.setVertexAttribute(7, VertexArray.INSTANCE_BUFFER, 1, VertexArrayBuilder.DataType.UNSIGNED_SHORT, true, Float.BYTES * 21); // angle + builder.setVertexAttribute(8, VertexArray.INSTANCE_BUFFER, 3, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 21 + 2); // distance/occlusion/in-scatting } @Override diff --git a/common/src/main/java/foundry/veil/impl/client/render/light/InstancedPointLightRenderer.java b/common/src/main/java/foundry/veil/impl/client/render/light/InstancedPointLightRenderer.java index 5a78721db..ef61bb37f 100644 --- a/common/src/main/java/foundry/veil/impl/client/render/light/InstancedPointLightRenderer.java +++ b/common/src/main/java/foundry/veil/impl/client/render/light/InstancedPointLightRenderer.java @@ -9,6 +9,7 @@ import foundry.veil.api.client.render.light.data.PointLightData; import foundry.veil.api.client.render.light.renderer.*; import foundry.veil.api.client.render.rendertype.VeilRenderType; +import foundry.veil.api.client.render.vertex.VertexArray; import foundry.veil.api.client.render.vertex.VertexArrayBuilder; import net.minecraft.client.renderer.RenderType; import net.minecraft.resources.ResourceLocation; @@ -37,11 +38,9 @@ protected MeshData createMesh() { @Override protected void setupBufferState(VertexArrayBuilder builder) { - builder.setVertexAttribute(1, 2, 3, VertexArrayBuilder.DataType.FLOAT, false, 0); // LightPosition - builder.setVertexAttribute(2, 2, 3, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 3); // Color - builder.setVertexAttribute(3, 2, 1, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 6); // Distance - builder.setVertexAttribute(4, 2, 1, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 7); // Occluded - builder.setVertexAttribute(5, 2, 1, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 8); // In-scattering + builder.setVertexAttribute(1, VertexArray.INSTANCE_BUFFER, 3, VertexArrayBuilder.DataType.FLOAT, false, 0); // LightPosition + builder.setVertexAttribute(2, VertexArray.INSTANCE_BUFFER, 3, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 3); // Color + builder.setVertexAttribute(3, VertexArray.INSTANCE_BUFFER, 3, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 6); // distance/occlusion/in-scatting } @Override diff --git a/common/src/main/java/foundry/veil/impl/client/render/light/SpotLightRenderer.java b/common/src/main/java/foundry/veil/impl/client/render/light/SpotLightRenderer.java index e984933b6..f87f7fe1b 100644 --- a/common/src/main/java/foundry/veil/impl/client/render/light/SpotLightRenderer.java +++ b/common/src/main/java/foundry/veil/impl/client/render/light/SpotLightRenderer.java @@ -12,6 +12,7 @@ import foundry.veil.api.client.render.light.renderer.LightRenderHandle; import foundry.veil.api.client.render.light.renderer.LightTypeRenderer; import foundry.veil.api.client.render.rendertype.VeilRenderType; +import foundry.veil.api.client.render.vertex.VertexArray; import foundry.veil.api.client.render.vertex.VertexArrayBuilder; import net.minecraft.client.renderer.RenderType; import net.minecraft.resources.ResourceLocation; @@ -25,6 +26,7 @@ public class SpotLightRenderer extends InstancedLightRenderer implements DDALightRenderer { private static final ResourceLocation RENDER_TYPE = Veil.veilPath("light/spot"); + private static final ResourceLocation INSCATTERING_RENDER_TYPE = Veil.veilPath("light/inscattering/spot"); public SpotLightRenderer() { super(Float.BYTES * 22 + 2); @@ -39,15 +41,14 @@ protected MeshData createMesh() { @Override protected void setupBufferState(VertexArrayBuilder builder) { - builder.setVertexAttribute(1, 2, 4, VertexArrayBuilder.DataType.FLOAT, false, 0); - builder.setVertexAttribute(2, 2, 4, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 4); - builder.setVertexAttribute(3, 2, 4, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 8); - builder.setVertexAttribute(4, 2, 4, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 12); // matrix ! - builder.setVertexAttribute(5, 2, 3, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 16); // color - builder.setVertexAttribute(6, 2, 1, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 19); // size - builder.setVertexAttribute(7, 2, 1, VertexArrayBuilder.DataType.UNSIGNED_SHORT, true, Float.BYTES * 20); // angle - builder.setVertexAttribute(8, 2, 1, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 20 + 2); // distance - builder.setVertexAttribute(9, 2, 1, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 20 + 2 + Float.BYTES); // occluded + builder.setVertexAttribute(1, VertexArray.INSTANCE_BUFFER, 4, VertexArrayBuilder.DataType.FLOAT, false, 0); + builder.setVertexAttribute(2, VertexArray.INSTANCE_BUFFER, 4, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 4); + builder.setVertexAttribute(3, VertexArray.INSTANCE_BUFFER, 4, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 8); + builder.setVertexAttribute(4, VertexArray.INSTANCE_BUFFER, 4, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 12); // matrix ! + builder.setVertexAttribute(5, VertexArray.INSTANCE_BUFFER, 3, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 16); // color + builder.setVertexAttribute(6, VertexArray.INSTANCE_BUFFER, 1, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 19); // size + builder.setVertexAttribute(7, VertexArray.INSTANCE_BUFFER, 1, VertexArrayBuilder.DataType.UNSIGNED_SHORT, true, Float.BYTES * 20); // angle + builder.setVertexAttribute(8, VertexArray.INSTANCE_BUFFER, 3, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 21 + 2); // distance/occlusion/in-scatting } @Override @@ -55,6 +56,11 @@ protected void setupBufferState(VertexArrayBuilder builder) { return VeilRenderType.get(RENDER_TYPE); } + @Override + protected @Nullable RenderType getInscatteringRenderType(List> lights) { + return VeilRenderType.get(INSCATTERING_RENDER_TYPE); + } + @Override public void uploadVoxelGridUniforms(int voxelGridTexture, Vector3fc voxelGridOrigin) { RenderType renderType = VeilRenderType.get(RENDER_TYPE); diff --git a/common/src/main/resources/assets/veil/pinwheel/post/core/composite.json b/common/src/main/resources/assets/veil/pinwheel/post/core/composite.json index 53d3d694a..4844f87a2 100644 --- a/common/src/main/resources/assets/veil/pinwheel/post/core/composite.json +++ b/common/src/main/resources/assets/veil/pinwheel/post/core/composite.json @@ -1,9 +1,81 @@ { "stages": [ + { + "type": "veil:blit", + "shader": "veil:core/blur", + "in": "veil:light_inscattering", + "out": "layer1", + "clear": false + }, + { + "type": "veil:blit", + "shader": "veil:core/unblur", + "in": "layer1", + "out": "layer2", + "clear": false + }, + { + "type": "veil:blit", + "shader": "veil:core/unblur", + "in": "layer2", + "out": "layer3", + "clear": false + }, + { + "type": "veil:blit", + "shader": "veil:core/unblur", + "in": "layer3", + "out": "layer4", + "clear": false + }, { "type": "veil:blit", "shader": "veil:core/composite", "in": "minecraft:main" } - ] + ], + "framebuffers": { + "layer1": { + "width": "q.screen_width / 8.0", + "height": "q.screen_height / 8.0", + "depth": false, + "filter": { + "blur": true + }, + "format": "RGB16F" + }, + "layer2": { + "width": "q.screen_width / 4.0", + "height": "q.screen_height / 4.0", + "depth": false, + "filter": { + "blur": true + }, + "format": "RGB16F" + }, + "layer3": { + "width": "q.screen_width / 2.0", + "height": "q.screen_height / 2.0", + "depth": false, + "filter": { + "blur": true + }, + "format": "RGB16F" + }, + "layer4": { + "width": "q.screen_width", + "height": "q.screen_height", + "depth": false, + "filter": { + "blur": true + }, + "format": "RGB16F" + } + }, + "textures": { + "UpscaledLightInscatteringSampler": { + "type": "framebuffer", + "name": "layer4" + } + } } \ No newline at end of file diff --git a/common/src/main/resources/assets/veil/pinwheel/shaders/include/inscattering.glsl b/common/src/main/resources/assets/veil/pinwheel/shaders/include/inscattering.glsl new file mode 100644 index 000000000..a78fedbe2 --- /dev/null +++ b/common/src/main/resources/assets/veil/pinwheel/shaders/include/inscattering.glsl @@ -0,0 +1,11 @@ +//Fog density +#define DENSITY 2.6 +//Surface pass rate +#define PASSTHROUGH 0.5 + +#define STEPS 50.0 + +float sdSphere(vec3 p, float r) +{ + return (length(p) - r) / DENSITY + PASSTHROUGH; +} diff --git a/common/src/main/resources/assets/veil/pinwheel/shaders/program/core/composite.fsh b/common/src/main/resources/assets/veil/pinwheel/shaders/program/core/composite.fsh index 250d0c347..a9726f865 100644 --- a/common/src/main/resources/assets/veil/pinwheel/shaders/program/core/composite.fsh +++ b/common/src/main/resources/assets/veil/pinwheel/shaders/program/core/composite.fsh @@ -1,40 +1,16 @@ uniform sampler2D DiffuseSampler0; uniform sampler2D DiffuseDepthSampler; uniform sampler2D LightSampler; -uniform sampler2D LightInscatteringSampler; - -uniform vec4 ColorModulator; -uniform vec2 ScreenSize; +uniform sampler2D UpscaledLightInscatteringSampler; in vec2 texCoord; out vec4 fragColor; -#define BOXRADIUS 3 - -vec3 boxBlur(vec2 uv, vec2 size) -{ - int kernel_window_size = BOXRADIUS * 2 + 1; - int samples = kernel_window_size * kernel_window_size; - - vec3 color = vec3(0); - - float wsum = 0.0; - for (int ry = -BOXRADIUS; ry <= BOXRADIUS; ++ry) - for (int rx = -BOXRADIUS; rx <= BOXRADIUS; ++rx) - { - float w = 1.0; - wsum += w; - color += texture(LightInscatteringSampler, uv + vec2(rx, ry) / size).rgb * w; - } - - return color/wsum; -} - void main() { vec4 main = texture(DiffuseSampler0, texCoord); float mainDepth = texture(DiffuseDepthSampler, texCoord).r; - vec3 light = texture(LightSampler, texCoord).rgb + boxBlur(texCoord, ScreenSize / 4.0).rgb; + vec3 light = texture(LightSampler, texCoord).rgb + texture(UpscaledLightInscatteringSampler, texCoord).rgb; fragColor = vec4(main.rgb + light, main.a); gl_FragDepth = mainDepth; } diff --git a/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/area.fsh b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/area.fsh index fa9b7fbae..9d7bca07b 100644 --- a/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/area.fsh +++ b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/area.fsh @@ -3,18 +3,27 @@ #include veil:color_utilities #include veil:light #include veil:voxel_shadow +#ifdef INSCATTERING +#include veil:inscattering +#endif in mat4 lightMat; in mat4 invLightMat; in vec3 lightColor; +#ifdef SPOTLIGHT +in float size; +#else in vec2 size; +#endif in float maxAngle; in float maxDistance; in float occluded; in float inscattering; +#ifndef INSCATTERING uniform sampler2D AlbedoSampler; uniform sampler2D NormalSampler; +#endif uniform sampler2D DepthSampler; uniform vec2 ScreenSize; @@ -48,6 +57,83 @@ AreaLightResult closestPointOnPlaneAndAngle(vec3 point, mat4 planeMatrix, mat4 i return AreaLightResult((invPlaneMatrix * vec4(localSpacePointOnPlane, 1.0)).xyz, angle); } +struct SpotLightResult { vec3 position; float angle; }; +SpotLightResult spotLightPositionAndAngle(vec3 point, mat4 lightMatrix) { + // no idea why i need to do this + lightMatrix[3].xyz *= -1.0; + + vec3 localSpacePoint = (lightMatrix * vec4(point, 1.0)).xyz; + vec3 localDir = normalize(localSpacePoint); + float angle = sacos(localDir.z); + + vec3 worldPos = (inverse(lightMatrix) * vec4(0.0, 0.0, 0.0, 1.0)).xyz; + return SpotLightResult(worldPos, angle); +} + +#ifdef INSCATTERING +vec3 ray(vec3 dir, vec3 pos, vec3 fragPos) { + //Output brightness + #define BRIGHTNESS 0.004 + + //Accumulative color + vec3 col = vec3(0.0); + float d = 0; + float fragDistance = distance(pos, fragPos); + + //Glow raymarch loop + for(float i = 0.0; i 0.0) { + float depth = texture(DepthSampler, screenUv).r; + volume = ray(viewDirFromUv(screenUv), VeilCamera.CameraPosition + VeilCamera.CameraBobOffset, screenToWorldSpace(screenUv, depth).xyz); + } + + fragColor = vec4(volume, 1.0); +} +#else void main() { vec2 screenUv = gl_FragCoord.xy / ScreenSize; @@ -60,19 +146,30 @@ void main() { vec3 pos = screenToWorldSpace(screenUv, depth).xyz; // lighting calculation + #ifdef SPOTLIGHT + SpotLightResult spotLightInfo = spotLightPositionAndAngle(pos, lightMat); + vec3 lightPos = spotLightInfo.position; + #else AreaLightResult areaLightInfo = closestPointOnPlaneAndAngle(pos, lightMat, invLightMat, size); vec3 lightPos = areaLightInfo.position; float angle = areaLightInfo.angle; + #endif vec3 offset = lightPos - pos; vec3 lightDirection = normalize((VeilCamera.ViewMat * vec4(offset, 0.0)).xyz); float diffuse = (dot(normalVS, lightDirection) + 1.0) * 0.5; diffuse = (diffuse + MINECRAFT_AMBIENT_LIGHT) / (1.0 + MINECRAFT_AMBIENT_LIGHT); diffuse *= attenuate_no_cusp(length(offset), maxDistance); + // angle falloff + #ifdef SPOTLIGHT + float angleFalloff = smoothstep(size, size - maxAngle, spotLightInfo.angle); + #else float angleFalloff = clamp(angle, 0.0, maxAngle) / maxAngle; angleFalloff = smoothstep(1.0, 0.0, angleFalloff); + #endif diffuse *= angleFalloff; + if (occluded > 0.5) { vec3 normalWS = normalize((VeilCamera.IViewMat * vec4(normalVS, 0.0)).xyz); diffuse *= voxelshadowVisibility(pos + normalWS * 0.01, lightPos); @@ -83,3 +180,4 @@ void main() { fragColor = vec4(albedoColor.rgb * diffuseColor * (1.0 - reflectivity) + diffuseColor * reflectivity, 1.0); } +#endif diff --git a/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/area.vsh b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/area.vsh index 8f4cddcca..369fd897d 100644 --- a/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/area.vsh +++ b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/area.vsh @@ -3,27 +3,50 @@ layout (location = 0) in vec3 Position; layout (location = 1) in mat4 LightMatrix; layout (location = 5) in vec3 Color; +#ifdef SPOTLIGHT +layout (location = 6) in float Size; +#else layout (location = 6) in vec2 Size; +#endif layout (location = 7) in float NormalizedAngle; -layout (location = 8) in float Distance; -layout (location = 9) in float Occluded; -layout (location = 10) in float Inscattering; +layout (location = 8) in vec3 Settings; + +#define Distance Settings.x +#define Occluded Settings.y +#define Inscattering Settings.z out mat4 lightMat; out mat4 invLightMat; out vec3 lightColor; +#ifdef SPOTLIGHT +out float size; +#else out vec2 size; +#endif out float maxAngle; out float maxDistance; out float occluded; +#ifdef INSCATTERING out float inscattering; +#endif void main() { vec3 vertexPos = Position; float Angle = NormalizedAngle * 6.28318530718; - vertexPos.z = clamp(vertexPos.z, min(cos(Angle), 0), 1); - float angleTerm = sin(min(Angle, 1.57079633)) * Distance; + + #ifdef SPOTLIGHT + float term = Size; + #else + float term = Angle; + #endif + vertexPos.z = clamp(vertexPos.z, min(cos(term), 0), 1); + float angleTerm = sin(min(term, 1.57079633)) * Distance; + + #ifdef SPOTLIGHT + vertexPos *= vec3(angleTerm, angleTerm, Distance); + #else vertexPos *= vec3(Size.x + angleTerm, Size.y + angleTerm, Distance); + #endif // awful fix but not sure why just multiplying the matrix doesnt work? it does what it should in // all the second calculations. really weird! @@ -41,5 +64,7 @@ void main() { maxAngle = Angle; maxDistance = Distance; occluded = Occluded; + #ifdef INSCATTERING inscattering = Inscattering; + #endif } diff --git a/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/inscattering/area.fsh b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/inscattering/area.fsh deleted file mode 100644 index 34fa7dbf7..000000000 --- a/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/inscattering/area.fsh +++ /dev/null @@ -1,111 +0,0 @@ -#include veil:common -#include veil:space_helper -#include veil:color_utilities -#include veil:light -#include veil:voxel_shadow - -in mat4 lightMat; -in mat4 invLightMat; -in vec3 lightColor; -in vec2 size; -in float maxAngle; -in float maxDistance; -in float occluded; -in float inscattering; - -uniform sampler2D DepthSampler; - -uniform vec2 ScreenSize; - -out vec4 fragColor; - -// acos approximation -// faster and also doesn't flicker weirdly -float sacos(float x) -{ - float y = abs(clamp(x, -1.0, 1.0)); - float z = (-0.168577*y + 1.56723) * sqrt(1.0 - y); - return mix(0.5*3.1415927, z, sign(x)); -} - -struct AreaLightResult { vec3 position; float angle; }; -AreaLightResult closestPointOnPlaneAndAngle(vec3 point, mat4 planeMatrix, mat4 invPlaneMatrix, vec2 planeSize) { - // no idea why i need to do this - planeMatrix[3].xyz *= -1.0; - invPlaneMatrix[3].xyz *= -1.0; - // transform the point to the plane's local space - vec3 localSpacePoint = (planeMatrix * vec4(point, 1.0)).xyz; - // clamp position - vec3 localSpacePointOnPlane = vec3(clamp(localSpacePoint.xy, -planeSize, planeSize), 0); - - // calculate the angles - vec3 direction = normalize(localSpacePoint - localSpacePointOnPlane); - float angle = sacos(dot(direction, vec3(0.0, 0.0, 1.0))); - - // transform back to global space - return AreaLightResult((invPlaneMatrix * vec4(localSpacePointOnPlane, 1.0)).xyz, angle); -} - -//Fog density -#define DENSITY 2.6 -//Surface pass rate -#define PASSTHROUGH 0.5 - -#define STEPS 50.0 - -float sdSphere( vec3 p, float r ) -{ - return (length(p) - r) / DENSITY + PASSTHROUGH; -} - -vec3 ray(vec3 dir, vec3 pos, vec3 fragPos) { - //Output brightness - #define BRIGHTNESS 0.004 - - //Accumulative color - vec3 col = vec3(0.0); - float d = 0; - float fragDistance = distance(pos, fragPos); - - //Glow raymarch loop - for(float i = 0.0; i 0.0) { - float depth = texture(DepthSampler, screenUv).r; - volume = ray(viewDirFromUv(screenUv), VeilCamera.CameraPosition + VeilCamera.CameraBobOffset, screenToWorldSpace(screenUv, depth).xyz); - } - - fragColor = vec4(volume, 1.0); -} diff --git a/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/inscattering/area.json b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/inscattering/area.json index 1625fa8d5..64589bcd4 100644 --- a/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/inscattering/area.json +++ b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/inscattering/area.json @@ -1,10 +1,13 @@ { "vertex": "veil:light/area", - "fragment": "veil:light/inscattering/area", + "fragment": "veil:light/area", "textures": { "DepthSampler": { "type": "framebuffer", "name": "veil:light_inscattering:depth" } - } + }, + "definitions": [ + "INSCATTERING" + ] } diff --git a/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/inscattering/point.fsh b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/inscattering/point.fsh deleted file mode 100644 index d4f2dd832..000000000 --- a/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/inscattering/point.fsh +++ /dev/null @@ -1,75 +0,0 @@ -#include veil:common -#include veil:space_helper -#include veil:color_utilities -#include veil:light -#include veil:voxel_shadow - -in vec3 lightPos; -in vec3 lightColor; -in float radius; -in float occluded; -in float inscattering; - -uniform sampler2D DepthSampler; - -uniform vec2 ScreenSize; - -out vec4 fragColor; - -//Fog density -#define DENSITY 2.6 -//Surface pass rate -#define PASSTHROUGH 0.5 - -#define STEPS 50.0 - -float sdSphere( vec3 p, float r ) -{ - return (length(p - lightPos) - r) / DENSITY + PASSTHROUGH; -} - -vec3 ray(vec3 dir, vec3 pos, vec3 fragPos) { - //Output brightness - #define BRIGHTNESS 0.004 - - //Accumulative color - vec3 col = vec3(0.0); - float d = 0; - float fragDistance = distance(pos, fragPos); - - //Glow raymarch loop - for(float i = 0.0; i 0.0) { - float depth = texture(DepthSampler, screenUv).r; - volume = ray(viewDirFromUv(screenUv), VeilCamera.CameraPosition + VeilCamera.CameraBobOffset, screenToWorldSpace(screenUv, depth).xyz); - } - - fragColor = vec4(volume, 1.0); -} diff --git a/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/inscattering/point.json b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/inscattering/point.json index 21a5c297b..4b177b092 100644 --- a/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/inscattering/point.json +++ b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/inscattering/point.json @@ -1,10 +1,13 @@ { "vertex": "veil:light/point", - "fragment": "veil:light/inscattering/point", + "fragment": "veil:light/point", "textures": { "DepthSampler": { "type": "framebuffer", "name": "minecraft:main:depth" } - } + }, + "definitions": [ + "INSCATTERING" + ] } diff --git a/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/inscattering/spot.json b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/inscattering/spot.json new file mode 100644 index 000000000..80158df5d --- /dev/null +++ b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/inscattering/spot.json @@ -0,0 +1,14 @@ +{ + "vertex": "veil:light/area", + "fragment": "veil:light/area", + "textures": { + "DepthSampler": { + "type": "framebuffer", + "name": "veil:light_inscattering:depth" + } + }, + "definitions": [ + "INSCATTERING", + "SPOTLIGHT" + ] +} diff --git a/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/point.fsh b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/point.fsh index 6769de4c3..a52f94bd5 100644 --- a/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/point.fsh +++ b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/point.fsh @@ -3,6 +3,9 @@ #include veil:color_utilities #include veil:light #include veil:voxel_shadow +#ifdef INSCATTERING +#include veil:inscattering +#endif in vec3 lightPos; in vec3 lightColor; @@ -10,14 +13,62 @@ in float radius; in float occluded; in float inscattering; +#ifndef INSCATTERING uniform sampler2D AlbedoSampler; uniform sampler2D NormalSampler; +#endif uniform sampler2D DepthSampler; uniform vec2 ScreenSize; out vec4 fragColor; +#ifdef INSCATTERING +vec3 ray(vec3 dir, vec3 pos, vec3 fragPos) { + //Output brightness + #define BRIGHTNESS 0.004 + + //Accumulative color + vec3 col = vec3(0.0); + float d = 0; + float fragDistance = distance(pos, fragPos); + + //Glow raymarch loop + for (float i = 0.0; i < STEPS; i++) + { + //Glow density + float vol = sdSphere(pos - lightPos, 1.0); + vec3 offset = lightPos - pos; + float atten = attenuate_no_cusp(length(offset), radius); + //Step forward + pos += dir * vol; + + d += vol; + if (fragDistance - d < 1.0) { + atten *= smoothstep(0.0, 1.0, fragDistance - d); + } + + //Add the sample color + col += (lightColor * inscattering * atten) / vol; + } + //Tanh tonemapping + //https://mini.gmshaders.com/p/tonemaps + col = tanh(BRIGHTNESS * col); + + return col; +} + +void main() { + vec3 volume = vec3(0); + if (inscattering > 0.0) { + vec2 screenUv = gl_FragCoord.xy / (ScreenSize / 4.0); + float depth = texture(DepthSampler, screenUv).r; + volume = ray(viewDirFromUv(screenUv), VeilCamera.CameraPosition + VeilCamera.CameraBobOffset, screenToWorldSpace(screenUv, depth).xyz); + } + + fragColor = vec4(volume, 1.0); +} +#else void main() { vec2 screenUv = gl_FragCoord.xy / ScreenSize; @@ -46,3 +97,4 @@ void main() { vec3 diffuseColor = diffuse * lightColor; fragColor = vec4(albedoColor.rgb * diffuseColor * (1.0 - reflectivity) + diffuseColor * reflectivity, 1.0); } +#endif diff --git a/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/point.vsh b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/point.vsh index 328f81536..74ff8ee8b 100644 --- a/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/point.vsh +++ b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/point.vsh @@ -3,15 +3,19 @@ layout (location = 0) in vec3 Position; layout (location = 1) in vec3 LightPosition; layout (location = 2) in vec3 Color; -layout (location = 3) in float Distance; -layout (location = 4) in float Occluded; -layout (location = 5) in float Inscattering; +layout (location = 3) in vec3 Settings; + +#define Distance Settings.x +#define Occluded Settings.y +#define Inscattering Settings.z out vec3 lightPos; out vec3 lightColor; out float radius; out float occluded; +#ifdef INSCATTERING out float inscattering; +#endif void main() { vec3 size = Position * Distance; @@ -24,5 +28,7 @@ void main() { lightColor = Color; radius = Distance; occluded = Occluded; + #ifdef INSCATTERING inscattering = Inscattering; + #endif } diff --git a/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/spot.fsh b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/spot.fsh deleted file mode 100644 index 17c87ca77..000000000 --- a/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/spot.fsh +++ /dev/null @@ -1,78 +0,0 @@ -#include veil:common -#include veil:space_helper -#include veil:color_utilities -#include veil:light -#include veil:voxel_shadow - -in mat4 lightMat; -in vec3 lightColor; -in float size; -in float maxAngle; -in float maxDistance; -in float occluded; - -uniform sampler2D AlbedoSampler; -uniform sampler2D NormalSampler; -uniform sampler2D DepthSampler; - -uniform vec2 ScreenSize; - -out vec4 fragColor; - -// acos approximation -// faster and also doesn't flicker weirdly -float sacos(float x) -{ - float y = abs(clamp(x, -1.0, 1.0)); - float z = (-0.168577*y + 1.56723) * sqrt(1.0 - y); - return mix(0.5*3.1415927, z, sign(x)); -} - -struct SpotLightResult { vec3 position; float angle; }; -SpotLightResult spotLightPositionAndAngle(vec3 point, mat4 lightMatrix) { - // no idea why i need to do this - lightMatrix[3].xyz *= -1.0; - - vec3 localSpacePoint = (lightMatrix * vec4(point, 1.0)).xyz; - vec3 localDir = normalize(localSpacePoint); - float angle = sacos(localDir.z); - - vec3 worldPos = (inverse(lightMatrix) * vec4(0.0, 0.0, 0.0, 1.0)).xyz; - return SpotLightResult(worldPos, angle); -} - -void main() { - vec2 screenUv = gl_FragCoord.xy / ScreenSize; - - vec4 albedoColor = texture(AlbedoSampler, screenUv); - if (albedoColor.a == 0) { - discard; - } - - vec3 normalVS = texture(NormalSampler, screenUv).xyz; - float depth = texture(DepthSampler, screenUv).r; - vec3 pos = screenToWorldSpace(screenUv, depth).xyz; - - // lighting calculation - SpotLightResult spotLightInfo = spotLightPositionAndAngle(pos, lightMat); - vec3 lightPos = spotLightInfo.position; - - vec3 offset = lightPos - pos; - vec3 lightDirection = normalize((VeilCamera.ViewMat * vec4(offset, 0.0)).xyz); - float diffuse = (dot(normalVS, lightDirection) + 1.0) * 0.5; - diffuse = (diffuse + MINECRAFT_AMBIENT_LIGHT) / (1.0 + MINECRAFT_AMBIENT_LIGHT); - diffuse *= attenuate_no_cusp(length(offset), maxDistance); - - float angleFalloff = smoothstep(size, size - maxAngle, spotLightInfo.angle); - diffuse *= angleFalloff; - - if (occluded > 0.5) { - vec3 normalWS = normalize((VeilCamera.IViewMat * vec4(normalVS, 0.0)).xyz); - diffuse *= voxelshadowVisibility(pos + normalWS * 0.01, lightPos); - } - - float reflectivity = 0.05; - vec3 diffuseColor = diffuse * lightColor; - - fragColor = vec4(albedoColor.rgb * diffuseColor * (1.0 - reflectivity) + diffuseColor * reflectivity, 1.0); -} \ No newline at end of file diff --git a/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/spot.json b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/spot.json index 8a92782f3..22eb7d6a0 100644 --- a/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/spot.json +++ b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/spot.json @@ -1,6 +1,6 @@ { - "vertex": "veil:light/spot", - "fragment": "veil:light/spot", + "vertex": "veil:light/area", + "fragment": "veil:light/area", "textures": { "AlbedoSampler": "veil:dynamic_buffer/albedo", "NormalSampler": "veil:dynamic_buffer/normal", @@ -8,5 +8,8 @@ "type": "framebuffer", "name": "minecraft:main:depth" } - } + }, + "definitions": [ + "SPOTLIGHT" + ] } diff --git a/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/spot.vsh b/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/spot.vsh deleted file mode 100644 index 8bcae8970..000000000 --- a/common/src/main/resources/assets/veil/pinwheel/shaders/program/light/spot.vsh +++ /dev/null @@ -1,39 +0,0 @@ -#veil:buffer veil:camera VeilCamera - -layout (location = 0) in vec3 Position; -layout (location = 1) in mat4 LightMatrix; -layout (location = 5) in vec3 Color; -layout (location = 6) in float Size; -layout (location = 7) in float NormalizedAngle; -layout (location = 8) in float Distance; -layout (location = 9) in float Occluded; - -out mat4 lightMat; -out vec3 lightColor; -out float size; -out float maxAngle; -out float maxDistance; -out float occluded; - -void main() { - vec3 vertexPos = Position; - vertexPos.z = clamp(vertexPos.z, min(cos(Size), 0), 1); - float angleTerm = sin(min(Size, 1.57079633)) * Distance; - vertexPos *= vec3(angleTerm, angleTerm, Distance); - - // awful fix but not sure why just multiplying the matrix doesnt work? it does what it should in - // all the second calculations. really weird! - vec3 lightPos = LightMatrix[3].xyz; - mat3 rotationMatrix = mat3(LightMatrix); - lightPos = inverse(rotationMatrix) * lightPos; - vertexPos = inverse(rotationMatrix) * vertexPos; - vertexPos += lightPos; - gl_Position = VeilCamera.ProjMat * VeilCamera.ViewMat * vec4(vertexPos - VeilCamera.CameraPosition, 1.0); - - lightMat = LightMatrix; - lightColor = Color; - size = Size; - maxAngle = NormalizedAngle * 6.28318530718; - maxDistance = Distance; - occluded = Occluded; -} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 932323ae2..78e12b479 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ # Project -version=4.3.0 +version=4.4.0 group=foundry.veil java_version=21 From 0a845e783f1727ed394aaba97b35a2e56d70bcd9 Mon Sep 17 00:00:00 2001 From: Redcat_XVIII <124214835+redboi18@users.noreply.github.com> Date: Mon, 27 Jul 2026 20:40:24 -0700 Subject: [PATCH 6/7] Spot Light Stuff --- .../render/light/data/SpotLightData.java | 62 ++++++++++++++++++- .../render/light/SpotLightRenderer.java | 4 +- .../rendertypes/light/inscattering/spot.json | 26 ++++++++ wiki/Lights.md | 3 + 4 files changed, 92 insertions(+), 3 deletions(-) create mode 100644 common/src/main/resources/assets/veil/pinwheel/rendertypes/light/inscattering/spot.json diff --git a/common/src/main/java/foundry/veil/api/client/render/light/data/SpotLightData.java b/common/src/main/java/foundry/veil/api/client/render/light/data/SpotLightData.java index 9490da27c..18518070b 100644 --- a/common/src/main/java/foundry/veil/api/client/render/light/data/SpotLightData.java +++ b/common/src/main/java/foundry/veil/api/client/render/light/data/SpotLightData.java @@ -1,11 +1,14 @@ package foundry.veil.api.client.render.light.data; +import com.mojang.blaze3d.vertex.VertexConsumer; import foundry.veil.api.client.color.Colorc; import foundry.veil.api.client.editor.EditorAttributeProvider; import foundry.veil.api.client.registry.LightTypeRegistry; import foundry.veil.api.client.render.CullFrustum; +import foundry.veil.api.client.render.MatrixStack; import foundry.veil.api.client.render.light.DDALightData; import foundry.veil.api.client.render.light.InstancedLightData; +import foundry.veil.api.client.render.light.LightGuideProvider; import imgui.ImGui; import net.minecraft.client.Camera; import net.minecraft.util.Mth; @@ -20,7 +23,7 @@ * * @since 4.4.0 */ -public class SpotLightData extends LightData implements InstancedLightData, DDALightData, EditorAttributeProvider { +public class SpotLightData extends LightData implements InstancedLightData, DDALightData, EditorAttributeProvider, LightGuideProvider { private static final float MAX_ANGLE_SIZE = (float) (65535.0 / 2.0 / Math.PI); @@ -33,6 +36,7 @@ public class SpotLightData extends LightData implements InstancedLightData, DDAL protected float angle; protected float distance; protected boolean occlusionEnabled; + protected float inscattering; public SpotLightData() { this.matrix = new Matrix4d(); @@ -44,6 +48,7 @@ public SpotLightData() { this.angle = (float) Math.toRadians(45); this.distance = 1.0F; this.occlusionEnabled = false; + this.inscattering = 0.0F; } @Override @@ -111,6 +116,14 @@ public boolean isOcclusionEnabled() { return this.occlusionEnabled; } + /** + * @return The strength of the light's in-scattering effect. + * @since 4.4.0 + */ + public float getInscatteringStrength() { + return this.inscattering; + } + /** * Sets the size of the light's surface * @@ -150,6 +163,15 @@ public SpotLightData setOcclusionEnabled(boolean occlusionEnabled) { return this; } + /** + * @since 4.4.0 + * */ + public SpotLightData setInscatteringStrength(float inscattering) { + this.inscattering = inscattering; + this.markDirty(); + return this; + } + @Override public SpotLightData setColor(Vector3fc color) { super.setColor(color); @@ -194,6 +216,7 @@ public void store(ByteBuffer buffer) { buffer.putShort((short) Mth.clamp((int) (this.angle * MAX_ANGLE_SIZE), 0, 65535)); buffer.putFloat(this.distance); buffer.putFloat(this.occlusionEnabled ? 1.0F : 0.0F); + buffer.putFloat(this.inscattering); } @Override @@ -234,6 +257,8 @@ public void renderImGuiAttributes() { float[] editAngle = new float[]{this.angle}; float[] editDistance = new float[]{this.distance}; + float[] editInscattering = new float[]{this.inscattering}; + if (ImGui.sliderAngle("size", editSize, 0.1F, 180.0F, "%.1f")) { this.setSize(editSize[0]); } @@ -293,5 +318,40 @@ public void renderImGuiAttributes() { this.occlusionEnabled = !this.occlusionEnabled; this.markDirty(); } + + if (ImGui.dragScalar("In-scattering", editInscattering, 0.01F, 0.0F)) { + this.setInscatteringStrength(editInscattering[0]); + } + } + + @Override + public void renderLightGuide(MatrixStack stack, VertexConsumer consumer) { + stack.matrixPush(); + + stack.translate(this.position.x, this.position.y, this.position.z); + Vector3f rot = this.orientation.getEulerAnglesXYZ(new Vector3f()).mul(-1); + stack.rotate(new Quaternionf().rotateX(rot.x).rotateLocalY(rot.y).rotateLocalZ(rot.z)); + + Matrix4f pose = stack.position(); + + float radius = this.distance * (float) Math.tan(this.size); + + for (int i = -8; i < 25; i++) { + float x = (float) Math.sin(i / (float) 32 * Mth.TWO_PI) * radius; + float y = (float) Math.cos(i / (float) 32 * Mth.TWO_PI) * radius; + consumer.addVertex(pose, x, y, this.distance).setColor(this.color.red(), this.color.green(), this.color.blue(), this.color.alpha()); + } + + int spokes = 8; + for (int i = 0; i < spokes; i++) { + float theta = i / (float) spokes * Mth.TWO_PI; + float x = (float) Math.sin(theta) * radius; + float y = (float) Math.cos(theta) * radius; + + consumer.addVertex(pose, 0, 0, 0).setColor(this.color.red(), this.color.green(), this.color.blue(), this.color.alpha()); + consumer.addVertex(pose, x, y, this.distance).setColor(this.color.red(), this.color.green(), this.color.blue(), this.color.alpha()); + } + + stack.matrixPop(); } } diff --git a/common/src/main/java/foundry/veil/impl/client/render/light/SpotLightRenderer.java b/common/src/main/java/foundry/veil/impl/client/render/light/SpotLightRenderer.java index f87f7fe1b..97014782f 100644 --- a/common/src/main/java/foundry/veil/impl/client/render/light/SpotLightRenderer.java +++ b/common/src/main/java/foundry/veil/impl/client/render/light/SpotLightRenderer.java @@ -29,7 +29,7 @@ public class SpotLightRenderer extends InstancedLightRenderer imp private static final ResourceLocation INSCATTERING_RENDER_TYPE = Veil.veilPath("light/inscattering/spot"); public SpotLightRenderer() { - super(Float.BYTES * 22 + 2); + super(Float.BYTES * 23 + 2); } @Override @@ -48,7 +48,7 @@ protected void setupBufferState(VertexArrayBuilder builder) { builder.setVertexAttribute(5, VertexArray.INSTANCE_BUFFER, 3, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 16); // color builder.setVertexAttribute(6, VertexArray.INSTANCE_BUFFER, 1, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 19); // size builder.setVertexAttribute(7, VertexArray.INSTANCE_BUFFER, 1, VertexArrayBuilder.DataType.UNSIGNED_SHORT, true, Float.BYTES * 20); // angle - builder.setVertexAttribute(8, VertexArray.INSTANCE_BUFFER, 3, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 21 + 2); // distance/occlusion/in-scatting + builder.setVertexAttribute(8, VertexArray.INSTANCE_BUFFER, 3, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 20 + 2); // distance/occlusion/in-scatting } @Override diff --git a/common/src/main/resources/assets/veil/pinwheel/rendertypes/light/inscattering/spot.json b/common/src/main/resources/assets/veil/pinwheel/rendertypes/light/inscattering/spot.json new file mode 100644 index 000000000..942a551b1 --- /dev/null +++ b/common/src/main/resources/assets/veil/pinwheel/rendertypes/light/inscattering/spot.json @@ -0,0 +1,26 @@ +{ + "format": "POSITION_TEX", + "mode": "QUADS", + "bufferSize": "TRANSIENT", + "sort": false, + "affectsCrumbling": false, + "outline": false, + "layers": [ + { + "type": "veil:shader", + "name": "veil:light/inscattering/spot" + }, + { + "type": "minecraft:write_mask", + "depth": false + }, + { + "type": "minecraft:depth_test", + "mode": "notequal" + }, + { + "type": "minecraft:transparency", + "mode": "additive" + } + ] +} \ No newline at end of file diff --git a/wiki/Lights.md b/wiki/Lights.md index 843592607..38cc6c0a2 100644 --- a/wiki/Lights.md +++ b/wiki/Lights.md @@ -15,6 +15,9 @@ Area lights emit light from a quad, which can be rotated to face any direction. ### [Directional Lights](https://github.com/FoundryMC/Veil/blob/1.21/common/src/main/java/foundry/veil/api/client/render/light/data/DirectionalLightData.java) Directional lights, unlike point and area lights, do not have a position, merely having a `direction` property. Directional lights simulate the sun, shadowing faces away from the light. +### [Spot Lights](https://github.com/FoundryMC/Veil/blob/1.21/common/src/main/java/foundry/veil/api/client/render/light/data/SpotLightData.java) +Spot lights emit light from a point, which can be rotated to face any direction. Spot lights have the same values as Area lights except it has one `size` value instead of separate width and height values. + ## Light Renderers Each light type additionally its own [`LightTypeRenderer`](https://github.com/FoundryMC/Veil/blob/1.21/common/src/main/java/foundry/veil/api/client/render/light/renderer/LightTypeRenderer.java). Each `LightTypeRenderer` is responsible for rendering all lights of its assigned types. Lights that have a position (like point and area lights) can extend the [`InstancedLightRenderer`](https://github.com/FoundryMC/Veil/blob/1.21/common/src/main/java/foundry/veil/api/client/render/light/renderer/InstancedLightRenderer.java) to have some methods built in. From 59e9d018f3c7d08c68b268ab7bb6e8b73a3fe0ee Mon Sep 17 00:00:00 2001 From: Ocelot Date: Mon, 27 Jul 2026 22:55:11 -0600 Subject: [PATCH 7/7] Fix incorrect light buffer offsets --- .../veil/api/client/render/light/data/AreaLightData.java | 1 + .../veil/api/client/render/light/data/SpotLightData.java | 1 + .../veil/impl/client/render/light/AreaLightRenderer.java | 4 ++-- .../veil/impl/client/render/light/SpotLightRenderer.java | 4 ++-- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/common/src/main/java/foundry/veil/api/client/render/light/data/AreaLightData.java b/common/src/main/java/foundry/veil/api/client/render/light/data/AreaLightData.java index 63450e52d..1d21ae756 100644 --- a/common/src/main/java/foundry/veil/api/client/render/light/data/AreaLightData.java +++ b/common/src/main/java/foundry/veil/api/client/render/light/data/AreaLightData.java @@ -242,6 +242,7 @@ public void store(ByteBuffer buffer) { buffer.position(buffer.position() + Float.BYTES * 2); buffer.putShort((short) Mth.clamp((int) (this.angle * MAX_ANGLE_SIZE), 0, 65535)); + buffer.position(buffer.position() + 2); // Padding buffer.putFloat(this.distance); buffer.putFloat(this.occlusionEnabled ? 1.0F : 0.0F); buffer.putFloat(this.inscattering); diff --git a/common/src/main/java/foundry/veil/api/client/render/light/data/SpotLightData.java b/common/src/main/java/foundry/veil/api/client/render/light/data/SpotLightData.java index 9490da27c..037d10b96 100644 --- a/common/src/main/java/foundry/veil/api/client/render/light/data/SpotLightData.java +++ b/common/src/main/java/foundry/veil/api/client/render/light/data/SpotLightData.java @@ -192,6 +192,7 @@ public void store(ByteBuffer buffer) { buffer.putFloat(this.size); buffer.putShort((short) Mth.clamp((int) (this.angle * MAX_ANGLE_SIZE), 0, 65535)); + buffer.position(buffer.position() + 2); // Padding buffer.putFloat(this.distance); buffer.putFloat(this.occlusionEnabled ? 1.0F : 0.0F); } diff --git a/common/src/main/java/foundry/veil/impl/client/render/light/AreaLightRenderer.java b/common/src/main/java/foundry/veil/impl/client/render/light/AreaLightRenderer.java index 0b1b24b3d..5c4bf64e0 100644 --- a/common/src/main/java/foundry/veil/impl/client/render/light/AreaLightRenderer.java +++ b/common/src/main/java/foundry/veil/impl/client/render/light/AreaLightRenderer.java @@ -29,7 +29,7 @@ public class AreaLightRenderer extends InstancedLightRenderer imp private static final ResourceLocation INSCATTERING_RENDER_TYPE = Veil.veilPath("light/inscattering/area"); public AreaLightRenderer() { - super(Float.BYTES * 24 + 2); + super(Float.BYTES * 24 + 4); } @Override @@ -48,7 +48,7 @@ protected void setupBufferState(VertexArrayBuilder builder) { builder.setVertexAttribute(5, VertexArray.INSTANCE_BUFFER, 3, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 16); // color builder.setVertexAttribute(6, VertexArray.INSTANCE_BUFFER, 2, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 19); // size builder.setVertexAttribute(7, VertexArray.INSTANCE_BUFFER, 1, VertexArrayBuilder.DataType.UNSIGNED_SHORT, true, Float.BYTES * 21); // angle - builder.setVertexAttribute(8, VertexArray.INSTANCE_BUFFER, 3, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 21 + 2); // distance/occlusion/in-scatting + builder.setVertexAttribute(8, VertexArray.INSTANCE_BUFFER, 3, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 21 + 4); // distance/occlusion/in-scatting } @Override diff --git a/common/src/main/java/foundry/veil/impl/client/render/light/SpotLightRenderer.java b/common/src/main/java/foundry/veil/impl/client/render/light/SpotLightRenderer.java index f87f7fe1b..8817ecd1e 100644 --- a/common/src/main/java/foundry/veil/impl/client/render/light/SpotLightRenderer.java +++ b/common/src/main/java/foundry/veil/impl/client/render/light/SpotLightRenderer.java @@ -29,7 +29,7 @@ public class SpotLightRenderer extends InstancedLightRenderer imp private static final ResourceLocation INSCATTERING_RENDER_TYPE = Veil.veilPath("light/inscattering/spot"); public SpotLightRenderer() { - super(Float.BYTES * 22 + 2); + super(Float.BYTES * 23 + 4); } @Override @@ -48,7 +48,7 @@ protected void setupBufferState(VertexArrayBuilder builder) { builder.setVertexAttribute(5, VertexArray.INSTANCE_BUFFER, 3, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 16); // color builder.setVertexAttribute(6, VertexArray.INSTANCE_BUFFER, 1, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 19); // size builder.setVertexAttribute(7, VertexArray.INSTANCE_BUFFER, 1, VertexArrayBuilder.DataType.UNSIGNED_SHORT, true, Float.BYTES * 20); // angle - builder.setVertexAttribute(8, VertexArray.INSTANCE_BUFFER, 3, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 21 + 2); // distance/occlusion/in-scatting + builder.setVertexAttribute(8, VertexArray.INSTANCE_BUFFER, 3, VertexArrayBuilder.DataType.FLOAT, false, Float.BYTES * 20 + 4); // distance/occlusion/in-scatting } @Override