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.mzph.cn/news/181206.shtml

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

相关文章

贪心 376. 摆动序列

376. 摆动序列 题目&#xff1a; 连续数字&#xff08;前减后&#xff09;的差严格在正负之间交替&#xff0c;差值不能有0为摆动序列。 [1,7,4,9,2,5] 是一个摆动序列&#xff0c;因为差值 (6,-3,5,-7,3) 是正负交替出现的 给定一个整数序列&#xff0c;返回作为摆动序列…

Docker监控Weave Scope的安装和使用

1.本地安装Weave Scope 1&#xff09;创建文件夹。 mkdir /usr/local/bin/scope 2&#xff09;从本地上传文件。 rz scope.bin以资源形式已上传到文章开篇。 3&#xff09;修改scope.bin文件为可执行文件。 chmod 755 /usr/local/bin/scope/scope.bin 4&#xff09;执行sco…

AntDB数据库:从海量数据处理,到5G计费商用核心

AntDB数据库自2008年研发面世以来&#xff0c;首先被应用于运营商的核心系统&#xff0c;满足运营商海量数据处理的需求。随着数字科技的不断发展&#xff0c;AntDB也在不断地更新迭代&#xff0c;逐渐地为更多行业与客户提供更全面的服务。5G时代来临&#xff0c;AntDB抓住发展…

《微信小程序开发从入门到实战》学习三十六

4.2 云开发JSON数据库 4.2.6 云开发JSON数据库 在集合对象上调用add方法可以在集和中可以插入一条记录&#xff0c;代码如下&#xff1a; db.collection(testOne).add({ // 在JSON数据库的testOne集合中增加一个记录 data:{ name: "write paper" }, // 插入数据成功…

华住三季报:韧性增长超预期,夯实可持续发展底座

美股研究社获悉&#xff0c;2023年11月27日&#xff0c;华住集团2023年第三季度业绩发布会在线上举行。受华住三季度财报向好表现&#xff0c;多家证券公司给予买入评级。中信证券认为&#xff0c;本季度华住集团国内外RevPAR修复优于前期指引上限、净开店数优于预期。且华住集…

如何手工获取并更新ESXi中macOS的VMware Tools版本

正文共&#xff1a;1128 字 22 图&#xff0c;预估阅读时间&#xff1a;1 分钟 前面我们介绍了如何在VMware ESXi创建macOS虚拟机&#xff08;VMware ESXI部署macOS Monterey&#xff09;&#xff0c;也大概介绍了如何安装VMware Tools&#xff0c;因为VMware Tools可以提供对虚…

一名技术Leader应该是创作者

今天看了一本书叫做《黑客与画家》。它里面提到一个很重要的概念就是黑客&#xff08;优秀的程序员&#xff09;是一名建筑师&#xff0c;而不是一名工程师。 传统的主管和互联网的Leader 这两者有什么区别呢&#xff1f;关键点在于建筑师是思考做什么&#xff0c;而工程师是…

超实用电脑技巧分享,快速提高工作效率!

“我是个刚开始学习使用电脑的新手&#xff0c;想问问大家有什么比较好用的电脑使用技巧可以推荐一下吗&#xff1f;非常感谢&#xff01;” 在使用电脑时&#xff0c;如果我们适当掌握一些技巧&#xff0c;可以有效提高效率。那么&#xff0c;今天小编就给大家分享一些常见的电…

前五年—中国十大科技进展新闻(2012年—2017年)

前五年—中国十大科技进展新闻&#xff08;2012-2017&#xff09; 2017年中国十大科技进展新闻1. 我国科学家利用化学物质合成完整活性染色体2. 国产水下滑翔机下潜6329米刷新世界纪录3. 世界首台超越早期经典计算机的光量子计算机诞生4. 国产大型客机C919首飞5. 我国首次海域天…

leetcode:用栈实现队列(先进先出)

题目描述 题目链接&#xff1a;232. 用栈实现队列 - 力扣&#xff08;LeetCode&#xff09; 题目分析 我们先把之前写的数组栈的实现代码搬过来 用栈实现队列最主要的是实现队列先进先出的特点&#xff0c;而栈的特点是后进先出&#xff0c;那么我们可以用两个栈来实现&…

盖茨表示GPT-5不会比GPT-4有太大改进;Intro to Large Language Models

&#x1f989; AI新闻 &#x1f680; 盖茨表示GPT-5不会比GPT-4有太大改进 摘要&#xff1a;比尔盖茨在与德国《商报》的采访中透露&#xff0c;虽然OpenAI内部有人相信GPT-5会优于GPT-4&#xff0c;但他认为目前的生成式人工智能已经达到极限。盖茨对GPT-5未来的发展并不乐观…

麒麟操作系统光盘救援模式

麒麟操作系统光盘救援模式 Kylin V4 桌面版&#xff1a; 启动主机后&#xff0c;插入系统光盘&#xff0c;在 BIOS 启动项里设置成从光盘启动后保存退出重启主机。 稍等片刻就会到启动菜单选项&#xff0c;到启动菜单界面后选择第一项试用银河麒麟操作系统而不安 装&#xff…

一个人撸码!之vue3+vite+element-plus后台管理(标签页组件)

一个后台管理常常需要一个标签页来管理已经打开的页面&#xff0c;这里我们单独写一个组件来展示标签页数组。 该标签页组件只做展示不涉及操作数据。标签页数组可记录已打开的数组&#xff0c;还能定义什么页面需要缓存&#xff0c;是一个重要的功能呢。 首先&#xff0c;建立…

Java(九)(多线程,线程安全,实现线程的方法,线程同步,线程池,并发和并行,线程的六种状态)

目录 多线程 线程 实现线程的方法 方法一:继承Thread父类 方法二:实现Runnable接口 方法三:Callable接口和FutureTask类来实现 Thread方法 线程安全 线程同步 同步代码块 同步方法 Lock锁 线程池 线程池对象的创建方式一: 线程池处理Runnable任务 线程池处理Cal…

BGP综合实验

任务如下&#xff1a; 1.AS1存在两个环回&#xff0c;一个地址为192.168.1.0/24该地址不能在任何协议中宣告 AS3存在两个环回&#xff0c;一个地址为192.168.2.0/24该地址不能在任何协议中宣告&#xff0c;最终要求这两个环回可以互相通讯 2.整个AS2的IP地址为172.16.0.0/16&…

Sentaurus TCAD半导体器件入门常用案例合集

Sentaurus TCAD是用于模拟半导体器件和工艺的工具之一&#xff0c;可以帮助工程师设计电路元件&#xff0c;优化半导体工艺和器件性能。主要功能包括&#xff1a;半导体器件建模&#xff08;用于建立各种半导体器件的物理模型工艺模拟&#xff09;、半导体器件的制造工艺模拟&a…

Debian10安装VMware Tools

一、原系统 首先我在界面按CTRLALTT和CTRLSiftT都没有反应&#xff0c;没关系&#xff0c;我有办法 系统版本 管理员用户 步骤一&#xff1a;打开VMware Tools文件 步骤二、将文件复制到自己熟悉的文件内 步骤三、命令行查看文件是否复制成功存在 步骤四、解压VMware-tools…

宋仕强论道之华强北的商业配套(十三)

宋仕强论道之华强北的商业配套&#xff08;十三&#xff09;&#xff1a;金航标电子萨科微半导体总经理宋仕强先生发布“宋仕强论道”系列视频&#xff0c;分享多年学习、生活和工作经验和感悟&#xff0c;甚至涵盖了文学、艺术、哲学、宗教。这段时间发表的是对华强北&#xf…

程序设计基础中可能出现的简单编程题2(以郑大为主体)

我们在学习编程过程中往往不仅有C语言实验报告&#xff0c;还有程序设计实验报告。程序设计这一科目主要是为了培养我们写代码时的计算思维&#xff0c;养成从问题到代码实现逐步分析&#xff0c;逐步深入的好习惯。前面有一篇文章介绍了部分程序设计实验报告中的编程题&#x…

第二十章 -----多线程

20.1 线程简介 计算机完全可以将多种活动同时进行&#xff0c;这种思想在java中称为并发&#xff0c;将并发完成的每一件事情称为线程 线程的特点&#xff1a; 极小的单位 一个进程有很多个线程 线程共享进程的资源 20.2 创建线程 20.2.1 继承Thread类 Thread类是Java.l…