Fog modes for new rendering.

- For each fog mode, compile a different version of each shader.
 - Could maybe do some fancier preprocessing to make writing shaders easier?
This commit is contained in:
JozsefA 2021-03-04 23:43:28 -08:00
parent b511a20814
commit 25fdf08e11
19 changed files with 306 additions and 49 deletions

View file

@ -1,5 +1,6 @@
package com.simibubi.create.content.contraptions.components.structureMovement.render;
import com.simibubi.create.foundation.render.backend.gl.shader.ProgramFogMode;
import org.lwjgl.opengl.GL20;
import com.simibubi.create.foundation.render.backend.gl.BasicProgram;
@ -15,8 +16,8 @@ public class ContraptionProgram extends BasicProgram {
protected int uLightVolume;
public ContraptionProgram(ResourceLocation name, int handle) {
super(name, handle);
public ContraptionProgram(ResourceLocation name, int handle, ProgramFogMode.Factory fogFactory) {
super(name, handle, fogFactory);
uLightBoxSize = getUniformLocation("uLightBoxSize");
uLightBoxMin = getUniformLocation("uLightBoxMin");
uModel = getUniformLocation("uModel");

View file

@ -26,6 +26,7 @@ import net.minecraft.util.Direction;
import net.minecraft.util.Direction.Axis;
import net.minecraft.util.Direction.AxisDirection;
import net.minecraft.util.IStringSerializable;
import net.minecraft.util.Rotation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.shapes.ISelectionContext;
import net.minecraft.util.math.shapes.VoxelShape;
@ -211,6 +212,14 @@ public class BeltTunnelBlock extends Block implements ITE<BeltTunnelTileEntity>,
return ActionResultType.SUCCESS;
}
@Override
public BlockState rotate(BlockState state, Rotation rotation) {
Direction fromAxis = Direction.getFacingFromAxis(AxisDirection.POSITIVE, state.get(HORIZONTAL_AXIS));
Direction rotated = rotation.rotate(fromAxis);
return state.with(HORIZONTAL_AXIS, rotated.getAxis());
}
@Override
public void neighborChanged(BlockState state, World worldIn, BlockPos pos, Block blockIn, BlockPos fromPos,
boolean isMoving) {

View file

@ -4,10 +4,14 @@ import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.FloatBuffer;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Predicate;
import com.simibubi.create.foundation.render.backend.gl.GlFog;
import com.simibubi.create.foundation.render.backend.gl.GlFogMode;
import com.simibubi.create.foundation.render.backend.gl.shader.*;
import com.simibubi.create.foundation.render.backend.gl.versioned.GlFeatureCompat;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -16,10 +20,6 @@ import org.lwjgl.opengl.GLCapabilities;
import org.lwjgl.system.MemoryUtil;
import com.simibubi.create.foundation.config.AllConfigs;
import com.simibubi.create.foundation.render.backend.gl.shader.GlProgram;
import com.simibubi.create.foundation.render.backend.gl.shader.GlShader;
import com.simibubi.create.foundation.render.backend.gl.shader.ProgramSpec;
import com.simibubi.create.foundation.render.backend.gl.shader.ShaderType;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.texture.TextureUtil;
@ -31,11 +31,13 @@ import net.minecraftforge.resource.ISelectiveResourceReloadListener;
import net.minecraftforge.resource.VanillaResourceType;
public class Backend {
public static final Boolean SHADER_DEBUG_OUTPUT = false;
public static final Logger log = LogManager.getLogger(Backend.class);
public static final FloatBuffer MATRIX_BUFFER = MemoryUtil.memAllocFloat(16);
private static final Map<ResourceLocation, ProgramSpec<?>> registry = new HashMap<>();
private static final Map<ProgramSpec<?>, GlProgram> programs = new HashMap<>();
private static final Map<ProgramSpec<?>, ProgramGroup<?>> programs = new HashMap<>();
private static boolean enabled;
@ -60,7 +62,7 @@ public class Backend {
@SuppressWarnings("unchecked")
public static <P extends GlProgram, S extends ProgramSpec<P>> P getProgram(S spec) {
return (P) programs.get(spec);
return (P) programs.get(spec).get(GlFog.getFogMode());
}
public static boolean available() {
@ -109,7 +111,7 @@ public class Backend {
if (gl20()) {
programs.values().forEach(GlProgram::delete);
programs.values().forEach(ProgramGroup::delete);
programs.clear();
for (ProgramSpec<?> shader : registry.values()) {
loadProgram(manager, shader);
@ -123,23 +125,39 @@ public class Backend {
}
private static <P extends GlProgram, S extends ProgramSpec<P>> void loadProgram(IResourceManager manager, S programSpec) {
GlShader vert = null;
GlShader frag = null;
try {
vert = loadShader(manager, programSpec.getVert(), ShaderType.VERTEX, programSpec.defines);
frag = loadShader(manager, programSpec.getFrag(), ShaderType.FRAGMENT, programSpec.defines);
Map<GlFogMode, P> programGroup = new EnumMap<>(GlFogMode.class);
GlProgram.Builder builder = GlProgram.builder(programSpec.name).attachShader(vert).attachShader(frag);
for (GlFogMode fogMode : GlFogMode.values()) {
programGroup.put(fogMode, loadProgram(manager, programSpec, fogMode));
}
programSpec.attributes.forEach(builder::addAttribute);
P program = builder.build(programSpec.factory);
programs.put(programSpec, program);
programs.put(programSpec, new ProgramGroup<>(programGroup));
log.info("Loaded program {}", programSpec.name);
} catch (IOException ex) {
log.error("Failed to load program {}", programSpec.name, ex);
return;
}
}
private static <P extends GlProgram, S extends ProgramSpec<P>> P loadProgram(IResourceManager manager, S programSpec, GlFogMode fogMode) throws IOException {
GlShader vert = null;
GlShader frag = null;
try {
ShaderConstants defines = new ShaderConstants(programSpec.defines);
defines.defineAll(fogMode.getDefines());
vert = loadShader(manager, programSpec.getVert(), ShaderType.VERTEX, defines);
frag = loadShader(manager, programSpec.getFrag(), ShaderType.FRAGMENT, defines);
GlProgram.Builder builder = GlProgram.builder(programSpec.name, fogMode).attachShader(vert).attachShader(frag);
programSpec.attributes.forEach(builder::addAttribute);
return builder.build(programSpec.factory);
} finally {
if (vert != null) vert.delete();
if (frag != null) frag.delete();

View file

@ -1,5 +1,6 @@
package com.simibubi.create.foundation.render.backend.gl;
import com.simibubi.create.foundation.render.backend.gl.shader.ProgramFogMode;
import org.lwjgl.opengl.GL20;
import com.simibubi.create.foundation.render.backend.Backend;
@ -14,20 +15,20 @@ public class BasicProgram extends GlProgram {
protected final int uViewProjection;
protected final int uDebug;
protected final int uCameraPos;
protected final int uFogRange;
protected final int uFogColor;
protected final ProgramFogMode fogMode;
protected int uBlockAtlas;
protected int uLightMap;
public BasicProgram(ResourceLocation name, int handle) {
public BasicProgram(ResourceLocation name, int handle, ProgramFogMode.Factory fogFactory) {
super(name, handle);
uTime = getUniformLocation("uTime");
uViewProjection = getUniformLocation("uViewProjection");
uDebug = getUniformLocation("uDebug");
uCameraPos = getUniformLocation("uCameraPos");
uFogRange = getUniformLocation("uFogRange");
uFogColor = getUniformLocation("uFogColor");
fogMode = fogFactory.create(this);
bind();
registerSamplers();
@ -48,8 +49,7 @@ public class BasicProgram extends GlProgram {
uploadMatrixUniform(uViewProjection, viewProjection);
GL20.glUniform3f(uCameraPos, (float) camX, (float) camY, (float) camZ);
GL20.glUniform2f(uFogRange, GlFog.getFogStart(), GlFog.getFogEnd());
GL20.glUniform4fv(uFogColor, GlFog.FOG_COLOR);
fogMode.bind();
}
protected static void uploadMatrixUniform(int uniform, Matrix4f mat) {

View file

@ -1,6 +1,7 @@
package com.simibubi.create.foundation.render.backend.gl;
import com.mojang.blaze3d.platform.GlStateManager;
import org.lwjgl.opengl.GL11;
public class GlFog {
public static float[] FOG_COLOR = new float[] {0, 0, 0, 0};
@ -9,7 +10,7 @@ public class GlFog {
return GlStateManager.FOG.field_179049_a.field_179201_b;
}
public static int getFogMode() {
public static int getFogModeGlEnum() {
return GlStateManager.FOG.field_179047_b;
}
@ -24,4 +25,22 @@ public class GlFog {
public static float getFogStart() {
return GlStateManager.FOG.field_179045_d;
}
public static GlFogMode getFogMode() {
if (!fogEnabled()) {
return GlFogMode.NONE;
}
int mode = getFogModeGlEnum();
switch (mode) {
case GL11.GL_EXP2:
case GL11.GL_EXP:
return GlFogMode.EXP2;
case GL11.GL_LINEAR:
return GlFogMode.LINEAR;
default:
throw new UnsupportedOperationException("Unknown fog mode: " + mode);
}
}
}

View file

@ -0,0 +1,39 @@
package com.simibubi.create.foundation.render.backend.gl;
import com.google.common.collect.Lists;
import com.simibubi.create.foundation.render.backend.gl.shader.GlShader;
import com.simibubi.create.foundation.render.backend.gl.shader.ProgramFogMode;
import org.lwjgl.opengl.GL11;
import java.util.Collections;
import java.util.List;
public enum GlFogMode {
NONE(ProgramFogMode.None::new),
LINEAR(ProgramFogMode.Linear::new, "USE_FOG_LINEAR"),
EXP2(ProgramFogMode.Exp2::new, "USE_FOG_EXP2"),
;
public static final String USE_FOG = "USE_FOG";
private final ProgramFogMode.Factory fogFactory;
private final List<String> defines;
GlFogMode(ProgramFogMode.Factory fogFactory) {
this.fogFactory = fogFactory;
this.defines = Collections.emptyList();
}
GlFogMode(ProgramFogMode.Factory fogFactory, String name) {
this.fogFactory = fogFactory;
this.defines = Lists.newArrayList(USE_FOG, name);
}
public List<String> getDefines() {
return defines;
}
public ProgramFogMode.Factory getFogFactory() {
return fogFactory;
}
}

View file

@ -1,5 +1,6 @@
package com.simibubi.create.foundation.render.backend.gl.shader;
import com.simibubi.create.foundation.render.backend.gl.GlFogMode;
import org.lwjgl.opengl.GL20;
import com.simibubi.create.foundation.render.backend.Backend;
@ -17,8 +18,8 @@ public abstract class GlProgram extends GlObject {
this.name = name;
}
public static Builder builder(ResourceLocation name) {
return new Builder(name);
public static Builder builder(ResourceLocation name, GlFogMode fogMode) {
return new Builder(name, fogMode);
}
public void bind() {
@ -37,7 +38,7 @@ public abstract class GlProgram extends GlObject {
public int getUniformLocation(String uniform) {
int index = GL20.glGetUniformLocation(this.handle(), uniform);
if (index < 0) {
if (index < 0 && Backend.SHADER_DEBUG_OUTPUT) {
Backend.log.warn("No active uniform '{}' exists in program '{}'. Could be unused.", uniform, this.name);
}
@ -69,12 +70,14 @@ public abstract class GlProgram extends GlObject {
public static class Builder {
private final ResourceLocation name;
private final int program;
private final GlFogMode fogMode;
private int attributeIndex;
public Builder(ResourceLocation name) {
public Builder(ResourceLocation name, GlFogMode fogMode) {
this.name = name;
this.program = GL20.glCreateProgram();
this.fogMode = fogMode;
}
public Builder attachShader(GlShader shader) {
@ -113,12 +116,12 @@ public abstract class GlProgram extends GlObject {
throw new RuntimeException("Shader program linking failed, see log for details");
}
return factory.create(this.name, this.program);
return factory.create(this.name, this.program, this.fogMode.getFogFactory());
}
}
@FunctionalInterface
public interface ProgramFactory<P extends GlProgram> {
P create(ResourceLocation name, int handle);
P create(ResourceLocation name, int handle, ProgramFogMode.Factory fogFactory);
}
}

View file

@ -19,6 +19,8 @@ public class GlShader extends GlObject {
if (preProcessor != null) {
source = preProcessor.process(source);
if (Backend.SHADER_DEBUG_OUTPUT)
Backend.log.info("Preprocessor run on " + name);// + ":\n" + source);
}

View file

@ -0,0 +1,57 @@
package com.simibubi.create.foundation.render.backend.gl.shader;
import com.simibubi.create.foundation.render.backend.gl.GlFog;
import org.lwjgl.opengl.GL20;
public abstract class ProgramFogMode {
public abstract void bind();
public static class None extends ProgramFogMode {
public None(GlProgram program) {
}
@Override
public void bind() {
}
}
public static class Linear extends ProgramFogMode {
private final int uFogColor;
private final int uFogRange;
public Linear(GlProgram program) {
this.uFogColor = program.getUniformLocation("uFogColor");
this.uFogRange = program.getUniformLocation("uFogRange");
}
@Override
public void bind() {
GL20.glUniform2f(uFogRange, GlFog.getFogStart(), GlFog.getFogEnd());
GL20.glUniform4fv(uFogColor, GlFog.FOG_COLOR);
}
}
public static class Exp2 extends ProgramFogMode {
private final int uFogColor;
private final int uFogDensity;
public Exp2(GlProgram program) {
this.uFogColor = program.getUniformLocation("uFogColor");
this.uFogDensity = program.getUniformLocation("uFogDensity");
}
@Override
public void bind() {
GL20.glUniform1f(uFogDensity, GlFog.getFogDensity());
GL20.glUniform4fv(uFogColor, GlFog.FOG_COLOR);
}
}
public interface Factory {
ProgramFogMode create(GlProgram program);
}
}

View file

@ -0,0 +1,22 @@
package com.simibubi.create.foundation.render.backend.gl.shader;
import com.simibubi.create.foundation.render.backend.gl.GlFogMode;
import java.util.Map;
public class ProgramGroup<P extends GlProgram> {
private final Map<GlFogMode, P> programs;
public ProgramGroup(Map<GlFogMode, P> programs) {
this.programs = programs;
}
public P get(GlFogMode fogMode) {
return programs.get(fogMode);
}
public void delete() {
programs.values().forEach(GlProgram::delete);
}
}

View file

@ -50,7 +50,7 @@ public class ProgramSpec<P extends GlProgram> {
public static class Builder<P extends GlProgram> {
private ResourceLocation vert;
private ResourceLocation frag;
private ShaderConstants defines = null;
private ShaderConstants defines = ShaderConstants.EMPTY;
private final ResourceLocation name;
private final GlProgram.ProgramFactory<P> factory;

View file

@ -1,12 +1,16 @@
package com.simibubi.create.foundation.render.backend.gl.shader;
import com.google.common.collect.Lists;
import java.io.BufferedReader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class ShaderConstants implements GlShader.PreProcessor {
public static final ShaderConstants EMPTY = new ShaderConstants();
private final ArrayList<String> defines;
@ -14,6 +18,10 @@ public class ShaderConstants implements GlShader.PreProcessor {
defines = new ArrayList<>();
}
public ShaderConstants(ShaderConstants other) {
this.defines = Lists.newArrayList(other.defines);
}
public static ShaderConstants define(String def) {
return new ShaderConstants().def(def);
}
@ -23,6 +31,11 @@ public class ShaderConstants implements GlShader.PreProcessor {
return this;
}
public ShaderConstants defineAll(Collection<String> defines) {
this.defines.addAll(defines);
return this;
}
public ArrayList<String> getDefines() {
return defines;
}

View file

@ -33,7 +33,10 @@ uniform mat4 uViewProjection;
uniform int uDebug;
uniform vec3 uCameraPos;
#if defined(USE_FOG)
varying float FragDistance;
#endif
mat4 rotate(vec3 axis, float angle) {
float s = sin(angle);
@ -71,12 +74,16 @@ void main() {
mat4 normalMat = uModel * localRotation;
BoxCoord = (worldPos.xyz - uLightBoxMin) / uLightBoxSize;
#if defined(USE_FOG)
FragDistance = length(worldPos.xyz);
#endif
#else
mat4 normalMat = localRotation;
#if defined(USE_FOG)
FragDistance = length(worldPos.xyz - uCameraPos);
#endif
#endif
vec3 norm = normalize(normalMat * vec4(aNormal, 0.)).xyz;

View file

@ -4,17 +4,35 @@ varying vec2 TexCoords;
varying vec4 Color;
varying float Diffuse;
varying vec2 Light;
varying float FragDistance;
varying vec3 BoxCoord;
uniform vec2 uFogRange;
uniform vec4 uFogColor;
uniform sampler2D uBlockAtlas;
uniform sampler2D uLightMap;
uniform sampler3D uLightVolume;
#if defined(USE_FOG)
varying float FragDistance;
uniform vec4 uFogColor;
#endif
#if defined(USE_FOG_LINEAR)
uniform vec2 uFogRange;
float fogFactor() {
return (uFogRange.y - FragDistance) / (uFogRange.y - uFogRange.x);
}
#endif
#ifdef USE_FOG_EXP2
uniform float uFogDensity;
float fogFactor() {
float dist = FragDistance * uFogDensity;
return 1. / exp2(dist * dist);
}
#endif
vec4 light() {
vec2 lm = texture3D(uLightVolume, BoxCoord).rg * 0.9375 + 0.03125;
return texture2D(uLightMap, max(lm, Light));
@ -25,9 +43,12 @@ void main() {
vec4 color = vec4(tex.rgb * light().rgb * Diffuse * Color.rgb, tex.a);
float fog = (uFogRange.y - FragDistance) / (uFogRange.y - uFogRange.x);
fog = clamp(fog, 0., 1.);
#if defined(USE_FOG)
float fog = clamp(fogFactor(), 0., 1.);
gl_FragColor = mix(uFogColor, color, fog);
gl_FragColor.a = color.a;
#else
gl_FragColor = color;
#endif
}

View file

@ -29,7 +29,10 @@ uniform mat4 uViewProjection;
uniform int uDebug;
uniform vec3 uCameraPos;
#if defined(USE_FOG)
varying float FragDistance;
#endif
mat4 rotate(vec3 axis, float angle) {
float s = sin(angle);
@ -77,9 +80,12 @@ void main() {
Diffuse = diffuse(norm);
TexCoords = aTexCoords;
Light = aModelLight;
FragDistance = length(worldPos.xyz);
gl_Position = uViewProjection * worldPos;
#if defined(USE_FOG)
FragDistance = length(worldPos.xyz);
#endif
if (uDebug == 2) {
Color = vec4(norm, 1.);
} else {

View file

@ -22,7 +22,10 @@ uniform mat4 uViewProjection;
uniform int uDebug;
uniform vec3 uCameraPos;
#if defined(USE_FOG)
varying float FragDistance;
#endif
mat4 rotate(vec3 axis, float angle) {
float s = sin(angle);
@ -52,8 +55,10 @@ void main() {
Color = aColor / diffuse(aNormal);
TexCoords = aTexCoords;
Light = aModelLight;
FragDistance = length(viewPos.xyz);
gl_Position = uViewProjection * viewPos;
#if defined(USE_FOG)
FragDistance = length(viewPos.xyz);
#endif
if (uDebug == 2) {
Color = vec4(norm, 1.);

View file

@ -35,7 +35,10 @@ uniform mat4 uViewProjection;
uniform int uDebug;
uniform vec3 uCameraPos;
#if defined(USE_FOG)
varying float FragDistance;
#endif
float diffuse(vec3 normal) {
float x = normal.x;
@ -86,12 +89,16 @@ void main() {
mat4 normalMat = uModel * orientation * flapRotation;
BoxCoord = (worldPos.xyz - uLightBoxMin) / uLightBoxSize;
#if defined(USE_FOG)
FragDistance = length(worldPos.xyz);
#endif
#else
mat4 normalMat = orientation * flapRotation;
#if defined(USE_FOG)
FragDistance = length(worldPos.xyz - uCameraPos);
#endif
#endif
vec3 norm = normalize(normalMat * vec4(aNormal, 0.)).xyz;

View file

@ -4,14 +4,32 @@ varying vec2 TexCoords;
varying vec2 Light;
varying float Diffuse;
varying vec4 Color;
varying float FragDistance;
uniform vec2 uFogRange;
uniform vec4 uFogColor;
uniform sampler2D uBlockAtlas;
uniform sampler2D uLightMap;
#if defined(USE_FOG)
varying float FragDistance;
uniform vec4 uFogColor;
#endif
#if defined(USE_FOG_LINEAR)
uniform vec2 uFogRange;
float fogFactor() {
return (uFogRange.y - FragDistance) / (uFogRange.y - uFogRange.x);
}
#endif
#ifdef USE_FOG_EXP2
uniform float uFogDensity;
float fogFactor() {
float dist = FragDistance * uFogDensity;
return 1. / exp2(dist * dist);
}
#endif
vec4 light() {
vec2 lm = Light * 0.9375 + 0.03125;
return texture2D(uLightMap, lm);
@ -22,9 +40,12 @@ void main() {
vec4 color = vec4(tex.rgb * light().rgb * Diffuse, tex.a) * Color;
float fog = (uFogRange.y - FragDistance) / (uFogRange.y - uFogRange.x);
fog = clamp(fog, 0., 1.);
#if defined(USE_FOG)
float fog = clamp(fogFactor(), 0., 1.);
gl_FragColor = mix(uFogColor, color, fog);
gl_FragColor.a = color.a;
#else
gl_FragColor = color;
#endif
}

View file

@ -29,7 +29,10 @@ uniform mat4 uViewProjection;
uniform int uDebug;
uniform vec3 uCameraPos;
#if defined(USE_FOG)
varying float FragDistance;
#endif
mat4 rotate(vec3 axis, float angle) {
float s = sin(angle);
@ -69,12 +72,16 @@ void main() {
mat4 normalMat = uModel * kineticRotation;
BoxCoord = (worldPos.xyz - uLightBoxMin) / uLightBoxSize;
#if defined(USE_FOG)
FragDistance = length(worldPos.xyz);
#endif
#else
mat4 normalMat = kineticRotation;
#if defined(USE_FOG)
FragDistance = length(worldPos.xyz - uCameraPos);
#endif
#endif
vec3 norm = normalize(normalMat * vec4(aNormal, 0.)).xyz;