- Home /
Randomness in CG shaders
Hi all, I need a random value in the shader I'm creating now and I know I can pass a value from a script very easily but what if I want to avoid the script? Is there a way to generate a random constant inside a CG shader?
Answer by Aily · Jan 24, 2014 at 09:56 AM
float rand(float3 co)
{
return frac(sin( dot(co.xyz ,float3(12.9898,78.233,45.5432) )) * 43758.5453);
}
You can use it with world position of pixel in Unity's surface shaders , like this:
...
struct Input
{
....
float3 worldPos;
};
.....
void surf (Input IN, inout SurfaceOutput o)
{
o.Albedo = half3(rand(IN.worldPos));
o.Alpha = 1.0;
}
Hi, I wanted to dig this discussion up again :). I found this post looking for random numbers in cg, and had a play with this result.
Shader "Custom/noise" {
Properties {
_Color("Colour (RGBA)", Color) = (0,0,0,1)
_Res("Noise Resolution",Float)=128
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRA$$anonymous$$
#pragma surface surf Lambert
float4 _Color;
float _Res;
struct Input {
float2 uv: TEXCOORD;
float3 worldPos;
};
float rand(float3 myVector) {
return frac(sin( dot(myVector ,float3(12.9898,78.233,45.5432) )) * 43758.5453);
}
void surf (Input IN, inout SurfaceOutput o) {
float3 vWPos=IN.worldPos;
//float3 vTimeOffset=float3(1,1,1)*_Time[0];
//vWPos+=vTimeOffset;
vWPos*=_Res;
float Rand1=rand(round(vWPos));
vWPos+=float3(.5,.5,.5);
Rand1+=rand(round(vWPos));
vWPos-=float3(1,1,1);
//Rand1+=rand(round(vWPos));
Rand1/=2;
o.Albedo =float3(Rand1,Rand1,Rand1)*_Color.xyz;
o.Alpha = 1;
}
ENDCG
}
FallBack "Diffuse"
}
However, I cannot get more than 3 random calls on this and noticed that the patterns become recurrent very quickly. Ideally I'd need 4 calls for a perlin effect that I read about, but more importantly this would be more useful if it were faster anyway.
Is there an equation that doesn't require sin and cos? these are costly.
Also, is there a simpler version that just takes single values ins$$anonymous$$d of float3?
Ideally I'd like a cg function that could receive an int seed, and return an "apparently random number". Ranges that go above 256 values are not needed, a returned int of 0-100 would be fine, as would a returned float or half of 0 to 1 with an accuracy of about .01.
I am aware that you can use texture lookups, but pulling a number out of simple maths is surely faster than a texture lookup in some cases, particularly if you want worldspace based uvs? [edit] just to clarify I don't want "real" random numbers, the same seed should produce the same result.
Why not just add _Time to the rand-function like this: float rand(float3 co) { return frac(sin( _Time dot(co.xyz ,float3(12.9898,78.233,45.5432) )) 43758.5453); }
Answer by Dave-Carlile · Feb 15, 2013 at 03:24 PM
I don't believe there is a direct way to generate random numbers in a shader. You can fake it out though.
Create a texture that contains random values, set it to repeat
Use incoming vertex or uv values to "sample" the random number texture
Or you can implement your own function fairly easily by converting a standard psuedo-random algorithm...
http://en.wikipedia.org/wiki/Random_number_generators http://mathoverflow.net/questions/29494/pseudo-random-number-generation-algorithms
Thank you for your answer Dave. Very clarifying. I used this approach in the past. I think I'll end up using _Time and length(vertex) in some expression to generate my value. I also took a look to the noise function but people suggest to avoid using it. Thanks a lot. Sorry I can't vote you up yet.
if you need a single random value, you can gen it in a unityscript and use material.SetFloat
Answer by Lloyd_RedironLabs · Feb 13, 2016 at 05:44 AM
I know this is an old post, but I thought I'd share my variation of the above.
It adds metallic, smoothness, transparency, illumination, emission, allows textures and uses _Time for randomizing as suggested above - mostly just ripped from the Unity 5 shader concept.
If you want to see something cool, put a fade effect on it. Use a sphere. And make it red. It looks like a giant alien computer coming to eat your face!!!
Shader "Custom/Noise/NoiseRandomAdvanced" {
Properties{
_Color("Colour (RGBA)", Color) = (0,0,0,1)
_Res("Noise Resolution",Float) = 128
_MainTex("Color (RGB) Alpha (A)", 2D) = "white" {}
_Glossiness("Smoothness", Range(0,1)) = 0.5
_Metallic("Metallic", Range(0,1)) = 0.0
_Illum("Illumin (A)", 2D) = "white" {}
_Emission("Emission (Lightmapper)", Float) = 1.0
}
SubShader{
Tags{ "Queue" = "Transparent" "RenderType" = "Transparent" }
LOD 200
CGPROGRAM
#pragma surface surf Standard fullforwardshadows alpha
#pragma target 3.0
float4 _Color;
float _Res;
struct Input {
float2 uv: TEXCOORD;
float3 worldPos;
float2 uv_MainTex;
float2 uv_Illum;
};
half _Glossiness;
half _Metallic;
sampler2D _Illum;
fixed _Emission;
float rand(float3 myVector) {
return frac(sin(_Time[0] * dot(myVector ,float3(12.9898,78.233,45.5432))) * 43758.5453);
}
sampler2D _MainTex;
void surf(Input IN, inout SurfaceOutputStandard o) {
float3 vWPos = IN.worldPos;
vWPos *= _Res;
float Rand1 = rand(round(vWPos));
vWPos += float3(.5,.5,.5);
Rand1 += rand(round(vWPos));
vWPos -= float3(1,1,1);
Rand1 /= 2;
fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color * float4(Rand1, Rand1, Rand1, Rand1);
o.Albedo = c;
o.Metallic = _Metallic - (1- _Color.a);
o.Smoothness = _Glossiness;
o.Emission = c.rgb * tex2D(_Illum, IN.uv_Illum).a * _Emission.rrr;
o.Alpha = _Color.a;
}
ENDCG
}
FallBack "Diffuse"
}
I am getting completely black shader on the cube using this. How do you set it up?
Did you press Play? The shader will be black when not in play mode.
I am disappointed not seeing a screenshot of the cool thing :-(