Unity内置BRDF光照模型的详解和实现

参考:http://www.cnblogs.com/wbaoqing/p/9810386.html

https://blog.csdn.net/linuxheik/article/details/78662590

BRDF(双向反射分布函数):光线照到一个物体产生了反射,由于该物体的表面不是理想的镜面,所以光线向各个方向反射(双向反射命名的含义),而且各个方向上的反射强度不同(反射包括漫反射和镜面高光反射),我们以能量的概念来描述这一物理现象,并以某种算法来计算各个现象的权重值,并得到最终的颜色。

  Unity中采用基于物理的BRDF模型,核心算法是基于微表面原理的Cook-Torrance BRDF公式

 f(l,v)=D(h)F(v,h)G(l,v,h)/(4(n⋅l)(n⋅v));

v——视线方向(从顶点到摄像机的向量)

h——微表面法线

l——光线方向(从顶点到光源的向量)

n——顶点法线

公式可以分解为D、G、F三项计算分别表示漫反射,高光反射和菲涅耳反射(各项的算法参考文件里有,代码注释里也有)

Unity的内置文件UnityStandardBRDF.cginc中BRDF1_Unity_PBS实现了BRDF的效果,本人参考该文件,整理了如下代码:

Shader "Unlit/NewUnlitShader"
{
    Properties
    {
        _Color("Color", Color) = (1,1,1,1)
        _MainTex("Albedo (RGB)", 2D) = "white" {}
        _Glossiness("Smoothness", Range(0,1)) = 0.5
        _Metallic("Metallic", Range(0,1)) = 0.0
        _IndirectDiffuseValue("IndirectAiffuseValue",float)=0.5
        _IndirectSpecularValue("IndirectSpecularValue",float) = 0.5
    }

        SubShader
    {
        Tags{ "RenderType" = "Opaque" "LightMode" = "ForwardBase" }
        LOD 100
        Pass

    {
        CGPROGRAM
       #pragma vertex vert
       #pragma fragment frag
       #include "UnityCG.cginc"
       #include "Lighting.cginc"

       sampler2D _MainTex;
       half _Glossiness;
       half _Metallic;
       fixed4 _Color;
       float _IndirectDiffuseValue;
       float _IndirectSpecularValue;       

    struct OutPut
    {
        fixed3 Albedo;      
        fixed3 Normal;      
        half3 Emission;
        half Metallic;                                                          
        half Smoothness;    
        half Occlusion;     
        fixed Alpha;   
        half3 viewDir;
        fixed3 lightDir;

    };
    half DisneyDiffuse_Custom(half NdotV, half NdotL, half LdotH, half perceptualRoughness)
    {
        half fd90 = 0.5 + 2 * LdotH * LdotH * perceptualRoughness;
        half lightScatter = (1 + (fd90 - 1) * Pow5(1 - NdotL));
        half viewScatter = (1 + (fd90 - 1) * Pow5(1 - NdotV));
        return lightScatter * viewScatter;
    }
    inline half SmithJointGGXVisibilityTerm_Custom(half NdotL, half NdotV, half roughness)
    {
#if 0    //原版(理论基础)
        // Original formulation:
        //  lambda_v    = (-1 + sqrt(a2 * (1 - NdotL2) / NdotL2 + 1)) * 0.5f;
        //  lambda_l    = (-1 + sqrt(a2 * (1 - NdotV2) / NdotV2 + 1)) * 0.5f;
        //  G           = 1 / (1 + lambda_v + lambda_l);
        //return (2.0f * NdotL * NdotV) / ((4.0f * NdotL * NdotV) * (lambda_v + lambda_l + 1e-5f));
        //这里计算了Cook-Torrance的微表面高光BRDF公式的G项和4(nl)(nv)(分母项),还剩D项和F项
        half a = roughness;
        half a2 = a * a;
        half lambdaV = NdotL * sqrt((-NdotV * a2 + NdotV) * NdotV + a2);
        half lambdaL = NdotV * sqrt((-NdotL * a2 + NdotL) * NdotL + a2);
        return 0.5f / (lambdaV + lambdaL + 1e-5f);  
#else    //简版(实际使用)
        half a = roughness;
        half lambdaV = NdotL * (NdotV * (1 - a) + a);
        half lambdaL = NdotV * (NdotL * (1 - a) + a);

        return 0.5f / (lambdaV + lambdaL + 1e-5f);
#endif
    }
    inline half GGXTerm_Custom(half NdotH, half roughness)//这里计算D项   D=α²/(π((N·M)²(α²-1)+1)²)
    {
        half a2 = roughness * roughness;
        half d = (NdotH * a2 - NdotH) * NdotH + 1.0f;
        return UNITY_INV_PI * a2 / (d * d + 1e-7f); 
                                                    
    }
    inline half3 FresnelTerm_Custom(half3 F0, half cosA)//这里计算F项 菲涅耳 Schlick公式: Fschlick(v,h)=cspec+(1−cspec)(1−(v⋅h))5
    {
        half t = Pow5(1 - cosA);   
        return F0 + (1 - F0) * t;
    }
    inline half3 FresnelLerp_Custom(half3 F0, half3 F90, half cosA)
    {
        half t = Pow5(1 - cosA);   
        return lerp(F0, F90, t);
    }
    half4 BRDF_Unity_PBS_Custom(OutPut s, half3 specColor, half oneMinusReflectivity)
    {
        half perceptualRoughness = 1-s.Smoothness;  //preroughness
        half3 halfDir = Unity_SafeNormalize(s.lightDir + s.viewDir);
        half nv = abs(dot(s.Normal, s.viewDir));                
        half nl = saturate(dot(s.Normal, s.lightDir));
        half nh = saturate(dot(s.Normal, halfDir));
        half lv = saturate(dot(s.lightDir, s.viewDir));
        half lh = saturate(dot(s.lightDir, halfDir));
        half diffuseTerm = DisneyDiffuse_Custom(nv, nl, lh, perceptualRoughness) * nl;  //漫反射部分D
        half roughness = perceptualRoughness*perceptualRoughness;// preroughness²

        //---------Cook-Torrance BRDF公式:  f(l,v)=D(h)F(v,h)G(l,v,h)/(4(n⋅l)(n⋅v))
        half V = SmithJointGGXVisibilityTerm_Custom(nl, nv, roughness);
        half D = GGXTerm_Custom(nh, roughness);
        half specularTerm = V*D * UNITY_PI; 
        specularTerm = max(0, specularTerm * nl);   //Torrance-Sparrow model,高光部分G

        half3 F = FresnelTerm_Custom(specColor, lh);//菲涅耳F
                
        //各部分权重和融合计算
        half surfaceReduction = 1.0 / (roughness*roughness + 1.0);
        specularTerm *= any(specColor) ? 1.0 : 0.0;
        half grazingTerm = saturate(s.Smoothness + (1 - oneMinusReflectivity));
        half3 Fidentity = FresnelLerp_Custom(specColor, grazingTerm, nv);
        half3 color = s.Albedo * (_IndirectDiffuseValue + _LightColor0.rgb * diffuseTerm)+ specularTerm *  _LightColor0.rgb * F+ surfaceReduction *_IndirectSpecularValue *Fidentity;
        return half4(color, 1);
    }

    inline half4 LightingStandard_Custom(OutPut s)
    {
        s.Normal = normalize(s.Normal);    
        half3 specColor = lerp(unity_ColorSpaceDielectricSpec.rgb, s.Albedo, s.Metallic);//金属颜色差值
        half oneMinusReflectivity=unity_ColorSpaceDielectricSpec.a*(1 - s.Metallic);  //unity_ColorSpaceDielectricSpec定义在UnityCG.cginc中
        s.Albedo = s.Albedo*oneMinusReflectivity;
        half4 c = BRDF_Unity_PBS_Custom(s, specColor, oneMinusReflectivity);
        return c;
    }
    struct v2f
    {
        float4 pos : SV_POSITION;
        half3 worldNormal : TEXCOORD1;
        float3 worldPos : TEXCOORD2;
        float2 uv:TEXCOORD0;
    };
    v2f vert(appdata_full v)
    {
        v2f o;
        o.pos = UnityObjectToClipPos(v.vertex);
        o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
        o.worldNormal = UnityObjectToWorldNormal(v.normal);
        o.uv = v.texcoord;
        return o;
    }

    fixed4 frag(v2f i) : SV_Target
    {
        fixed3 lightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
        fixed3 worldViewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
        //-------配置OutPut结构体
        OutPut o;
        fixed4 col = tex2D(_MainTex, i.uv)*_Color;
        o.Albedo = col.rgb;
        o.Metallic = _Metallic;
        o.Smoothness = _Glossiness;
        o.Normal = i.worldNormal;
        o.viewDir = worldViewDir;
        o.lightDir = lightDir;
        fixed4 c=0;
        c+= LightingStandard_Custom(o);
        return c;
    }
        ENDCG
    }
    }
    FallBack"Diffuse"
}

左边是该Shader的效果,右边是StandardShader的效果,少了环境映射,写不动了(TT)。

  • 3
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: 下面是一个简单的Unity基础光照模型Shader的代码: ``` Shader "Custom/BasicLighting" { Properties { _Color ("Color", Color) = (1,1,1,1) _MainTex ("Albedo (RGB)", 2D) = "white" {} _Metallic ("Metallic", Range(0,1)) = 0.0 _Smoothness ("Smoothness", Range(0,1)) = 0.5 _Emission ("Emission", 2D) = "white" {} } SubShader { Tags {"Queue"="Geometry" "RenderType"="Opaque"} LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float3 normal : NORMAL; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float3 normal : TEXCOORD1; float3 viewDir : TEXCOORD2; float3 worldPos : TEXCOORD3; float4 pos : SV_POSITION; }; sampler2D _MainTex; sampler2D _Emission; float4 _Color; float _Metallic; float _Smoothness; v2f vert (appdata v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.uv = v.uv; o.normal = UnityObjectToWorldNormal(v.normal); o.viewDir = normalize(_WorldSpaceCameraPos - v.vertex.xyz); o.worldPos = v.vertex.xyz; return o; } float4 frag (v2f i) : SV_Target { float4 col = _Color * texture(_MainTex, i.uv); float3 diffuse = col.rgb * max(0, dot(i.normal, -_WorldSpaceLightPos0.xyz)); float3 specular = pow(max(0, dot(reflect(i.viewDir, i.normal), -_WorldSpaceLightPos0.xyz)), _Gloss); float4 emission = texture(_Emission, i.uv); float4 finalColor = col * (diffuse + specular) + emission; finalColor.a = col.a; return finalColor; } ENDCG } } FallBack "Diffuse" } ``` 这是一个使用简单的基础光照模型,其中包含颜色,主纹理,金属感,光滑度和发射量等 ### 回答2: Unity的基础光照模型shader可以通过在顶点和片段着色器中进行编写来实现。以下是一个简单的Unity基础光照模型shader的示例: 顶点着色器: ``` Shader "Custom/BasicLightingShader" { Properties { _MainTex ("Texture", 2D) = "white" {} } SubShader { Tags {"Queue"="Transparent" "RenderType"="Opaque"} LOD 200 CGPROGRAM #pragma surface surf Lambert sampler2D _MainTex; struct Input { float2 uv_MainTex; }; void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; o.Alpha = tex2D (_MainTex, IN.uv_MainTex).a; } ENDCG } } ``` 片段着色器: ``` Shader "Custom/BasicLightingShader" { Properties { _MainTex ("Texture", 2D) = "white" {} } SubShader { Tags {"Queue"="Transparent" "RenderType"="Opaque"} LOD 200 CGPROGRAM #pragma surface surf Lambert sampler2D _MainTex; struct Input { float2 uv_MainTex; }; void surf (Input IN, inout SurfaceOutput o) { fixed3 lightDir = normalize(UnityLight.dir); float lambertTerm = dot(normalize(o.Normal), lightDir); o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb * lambertTerm; o.Alpha = tex2D (_MainTex, IN.uv_MainTex).a; } ENDCG } } ``` 这个shader使用了Lambert光照模型来计算每个像素的颜色,实现了基本的光照效果。在顶点着色器中,我们通过获取_MainTex纹理的uv坐标,并将其存储在Input结构体中。然后,在surf函数中,我们使用tex2D函数来获取_MainTex纹理的颜色值,并将其赋给SurfaceOutput结构体的Albedo成员,以实现纹理贴图。在片段着色器中,我们通过获取UnityLight的dir属性来获取光照的方向,并将其与表面法线计算出的兰伯特项相乘,得到每个像素的最终颜色。最后,我们将最终颜色赋给SurfaceOutput结构体的Albedo成员,以实现光照效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值