Comments (11)
其实在定义切线空间的时候,Z轴方向即切线方向是有无数条的,建模软件在计算切线的时候会选择和UV展开方向相同的那个方向作为切线方向,这样的选择导致如果直接用法线插乘切线的方向也一定会和UV相关,但这可能不是模型的法线方向(可能是反的),因此w会再存储一个信息。
from unity_shaders_book.
@candycat1992
书中说到“w分量决定副切线的方向性”,但如你所说,叉乘的结果可能是相反的,也可能是正确的。
又依据书中给出的计算方法,叉乘结果 * w = 正确结果
1.叉乘结果正确 -> w = 1
2.叉乘结果相反 -> w = -1
w在存储值时事先做了处理,预知了叉乘结果,并将对应“修正值”存储。
所以w并不能决定副切线的方向性,而应该是修正因子(标量)。
from unity_shaders_book.
我不明白你的意思,在我看来乘以w决定了副切线到底取哪个方向,这里说的方向性不是说左手性右手性,而是说方向,有什么不对么?
from unity_shaders_book.
@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
from unity_shaders_book.
是不是有些在概念上太钻牛角尖了?叉乘的结果会得到一个方向,然后w分量又进一步决定了最终的正确方向,没有人说仅仅知道w就可以知道正确方向呀~因为我对建模软件不够了解,因此我没法跟你解释清楚它们到底是怎么生成的。如果你这么想了解可以去建模软件自己生成一个法线贴图试一下呗。你的第5点我没看出来和我的观点有任何冲突的地方啊,本来就都是已知的,建模软件对法线纹理的生成是一个encode的过程,而我们在shader里做的是一个decode的工程,这样说你是不是会明白一些
from unity_shaders_book.
@candycat1992
抱歉,这牛角确实钻得太深了。
我上一条回复内容只是简述了一下我的思考顺序和逻辑,感觉自己直接用类代码的东西可能更容易表述意思,并不是提问。
其实理论我都懂了,主要是书中说到“w分量决定副切线的方向性”,我将这句话理解成 “w分量代表了副切线的方向性”,然后认为这句话与实际情况不符。
给你带来困扰,实在不好意思
from unity_shaders_book.
没关系~说不定下一次钻牛角尖就能发现新的东西呀
感谢对书的支持 :)
from unity_shaders_book.
這個問題我之前剛好有測試過
可以把下面這個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
from unity_shaders_book.
又發現一種情況
當在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不是也很奇怪嗎?
from unity_shaders_book.
再可視化一次頂點的tangent,看看三角形內插時T、N、BN會怎麼變化
https://goo.gl/photos/kdht3gsNiRrDnxLt7
(因為T、N、BN在內插下還是平順的、連續的變化,所以肉眼也看不出有何異樣吧?)
from unity_shaders_book.
當沒有smooth時,4個頂點上的normal變化,可以想像成1張名片
當smooth時,4個頂點上的normal變化,可以想像成你把4個手指放在名片上,向內擠壓
from unity_shaders_book.
Related Issues (20)
- 随书附赠的代码勘误-Chapter9 Unity阴影-Shadow.shader HOT 2
- Github提供的第四章勘误中 4.6.7 的不等式沒有修改
- 散射是否改变光线颜色的问题 HOT 2
- 一直clone失败 有小伙伴遇到一样问题吗 HOT 4
- P43 第四段是不是应该为图4.9(图3.9)
- 第8章 8.7 双面渲染的透明效果 p176 p177 背面法线方向问题
- 12.4 高斯模糊里的纹理采样按照for循环来进行采样好像会有一个纹理坐标用不上 HOT 2
- P146页的问题
- 第二章 2.3.3 裁剪 内容和后面有点矛盾 HOT 2
- Unity2020.3在立方体纹理实现的反射效果 中处理光照衰减之后有奇怪的阴影 HOT 2
- P232这里的uv坐标直接用减法会为负吗? HOT 2
- P92,投影空间问题 HOT 2
- 7.2.2.1在切线空间下计算从模型到切线空间的变换矩阵 HOT 1
- 10.1.15 菲涅尔反射文章《Everything has Fresnel》,链接失效.
- 第十一章中 Billboard 的 Shader 代码是不对的 HOT 5
- 刚打开项目的时候物体偏暗,添加一个物体后就正常了这是为啥?
- 关于12.6节动态模糊,accumulateTexture变量的问题 HOT 2
- 这块应该是减小吧 HOT 2
- 第七章关于法线贴图反映射的计算
- P56 第四章pdf中练习题第三题 应为“是多少” HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from unity_shaders_book.