Last active
August 26, 2019 00:42
-
-
Save rafaskb/1a57758f9ce1001f0f59464fb77788ba to your computer and use it in GitHub Desktop.
Distorted TV Effect for libGDX and libgdx-postprocessing-contribs or gdx-vfx. Example: https://www.shadertoy.com/view/Mt2XDV
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/****************************************************************************** | |
* Shader borrowed from ehj1 and adapted to LibGDX's format, with a few changes. | |
* Noise generation functions borrowed from ashima. | |
* | |
* Shader: Distorted TV, by ehj1 (https://www.shadertoy.com/view/ldXGW4) | |
* Noise functions: https://github.com/ashima/webgl-noise/blob/master/src/noise2D.glsl | |
******************************************************************************/ | |
#ifdef GL_ES | |
#define PRECISION mediump | |
precision PRECISION float; | |
#else | |
#define PRECISION | |
#endif | |
const float EPSILON = 0.00001; | |
uniform PRECISION sampler2D u_texture0; | |
uniform float u_time; | |
#ifdef ENABLE_VERTICAL_JERK | |
uniform float u_vertJerkOpt; | |
#endif | |
#ifdef ENABLE_VERTICAL_MOVEMENT | |
uniform float u_vertMovementOpt; | |
#endif | |
#ifdef ENABLE_BOTTOM_STATIC | |
uniform float u_bottomStaticOpt; | |
#endif | |
#ifdef ENABLE_SCANLINES | |
uniform float u_scalinesOpt; | |
#endif | |
#ifdef ENABLE_RGB_OFFSET | |
uniform float u_rgbOffsetOpt; | |
#endif | |
#ifdef ENABLE_HORIZONTAL_FUZZ | |
uniform float u_horzFuzzOpt; | |
#endif | |
varying vec2 v_texCoords; | |
vec3 mod289(vec3 x) { | |
return x - floor(x * (1.0 / 289.0)) * 289.0; | |
} | |
vec2 mod289(vec2 x) { | |
return x - floor(x * (1.0 / 289.0)) * 289.0; | |
} | |
vec3 permute(vec3 x) { | |
return mod289(((x * 34.0) + 1.0) * x); | |
} | |
float snoise(vec2 v) { | |
const vec4 C = vec4(0.211324865405187, // (3.0-sqrt(3.0))/6.0 | |
0.366025403784439, // 0.5*(sqrt(3.0)-1.0) | |
-0.577350269189626, // -1.0 + 2.0 * C.x | |
0.024390243902439); // 1.0 / 41.0 | |
// First corner | |
vec2 i = floor(v + dot(v, C.yy)); | |
vec2 x0 = v - i + dot(i, C.xx); | |
// Other corners | |
vec2 i1; | |
//i1.x = step( x0.y, x0.x ); // x0.x > x0.y ? 1.0 : 0.0 | |
//i1.y = 1.0 - i1.x; | |
i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0); | |
// x0 = x0 - 0.0 + 0.0 * C.xx ; | |
// x1 = x0 - i1 + 1.0 * C.xx ; | |
// x2 = x0 - 1.0 + 2.0 * C.xx ; | |
vec4 x12 = x0.xyxy + C.xxzz; | |
x12.xy -= i1; | |
// Permutations | |
i = mod289(i); // Avoid truncation effects in permutation | |
vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 )) + i.x + vec3(0.0, i1.x, 1.0 )); | |
vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0); | |
m = m*m; | |
m = m*m; | |
// Gradients: 41 points uniformly over a line, mapped onto a diamond. | |
// The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287) | |
vec3 x = 2.0 * fract(p * C.www) - 1.0; | |
vec3 h = abs(x) - 0.5; | |
vec3 ox = floor(x + 0.5); | |
vec3 a0 = x - ox; | |
// Normalise gradients implicitly by scaling m | |
// Approximation of: m *= inversesqrt( a0*a0 + h*h ); | |
m *= 1.79284291400159 - 0.85373472095314 * (a0 * a0 + h * h); | |
// Compute final noise value at P | |
vec3 g; | |
g.x = a0.x * x0.x + h.x * x0.y; | |
g.yz = a0.yz * x12.xz + h.yz * x12.yw; | |
return 130.0 * dot(m, g); | |
} | |
float fnoise(vec2 v) { | |
return fract(sin(dot(v, vec2(12.9898, 78.233))) * 43758.5453) * 0.55; | |
} | |
#ifdef ENABLE_BOTTOM_STATIC | |
float staticV(vec2 uv) { | |
float staticHeight = fnoise(vec2(9.0, u_time * 1.2 + 3.0)) * 0.3 + 5.0; | |
float staticAmount = fnoise(vec2(1.0, u_time * 1.2 - 6.0)) * 0.1 + 0.3; | |
float staticStrength = fnoise(vec2(-9.75, u_time * 0.6 - 3.0)) * 2.0 + 2.0; | |
return (1.0 - step(snoise(vec2(5.0 * pow(u_time, 2.0) + pow(uv.x * 7.0, 1.2), pow((mod(u_time, 100.0) + 100.0) * uv.y * 0.3 + 3.0, staticHeight))), staticAmount)) * staticStrength; | |
} | |
#endif | |
void main() { | |
// Common variables | |
vec2 uv = v_texCoords; | |
float xOffset = 0.0; | |
float yOffset = 0.0; | |
float y = 0.0; | |
// Module: Vertical jerk and movement | |
float vertMovementOn = 0.0; | |
float vertJerk = 0.0; | |
float vertJerk2 = 0.0; | |
#ifdef ENABLE_VERTICAL_JERK | |
vertJerk = (1.0 - step(fnoise(vec2(u_time * 1.5, 5.0)), 0.6)) * u_vertJerkOpt; | |
vertJerk2 = (1.0 - step(fnoise(vec2(u_time * 5.5, 5.0)), 0.2)) * u_vertJerkOpt; | |
#endif | |
#ifdef ENABLE_VERTICAL_MOVEMENT | |
vertMovementOn = (1.0 - step(snoise(vec2(u_time * 0.2, 8.0)), 0.4)) * u_vertMovementOpt; | |
#endif | |
#if defined(ENABLE_VERTICAL_JERK) || defined(ENABLE_VERTICAL_MOVEMENT) | |
yOffset = abs(sin(u_time) * 4.0) * vertMovementOn + vertJerk * vertJerk2 * 0.3; | |
#endif | |
y = mod(uv.y + yOffset, 1.0); | |
// Module: Horizontal Fuzz | |
#ifdef ENABLE_HORIZONTAL_FUZZ | |
float fuzzOffset = fnoise(vec2(u_time * 15.0, uv.y * 80.0)) * 0.003; | |
float largeFuzzOffset = fnoise(vec2(u_time * 1.0, uv.y * 25.0)) * 0.004; | |
xOffset = (fuzzOffset + largeFuzzOffset) * u_horzFuzzOpt; | |
#endif | |
// Sample color from texture | |
vec4 color = texture2D(u_texture0, vec2(uv.x + xOffset, y)); | |
// Module: RGB Offset | |
#ifdef ENABLE_RGB_OFFSET | |
color.r = texture2D(u_texture0, vec2(uv.x + xOffset - 0.01 * u_rgbOffsetOpt, y)).r; | |
color.b = texture2D(u_texture0, vec2(uv.x + xOffset + 0.01 * u_rgbOffsetOpt, y)).b; | |
#endif | |
// Module: Bottom Static | |
#ifdef ENABLE_BOTTOM_STATIC | |
float staticVal = 0.0; | |
for (float y = -1.0; y <= 1.0; y += 1.0) { | |
float maxDist = 5.0 / 200.0; | |
float dist = y / 200.0; | |
staticVal += staticV(vec2(uv.x, uv.y + dist)) * (maxDist - abs(dist)) * 1.5; | |
} | |
staticVal *= u_bottomStaticOpt; | |
color.rgb += staticVal; | |
#endif | |
// Module: Scanlines | |
#ifdef ENABLE_SCANLINES | |
color.rgb -= sin(uv.y * 800.0) * 0.04 * u_scalinesOpt; | |
#endif | |
// Apply final color to fragment | |
gl_FragColor = color; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.grashers.core.postprocessing; | |
import com.badlogic.gdx.graphics.glutils.FrameBuffer; | |
import com.bitfire.postprocessing.PostProcessorEffect; | |
public final class DistortedTvEffect extends PostProcessorEffect { | |
private DistortedTvFilter distortion; | |
public DistortedTvEffect(int effectsSupport) { | |
distortion = new DistortedTvFilter(effectsSupport); | |
} | |
@Override | |
public void dispose() { | |
distortion.dispose(); | |
} | |
// setters | |
@Override | |
public void setTime(float elapsedSecs) { | |
distortion.setTime(elapsedSecs); | |
} | |
/** Sets this effect's value. Use {@code 0.0} to disable it, {@code 1.0} for a normal effect and any positive value for a | |
* custom result. */ | |
public void setVerticalJerk(float value) { | |
distortion.setVerticalJerk(value); | |
} | |
/** Sets this effect's value. Use {@code 0.0} to disable it, {@code 1.0} for a normal effect and any positive value for a | |
* custom result. */ | |
public void setVerticalMovement(float value) { | |
distortion.setVerticalMovement(value); | |
} | |
/** Sets this effect's value. Use {@code 0.0} to disable it, {@code 1.0} for a normal effect and any positive value for a | |
* custom result. */ | |
public void setBottomStatic(float value) { | |
distortion.setBottomStatic(value); | |
} | |
/** Sets this effect's value. Use {@code 0.0} to disable it, {@code 1.0} for a normal effect and any positive value for a | |
* custom result. */ | |
public void setScanlines(float value) { | |
distortion.setScanlines(value); | |
} | |
/** Sets this effect's value. Use {@code 0.0} to disable it, {@code 1.0} for a normal effect and any positive value for a | |
* custom result. */ | |
public void setRgbOffset(float value) { | |
distortion.setRgbOffset(value); | |
} | |
/** Sets this effect's value. Use {@code 0.0} to disable it, {@code 1.0} for a normal effect and any positive value for a | |
* custom result. */ | |
public void setHorizontalFuzz(float value) { | |
distortion.setHorizontalFuzz(value); | |
} | |
@Override | |
public void rebind() { | |
distortion.rebind(); | |
} | |
@Override | |
public void render(FrameBuffer src, FrameBuffer dest) { | |
restoreViewport(dest); | |
distortion.setInput(src).setOutput(dest).render(); | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.grashers.core.postprocessing; | |
import com.bitfire.postprocessing.filters.Filter; | |
import com.bitfire.utils.ShaderLoader; | |
import com.grashers.core.utilities.general.GameMath; | |
public class DistortedTvFilter extends Filter<DistortedTvFilter> { | |
private float vertJerkOpt = 1.0f; | |
private float vertMovementOpt = 1.0f; | |
private float bottomStaticOpt = 1.0f; | |
private float scalinesOpt = 1.0f; | |
private float rgbOffsetOpt = 1.0f; | |
private float horzFuzzOpt = 1.0f; | |
private float elapsedSecs = 0; | |
private int effectsSupport = 0; | |
private float timeOffset = 0; | |
public DistortedTvFilter(int effectsSupport) { | |
// @formatter:off | |
super(ShaderLoader.fromFile("screenspace", "distorted-tv", | |
(isSet(Effect.VerticalJerk.v, effectsSupport) ? "#define " + Effect.VerticalJerk.define + "\n" : "") | |
+ (isSet(Effect.VerticalMovement.v, effectsSupport) ? "#define " + Effect.VerticalMovement.define + "\n" : "") | |
+ (isSet(Effect.BottomStatic.v, effectsSupport) ? "#define " + Effect.BottomStatic.define + "\n" : "") | |
+ (isSet(Effect.Scanlines.v, effectsSupport) ? "#define " + Effect.Scanlines.define + "\n" : "") | |
+ (isSet(Effect.RgbOffset.v, effectsSupport) ? "#define " + Effect.RgbOffset.define + "\n" : "") | |
+ (isSet(Effect.HorizontalFuzz.v, effectsSupport) ? "#define " + Effect.HorizontalFuzz.define + "\n" : "") | |
)); | |
// @formatter:on | |
this.effectsSupport = effectsSupport; | |
this.timeOffset = GameMath.nextFloat(0, 200); | |
setTime(0f); | |
rebind(); | |
} | |
public void setTime(float elapsedSecs) { | |
this.elapsedSecs = elapsedSecs; | |
setParam(Param.Time, elapsedSecs + timeOffset); | |
} | |
/** Sets this effect's value. Use {@code 0.0} to disable it, {@code 1.0} for a normal effect and any positive value for a | |
* custom result. */ | |
public void setVerticalJerk(float value) { | |
this.vertJerkOpt = value; | |
if(isSet(Effect.VerticalJerk.v, effectsSupport)) | |
setParam(Param.VerticalJerk, value); | |
} | |
/** Sets this effect's value. Use {@code 0.0} to disable it, {@code 1.0} for a normal effect and any positive value for a | |
* custom result. */ | |
public void setVerticalMovement(float value) { | |
this.vertMovementOpt = value; | |
if(isSet(Effect.VerticalMovement.v, effectsSupport)) | |
setParam(Param.VerticalMovement, value); | |
} | |
/** Sets this effect's value. Use {@code 0.0} to disable it, {@code 1.0} for a normal effect and any positive value for a | |
* custom result. */ | |
public void setBottomStatic(float value) { | |
this.bottomStaticOpt = value; | |
if(isSet(Effect.BottomStatic.v, effectsSupport)) | |
setParam(Param.BottomStatic, value); | |
} | |
/** Sets this effect's value. Use {@code 0.0} to disable it, {@code 1.0} for a normal effect and any positive value for a | |
* custom result. */ | |
public void setScanlines(float value) { | |
this.scalinesOpt = value; | |
if(isSet(Effect.Scanlines.v, effectsSupport)) | |
setParam(Param.Scanlines, value); | |
} | |
/** Sets this effect's value. Use {@code 0.0} to disable it, {@code 1.0} for a normal effect and any positive value for a | |
* custom result. */ | |
public void setRgbOffset(float value) { | |
this.rgbOffsetOpt = value; | |
if(isSet(Effect.RgbOffset.v, effectsSupport)) | |
setParam(Param.RgbOffset, value); | |
} | |
/** Sets this effect's value. Use {@code 0.0} to disable it, {@code 1.0} for a normal effect and any positive value for a | |
* custom result. */ | |
public void setHorizontalFuzz(float value) { | |
this.horzFuzzOpt = value; | |
if(isSet(Effect.HorizontalFuzz.v, effectsSupport)) | |
setParam(Param.HorizontalFuzz, value); | |
} | |
@Override | |
protected void onBeforeRender() { | |
inputTexture.bind(u_texture0); | |
} | |
@Override | |
public void rebind() { | |
setParams(Param.Texture0, u_texture0); | |
setParams(Param.Time, elapsedSecs + timeOffset); | |
if(isSet(Effect.VerticalJerk.v, effectsSupport)) setParams(Param.VerticalJerk, vertJerkOpt); | |
if(isSet(Effect.VerticalMovement.v, effectsSupport)) setParams(Param.VerticalMovement, vertMovementOpt); | |
if(isSet(Effect.BottomStatic.v, effectsSupport)) setParams(Param.BottomStatic, bottomStaticOpt); | |
if(isSet(Effect.Scanlines.v, effectsSupport)) setParams(Param.Scanlines, scalinesOpt); | |
if(isSet(Effect.RgbOffset.v, effectsSupport)) setParams(Param.RgbOffset, rgbOffsetOpt); | |
if(isSet(Effect.HorizontalFuzz.v, effectsSupport)) setParams(Param.HorizontalFuzz, horzFuzzOpt); | |
endParams(); | |
} | |
public enum Param implements Parameter { | |
Texture0("u_texture0", 0), | |
Time("u_time", 0), | |
VerticalJerk("u_vertJerkOpt", 0), | |
VerticalMovement("u_vertMovementOpt", 0), | |
BottomStatic("u_bottomStaticOpt", 0), | |
Scanlines("u_scalinesOpt", 0), | |
RgbOffset("u_rgbOffsetOpt", 0), | |
HorizontalFuzz("u_horzFuzzOpt", 0); | |
private final String mnemonic; | |
private int elementSize; | |
Param(String m, int elementSize) { | |
this.mnemonic = m; | |
this.elementSize = elementSize; | |
} | |
@Override | |
public String mnemonic() { | |
return this.mnemonic; | |
} | |
@Override | |
public int arrayElementSize() { | |
return this.elementSize; | |
} | |
} | |
private static boolean isSet(int flag, int flags) { | |
return (flags & flag) == flag; | |
} | |
public enum Effect { | |
// @formatter:off | |
None (0, ""), | |
VerticalJerk (1, "ENABLE_VERTICAL_JERK"), | |
VerticalMovement (2, "ENABLE_VERTICAL_MOVEMENT"), | |
BottomStatic (4, "ENABLE_BOTTOM_STATIC"), | |
Scanlines (8, "ENABLE_SCANLINES"), | |
RgbOffset (16, "ENABLE_RGB_OFFSET"), | |
HorizontalFuzz (32, "ENABLE_HORIZONTAL_FUZZ"); | |
// @formatter:on | |
public int v; | |
public String define; | |
Effect(int value, String define) { | |
this.v = value; | |
this.define = define; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment