Skip to content

Instantly share code, notes, and snippets.

@rafaskb
Last active August 26, 2019 00:42
Show Gist options
  • Save rafaskb/1a57758f9ce1001f0f59464fb77788ba to your computer and use it in GitHub Desktop.
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
/******************************************************************************
* 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;
}
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();
}
}
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