【Unity】三渲二Shader学习记录(持续更新)

【UnityShader】 三渲二渲染学习笔记

跟B站上的一个大大学的【虚幻&Unity】两种引擎 原神风格基础卡通渲染 完整流程,现趁忘记之前赶紧记下所有细节()

完成画面↓

【Unity/Shader】三渲二纳西妲

首先是重中之重,Shader的参数部分

参数

可控参数如下

环境颜色 Ambient Color

​ 用于调整整体色彩倾向

漫反射颜色 Diffuse Color

​ 用于调整亮面色彩倾向

阴影颜色 Shadow Color

​ 用于调整暗面色彩倾向

基础贴图 Base Tex

​ 用于最基础的漫反射贴图

基础贴图影响因子 Base Tex Fac

​ 用于调整漫反射贴图的贡献(影响大小)

卡通贴图 Toon Tex

​ 用于将法向朝向地面的面的亮度压低,提高立体感

卡通贴图影响因子 Toon Tex Fac

​ 调整卡通贴图贡献

球形纹理 Sphere Tex

​ 提供球形纹理的反射颜色

球形纹理影响因子 Sphere Tex Fac

​ 调整球形纹理贡献

球形纹理乘或加 Sphere Mul/Add

​ 更改球形纹理混合方式,通常Add会使影响更加明显

双面渲染 Double Sided

​ 控制是否进行双面渲染

透明度 Alpha

​ 控制是否透明

金属贴图 Metal Tex

​ 控制确认金属部分

高光分布 Spec Exponent

​ 控制高光的散步范围(越小越集中,类似镜面反射)

非金属反射 Non-Metallic

​ 控制非金属反射强度

金属反射 Metallic

​ 控制金属反射强度

法线贴图 Normal Map

​ 表面细节

ID图 ILM

​ 区分各种部分

阴影分割图和对应行 Ramp Tex

​ 提高光线的层次感

描边粗度和对应颜色

​ 进行描边

实现部分

ShadowCaster

该Pass负责Unity中的阴影投射和阴影接收,以下是其默认实现

Pass
        {
            Name "ShadowCaster"
            Tags{"LightMode" = "ShadowCaster"}

            ZWrite On
            ZTest LEqual
            ColorMask 0
            Cull Off

            HLSLPROGRAM
            #pragma exclude_renderers gles gles3 glcore
            #pragma target 4.5

            // -------------------------------------
            // Material Keywords
            #pragma shader_feature_local_fragment _ALPHATEST_ON
            #pragma shader_feature_local_fragment _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A

            //--------------------------------------
            // GPU Instancing
            #pragma multi_compile_instancing
            #pragma multi_compile _ DOTS_INSTANCING_ON

            // -------------------------------------
            // Universal Pipeline keywords

            // This is used during shadow map generation to differentiate between directional and punctual light shadows, as they use different formulas to apply Normal Bias
            #pragma multi_compile_vertex _ _CASTING_PUNCTUAL_LIGHT_SHADOW

            #pragma vertex ShadowPassVertex
            #pragma fragment ShadowPassFragment

            #include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/Shaders/ShadowCasterPass.hlsl"
            ENDHLSL
        }

DepthNormals

这个pass通常用于需要获取摄像机、屏幕深度的场景,比如rim轮廓光

// This pass is used when drawing to a _CameraNormalsTexture texture
        Pass
        {
            Name "DepthNormals"
            Tags{"LightMode" = "DepthNormals"}

            ZWrite On
            Cull Off

            HLSLPROGRAM
            #pragma exclude_renderers gles gles3 glcore
            #pragma target 4.5

            #pragma vertex DepthNormalsVertex
            #pragma fragment DepthNormalsFragment

            // -------------------------------------
            // Material Keywords
            #pragma shader_feature_local _NORMALMAP
            #pragma shader_feature_local _PARALLAXMAP
            #pragma shader_feature_local _ _DETAIL_MULX2 _DETAIL_SCALED
            #pragma shader_feature_local_fragment _ALPHATEST_ON
            #pragma shader_feature_local_fragment _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A

            //--------------------------------------
            // GPU Instancing
            #pragma multi_compile_instancing
            #pragma multi_compile _ DOTS_INSTANCING_ON

            #include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/Shaders/LitDepthNormalsPass.hlsl"
            ENDHLSL
        }

DrawObject

该Pass负责进行模型的绘制

数据结构部分

首先是带入顶点着色器的appdata

struct appdata
{
    float4 vertex : POSITION;
    float2 uv : TEXCOORD0;
    half3 normal : NORMAL;
    half4 tangent : TANGENT;
    half4 color : COLOR0;
};

POSITION代表着顶点的位置信息

TEXCOORD0代表着第一套UV信息,Shader中支持多个UV信息,如漫反射UV,光照贴图UV,动态光照UV等。该参数在内置管线限制为4个,在URP中没有限制,具体视平台限制而定

NORMAL即法线信息

TANGENT是顶点的切线信息,Normal垂直于表面,切线切过表面,二者互相垂直

COLOR0即顶点颜色

接着是片元着色器所需要的数据结构v2f

struct v2f
{
    float2 uv : TEXCOORD0;

    float3 positionWS : TEXCOORD1;
    float3 positionVS : TEXCOORD2;
    float4 positionCS : SV_POSITION;
    float4 positionNDC : TEXCOORD3;

    float3 normalWS : TEXCOORD4;
    float3 tangentWS : TEXCOORD5;
    float3 bitangentWS : TEXCOORD6;

    float fogCoord : TEXCOORD7;
    float4 shadowCoord : TEXCOORD8;
};

uv负责采样大部分贴图

世界,模型,摄像机,标准屏幕坐标系四个空间下的片元位置

世界空间下的法线,切线,副切线的片元所在面朝向向量

fogCoord和shadowCoord是两个雾效

顶点着色器
v2f vert(appdata v)
{
    v2f o;
    VertexPositionInputs vertexInput = GetVertexPositionInputs(v.vertex.xyz);
    o.uv = TRANSFORM_TEX(v.uv, _BaseTex);
    o.positionWS = vertexInput.positionWS;
    o.positionVS = vertexInput.positionVS;
    o.positionCS = vertexInput.positionCS;
    o.positionNDC = vertexInput.positionNDC;

    VertexNormalInputs vertexNormalInput = GetVertexNormalInputs(v.normal, v.tangent);
    o.tangentWS = vertexNormalInput.tangentWS;
    o.bitangentWS = vertexNormalInput.bitangentWS;
    o.normalWS = vertexNormalInput.normalWS;

    o.fogCoord = ComputeFogFactor(vertexInput.positionCS.z);
    o.shadowCoord = TransformWorldToShadowCoord(vertexInput.positionWS);

    return o;
}

GetVertexPositionInputs函数,通过输入顶点坐标,获得对应的顶点信息,所有顶点位置都由其获得

GetVertexNormalInputs函数,通过输入法向和切线,返回对应的顶点的法线相关信息

后面通过对应函数计算fogCoord和shadowCoord,提交返回构造好的v2f

片元着色器
定义函数参数
float4 frag(v2f i, bool isFacing : SV_IsFrontFace) : SV_Target

返回一个rgba值表示片元颜色,接收收到的v2f,接收isFacing判断正反面

SV_Target用于多目标渲染,在后面加上序号(从0开始,不加也是0),可一次渲染到多个渲染目标

获取基础信息

先获取光照信息,使用如下函数

Light light = GetMainLight(i.shadowCoord);

接着,采样法线贴图,并将其转换为切线空间

float4 normalMap = tex2D(_NormalMap, i.uv);
float3 normalTS = float3(normalMap.ag * 2 - 1, 0);
normalTS.z = sqrt(1 - dot(normalTS.xy, normalTS.xy));
  • normalMap.ag:这表示从法线贴图中采样出的红色(a通道)和绿色(g通道)分量。

  • normalMap.ag * 2 - 1:这一步将采样到的a和g通道值进行缩放和偏移,将其映射到[-1, 1]的范围内。这是因为法线贴图的a和g通道通常存储在[0, 1]范围内,而法线向量在计算中常常需要在[-1, 1]范围内表示。

  • float3(…, 0):将得到的a和g通道值作为X和Y分量,构成一个三维向量,Z分量设置为0。这样构造的normalTS向量表示了法线向量在切线空间(Tangent Space)的局部方向。

  • normalTS.xy :这是normalTS向量的X和Y分量,即法线在切线空间中的水平和垂直分量。

  • **dot(normalTS.xy, normalTS.xy) **:这是法线在切线空间中水平和垂直分量的长度的平方。

  • **1 - dot(normalTS.xy, normalTS.xy) **:这是用来计算法线在切线空间中Z分量的平方。因为法线向量在切线空间中是单位向量,所以其X、Y、Z分量的平方和应该等于1。

  • **sqrt(…) :**对上述值进行平方根运算,得到Z分量的实际值。

然后求出基本数据和对应点乘

//N 世界空间法线向量    V 摄像机向量      L 光线向量      H 半角向量
float3 N = normalize(mul(normalTS, float3x3(i.tangentWS, i.bitangentWS, i.normalWS)));
float3 V = normalize(mul((float3x3)UNITY_MATRIX_I_V, i.positionVS * (-1)));
float3 L = normalize(light.direction);
float3 H = normalize(V + L);

float NoL = dot(N, L);
float NoH = dot(N, H);
float NoV = dot(N, V);

float3 normalVS = normalize(mul((float3x3)UNITY_MATRIX_V, N));
float2 matcapUV = normalVS.xy * 0.5 + 0.5;

N:一个3x3的浮点数矩阵,用来将世界空间中的切线、副切线和法线向量组合起来。这种方式通常被用来构建一个从世界空间到切线空间(Tangent Space)的转换矩阵。将其与切线normalTS相乘,最终可得到世界空间下的法线。

V:这是将逆视图矩阵的旋转-缩放部分应用于顶点位置的处理。通过这个乘法,我们得到了一个在世界空间中相对于相机视角反向的方向向量。

L:将光线方向规格化。

H:半角向量,计算了光源方向向量L和视角方向向量V的平均方向,即半向量H,并确保它是一个单位向量。这在光照模型中是常见的步骤,用于确定表面的高光反射方向。

NoL:表示光线与表面的夹角关系,越大表明光线与表面越垂直,越小表示越平行,如果直接点乘,则结果为负时,照在表面正方向,为正则照在表面负方向。在光照模型中,NoL用来计算漫反射光的强度,即光线在表面上的投影对光照强度的影响。

NoH:半向量H代表了光源和视角的平均方向,对于镜面反射效果尤为重要。NoH的值告诉我们光线和视角之间的夹角余弦值,这在光照模型中用来计算高光反射的强度。较大的NoH值表示更强的镜面高光效果。

NoV:NoV告诉我们观察者视角下表面的朝向情况,其值越大表明表面法线越朝向观察者,越小则越背向。在光照模型中,NoV通常用来模拟视角相关的效果,如环境反射或透视效果的强度。

normalVS:

  • UNITY_MATRIX_V:这是Unity中的一个预定义变量,表示视图矩阵。视图矩阵用来将物体从世界空间转换到相机空间或观察空间。
  • N:法线向量,通常在顶点着色器中计算并传递给片元着色器。
  • (float3x3)UNITY_MATRIX_V:将UNITY_MATRIX_V强制转换为float3x3类型的矩阵,用于乘法操作。
  • mul((float3x3)UNITY_MATRIX_V, N):这行代码将法线向量N从物体空间(或世界空间)变换到相机空间。因为UNITY_MATRIX_V是视图矩阵,它的作用是将物体坐标变换到相机坐标系下。
  • **normalize(…):**对变换后的法线向量进行归一化,确保其长度为1,保持单位长度的方向向量。

经过视图矩阵变换并归一化后的法线向量,在视图空间中表示的表面法线方向。

matcapUV:

  • normalVS.xy:这是视图空间中的法线向量的xy分量,即水平和垂直分量。
  • 0.5:这个数值用来将法线向量的范围缩小到0到1之间。
  • + 0.5:这个操作将归一化后的法线向量范围从[-1, 1]映射到[0, 1]之间。

是法线向量在Matcap贴图中的UV坐标。Matcap贴图通常用来实现表面上的高光效果,其UV坐标的计算基于表面法线向量在视图空间中的投影。

  • 59
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值