Unity中Shader的BRDF解析(三)

文章目录

  • 前言
  • 一、BRDF中的镜面反射项
  • 二、分别解析每一个参数
    • 1、D、G函数:speclarTerm
    • 2、其他中间步骤
    • 3、光照颜色
    • 4、F函数(菲涅尔函数) :FresnelTerm
    • IBL在下篇文章中继续解析
  • 三、最终代码
    • .cginc文件:
    • Shader文件:


前言

在上篇文章中,我们解析了BRDF的漫反射项,这篇文章我们继续解析BRDF中的镜面反射

  • Unity中Shader的BRDF解析(二)

一、BRDF中的镜面反射项

在这里插入图片描述
在这里插入图片描述

  • 我们返回 specular ,看一下高光效果:

return fixed4(specular,1);

在这里插入图片描述


二、分别解析每一个参数

在这里插入图片描述

1、D、G函数:speclarTerm

//镜面反射中的DV项的计算
//最后乘以PI的原因是因为计算 漫反射 时,等式右边没有除以PI。
//导致算出的结果,等效于分母中多乘了一个PI,所以需要在计算公式时,乘以一个PI,消除PI
float specularTerm = V*D * UNITY_PI; // Torrance-Sparrow model, Fresnel is applied later

在这里插入图片描述

  • roughness:学术粗糙度 = 感性粗糙度 * 感性粗糙度

//声明一个学术上的粗糙度 = perceptualRoughness * perceptualRoughness
float roughness = PerceptualRoughnessToRoughness(perceptualRoughness);

  • UNITY_BRDF_GGX:默认为1

#ifndef UNITY_BRDF_GGX
#define UNITY_BRDF_GGX 1
#endif

  • D函数:法线分布函数

作用:使只有 视线向量 和 光方向向量 的半角向量 与 微平面法线重合时,才将光线 l 进行反射

在这里插入图片描述

  • 公式推导:(float D = GGXTerm (nh, roughness);)

在这里插入图片描述

这里变化公式的原因:为了节省性能
最后,把分母的1替换为 1e-7f的原因:保证分母不为0

inline float GGXTerm (float NdotH, float roughness)
{
float a2 = roughness * roughness;
float d = (NdotH * a2 - NdotH) * NdotH + 1.0f; // 2 mad
return UNITY_INV_PI * a2 / (d * d + 1e-7f); // This function is not intended to be running on Mobile,
// therefore epsilon is smaller than what can be represented by half
}

  • V函数:

在这里插入图片描述

float V = SmithJointGGXVisibilityTerm1 (nl, nv, roughness);


//V函数的计算:// Ref: http://jcgt.org/published/0003/02/03/paper.pdfinline float SmithJointGGXVisibilityTerm1 (float NdotL, float NdotV, float 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);// Reorder code to be more optimalhalf a          = roughness;half a2         = a * a;half lambdaV    = NdotL * sqrt((-NdotV * a2 + NdotV) * NdotV + a2);half lambdaL    = NdotV * sqrt((-NdotL * a2 + NdotL) * NdotL + a2);// Simplify visibility term: (2.0f * NdotL * NdotV) /  ((4.0f * NdotL * NdotV) * (lambda_v + lambda_l + 1e-5f));return 0.5f / (lambdaV + lambdaL + 1e-5f);  // This function is not intended to be running on Mobile,// therefore epsilon is smaller than can be represented by half#else//上面公式的一个近似实现(简化平方根,数学上不太精确,但是效果比较接近,性能好)// Approximation of the above formulation (simplify the sqrt, not mathematically correct but close enough)float a = roughness;float lambdaV = NdotL * (NdotV * (1 - a) + a);float lambdaL = NdotV * (NdotL * (1 - a) + a);#if defined(SHADER_API_SWITCH)return 0.5f / (lambdaV + lambdaL + UNITY_HALF_MIN);#elsereturn 0.5f / (lambdaV + lambdaL + 1e-5f);#endif#endif}

在这里插入图片描述

2、其他中间步骤

  • 如果颜色空间为Gamma空间:

//如果颜色空间为Gamma空间:
# ifdef UNITY_COLORSPACE_GAMMA
specularTerm = sqrt(max(1e-4h, specularTerm));
# endif

  • 材质上的镜面高光开关

//材质上的镜面高光开关
#if defined(_SPECULARHIGHLIGHTS_OFF)
specularTerm = 0.0;
#endif

  • any(a)函数:

any (a)
如果a=0或者a中的所有分量为0,则返回0(false);否则返回1(true).
bool any(bool4 a)
{
return a.x || a.y || a.z || a.w;
}

我们在片元着色器中,输出测试一下:

return any(0);

return any(fixed4(0,0,0,0));

在这里插入图片描述

return any(1);

return any(fixed4(0,0,1,0));

在这里插入图片描述

  • 当我们的 metallic = 1时,并且Albedo为纯黑色的情况,不希望有金属反射效果

// To provide true Lambert lighting, we need to be able to kill specular completely.
specularTerm *= any(specColor) ? 1.0 : 0.0;

  • 这句话可能Unity有自己的考虑:

specularTerm *= any(specColor) ? 1.0 : 0.0;
等效于
specularTerm *= any(specColor);

3、光照颜色

光照颜色就是单纯的乘以光照颜色

4、F函数(菲涅尔函数) :FresnelTerm

在这里插入图片描述

FresnelTerm1 (specColor, lh);

//F函数的计算:(菲涅尔效果)
inline half3 FresnelTerm1 (half3 F0, half cosA)
{
half t = Pow5 (1 - cosA); // ala Schlick interpoliation
return F0 + (1-F0) * t;
}

F0代表 视线方向 与 法线方向呈 0°夹角
F90代表 视线方向 与 法线方向呈 90°夹角

  • 参数:lh

公式中使用的是 VdotH。但是,这里传入的是 LdotH 的原因:
视线单位向量 dot 半角单位向量的点积 = 光方向单位向量 dot 半角单位向量的点积
因为,夹角是一样的。所以节省性能,使用 LdotH 代替 VdotH

  • 参数:specColor

由金属度决定的

IBL在下篇文章中继续解析


三、最终代码

.cginc文件:

#ifndef MYPHYSICALLYBASERENDERING_INCLUDE#define MYPHYSICALLYBASERENDERING_INCLUDE//Standard的漫反射和镜面反射计算↓//F函数的计算:(菲涅尔效果)inline half3 FresnelTerm1 (half3 F0, half cosA){half t = Pow5 (1 - cosA);   // ala Schlick interpoliationreturn F0 + (1-F0) * t;}//V函数的计算:// Ref: http://jcgt.org/published/0003/02/03/paper.pdfinline float SmithJointGGXVisibilityTerm1 (float NdotL, float NdotV, float 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);// Reorder code to be more optimalhalf a          = roughness;half a2         = a * a;half lambdaV    = NdotL * sqrt((-NdotV * a2 + NdotV) * NdotV + a2);half lambdaL    = NdotV * sqrt((-NdotL * a2 + NdotL) * NdotL + a2);// Simplify visibility term: (2.0f * NdotL * NdotV) /  ((4.0f * NdotL * NdotV) * (lambda_v + lambda_l + 1e-5f));return 0.5f / (lambdaV + lambdaL + 1e-5f);  // This function is not intended to be running on Mobile,// therefore epsilon is smaller than can be represented by half#else//上面公式的一个近似实现(简化平方根,数学上不太精确,但是效果比较接近,性能好)// Approximation of the above formulation (simplify the sqrt, not mathematically correct but close enough)float a = roughness;float lambdaV = NdotL * (NdotV * (1 - a) + a);float lambdaL = NdotV * (NdotL * (1 - a) + a);#if defined(SHADER_API_SWITCH)return 0.5f / (lambdaV + lambdaL + UNITY_HALF_MIN);#elsereturn 0.5f / (lambdaV + lambdaL + 1e-5f);#endif#endif}//D函数的计算:inline float GGXTerm1 (float NdotH, float roughness){float a2 = roughness * roughness;float d = (NdotH * a2 - NdotH) * NdotH + 1.0f; // 2 madreturn UNITY_INV_PI * a2 / (d * d + 1e-7f); // This function is not intended to be running on Mobile,// therefore epsilon is smaller than what can be represented by half}//为了保证分母不为0,而使用的一种安全的归一化inline float3 Unity_SafeNormalize1(float3 inVec){//normalize(v) = rsqrt(dot(v,v)) * v;float dp3 = max(0.001f, dot(inVec, inVec));return inVec * rsqrt(dp3);}//迪士尼的漫反射计算half DisneyDiffuse1(half NdotV, half NdotL, half LdotH, half perceptualRoughness){half fd90 = 0.5 + 2 * LdotH * LdotH * perceptualRoughness;// Two schlick fresnel termhalf lightScatter   = (1 + (fd90 - 1) * Pow5(1 - NdotL));half viewScatter    = (1 + (fd90 - 1) * Pow5(1 - NdotV));return lightScatter * viewScatter;}// Main Physically Based BRDF// Derived from Disney work and based on Torrance-Sparrow micro-facet model////   BRDF = kD / pi + kS * (D * V * F) / 4//   I = BRDF * NdotL//// * NDF (depending on UNITY_BRDF_GGX)://  a) Normalized BlinnPhong//  b) GGX// * Smith for Visiblity term// * Schlick approximation for Fresnelhalf4 BRDF1_Unity_PBS1 (half3 diffColor, half3 specColor, half oneMinusReflectivity, half smoothness,float3 normal, float3 viewDir,UnityLight light, UnityIndirect gi){//感性的粗糙的 = 1 - smoothnessfloat perceptualRoughness = SmoothnessToPerceptualRoughness (smoothness);//半角向量(一般用 H 表示): H = 光线向量 + 视线向量(此处的 光线向量 和 视线向量 为单位向量,根据向量相加的四边形法则得出半角向量)float3 halfDir = Unity_SafeNormalize1 (float3(light.dir) + viewDir);//法线 与 视线的点积在可见像素上不应该出现负值,但是他有可能发生在 投影 与 法线 映射 时//所以,可以通过某些方式来修正,但是会产生额外的指令运算//替代方案采用abs的形式,同样可以工作只是正确性少一些    // NdotV should not be negative for visible pixels, but it can happen due to perspective projection and normal mapping// In this case normal should be modified to become valid (i.e facing camera) and not cause weird artifacts.// but this operation adds few ALU and users may not want it. Alternative is to simply take the abs of NdotV (less correct but works too).// Following define allow to control this. Set it to 0 if ALU is critical on your platform.// This correction is interesting for GGX with SmithJoint visibility function because artifacts are more visible in this case due to highlight edge of rough surface// Edit: Disable this code by default for now as it is not compatible with two sided lighting used in SpeedTree.#define UNITY_HANDLE_CORRECTLY_NEGATIVE_NDOTV 0#if UNITY_HANDLE_CORRECTLY_NEGATIVE_NDOTV// The amount we shift the normal toward the view vector is defined by the dot product.half shiftAmount = dot(normal, viewDir);normal = shiftAmount < 0.0f ? normal + viewDir * (-shiftAmount + 1e-5f) : normal;// A re-normalization should be applied here but as the shift is small we don't do it to save ALU.//normal = normalize(normal);float nv = saturate(dot(normal, viewDir)); // TODO: this saturate should no be necessary here#elsehalf nv = abs(dot(normal, viewDir));    // This abs allow to limit artifact#endif//其他向量之间的点积float nl = saturate(dot(normal, light.dir));//法线 点积 光线float nh = saturate(dot(normal, halfDir));//法线 点积 半角half lv = saturate(dot(light.dir, viewDir));//光线 点积 视线half lh = saturate(dot(light.dir, halfDir));//光线 点积 半角// Diffuse term//迪士尼原则的漫反射half diffuseTerm = DisneyDiffuse1(nv, nl, lh, perceptualRoughness) * nl;// Specular term// HACK: theoretically we should divide diffuseTerm by Pi and not multiply specularTerm!// 理论上漫反射项中应该除以 PI,但是由于以下两个原因没有这样做// BUT 1) that will make shader look significantly darker than Legacy ones//原因一:这样会导致最终效果偏暗// and 2) on engine side "Non-important" lights have to be divided by Pi too in cases when they are injected into ambient SH//原因二:当引擎光照为 不重要光照 时,进行球谐光照计算,会再除以一个 PI。所以,在Unity计算迪士尼漫反射时,不除以PI//声明一个学术上的粗糙度 = perceptualRoughness * perceptualRoughnessfloat roughness = PerceptualRoughnessToRoughness(perceptualRoughness);//GGX模型拥有比较好的效果,默认使用这个模型(并且,UNITY_BRDF_GGX在定义时,默认为 1)#if UNITY_BRDF_GGX// GGX with roughtness to 0 would mean no specular at all, using max(roughness, 0.002) here to match HDrenderloop roughtness remapping.//使用max来限定 roughtness 最小等于0 的原因:当 roughtness 为0时,结果会直接为0,导致效果丢失roughness = max(roughness, 0.002);float V = SmithJointGGXVisibilityTerm1 (nl, nv, roughness);float D = GGXTerm1 (nh, roughness);#else// Legacyhalf V = SmithBeckmannVisibilityTerm1 (nl, nv, roughness);half D = NDFBlinnPhongNormalizedTerm1 (nh, PerceptualRoughnessToSpecPower(perceptualRoughness));#endif//镜面反射中的DV项的计算//最后乘以PI的原因是因为上面计算漫反射时,等式右边没有除以PI。//导致算出的结果,等效于分母中多乘了一个PI,所以需要在计算公式时,乘以一个PI,消除PIfloat specularTerm = V*D * UNITY_PI; // Torrance-Sparrow model, Fresnel is applied later//如果颜色空间为Gamma空间:    #   ifdef UNITY_COLORSPACE_GAMMAspecularTerm = sqrt(max(1e-4h, specularTerm));#   endif// specularTerm * nl can be NaN on Metal in some cases, use max() to make sure it's a sane valuespecularTerm = max(0, specularTerm * nl);//材质上的镜面高光开关    #if defined(_SPECULARHIGHLIGHTS_OFF)specularTerm = 0.0;#endif// surfaceReduction = Int D(NdotH) * NdotH * Id(NdotL>0) dH = 1/(roughness^2+1)half surfaceReduction;#   ifdef UNITY_COLORSPACE_GAMMAsurfaceReduction = 1.0-0.28*roughness*perceptualRoughness;      // 1-0.28*x^3 as approximation for (1/(x^4+1))^(1/2.2) on the domain [0;1]#   elsesurfaceReduction = 1.0 / (roughness*roughness + 1.0);           // fade \in [0.5;1]#   endif// To provide true Lambert lighting, we need to be able to kill specular completely.// 当我们的 metallic = 1时,并且Albedo为纯黑色的情况,不希望有金属反射效果specularTerm *= any(specColor) ? 1.0 : 0.0;half grazingTerm = saturate(smoothness + (1-oneMinusReflectivity));//漫反射颜色 = 贴图 * (gi漫反射 + 灯光颜色 * 迪士尼漫反射)half3 diffuse = diffColor * (gi.diffuse + light.color * diffuseTerm);//镜面反射 DFG / 4cos(θl)cos(θv)//speclarTerm : D G 函数//light.color : 光照颜色//FresnelTerm (specColor, lh) : F 函数half3 specular = specularTerm * light.color * FresnelTerm1 (specColor, lh);//IBLhalf3 ibl = surfaceReduction * gi.specular * FresnelLerp (specColor, grazingTerm, nv);half3 color = diffuse + specular + ibl;return half4(color, 1);}// Based on Minimalist CookTorrance BRDF// Implementation is slightly different from original derivation: http://www.thetenthplanet.de/archives/255//// * NDF (depending on UNITY_BRDF_GGX)://  a) BlinnPhong//  b) [Modified] GGX// * Modified Kelemen and Szirmay-​Kalos for Visibility term// * Fresnel approximated with 1/LdotHhalf4 BRDF2_Unity_PBS1 (half3 diffColor, half3 specColor, half oneMinusReflectivity, half smoothness,float3 normal, float3 viewDir,UnityLight light, UnityIndirect gi){float3 halfDir = Unity_SafeNormalize (float3(light.dir) + viewDir);half nl = saturate(dot(normal, light.dir));float nh = saturate(dot(normal, halfDir));half nv = saturate(dot(normal, viewDir));float lh = saturate(dot(light.dir, halfDir));// Specular termhalf perceptualRoughness = SmoothnessToPerceptualRoughness (smoothness);half roughness = PerceptualRoughnessToRoughness(perceptualRoughness);#if UNITY_BRDF_GGX// GGX Distribution multiplied by combined approximation of Visibility and Fresnel// See "Optimizing PBR for Mobile" from Siggraph 2015 moving mobile graphics course// https://community.arm.com/events/1155float a = roughness;float a2 = a*a;float d = nh * nh * (a2 - 1.f) + 1.00001f;#ifdef UNITY_COLORSPACE_GAMMA// Tighter approximation for Gamma only rendering mode!// DVF = sqrt(DVF);// DVF = (a * sqrt(.25)) / (max(sqrt(0.1), lh)*sqrt(roughness + .5) * d);float specularTerm = a / (max(0.32f, lh) * (1.5f + roughness) * d);#elsefloat specularTerm = a2 / (max(0.1f, lh*lh) * (roughness + 0.5f) * (d * d) * 4);#endif// on mobiles (where half actually means something) denominator have risk of overflow// clamp below was added specifically to "fix" that, but dx compiler (we convert bytecode to metal/gles)// sees that specularTerm have only non-negative terms, so it skips max(0,..) in clamp (leaving only min(100,...))#if defined (SHADER_API_MOBILE)specularTerm = specularTerm - 1e-4f;#endif#else// Legacyhalf specularPower = PerceptualRoughnessToSpecPower(perceptualRoughness);// Modified with approximate Visibility function that takes roughness into account// Original ((n+1)*N.H^n) / (8*Pi * L.H^3) didn't take into account roughness// and produced extremely bright specular at grazing angleshalf invV = lh * lh * smoothness + perceptualRoughness * perceptualRoughness; // approx ModifiedKelemenVisibilityTerm(lh, perceptualRoughness);half invF = lh;half specularTerm = ((specularPower + 1) * pow (nh, specularPower)) / (8 * invV * invF + 1e-4h);#ifdef UNITY_COLORSPACE_GAMMAspecularTerm = sqrt(max(1e-4f, specularTerm));#endif#endif#if defined (SHADER_API_MOBILE)specularTerm = clamp(specularTerm, 0.0, 100.0); // Prevent FP16 overflow on mobiles#endif#if defined(_SPECULARHIGHLIGHTS_OFF)specularTerm = 0.0;#endif// surfaceReduction = Int D(NdotH) * NdotH * Id(NdotL>0) dH = 1/(realRoughness^2+1)// 1-0.28*x^3 as approximation for (1/(x^4+1))^(1/2.2) on the domain [0;1]// 1-x^3*(0.6-0.08*x)   approximation for 1/(x^4+1)#ifdef UNITY_COLORSPACE_GAMMAhalf surfaceReduction = 0.28;#elsehalf surfaceReduction = (0.6-0.08*perceptualRoughness);#endifsurfaceReduction = 1.0 - roughness*perceptualRoughness*surfaceReduction;half grazingTerm = saturate(smoothness + (1-oneMinusReflectivity));half3 color =   (diffColor + specularTerm * specColor) * light.color * nl+ gi.diffuse * diffColor+ surfaceReduction * gi.specular * FresnelLerpFast (specColor, grazingTerm, nv);return half4(color, 1);}sampler2D_float unity_NHxRoughness1;half3 BRDF3_Direct1(half3 diffColor, half3 specColor, half rlPow4, half smoothness){half LUT_RANGE = 16.0; // must match range in NHxRoughness() function in GeneratedTextures.cpp// Lookup texture to save instructionshalf specular = tex2D(unity_NHxRoughness1, half2(rlPow4, SmoothnessToPerceptualRoughness(smoothness))).r * LUT_RANGE;#if defined(_SPECULARHIGHLIGHTS_OFF)specular = 0.0;#endifreturn diffColor + specular * specColor;}half3 BRDF3_Indirect1(half3 diffColor, half3 specColor, UnityIndirect indirect, half grazingTerm, half fresnelTerm){half3 c = indirect.diffuse * diffColor;c += indirect.specular * lerp (specColor, grazingTerm, fresnelTerm);return c;}// Old school, not microfacet based Modified Normalized Blinn-Phong BRDF// Implementation uses Lookup texture for performance//// * Normalized BlinnPhong in RDF form// * Implicit Visibility term// * No Fresnel term//// TODO: specular is too weak in Linear rendering modehalf4 BRDF3_Unity_PBS1 (half3 diffColor, half3 specColor, half oneMinusReflectivity, half smoothness,float3 normal, float3 viewDir,UnityLight light, UnityIndirect gi){float3 reflDir = reflect (viewDir, normal);half nl = saturate(dot(normal, light.dir));half nv = saturate(dot(normal, viewDir));// Vectorize Pow4 to save instructionshalf2 rlPow4AndFresnelTerm = Pow4 (float2(dot(reflDir, light.dir), 1-nv));  // use R.L instead of N.H to save couple of instructionshalf rlPow4 = rlPow4AndFresnelTerm.x; // power exponent must match kHorizontalWarpExp in NHxRoughness() function in GeneratedTextures.cpphalf fresnelTerm = rlPow4AndFresnelTerm.y;half grazingTerm = saturate(smoothness + (1-oneMinusReflectivity));half3 color = BRDF3_Direct1(diffColor, specColor, rlPow4, smoothness);color *= light.color * nl;color += BRDF3_Indirect1(diffColor, specColor, gi, grazingTerm, fresnelTerm);return half4(color, 1);}// Default BRDF to use://在 ProjectSetting->Graphics->TierSetting中设置//StandardShaderQuality = low(UNITY_PBS_USE_BRDF3)//StandardShaderQuality = Medium(UNITY_PBS_USE_BRDF2)//StandardShaderQuality = High(UNITY_PBS_USE_BRDF1)#if !defined (UNITY_BRDF_PBS1) // allow to explicitly override BRDF in custom shader// still add safe net for low shader models, otherwise we might end up with shaders failing to compile#if SHADER_TARGET < 30 || defined(SHADER_TARGET_SURFACE_ANALYSIS) // only need "something" for surface shader analysis pass; pick the cheap one#define UNITY_BRDF_PBS1 BRDF3_Unity_PBS1  //效果最差的BRDF#elif defined(UNITY_PBS_USE_BRDF3)#define UNITY_BRDF_PBS1 BRDF3_Unity_PBS1#elif defined(UNITY_PBS_USE_BRDF2)#define UNITY_BRDF_PBS1 BRDF2_Unity_PBS1#elif defined(UNITY_PBS_USE_BRDF1)#define UNITY_BRDF_PBS1 BRDF1_Unity_PBS1#else#error something broke in auto-choosing BRDF#endif#endifinline half OneMinusReflectivityFromMetallic1(half metallic){// We'll need oneMinusReflectivity, so//   1-reflectivity = 1-lerp(dielectricSpec, 1, metallic) = lerp(1-dielectricSpec, 0, metallic)// store (1-dielectricSpec) in unity_ColorSpaceDielectricSpec.a, then//   1-reflectivity = lerp(alpha, 0, metallic) = alpha + metallic*(0 - alpha) =//                  = alpha - metallic * alphahalf oneMinusDielectricSpec = unity_ColorSpaceDielectricSpec.a;return oneMinusDielectricSpec - metallic * oneMinusDielectricSpec;}inline half3 DiffuseAndSpecularFromMetallic1 (half3 albedo, half metallic, out half3 specColor, out half oneMinusReflectivity){//计算镜面高光颜色//当metallic为0(即非金属时),返回unity_ColorSpaceDielectricSpec.rgb(0.04)//unity_ColorSpaceDielectricSpec.rgb表示的是绝缘体的通用反射颜色//迪士尼经大量测量用 0.04 来表示//当 metallic = 1 时(金属),返回Albedo,也就是物体本身的颜色specColor = lerp (unity_ColorSpaceDielectricSpec.rgb, albedo, metallic);oneMinusReflectivity = OneMinusReflectivityFromMetallic1(metallic);return albedo * oneMinusReflectivity;}//s : 物体表面数据信息//viewDir : 视线方向//gi : 全局光照(GI漫反射 和 GI镜面反射)inline half4 LightingStandard1 (SurfaceOutputStandard s, float3 viewDir, UnityGI gi){s.Normal = normalize(s.Normal);half oneMinusReflectivity;//镜面高光颜色half3 specColor;s.Albedo = DiffuseAndSpecularFromMetallic1 (s.Albedo, s.Metallic, /*out*/ specColor, /*out*/ oneMinusReflectivity);// shader relies on pre-multiply alpha-blend (_SrcBlend = One, _DstBlend = OneMinusSrcAlpha)// this is necessary to handle transparency in physically correct way - only diffuse component gets affected by alpha//当开启半透明模式时,对 Alpha 进行相关计算half outputAlpha;s.Albedo = PreMultiplyAlpha (s.Albedo, s.Alpha, oneMinusReflectivity, /*out*/ outputAlpha);//具体的BRDF计算//s.Albedo : 物体表面的基础颜色//specColor : 镜面反射颜色//oneMinusReflectivity : 漫反射率 = 1 - 镜面反射率//s.Smoothness : 物体表面的光滑度//s.Normal : 物体表面的法线//viewDir : 视线方向//gi.light : 直接光信息//gi.indirect : GI间接光信息half4 c = UNITY_BRDF_PBS1 (s.Albedo, specColor, oneMinusReflectivity, s.Smoothness, s.Normal, viewDir, gi.light, gi.indirect);c.a = outputAlpha;return c;}//Standard的GI计算↓half3 Unity_GlossyEnvironment1 (UNITY_ARGS_TEXCUBE(tex), half4 hdr, Unity_GlossyEnvironmentData glossIn){half perceptualRoughness = glossIn.roughness /* perceptualRoughness */ ;// TODO: CAUTION: remap from Morten may work only with offline convolution, see impact with runtime convolution!// For now disabled#if 0float m = PerceptualRoughnessToRoughness(perceptualRoughness); // m is the real roughness parameterconst float fEps = 1.192092896e-07F;        // smallest such that 1.0+FLT_EPSILON != 1.0  (+1e-4h is NOT good here. is visibly very wrong)float n =  (2.0/max(fEps, m*m))-2.0;        // remap to spec power. See eq. 21 in --> https://dl.dropboxusercontent.com/u/55891920/papers/mm_brdf.pdfn /= 4;                                     // remap from n_dot_h formulatino to n_dot_r. See section "Pre-convolved Cube Maps vs Path Tracers" --> https://s3.amazonaws.com/docs.knaldtech.com/knald/1.0.0/lys_power_drops.htmlperceptualRoughness = pow( 2/(n+2), 0.25);      // remap back to square root of real roughness (0.25 include both the sqrt root of the conversion and sqrt for going from roughness to perceptualRoughness)#else// MM: came up with a surprisingly close approximation to what the #if 0'ed out code above does.//r = r * (1.7 - 0.7*r)//由于粗糙度与反射探针的mip变化不呈现线性正比,所以需要一个公式来改变perceptualRoughness = perceptualRoughness*(1.7 - 0.7*perceptualRoughness);#endif//UNITY_SPECCUBE_LOD_STEPS = 6,表示反射探针的mip级别有 6 档。粗糙度X6得到最终得mip级别half mip = perceptualRoughnessToMipmapLevel(perceptualRoughness);half3 R = glossIn.reflUVW;half4 rgbm = UNITY_SAMPLE_TEXCUBE_LOD(tex, R, mip);return DecodeHDR(rgbm, hdr);}//GI中的镜面反射inline half3 UnityGI_IndirectSpecular1(UnityGIInput data, half occlusion, Unity_GlossyEnvironmentData glossIn){half3 specular;//如果开启了反射探针的Box Projection#ifdef UNITY_SPECCUBE_BOX_PROJECTION// we will tweak reflUVW in glossIn directly (as we pass it to Unity_GlossyEnvironment twice for probe0 and probe1), so keep original to pass into BoxProjectedCubemapDirectionhalf3 originalReflUVW = glossIn.reflUVW;glossIn.reflUVW = BoxProjectedCubemapDirection (originalReflUVW, data.worldPos, data.probePosition[0], data.boxMin[0], data.boxMax[0]);#endif#ifdef _GLOSSYREFLECTIONS_OFFspecular = unity_IndirectSpecColor.rgb;#elsehalf3 env0 = Unity_GlossyEnvironment1 (UNITY_PASS_TEXCUBE(unity_SpecCube0), data.probeHDR[0], glossIn);//如果开启了反射探针混合#ifdef UNITY_SPECCUBE_BLENDINGconst float kBlendFactor = 0.99999;float blendLerp = data.boxMin[0].w;UNITY_BRANCHif (blendLerp < kBlendFactor){#ifdef UNITY_SPECCUBE_BOX_PROJECTIONglossIn.reflUVW = BoxProjectedCubemapDirection (originalReflUVW, data.worldPos, data.probePosition[1], data.boxMin[1], data.boxMax[1]);#endifhalf3 env1 = Unity_GlossyEnvironment (UNITY_PASS_TEXCUBE_SAMPLER(unity_SpecCube1,unity_SpecCube0), data.probeHDR[1], glossIn);specular = lerp(env1, env0, blendLerp);}else{specular = env0;}#elsespecular = env0;#endif#endifreturn specular * occlusion;}inline UnityGI UnityGlobalIllumination1 (UnityGIInput data, half occlusion, half3 normalWorld){return UnityGI_Base(data, occlusion, normalWorld);}//GI计算inline UnityGI UnityGlobalIllumination1 (UnityGIInput data, half occlusion, half3 normalWorld, Unity_GlossyEnvironmentData glossIn){//计算得出GI中的漫反射UnityGI o_gi = UnityGI_Base(data, occlusion, normalWorld);//计算得出GI中的镜面反射o_gi.indirect.specular = UnityGI_IndirectSpecular1(data, occlusion, glossIn);return o_gi;}float SmoothnessToPerceptualRoughness1(float smoothness){return (1 - smoothness);}Unity_GlossyEnvironmentData UnityGlossyEnvironmentSetup1(half Smoothness, half3 worldViewDir, half3 Normal, half3 fresnel0){Unity_GlossyEnvironmentData g;//粗糙度g.roughness /* perceptualRoughness */   = SmoothnessToPerceptualRoughness1(Smoothness);//反射球的采样坐标g.reflUVW   = reflect(-worldViewDir, Normal);return g;}//PBR光照模型的GI计算inline void LightingStandard_GI1(SurfaceOutputStandard s,UnityGIInput data,inout UnityGI gi){//如果是延迟渲染PASS并且开启了延迟渲染反射探针的话#if defined(UNITY_PASS_DEFERRED) && UNITY_ENABLE_REFLECTION_BUFFERSgi = UnityGlobalIllumination1(data, s.Occlusion, s.Normal);#else//Unity_GlossyEnvironmentData表示GI中的反射准备数据Unity_GlossyEnvironmentData g = UnityGlossyEnvironmentSetup1(s.Smoothness, data.worldViewDir, s.Normal,lerp(unity_ColorSpaceDielectricSpec.rgb, s.Albedo,s.Metallic));//进行GI计算并返回输出gigi = UnityGlobalIllumination1(data, s.Occlusion, s.Normal, g);#endif}#endif

Shader文件:

//Standard材质
Shader "MyShader/P2_2_8"
{Properties{_Color ("Color", Color) = (1,1,1,1)_MainTex ("Albedo (RGB)", 2D) = "white" {}[NoScaleOffset]_MetallicTex("Metallic(R) Smoothness(G) AO(B)",2D) = "white" {}[NoScaleOffset][Normal]_NormalTex("NormalTex",2D) = "bump" {}_Glossiness ("Smoothness", Range(0,1)) = 0.0_Metallic ("Metallic", Range(0,1)) = 0.0_AO("AO",Range(0,1)) = 1.0}SubShader{Tags{"RenderType"="Opaque"}LOD 200// ---- forward rendering base pass:Pass{Name "FORWARD"Tags{"LightMode" = "ForwardBase"}CGPROGRAM// compile directives#pragma vertex vert#pragma fragment frag#pragma target 3.0#pragma multi_compile_instancing#pragma multi_compile_fog#pragma multi_compile_fwdbase#include "UnityCG.cginc"#include "Lighting.cginc"#include "UnityPBSLighting.cginc"#include "AutoLight.cginc"#include "CGInclude/MyPhysicallyBasedRendering.cginc"sampler2D _MainTex;float4 _MainTex_ST;half _Glossiness;half _Metallic;fixed4 _Color;sampler2D _MetallicTex;half _AO;sampler2D _NormalTex;struct appdata{float4 vertex : POSITION;float4 tangent : TANGENT;float3 normal : NORMAL;float4 texcoord : TEXCOORD0;float4 texcoord1 : TEXCOORD1;float4 texcoord2 : TEXCOORD2;float4 texcoord3 : TEXCOORD3;fixed4 color : COLOR;UNITY_VERTEX_INPUT_INSTANCE_ID};// vertex-to-fragment interpolation data// no lightmaps:struct v2f{float4 pos : SV_POSITION;float2 uv : TEXCOORD0; // _MainTexfloat3 worldNormal : TEXCOORD1;float3 worldPos : TEXCOORD2;#if UNITY_SHOULD_SAMPLE_SHhalf3 sh : TEXCOORD3; // SH#endif//切线空间需要使用的矩阵float3 tSpace0 : TEXCOORD4;float3 tSpace1 : TEXCOORD5;float3 tSpace2 : TEXCOORD6;UNITY_FOG_COORDS(7)UNITY_SHADOW_COORDS(8)};// vertex shaderv2f vert(appdata v){v2f o;o.pos = UnityObjectToClipPos(v.vertex);o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex);float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;float3 worldNormal = UnityObjectToWorldNormal(v.normal);//世界空间下的切线half3 worldTangent = UnityObjectToWorldDir(v.tangent);//切线方向half tangentSign = v.tangent.w * unity_WorldTransformParams.w;//世界空间下的副切线half3 worldBinormal = cross(worldNormal, worldTangent) * tangentSign;//切线矩阵o.tSpace0 = float3(worldTangent.x, worldBinormal.x, worldNormal.x);o.tSpace1 = float3(worldTangent.y, worldBinormal.y, worldNormal.y);o.tSpace2 = float3(worldTangent.z, worldBinormal.z, worldNormal.z);o.worldPos.xyz = worldPos;o.worldNormal = worldNormal;// SH/ambient and vertex lights#if UNITY_SHOULD_SAMPLE_SH && !UNITY_SAMPLE_FULL_SH_PER_PIXELo.sh = 0;// Approximated illumination from non-important point lights#ifdef VERTEXLIGHT_ONo.sh += Shade4PointLights (unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0,unity_LightColor[0].rgb, unity_LightColor[1].rgb, unity_LightColor[2].rgb, unity_LightColor[3].rgb,unity_4LightAtten0, worldPos, worldNormal);#endifo.sh = ShadeSHPerVertex (worldNormal, o.sh);#endifUNITY_TRANSFER_LIGHTING(o, v.texcoord1.xy);UNITY_TRANSFER_FOG(o, o.pos); // pass fog coordinates to pixel shaderreturn o;}// fragment shaderfixed4 frag(v2f i) : SV_Target{UNITY_EXTRACT_FOG(i);float3 worldPos = i.worldPos.xyz;float3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos));SurfaceOutputStandard o;UNITY_INITIALIZE_OUTPUT(SurfaceOutputStandard, o);fixed4 mainTex = tex2D(_MainTex, i.uv);o.Albedo = mainTex.rgb * _Color;o.Emission = 0.0;fixed4 metallicTex = tex2D(_MetallicTex, i.uv);o.Metallic = metallicTex.r * _Metallic;o.Smoothness = metallicTex.g * _Glossiness;o.Occlusion = metallicTex.b * _AO;o.Alpha = 1;half3 normalTex = UnpackNormal(tex2D(_NormalTex,i.uv));half3 worldNormal = half3(dot(i.tSpace0,normalTex),dot(i.tSpace1,normalTex),dot(i.tSpace2,normalTex));o.Normal = worldNormal;// compute lighting & shadowing factorUNITY_LIGHT_ATTENUATION(atten, i, worldPos)// Setup lighting environmentUnityGI gi;UNITY_INITIALIZE_OUTPUT(UnityGI, gi);gi.indirect.diffuse = 0;gi.indirect.specular = 0;gi.light.color = _LightColor0.rgb;gi.light.dir = _WorldSpaceLightPos0.xyz;// Call GI (lightmaps/SH/reflections) lighting functionUnityGIInput giInput;UNITY_INITIALIZE_OUTPUT(UnityGIInput, giInput);giInput.light = gi.light;giInput.worldPos = worldPos;giInput.worldViewDir = worldViewDir;giInput.atten = atten;#if defined(LIGHTMAP_ON) || defined(DYNAMICLIGHTMAP_ON)giInput.lightmapUV = IN.lmap;#elsegiInput.lightmapUV = 0.0;#endif#if UNITY_SHOULD_SAMPLE_SH && !UNITY_SAMPLE_FULL_SH_PER_PIXELgiInput.ambient = i.sh;#elsegiInput.ambient.rgb = 0.0;#endifgiInput.probeHDR[0] = unity_SpecCube0_HDR;giInput.probeHDR[1] = unity_SpecCube1_HDR;#if defined(UNITY_SPECCUBE_BLENDING) || defined(UNITY_SPECCUBE_BOX_PROJECTION)giInput.boxMin[0] = unity_SpecCube0_BoxMin; // .w holds lerp value for blending#endif#ifdef UNITY_SPECCUBE_BOX_PROJECTIONgiInput.boxMax[0] = unity_SpecCube0_BoxMax;giInput.probePosition[0] = unity_SpecCube0_ProbePosition;giInput.boxMax[1] = unity_SpecCube1_BoxMax;giInput.boxMin[1] = unity_SpecCube1_BoxMin;giInput.probePosition[1] = unity_SpecCube1_ProbePosition;#endifLightingStandard_GI1(o, giInput, gi);//return fixed4(gi.indirect.specular,1);// PBS的核心计算fixed4 c = LightingStandard1(o, worldViewDir, gi);UNITY_APPLY_FOG(_unity_fogCoord, c); // apply fogUNITY_OPAQUE_ALPHA(c.a); //把c的Alpha置1return c;}ENDCG}}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/226090.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Matplotlib图形注释_Python数据分析与可视化

Matplotlib图形注释 添加注释文字、坐标变换 有的时候单单使用图形无法完整清晰的表达我们的信息&#xff0c;我们还需要进行文字进行注释&#xff0c;所以matplotlib提供了文字、箭头等注释可以突出图形中重点信息。 添加注释 为了使我们的可视化图形让人更加容易理解&#…

西工大网络空间安全学院计算机系统基础实验一(123)

在实验零中&#xff0c;我们拿到了lab1-handout.zip压缩文件&#xff0c;接着&#xff0c;我们使用unzip ./lab1-handout.zip命令&#xff0c;解压缩该压缩文件。解压缩成功后&#xff0c;使用"ls"命令查看当前工作目录下的文件和文件夹&#xff0c;发现得到了"…

16.spirng源码解析-registerBeanPostProcessors

注册拦截bean创建的bean处理器 此部分实质上是在BeanDefinitions中寻找BeanPostProcessor&#xff0c;之后调用BeanFactory.addBeanPostProcessor方法保存在一个List中&#xff0c;注意添加时仍然有优先级的概念&#xff0c;优先级高的在前面。

竞赛选题 题目:基于机器视觉的图像矫正 (以车牌识别为例) - 图像畸变校正

文章目录 0 简介1 思路简介1.1 车牌定位1.2 畸变校正 2 代码实现2.1 车牌定位2.1.1 通过颜色特征选定可疑区域2.1.2 寻找车牌外围轮廓2.1.3 车牌区域定位 2.2 畸变校正2.2.1 畸变后车牌顶点定位2.2.2 校正 7 最后 0 简介 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享…

漏电保护器工作原理

漏电保护器 漏电保护器是低压线路中最常用的保护器之一&#xff0c;简称漏保&#xff0c;又称漏电开关或漏电断路器。漏电保护器除了具有空开的所有保护功能外&#xff0c;还具备漏电保护功能。 需要了解 一根通电导线可以产生磁场&#xff0c;磁场与电流方向遵循右手螺旋关…

微信如何养号?

目前导致微信封号的几点原因如下&#xff1a; 1.使用虚拟定位软件&#xff0c;进行附近人任务&#xff0c;频繁的切换ip&#xff0c;极易被判定微信被盗,对其封号 2.频发的群发骚扰消息&#xff0c;朋友圈发送过于频繁&#xff0c;给别人造成骚扰及其投诉 3.和微信好友聊天涉…

计算机基础知识59

MySQL的卸载流程 1、先停止MySQL服务&#xff1a;右键“此电脑”&#xff0c;选择“管理”&#xff0c;之后选择“服务和应用程序”--“服务”&#xff0c;在服务中找到“MySQL”&#xff0c;右键选择“停止”。 2、找到“控制面板”--“程序和功能”&#xff0c;找到MySQL&…

Vue框架学习笔记——事件处理:v-on指令+methods

文章目录 前文提要事件处理的解析过程&#xff0c;v-on:事件名样例代码如下&#xff1a;效果展示图片&#xff1a;v-on:事件名"响应函数"v-on简写形式响应函数添加响应函数传参占位符"$event"注意事项 前文提要 本人仅做个人学习记录&#xff0c;如有错误…

代码随想录算法训练营第35天| 860.柠檬水找零 406.根据身高重建队列 452. 用最少数量的箭引爆气球

JAVA代码编写 860.柠檬水找零 在柠檬水摊上&#xff0c;每一杯柠檬水的售价为 5 美元。顾客排队购买你的产品&#xff0c;&#xff08;按账单 bills 支付的顺序&#xff09;一次购买一杯。 每位顾客只买一杯柠檬水&#xff0c;然后向你付 5 美元、10 美元或 20 美元。你必须…

手把手实操分享:1天开发一个教育类App!我是怎么办到的

最近项目组接了一个开发教育类APP的需求&#xff0c;经典要求&#xff1a;多快好省&#xff08;功能样样有、项目要得急、还得低成本&#xff09;手动&#x1f436;。组长开始让调研一下给个方案&#xff0c;结果我啃哧啃哧1天就搞出来了&#xff0c;目前运行良好。在此记录一下…

【Linux专题】http(s)代理

【赠送】IT技术视频教程&#xff0c;白拿不谢&#xff01;思科、华为、红帽、数据库、云计算等等_厦门微思网络的博客-CSDN博客文章浏览阅读444次。风和日丽&#xff0c;小微给你送福利~如果你是小微的老粉&#xff0c;这里有一份粉丝福利待领取...如果你是新粉关注到了小微&am…

【Flutter】graphic图表的快速上手

简介 graphic是一个数据可视化语法和Flutter图表库。 官方github示例 网上可用资源很少,只有作者的几篇文章,并且没有特别详细的文档,使用的话还是需要一定的时间去调研,在此简单记录。 示例 以折线图为例(因为我只用到了折线图,但其他的图大差不差) 创建一个两个文…