Vertex position relative to normal

心已入冬 提交于 2019-12-23 13:35:14

问题


In a surface shader, given the world's up axis (and the others too), a world space position and a normal in world space, how can we rotate the worldspace position into the space of the normal?

That is, given a up vector and a non-orthogonal target-up vector, how can we transform the position by rotating its up vector?

I need this so I can get the vertex position only affected by the object's rotation matrix, which I don't have access to.

Here's a graphical visualization of what I want to do:

  • Up is the world up vector
  • Target is the world space normal
  • Pos is arbitrary

The diagram is bidimensional, but I need to solve this for a 3D space.


回答1:


Looks like you're trying to rotate pos by the same rotation that would transform up to new_up.

Using the rotation matrix found here, we can rotate pos using the following code. This will work either in the surface function or a supplementary vertex function, depending on your application:

// Our 3 vectors
float3 pos;
float3 new_up;
float3 up = float3(0,1,0);

// Build the rotation matrix using notation from the link above
float3 v = cross(up, new_up);
float s = length(v);  // Sine of the angle
float c = dot(up, new_up); // Cosine of the angle
float3x3 VX = float3x3 (
    0, -1 * v.z, v.y,
    v.z, 0, -1 * v.x,
    -1 * v.y, v.x, 0
); // This is the skew-symmetric cross-product matrix of v
float3x3 I = float3x3 (
    1, 0, 0,
    0, 1, 0,
    0, 0, 1
); // The identity matrix
float 3x3 R = I + VX + mul(VX, VX) * (1 - c)/pow(s,2) // The rotation matrix! YAY!

// Finally we rotate
float3 new_pos = mul(R, pos);

This is assuming that new_up is normalized.

If the "target up normal" is a constant, the calculation of R could (and should) only happen once per frame. I'd recommend doing it on the CPU side and passing it into the shader as a variable. Calculating it for every vertex/fragment is costly, consider what it is you actually need.

If your pos is a vector-4, just do the above with the first three elements, the fourth element can remain unchanged (it doesn't really mean anything in this context anyway).

I'm away from a machine where I can run shader code, so if I made any syntactical mistakes in the above, please forgive me.




回答2:


Not tested, but should be able to input a starting point and an axis. Then all you do is change procession which is a normalized (0-1) float along the circumference and your point will update accordingly.

using UnityEngine;
using System.Collections;

public class Follower : MonoBehaviour {

    Vector3 point;
    Vector3 origin = Vector3.zero;
    Vector3 axis = Vector3.forward;
    float distance;
    Vector3 direction;
    float procession = 0f;  // < normalized

    void Update() {
        Vector3 offset = point - origin;

        distance = offset.magnitude;
        direction = offset.normalized;

        float circumference = 2 * Mathf.PI * distance;

        angle = (procession % 1f) * circumference;

        direction *= Quaternion.AngleAxis(Mathf.Rad2Deg * angle, axis);
        Ray ray = new Ray(origin, direction);

        point = ray.GetPoint(distance);
    }
}


来源:https://stackoverflow.com/questions/32257338/vertex-position-relative-to-normal

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!