I'm attempting to write a simple wave-like shader in Unity 2017.1.0f3 using the sin function, however it's all an undefined one color shape without redefining the normals so it can get the shading right. However despite my maths I can't seem to get these normals to look right, and as you can see in the GIF it's all super messed up.
So here's what I'm doing:
void vert(inout appdata_full v, out Input o)
{
UNITY_INITIALIZE_OUTPUT(Input, o);
//Just basing the height of the wave on distance from the center and time
half offsetvert = o.offsetVert = ((v.vertex.x*v.vertex.x) + (v.vertex.z * v.vertex.z))*100;//The 100 is to compensate for the massive scaling of the object
half value = _Scale * sin(-_Time.w * _Speed + offsetvert * _Frequency)/100;
v.vertex.y += value;
o.pos = v.vertex.xyz;
}
// Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
// See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
// #pragma instancing_options assumeuniformscaling
UNITY_INSTANCING_CBUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_CBUFFER_END
void surf (Input IN, inout SurfaceOutputStandard o)
{
//Calculate new normals
//Refer to MATH (1) for how I'm getting the y
float3 norm = (0,sqrt(1/(1+1/(-100/(_Scale*_Frequency*cos(_Time.w * _Speed + IN.offsetVert * _Frequency))))),0);
//Refer to Math (2) for how I'm getting the x and z
float derrivative = _Scale*_Frequency*cos(-_Time.w * _Speed + IN.offsetVert * _Frequency)/100;
float3 norm = (0,sqrt(1/(1+1/(-1/(derrivative)))),0);
float remaining = 1 - pow(norm.y,2);
norm.x = sqrt(remaining/(1 + IN.pos.z*IN.pos.z/(IN.pos.x*IN.pos.x)));
norm.z = sqrt(1-norm.y*norm.y-norm.x*norm.x);
//Assume this is facing away from the center
if (IN.pos.z<0)
norm.z = -norm.z;
if (IN.pos.x<0)
norm.x = -norm.x;
//Flip the direction if necessary
if (derrivative > 0){
norm.x = -norm.x;
norm.z = -norm.z;
}
norm.y = abs(norm.y);
norm = normalize(norm);//Just to be safe
o.Albedo = _Color.rgb;
// Metallic and smoothness come from slider variables
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
o.Normal.xyz = norm;
}
MATH 1 If the y as a function of distance is
y = (scale/100)sin(time.w * speed + distance * frequency)
then
dy/d(distance) = (scale/100) * frequency * cos(time.w * speed + distance * frequency)
making the gradient of the normal of
y/(some x and z direction) -100/(scale * frequency * cos(time.w * speed + distance * frequency)).
We also know that
(y component)^2 + (some xz component)^2 = 1,
where
(y component)/(some xz component) = the normal gradient defined.
Solving these two simultaneous equations we get
y component = sqrt(1/(1+1/(gradient^2)))
MATH 2 We know that
(x component)/(z component) = (x position)/(z position)
and, by Pythagoras, that
(x component)^2 + (z component)^2 = 1 - (y component)^2
and solving these simultaneous equations we get
x component = sqrt((1 - (y component)^2)/(1 + (z position / x position)^2))
We can then get the z component through Pythagoras.
Please, let me know if you figure out what's wrong :)
