Xoay một vectơ bởi một vectơ khác trong shader


8

Tôi có một bề mặt địa hình với một điểm bình thường cho mỗi điểm trên địa hình.

Tôi có một chi tiết thứ hai bản đồ bình thường được áp dụng cho địa hình.

  • Các quy tắc này là trong 3 không gian.

  • Giá trị Y của cả hai giá trị luôn dương.

  • Các giá trị X, Z của cả hai quy tắc có thể dương / âm / không.

  • Vectơ bình thường thứ nhất (màu xanh) mà chúng ta đang xoay vectơ thứ 2 (màu cam) theo, có thể gần như nằm ngang.

Tôi ổn với một giải pháp gần đúng nếu nó làm cho nó dễ dàng hơn / nhanh hơn để tính toán. Hình ảnh bề mặt màu xanh bình thường, bản đồ màu cam bình thường bình thường và kết quả màu xanh lá cây mong muốn bình thường.

Trong hình trên, bạn thấy bề mặt màu xanh bình thường (từ bản đồ bình thường thứ 1), bản đồ bình thường màu cam bình thường (từ bản đồ bình thường thứ 2) và kết quả màu xanh lá cây mong muốn bình thường.

Số lượng mà vectơ màu cam được xoay phải xấp xỉ (hoặc nếu có thể chính xác) bằng với góc mà vectơ bình thường màu xanh tạo thành với mặt phẳng XZ (trong đó Y lên, giống như hệ tọa độ DirectX).

Đây là một kịch bản thứ hai:

Bề mặt màu xanh bình thường gần như nằm ngang, do đó bản đồ bình thường thứ 2 đang được áp dụng cho bề mặt gần như thẳng đứng, do đó, vectơ bản đồ màu cam bình thường được xoay thêm

Trong hình trên, bề mặt màu xanh bình thường gần như nằm ngang, do đó bản đồ bình thường thứ 2 đang được áp dụng cho bề mặt gần như thẳng đứng, do đó, vectơ bản đồ màu cam bình thường được xoay thêm.

Việc xoay vòng đang được thực hiện trong một shader HLSL.

Làm cách nào để xoay quả cam thứ 1 bình thường dựa trên hướng của màu xanh thứ 2 bình thường?

Chỉnh sửa: Có lẽ tôi cần một tiếp tuyến và bitangent như bình thường?

Đây là cách tôi có được bình thường:

float4 ComputeNormals(VS_OUTPUT input) : COLOR
{
    float2 uv = input.TexCoord;

    // top left, left, bottom left, top, bottom, top right, right, bottom right
    float tl = abs(tex2D(HeightSampler, uv + TexelSize * float2(-1, -1)).x);
    float  l = abs(tex2D(HeightSampler, uv + TexelSize * float2(-1,  0)).x);
    float bl = abs(tex2D(HeightSampler, uv + TexelSize * float2(-1,  1)).x);
    float  t = abs(tex2D(HeightSampler, uv + TexelSize * float2( 0, -1)).x);
    float  b = abs(tex2D(HeightSampler, uv + TexelSize * float2( 0,  1)).x);
    float tr = abs(tex2D(HeightSampler, uv + TexelSize * float2( 1, -1)).x);
    float  r = abs(tex2D(HeightSampler, uv + TexelSize * float2( 1,  0)).x);
    float br = abs(tex2D(HeightSampler, uv + TexelSize * float2( 1,  1)).x);

    // Compute dx using Sobel filter.
    //           -1  0  1 
    //           -2  0  2
    //           -1  0  1
    float dX = tr + 2*r + br - tl - 2*l - bl;

    // Compute dy using Sobel filter.
    //           -1 -2 -1 
    //            0  0  0
    //            1  2  1
    float dY = bl + 2*b + br - tl - 2*t - tr;

    // Compute cross-product and renormalize
    float3 N = normalize(float3(-dX, NormalStrength, -dY));    

    // Map [-1.0 , 1.0] to [0.0 , 1.0];
    return float4(N * 0.5f + 0.5f, 1.0f);
}

Làm thế nào tôi có thể có được các vectơ tiếp tuyến và bitangent sau đó?

Có đủ để lấy sản phẩm chéo của bình thường với vectơ đơn vị trục Z để tìm vectơ tiếp tuyến không? (Vì bình thường. Y luôn dương, trong đó Y lên và Z đang chỉ vào màn hình của bạn).

Và sau đó lấy vectơ tiếp tuyến đó và vượt qua nó với bình thường để thu được bitangent?

Và sau đó lấy các bình thường, tiếp tuyến và bitangent với nhau để tạo thành một ma trận xoay để xoay vector bản đồ bình thường màu cam?

Ngay cả khi điều đó hoạt động, điều này có vẻ như rất nhiều tính toán cho một shader pixel. Điều này có hiệu quả không, và có cách nào hiệu quả hơn không?

Biên tập:

Hình ảnh này có thể giúp bạn hiểu những gì tôi đang cố gắng làm: Ánh xạ bình thường.

Nếu bạn biết ánh xạ bình thường là gì, thì điều này nên đơn giản, tôi nghĩ vậy.

Tôi đang cố gắng lấy bản đồ bình thường, chứa các quy tắc khác nhau và áp dụng chúng lên một bề mặt. Bề mặt có quy tắc riêng của nó. Bản đồ bình thường sẽ chứa nhiều quy tắc hơn bề mặt, do đó, một số quy tắc bản đồ bình thường được lấy mẫu trên một phần của bề mặt chỉ có một bình thường duy nhất.


Câu hỏi của bạn không có ý nghĩa. Bạn không thể xoay một vectơ bằng một vectơ (bạn có thể xoay quanh một vectơ và trên thực tế bạn phải chỉ định một trục xoay trong 3 không gian). Bạn có thể tìm góc tạo thành một vectơ với mặt phẳng. Nhưng trong trường hợp bề mặt bình thường, nó được xác định là 90 độ. Ngoài ra, xoay một vectơ 90 độ sẽ không có ý nghĩa trừ khi bạn chỉ định, như tôi đã đề cập, trục quay.
Andrew Russell

Bề mặt bình thường là 90 độ so với bề mặt và một số góc khác từ mặt phẳng XZ (với Y hướng lên và Z hướng về màn hình, như trong Direct X). Tôi muốn xoay vectơ theo góc mà hình dạng bình thường với mặt phẳng XZ.
Olhovsky

Những gì tôi đang cố gắng làm là áp dụng bản đồ bình thường lên bề mặt địa hình. Tôi đoán một cách là tìm vectơ tiếp tuyến và bitangent, sau đó sử dụng các vectơ có bề mặt bình thường để tạo thành ma trận, mà tôi nhân với bản đồ bình thường màu cam bình thường, để thu được kết quả màu xanh lá cây bình thường. Tôi không chắc chắn làm thế nào để tìm bitangent và tiếp tuyến, hoặc nếu có một cách dễ dàng hơn. Tôi đã cập nhật câu hỏi của mình để cho thấy cách tôi nhận được bình thường từ địa hình.
Olhovsky

Tôi đã thêm một hình ảnh khác vào câu hỏi của mình để làm rõ hơn những gì tôi đang cố gắng làm. Không có gì thực sự ưa thích, chỉ là bản đồ bình thường.
Olhovsky

Sơ đồ mới nhất của bạn làm cho nhiều ý nghĩa hơn.
Andrew Russell

Câu trả lời:


4

Tôi nghĩ rằng tôi nhận được câu hỏi của bạn, mặc dù tiêu đề và 2 hình ảnh đầu tiên là khó hiểu.

Vấn đề là, có một bản đồ bình thường được ánh xạ trên mặt phẳng xz (vectơ màu cam). Bây giờ mặt phẳng thực được mô tả bởi màu xanh bình thường không nhất thiết phải nằm trong mặt phẳng xz, vì vậy người ta phải tìm một phép quay từ mặt phẳng xz sang mặt phẳng thực, để xoay vectơ màu cam để ánh xạ nó trên mặt phẳng thực.

Bây giờ làm thế nào chúng ta có thể làm điều đó? Về cơ bản, chúng ta phải tìm phép quay, quay mặt phẳng xz sang mặt phẳng được mô tả bởi màu xanh bình thường.

  1. Tìm trục xoay bằng cách lấy sản phẩm chéo của màu xanh bình thường và bình thường từ mặt phẳng xz {0, 1, 0}, vectơ màu cam phải được xoay quanh trục này.

    axis = N_xz cross N_blue
  2. Tìm góc quay bằng cách lấy arccosine của sản phẩm chấm của cả hai quy tắc

    angle = acos(N_xz dot N_blue)
  3. Bây giờ tạo ma trận xoay từ các giá trị đó và biến đổi màu cam bình thường với ma trận đó.


Những gì bạn mô tả có thể hoạt động, nhưng tôi không thích làm tất cả những điều đó trong một shader, 920000 lần trên mỗi khung hình (tức là một lần trên mỗi pixel). Tôi chỉ có thể tạo thành một ma trận từ bình thường, chéo (bình thường, float3 (1,0,0)) và chéo (bình thường, float3 (0,0,1))? Tôi đang thử điều đó ngay bây giờ, mặc dù nó dường như không hoạt động.
Olhovsky

Tôi đã thêm một hình ảnh vào câu hỏi của mình để làm rõ hơn những gì tôi đang cố gắng làm. Không có gì thực sự ưa thích, chỉ là bản đồ bình thường.
Olhovsky

Bạn có thể tính toán ma trận đó một lần trên mỗi mặt phẳng trong shader đỉnh và chuyển nó tới pixel-shader hoặc tính toán nó trên CPU trên mỗi mặt phẳng và truyền ma trận đó cho shader. Nhưng điều này là tối ưu hóa, trước tiên hãy làm cho nó chạy và hiểu cách thực hiện, sau đó tối ưu hóa nó;)
Maik Semder

Ok, thử nó ngay bây giờ.
Olhovsky

Điều này hoạt động, +1. Cảm ơn. Tuy nhiên, nó hơi chậm. Tôi đang cố gắng tìm hiểu làm thế nào tôi có thể lưu trữ một phần tư cho ma trận xoay vòng này thay vì lưu trữ màu xanh bình thường, và sau đó xây dựng bình thường từ phần tư đó. Bạn có biết làm thế nào tôi có thể làm điều đó?
Olhovsky

2

Tôi tin rằng bạn đang nghĩ về vấn đề của bạn.

Để quay lại ánh xạ chuẩn thông thường, bạn thường có 3 vectơ: bề mặt bình thường, vectơ tiếp tuyến vuông góc với vectơ đó và một vectơ vuông góc với cả hai vectơ đó. Cùng nhau, chúng tạo thành không gian tiếp tuyến trong đó bản đồ bình thường mô tả một hướng. Với thiết lập này, bạn chỉ cần nhân mỗi lần đọc thành phần của bản đồ bình thường với trục không gian tiếp tuyến tương ứng của nó và tính tổng kết quả.

Vì vậy, bạn có một địa hình và một bản đồ bình thường mà bạn muốn áp dụng cho nó. Bạn đã tìm ra cách lấy chiều cao delta và tạo bề mặt bình thường. Bây giờ bạn đang tìm kiếm tiếp tuyến và cotangent.

Vậy tiếp tuyến là gì? Chà, từ mô tả ở trên, thực ra đó là hướng 3D được mô tả bởi một trong các trục UV trong ánh xạ kết cấu của bạn. Rốt cuộc, đó là những gì nó được sử dụng cho, phải không? Vậy làm thế nào để bạn tìm thấy hướng này? Chà, được cho một lưới tùy ý, nó hơi khó; nhưng với địa hình lưới thông thường, điều đó là hiển nhiên: nếu bạn có lưới thường xuyên với các tia cực tím thông thường, thì các hướng tiếp tuyến và hướng động chỉ là hai cạnh thẳng hàng rời khỏi đỉnh của bạn.

Vì vậy, bạn biết chiều cao của các điểm xung quanh bạn là gì. Bạn có thể vượt qua bao xa những điểm này trên máy bay. Trừ đi những điểm từ điểm bạn đang ở, bình thường hóa và ở đó bạn đi: không gian tiếp tuyến tức thì.


+1 vì đây là một đóng góp hữu ích. Tôi biết rằng bạn có thể tạo thành một ma trận cơ bản biến chúng ta thành không gian tiếp tuyến (đó là những gì bạn đang đề xuất tôi nghĩ). Tôi không biết làm thế nào để hình thành nó. Câu trả lời của bạn ám chỉ về cách bắt đầu, nhưng không thực sự chỉ ra cách thực hiện. Ví dụ: "Bạn có thể vượt qua khoảng cách các điểm này trên máy bay. Trừ đi những điểm đó từ điểm bạn đang đi, bình thường hóa và ở đó bạn đi: không gian tiếp tuyến tức thì." thực sự mơ hồ. Hiện tại bản đồ bình thường của tôi không ở trong không gian tiếp tuyến. Bây giờ là 4 giờ sáng, vì vậy tôi sẽ xem lại vào ngày mai.
Olhovsky

0

Nếu bạn cũng có tiếp tuyến, bạn có thể sử dụng chức năng này để tạo ra sự va chạm bình thường mà bạn đang theo đuổi. Nếu không, hãy thoải mái đạo văn trong tương lai!

//------------------------------------------------------------------------------
// Transforms a normal map sample to world space.
//------------------------------------------------------------------------------


  float3 NormalSampleToWorldSpace(float3 normalMapSample, float3 unitNormalW, float3 tangentW)
    {
        // Uncompress each component from [0,1] to [-1,1].
        float3 normalT = 2.0f * normalMapSample - 1.0f;

        // Build orthonormal basis.
        float3 N = unitNormalW;
        float3 T = normalize(tangentW - dot(tangentW, N) * N);
        float3 B = cross(N, T);

        float3x3 TBN = float3x3(T, B, N);

        // Transform from tangent space to world space.
        float3 bumpedNormalW = mul(normalT, TBN);

        return bumpedNormalW;
    }

-1

Bạn có thể sử dụng hàm LERP, điều này sẽ nội suy tuyến tính giữa hai vectơ. Cung cấp cho bạn một 'bình thường' tốt đẹp bình thường.

http://msdn.microsoft.com/en-us/l Library / bb509618 (VS85) .aspx

(Tất nhiên điều này không thực sự xoay, nhưng với vectơ màu cam và màu xanh lam, điều này sẽ giúp bạn có được vectơ màu xanh lá cây)


Cảm ơn. Tuy nhiên, vectơ màu xanh có thể được nâng lên khoảng 30 độ từ mặt phẳng XZ, trong trường hợp đó, việc nới lỏng sẽ không hoạt động (trừ khi tôi thiếu thứ gì đó?). Tôi nhận ra hình ảnh của bạn btw, bạn đã viết một ví dụ tìm kiếm A * cho XNA.
Olhovsky

Này, đó là tôi :). Về câu hỏi của bạn, tôi nghĩ rằng việc nới lỏng vẫn sẽ hoạt động, bạn có thể phải tái chuẩn hóa vector mặc dù. Nếu nó không hoạt động, bạn có thể cung cấp một trường hợp thử nghiệm nhỏ (không phải trong HLSL, Vector của XNA cũng có Lerp) với hai vectơ, kết quả mong đợi và kết quả trả về thì tôi có thể nói cho bạn biết thêm .
Roy T.

Tôi đã thêm một hình ảnh thứ hai để minh họa tại sao việc nới lỏng không hoạt động. Bản đồ bình thường màu cam phải được xoay để áp dụng cho bề mặt. Ví dụ: nếu bạn có một bề mặt bình thường bằng với bản đồ bình thường màu da cam bình thường, nhưng cả hai đều được xoay, thì việc nới lỏng bất kỳ số tiền nào sẽ chỉ cung cấp cho bạn cùng một vectơ và sẽ không xảy ra xoay vòng nào.
Olhovsky
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.