Uber smooth

- Add material shader component to specify smooth lighting behavior
- Allows much easier composition of smooth lighting/material shader
  effects, and potentially gives backends the option to specialize
  shaders on the complexity of shader lighting
- Pack fog, cutout, and light into a single uint
This commit is contained in:
Jozufozu 2024-07-27 15:31:08 -07:00
parent cdc68244e7
commit 69411fb36f
20 changed files with 132 additions and 19 deletions

View File

@ -0,0 +1,11 @@
package dev.engine_room.flywheel.api.material;
import dev.engine_room.flywheel.api.internal.FlwApiLink;
import dev.engine_room.flywheel.api.registry.Registry;
import net.minecraft.resources.ResourceLocation;
public interface LightShader {
Registry<LightShader> REGISTRY = FlwApiLink.INSTANCE.createRegistry();
ResourceLocation source();
}

View File

@ -9,6 +9,8 @@ public interface Material {
CutoutShader cutout();
LightShader light();
ResourceLocation texture();
/**

View File

@ -8,6 +8,7 @@ import org.jetbrains.annotations.Unmodifiable;
import dev.engine_room.flywheel.api.material.CutoutShader;
import dev.engine_room.flywheel.api.material.FogShader;
import dev.engine_room.flywheel.api.material.LightShader;
import dev.engine_room.flywheel.api.material.MaterialShaders;
import dev.engine_room.flywheel.api.registry.Registry;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
@ -25,6 +26,8 @@ public final class MaterialShaderIndices {
private static Index fogSources;
@Nullable
private static Index cutoutSources;
@Nullable
private static Index lightSources;
private MaterialShaderIndices() {
}
@ -57,6 +60,13 @@ public final class MaterialShaderIndices {
return cutoutSources;
}
public static Index lightSources() {
if (lightSources == null) {
lightSources = indexFromRegistry(LightShader.REGISTRY, LightShader::source);
}
return lightSources;
}
public static int vertexIndex(MaterialShaders shaders) {
return vertexSources().index(shaders.vertexSource());
}
@ -73,6 +83,10 @@ public final class MaterialShaderIndices {
return cutoutSources().index(cutoutShader.source());
}
public static int lightIndex(LightShader lightShader) {
return lightSources().index(lightShader.source());
}
private static <T> Index indexFromRegistry(Registry<T> registry, Function<T, ResourceLocation> sourceFunc) {
if (!registry.isFrozen()) {
throw new IllegalStateException("Cannot create index from registry that is not frozen!");

View File

@ -46,15 +46,16 @@ public final class FlwPrograms {
var fragmentMaterialComponent = createFragmentMaterialComponent(loader);
var fogComponent = createFogComponent(loader);
var cutoutComponent = createCutoutComponent(loader);
var lightComponent = createLightComponent(loader);
if (stats.errored() || vertexComponentsHeader == null || fragmentComponentsHeader == null || vertexMaterialComponent == null || fragmentMaterialComponent == null || fogComponent == null || cutoutComponent == null) {
if (stats.errored() || vertexComponentsHeader == null || fragmentComponentsHeader == null || vertexMaterialComponent == null || fragmentMaterialComponent == null || fogComponent == null || cutoutComponent == null || lightComponent == null) {
// Probably means the shader sources are missing.
stats.emitErrorLog();
return;
}
List<SourceComponent> vertexComponents = List.of(vertexComponentsHeader, vertexMaterialComponent);
List<SourceComponent> fragmentComponents = List.of(fragmentComponentsHeader, fragmentMaterialComponent, fogComponent, cutoutComponent);
List<SourceComponent> fragmentComponents = List.of(fragmentComponentsHeader, fragmentMaterialComponent, fogComponent, cutoutComponent, lightComponent);
var pipelineKeys = createPipelineKeys();
InstancingPrograms.reload(sources, pipelineKeys, vertexComponents, fragmentComponents);
@ -118,4 +119,17 @@ public final class FlwPrograms {
.switchOn(GlslExpr.variable("_flw_uberCutoutIndex"))
.build(loader);
}
@Nullable
private static UberShaderComponent createLightComponent(SourceLoader loader) {
return UberShaderComponent.builder(Flywheel.rl("light"))
.materialSources(MaterialShaderIndices.lightSources()
.all())
.adapt(FnSignature.create()
.returnType("void")
.name("flw_shaderLight")
.build())
.switchOn(GlslExpr.variable("_flw_uberLightIndex"))
.build(loader);
}
}

View File

@ -52,10 +52,11 @@ public final class MaterialEncoder {
return ((1 << bitLength) - 1) << bitOffset;
}
public static int packFogAndCutout(Material material) {
public static int packUberShader(Material material) {
var fog = MaterialShaderIndices.fogIndex(material.fog());
var cutout = MaterialShaderIndices.cutoutIndex(material.cutout());
return fog & 0xFFFF | (cutout & 0xFFFF) << 16;
var light = MaterialShaderIndices.lightIndex(material.light());
return (light & 0x3FF) | (cutout & 0x3FF) << 10 | (fog & 0x3FF) << 20;
}
// Packed format:

View File

@ -34,7 +34,7 @@ public class IndirectDraw {
this.materialVertexIndex = MaterialShaderIndices.vertexIndex(material.shaders());
this.materialFragmentIndex = MaterialShaderIndices.fragmentIndex(material.shaders());
this.packedFogAndCutout = MaterialEncoder.packFogAndCutout(material);
this.packedFogAndCutout = MaterialEncoder.packUberShader(material);
this.packedMaterialProperties = MaterialEncoder.packProperties(material);
}
@ -88,7 +88,7 @@ public class IndirectDraw {
MemoryUtil.memPutInt(ptr + 24, MaterialShaderIndices.vertexIndex(materialOverride.shaders())); // materialVertexIndex
MemoryUtil.memPutInt(ptr + 28, MaterialShaderIndices.fragmentIndex(materialOverride.shaders())); // materialFragmentIndex
MemoryUtil.memPutInt(ptr + 32, MaterialEncoder.packFogAndCutout(materialOverride)); // packedFogAndCutout
MemoryUtil.memPutInt(ptr + 32, MaterialEncoder.packUberShader(materialOverride)); // packedFogAndCutout
MemoryUtil.memPutInt(ptr + 36, MaterialEncoder.packProperties(materialOverride)); // packedMaterialProperties
}

View File

@ -206,7 +206,7 @@ public class InstancedDrawManager extends DrawManager<InstancedInstancer<?>> {
int uniformLocation = program.getUniformLocation("_flw_packedMaterial");
int vertexIndex = MaterialShaderIndices.vertexIndex(material.shaders());
int fragmentIndex = MaterialShaderIndices.fragmentIndex(material.shaders());
int packedFogAndCutout = MaterialEncoder.packFogAndCutout(material);
int packedFogAndCutout = MaterialEncoder.packUberShader(material);
int packedMaterialProperties = MaterialEncoder.packProperties(material);
GL32.glUniform4ui(uniformLocation, vertexIndex, fragmentIndex, packedFogAndCutout, packedMaterialProperties);
}

View File

@ -47,6 +47,10 @@ void _flw_main() {
vec4 color = flw_fragColor;
if (flw_discardPredicate(color)) {
discard;
}
float diffuseFactor = _flw_diffuseFactor();
color.rgb *= diffuseFactor;
@ -57,14 +61,12 @@ void _flw_main() {
vec4 lightColor = vec4(1.);
if (flw_material.useLight) {
flw_shaderLight();
lightColor = texture(flw_lightTex, clamp(flw_fragLight, 0.5 / 16.0, 15.5 / 16.0));
color *= lightColor;
}
if (flw_discardPredicate(color)) {
discard;
}
switch (_flw_debugMode) {
case 1u:
color = vec4(flw_vertexNormal * .5 + .5, 1.);

View File

@ -1,3 +1,4 @@
uint _flw_uberMaterialFragmentIndex;
uint _flw_uberFogIndex;
uint _flw_uberCutoutIndex;
uint _flw_uberLightIndex;

View File

@ -6,7 +6,7 @@ flat in uvec3 _flw_packedMaterial;
void main() {
_flw_uberMaterialFragmentIndex = _flw_packedMaterial.x;
_flw_unpackUint2x16(_flw_packedMaterial.y, _flw_uberCutoutIndex, _flw_uberFogIndex);
_flw_unpackUint3x10(_flw_packedMaterial.y, _flw_uberFogIndex, _flw_uberCutoutIndex, _flw_uberLightIndex);
_flw_unpackMaterialProperties(_flw_packedMaterial.z, flw_material);
_flw_main();

View File

@ -5,7 +5,7 @@ uniform uvec4 _flw_packedMaterial;
void main() {
_flw_uberMaterialFragmentIndex = _flw_packedMaterial.y;
_flw_unpackUint2x16(_flw_packedMaterial.z, _flw_uberCutoutIndex, _flw_uberFogIndex);
_flw_unpackUint3x10(_flw_packedMaterial.z, _flw_uberFogIndex, _flw_uberCutoutIndex, _flw_uberLightIndex);
_flw_unpackMaterialProperties(_flw_packedMaterial.w, flw_material);
_flw_main();

View File

@ -53,3 +53,9 @@ void _flw_unpackUint2x16(uint s, out uint hi, out uint lo) {
hi = (s >> 16) & 0xFFFFu;
lo = s & 0xFFFFu;
}
void _flw_unpackUint3x10(uint s, out uint hi, out uint mi, out uint lo) {
hi = (s >> 20) & 0x3FFu;
mi = (s >> 10) & 0x3FFu;
lo = s & 0x3FFu;
}

View File

@ -0,0 +1,19 @@
package dev.engine_room.flywheel.lib.material;
import org.jetbrains.annotations.ApiStatus;
import dev.engine_room.flywheel.api.Flywheel;
import dev.engine_room.flywheel.api.material.LightShader;
public class LightShaders {
public static final LightShader SMOOTH_WHEN_EMBEDDED = LightShader.REGISTRY.registerAndGet(new SimpleLightShader(Flywheel.rl("light/smooth_when_embedded.glsl")));
public static final LightShader SMOOTH = LightShader.REGISTRY.registerAndGet(new SimpleLightShader(Flywheel.rl("light/smooth.glsl")));
public static final LightShader FLAT = LightShader.REGISTRY.registerAndGet(new SimpleLightShader(Flywheel.rl("light/flat.glsl")));
private LightShaders() {
}
@ApiStatus.Internal
public static void init() {
}
}

View File

@ -0,0 +1,7 @@
package dev.engine_room.flywheel.lib.material;
import dev.engine_room.flywheel.api.material.LightShader;
import net.minecraft.resources.ResourceLocation;
public record SimpleLightShader(@Override ResourceLocation source) implements LightShader {
}

View File

@ -3,6 +3,7 @@ package dev.engine_room.flywheel.lib.material;
import dev.engine_room.flywheel.api.material.CutoutShader;
import dev.engine_room.flywheel.api.material.DepthTest;
import dev.engine_room.flywheel.api.material.FogShader;
import dev.engine_room.flywheel.api.material.LightShader;
import dev.engine_room.flywheel.api.material.Material;
import dev.engine_room.flywheel.api.material.MaterialShaders;
import dev.engine_room.flywheel.api.material.Transparency;
@ -14,6 +15,7 @@ public class SimpleMaterial implements Material {
protected final MaterialShaders shaders;
protected final FogShader fog;
protected final CutoutShader cutout;
protected final LightShader light;
protected final ResourceLocation texture;
protected final boolean blur;
@ -33,6 +35,7 @@ public class SimpleMaterial implements Material {
shaders = builder.shaders();
fog = builder.fog();
cutout = builder.cutout();
light = builder.light();
texture = builder.texture();
blur = builder.blur();
mipmap = builder.mipmap();
@ -69,6 +72,11 @@ public class SimpleMaterial implements Material {
return cutout;
}
@Override
public LightShader light() {
return light;
}
@Override
public ResourceLocation texture() {
return texture;
@ -128,6 +136,7 @@ public class SimpleMaterial implements Material {
protected MaterialShaders shaders;
protected FogShader fog;
protected CutoutShader cutout;
protected LightShader light;
protected ResourceLocation texture;
protected boolean blur;
@ -147,6 +156,7 @@ public class SimpleMaterial implements Material {
shaders = StandardMaterialShaders.DEFAULT;
fog = FogShaders.LINEAR;
cutout = CutoutShaders.OFF;
light = LightShaders.SMOOTH_WHEN_EMBEDDED;
texture = InventoryMenu.BLOCK_ATLAS;
blur = false;
mipmap = true;
@ -197,6 +207,11 @@ public class SimpleMaterial implements Material {
return this;
}
public Builder light(LightShader value) {
this.light = value;
return this;
}
public Builder texture(ResourceLocation value) {
this.texture = value;
return this;
@ -267,6 +282,11 @@ public class SimpleMaterial implements Material {
return cutout;
}
@Override
public LightShader light() {
return light;
}
@Override
public ResourceLocation texture() {
return texture;

View File

@ -0,0 +1,6 @@
void flw_shaderLight() {
vec2 embeddedLight;
if (flw_lightFetch(ivec3(floor(flw_vertexPos.xyz)), embeddedLight)) {
flw_fragLight = max(flw_fragLight, embeddedLight);
}
}

View File

@ -0,0 +1,6 @@
void flw_shaderLight() {
vec2 embeddedLight;
if (flw_light(flw_vertexPos.xyz, flw_vertexNormal, embeddedLight)) {
flw_fragLight = max(flw_fragLight, embeddedLight);
}
}

View File

@ -0,0 +1,8 @@
void flw_shaderLight() {
#ifdef FLW_EMBEDDED
vec2 embeddedLight;
if (flw_light(flw_vertexPos.xyz, flw_vertexNormal, embeddedLight)) {
flw_fragLight = max(flw_fragLight, embeddedLight);
}
#endif
}

View File

@ -1,8 +1,2 @@
void flw_materialFragment() {
#ifdef FLW_EMBEDDED
vec2 embeddedLight;
if (flw_light(flw_vertexPos.xyz, flw_vertexNormal, embeddedLight)) {
flw_fragLight = max(flw_fragLight, embeddedLight);
}
#endif
}

View File

@ -10,6 +10,7 @@ import dev.engine_room.flywheel.impl.registry.RegistryImpl;
import dev.engine_room.flywheel.lib.instance.InstanceTypes;
import dev.engine_room.flywheel.lib.material.CutoutShaders;
import dev.engine_room.flywheel.lib.material.FogShaders;
import dev.engine_room.flywheel.lib.material.LightShaders;
import dev.engine_room.flywheel.lib.material.StandardMaterialShaders;
import dev.engine_room.flywheel.lib.util.ShadersModHandler;
import dev.engine_room.flywheel.vanilla.VanillaVisuals;
@ -30,6 +31,7 @@ public final class FlwImpl {
InstanceTypes.init();
CutoutShaders.init();
FogShaders.init();
LightShaders.init();
StandardMaterialShaders.init();
// backend