【问题标题】:3D graphics mesh normal vector rotation is doubled3D图形网格法线向量旋转加倍
【发布时间】:2017-04-05 23:29:25
【问题描述】:

我目前正在实现一个模拟 OpenGL 的软件渲染器,作为基于 these lessons 的学习体验。我的项目代码可以是found here.

我在处理顶点法线时遇到了一些困难。我想用模型矩阵转换它们,我知道当矩阵不正交时我应该使用模型矩阵的逆转置。光照方向应在世界空间中指定,因此应转换法线,然后使用世界空间光照方向的点积来计算光照强度。

这就是问题所在。它在这里工作正常,请注意相机在向上轴旋转 45 度查看模型。

如果我在任何轴上将模型旋转 90 度,现在取向上轴,光线方向会翻转指向另一个方向。正如您在此处看到的那样,光线来自背面。

如果我旋转到 180 度,又可以了。

如果我旋转到 45 度,那么光点就会变成 90 度,如此处所示。注意尖峰以查看光的来源。

这让我困惑了好几个小时。我无法弄清楚出了什么问题。就好像在灯光下旋转加倍。光的矢量并没有改变,看这里:

vec4 SmoothShader::Vertex(int iFace, int nthVert)
{
    vec3 vposition = model->vert(iFace, nthVert);
    vec4 projectionSpace = MVP * embed<4>(vposition);

    vec3 light = vec3(0, 0, 1);

    mat4 normTrans = M.invert_transpose();
    vec4 normal =  normTrans * embed<4>(model->normal(iFace, nthVert), 0.f);
    vec3 norm = proj<3>(normal);
    intensity[nthVert] = std::max(0.0f, norm.normalise() * light.normalise());

    return projectionSpace;
}

bool SmoothShader::Fragment(vec3 barycentric, vec3 &Colour)
{
    float pixelIntensity = intensity * barycentric;
    Colour = vec3(255, 122, 122) * pixelIntensity;
    return true;
}

MVP(模型、视图、投影)和 M(模型)矩阵的计算如下:

// Model Matrix, converts to world space
mat4 scale = MakeScale(o->scale); 
mat4 translate = MakeTranslate(o->position);
mat4 rotate = MakeRotate(o->rotation);

// Move objects backward from the camera's position
mat4 cameraTranslate = MakeTranslate(vec3(-cameraPosition.x, -cameraPosition.y, -cameraPosition.z));

// Get the camera's rotated basis vectors to rotate everything to camera space.
vec3 Forward;
vec3 Right;
vec3 Up;
GetAxesFromRotation(cameraRotation, Forward, Right, Up);
mat4 cameraRotate = MakeLookAt(Forward, Up);

// Convert from camera space to perspective projection space
mat4 projection = MakePerspective(surf->w, surf->h, 1, 10, cameraFOV);

// Convert from projection space (-1, 1) to viewport space
mat4 viewport = MakeViewport(surf->w, surf->h);

mat4 M = translate * rotate * scale;
mat4 MVP = viewport * projection * cameraRotate * cameraTranslate * M;

知道我做错了什么吗?

【问题讨论】:

    标签: c++ matrix graphics 3d rotation


    【解决方案1】:

    您应该使用模型矩阵转换法线,而不是它的逆矩阵。您的光照表现如此,因为您正在以与顶点位置相反的方向旋转顶点法线。

    vec4 normal =  M * embed<4>(model->normal(iFace, nthVert), 0.f);
    

    为避免这种混淆,我建议使用命名方案advocated by Tom Forsyth,并将world_from_object 矩阵称为M,因为它是从对象 空间到世界的转换 空间。

    vec4 light_world = vec4(0.f, 0.f, 1.f, 0.f);
    vec4 normal_object = embed<4>(model->normal(iFace, nthVert), 0.f);
    vec4 normal_world = world_from_object * normal_object;
    float intensity = std::max(0.f, light_world * normal_world);
    

    如果你使用了这个方案,很明显你使用了错误的转换。

    mat4 object_from_world = world_from_object.invert_transpose();
    vec4 normal_world = object_from_world * normal_object; // wrong!
    

    我个人使用以下术语来描述不同的空间:

    • 对象空间 – 模型的局部坐标系
    • 视图空间 – 相机的局部坐标系
    • 灯光空间 – 灯光的局部坐标系
    • 世界空间 - 场景的全局坐标系
    • 剪辑空间 - 标准化的屏幕坐标

    因此,我将MVP 矩阵称为clip_from_object 矩阵。

    【讨论】:

    • 这个恐怕不行,没有变化。在这种情况下,模型矩阵是正交的,因此它的逆转置实际上是一回事。尽管许多在线资源表示,在进行非均匀缩放(非正交)时,例如在变换法线时需要逆转置。
    • @Constan7ine 对不起。我错误地认为你是乘以倒数。反向转置是有道理的。我再看看。
    • @Constan7ine 我看不到任何其他问题。我认为您的问题可能在您的代码库的其他地方。
    【解决方案2】:

    您将模型矩阵传递给着色器:

                    o->shader->M = cameraRotate * cameraTranslate * Model;
    

    所以实际的 M 矩阵不是模型矩阵而是模型视图矩阵,即你现在在模型视图空间中相乘。我不确定,但可能会导致结果模棱两可。

    【讨论】:

    • 是的,这是来自 github repo 不是吗,它有点过时了。上面的图像是在仅传递模型矩阵时拍摄的。不过还是谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-12
    • 1970-01-01
    • 1970-01-01
    • 2021-01-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多