What causes this sort of tearing at the edges? - glsl

What causes this kind of tearing in GLSL? I noticed the same kind of thing going on at the teamLab art exhibit in Tokyo. Notice the imperfections in the edges.
Here is my fragment shader:
#version 300 es
precision highp float;
precision highp int;
uniform float time;
in vec2 v_pos;
out vec4 FragColor;
vec4 measureFloat(float measure_number_float){
return vec4(
float( mod ( measure_number_float, 60.0 ) ) / 60.0,
float( mod ( measure_number_float, 3600.0 ) ) / (60.0 * 60.0),
float( mod ( measure_number_float, 216000.0 ) ) / (60.0 * 60.0 * 60.0),
1.0
);
}
vec4 measureInt( int measure_number_int ){
return vec4(
float( measure_number_int % 60 ) / 60.0,
float( measure_number_int % 3600 ) / (60.0 * 60.0),
float( measure_number_int % 216000 ) / (60.0 * 60.0 * 60.0),
1.0
);
}
vec4 sampleColor(){
int x_coord_i, y_coord_i = 0;
float x_coord_f, y_coord_f = 0.0;
float res_mul_x_f = 20.0; // mul = multiplier
float res_mul_y_f = 20.0; // mul = multiplier
x_coord_i = int(v_pos.x * res_mul_x_f);
y_coord_i = int(v_pos.y * res_mul_y_f);
// return back to float:
x_coord_f = float(x_coord_i) / res_mul_x_f;
y_coord_f = float(y_coord_i) / res_mul_y_f;
int cell_number = y_coord_i * int(res_mul_x_f) + x_coord_i;
// return measureInt(cell_number);
return measureFloat(float(cell_number));
}
void main(void) {
FragColor = sampleColor();
}
This bug was present in both Chrome and Firefox, using both the measureInt function and the measureFloat function. I also don't know what to call this, so if there is a technical name for it, please do inform.

Related

Blobs shader GLSL

I want to create a similar background with a shader to these images:
:
These are just blurred blobs with colors, distributed across the whole page:
Here's my current progress: https://codesandbox.io/s/lucid-bas-wvlzl9?file=/src/components/Background/Background.tsx
Vertex shader:
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
Fragment shader:
precision highp float;
uniform float uTime;
uniform float uAmplitude;
uniform float uFrequency;
varying vec2 vUv;
uniform vec2 uResolution;
vec4 Sphere(vec2 position, float radius)
{
// float dist = radius / distance(vUv, position);
// float strength = 0.01 / distance(vUv, position);
float strength = 0.1 / distance(vec2(vUv.x, (vUv.y - 0.5) * 8. + 0.5), vec2(0.));
return vec4(strength * strength);
}
void main()
{
vec2 uv = vUv;
vec4 pixel = vec4(0.0, 0.0, 0.0, 0.0);
vec2 positions[4];
positions[0] = vec2(.5, .5);
// positions[1] = vec2(sin(uTime * 3.0) * 0.5, (cos(uTime * 1.3) * 0.6) + vUv.y);
// positions[2] = vec2(sin(uTime * 2.1) * 0.1, (cos(uTime * 1.9) * 0.8) + vUv.y);
// positions[3] = vec2(sin(uTime * 1.1) * 1.1, (cos(uTime * 2.6) * 0.7) + vUv.y);
for (int i = 0; i < 2; i++)
pixel += Sphere(positions[i], 0.22);
pixel = pixel * pixel;
gl_FragColor = pixel;
}
For each blob, you can multiply it's color by a a noise function and then a 2D gaussian curve centered in a random point. Then add all the blobs together. I only added the ones of the adjacent cells to make it scrollable and the numbers in the for loops might be increased for bigger blobs.
here is my code :
#ifdef GL_ES
precision mediump float;
#endif
uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;
const float blobSize = 0.125;
const float cellSize = .75;
const float noiseScale = .375;
const float background = .125;
const float blobsLuminosity = .75;
const float blobsSaturation = .5;
vec2 random2(vec2 st){
st = vec2( dot(st,vec2(127.1,311.7)),
dot(st,vec2(269.5,183.3)) );
return -1.0 + 2.0*fract(sin(st)*43758.5453123);
}
// Gradient Noise by Inigo Quilez - iq/2013
// https://www.shadertoy.com/view/XdXGW8
float noise(vec2 st) {
vec2 i = floor(st);
vec2 f = fract(st);
vec2 u = f*f*(3.0-2.0*f);
return mix( mix( dot( random2(i + vec2(0.0,0.0) ), f - vec2(0.0,0.0) ),
dot( random2(i + vec2(1.0,0.0) ), f - vec2(1.0,0.0) ), u.x),
mix( dot( random2(i + vec2(0.0,1.0) ), f - vec2(0.0,1.0) ),
dot( random2(i + vec2(1.0,1.0) ), f - vec2(1.0,1.0) ), u.x), u.y)*.5+.5;
}
float gaussFunction(vec2 st, vec2 p, float r) {
return exp(-dot(st-p, st-p)/2./r/r);
}
// Function from Iñigo Quiles
// https://www.shadertoy.com/view/MsS3Wc
vec3 hsb2rgb( in vec3 c ){
vec3 rgb = clamp(abs(mod(c.x*6.0+vec3(0.0,4.0,2.0),
6.0)-3.0)-1.0,
0.0,
1.0 );
rgb = rgb*rgb*(3.0-2.0*rgb);
return c.z * mix( vec3(1.0), rgb, c.y);
}
vec3 hash32(vec2 p)
{
vec3 p3 = fract(vec3(p.xyx) * vec3(.1031, .1030, .0973));
p3 += dot(p3, p3.yxz+33.33);
return fract((p3.xxy+p3.yzz)*p3.zyx);
}
vec3 blobs(vec2 st){
vec2 i = floor(st/cellSize);
vec3 c = vec3(0.);
for(int x = -1; x <= 1; x++)
for(int y = -1; y <= 1; y++){
vec3 h = hash32(i+vec2(x, y));
c += hsb2rgb(vec3(h.z, blobsSaturation, blobsLuminosity)) * gaussFunction(st/cellSize, i + vec2(x, y) + h.xy, blobSize) * smoothstep(0., 1., noise(noiseScale*st/cellSize / blobSize));
//c += hsb2rgb(vec3(h.z, blobsSaturation, blobsLuminosity)) * gaussFunction(st/cellSize, i + vec2(x, y) + h.xy, blobSize) * noise(noiseScale*st/cellSize / blobSize);
}
return c + vec3(background);
}
float map(float x, float a, float b, float c, float d){
return (x-a)/(b-a)*(d-c)+c;
}
void main() {
vec2 st = gl_FragCoord.xy/u_resolution.xy;
st.x *= u_resolution.x/u_resolution.y;
vec3 color = vec3(0.0);
color = vec3(blobs(st - u_mouse/u_resolution.xy*4.));
gl_FragColor = vec4(color,1.0);
}
made in this shader editor.

Implementing RayPicking in a Fragment Shader

I am having trouble implementing RayPicking in a Fragment-Shader, I understand I must start from my mouse coordinates, but I am not sure what to multiply my origin with.
I have tried creating a 3 component vector, with x and y as my mouse coordinates divided by my resolution, and in z I have tried using my p(for point in space, calculated as rayOrigin + rayDirection * t) with no luck.
Here is a Shadertoy that tries what I am looking for.
float ray( vec3 ro, vec3 rd, out float d )
{
float t = 0.0; d = 0.0;
for( int i = 0; i < STEPS; ++i )
{
vec3 p = ro + rd * t;
d = map( p );
if( d < EPS || t > FAR ) break;
t += d;
}
return t;
}
vec3 shad( vec3 ro, vec3 rd, vec2 uv )
{
float t = 0.0, d = 0.0;
t = ray( ro, rd, d );
float x = ( 2.0 * iMouse.x ) / iResolution.x - 1.0;
float y = 1.0 - ( 2.0 * iMouse.y ) / iResolution.y;
float z = 1.0;
vec3 p = ro + rd * t;
vec3 n = nor( p );
vec3 lig = ( vec3( x, -y, z ) );
lig += ro + rd;
lig = normalize( lig );
vec3 ref = reflect( rd, n );
float amb = 0.5 + 0.5 * n.y;
float dif = max( 0.0, dot( n, lig ) );
float spe = pow( clamp( dot( ref, lig ), 0.0, 1.0 ), 16.0 );
vec3 col = vec3( 0 );
col += 0.1 * amb;
col += 0.2 * dif;
col += spe;
return col;
}
I expect to get a light that moves as if I was shooting a ray from my mouse coordinates to the SDF.
This is the correct code:
// Our sphere-tracing algorithm.
float ray( vec3 ro, vec3 rd, out float d )
{
float t = 0.0; d = 0.0;
for( int i = 0; i < STEPS; ++i )
{
vec3 p = ro + rd * t;
d = map( p );
if( d < EPS || t > FAR ) break;
t += d;
}
return t;
}
// Here we compute all our lighting calculations.
vec3 shad( vec3 ro, vec3 rd, vec2 uv )
{
float t = 0.0, d = 0.0;
t = ray( ro, rd, d );
vec3 p = ro + rd * t;
vec3 n = nor( p );
// The values of the variable lig are not random they are in the same position as our rayOrigin for our sphere tracing algo, that goes in main's body.
vec3 lig = ( vec3( 0, 0, 2 ) );
// Here is where we "shoot" our ray from the mouse position. Our ray's origin.
vec2 uvl = ( -iResolution.xy + 2.0 * iMouse.xy ) / iResolution.y;
// This is our ray's direction.
vec3 lir = normalize( vec3( uvl, -1 ) );
// Here we get our SDF(dO) and our incrementing value(tO).
float dO = 0.0, tO = ray( lig, lir, dO );
// Now we update our vector with the direction and incrementing steps.
lig += lir * tO;
// We must normalize lights as they are just a direction, the magnitude screws the lighting calculations.
lig = normalize( lig );
vec3 ref = reflect( rd, n );
float amb = 0.5 + 0.5 * n.y;
float dif = max( 0.0, dot( n, lig ) );
float spe = pow( clamp( dot( ref, lig ), 0.0, 1.0 ), 16.0 );
vec3 col = vec3( 0 );
col += 0.1 * amb;
col += 0.2 * dif;
col += spe;
return col;
}
// Last step, here we create the origin and direction of our rays that we shoot against the SDF.
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
//Normalize the coordinates.
vec2 uv = ( -iResolution.xy + 2.0 * fragCoord.xy ) / iResolution.y;
// This is our ray's origin. We must use the same values for our lig's origin.
vec3 ro = vec3( 0, 0, 2 );
// This is our ray's direction.
vec3 rd = normalize( vec3( uv, -1 ) );
// Our SDF(d) and our incrementing steps(t), we only need our SDF(d) to bail the shading calculations according to our epsilon(EPS).
float t = 0.0, d = 0.0;
t = ray( ro, rd, d );
vec3 col = d < EPS ? shad( ro, rd, uv ) : vec3( 0 );
fragColor = vec4( col, 1 );
}

Why isn't this shadertoy displaying correctly in glsl?

I'm working on porting this shader from shadertoy into glsl for a GPUImage based iOS platform.
https://www.shadertoy.com/view/4s2yW1
technically, it's running on the device effectively. However, it's only displaying the background, and not the circles, which is the whole point.
I'm wondering if someone can give me some clue as to why this isn't showing up properly. For whatever reason I'm getting the same results when using shaderfrog.
Here's the shaderfrog link:
http://shaderfrog.com/app/view/1463
and the code itself:
#define PI 3.14159265359
NSString *const kGPUImageBokehFragmentShaderString = SHADER_STRING
(
precision highp float;
varying highp vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
uniform highp float time;
void Rotate( vec2 p, float a )
{
p = cos( a ) * p + sin( a ) * vec2( p.y, -p.x );
}
float Circle( vec2 p, float r )
{
return ( length( p / r ) - 1.0 ) * r;
}
float Rand( vec2 c )
{
return fract( sin( dot( c.xy, vec2( 12.9898, 78.233 ) ) ) * 43758.5453 );
}
float saturate( float x )
{
return clamp( x, 0.0, 1.0 );
}
void BokehLayer(vec3 color, vec2 p, vec3 c )
{
float wrap = 450.0;
if ( mod( floor( p.y / wrap + 0.5 ), 2.0 ) == 0.0 )
{
p.x += wrap * 0.5;
}
vec2 p2 = mod( p + 0.5 * wrap, wrap ) - 0.5 * wrap;
vec2 cell = floor( p / wrap + 0.5 );
float cellR = Rand( cell );
c *= fract( cellR * 3.33 + 3.33 );
float radius = mix( 30.0, 70.0, fract( cellR * 7.77 + 7.77 ) );
p2.x *= mix( 0.9, 1.1, fract( cellR * 11.13 + 11.13 ) );
p2.y *= mix( 0.9, 1.1, fract( cellR * 17.17 + 17.17 ) );
float sdf = Circle( p2, radius );
float circle = 1.0 - smoothstep( 0.0, 1.0, sdf * 0.04 );
float glow = exp( -sdf * 0.025 ) * 0.3 * ( 1.0 - circle );
color += c * ( circle + glow );
}
void main()
{
vec2 iResolution = vec2(1., 1.);
vec2 uv = textureCoordinate.xy/iResolution.xy;
vec2 p = ( 2.0 * textureCoordinate - iResolution.xy) / iResolution.x * 1000.0;
// background
vec3 color = mix( vec3( 0.3, 0.1, 0.3 ), vec3( 0.1, 0.4, 0.5 ), dot( uv, vec2( 0.2, 0.7 ) ) );
float timeElapsed = time - 15.0;
Rotate( p, 0.2 + timeElapsed * 0.03 );
BokehLayer( color, p + vec2( -50.0 * timeElapsed + 0.0, 0.0 ), 3.0 * vec3( 0.4, 0.1, 0.2 ) );
Rotate( p, 0.3 - timeElapsed * 0.05 );
BokehLayer( color, p + vec2( -70.0 * timeElapsed + 33.0, -33.0 ), 3.5 * vec3( 0.6, 0.4, 0.2 ) );
Rotate( p, 0.5 + timeElapsed * 0.07 );
BokehLayer( color, p + vec2( -60.0 * timeElapsed + 55.0, 55.0 ), 3.0 * vec3( 0.4, 0.3, 0.2 ) );
Rotate( p, 0.9 - timeElapsed * 0.03 );
BokehLayer( color, p + vec2( -25.0 * timeElapsed + 77.0, 77.0 ), 3.0 * vec3( 0.4, 0.2, 0.1 ) );
Rotate( p, 0.0 + timeElapsed * 0.05 );
BokehLayer( color, p + vec2( -15.0 * timeElapsed + 99.0, 99.0 ), 3.0 * vec3( 0.2, 0.0, 0.4 ) );
vec4 bokehColor = vec4( color, 1.0 );
gl_FragColor = bokehColor;
} );
Anything you can tell me about this would be greatly appreciated. Thanks!
The problem is you don't have color marked as inout
void BokehLayer(vec3 color, vec2 p, vec3 c ) // bad
void BokehLayer(inout vec3 color, vec2 p, vec3 c ) // good
BokehLayer wants to modify color but without the inout keyword it's just a parameter to the function. With inout it's a reference original variable.

Custom Minecraft Shader Bugs

I copied some files from a few shaders, and it works out for the most part, but I sometimes get graphical glitches like this where faces of grass blocks are completely transparent:
I believe my gbuffers_terrain.fsh or gbuffers_terrain.vsh is what is causing the problem.
gbuffers_terrain.fsh:
#version 120
#extension GL_ARB_shader_texture_lod : enable
/* DRAWBUFFERS:0247 */
/*
RRe36's Shaders, derived from Chocapic13 v4
Place two leading Slashes in front of the following '#define' lines in order to disable an option.
IMPORTANT: Placing Slashes in front of lines like '#define FILTER_LEVEL 15.0' will cause errors!
*/
//-------- Adjustable Variables --------//
//---- Normal Mapping ----//
#define NORMAL_MAPPING
#define NORMAL_MAP_MAX_ANGLE 1.0
//---- End of Normal Mapping ----//
//---- Parallax Displacement aka. POM ----//
//#define POM
#define POM_MAP_RES 128.0
#define POM_DEPTH (1.0/20.0)
const float MAX_OCCLUSION_DISTANCE = 32.0;
const float MIX_OCCLUSION_DISTANCE = 28.0;
const int MAX_OCCLUSION_POINTS = 12;
//---- End of Parallax Displacement ----//
//---- Lightmaps ----//
#define MIN_LIGHTAMOUNT 0.1 //affect the minecraft lightmap (not torches)
#define MINELIGHTMAP_EXP 2.0 //affect the minecraft lightmap (not torches)
//---- End of Lightmaps ----//
//-------- End of Adjustable Variables --------//
/* Here, intervalMult might need to be tweaked per texture pack.
The first two numbers determine how many samples are taken per fragment. They should always be the equal to eachother.
The third number divided by one of the first two numbers is inversely proportional to the range of the height-map. */
const vec3 intervalMult = vec3(1.0, 1.0, 1.0/POM_DEPTH)/POM_MAP_RES * 1.0;
const int RGBA16 = 3;
const int RGB16 = 2;
const int RGB8 = 1;
const int gnormalFormat = RGB16;
const int compositeFormat = RGBA16;
const int gaux2Format = RGBA16;
const int gcolorFormat = RGB8;
const int GL_EXP = 2048;
const int GL_LINEAR = 9729;
const float bump_distance = 64.0; //bump render distance: tiny = 32, short = 64, normal = 128, far = 256
const float pom_distance = 32.0; //POM render distance: tiny = 32, short = 64, normal = 128, far = 256
const float fademult = 0.1;
varying vec2 lmcoord;
varying vec4 color;
varying float translucent;
varying vec4 vtexcoordam; // .st for add, .pq for mul
varying vec4 vtexcoord;
varying float dist;
varying vec3 tangent;
varying vec3 normal;
varying vec3 binormal;
varying vec3 viewVector;
uniform sampler2D texture;
uniform sampler2D normals;
uniform sampler2D specular;
uniform vec3 sunPosition;
uniform vec3 moonPosition;
uniform int fogMode;
uniform int worldTime;
uniform float wetness;
uniform float rainStrength;
float totalspec = 0.0;
float wetx = clamp(wetness, 0.0f, 1.0)/1.0;
const float mincoord = 1.0/4096.0;
const float maxcoord = 1.0-mincoord;
vec2 dcdx = dFdx(vtexcoord.st*vtexcoordam.pq);
vec2 dcdy = dFdy(vtexcoord.st*vtexcoordam.pq);
vec4 readTexture(in vec2 coord){
return texture2DGradARB(texture,fract(coord)*vtexcoordam.pq+vtexcoordam.st,dcdx,dcdy);
}
vec4 readNormal(in vec2 coord){
return texture2DGradARB(normals,fract(coord)*vtexcoordam.pq+vtexcoordam.st,dcdx,dcdy);
}
float normalmap_intensity = NORMAL_MAP_MAX_ANGLE-((NORMAL_MAP_MAX_ANGLE*rainStrength)*0.95);
//////////////////////////////main//////////////////////////////
//////////////////////////////main//////////////////////////////
//////////////////////////////main//////////////////////////////
//////////////////////////////main//////////////////////////////
//////////////////////////////main//////////////////////////////
void main() {
vec2 adjustedTexCoord = vtexcoord.st*vtexcoordam.pq+vtexcoordam.st;
#ifdef POM
if (dist < MAX_OCCLUSION_DISTANCE) {
if ( viewVector.z < 0.0 && readNormal(vtexcoord.st).a < 0.99 && readNormal(vtexcoord.st).a > 0.01)
{
vec3 interval = viewVector.xyz * intervalMult;
vec3 coord = vec3(vtexcoord.st, 1.0);
for (int loopCount = 0;
(loopCount < MAX_OCCLUSION_POINTS) && (readNormal(coord.st).a < coord.p);
++loopCount) {
coord = coord+interval;
}
// Don't wrap around top of tall grass/flower
if (coord.t < mincoord) {
if (readTexture(vec2(coord.s,mincoord)).a == 0.0) {
coord.t = mincoord;
discard;
}
}
adjustedTexCoord = mix(fract(coord.st)*vtexcoordam.pq+vtexcoordam.st , adjustedTexCoord , max(dist-MIX_OCCLUSION_DISTANCE,0.0)/(MAX_OCCLUSION_DISTANCE-MIX_OCCLUSION_DISTANCE));
}
}
#endif
vec3 lightVector;
vec3 specularity = texture2DGradARB(specular, adjustedTexCoord.st, dcdx, dcdy).rgb;
float atten = 1.0-(specularity.b)*0.86;
vec4 frag2 = vec4(normal, 1.0f);
vec3 bump = texture2DGradARB(normals, adjustedTexCoord.st, dcdx, dcdy).rgb*2.0-1.0;
float bumpmult = normalmap_intensity*(1.0-wetness*lmcoord.t*0.65)*atten;
bump = bump * vec3(bumpmult, bumpmult, bumpmult) + vec3(0.0f, 0.0f, 1.0f - bumpmult);
mat3 tbnMatrix = mat3(tangent.x, binormal.x, normal.x,
tangent.y, binormal.y, normal.y,
tangent.z, binormal.z, normal.z);
frag2 = vec4(normalize(bump * tbnMatrix) * 0.5 + 0.5, 1.0);
float dirtest = 0.4;
if (worldTime < 12700 || worldTime > 23250) {
lightVector = normalize(sunPosition);
}
else {
lightVector = normalize(moonPosition);
}
dirtest = mix(1.0-0.8*step(dot(frag2.xyz*2.0-1.0,lightVector),-0.02),0.4,float(translucent > 0.01));
/* DRAWBUFFERS:0246 */
gl_FragData[0] = texture2DGradARB(texture, adjustedTexCoord.st, dcdx, dcdy) * color;
gl_FragData[1] = frag2;
gl_FragData[2] = vec4(lmcoord.t, dirtest, lmcoord.s, 1.0);
gl_FragData[3] = texture2DGradARB(specular, adjustedTexCoord.st, dcdx, dcdy);
}
My gbuffers_terrain.vsh:
#version 120
/*
RRe36's Shaders, derived from Chocapic13 v4
Place two leading Slashes in front of the following '#define' lines in order to disable an option.
IMPORTANT: Placing Slashes in front of lines like '#define FILTER_LEVEL 15.0' will cause errors!
*/
//-------- Adjustable Variables --------//
//---- Waving Effects ----//
#define WAVING_LEAVES
#define WAVING_VINES
#define WAVING_GRASS
#define WAVING_WHEAT
#define WAVING_FLOWERS
#define WAVING_FIRE
#define WAVING_LAVA
#define WAVING_LILYPAD
#define WAVING_TALLGRASS
#define WAVING_REEDS
//---- End of Waving Effects ----//
//---- Entity IDs ----//
#define ENTITY_LEAVES 18.0
#define ENTITY_VINES 106.0
#define ENTITY_TALLGRASS 31.0
#define ENTITY_DANDELION 37.0
#define ENTITY_ROSE 38.0
#define ENTITY_WHEAT 59.0
#define ENTITY_LILYPAD 111.0
#define ENTITY_FIRE 51.0
#define ENTITY_LAVAFLOWING 10.0
#define ENTITY_LAVASTILL 11.0
#define ENTITY_SAPLING 6.0
//---- End of Entity IDs ----//
//---- World Effects -----//
//#define WORLD_CURVATURE // will cause bug at high shadowdistances: looks like a dark circle around you
const float WORLD_RADIUS = 6000.0; //Increase for a stronger rounded world
const float WORLD_RADIUS_SQUARED = 15000000.0;
//---- End of World Effects ----//
//-------- End of Adjustable Variables --------//
const float PI = 3.1415927;
varying vec4 color;
varying vec2 lmcoord;
varying float translucent;
varying vec4 vtexcoordam; // .st for add, .pq for mul
varying vec4 vtexcoord;
varying float dist;
varying vec3 tangent;
varying vec3 normal;
varying vec3 binormal;
varying vec3 viewVector;
attribute vec4 mc_Entity;
attribute vec4 mc_midTexCoord;
uniform vec3 cameraPosition;
uniform mat4 gbufferModelView;
uniform mat4 gbufferModelViewInverse;
uniform int worldTime;
uniform float frameTimeCounter;
uniform float rainStrength;
float timefract = worldTime;
//Calculate Time of Day
float TimeSunrise = ((clamp(timefract, 23000.0, 24000.0) - 23000.0) / 1000.0) + (1.0 - (clamp(timefract, 0.0, 4000.0)/4000.0));
float TimeNoon = ((clamp(timefract, 0.0, 4000.0)) / 4000.0) - ((clamp(timefract, 8000.0, 12000.0) - 8000.0) / 4000.0);
float TimeSunset = ((clamp(timefract, 8000.0, 12000.0) - 8000.0) / 4000.0) - ((clamp(timefract, 12000.0, 12750.0) - 12000.0) / 750.0);
float TimeMidnight = ((clamp(timefract, 12000.0, 12750.0) - 12000.0) / 750.0) - ((clamp(timefract, 23000.0, 24000.0) - 23000.0) / 1000.0);
float animationTime = worldTime/20.0f;
float pi2wt = PI*2*(frameTimeCounter*24 + (rainStrength*2));
vec3 calcWave(in vec3 pos, in float fm, in float mm, in float ma, in float f0, in float f1, in float f2, in float f3, in float f4, in float f5) {
vec3 ret;
float magnitude,d0,d1,d2,d3;
magnitude = sin(pi2wt*fm + pos.x*0.5 + pos.z*0.5 + pos.y*0.5) * mm + ma;
d0 = sin(pi2wt*f0)*(rainStrength*2);
d1 = sin(pi2wt*f1)*(rainStrength*1.75);
d2 = sin(pi2wt*f2)*(rainStrength*1.5);
ret.x = sin(pi2wt*f3 + d0 + d1 - pos.x + pos.z + pos.y) * magnitude;
ret.z = sin(pi2wt*f4 + d1 + d2 + pos.x - pos.z + pos.y) * magnitude;
ret.y = sin(pi2wt*f5 + d2 + d0 + pos.z + pos.y - pos.y) * magnitude;
return ret;
}
vec3 calcMove(in vec3 pos, in float f0, in float f1, in float f2, in float f3, in float f4, in float f5, in vec3 amp1, in vec3 amp2) {
vec3 move1 = calcWave(pos , 0.0027, 0.0400, 0.0400, 0.0127, 0.0089, 0.0114, 0.0063, 0.0224, 0.0015) * amp1;
vec3 move2 = calcWave(pos+move1, 0.0348, 0.0400, 0.0400, f0, f1, f2, f3, f4, f5) * amp2;
return move1+move2;
}
vec3 calcWaterMove(in vec3 pos) {
float fy = fract(pos.y + 0.001);
if (fy > 0.002) {
float wave = 0.05 * sin(2 * PI * (worldTime / 86.0 + pos.x / 7.0 + pos.z / 13.0))
+ 0.05 * sin(2 * PI * (worldTime / 60.0 + pos.x / 11.0 + pos.z / 5.0));
return vec3(0, clamp(wave, -fy, 1.0-fy), 0);
}
else {
return vec3(0);
}
}
//////////////////////////////main//////////////////////////////
//////////////////////////////main//////////////////////////////
//////////////////////////////main//////////////////////////////
//////////////////////////////main//////////////////////////////
//////////////////////////////main//////////////////////////////
void main() {
vec2 texcoord = (gl_TextureMatrix[0] * gl_MultiTexCoord0).st;
vec2 midcoord = (gl_TextureMatrix[0] * mc_midTexCoord).st;
vec2 texcoordminusmid = texcoord-midcoord;
vtexcoordam.pq = abs(texcoordminusmid)*2;
vtexcoordam.st = min(texcoord,midcoord-texcoordminusmid);
vtexcoord.st = sign(texcoordminusmid)*0.5+0.5;
translucent = 0.0f;
float istopv = 0.0;
if (gl_MultiTexCoord0.t < mc_midTexCoord.t) istopv = 1.0;
/* un-rotate */
vec4 position = gbufferModelViewInverse * gl_ModelViewMatrix * gl_Vertex;
vec3 worldpos = position.xyz + cameraPosition;
//initialize per-entity waving parameters
float parm0,parm1,parm2,parm3,parm4,parm5 = rainStrength*4;
vec3 ampl1,ampl2;
ampl1 = vec3(0.0);
ampl2 = vec3(0.0);
#ifdef WAVING_LEAVES
if (( mc_Entity.x == ENTITY_LEAVES || mc_Entity.x == 161 )) {
parm0 = 0.0040;
parm1 = 0.0064;
parm2 = 0.0043;
parm3 = 0.0035;
parm4 = 0.0037;
parm5 = 0.0041;
ampl1 = vec3(1.0,0.2,1.0) + rainStrength/4;
ampl2 = vec3(0.5,0.1,0.5) + rainStrength/4;
}
#endif
#ifdef WAVING_VINES
if ( mc_Entity.x == ENTITY_VINES ) {
parm0 = 0.0040;
parm1 = 0.0064;
parm2 = 0.0043;
parm3 = 0.0035;
parm4 = 0.0037;
parm5 = 0.0041;
ampl1 = vec3(1.0,0.2,1.0);
ampl2 = vec3(0.5,0.1,0.5);
}
#endif
#ifdef WAVING_REEDS
if ( mc_Entity.x == 83 ) {
parm0 = 0.0024;
parm1 = 0.0020;
parm2 = 0.0016;
parm3 = 0.0010;
parm4 = 0.0009;
parm5 = 0.0002;
ampl1 = vec3(0.25,0.05,0.25) + rainStrength/5;
ampl2 = vec3(0.125,0.025,0.125) + rainStrength/5;
}
#endif
if (istopv > 0.9) {
#ifdef WAVING_GRASS
if ( mc_Entity.x == ENTITY_TALLGRASS || mc_Entity.x == ENTITY_SAPLING) {
parm0 = 0.0041;
parm1 = 0.0070;
parm2 = 0.0044;
parm3 = 0.0038;
parm4 = 0.0063;
parm5 = 0.0;
ampl1 = vec3(0.8,0.0,0.8) + 0.2 + rainStrength/3;
ampl2 = vec3(0.4,0.0,0.4) + 0.2 + rainStrength/3;
}
#endif
#ifdef WAVING_FLOWERS
if ((mc_Entity.x == ENTITY_DANDELION || mc_Entity.x == ENTITY_ROSE)) {
parm0 = 0.0041;
parm1 = 0.005;
parm2 = 0.0044;
parm3 = 0.0038;
parm4 = 0.0240;
parm5 = 0.0;
ampl1 = vec3(0.8,0.0,0.8) + rainStrength/3;
ampl2 = vec3(0.4,0.0,0.4) + rainStrength/3;
}
#endif
#ifdef WAVING_WHEAT
if ( mc_Entity.x == ENTITY_WHEAT || mc_Entity.x == 141 || mc_Entity.x == 142) {
parm0 = 0.0041;
parm1 = 0.0070;
parm2 = 0.0044;
parm3 = 0.0240;
parm4 = 0.0063;
parm5 = 0.0;
ampl1 = vec3(0.6,0.0,0.8) + rainStrength/3;
ampl2 = vec3(0.4,0.0,0.5) + rainStrength/3;
}
#endif
#ifdef WAVING_FIRE
if ( mc_Entity.x == ENTITY_FIRE) {
parm0 = 0.0105;
parm1 = 0.0096;
parm2 = 0.0087;
parm3 = 0.0063;
parm4 = 0.0097;
parm5 = 0.0156;
ampl1 = vec3(1.2,0.4,1.2) + rainStrength*1.8;
ampl2 = vec3(0.8,0.8,0.8) + rainStrength*1.8;
}
#endif
}
float movemult = 0.0;
#ifdef WAVING_LAVA
if ( mc_Entity.x == ENTITY_LAVAFLOWING || mc_Entity.x == ENTITY_LAVASTILL ) movemult = 0.25;
#endif
#ifdef WAVING_LILYPAD
if ( mc_Entity.x == ENTITY_LILYPAD ) movemult = 1.0 + (rainStrength*0.5);
#endif
#ifdef WAVING_TALLGRASS
if ( mc_Entity.x == 175) {
parm0 = 0.0031;
parm1 = 0.004;
parm2 = 0.0034;
parm3 = 0.0028;
parm4 = 0.0163;
parm5 = 0.0;
ampl1 = vec3(0.4,0.0,0.4);
ampl2 = vec3(0.2,0.0,0.2);
}
#endif
#ifdef WORLD_CURVATURE
if (gl_Color.a != 0.8) {
float distanceSquared = position.x * position.x + position.z * position.z;
position.y -= WORLD_RADIUS - sqrt(max(1.0 - distanceSquared / WORLD_RADIUS_SQUARED, 0.0)) * WORLD_RADIUS;
}
#endif
position.xyz += calcWaterMove(worldpos.xyz) * movemult;
position.xyz += calcMove(worldpos.xyz, parm0, parm1, parm2, parm3, parm4, parm5, ampl1, ampl2);
if ( mc_Entity.x == 37 || mc_Entity.x == 31 || mc_Entity.x == 175 || mc_Entity.x == 59 || mc_Entity.x == 141 || mc_Entity.x == 142 || mc_Entity.x == 31 )
translucent = 1.0;
/* re-rotate */
/* projectify */
gl_Position = gl_ProjectionMatrix * gbufferModelView * position;
color = gl_Color;
lmcoord = (gl_TextureMatrix[1] * gl_MultiTexCoord1).xy;
normal = normalize(gl_NormalMatrix * gl_Normal);
tangent = vec3(0.0);
binormal = vec3(0.0);
normal = normalize(gl_NormalMatrix * gl_Normal);
if (gl_Normal.x > 0.5) {
// 1.0, 0.0, 0.0
tangent = normalize(gl_NormalMatrix * vec3( 0.0, 0.0, 1.0));
binormal = normalize(gl_NormalMatrix * vec3( 0.0, -1.0, 0.0));
} else if (gl_Normal.x < -0.5) {
// -1.0, 0.0, 0.0
tangent = normalize(gl_NormalMatrix * vec3( 0.0, 0.0, 1.0));
binormal = normalize(gl_NormalMatrix * vec3( 0.0, -1.0, 0.0));
} else if (gl_Normal.y > 0.5) {
// 0.0, 1.0, 0.0
tangent = normalize(gl_NormalMatrix * vec3( 1.0, 0.0, 0.0));
binormal = normalize(gl_NormalMatrix * vec3( 0.0, 0.0, 1.0));
} else if (gl_Normal.y < -0.5) {
// 0.0, -1.0, 0.0
tangent = normalize(gl_NormalMatrix * vec3( 1.0, 0.0, 0.0));
binormal = normalize(gl_NormalMatrix * vec3( 0.0, 0.0, 1.0));
} else if (gl_Normal.z > 0.5) {
// 0.0, 0.0, 1.0
tangent = normalize(gl_NormalMatrix * vec3( 1.0, 0.0, 0.0));
binormal = normalize(gl_NormalMatrix * vec3( 0.0, -1.0, 0.0));
} else if (gl_Normal.z < -0.5) {
// 0.0, 0.0, -1.0
tangent = normalize(gl_NormalMatrix * vec3( 1.0, 0.0, 0.0));
binormal = normalize(gl_NormalMatrix * vec3( 0.0, -1.0, 0.0));
}
mat3 tbnMatrix = mat3(tangent.x, binormal.x, normal.x,
tangent.y, binormal.y, normal.y,
tangent.z, binormal.z, normal.z);
viewVector = ( gl_ModelViewMatrix * gl_Vertex).xyz;
viewVector = normalize(tbnMatrix * viewVector);
dist = 0.0;
dist = length(gbufferModelView *gbufferModelViewInverse * gl_ModelViewMatrix * gl_Vertex);
}
My composite.fsh:
#version 120
//------------------------------------
//datLax' OnlyWater v2.0
//
//If you have questions, suggestions or bugs you want to report, please comment on my minecraftforum.net thread:
//http://www.minecraftforum.net/forums/mapping-and-modding/minecraft-mods/2381727-shader-pack-datlax-onlywater-only-water
//
//This code is free to use,
//but don't forget to give credits! :)
//------------------------------------
uniform sampler2D gcolor;
uniform sampler2D depthtex0;
uniform sampler2D gaux1;
varying vec4 texcoord;
varying vec3 ambient_color;
varying float TimeMidnight;
uniform int worldTime;
uniform mat4 gbufferProjection;
uniform mat4 gbufferProjectionInverse;
uniform mat4 gbufferModelViewInverse;
uniform float near;
uniform float far;
uniform float viewWidth;
uniform float viewHeight;
float ld(float depth) {
return (2.0 * near) / (far + near - depth * (far - near));
}
vec3 aux = texture2D(gaux1, texcoord.st).rgb;
float land = aux.b;
float torch_lightmap = aux.b;
float sky_lightmap = aux.g;
float landx = 1.0-step(land, 0.01);
float iswater = 0.0;
float pixeldepth = texture2D(depthtex0,texcoord.xy).x;
float pw = 1.0/ viewWidth;
float ph = 1.0/ viewHeight;
float callwaves(vec2 pos) {
float wsize = 2.9;
float wspeed = 0.025f;
float rs0 = abs(sin((worldTime*wspeed/5.0) + (pos.s*wsize) * 20.0)+0.2);
float rs1 = abs(sin((worldTime*wspeed/7.0) + (pos.t*wsize) * 27.0));
float rs2 = abs(sin((worldTime*wspeed/2.0) + (pos.t*wsize) * 60.0 - sin(pos.s*wsize) * 13.0)+0.4);
float rs3 = abs(sin((worldTime*wspeed/1.0) - (pos.s*wsize) * 20.0 + cos(pos.t*wsize) * 83.0)+0.1);
float wsize2 = 1.7;
float wspeed2 = 0.017f;
float rs0a = abs(sin((worldTime*wspeed2/4.0) + (pos.s*wsize2) * 24.0));
float rs1a = abs(sin((worldTime*wspeed2/11.0) + (pos.t*wsize2) * 77.0 )+0.3);
float rs2a = abs(sin((worldTime*wspeed2/6.0) + (pos.s*wsize2) * 50.0 - (pos.t*wsize2) * 23.0)+0.12);
float rs3a = abs(sin((worldTime*wspeed2/14.0) - (pos.t*wsize2) * 4.0 + (pos.s*wsize2) * 98.0));
float wsize3 = 0.3;
float wspeed3 = 0.03f;
float rs0b = abs(sin((worldTime*wspeed3/4.0) + (pos.s*wsize3) * 14.0));
float rs1b = abs(sin((worldTime*wspeed3/11.0) + (pos.t*wsize3) * 37.0));
float rs2b = abs(sin((worldTime*wspeed3/6.0) + (pos.t*wsize3) * 47.0 - cos(pos.s*wsize3) * 33.0 + rs0a + rs0b));
float rs3b = abs(sin((worldTime*wspeed3/14.0) - (pos.s*wsize3) * 13.0 + sin(pos.t*wsize3) * 98.0 + rs0 + rs1));
float waves = (rs1 * rs0 + rs2 * rs3)/2.0f;
float waves2 = (rs0a * rs1a + rs2a * rs3a)/2.0f;
float waves3 = (rs0b + rs1b + rs2b + rs3b)*0.25;
return (waves + waves2 + waves3)/3.0f;
}
//----------------MAIN------------------
void main() {
if(aux.g > 0.01 && aux.g < 0.07) {
iswater = 1.0;
}
vec4 fragposition = gbufferProjectionInverse * vec4(texcoord.s * 2.0f - 1.0f, texcoord.t * 2.0f - 1.0f, 2.0f * pixeldepth - 1.0f, 1.0f);
fragposition /= fragposition.w;
float dist = length(fragposition.xyz);
vec4 worldposition = vec4(0.0);
worldposition = gbufferModelViewInverse * fragposition;
vec3 color = texture2D(gcolor, texcoord.st).rgb;
const float rspread = 0.30f;
float wave = 0.0;
if (iswater == 1.0) {
wave = callwaves(worldposition.xz*0.02)*2.0-1.0;
wave = wave;
const float wnormalclamp = 0.05f;
float rdepth = pixeldepth;
float waves = wave;
float wnormal_x1 = texture2D(depthtex0, texcoord.st + vec2(pw, 0.0f)).x - texture2D(depthtex0, texcoord.st).x;
float wnormal_x2 = texture2D(depthtex0, texcoord.st).x - texture2D(depthtex0, texcoord.st + vec2(-pw, 0.0f)).x;
float wnormal_x = 0.0f;
if(abs(wnormal_x1) > abs(wnormal_x2)){
wnormal_x = wnormal_x2;
} else {
wnormal_x = wnormal_x1;
}
wnormal_x /= 1.0f - rdepth;
wnormal_x = clamp(wnormal_x, -wnormalclamp, wnormalclamp);
wnormal_x *= rspread;
float wnormal_y1 = texture2D(depthtex0, texcoord.st + vec2(0.0f, ph)).x - texture2D(depthtex0, texcoord.st).x;
float wnormal_y2 = texture2D(depthtex0, texcoord.st).x - texture2D(depthtex0, texcoord.st + vec2(0.0f, -ph)).x;
float wnormal_y;
if(abs(wnormal_y1) > abs(wnormal_y2)){
wnormal_y = wnormal_y2;
} else {
wnormal_y = wnormal_y1;
}
wnormal_y /= 1.0f - rdepth;
wnormal_y = clamp(wnormal_y, -wnormalclamp, wnormalclamp);
wnormal_y *= rspread;
//Calculate distance of objects behind water
float refractdist = 0.2 * 10.0f;
//Perform refraction
float refractamount = 500.1154f*0.35f*refractdist;
float refractamount2 = 0.0214f*0.05f*refractdist;
float refractamount3 = 0.214f*0.15f*refractdist;
float waberration = 0.105;
vec3 refracted = vec3(0.0f);
float refractedmask = 0.0;
float bigWaveRefract = 0.0;
float bigWaveRefractScale = 0.0;
vec2 bigRefract = vec2(wnormal_x*bigWaveRefract, wnormal_y*bigWaveRefract);
vec2 refractcoord_r = texcoord.st;
vec2 refractcoord_g = texcoord.st;
vec2 refractcoord_b = texcoord.st;
for (int i = 0; i < 1; ++i) {
refractcoord_r = texcoord.st * (1.0f + waves*refractamount3) - (waves*refractamount3/2.0f) + vec2( waves*refractamount2 + (-wnormal_x*0.4f) - bigRefract.x, waves*refractamount2 + (-wnormal_y*0.4f) - bigRefract.y) * (waberration * 2.0f + 1.0f);
refractcoord_r = refractcoord_r * vec2(1.0f - abs(wnormal_x) * bigWaveRefractScale, 1.0f - abs(wnormal_y) * bigWaveRefractScale) + vec2(abs(wnormal_x) * bigWaveRefractScale * 0.5f, abs(wnormal_y) * bigWaveRefractScale * 0.5f);
refractcoord_r.s = clamp(refractcoord_r.s, 0.001f, 0.999f);
refractcoord_r.t = clamp(refractcoord_r.t, 0.001f, 0.999f);
if (refractcoord_r.s > 1.0 || refractcoord_r.s < 0.0 || refractcoord_r.t > 1.0 || refractcoord_r.t < 0.0) {
break;
}
refracted.rgb = texture2D(gcolor, refractcoord_r).rgb;
refractedmask = texture2D(gaux1, refractcoord_r).g;
if(refractedmask > 0.01 && refractedmask < 0.07) {
refractedmask = 1.0;
}else refractedmask = 0.0;
}
color.rgb = mix(color.rgb, refracted.rgb, vec3(refractedmask));
}
wave = wave*0.5+0.5;
if (iswater > 0.9){
wave += 0.02;
}else{
wave = 0.0;
}
if (landx == 1.0) {
//vec3 torchcolor = vec3(1.0, 0.675, 0.415);
vec3 torchcolor = vec3(0.8, 0.7, 0.3);
vec3 torchlight_lightmap = torch_lightmap * torchcolor;
color = color * torchlight_lightmap * TimeMidnight + color * ambient_color;
}
/* DRAWBUFFERS:3 */
gl_FragData[0] = vec4(color, 1.0);
/* DRAWBUFFERS:NNN3N5 */
gl_FragData[5] = vec4(0.0, wave, 0.0, 0.0);
gl_FragData[3] = vec4(color, land);
}
My composite.vsh:
#version 120
//------------------------------------
//datLax' OnlyWater v2.0
//
//If you have questions, suggestions or bugs you want to report, please comment on my minecraftforum.net thread:
//http://www.minecraftforum.net/forums/mapping-and-modding/minecraft-mods/2381727-shader-pack-datlax-onlywater-only-water
//
//This code is free to use,
//but don't forget to give credits! :)
//------------------------------------
varying vec4 texcoord;
varying float TimeMidnight;
varying float TimeSunset;
varying float TimeNoon;
varying float TimeSunrise;
varying vec3 ambient_color;
uniform int worldTime;
void main() {
gl_Position = ftransform();
texcoord = gl_MultiTexCoord0;
float timefract = float(worldTime);
TimeSunrise = ((clamp(timefract, 23000.0, 24000.0) - 23000.0) / 1000.0) + (1.0-(clamp(timefract, 0.0, 6000.0)/6000.0));
TimeNoon = ((clamp(timefract, 0.0, 6000.0)) / 6000.0) - ((clamp(timefract, 6000.0, 12000.0) - 6000.0) / 6000.0);
TimeSunset = ((clamp(timefract, 6000.0, 12000.0) - 6000.0) / 6000.0) - ((clamp(timefract, 12000.0, 12750.0) - 12000.0) / 750.0);
TimeMidnight = ((clamp(timefract, 12000.0, 12750.0) - 12000.0) / 750.0) - ((clamp(timefract, 23000.0, 24000.0) - 23000.0) / 1000.0);
vec3 sunrise_amb;
sunrise_amb.r = TimeSunrise;
sunrise_amb.g = TimeSunrise;
sunrise_amb.b = TimeSunrise;
vec3 noon_amb;
noon_amb.r = TimeNoon;
noon_amb.g = TimeNoon;
noon_amb.b = TimeNoon;
vec3 sunset_amb;
sunset_amb.r = 0.95*TimeSunset;
sunset_amb.g = 0.95*TimeSunset;
sunset_amb.b = 0.95*TimeSunset;
vec3 midnight_amb;
midnight_amb.r = 0.45*TimeMidnight;
midnight_amb.g = 0.45*TimeMidnight;
midnight_amb.b = 0.7*TimeMidnight;
ambient_color.r = sunrise_amb.r + noon_amb.r + sunset_amb.r + midnight_amb.r;
ambient_color.g = sunrise_amb.g + noon_amb.g + sunset_amb.g + midnight_amb.g;
ambient_color.b = sunrise_amb.b + noon_amb.b + sunset_amb.b + midnight_amb.b;
}
I am using a 128x128 resource pack, and I don't know much about shaders. Why are faces of grass blocks flickering, and is there a way to fix this? Also, when recording from OBS screen capture, the video turns out flashing. Reminder: I pulled gbuffers_terrain from a completely different shaderpack.
Specs:
OS: Ubuntu 16.04 LTS 64-bit
RAM: 8GB
CPU: Intel® Core™ i5-4460 CPU # 3.20GHz × 4
GPU: Intel® Haswell Desktop
OpenGL:
OpenGL vendor string: Intel Open Source Technology Center
OpenGL renderer string: Mesa DRI Intel(R) Haswell Desktop
OpenGL core profile version string: 3.3 (Core Profile) Mesa 12.1.0-devel
OpenGL core profile shading language version string: 3.30
OpenGL core profile context flags: (none)
OpenGL core profile profile mask: core profile
OpenGL core profile extensions:
OpenGL version string: 3.0 Mesa 12.1.0-devel
OpenGL shading language version string: 1.30
OpenGL context flags: (none)
OpenGL extensions:
OpenGL ES profile version string: OpenGL ES 3.0 Mesa 12.1.0-devel
OpenGL ES profile shading language version string: OpenGL ES GLSL ES 3.00
OpenGL ES profile extensions:

Drawing a rectangle in a Fragment Shader

I'd like to know if there is a possibility to draw a rectangle in a fragment shader without using any if. I tried this :
void main(void)
{
vec2 resolution = vec2( 300.0, 300.0 );
vec2 center = resolution / 2.0;
vec2 position = ( gl_FragCoord.xy - center ) / resolution;
if ( ( abs(position.x) < 0.5 ) && ( abs(position.y) < 0.5 ) )
gl_FragColor = vec4( 0.0, 0.0, 1.0, 1.0 );
}
And when i test if the pixel is in the rectangle i must perform an if test.
I think there's a solution not to have an if, if you can help me ?
I'm not sure what you are trying to do but here's one idea?
precision mediump float;
uniform float time;
uniform vec2 mouse;
uniform vec2 resolution;
void main( void ) {
vec2 position = ( gl_FragCoord.xy / resolution.xy ) + mouse / 4.0;
// x1 y1 x2 y2
vec4 rect = vec4(0.2, 0.3, 0.4, 0.5);
vec2 hv = step(rect.xy, position) * step(position, rect.zw);
float onOff = hv.x * hv.y;
gl_FragColor = mix(vec4(0,0,0,0), vec4(1,0,0,0), onOff);
}
Here's a working version