From a09bf4efd85d248c44f73a08c9b9874e96bde139 Mon Sep 17 00:00:00 2001 From: vinceh121 Date: Sat, 2 Dec 2023 15:21:41 +0100 Subject: [PATCH] feat: rework sky shader keyframing --- android/assets/shaders/sky.frag | 132 ++---------------- android/assets/skies.json | 58 ++++++-- .../vinceh121/wanderer/glx/SkyProperties.java | 71 +++++++--- .../me/vinceh121/wanderer/glx/SkyShader.java | 48 +++++-- .../wanderer/glx/SkyboxRenderer.java | 96 +++---------- .../vinceh121/wanderer/ui/DebugOverlay.java | 7 +- 6 files changed, 164 insertions(+), 248 deletions(-) diff --git a/android/assets/shaders/sky.frag b/android/assets/shaders/sky.frag index 949b984..4f99b92 100644 --- a/android/assets/shaders/sky.frag +++ b/android/assets/shaders/sky.frag @@ -1,90 +1,18 @@ precision highp float; -#define MORNING 0.0 -#define NOON 0.25 -#define EVENING_START 0.375 -#define EVENING_MID 0.4375 -#define EVENING_END 0.5 -#define MIDNIGHT_START 0.625 -#define MIDNIGHT 0.75 +uniform vec4 skyTop; +uniform vec4 skyMiddle; +uniform vec4 skyBottom; -const vec3 skyTopNoon = vec3(0.09, 0.106, 0.129); -const vec3 skyMiddleNoon = vec3(0.741, 0.871, 0.882); -const vec3 skyBottomNoon = vec3(0.62, 0.663, 0.627); - -const vec3 skyTopEvening = skyTopNoon; -const vec3 skyMiddleEvening = vec3(0.949, 0.851, 0.263); -const vec3 skyBottomEvening = skyBottomNoon; - -const vec3 skyTopMidnight = vec3(0.054902, 0.086275, 0.121569); -const vec3 skyMiddleMidnight = vec3(0.266667, 0.321569, 0.337255); -const vec3 skyBottomMidnight = vec3(0.141176, 0.145098, 0.149020); - -uniform float time; uniform vec3 sunDir; in vec4 vertPos; -float logFrac(float f) { - return log2(f * 255 + 1) / 8; -} - -float progress(float start, float end) { - return (time - start) / (end - start); -} - float angle; float sunSkyAngle; -vec3 topMorning() { - return mix(skyTopNoon, skyMiddleNoon, - logFrac(angle) * progress(MORNING, NOON)); -} - -vec3 bottomMorning() { - return mix(mix(skyTopNoon, skyBottomNoon, progress(MORNING, NOON)), - topMorning(), logFrac(angle)); -} - -vec3 topNoon() { - return mix(skyTopNoon, skyMiddleNoon, logFrac(angle)); -} - -vec3 bottomNoon() { - return mix(skyBottomNoon, topNoon(), angle); -} - -vec3 topEveningStart() { - return mix(skyTopEvening, skyMiddleEvening, angle); -} - -vec3 bottomEveningStart() { - return mix(skyBottomNoon, topEveningStart(), angle); -} - -vec3 topEveningMid() { - return mix(skyTopEvening, skyMiddleEvening, angle * progress(EVENING_MID, - EVENING_END) * (1 - logFrac(sunSkyAngle))); -} - -vec3 bottomEveningMid() { - return mix(skyBottomEvening, topEveningMid(), angle); -} - -vec3 topMidnightStart() { - return mix(skyTopMidnight, skyMiddleMidnight, 1 - sunSkyAngle); -} - -vec3 bottomMidnightStart() { - return mix(skyBottomMidnight, topMidnightStart(), angle); -} - -vec3 topMidnight() { - return mix(skyTopMidnight, skyMiddleMidnight, angle); -} - -vec3 bottomMidnight() { - return mix(skyBottomMidnight, skyMiddleMidnight, angle); +float logFrac(float f) { + return log2(f * 255 + 1) / 8; } void main() { @@ -96,56 +24,14 @@ void main() { angle = 1 - angle; sunSkyAngle = dot(normVert, sunDir); sunSkyAngle = 1 - sunSkyAngle; - if (time > MORNING && time < NOON) { - gl_FragColor = vec4(topMorning(), 1); - } else if (time > NOON && time < EVENING_START) { - gl_FragColor = vec4( - mix(topMorning(), topNoon(), progress(NOON, EVENING_START)), - 1); - } else if (time > EVENING_START && time < EVENING_MID) { - gl_FragColor = vec4( - mix(topNoon(), topEveningStart(), - progress(EVENING_START, EVENING_MID)), 1); - } else if (time > EVENING_MID && time < EVENING_END) { - gl_FragColor = vec4( - mix(topEveningStart(), topEveningMid(), - progress(EVENING_MID, EVENING_END)), 1); - } else if (time > EVENING_END && time < MIDNIGHT_START) { - gl_FragColor = vec4( - mix(topEveningMid(), topMidnightStart(), - progress(EVENING_END, MIDNIGHT_START)), 1); - } else if (time > MIDNIGHT_START && time < MIDNIGHT) { - gl_FragColor = vec4( - mix(topMidnightStart(), topMidnight(), - progress(MIDNIGHT_START, MIDNIGHT)), 1); - } + + gl_FragColor = mix(skyTop, skyMiddle, logFrac(angle)); } else { // bottom half angle += 1; // need to invert one of those dirs sunSkyAngle = dot(-1 * normVert, sunDir); sunSkyAngle += 1; - if (time > MORNING && time < NOON) { - gl_FragColor = vec4(bottomMorning(), 1); - } else if (time > NOON && time < EVENING_START) { - gl_FragColor = vec4( - mix(bottomMorning(), bottomNoon(), - progress(NOON, EVENING_START)), 1); - } else if (time > EVENING_START && time < EVENING_MID) { - gl_FragColor = vec4( - mix(bottomNoon(), bottomEveningStart(), - progress(EVENING_START, EVENING_MID)), 1); - } else if (time > EVENING_MID && time < EVENING_END) { - gl_FragColor = vec4( - mix(bottomEveningStart(), bottomEveningMid(), - progress(EVENING_MID, EVENING_END)), 1); - } else if (time > EVENING_END && time < MIDNIGHT_START) { - gl_FragColor = vec4( - mix(bottomEveningMid(), bottomMidnightStart(), - progress(EVENING_END, MIDNIGHT_START)), 1); - } else if (time > MIDNIGHT_START && time < MIDNIGHT) { - gl_FragColor = vec4( - mix(bottomMidnightStart(), bottomMidnight(), - progress(MIDNIGHT_START, MIDNIGHT)), 1); - } + + gl_FragColor = mix(skyMiddle, skyBottom, logFrac(angle)); } } diff --git a/android/assets/skies.json b/android/assets/skies.json index 76dd741..36dc59d 100644 --- a/android/assets/skies.json +++ b/android/assets/skies.json @@ -3,37 +3,37 @@ "sunColor": { }, "sunLightColor": { - "MORNING": { + "0": { "r": 0.949, "g": 0.851, "b": 0.263, "a": 1.0 }, - "NOON": { + "0.25": { "r": 0.8, "g": 0.8, "b": 0.8, "a": 1.0 }, - "EVENING_START": { + "0.375": { "r": 0.849, "g": 0.79, "b": 0.763, "a": 1.0 }, - "EVENING_MID": { + "0.4375": { "r": 0.949, "g": 0.851, "b": 0.263, "a": 1.0 }, - "EVENING_END": { + "0.5": { "r": 0.949, "g": 0.851, "b": 0.263, "a": 1.0 }, - "MIDNIGHT_START": { + "0.625": { "r": 0.949, "g": 0.851, "b": 0.263, @@ -41,31 +41,31 @@ } }, "ambLightColor": { - "MORNING": { + "0": { "r": 0.4, "g": 0.4, "b": 0.4, "a": 1.0 }, - "NOON": { + "0.25": { "r": 0.6, "g": 0.6, "b": 0.6, "a": 1.0 }, - "MIDNIGHT_START": { + "0.625": { "r": 0.5, "g": 0.5, "b": 0.5, "a": 1.0 }, - "MIDNIGHT": { + "0.75": { "r": 0.3, "g": 0.3, "b": 0.3, "a": 1.0 }, - "NIGHT_END": { + "0.875": { "r": 0.5, "g": 0.5, "b": 0.5, @@ -73,8 +73,44 @@ } }, "skyTopColor": { + "0": { + "r": 0.09, + "g": 0.106, + "b": 0.129, + "a": 1.0 + }, + "0.5": { + "r": 0.09, + "g": 0.106, + "b": 0.129, + "a": 1.0 + }, + "0.75": { + "r": 0.054902, + "g": 0.086275, + "b": 0.121569, + "a": 1.0 + } }, "skyMiddleColor": { + "0": { + "r": 0.741, + "g": 0.871, + "b": 0.882, + "a": 1.0 + }, + "0.5": { + "r": 0.949, + "g": 0.851, + "b": 0.263, + "a": 1.0 + }, + "0.75": { + "r": 0.266667, + "g": 0.321569, + "b": 0.337255, + "a": 1.0 + } }, "skyBottomColor": { } diff --git a/core/src/me/vinceh121/wanderer/glx/SkyProperties.java b/core/src/me/vinceh121/wanderer/glx/SkyProperties.java index 89408dc..61c752d 100644 --- a/core/src/me/vinceh121/wanderer/glx/SkyProperties.java +++ b/core/src/me/vinceh121/wanderer/glx/SkyProperties.java @@ -1,41 +1,68 @@ package me.vinceh121.wanderer.glx; -import java.util.EnumMap; -import java.util.Map; +import java.util.NavigableMap; +import java.util.Objects; +import java.util.TreeMap; import com.badlogic.gdx.graphics.Color; -import me.vinceh121.wanderer.glx.SkyboxRenderer.TimeRange; - public class SkyProperties { - private final Map sunColor = new EnumMap<>(TimeRange.class); - private final Map sunLightColor = new EnumMap<>(TimeRange.class); - private final Map ambLightColor = new EnumMap<>(TimeRange.class); - private final Map skyTopColor = new EnumMap<>(TimeRange.class); - private final Map skyMiddleColor = new EnumMap<>(TimeRange.class); - private final Map skyBottomColor = new EnumMap<>(TimeRange.class); + private final NavigableMap sunColor = new TreeMap<>(); + private final NavigableMap sunLightColor = new TreeMap<>(); + private final NavigableMap ambLightColor = new TreeMap<>(); + private final NavigableMap skyTopColor = new TreeMap<>(); + private final NavigableMap skyMiddleColor = new TreeMap<>(); + private final NavigableMap skyBottomColor = new TreeMap<>(); + + public NavigableMap getSunColor() { + return sunColor; + } + + public NavigableMap getSunLightColor() { + return sunLightColor; + } + + public NavigableMap getAmbLightColor() { + return ambLightColor; + } - public Map getSunColor() { - return this.sunColor; + public NavigableMap getSkyTopColor() { + return skyTopColor; } - public Map getSunLightColor() { - return this.sunLightColor; + public NavigableMap getSkyMiddleColor() { + return skyMiddleColor; } - public Map getAmbLightColor() { - return this.ambLightColor; + public NavigableMap getSkyBottomColor() { + return skyBottomColor; } - public Map getSkyTopColor() { - return this.skyTopColor; + @Override + public int hashCode() { + return Objects.hash(ambLightColor, skyBottomColor, skyMiddleColor, skyTopColor, sunColor, sunLightColor); } - public Map getSkyMiddleColor() { - return this.skyMiddleColor; + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + SkyProperties other = (SkyProperties) obj; + return Objects.equals(ambLightColor, other.ambLightColor) + && Objects.equals(skyBottomColor, other.skyBottomColor) + && Objects.equals(skyMiddleColor, other.skyMiddleColor) + && Objects.equals(skyTopColor, other.skyTopColor) && Objects.equals(sunColor, other.sunColor) + && Objects.equals(sunLightColor, other.sunLightColor); } - public Map getSkyBottomColor() { - return this.skyBottomColor; + @Override + public String toString() { + return "SkyProperties [sunColor=" + sunColor + ", sunLightColor=" + sunLightColor + ", ambLightColor=" + + ambLightColor + ", skyTopColor=" + skyTopColor + ", skyMiddleColor=" + skyMiddleColor + + ", skyBottomColor=" + skyBottomColor + "]"; } } diff --git a/core/src/me/vinceh121/wanderer/glx/SkyShader.java b/core/src/me/vinceh121/wanderer/glx/SkyShader.java index b0d508c..5f09dcd 100644 --- a/core/src/me/vinceh121/wanderer/glx/SkyShader.java +++ b/core/src/me/vinceh121/wanderer/glx/SkyShader.java @@ -1,26 +1,34 @@ package me.vinceh121.wanderer.glx; +import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.g3d.Attributes; import com.badlogic.gdx.graphics.g3d.Renderable; import com.badlogic.gdx.graphics.g3d.Shader; import com.badlogic.gdx.math.Vector3; public class SkyShader extends WandererShader { - public final int u_time, u_sunDir; - private float timeOfDay; + public final int u_sunDir, u_skyTop, u_skyMiddle, u_skyBottom; private final Vector3 sunDir = new Vector3(0, 1, 0); + private final Color skyTop = new Color(); + private final Color skyMiddle = new Color(); + private final Color skyBottom = new Color(); public SkyShader(final Renderable renderable, final Config config) { super(renderable, config); - this.u_time = this.register("time"); this.u_sunDir = this.register("sunDir"); + this.u_skyTop = this.register("skyTop"); + this.u_skyMiddle = this.register("skyMiddle"); + this.u_skyBottom = this.register("skyBottom"); } @Override public void render(final Renderable renderable, final Attributes combinedAttributes) { - this.set(this.u_time, this.timeOfDay); this.set(this.u_sunDir, this.sunDir); + this.set(this.u_skyTop, this.skyTop); + this.set(this.u_skyMiddle, this.skyMiddle); + this.set(this.u_skyBottom, this.skyBottom); + super.render(renderable, combinedAttributes); } @@ -34,14 +42,6 @@ public boolean canRender(final Renderable instance) { return true; } - public float getTimeOfDay() { - return this.timeOfDay; - } - - public void setTimeOfDay(final float timeOfDay) { - this.timeOfDay = timeOfDay; - } - public Vector3 getSunDir() { return this.sunDir; } @@ -49,4 +49,28 @@ public Vector3 getSunDir() { public void setSunDir(final Vector3 sunDir) { this.sunDir.set(sunDir); } + + public Color getSkyTop() { + return skyTop; + } + + public void setSkyTop(Color skyTop) { + this.skyTop.set(skyTop); + } + + public Color getSkyMiddle() { + return skyMiddle; + } + + public void setSkyMiddle(Color skyMiddle) { + this.skyMiddle.set(skyMiddle); + } + + public Color getSkyBottom() { + return skyBottom; + } + + public void setSkyBottom(Color skyBottm) { + this.skyBottom.set(skyBottom); + } } diff --git a/core/src/me/vinceh121/wanderer/glx/SkyboxRenderer.java b/core/src/me/vinceh121/wanderer/glx/SkyboxRenderer.java index c46d90a..8705a4f 100644 --- a/core/src/me/vinceh121/wanderer/glx/SkyboxRenderer.java +++ b/core/src/me/vinceh121/wanderer/glx/SkyboxRenderer.java @@ -1,9 +1,10 @@ package me.vinceh121.wanderer.glx; import java.io.IOException; -import java.util.Collection; import java.util.Hashtable; import java.util.Map; +import java.util.Map.Entry; +import java.util.NavigableMap; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.Color; @@ -88,18 +89,19 @@ public void update(final float time) { final float delta = time - this.previous; this.previous = time; - if (this.shader != null) { - this.shader.setTimeOfDay(time); - } - this.stars.transform.rotateRad(Vector3.Y, 0.02f * delta / 0.016666668f); this.move(this.sun, MathUtils.PI * 0.65f, time * MathUtils.PI2, 0.6f, 0); this.sunDir.setFromSpherical(MathUtils.PI * 0.65f, time * MathUtils.PI2); + if (this.shader != null) { // shader needs sunDir to NOT be inverted this.shader.setSunDir(this.sunDir); + this.shader.setSkyTop(this.interpolatedColor(time, this.skyProperties.getSkyTopColor())); + this.shader.setSkyMiddle(this.interpolatedColor(time, this.skyProperties.getSkyMiddleColor())); + this.shader.setSkyBottom(this.interpolatedColor(time, this.skyProperties.getSkyBottomColor())); } + this.sunDir.scl(-1); this.move(this.mars, MathUtils.PI2 * time, 0.12f * MathUtils.PI2, 1f, 0); @@ -118,49 +120,28 @@ public void update(final float time) { this.ambiantLight.color.set(this.interpolatedColor(time, this.skyProperties.getAmbLightColor())); } - private Color interpolatedColor(final float time, final Map colors) { - final TimeRange range = this.currentTimeRange(time, colors.keySet()); - if (range == null) { - return new Color(); + private Color interpolatedColor(final float time, final NavigableMap colors) { + // only way left is null is that time is negative, which shouldn't happen + final Entry left = colors.floorEntry(time); + + Entry right = colors.ceilingEntry(time); + + if (right == null) { // wrap around + right = colors.firstEntry(); } - final TimeRange nextRange = this.nextTimeRange(range.ordinal(), colors.keySet()); - if (nextRange == null) { - return new Color(); + if (right == null || left == null) { + return Color.BLACK; } - final float alpha = (time - range.getRangeStart()) / (range.getRangeEnd() - range.getRangeStart()); + final float alpha = (time - left.getKey()) / (right.getKey() - left.getKey()); - final Color c = colors.get(range).cpy(); - c.lerp(colors.get(nextRange), alpha); + final Color c = left.getValue().cpy(); + c.lerp(right.getValue(), alpha); return c; } - private TimeRange currentTimeRange(final float time, final Collection ranges) { - final TimeRange range = SkyboxRenderer.getTimeRange(time); - if (ranges.contains(range)) { - return range; - } - for (int i = range.ordinal() - 1; i >= 0; i--) { - final TimeRange t = TimeRange.values()[i]; - if (ranges.contains(t)) { - return t; - } - } - return null; - } - - private TimeRange nextTimeRange(final int start, final Collection ranges) { - for (int i = start + 1; i < TimeRange.values().length + 1; i++) { - final TimeRange r = TimeRange.values()[i % TimeRange.values().length]; - if (ranges.contains(r)) { - return r; - } - } - return null; - } - public void setSkycapTexture(final String tex) { final Texture texture = WandererConstants.ASSET_MANAGER.get(tex); texture.setFilter(TextureFilter.MipMapLinearLinear, TextureFilter.Linear); @@ -208,7 +189,6 @@ protected Shader createShader(final Renderable renderable) { final SkyShader s = new SkyShader(renderable, new Config(Gdx.files.internal("shaders/sky.vert").readString(), Gdx.files.internal("shaders/sky.frag").readString())); - s.setTimeOfDay(SkyboxRenderer.this.previous); s.setSunDir(SkyboxRenderer.this.sunDir); SkyboxRenderer.this.shader = s; return s; @@ -352,40 +332,4 @@ public DirectionalShadowLight getSunLight() { public DirectionalLight getMoonLight() { return this.moonLight; } - - public static TimeRange getTimeRange(final float time) { - for (final TimeRange r : TimeRange.values()) { - if (time <= r.getRangeEnd()) { - return r; - } - } - return null; - } - - public enum TimeRange { - MORNING(0, 0.25f), - NOON(0.25f, 0.375f), - EVENING_START(0.375f, 0.4375f), - EVENING_MID(0.4375f, 0.5f), - EVENING_END(0.5f, 0.625f), - MIDNIGHT_START(0.625f, 0.75f), - MIDNIGHT(0.75f, 0.875f), - /// .... - NIGHT_END(0.875f, 1f); - - private final float rangeStart, rangeEnd; - - TimeRange(final float rangeStart, final float rangeEnd) { - this.rangeStart = rangeStart; - this.rangeEnd = rangeEnd; - } - - public float getRangeStart() { - return this.rangeStart; - } - - public float getRangeEnd() { - return this.rangeEnd; - } - } } diff --git a/core/src/me/vinceh121/wanderer/ui/DebugOverlay.java b/core/src/me/vinceh121/wanderer/ui/DebugOverlay.java index 5722420..faf32a9 100644 --- a/core/src/me/vinceh121/wanderer/ui/DebugOverlay.java +++ b/core/src/me/vinceh121/wanderer/ui/DebugOverlay.java @@ -11,7 +11,6 @@ import me.vinceh121.wanderer.Wanderer; import me.vinceh121.wanderer.WandererConstants; import me.vinceh121.wanderer.entity.AbstractEntity; -import me.vinceh121.wanderer.glx.SkyboxRenderer; public class DebugOverlay extends Table { private final Wanderer game; @@ -38,6 +37,7 @@ public DebugOverlay(final Wanderer game) { public void act(final float delta) { this.lblFps.setText("FPS: " + Gdx.graphics.getFramesPerSecond()); this.lblEntities.setText("Entities: " + this.game.getEntities().size); + if (this.game.getControlledEntity() instanceof AbstractEntity) {// doubles as null-check this.lblCoords.setText("Coords (controlled): " + ((AbstractEntity) this.game.getControlledEntity()).getTransform().getTranslation(new Vector3()) @@ -45,9 +45,8 @@ public void act(final float delta) { } else { this.lblCoords.setText("Coords (camera): " + this.game.getCamera().position); } - this.lblTime.setText(String.format("Time: %.2f%% %s", - this.game.getTimeOfDay() * 100, - SkyboxRenderer.getTimeRange(this.game.getTimeOfDay()).name())); + + this.lblTime.setText(String.format("Time: %.2f%%", this.game.getTimeOfDay() * 100)); if (this.game instanceof StoryWanderer) { try {