Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Page151 7.2.3(9)v.tangent.w #32

Closed
777777716 opened this issue Aug 22, 2016 · 11 comments
Closed

Page151 7.2.3(9)v.tangent.w #32

777777716 opened this issue Aug 22, 2016 · 11 comments

Comments

@777777716
Copy link

在原文中写到:“因为和切线与法线方向都垂直的方向有两个”
但是实际上,两向量叉乘只会得到一个向量,因为计算时只会使用两个法则之一,所以得到的结果应该是确定的。
那为何还需要w来确定方向呢?
这个w又是从何而来呢?

@candycat1992
Copy link
Owner

其实在定义切线空间的时候,Z轴方向即切线方向是有无数条的,建模软件在计算切线的时候会选择和UV展开方向相同的那个方向作为切线方向,这样的选择导致如果直接用法线插乘切线的方向也一定会和UV相关,但这可能不是模型的法线方向(可能是反的),因此w会再存储一个信息。

@777777716
Copy link
Author

777777716 commented Aug 23, 2016

@candycat1992
书中说到“w分量决定副切线的方向性”,但如你所说,叉乘的结果可能是相反的,也可能是正确的。
又依据书中给出的计算方法,叉乘结果 * w = 正确结果
1.叉乘结果正确 -> w = 1
2.叉乘结果相反 -> w = -1
w在存储值时事先做了处理,预知了叉乘结果,并将对应“修正值”存储。
所以w并不能决定副切线的方向性,而应该是修正因子(标量)。

@candycat1992
Copy link
Owner

我不明白你的意思,在我看来乘以w决定了副切线到底取哪个方向,这里说的方向性不是说左手性右手性,而是说方向,有什么不对么?

@777777716
Copy link
Author

777777716 commented Aug 24, 2016

@candycat1992
1.w受什么控制(从何而得)
2.w可能的取值
3.叉乘方向 * w = 正确方向
4.
if(叉乘方向 == 正确方向){
//w为1
}else{
//w为-1
}
5.在我看来w已经是事先知道的,那么意味着这就预知了“叉乘方向和正确方向之间的关系”。
6.如果仅仅知道w就可以知道正确方向,那“.叉乘方向 * w = 正确方向”这个想法就是错的,打个比方,一维方向,w表示方向(-1或1),那应该是|A| * w = B,而不是 A * w = B

@candycat1992
Copy link
Owner

是不是有些在概念上太钻牛角尖了?叉乘的结果会得到一个方向,然后w分量又进一步决定了最终的正确方向,没有人说仅仅知道w就可以知道正确方向呀~因为我对建模软件不够了解,因此我没法跟你解释清楚它们到底是怎么生成的。如果你这么想了解可以去建模软件自己生成一个法线贴图试一下呗。你的第5点我没看出来和我的观点有任何冲突的地方啊,本来就都是已知的,建模软件对法线纹理的生成是一个encode的过程,而我们在shader里做的是一个decode的工程,这样说你是不是会明白一些

@777777716
Copy link
Author

@candycat1992
抱歉,这牛角确实钻得太深了。
我上一条回复内容只是简述了一下我的思考顺序和逻辑,感觉自己直接用类代码的东西可能更容易表述意思,并不是提问。
其实理论我都懂了,主要是书中说到“w分量决定副切线的方向性”,我将这句话理解成 “w分量代表了副切线的方向性”,然后认为这句话与实际情况不符。
给你带来困扰,实在不好意思

@candycat1992
Copy link
Owner

没关系~说不定下一次钻牛角尖就能发现新的东西呀

感谢对书的支持 :)

@xwc2021
Copy link

xwc2021 commented May 22, 2017

@777777716

這個問題我之前剛好有測試過

可以把下面這個script放到顯示你模型的GameObject上。

public class PeekGeo : MonoBehaviour {

    public Transform r;//自己準備1個紅色球
    public Transform g;//自己準備1個綠色球
    public Transform b;//自己準備1個藍色球
    public void createGS()
    {
        Mesh mesh = GetComponent<MeshFilter>().sharedMesh;

        int triCount = mesh.triangles.Length / 3;
        int[] triangles = mesh.triangles;

        Vector4[] tangents = mesh.tangents;
        Vector3[] normals = mesh.normals;
        Vector3[] vertices = mesh.vertices;
        for (int i = 0; i < triCount; i++)
        {
            //只查看你要的索引資料
            //if (i != 33 && i != 18)
            //    continue;

            int v0 = triangles[3 * i];
            int v1 = triangles[3 * i + 1];
            int v2 = triangles[3 * i + 2];





            Vector3 center = transform.position + vertices[v0] ;
            Instantiate(r, center, Quaternion.identity, this.transform);

            //紅球指向綠球就是normal的方向
            Vector3 normal = center + normals[v0];
            Instantiate(g, normal, Quaternion.identity, this.transform);

            //紅球指向藍球就是tangent的方向
            Vector3 temp = new Vector3(tangents[v0].x, tangents[v0].y, tangents[v0].z);
            Vector3 tangent = center + temp;
            Transform obj=Instantiate(b, tangent, Quaternion.identity, this.transform);
            obj.name = obj.name + "[("+i+")tangent.w=" + tangents[v0].w + "]";

            center = transform.position + vertices[v1];
            Instantiate(r, center, Quaternion.identity, this.transform);

           

            center = transform.position + vertices[v2];
            Instantiate(r, center, Quaternion.identity, this.transform);

           

        }

    }
}

再把下面這個script放到路徑Assets/Editor/的資料夾下。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(PeekGeo))]
public class PeekGeoEditor : UnityEditor.Editor
{

    PeekGeo behavior;
    void OnEnable()
    {
        behavior = (PeekGeo)target;
    }

    public override void OnInspectorGUI()
    {
        DrawDefaultInspector();

        if (GUILayout.Button("createGS"))
        {
            behavior.createGS();
        }

    }
}

(一個三角形有3個頂點ABC,它們的uv分別是abc,頂點順序是A->B->C)
測試後應該可以發現,如果你的模形裡的三角形存放的uv是正常的(abc的uv順序是順時針)
那麼tangent的w都是-1。

以下解釋原因:
3dsmax是使用右手座標來觀看和建立資料,在3dsmax裡:
(這裡是用右手法則作cross)
(一)當三角型的uv是正常的(abc的uv順序是順時針),tangent的w會是1,在3dsmax裡計算 cross (N,T)*tangent.w就可得到正確的BN。
(二)當三角型的uv是鏡射的(abc的uv順序是逆時針),tangent的w會是-1,在3dsmax裡計算 cross (N,T)*tangent.w就可得到正確的BN。

//////////////////////////分隔線

再來看看當模型從3dsmax被放入unity會怎麼被處理:

在3dsmax是z軸向上,假設在3dsmax裡建了1個模型,這個模型是一隻打開的右手掌,而且中指的方向指向世界的z軸,那這個手掌在3dsmax裡看起來就是立起來的(手心指向負y軸)。
unity是左手座標(在還沒到view space前都是),而且y軸向上.

「如果把3dsmax建的右手掌,沒作任何處理」就放進unity render,畫出來會是什麼?
你可以自己在筆記本上畫看看...
(具體的作法是,畫3軸和手簡單的輪廓,然後想像當這裡的資料,被放進那樣的3軸,會發生什麼事)

應該會發現1個「左」手掌平放在世界上,而且手背是向上的(在unity裡Y軸向上)
變成左手了,而且是平放的,這樣看起來跟3dsmax裡完全不一樣阿怎麼辦?

所以Unity「可以」對資料作下面這樣的處理
(1)先定義這樣的3軸對從3dsmax來的模型資料作一次轉換
X軸 (-1,0,0)
Y軸 (0,1,0)
Z軸 (0,0,1)
這樣一來至少可以得到平放的「右」手掌

(2)然後他對物件transform的x軸旋轉了-90度
這樣右手掌就又立起來了。

至於tangent,在Unity裡可以選擇[import]或是[cauculate tangent space]。
(tangent可以透過uv和vertex算出來)

不管選了那一個,因為有發生(1)這個轉換,所以normal和tangent在進入Unity後也被改變了。
這時在shader(用左手法則)裡作 cross (N,T)得到的BT,就需要乘上-1。
(你可以自己在筆記本上畫看看)
所以原本在3dsmax裡tangent.w是1的,到了unity就變成-1了。

//////////////////////////分隔線

順道在maya測試了一下發現maya是右手座標,但在maya裡向上是Y軸。
所以從maya滙入unity的資料,就不需要再對物件transform的x軸旋轉90度了。

//////////////////////////分隔線

「abc的uv順序是順時針」是指:
https://goo.gl/photos/yn9JXA2DLZrhNrwm9

//////////////////////////分隔線

3dsmax如何判定tangent.w是正還是負:
https://goo.gl/photos/DtRctCWYnjrmyjKh7
https://goo.gl/photos/f8WbG6MBdXYJQsWU9

//////////////////////////分隔線

如何計算tanget:
(只是用代數去推導,還沒實際帶資料進去驗證)
https://goo.gl/photos/DnrdqWmZRnv9uGsX8
https://goo.gl/photos/SKd5muzefZr1CCgS9

@xwc2021
Copy link

xwc2021 commented May 23, 2017

又發現一種情況
當在3dsmax設定smooth時,頂點上的法向量Normal會由鄰近三角形原來的法向量作平均(大概吧)。
放入Unity測試後,發現這時Tangnet不再相切於平面了。
https://goo.gl/photos/YSPPdbzWmUaR1w4x5
(好吧其實是還蠻普遍情況,大多的模型都會作smooth)

google發現了這個
https://gist.github.com/aras-p/2843984

看起來如果有模型是有smooth的,會這樣:
void CreateTangentSpaceTangentsUnsplit..
先算完每個面的tangent和binormal
之後對每個頂點,找出相鄰頂點(meshConnection.vertices)的tangent和binormal作平均
(相鄰頂點是指所有position一樣的頂點,而這些頂點就存在於相鄰面上)
(這時的tanget和binormal未必會和normal垂直,所以要想辦法重新正交化)

(normal是建模軟體那傳過來的,建模軟體裡應該也是作平均)
最後一步,再比較cross(normal,tanent)的方向和真正的binormal有沒有一致來決定tangent.w是1還是負-1

但是為什麼可以直接對tanget和binormal作平均呢?

當沒有smooth:
因為tangent、binormal會和uv相關,可能導致cross(T,N)和BN方向不同。
所以只要在方向上作比較,就可以知道tanget.w是1或-1
而之後當shader在內插三角形上的T、N、BN時,T、BN是相切於三角形的,內插出來的T、BN也會和原來的三角形相切。

可是當smooth時:
直接把相鄰tanget和binormal,normal取平均,最後再正交化,那之前為了和uv相關所作的計算,還會有意義嗎?
而之後當shader在內插三角形時,3頂點上的T、BT並沒和三角形相切,那這樣內插出來的T、BN不是也很奇怪嗎?

@xwc2021
Copy link

xwc2021 commented May 24, 2017

再可視化一次頂點的tangent,看看三角形內插時T、N、BN會怎麼變化
https://goo.gl/photos/kdht3gsNiRrDnxLt7
(因為T、N、BN在內插下還是平順的、連續的變化,所以肉眼也看不出有何異樣吧?)

@xwc2021
Copy link

xwc2021 commented May 24, 2017

當沒有smooth時,4個頂點上的normal變化,可以想像成1張名片
當smooth時,4個頂點上的normal變化,可以想像成你把4個手指放在名片上,向內擠壓

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants