XDRender_LightMode_Anisotropic(1) 各项异性着色(1)
正文
@Author: 白袍小道
见笑各位大神
理论基础关键点
首先梳理一下和各项异性相关的知识和基础, 这里特别注意切线空间以及切线和副切线, 法线扰动. 下面做个大概列举, 论文和网络都有很详细的说明. 这里就不再继续展开.
外部链接:
1、http://www.bluevoid.com/opengl/sig00/advanced00/notes/node159.html
2、切线空间:https://zhuanlan.zhihu.com/p/139593847
各项异性
- 各项异性一般指各向异性。各向异性*是指物质的全部或部分化学、物理等性质随着方向的改变而有所变化,在不同的方向上呈现出差异的性质。
- 渲染方向上, 各项异性表面从表面上细致的纹理、槽或丝缕来获得它特有的外观,比如拉丝金属、丝绸,头发等等。当使用普通的材质进行光照时,计算仅考虑表面的法线向量、到光源的向量、及到相机的向量。但是对于各向异性表面,没有真正可以使用的连续的法线向量,因为每个丝缕或槽都有各种不同的法线方向,法线方向和槽的方向垂直。
法线扰动
- 法线扰动规则: 利用法线扰动切线, 或者利用其他来扰动法线, 具体是看我们利用什么来作为计算
- 在片元着色器中根据法线扰动规则重新计算法线
切线空间和切线
-
切线空间
1、还是和其他空间一样, 在这个空间描述的定义. 不能直接用于其他空间. (最好, 例外是两个空间恰好完全重叠,且坐标轴定义相同)
https://developer.download.nvidia.com/CgTutorial/cg_tutorial_chapter08.html
2、注意这个空间是相对某一个采样点来说. 所以我们自然会想到他的好处-----法线纹理. 这里不做展开了.
3、N和UV不一定就垂直, 因为UV是个投影(我们理解为蒙一个布上去,特别是光滑且很细的凹凸不平)
4、T和B在哪里?
-
切线、法线、法线纹理、副切线
1、理想中的切线、法线、法线纹理、副切线
2、数学上的定义
3、其实TBN是一个坐标系定义, N确定了. T关键还是看顶点走向(不一定就是U也可能是V,甚至有偏差)。U和V
-
TBN
一个空间转换矩阵, 定义为切线空间转换出来,或者逆矩阵反之.
这里假定了T是U相关的, (定义是指向U或V)
实现
1、世界空间的法线和切线、UV量可视化
这部分属于Utils部分,
a、需要查看计算好的切线,法线,
利用几何作色器部分,这里如果是Unity的话记得在Mac上使用OpenGLCoreAPI
大致代码
[maxvertexcount(9)]
void GS_Main(triangle v2g IN[3], inout LineStream<g2f> tristream)
{
g2f o;
for (uint i = 0; i < 3; i++)
{
if(_ShowValue == 0 || _ShowValue==1)
{
//看法线
o.vertex = UnityObjectToClipPos(IN[i].positionOS);
o.baseUV = IN[i].baseUV;
o.color = _NormalColor;
tristream.Append(o);
o.vertex = UnityObjectToClipPos(IN[i].positionOS+IN[i].normalOS*_LineLength);
o.baseUV = IN[i].baseUV;
o.color = _NormalColor;
tristream.Append(o);
tristream.RestartStrip();
}
if(_ShowValue == 0 || _ShowValue==2)
{
//看切线
o.vertex = UnityObjectToClipPos(IN[i].positionOS);
o.baseUV = IN[i].baseUV;
o.color = _TangentColor;
tristream.Append(o);
o.vertex = UnityObjectToClipPos(IN[i].positionOS+IN[i].tangentOS*_LineLength);
o.baseUV = IN[i].baseUV;
o.color = _TangentColor;
tristream.Append(o);
tristream.RestartStrip();
}
if(_ShowValue == 0 || _ShowValue==3)
{
//看副切线
o.vertex = UnityObjectToClipPos(IN[i].positionOS);
o.baseUV = IN[i].baseUV;
o.color = _BitangentColor;
tristream.Append(o);
o.vertex = UnityObjectToClipPos(IN[i].positionOS+IN[i].bitangentOS*_LineLength);
o.baseUV = IN[i].baseUV;
o.color = _BitangentColor;
tristream.Append(o);
tristream.RestartStrip();
}
if(_ShowValue == 0 || _ShowValue==4)
{
//看U
o.vertex = UnityObjectToClipPos(IN[i].positionOS);
o.baseUV = IN[i].baseUV;
o.color = float4(1,1,1,1);
tristream.Append(o);
o.vertex = UnityObjectToClipPos(IN[i].positionOS+IN[i].normalOS*IN[i].baseUV.x*1.0);
o.baseUV = IN[i].baseUV;
o.color = float4(1,1,1,1);
tristream.Append(o);
tristream.RestartStrip();
}
if(_ShowValue == 0 || _ShowValue==5)
{
o.vertex = UnityObjectToClipPos(IN[i].positionOS);
o.baseUV = IN[i].baseUV;
o.color = float4(1,1,0,1);
tristream.Append(o);
o.vertex = UnityObjectToClipPos(IN[i].positionOS+IN[i].normalOS*IN[i].baseUV.y*1.0);
o.baseUV = IN[i].baseUV;
o.color = float4(1,1,0,1);
tristream.Append(o);
tristream.RestartStrip();
}
}
}
这里看到原始的Tangent横了,下面我们看下UV.
2、计算所需要的切线
2.1 模型空间下的切线计算
2.2 世界空间下的切线(用来光照)
第一种:用顶点上的
备注:这里也可以全部转到切线空间下进行计算
real sign = tangentOS.w * GetOddNegativeScale();
tbn.normalWS = TransformObjectToWorldNormal(normalOS);
tbn.tangentWS = TransformObjectToWorldDir(tangentOS.xyz);
tbn.bitangentWS = cross(tbn.normalWS, tbn.tangentWS) * sign;
这里的GetNegativeScale() 使用是否反转副法线的,分量为- 1,否则为1。(这个变量我们可以按照Unity或者自己来, 存放到一个Float4的W中)
这里切线直接使用副切线, (由于制作模型的)
//2.1
float3 tangentWS = input.bitangentWS;
原因如下: 绿色是副切线,红色是切线
如果这里做一个Trik : 比如采样完计算好NormalWS,再求一次.
//float3 tangentWS = //cross(outSurfaceData.normalWS,input.tangentWS.xyz) * 0.7f;
//Cross src
//float3 tangentWS = //cross(input.normalWS,input.tangentWS.xyz) * 0.7f;
第二种:用纹理来记录的
float3 tangentWS = SAMPLE_TEXTURE2D(_AnositionDirMap, sampler_AnositionDirMap,AnositionDirUV);
//float3 tangentWS = SampleNormal(AnositionDirUV, TEXTURE2D_ARGS(_AnositionDirMap, sampler_AnositionDirMap),1.0);
tangentWS = TransformTangentToWorld(tangentWS,tbn2);
这里也可以来确定雕花的方向(刺绣也是有线向的), 当然可以参考Unreal3给出节点供给外部自求
https://docs.unrealengine.com/udk/Three/AnisotropicLightingCH.html
3、法线扰动
float3 ShiftTangent(float3 T, float3 N, float shift)
{
float3 shiftedT = T + (shift * N);
return normalize(shiftedT);
}
4、计算光照
漫反射部分
(2)续
镜面反射部分
(2)续
传统公式, 注意一下dirAtten
:https://www.zhihu.com/question/36946353/answer/361767154
float StrandSpecular(float3 T, float3 V, float L, float exponent)
{
float3 H = normalize(L + V);
float dotTH = dot(T, H);
float sinTH = sqrt(1.0 - dotTH*dotTH);
float dirAtten = smoothstep(-1.0, 0.0, dot(T, H));
return dirAtten * pow(sinTH, exponent);
}
参考文献
1、http://www.bluevoid.com/opengl/sig00/advanced00/notes/node159.html
2、https://github.com/mmikk/MikkTSpace/blob/master/mikktspace.c