【问题标题】:Vertex position relative to normal相对于法线的顶点位置
【发布时间】:2015-08-27 18:54:01
【问题描述】:

在表面着色器中,给定世界的上轴(以及其他轴)、世界空间位置和世界空间中的法线,我们如何将世界空间位置旋转到正常吗?

即给定一个上向量和一个非正交的目标上向量,我们如何通过旋转它的上向量来变换位置?

我需要这个,所以我可以获得仅受对象旋转矩阵影响的顶点位置,而我没有可以访问。

这是我想要做的图形可视化:

  • 向上是世界向上向量
  • 目标是世界空间法线
  • 位置是任意的

该图是二维的,但我需要为 3D 空间解决这个问题。

【问题讨论】:

  • 您没有将旋转矩阵传递给着色器是否有原因?这似乎是最明显的解决方案,所以我假设缺少一些信息。
  • @rutter Unity 不会将旋转矩阵暴露给着色器,根据 2 小时的谷歌搜索。要是这么简单!
  • 我对 Unity 着色器的攻击不多,但 this manual page 建议您应该有几个可用的矩阵。听起来你想要UNITY_MATRIX_IT_MV
  • @rutter MV 的逆向转置不就是从相机空间到世界空间的转换吗?我需要的是_World2Object之类的东西,但只有旋转部分。
  • 如果你使用_World2Object 的左上角3x3 部分,你会得到一个只有旋转和缩放的矩阵。对于哪种效果,您需要转换后的位置?用例似乎有点不寻常,也许有另一种方法可以解决您的问题。

标签: unity3d transform hlsl cg geometry-surface


【解决方案1】:

看起来您正试图以将 up 转换为 new_up 的相同旋转方式旋转 pos

使用找到here 的旋转矩阵,我们可以使用以下代码旋转pos。这将在表面函数或补充顶点函数中起作用,具体取决于您的应用程序:

// 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
float3x3 R = I + VX + mul(VX, VX) * (1 - c)/pow(s,2) // The rotation matrix! YAY!

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

这是假设 new_up 已标准化。

如果“目标向上正常”是一个常数,则 R 的计算可以(并且应该)每帧只发生一次。我建议在 CPU 端执行此操作并将其作为变量传递到着色器中。为每个顶点/片段计算它的成本很高,请考虑您实际需要什么。

如果您的 pos 是 vector-4,只需对前三个元素执行上述操作,第四个元素可以保持不变(无论如何,在这种情况下它实际上并没有任何意义)。

我远离可以运行着色器代码的机器,所以如果我在上面犯了任何语法错误,请原谅我。

【讨论】:

    【解决方案2】:

    未经测试,但应该能够输入起始pointaxis。然后你所做的就是改变procession,这是一个标准化的(0-1)沿圆周浮动,你的点会相应地更新。

    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);
        }
    }
    

    【讨论】:

    • 由于标签和第一句话,我认为这很清楚,我需要在着色器中执行此操作。具体来说,一个表面着色器。在着色器中,只有矩阵和向量需要处理。没有四元数或射线。
    • @Zze HLSL 是什么意思?
    • @Minzkraut 高级着色器语言,它是一种用于在 DirectX 平台上编程视觉效果的语言。