탄젠트 및 비트 당 벡터를 계산하는 방법

three.js에 텍스처를로드 한 다음 셰이더로 전달했습니다. 정점 셰이더에서 법선을 계산하고 uv 벡터를 변수에 저장합니다.

<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>

T 및 B 벡터는 어떻게 계산합니까?



답변

우선, 모든 3D 정점마다 무한 탄젠트 및 바이 탄젠트 벡터가 있습니다. 아래 이미지는 각 정점에 대해 무한한 탄젠트 공간이있는 이유를 설명합니다. 탄젠트 및 비트 탄젠트는 표시된 평면에서 어떤 방향을 가질 수 있습니다.

따라서 가장 유용한 1 탄젠트 공간 을 올바르게 계산하려면 x 축 (탄젠트)이 범프 맵의 u 방향에 해당하고 y 축 (비트 탄젠트)이 v 방향에 해당하도록 탄젠트 공간을 정렬해야합니다. 범프 맵에서 접선 공간의 Z 방향에 이미 해당하는 정점의 법선이 이미 있어야합니다.

(1) 텍스처에서 법선 벡터를 샘플링하기를 원하기 때문에 가장 유용합니다.

그림과 함께 가장 잘 설명되므로, 접선 공간(u, v)아래 그림 과 같이 정렬 되기를 원합니다 .

컴퓨터 그래픽과 밀접하게 관련되어 있지는 않지만 이미지 소스

컴퓨터 그래픽에서 개발자는 일반적 (u,v)으로 텍스처 좌표라고도합니다. 우리는 T가 탄젠트이고 B가 비트 탄젠트라고 가정 P0하고 삼각형의 일부인 목표 버텍스 (P0,P1,P2)입니다.

먼저 우리가하고 싶었던 것을 기억하십시오.

  1. T는 u와 정렬되고 B는 v와 정렬됩니다.
  2. T와 B는 정점 법선이있는 평면 (위의 이미지에 표시된 평면)에 놓입니다.

요점은 이미 T와 B가 같은 평면에 있고 U와 V에 해당한다고 가정합니다. 값을 알면 제품과 세 번째 벡터를 교차하여 세계에서 탄젠트 공간으로 변환 행렬을 만들 수 있습니다.

우리는 2D 벡터가 두 개의 독립적 인 벡터 2 의 선형 조합으로 쓰여질 수 있다는 것을 알고 있으며 위의 이미지에 표시된 삼각형 점 (가장자리)이 이미 있기 때문에. 우리는 쓸 수있다:

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

E2 = (u2-u0) T + (v2-v0) B

(2) 실제로 이것이 기본 행렬이 도출되는 방식입니다

위의 방정식은 행렬 형식으로 작성 될 수 있습니다.

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

행렬 방정식을 풀면 T 및 B 값을 결정할 수 있으며 변환 행렬을 구성 할 수 있습니다.

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

전체 소스 코드와 파생은 여기 에서 찾을 수 있습니다 .