Comments (28)
回報1個問題
上面的shader在偶數次鏡射時光照是正確的,但奇數次鏡射時binormal就會差1個方向
偶數次鏡射: scale(-1,-1,1) scale(-1,1,-1) scale(1,-1,-1)
奇數次鏡射: scale(-1,1,1) scale(1,-1,1) scale(1,1,-1) scale(-1,-1,-1)
測試後發現Unity的 surface shader有處理這個情況
Unity的Normal mapping sample在下面的連結裡
https://docs.unity3d.com/Manual/SL-SurfaceShaderExamples.html
按[show generate code]出來的結果如下
float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);
fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);
fixed tangentSign = v.tangent.w * unity_WorldTransformParams.w;
fixed3 worldBinormal = cross(worldNormal, worldTangent) * tangentSign;
o.tSpace0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);
o.tSpace1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);
o.tSpace2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);
上面的unity_WorldTransformParams.w應該就是[鏡射因子]
在奇數次鏡射會是(-1),而偶數次鏡射時還是(1)
/////////////////////////////////////////////////////////////////分隔線
另外在Unity裡作了一下實驗
發現即使作了scale.x=-1 Unity[在畫面上顯示的物件x軸]和[transform.right存儲的內容],編輯器裡的3軸顯示都不會反應出鏡射後方向上的變化
但像下面這麼作
Vector3 real_right =transform.lossyScale.x * transform.right;
Vector3 real_up =transform.lossyScale.y * transform.up;
Vector3 real_forward =transform.lossyScale.z * transform.forward;
還是可以得到object真實的軸向
然後檢查
cross(real_right ,real_up)和real_forward 是不是同向,就可以決定[鏡射因子]
但也有可能Unity是直接檢查sign(lossyScale.x * lossyScale.y * lossyScale.z)
from unity_shaders_book.
法线纹理:在切线空间下计算
由于前几天有个读者问了我一个问题,我突然发现P148在切线空间下计算法线光照一节是有问题的。
问题
目前书里面是使用内置宏TANGENT_SPACE_ROTATION来构建一个“从模型空间到切线空间的变换矩阵”:
///
/// Note that the code below can only handle uniform scales, not including non-uniform scales
///
// Compute the binormal
float3 binormal = cross( normalize(v.normal), normalize(v.tangent.xyz) ) * v.tangent.w;
// Construct a matrix which transform vectors from object space to tangent space
float3x3 rotation = float3x3(v.tangent.xyz, binormal, v.normal);
// Or just use the built-in macro
//TANGENT_SPACE_ROTATION;
// Transform the light direction from object space to tangent space
o.lightDir = mul(rotation, normalize(ObjSpaceLightDir(v.vertex))).xyz;
// Transform the view direction from object space to tangent space
o.viewDir = mul(rotation, normalize(ObjSpaceViewDir(v.vertex))).xyz;
上面代码的问题在于,如果模型存在非统一缩放,即缩放尺度各分量值不相等,那么上述方法就会造成错误的法线变换问题。
改正
由于光源方向、视角方向大多都是在世界空间下定义的,所以问题就是如何把它们从世界空间变换到切线空间下。我们可以先得到世界空间中切线空间的三个坐标轴的方向表示,然后把它们按列摆放,就可以得到从切线空间到世界空间的变换矩阵,那么再对这个矩阵求逆就可以得到从世界空间到切线空间的变换:
///
/// Note that the code below can handle both uniform and non-uniform scales
///
// Construct a matrix that transforms a point/vector from tangent space to world space
fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);
fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);
fixed3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w;
float4x4 tangentToWorld = float4x4(worldTangent.x, worldBinormal.x, worldNormal.x, 0.0,
worldTangent.y, worldBinormal.y, worldNormal.y, 0.0,
worldTangent.z, worldBinormal.z, worldNormal.z, 0.0,
0.0, 0.0, 0.0, 1.0);
// The matrix that transforms from world space to tangent space is inverse of tangentToWorld
float3x3 worldToTangent = inverse(tangentToWorld);
// Transform the light and view dir from world space to tangent space
o.lightDir = mul(worldToTangent, WorldSpaceLightDir(v.vertex));
o.viewDir = mul(worldToTangent, WorldSpaceViewDir(v.vertex));
由于Unity不支持Cg的inverse函数,所以还需要自己定义一个inverse函数,这可以参考更新后的Chapter7-NormalMapTangentSpace.shader。
这种做法明显比较麻烦。实际上,在Unity 4.x版本及其之前的版本中,内置的shader一直是原来书上那种不严谨的转换方法,这是因为Unity 5之前,如果我们对一个模型A进行了非统一缩放,Unity内部会重新在内存中创建一个新的模型B,模型B的大小和缩放后的A是一样的,但是它的缩放系数是统一缩放。换句话说,在Unity 5以前,实际上我们在Shader中根本不需要考虑模型的非统一缩放问题,因为在Shader阶段非统一缩放根本就不存在了。但从Unity 5以后,我们就需要考虑非统一缩放的问题了。
结论
所以结论就是,大家最好还是按Chapter7-NormalMapWorldSpace.shader转换到世界空间算更加方便,这也是Unity 5之后所有内置Shader的做法。当然,如果你可以确保没有非统一缩放那么哪种都可以。
from unity_shaders_book.
这正好解决了我的问题,非常有用的帮助
from unity_shaders_book.
乐乐女神的书不仅内容好,售后服务还做的这么好,这么前卫,O(∩_∩)O哈哈~ 我也是遇到了法线变换过程的迷惑,看了你这篇还有http://www.cnblogs.com/mengdd/archive/2011/08/30/2598025.html 这篇以后,就懂了!
from unity_shaders_book.
@hungry0 感谢支持🙂
from unity_shaders_book.
唔,我照着实现148页代码时发现到tangent space的转换有问题,过来发现确实已经有反馈过的了,这本书对于入门很有帮助,第四章写的很赞,非常感谢欸嘿嘿。; )
from unity_shaders_book.
@IronicGoose 有帮助就好~~~感谢支持 :)
from unity_shaders_book.
hi, candy,遮罩文理Chapter7-MaskTexture也使用了切线空间,用的TANGENT_SPACE_ROTATION。是不是也存在上面说的问题。
from unity_shaders_book.
@GITHUB243884919 对的,之后如果遇到法线纹理,还是转换到世界空间算更加方便。如果出第二版的话,会把所有的法线纹理转换到世界空间,不会再这么算了。
from unity_shaders_book.
您好,首先感谢你辛苦写的shader书籍,仅读了7章就对我带来了极大地帮助。其次我想请教下,我在使用上述切线空间下的法线纹理shader时,模型scale并无改变,但是shader的高光反射并没有效果。您先转换为世界座标计算法线的shader则没有这个问题。请问这可能因为什么?为何scale不统一缩放会影响模型空间下的法线输入?
from unity_shaders_book.
您好,上面7.3.2节 中平移矩阵和旋转矩阵是正交矩阵的问题。 #87这个帖子里您已经详细解释了我的问题,如果您看到问题仅考虑第一个问题就好啦。模型scale为Vector3.one时书上使用的切线空间下的法线shader不能显示高光反射的原因可能是什么呢?按照您在上述帖子的回答中所述,只要scale统一缩放,就不会影响到法线因为模型非同一缩放产生的偏移问题,为何不能正常显示高光
from unity_shaders_book.
@guishengshi 你好,书里面Scene_7_2_3是可以产生高光的。不知道你所说的情况是使用的哪个shader?
from unity_shaders_book.
仍旧是150页计算binormal方向时的疑问,感觉cross的2个参数需要颠倒一下顺序, 不然从左手坐标系来看,binormal的方向是反过来的。(跟图7.12所示是相反的)
float3 binormal = cross( normalize(v.normal), normalize(v.tangent.xyz) ) * v.tangent.w;
是不是应该改成
float3 binormal = cross( normalize(v.tangent.xyz), normalize(v.normal) ) * v.tangent.w;
from unity_shaders_book.
@laomoi
幫回覆下,那個 v.tangent.w是負值,所以方向就被修正回來了
from unity_shaders_book.
@xiangwei71
但是我实际测试下来,调过来之后,凹凸感才正确了, 按照书上例子实现后,感觉凹凸正好反过来了。。
如果按照书上例子来的话,bumpScale得调成负数才能有正确的凹凸感。
from unity_shaders_book.
@laomoi
還有一個可能是你的uv有翻轉過,你是使用自己的模型嗎?
或是你有在Unity裡對模型作負值的縮放嗎?
from unity_shaders_book.
@xiangwei71 用的是unity自带的Capsule。 我仔细看了书上配图7.15, bumpScale确实需要为负数才能有正确的凹凸感 = =
from unity_shaders_book.
@laomoi
你是不是用新版的unity,我發現我用unity2017使用內建的stander shader時,normal的方向也不正確也
(內建的stander shader 法向量剛好也反過來了阿阿)
from unity_shaders_book.
您好,我不太明白tangent和binormal跟uv之间的关系。您在书中说“切线往往与纹理空间对齐”。我自己搜了一下,看到这句话“tangent代表的是顶点u增大的方向,binormal代表v增大的方向,二者未必是互相垂直关系”,一直想不明白uv增大的方向为什么会不垂直。不垂直的话uv坐标轴是不是也不垂直?uv贴图难道不是方形的了吗?
from unity_shaders_book.
乐乐女神的书不仅内容好,售后服务还做的这么好,这么前卫,O(∩_∩)O哈哈~ 我也是遇到了法线变换过程的迷惑,看了你这篇还有http://www.cnblogs.com/mengdd/archive/2011/08/30/2598025.html 这篇以后,就懂了!
还是这个解释移动。书中的公式推到反而让人迷惑更多。
from unity_shaders_book.
乐乐女神的书不仅内容好,售后服务还做的这么好,这么前卫,O(∩_∩)O哈哈~ 我也是遇到了法线变换过程的迷惑,看了你这篇还有http://www.cnblogs.com/mengdd/archive/2011/08/30/2598025.html 这篇以后,就懂了!
还是这个解释移动。书中的公式推到反而让人迷惑更多。
那一篇文章啥推导都没有,啥引用都没有,就干巴巴地说什么是什么的逆矩阵之类的话,我觉得更加难以令人明白。
from unity_shaders_book.
仍旧是150页计算binormal方向时的疑问,感觉cross的2个参数需要颠倒一下顺序, 不然从左手坐标系来看,binormal的方向是反过来的。(跟图7.12所示是相反的)
float3 binormal = cross( normalize(v.normal), normalize(v.tangent.xyz) ) * v.tangent.w;
是不是应该改成
float3 binormal = cross( normalize(v.tangent.xyz), normalize(v.normal) ) * v.tangent.w;
from unity_shaders_book.
法线纹理:在切线空间下计算
由于前几天有个读者问了我一个问题,我突然发现P148在切线空间下计算法线光照一节是有问题的。
问题
目前书里面是使用内置宏TANGENT_SPACE_ROTATION来构建一个“从模型空间到切线空间的变换矩阵”:
/// /// Note that the code below can only handle uniform scales, not including non-uniform scales /// // Compute the binormal float3 binormal = cross( normalize(v.normal), normalize(v.tangent.xyz) ) * v.tangent.w; // Construct a matrix which transform vectors from object space to tangent space float3x3 rotation = float3x3(v.tangent.xyz, binormal, v.normal); // Or just use the built-in macro //TANGENT_SPACE_ROTATION; // Transform the light direction from object space to tangent space o.lightDir = mul(rotation, normalize(ObjSpaceLightDir(v.vertex))).xyz; // Transform the view direction from object space to tangent space o.viewDir = mul(rotation, normalize(ObjSpaceViewDir(v.vertex))).xyz;
上面代码的问题在于,如果模型存在非统一缩放,即缩放尺度各分量值不相等,那么上述方法就会造成错误的法线变换问题。
改正
由于光源方向、视角方向大多都是在世界空间下定义的,所以问题就是如何把它们从世界空间变换到切线空间下。我们可以先得到世界空间中切线空间的三个坐标轴的方向表示,然后把它们按列摆放,就可以得到从切线空间到世界空间的变换矩阵,那么再对这个矩阵求逆就可以得到从世界空间到切线空间的变换:
/// /// Note that the code below can handle both uniform and non-uniform scales /// // Construct a matrix that transforms a point/vector from tangent space to world space fixed3 worldNormal = UnityObjectToWorldNormal(v.normal); fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz); fixed3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w; float4x4 tangentToWorld = float4x4(worldTangent.x, worldBinormal.x, worldNormal.x, 0.0, worldTangent.y, worldBinormal.y, worldNormal.y, 0.0, worldTangent.z, worldBinormal.z, worldNormal.z, 0.0, 0.0, 0.0, 0.0, 1.0); // The matrix that transforms from world space to tangent space is inverse of tangentToWorld float3x3 worldToTangent = inverse(tangentToWorld); // Transform the light and view dir from world space to tangent space o.lightDir = mul(worldToTangent, WorldSpaceLightDir(v.vertex)); o.viewDir = mul(worldToTangent, WorldSpaceViewDir(v.vertex));
由于Unity不支持Cg的inverse函数,所以还需要自己定义一个inverse函数,这可以参考更新后的Chapter7-NormalMapTangentSpace.shader。
这种做法明显比较麻烦。实际上,在Unity 4.x版本及其之前的版本中,内置的shader一直是原来书上那种不严谨的转换方法,这是因为Unity 5之前,如果我们对一个模型A进行了非统一缩放,Unity内部会重新在内存中创建一个新的模型B,模型B的大小和缩放后的A是一样的,但是它的缩放系数是统一缩放。换句话说,在Unity 5以前,实际上我们在Shader中根本不需要考虑模型的非统一缩放问题,因为在Shader阶段非统一缩放根本就不存在了。但从Unity 5以后,我们就需要考虑非统一缩放的问题了。
结论
所以结论就是,大家最好还是按Chapter7-NormalMapWorldSpace.shader转换到世界空间算更加方便,这也是Unity 5之后所有内置Shader的做法。当然,如果你可以确保没有非统一缩放那么哪种都可以。
您好,我对更改前的方法有点疑惑,想请问一下,更改前的方法之所以不能处理非统一缩放,是因为计算的模型空间到切线空间的矩阵有问题吗,我不明白这个矩阵哪里有问题。倒是ObjSpaceLightDir(v.vertex)这个方法,如果是非统一缩放那么变换到模型空间的光照方向好像发生了变化?
from unity_shaders_book.
您好,我不太明白tangent和binormal跟uv之间的关系。您在书中说“切线往往与纹理空间对齐”。我自己搜了一下,看到这句话“tangent代表的是顶点u增大的方向,binormal代表v增大的方向,二者未必是互相垂直关系”,一直想不明白uv增大的方向为什么会不垂直。不垂直的话uv坐标轴是不是也不垂直?uv贴图难道不是方形的了吗?
如果妳是想知道「tangent和binormal跟uv之间的关系」:這張圖給妳參考
https://photos.app.goo.gl/1Nj8ojgitocw5iSu7
三角形的頂點是A、C、B、對映到的uv座標是a、b、c
uv座標的2個軸是u-axis、v-axis向量
a= (a.u,a.v) 代表
從uv座標的原點 o=(0,0)出發
沿著u-axis軸移動 a.u 長度、再沿著v-axis軸移動a.v長度,會到達點a
o + u-axis✖️a.u + v-axis✖️a.v = a
b和c也是類似的
o + u-axis✖️b.u + v-axis✖️b.v = b
o + u-axis✖️c.u + v-axis✖️c.v = c
這有什麼用呢?
現在換看頂點ACB
想像ACB會在同1個平面上P,然後再想像P上會有1個原點D
一開始我們不知道D的實際位置
但我們可以模仿uv空間
o + u-axis✖️a.u + v-axis✖️a.v = a
寫出
D + T✖️a.u + BN✖️a.v = A
D + T✖️b.u + BN✖️b.v = B
D + T✖️c.u + BN✖️c.v = C
再來只要解上面3個聯立方程式,就能找出tangent了
https://photos.app.goo.gl/1UVE8M3LQBSwupPm8
上面提到的T.w是什麼
那是個跟mirror有關的東西、除了是uv的mirror(因為artist在建模時、對稱的物件可以只用1半的uv)
也能用在物件的縮放上(比如說物件沿著自己的local x軸縮放-1)
如果你想知道更細節可以參考這裡
https://gpnnotes.blogspot.com/search/label/uv%20%26%20tangent
from unity_shaders_book.
from unity_shaders_book.
法线纹理:在切线空间下计算
由于前几天有个读者问了我一个问题,我突然发现P148在切线空间下计算法线光照一节是有问题的。
问题
目前书里面是使用内置宏TANGENT_SPACE_ROTATION来构建一个“从模型空间到切线空间的变换矩阵”:
/// /// Note that the code below can only handle uniform scales, not including non-uniform scales /// // Compute the binormal float3 binormal = cross( normalize(v.normal), normalize(v.tangent.xyz) ) * v.tangent.w; // Construct a matrix which transform vectors from object space to tangent space float3x3 rotation = float3x3(v.tangent.xyz, binormal, v.normal); // Or just use the built-in macro //TANGENT_SPACE_ROTATION; // Transform the light direction from object space to tangent space o.lightDir = mul(rotation, normalize(ObjSpaceLightDir(v.vertex))).xyz; // Transform the view direction from object space to tangent space o.viewDir = mul(rotation, normalize(ObjSpaceViewDir(v.vertex))).xyz;
上面代码的问题在于,如果模型存在非统一缩放,即缩放尺度各分量值不相等,那么上述方法就会造成错误的法线变换问题。
改正
由于光源方向、视角方向大多都是在世界空间下定义的,所以问题就是如何把它们从世界空间变换到切线空间下。我们可以先得到世界空间中切线空间的三个坐标轴的方向表示,然后把它们按列摆放,就可以得到从切线空间到世界空间的变换矩阵,那么再对这个矩阵求逆就可以得到从世界空间到切线空间的变换:
/// /// Note that the code below can handle both uniform and non-uniform scales /// // Construct a matrix that transforms a point/vector from tangent space to world space fixed3 worldNormal = UnityObjectToWorldNormal(v.normal); fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz); fixed3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w; float4x4 tangentToWorld = float4x4(worldTangent.x, worldBinormal.x, worldNormal.x, 0.0, worldTangent.y, worldBinormal.y, worldNormal.y, 0.0, worldTangent.z, worldBinormal.z, worldNormal.z, 0.0, 0.0, 0.0, 0.0, 1.0); // The matrix that transforms from world space to tangent space is inverse of tangentToWorld float3x3 worldToTangent = inverse(tangentToWorld); // Transform the light and view dir from world space to tangent space o.lightDir = mul(worldToTangent, WorldSpaceLightDir(v.vertex)); o.viewDir = mul(worldToTangent, WorldSpaceViewDir(v.vertex));
由于Unity不支持Cg的inverse函数,所以还需要自己定义一个inverse函数,这可以参考更新后的Chapter7-NormalMapTangentSpace.shader。
这种做法明显比较麻烦。实际上,在Unity 4.x版本及其之前的版本中,内置的shader一直是原来书上那种不严谨的转换方法,这是因为Unity 5之前,如果我们对一个模型A进行了非统一缩放,Unity内部会重新在内存中创建一个新的模型B,模型B的大小和缩放后的A是一样的,但是它的缩放系数是统一缩放。换句话说,在Unity 5以前,实际上我们在Shader中根本不需要考虑模型的非统一缩放问题,因为在Shader阶段非统一缩放根本就不存在了。但从Unity 5以后,我们就需要考虑非统一缩放的问题了。结论
所以结论就是,大家最好还是按Chapter7-NormalMapWorldSpace.shader转换到世界空间算更加方便,这也是Unity 5之后所有内置Shader的做法。当然,如果你可以确保没有非统一缩放那么哪种都可以。
您好,我对更改前的方法有点疑惑,想请问一下,更改前的方法之所以不能处理非统一缩放,是因为计算的模型空间到切线空间的矩阵有问题吗,我不明白这个矩阵哪里有问题。倒是ObjSpaceLightDir(v.vertex)这个方法,如果是非统一缩放那么变换到模型空间的光照方向好像发生了变化?
首先,第一个问题,随着物体的缩放(MVP矩阵M矩阵的变化),模型空间内的法向量也会进行同样的缩放,进而导致法向量不与平面垂直,我们如果想让他重新垂直的话可以使用书中变化到世界空间的方法来解决这个问题。原理的话可以参考这个链接:https://zhuanlan.zhihu.com/p/261667233
第二个问题,我感觉是的,原方法相当于直接构建了从模型空间到切线空间的变化,但是因为原模型空间的法线已经是错误的了,所以变化矩阵也是错误的。
from unity_shaders_book.
from unity_shaders_book.
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.