Làm thế nào để tính toán các vectơ tiếp tuyến và bitangent


9

Tôi có một kết cấu được tải trong ba.js, sau đó được chuyển đến các shader. Trong shader đỉnh tôi tính toán bình thường, và tôi lưu vào một biến vector vectơ.

<script id="vertexShader" type="x-shader/x-vertex">

                varying vec3 N,P;
                varying vec2 UV;

                void main() {
                    gl_Position= projectionMatrix * modelViewMatrix * vec4(position,1.0);
                    P= position;
                    N= normalMatrix * vec3(normal);
                    UV= uv;
                }
            </script>
            <script id="fragmentShader" type="x-shader/x-fragment">

                varying vec3 N,P;
                varying vec2 UV;
                uniform sampler2D texture;

                void main() {
                    gl_FragColor= texture2D(texture,UV);
                }

            </script>

Làm thế nào để tôi tính toán các vectơ T và B?


2
Bạn có muốn thuật toán nói chung hoặc cụ thể cho thư viện của bạn lựa chọn không?
Concept3d

Nếu tôi có thể tính toán nó với ba.js thì sẽ tốt hơn.
Ramy Al Zuhouri

Câu trả lời:


26

Trước hết, đối với mỗi đỉnh 3D có các vectơ tiếp tuyến và tiếp tuyến vô hạn. Hình ảnh dưới đây giải thích tại sao có vô số không gian tiếp tuyến cho mỗi đỉnh, tiếp tuyến và bitangent có thể có bất kỳ hướng nào trong mặt phẳng hiển thị.

Số lượng không gian tanget vô hạn cho mỗi đỉnh

Vì vậy, để sắp xếp đúng không gian 1 tiếp tuyến hữu ích nhất , chúng tôi muốn không gian tiếp tuyến của chúng ta được căn chỉnh sao cho trục x (tiếp tuyến) tương ứng với hướng u trong bản đồ va chạm và trục y (bitangent) tương ứng với hướng v trong bản đồ vết sưng, chúng ta đã có bình thường của đỉnh đã tương ứng với hướng Z trong không gian tiếp tuyến.

(1) hữu ích nhất vì cuối cùng chúng ta muốn các vectơ bình thường được lấy mẫu từ kết cấu

Điều đó tốt nhất được giải thích bằng hình ảnh, chúng tôi muốn không gian tiếp tuyến của chúng tôi được căn chỉnh như (u, v)hiển thị bên dưới.

nhập mô tả hình ảnh ở đây

Nguồn hình ảnh mặc dù không liên quan chặt chẽ đến đồ họa máy tính

Trong các nhà phát triển đồ họa máy tính thường sử dụng (u,v)còn được gọi là tọa độ kết cấu. Chúng ta sẽ giả sử T là tiếp tuyến và B là bitangent và P0là đỉnh đích của chúng ta, đó là một phần của tam giác (P0,P1,P2).

Trước tiên hãy nhớ những gì chúng tôi muốn làm, là tính toán tiếp tuyến và bitanget rằng:

  1. T thẳng hàng với u và B thẳng hàng với v.
  2. T và B nằm trong mặt phẳng với đỉnh bình thường (mặt phẳng thể hiện trong hình trên).

Vấn đề là chúng ta đã giả sử rằng T và B nằm trong cùng một mặt phẳng và tương ứng với U và V bây giờ nếu chúng ta có thể biết giá trị của chúng, chúng ta có thể xuyên qua sản phẩm và vectơ thứ ba để xây dựng ma trận biến đổi từ thế giới sang không gian tiếp tuyến.

nhập mô tả hình ảnh ở đây

Cho rằng chúng ta biết rằng bất kỳ vectơ 2D nào cũng có thể được viết dưới dạng kết hợp tuyến tính của hai vectơ độc lập 2 và vì chúng ta đã có các điểm tam giác (các cạnh), được hiển thị trong hình trên. Chúng tôi có thể viết:

E1 = (u1-u0) T + (v1-v0) B

E 2 = (u2-u0) T + (v2-v0) B

(2) thực sự đó là cách ma trận cơ sở được bắt nguồn

Phương trình trên có thể được viết dưới dạng ma trận,

| E1x E1y E1z |   | deltaU1 deltaV1 | * | Tx Ty Tz |
| E2x E2y E2z | = | deltaU2 deltaV2 |   | Bx By Bz |

Bằng cách giải phương trình ma trận, chúng ta có thể xác định giá trị T và B, chúng ta có thể xây dựng ma trận biến đổi.

Mã nguồn đầy đủ trong C ++

#include "Vector4D.h"


struct Triangle
{
    unsigned short  index[3];
};


void CalculateTangentArray(long vertexCount, const Point3D *vertex, const Vector3D *normal,
        const Point2D *texcoord, long triangleCount, const Triangle *triangle, Vector4D *tangent)
{
    Vector3D *tan1 = new Vector3D[vertexCount * 2];
    Vector3D *tan2 = tan1 + vertexCount;
    ZeroMemory(tan1, vertexCount * sizeof(Vector3D) * 2);

    for (long a = 0; a < triangleCount; a++)
    {
        long i1 = triangle->index[0];
        long i2 = triangle->index[1];
        long i3 = triangle->index[2];

        const Point3D& v1 = vertex[i1];
        const Point3D& v2 = vertex[i2];
        const Point3D& v3 = vertex[i3];

        const Point2D& w1 = texcoord[i1];
        const Point2D& w2 = texcoord[i2];
        const Point2D& w3 = texcoord[i3];

        float x1 = v2.x - v1.x;
        float x2 = v3.x - v1.x;
        float y1 = v2.y - v1.y;
        float y2 = v3.y - v1.y;
        float z1 = v2.z - v1.z;
        float z2 = v3.z - v1.z;

        float s1 = w2.x - w1.x;
        float s2 = w3.x - w1.x;
        float t1 = w2.y - w1.y;
        float t2 = w3.y - w1.y;

        float r = 1.0F / (s1 * t2 - s2 * t1);
        Vector3D sdir((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r,
                (t2 * z1 - t1 * z2) * r);
        Vector3D tdir((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r,
                (s1 * z2 - s2 * z1) * r);

        tan1[i1] += sdir;
        tan1[i2] += sdir;
        tan1[i3] += sdir;

        tan2[i1] += tdir;
        tan2[i2] += tdir;
        tan2[i3] += tdir;

        triangle++;
    }

    for (long a = 0; a < vertexCount; a++)
    {
        const Vector3D& n = normal[a];
        const Vector3D& t = tan1[a];

        // Gram-Schmidt orthogonalize
        tangent[a] = (t - n * Dot(n, t)).Normalize();

        // Calculate handedness
        tangent[a].w = (Dot(Cross(n, t), tan2[a]) < 0.0F) ? -1.0F : 1.0F;
    }

    delete[] tan1;
}

Mã nguồn đầy đủ và dẫn xuất có thể được tìm thấy ở đây .


Nếu tôi không có hình tam giác thì sao? Trong trường hợp của tôi, tôi có một kết cấu nên được áp dụng trên một hình cầu. Làm thế nào để thích ứng với trường hợp này?
Ramy Al Zuhouri

@RamyAlZuhouri không phải là hình cầu được xây dựng từ hình tam giác? Bạn chỉ cần lặp qua các đỉnh như trong mã. Nếu hình cầu của bạn không phải hình tam giác thì đó là một câu chuyện hoàn toàn khác.
concept3d

Tôi đang sử dụng ba.js SphereGeometry (bằng javascript). Có lẽ tôi nên chuyển tài sản mặt cho các shader? Hình cầu tôi vẽ có 1089 đỉnh và 1084 mặt.
Ramy Al Zuhouri

1
bạn tính toán không gian tiếp tuyến và sau đó chuyển tiếp tuyến cho các shader. Và bạn nên có quyền truy cập vào mặt / đỉnh để tính không gian tiếp tuyến.
Concept3d

Trong trường hợp của tôi, tôi sẽ có 1084 tiếp tuyến, làm cách nào để ánh xạ các tiếp tuyến với các đỉnh?
Ramy Al Zuhouri
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.