顶点位移后动态重新计算法线

问题描述

任何人都可以让我知道我是否在正确的做法上:我有一个顶点着色器,它根据传入的点动态向外凸出(想想在地毯下运行的鼠标)。为了让光照正确更新,我需要在修改顶点位置后重新计算法线。我可以访问每个顶点以及原点。

我目前的想法是我做某种数学运算来确定切线/双切线并使用叉积来确定法线。我的数学能力不是很好,我需要怎么做才能确定这些向量?

这是我当前的垂直着色器:

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

    v.vertex.y = smoothstep(_LiftHeight,distancetoLift / _LifTradius) * 5;
}

enter image description here

解决方法

Ronja 在 this tutorial 中介绍了一个简单的解决方案,我将在此处进行总结,并根据您的具体情况进行修改。

首先,找到从当前点偏移少量切线和双切线(您可以从法线和切线计算)的两个点:

float3 posPlusTangent = v.vertex + v.tangent * 0.01;
worldPos = mul(unity_ObjectToWorld,posPlusTangent).xyz;
distanceToLift = distance(worldPos,_LiftOrigin);
posPlusTangent.y = smoothstep(_LiftHeight,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,distanceToLift / _LiftRadius) * 5;

然后,找到这些偏移量和新顶点pos的差值,找到新的切线和副切线,然后再做一次叉积,找到得到的法线:

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

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

总和:

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

    return smoothstep(_LiftHeight,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);
}

这是一种近似方法,但它可能已经足够好了!