I'm trying to write a very simple shader that adds random sparkle to applicable objects. The way I'd like to do this is by adding a random shade of white (R = G = B) to the pixel value within the pixel shader.
It seems that noise()
doesn't work the way I hope it does:
float multiplier = noise(float3(Input.Position[0], Input.Position[1], time));
It gives me "error X4532: cannot map expression to pixel shader instruction set" referring to the call to noise()
.
Since I don't know of a way to retain a number between calls to the shader, I don't think I can just write a simple random number producing function based on a seed passed in before rendering.
Is there a way to produce a random number from inside a pixel shader? If there is a way, how?
UPDATE July 2017: I've made the "pseudo randomness" more stable
// Version 3
float random( vec2 p )
{
vec2 K1 = vec2(
23.14069263277926, // e^pi (Gelfond's constant)
2.665144142690225 // 2^sqrt(2) (Gelfond–Schneider constant)
);
return fract( cos( dot(p,K1) ) * 12345.6789 );
}
This are the versions:
float random( vec2 p )
{
// e^pi (Gelfond's constant)
// 2^sqrt(2) (Gelfond–Schneider constant)
vec2 K1 = vec2( 23.14069263277926, 2.665144142690225 );
//return fract( cos( mod( 12345678., 256. * dot(p,K1) ) ) ); // ver1
//return fract(cos(dot(p,K1)) * 123456.); // ver2
return fract(cos(dot(p,K1)) * 12345.6789); // ver3
}
// Minified version 3:
float random(vec2 p){return fract(cos(dot(p,vec2(23.14069263277926,2.665144142690225)))*12345.6789);}
Passing in a texture to generate noise is (usually) over engineered. There are times where it is handy but for the majority of the cases it is simpler and faster to just calculate a random number.
Since shader variables are independent per fragment they are unable to re-use existing variables between them. The problem then becomes one of how to use a "good" random number seed. Irrational numbers seems to fit the bill to start with. Then it is just a 'simple' matter of picking a good "permute" function.
Here is some free code which does the trick:
// Input: It uses texture coords as the random number seed.
// Output: Random number: [0,1), that is between 0.0 and 0.999999... inclusive.
// Author: Michael Pohoreski
// Copyright: Copyleft 2012 :-)
// NOTE: This has been upgraded to version 3 !!
float random( vec2 p )
{
// We need irrationals for pseudo randomness.
// Most (all?) known transcendental numbers will (generally) work.
const vec2 r = vec2(
23.1406926327792690, // e^pi (Gelfond's constant)
2.6651441426902251); // 2^sqrt(2) (Gelfond–Schneider constant)
return fract( cos( mod( 123456789., 1e-7 + 256. * dot(p,r) ) ) );
}
To understand how this works if we break the formula down into its constituent parts it becomes easier to visualize what is going on:
const vec2 k = vec2(23.1406926327792690,2.6651441426902251);
float rnd0( vec2 uv ) {return dot(uv,k); }
float rnd1( vec2 uv ) { return 1e-7 + 256. + dot(uv,k); }
float rnd2( vec2 uv ) { return mod( 123456789., 256. * dot(uv,k) ); }
float rnd3( vec2 uv ) { return cos( mod( 123456789., 256. * dot(uv,k) ) ); }
// We can even tweak the formula
float rnd4( vec2 uv ) { return fract( cos( mod( 1234., 1024. * dot(uv,k) ) ) ); }
float rnd5( vec2 uv ) { return fract( cos( mod( 12345., 1024. * dot(uv,k) ) ) ); }
float rnd6( vec2 uv ) { return fract( cos( mod( 123456., 1024. * dot(uv,k) ) ) ); }
float rnd7( vec2 uv ) { return fract( cos( mod( 1234567., 1024. * dot(uv,k) ) ) ); }
float rnd8( vec2 uv ) { return fract( cos( mod( 12345678., 1024. * dot(uv,k) ) ) ); }
float rnd9( vec2 uv ) { return fract( cos( mod( 123456780., 1024. * dot(uv,k) ) ) ); }
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
mediump vec2 uv = fragCoord.xy / iResolution.xy;
float i = rnd9(uv);
fragColor = vec4(i,i,i,1.);
}
Paste the above into:
I've also created a "comparison" ShaderToy example with 2 noise functions, and 2 random functions:
Demo of using noise "[2TC 15] Speckle Cross Fade"
A "classic" random function, sometimes called snoise3
is this bad one:
return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);
If you want to compare "pseudo random" functions check out Dave's Hash without sine shader.
What you generally do when you want a random value in a pixel shader is to pass in a texture containing noise. While it's not actually "random" - it looks random.
For example, here's some code from a pixel shader I have lying around:
float3 random = (tex2D(noiseTexture, texCoord * noiseScale + noiseOffset));
The texture I use is an RGB-noise texture, which can come in handy some times. But the same technique would work for a grayscale one.
By scaling it I ensure that the pixels in the noise texture line up to on-screen pixels (you may also want to set the texture sampler to "point" mode so you don't blur the noise texture).
By using an offset you can scroll the texture - which is kind of like seeding a random number generator. Use a random offset if you want to avoid that "scrolling" look.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With