SupriseMechanics
SupriseMechanics

Reputation: 31

Dynamically recalculating normals after vertex displacement

Can anyone let me know if I'm on the right tack with this: I have a vertex shader that bumps outward dynamically depending on a point passed in (think a mouse running under a rug). In order for the lighting to update properly, I need to recalculate the normals after modifying the vertex position. I have access to each vertex point as well as the origin.

My current thinking is I do some sort of math to determine the tangent / bitangent and use a cross product to determine the normal. My math skills aren't great, what would I need to do to determine those vectors?

Here's my current vert shader:

void vert(inout appdata_full v) 
{
    float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
    float distanceToLift = distance(worldPos, _LiftOrigin);

    v.vertex.y = smoothstep(_LiftHeight, 0, distanceToLift / _LiftRadius) * 5;
}

enter image description here

Upvotes: 1

Views: 3118

Answers (1)

Ruzihm
Ruzihm

Reputation: 20249

A simple solution is covered in this tutorial by Ronja, which I'll summarize here with modifications which reflect your specific case.

First, find two points offset from your current point by a small amount of tangent and bitangent (which you can calculate from normal and tangent):

float3 posPlusTangent = v.vertex + v.tangent * 0.01;
worldPos = mul(unity_ObjectToWorld, posPlusTangent).xyz;
distanceToLift = distance(worldPos, _LiftOrigin);
posPlusTangent.y = smoothstep(_LiftHeight, 0, distanceToLift / _LiftRadius) * 5;

float3 bitangent = cross(v.normal, v.tangent);

float3 posPlusBitangent = v.vertex + bitangent * 0.01;
worldPos = mul(unity_ObjectToWorld, bitangent).xyz;
distanceToLift = distance(worldPos, _LiftOrigin);
posPlusBitangent.y = smoothstep(_LiftHeight, 0, distanceToLift / _LiftRadius) * 5;

Then, find the difference between these offsets and the new vertex pos to find the new tangent and bitangent, then do another cross product to find the resulting normal:

float3 modifiedTangent = posPlusTangent - v.vertex;
float3 modifiedBitangent = posPlusBitangent - v.vertex;

float3 modifiedNormal = cross(modifiedTangent, modifiedBitangent);
v.normal = normalize(modifiedNormal);

Altogether:

float find_offset(float3 localV)
{
    float3 worldPos = mul(unity_ObjectToWorld, localV).xyz;
    float distanceToLift = distance(worldPos, _LiftOrigin);

    return smoothstep(_LiftHeight, 0, distanceToLift / _LiftRadius) * 5;
}

void vert(inout appdata_full v) 
{
    v.vertex.y = find_offset(v.vertex);

    float3 posPlusTangent = v.vertex + v.tangent * 0.01;
    posPlusTangent.y = find_offset(posPlusTangent);

    float3 bitangent = cross(v.normal, v.tangent);

    float3 posPlusBitangent = v.vertex + bitangent * 0.01;
    posPlusTangent.y = find_offset(posPlusBitangent);

    float3 modifiedTangent = posPlusTangent - v.vertex;
    float3 modifiedBitangent = posPlusBitangent - v.vertex;

    float3 modifiedNormal = cross(modifiedTangent, modifiedBitangent);
    v.normal = normalize(modifiedNormal);
}

This is a method of approximation, but it may be good enough!

Upvotes: 2

Related Questions