XDRender_LightMode_Anisotropic(1) 各项异性着色(1)

XDRender_LightMode_Anisotropic(1) 各项异性着色(1)

目录
  • XDRender_LightMode_Anisotropic(1) 各项异性着色(1)
    • 正文
      • 理论基础关键点
        • 各项异性
        • 法线扰动
        • 切线空间和切线
          • 切线空间
          • 切线、法线、法线纹理、副切线
      • 实现
        • 1、世界空间的法线和切线、UV量可视化
        • 2、计算所需要的切线
        • 3、法线扰动
        • 4、计算光照
          • 漫反射部分
          • 镜面反射部分

正文

@Author: 白袍小道

见笑各位大神

XDRender_LightMode_Anisotropic(1) 各项异性着色(1)_第1张图片

理论基础关键点

首先梳理一下和各项异性相关的知识和基础, 这里特别注意切线空间以及切线和副切线, 法线扰动. 下面做个大概列举, 论文和网络都有很详细的说明. 这里就不再继续展开.

外部链接:

1、http://www.bluevoid.com/opengl/sig00/advanced00/notes/node159.html

2、切线空间:https://zhuanlan.zhihu.com/p/139593847

XDRender_LightMode_Anisotropic(1) 各项异性着色(1)_第2张图片
各项异性
  • 各项异性一般指各向异性各向异性*是指物质的全部或部分化学、物理等性质随着方向的改变而有所变化,在不同的方向上呈现出差异的性质。
  • 渲染方向上, 各项异性表面从表面上细致的纹理、槽或丝缕来获得它特有的外观,比如拉丝金属、丝绸,头发等等。当使用普通的材质进行光照时,计算仅考虑表面的法线向量、到光源的向量、及到相机的向量。但是对于各向异性表面,没有真正可以使用的连续的法线向量,因为每个丝缕或槽都有各种不同的法线方向,法线方向和槽的方向垂直
法线扰动
  • 法线扰动规则: 利用法线扰动切线, 或者利用其他来扰动法线, 具体是看我们利用什么来作为计算
  • 在片元着色器中根据法线扰动规则重新计算法线
切线空间和切线
  • 切线空间

    1、还是和其他空间一样, 在这个空间描述的定义. 不能直接用于其他空间. (最好, 例外是两个空间恰好完全重叠,且坐标轴定义相同)

    XDRender_LightMode_Anisotropic(1) 各项异性着色(1)_第3张图片

    https://developer.download.nvidia.com/CgTutorial/cg_tutorial_chapter08.html

    XDRender_LightMode_Anisotropic(1) 各项异性着色(1)_第4张图片

    2、注意这个空间是相对某一个采样点来说. 所以我们自然会想到他的好处-----法线纹理. 这里不做展开了.

    3、N和UV不一定就垂直, 因为UV是个投影(我们理解为蒙一个布上去,特别是光滑且很细的凹凸不平)

    4、T和B在哪里?

  • 切线、法线、法线纹理、副切线

    1、理想中的切线、法线、法线纹理、副切线

    2、数学上的定义

    3、其实TBN是一个坐标系定义, N确定了. T关键还是看顶点走向(不一定就是U也可能是V,甚至有偏差)。U和V

  • TBN

    一个空间转换矩阵, 定义为切线空间转换出来,或者逆矩阵反之.

    这里假定了T是U相关的, (定义是指向U或V)

    XDRender_LightMode_Anisotropic(1) 各项异性着色(1)_第5张图片

实现

1、世界空间的法线和切线、UV量可视化

这部分属于Utils部分,

a、需要查看计算好的切线,法线,

利用几何作色器部分,这里如果是Unity的话记得在Mac上使用OpenGLCoreAPI

大致代码

[maxvertexcount(9)]
void GS_Main(triangle v2g IN[3], inout LineStream 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();
        }

    }
}

XDRender_LightMode_Anisotropic(1) 各项异性着色(1)_第6张图片

这里看到原始的Tangent横了,下面我们看下UV.

XDRender_LightMode_Anisotropic(1) 各项异性着色(1)_第7张图片
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;

原因如下: 绿色是副切线,红色是切线

XDRender_LightMode_Anisotropic(1) 各项异性着色(1)_第8张图片

XDRender_LightMode_Anisotropic(1) 各项异性着色(1)_第9张图片

如果这里做一个Trik : 比如采样完计算好NormalWS,再求一次.

//float3 tangentWS = //cross(outSurfaceData.normalWS,input.tangentWS.xyz) * 0.7f;
//Cross src
//float3 tangentWS = //cross(input.normalWS,input.tangentWS.xyz) * 0.7f;

XDRender_LightMode_Anisotropic(1) 各项异性着色(1)_第10张图片
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

你可能感兴趣的:(python,unity,机器学习,opengl,人工智能)