zoukankan      html  css  js  c++  java
  • XDRender_LightMode_Anisotropic(1) 各项异性着色(1)

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

    正文

    @Author: 白袍小道

    见笑各位大神

    image-20201118140602016

    image-20201119142941818

    理论基础关键点

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

    外部链接:

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

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

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

    各项异性

    • 各项异性一般指各向异性各向异性*是指物质的全部或部分化学、物理等性质随着方向的改变而有所变化,在不同的方向上呈现出差异的性质。
    • 渲染方向上, 各项异性表面从表面上细致的纹理、槽或丝缕来获得它特有的外观,比如拉丝金属、丝绸,头发等等。当使用普通的材质进行光照时,计算仅考虑表面的法线向量、到光源的向量、及到相机的向量。但是对于各向异性表面,没有真正可以使用的连续的法线向量,因为每个丝缕或槽都有各种不同的法线方向,法线方向和槽的方向垂直

    法线扰动

    • 法线扰动规则: 利用法线扰动切线, 或者利用其他来扰动法线, 具体是看我们利用什么来作为计算
    • 在片元着色器中根据法线扰动规则重新计算法线

    切线空间和切线

    • 切线空间

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

      fig8_5.jpg

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

      image-20201118124439087

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

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

      4、T和B在哪里?

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

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

      2、数学上的定义

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

    • TBN

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

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

      image-20201118133030952

    实现

    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();
            }
    
        }
    }
    

    image-20201118140940021

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

    image-20201118141012917

    image-20201118141038287

    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;
    

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

    image-20201118140745401

    image-20201118140659955

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

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

    image-20201118133803181

    第二种:用纹理来记录的

    float3 tangentWS = SAMPLE_TEXTURE2D(_AnositionDirMap, sampler_AnositionDirMap,AnositionDirUV);
                        //float3 tangentWS = SampleNormal(AnositionDirUV, TEXTURE2D_ARGS(_AnositionDirMap, sampler_AnositionDirMap),1.0);
                        tangentWS = TransformTangentToWorld(tangentWS,tbn2);
    

    image-20201119143155935

    这里也可以来确定雕花的方向(刺绣也是有线向的), 当然可以参考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

    人生当苦,笑着看看
  • 相关阅读:
    唤醒控件曾经拥有的能力(重新继承是可以使用的.)
    为ASP.NET MVC应用程序创建更复杂的数据模型
    Akka入门实例
    最火的.NET开源项目(转)
    Roslyn 编译平台概述
    C#开发157
    ASP.NET MVC学习之控制器篇
    AngularJS中数据双向绑定(two-way data-binding)
    订单系统中并发问题和锁机制的探讨
    Visual Studio 编辑器
  • 原文地址:https://www.cnblogs.com/BaiPao-XD/p/13999655.html
Copyright © 2011-2022 走看看