summaryrefslogtreecommitdiffstats
path: root/face_me.cginc
blob: e048dfdf5920b6b71878d45cfe1c00fa45c49f93 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#ifndef __FACE_ME_INC
#define __FACE_ME_INC

#include "cnlohr.cginc"
#include "interpolators.cginc"

#if defined(_FACE_ME)
// Rotate the object's position and normal so that it always faces the camera.
void face_me(inout appdata v) {
  [branch]
  if (_Face_Me_Enabled_Dynamic) {
    float3 object_center = mul(unity_ObjectToWorld, float4(0, 0, 0, 1));
    // Get forward axis of object coordinate system, i.e. the orientation of
    // the hip bone.
    // Then project it onto the xz plane.
    float3 forward_axis = mul(unity_ObjectToWorld, float3(0, 0, 1));
    forward_axis.y = 0;
    forward_axis = normalize(forward_axis);
    float4 worldPos = mul(unity_ObjectToWorld, v.vertex);
    float3 rd = normalize(object_center - getCenterCamPos());
    // We apply a factor of -1 to shift the result forward by a phase shift of pi.
    float cos_t = -dot(normalize(rd.xz), forward_axis.xz);
    // We want to get sin(t) using the identity:
    //   || a x b || = || a || || b || sin(t)
    // For normal vectors, this simplifies to:
    //   || a x b || = sin(t)
    // The issue is that the norm operator loses the sign.
    // We can estimate the sign by assuming that `rd` and `forward_axis` are on
    // the xz plane.
    // If that's the case, then the cross product is necessarily constrained to
    // the y axis.
    float sin_t_sign = sign(cross(rd, forward_axis).y);
    // Here we use the identity:
    //   sin(t) = sqrt(1 - cos(t)^2)
    // We simply apply the sign correction `sin_t_sign` to the result.
    // We then invert it, since the goal is not to amplify the rotation, but
    // to negate it.
    // Finally, we add a phase correction to make the abomination face us.
    float sin_t = -sqrt(1 - cos_t * cos_t) * sin_t_sign;

    // Double the angle using double-angle formulas
    if (isVR()) {
        float sin_2t = 2 * sin_t * cos_t;
        float cos_2t = cos_t * cos_t - sin_t * sin_t;  // or: 2 * cos_t * cos_t - 1
        sin_t = sin_2t;
        cos_t = cos_2t;
    }

    // Use the doubled angle values in your rotation matrix
    float2x2 face_me_rot = float2x2(cos_t, -sin_t, sin_t, cos_t);
    worldPos.xz = mul(face_me_rot, (worldPos.xz - object_center.xz)) + object_center.xz;
    v.vertex = mul(unity_WorldToObject, worldPos);

    float3 world_normal = UnityObjectToWorldNormal(v.normal);
    world_normal.xz = mul(face_me_rot, world_normal.xz);
    v.normal = normalize(mul(unity_WorldToObject, world_normal));

    float3 world_tangent = UnityObjectToWorldDir(v.tangent.xyz);
    world_tangent.xz = mul(face_me_rot, world_tangent.xz);
    v.tangent.xyz = normalize(mul(unity_WorldToObject, world_tangent));
  }
}
#endif  // _FACE_ME

#endif  // __FACE_ME_INC