Unity PBR基础知识

PBR原理

基于物理的渲染(Physically Based Rendering,PBR)是指使用基于物理原理和微平面理论建模的着色/光照模型,以及使用从现实中测量的表面参数来准确表示真实世界材质的渲染理念。

PBR基础理念

微平面理论(Microfacet Theory):微平面理论是将物体表面建模成做无数微观尺度上有随机朝向的理想镜面反射的小平面(microfacet)的理论。在实际的PBR 工作流中,这种物体表面的不规则性用粗糙度贴图或者高光度贴图来表示。
在这里插入图片描述
能量守恒(Energy Conservation):出射光线的能量永远不能超过入射光线的能量。随着粗糙度的上升镜面反射区域的面积会增加,作为平衡,镜面反射区域的平均亮度则会下降。
菲涅尔反射(Fresnel Reflectance):光线以不同角度入射会有不同的反射率。相同的入射角度,不同的物质也会有不同的反射率。万物皆有菲涅尔反射。F0是即 0 度角入射的菲涅尔反射值。大多数非金属的F0范围是0.02-0.04,大多数金属的F0范围是0.7-1.0。
线性空间(Linear Space):光照计算必须在线性空间完成,shader 中输入的gamma空间的贴图比如漫反射贴图需要被转成线性空间,在具体操作时需要根据不同引擎和渲染器的不同做不同的操作。而描述物体表面属性的贴图如粗糙度,高光贴图,金属贴图等必须保证是线性空间。
色调映射(Tone Mapping):也称色调复制(tone reproduction),是将宽范围的照明级别拟合到屏幕有限色域内的过程。因为基于HDR渲染出来的亮度值会超过显示器能够显示最大亮度,所以需要使用色调映射,将光照结果从HDR转换为显示器能够正常显示的LDR。
物质的光学特性(Substance Optical Properties):现实世界中有不同类型的物质可分为三大类:绝缘体(Insulators),半导体(semi-conductors)和导体(conductors)。在渲染和游戏领域,我们一般只对其中的两个感兴趣:导体(金属)和绝缘体(电解质,非金属)。其中非金属具有单色/灰色镜面反射颜色。而金属具有彩色的镜面反射颜色。即非金属的F0是一个float。而金属的F0是一个float3。

PBR的范畴

基于物理的材质(Material)
基于物理的光照(Lighting)
基于物理适配的摄像机(Camera)
在这里插入图片描述

渲染方程

目前主流的是迪士尼原则的BRDF,着色模型是艺术导向的,而不一定要是完全物理正确的,并且对微平面BRDF的各项都进行了严谨的调查,提出了清晰明确而简单的解决方案。

属性名含义说明
baseColor(基础色)表面颜色通常由纹理贴图提供。
subsurface(次表面)控制漫反射形状使用次表面近似模拟次表面散射效果。
metallic(金属度)电介质与金属之间的混合0 表示电介质,1 表示金属。金属模型没有漫反射成分,其镜面反射等于基础色。
specular(镜面反射强度)入射镜面反射量用于替代折射率(IOR)进行调节。
specularTint(镜面反射颜色)镜面反射的颜色调节用于控制基础色部分的镜面反射颜色。掠射角下仍为非彩色反射。
roughness(粗糙度)控制表面反射性质粗糙度越高,反射越模糊;同时影响漫反射和镜面反射。
anisotropic(各向异性强度)镜面反射各向异性程度0 表示各向同性,1 表示最大各向异性,用于拉伸高光。
sheen(光泽度)额外的掠射高光分量主要用于模拟布料材质的掠射光。
sheenTint(光泽颜色)光泽度的颜色调节控制 sheen 项的颜色倾向。
clearcoat(清漆强度)第二层镜面波瓣(specular lobe)用于模拟特殊材质表面(如车漆)的一层透明涂层。
clearcoatGloss(清漆光泽度)清漆表面光滑度0 表示“缎面(satin)”效果,1 表示“光泽(gloss)”效果。

💡 注释:
• lobe(波瓣) 指的是镜面反射的形状或区域,用于描述光照与表面之间的反射分布。在 PBR 渲染中,通常一个材质可以包含一个或多个镜面波瓣,用以模拟不同类型的反射。
在这里插入图片描述
BRDF是一个可以插入渲染方程的函数,是渲染方程的核心内容。所有 BRDF 函数输出的结果是:入射光和出射光能量之间的比率的值。
目前的BRDF 公式
BRDF = k d π + k s ⋅ D ⋅ G ⋅ F 4 ( N ⋅ L ) ( N ⋅ V ) \text{BRDF} = \frac{k_d}{\pi} + k_s \cdot \frac{D \cdot G \cdot F}{4 (\mathbf{N} \cdot \mathbf{L}) (\mathbf{N} \cdot \mathbf{V})} BRDF=πkd+ks4(NL)(NV)DGF
G 4 ( N ⋅ L ) ( N ⋅ V ) \frac{G}{4 (\mathbf{N} \cdot \mathbf{L}) (\mathbf{N} \cdot \mathbf{V})} 4(NL)(NV)G 合并为 V V V项再乘以 π \pi π得到
Unity BRDF = k d + k s ⋅ ( D ⋅ V ⋅ F ) ⋅ π \text{Unity BRDF} = k_d + k_s \cdot (D \cdot V \cdot F) \cdot \pi Unity BRDF=kd+ks(DVF)π

其中 k d = D i f f u s e C o l o r k_d= DiffuseColor kd=DiffuseColor k s = S p e c u l a r C o l o r k_s = SpecularColor ks=SpecularColor
光照公式:
Lighting = ( DiffuseColor π ) + ( SpecularColor ⋅ D ( x ) ⋅ V ( x ) ⋅ F ( x ) ⋅ ( N ⋅ L ) ⋅ LightColor ) \text{Lighting} = \left( \frac{\text{DiffuseColor}}{\pi} \right) + \left( \text{SpecularColor} \cdot D(x) \cdot V(x) \cdot F(x) \cdot (\mathbf{N} \cdot \mathbf{L}) \cdot \text{LightColor} \right) Lighting=(πDiffuseColor)+(SpecularColorD(x)V(x)F(x)(NL)LightColor)
其中
D项为法线分布函数

            /*** Summary:* 此函数实现了 GGX(Trowbridge-Reitz)微表面分布函数,用于物理基础渲染中的镜面反射部分。* * 参数说明:* - a2: 表示粗糙度参数 α 的平方(alpha^2),决定了表面微表面的分布宽窄。* - NoH: 表示法线向量和半角向量之间的点积,即 N·H,反映光照与视角间的关系。** 算法描述:* 1. 首先根据输入参数计算中间变量 d,其表达式可以化简为:*      d = (a2 - 1) * NoH² + 1*    该值用于控制分布函数的锐度。** 2. 最后返回的结果为:*      D = a2 / (PI * d²)*    这个公式即为 GGX 分布函数的标准形式,用于描述表面微细结构对镜面高光的影响,*    在物理基础渲染中常用于计算BRDF的镜面部分。*/float D_GGX_Func( float a2, float NoH ){float d = ( NoH * a2 - NoH ) * NoH + 1;	return a2 / ( PI*d*d );					}

V项为可见性项

            /*** Summary:* 此函数实现了 Smith 模型下的几何可见性项近似(针对 GGX 粗糙度),* 用于物理基础渲染中镜面部分的可见性计算。** 参数说明:* - a2: 粗糙度参数 \(\alpha^2\),影响微表面的分布范围。* - NoV: 表示法线与视线向量之间的点积(N·V)。* - NoL: 表示法线与光线向量之间的点积(N·L)。** 算法描述:* 1. 首先对粗糙度参数 a2 求平方根,得到参数 a,用于控制几何项的过渡。* 2. 分别计算对视线和光线的几何可见性项* 3. 取这两者和的一半的倒数作为最终可见性结果*/float Vis_SmithJointApprox( float a2, float NoV, float NoL ){float a = sqrt(a2);float Vis_SmithV = NoL * ( NoV * ( 1 - a ) + a );float Vis_SmithL = NoV * ( NoL * ( 1 - a ) + a );return 0.5 * rcp( Vis_SmithV + Vis_SmithL );}

F项为菲涅尔方程

            /*** Summary:* 此函数实现了 Schlick 的菲涅耳近似,用于物理基础渲染中镜面反射的 Fresnel 部分计算。** 参数说明:* - SpecularColor: 物体表面的镜面反射颜色(或称基础反射率)。* - VoH: 表示视线向量和半角向量的点积(V·H)。** 算法描述:* 1. 首先计算 ( 1 - VoH ) 的五次方以得到 Fresnel 的衰减系数 Fc。* 2. 结合一个经验性因子(示例中为 50.0 * SpecularColor.g)来增强金属高光特性:* 3. 最终返回由基础镜面色和衰减系数混合后的结果*    其中  saturate( 50.0 * SpecularColor.g )表示对值范围的钳制以避免溢出。*/float3 F_Schlick_Func( float3 SpecularColor, float VoH ){float Fc = Pow5( 1 - VoH );					return saturate( 50.0 * SpecularColor.g ) * Fc + (1 - Fc) * SpecularColor;}

代码实现

Shader "Test/PBRShader"
{Properties{[MainTexture] _BaseMap("Albedo", 2D) = "white" {}[MainColor] _BaseColor("Color", Color) = (1,1,1,1)_MetallicMap("Metallic Map",2D) = "white"{}_Metallic("Metallic",Range(0.0,1.0)) = 1.0_RoughnessMap("Roughness Map",2D) = "white"{}_Roughness("Roughness",Range(0.0,1.0)) = 1.0_NormalMap("Normal Map",2D) = "bump"{}_Normal("Normal",float) = 1.0_OcclusionMap("OcclusionMap",2D) = "white"{}_OcclusionStrength("Occlusion Strength",Range(0.0,1.0)) = 1.0_Cutoff("Alpha Cutoff", Range(0.0, 1.0)) = 0.5}SubShader{Tags{"RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" "UniversalMaterialType" = "Lit" "IgnoreProjector" = "True" "ShaderModel"="4.5"}LOD 0Pass{HLSLPROGRAM#pragma exclude_renderers gles gles3 glcore#pragma target 4.5// -------------------------------------// Material Keywords#pragma shader_feature_local_fragment _ALPHATEST_ON// -------------------------------------// Universal Pipeline keywords#pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE _MAIN_LIGHT_SHADOWS_SCREEN#pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS#pragma multi_compile_fragment _ _ADDITIONAL_LIGHT_SHADOWS#pragma multi_compile_fragment _ _REFLECTION_PROBE_BLENDING#pragma multi_compile_fragment _ _REFLECTION_PROBE_BOX_PROJECTION#pragma multi_compile_fragment _ _SHADOWS_SOFT#pragma multi_compile_fragment _ _SCREEN_SPACE_OCCLUSION//--------------------------------------// GPU Instancing#pragma multi_compile_instancing#pragma instancing_options renderinglayer#pragma multi_compile _ DOTS_INSTANCING_ON#pragma vertex LitPassVertex#pragma fragment LitPassFragment#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"inline half Pow2 (half x){return x*x;}inline half Pow4 (half x){return x*x * x*x;}inline half Pow5 (half x){return x*x * x*x * x;}/*** Summary:* 此函数实现了 Lambert 漫反射的基本公式,用于物理基础渲染中的漫反射部分。** 参数说明:* - DiffuseColor: 表示物体表面的漫反射颜色。** 算法描述:* 1. 直接使用 \( \pi \) 作为归一化因子,对漫反射颜色进行能量守恒校正。* 2. 最终返回的漫反射分量为:\[ \frac{\text{DiffuseColor}}{\pi} \],*    这是传统的 Lambert 漫反射模型公式。*/float3 Diffuse_Lambert( float3 DiffuseColor ){return DiffuseColor / PI;}/*** Summary:* 此函数实现了 GGX(Trowbridge-Reitz)微表面分布函数,用于物理基础渲染中的镜面反射部分。* * 参数说明:* - a2: 表示粗糙度参数 α 的平方(alpha^2),决定了表面微表面的分布宽窄。* - NoH: 表示法线向量和半角向量之间的点积,即 N·H,反映光照与视角间的关系。** 算法描述:* 1. 首先根据输入参数计算中间变量 d,其表达式可以化简为:*      d = (a2 - 1) * NoH² + 1*    该值用于控制分布函数的锐度。** 2. 最后返回的结果为:*      D = a2 / (PI * d²)*    这个公式即为 GGX 分布函数的标准形式,用于描述表面微细结构对镜面高光的影响,*    在物理基础渲染中常用于计算BRDF的镜面部分。*/float D_GGX_Func( float a2, float NoH ){float d = ( NoH * a2 - NoH ) * NoH + 1;	return a2 / ( PI*d*d );					}/*** Summary:* 此函数实现了 Smith 模型下的几何可见性项近似(针对 GGX 粗糙度),* 用于物理基础渲染中镜面部分的可见性计算。** 参数说明:* - a2: 粗糙度参数 \(\alpha^2\),影响微表面的分布范围。* - NoV: 表示法线与视线向量之间的点积(N·V)。* - NoL: 表示法线与光线向量之间的点积(N·L)。** 算法描述:* 1. 首先对粗糙度参数 a2 求平方根,得到参数 a,用于控制几何项的过渡。* 2. 分别计算对视线和光线的几何可见性项* 3. 取这两者和的一半的倒数作为最终可见性结果*/float Vis_SmithJointApprox( float a2, float NoV, float NoL ){float a = sqrt(a2);float Vis_SmithV = NoL * ( NoV * ( 1 - a ) + a );float Vis_SmithL = NoV * ( NoL * ( 1 - a ) + a );return 0.5 * rcp( Vis_SmithV + Vis_SmithL );}/*** Summary:* 此函数实现了 Schlick 的菲涅耳近似,用于物理基础渲染中镜面反射的 Fresnel 部分计算。** 参数说明:* - SpecularColor: 物体表面的镜面反射颜色(或称基础反射率)。* - VoH: 表示视线向量和半角向量的点积(V·H)。** 算法描述:* 1. 首先计算 ( 1 - VoH ) 的五次方以得到 Fresnel 的衰减系数 Fc。* 2. 结合一个经验性因子(示例中为 50.0 * SpecularColor.g)来增强金属高光特性:* 3. 最终返回由基础镜面色和衰减系数混合后的结果*    其中  saturate( 50.0 * SpecularColor.g )表示对值范围的钳制以避免溢出。*/float3 F_Schlick_Func( float3 SpecularColor, float VoH ){float Fc = Pow5( 1 - VoH );					return saturate( 50.0 * SpecularColor.g ) * Fc + (1 - Fc) * SpecularColor;}half3 EnvBRDFApprox( half3 SpecularColor, half Roughness, half NoV ){const half4 c0 = { -1, -0.0275, -0.572, 0.022 };const half4 c1 = { 1, 0.0425, 1.04, -0.04 };half4 r = Roughness * c0 + c1;half a004 = min( r.x * r.x, exp2( -9.28 * NoV ) ) * r.x + r.y;half2 AB = half2( -1.04, 1.04 ) * a004 + r.zw;AB.y *= saturate( 50.0 * SpecularColor.g );return SpecularColor * AB.x + AB.y;}float GetSpecularOcclusion(float NoV, float RoughnessSq, float AO){return saturate( pow( NoV + AO, RoughnessSq ) - 1 + AO );}float3 AOMultiBounce( float3 BaseColor, float AO ){float3 a =  2.0404 * BaseColor - 0.3324;float3 b = -4.7951 * BaseColor + 0.6417;float3 c =  2.7552 * BaseColor + 0.6903;return max( AO, ( ( AO * a + b ) * AO + c ) * AO );}float3 StandardBRDF( float3 DiffuseColor, float3 SpecularColor, float Roughness, float3 N, float3 V, float3 L,float3 LightColor,float Shadow){float a2 = Pow4( Roughness );float3 H = normalize(L + V);float NoH = saturate(dot(N,H));float NoV = saturate(abs(dot(N,V)) + 1e-5);float NoL = saturate(dot(N,L));float VoH = saturate(dot(V,H));float3 Radiance = NoL * LightColor * Shadow * PI;float3 DiffuseLighting = Diffuse_Lambert(DiffuseColor) * Radiance;float D = D_GGX_Func( a2, NoH );float Vis = Vis_SmithJointApprox( a2, NoV, NoL );float3 F = F_Schlick_Func( SpecularColor, VoH );float3 SpecularLighting = (D * Vis * F) * Radiance;float3 DirectLighting = DiffuseLighting + SpecularLighting;return DirectLighting;}float3 DirectLighting_float(float3 DiffuseColor, float3 SpecularColor, float Roughness,float3 WorldPos, float3 N, float3 V){float3 DirectLighting = half3(0,0,0);#if defined(_MAIN_LIGHT_SHADOWS_SCREEN) && !defined(_SURFACE_TYPE_TRANSPARENT)float4 positionCS = TransformWorldToHClip(WorldPos);float4 ShadowCoord = ComputeScreenPos(positionCS);#elsefloat4 ShadowCoord = TransformWorldToShadowCoord(WorldPos);#endiffloat4 ShadowMask = float4(1.0,1.0,1.0,1.0);half3 DirectLighting_MainLight = half3(0,0,0);{Light light = GetMainLight(ShadowCoord,WorldPos,ShadowMask);half3 L = light.direction;half3 LightColor = light.color;half Shadow = light.shadowAttenuation;DirectLighting_MainLight = StandardBRDF(DiffuseColor,SpecularColor,Roughness,N,V,L,LightColor,Shadow);}half3 DirectLighting_AddLight = half3(0,0,0);#ifdef _ADDITIONAL_LIGHTSuint pixelLightCount = GetAdditionalLightsCount();for(uint lightIndex = 0; lightIndex < pixelLightCount ; ++lightIndex){Light light = GetAdditionalLight(lightIndex,WorldPos,ShadowMask);half3 L = light.direction;half3 LightColor = light.color;half Shadow = light.shadowAttenuation * light.distanceAttenuation;DirectLighting_AddLight += StandardBRDF(DiffuseColor,SpecularColor,Roughness,N,V,L,LightColor,Shadow);}#endifDirectLighting = DirectLighting_MainLight + DirectLighting_AddLight;return DirectLighting;}float3 IndirectLighting_float(float3 DiffuseColor, float3 SpecularColor, float Roughness, float3 WorldPos, float3 N, float3 V,float Occlusion){float3 IndirectLighting = half3(0,0,0);float NoV = saturate(abs(dot(N,V)) + 1e-5);//SHfloat3 DiffuseAO = AOMultiBounce(DiffuseColor,Occlusion);float3 RadianceSH = SampleSH(N);float3 IndirectDiffuse = RadianceSH * DiffuseColor * DiffuseAO;//IBLhalf3 R = reflect(-V,N);half3 SpeucularLD = GlossyEnvironmentReflection(R,WorldPos,Roughness,Occlusion);half3 SpecularDFG = EnvBRDFApprox(SpecularColor,Roughness,NoV);float SpecularOcclusion = GetSpecularOcclusion(NoV,Pow2(Roughness),Occlusion);float3 SpecularAO = AOMultiBounce(SpecularColor,SpecularOcclusion);float3 IndirectSpecular = SpeucularLD * SpecularDFG * SpecularAO;IndirectLighting = IndirectDiffuse + IndirectSpecular;return IndirectLighting;}struct Attributes{float4 positionOS   : POSITION;float3 normalOS     : NORMAL;float4 tangentOS    : TANGENT;float2 texcoord     : TEXCOORD0;UNITY_VERTEX_INPUT_INSTANCE_ID};struct Varyings{float2 uv : TEXCOORD0;float3 positionWS : TEXCOORD1;half3 normalWS : TEXCOORD2;half4 tangentWS : TEXCOORD3;   float4 shadowCoord : TEXCOORD4;float4 positionCS : SV_POSITION;UNITY_VERTEX_INPUT_INSTANCE_ID};TEXTURE2D(_BaseMap);        SAMPLER(sampler_BaseMap);TEXTURE2D(_MetallicMap);    SAMPLER(sampler_MetallicMap);TEXTURE2D(_RoughnessMap);    SAMPLER(sampler_RoughnessMap);TEXTURE2D(_NormalMap);    SAMPLER(sampler_NormalMap);TEXTURE2D(_OcclusionMap);    SAMPLER(sampler_OcclusionMap);CBUFFER_START(UnityPerMaterial)half4 _BaseColor;half _Metallic;half _Roughness;half _Normal;half _OcclusionStrength;half _Cutoff;CBUFFER_END/*** Summary:* 该函数 LitPassVertex 是 Unity 中顶点着色器的一部分,用于处理传入的顶点属性,生成用于后续光照计算的中间数据(Varyings)。* * 主要步骤:* 1. 实例化支持:  *    - 调用 UNITY_SETUP_INSTANCE_ID(input) 初始化输入顶点的实例 ID。  *    - 通过 UNITY_TRANSFER_INSTANCE_ID(input, output) 将实例 ID 从输入传递到输出,确保 GPU 实例化时每个实例的信息可以正确传播。** 2. 位置与法线计算:  *    - 使用 GetVertexPositionInputs 从局部空间位置(input.positionOS.xyz)中获取世界空间位置(positionWS)和裁剪空间位置(positionCS)。*    - 使用 GetVertexNormalInputs 从法线(input.normalOS)和切线(input.tangentOS)中计算出经过转换的世界空间法线(normalWS)以及切线信息。** 3. UV 与切线数据输出:  *    - 将输入纹理坐标(input.texcoord)赋值给输出 UV。*    - 将计算得到的世界空间法线赋值给输出 normalWS。** 4. 切线处理:  *    - 计算 sign 值:通过 input.tangentOS.w 与 GetOddNegativeScale() 相乘,考虑到模型可能存在的奇异缩放情况。  *    - 构建世界空间切线向量 tangentWS,并将 sign 值作为 w 分量存储到输出中。** 5. 其他输出数据:  *    - 传递世界空间位置(positionWS)给输出,供后续光照计算使用。  *    - 计算并传递阴影坐标(shadowCoord),用于阴影贴图的采样。  *    - 将裁剪空间位置(positionCS)赋给输出,后续会用于顶点投影到屏幕上。** 6. 最终返回:  *    - 返回包含所有计算结果的输出结构体(Varyings),供后续像素着色器阶段使用。*/Varyings LitPassVertex(Attributes input){Varyings output = (Varyings)0;UNITY_SETUP_INSTANCE_ID(input);UNITY_TRANSFER_INSTANCE_ID(input, output);VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz);VertexNormalInputs normalInput = GetVertexNormalInputs(input.normalOS, input.tangentOS);output.uv = input.texcoord;output.normalWS = normalInput.normalWS;real sign = input.tangentOS.w * GetOddNegativeScale();half4 tangentWS = half4(normalInput.tangentWS.xyz, sign);output.tangentWS = tangentWS;//half3 viewDirWS = GetWorldSpaceNormalizeViewDir(vertexInput.positionWS);output.positionWS = vertexInput.positionWS;output.shadowCoord = GetShadowCoord(vertexInput);output.positionCS = vertexInput.positionCS;return output;}/*** Summary:* 该函数 LitPassFragment 是 Unity 中像素着色器的核心部分,负责最终颜色计算。通过采样纹理、处理材质属性、计算光照模型等步骤,生成物体表面每个像素的最终颜色输出。* * 主要步骤:* 1. 实例化与基础数据准备:*    - 调用 UNITY_SETUP_INSTANCE_ID(input) 初始化实例 ID。*    - 从输入结构提取 UV、世界坐标、视角方向等基础参数。** 2. 法线与切线空间处理:*    - 通过 TBN 矩阵(切线-副切线-法线矩阵)将法线从切线空间转换到世界空间:*      - 规范化输入的世界空间法线(WorldNormal)和切线(WorldTangent)*      - 计算副切线(WorldBinormal)并通过 tangentWS.w 处理镜像缩放*      - 构建 TBN 矩阵用于后续法线贴图转换** 3. 阴影与屏幕空间数据:*    - 获取阴影坐标(ShadowCoord)用于阴影贴图采样*    - 通过 GetNormalizedScreenSpaceUV 生成屏幕空间 UV(ScreenUV)*    - 初始化阴影掩码(ShadowMask)为默认值** 4. 基础材质属性处理:*    - 采样基础颜色贴图并与 _BaseColor 属性混合,得到 BaseColorAlpha*    - 执行 Alpha 测试(仅在 _ALPHATEST_ON 启用时):*      - 通过 clip 指令丢弃低于 _Cutoff 阈值的像素** 5. 高级材质属性计算:*    - 金属度(Metallic):通过贴图采样与 _Metallic 属性控制*    - 粗糙度(Roughness):通过贴图采样与属性控制,限制最小值避免除零错误*    - 法线贴图处理:解压法线贴图数据并应用 _Normal 强度参数*    - 环境光遮蔽(Occlusion):采样贴图并通过 _OcclusionStrength 控制影响强度** 6. 光照模型计算:*    - 分离漫反射颜色(DiffuseColor)和镜面反射颜色(SpecularColor):*      - 金属度越高,漫反射越暗,镜面反射越接近基础色*    - 计算直接光照(DirectLighting):*      - 调用 DirectLighting_float 函数处理平行光/点光源等直接光源贡献*    - 计算间接光照(IndirectLighting):*      - 调用 IndirectLighting_float 处理环境光、反射探针等间接光源*      - 应用环境光遮蔽系数** 7. 最终返回:*    - 将直接与间接光照结果相加,输出不透明度为 1 的最终颜色*    - 返回 half4 类型的颜色值到渲染目标(SV_Target)*/half4 LitPassFragment(Varyings input) : SV_Target{UNITY_SETUP_INSTANCE_ID(input);float2 UV = input.uv;float3 WorldPos = input.positionWS;half3 ViewDir = GetWorldSpaceNormalizeViewDir(WorldPos);half3 WorldNormal = normalize(input.normalWS);half3 WorldTangent = normalize(input.tangentWS.xyz);half3 WorldBinormal = normalize(cross(WorldNormal,WorldTangent) * input.tangentWS.w);half3x3 TBN = half3x3(WorldTangent,WorldBinormal,WorldNormal);float4 ShadowCoord = input.shadowCoord;float2 ScreenUV = GetNormalizedScreenSpaceUV(input.positionCS);half4 ShadowMask = float4(1.0,1.0,1.0,1.0);half4 BaseColorAlpha = SAMPLE_TEXTURE2D(_BaseMap,sampler_BaseMap,UV) * _BaseColor;half3 BaseColor = BaseColorAlpha.rgb;half BaseAlpha = BaseColorAlpha.a;#if defined(_ALPHATEST_ON)clip(BaseAlpha - _Cutoff);#endiffloat Metallic = saturate(SAMPLE_TEXTURE2D(_MetallicMap,sampler_MetallicMap,UV).r * _Metallic);float Roughness = saturate(SAMPLE_TEXTURE2D(_RoughnessMap,sampler_RoughnessMap,UV).r * _Roughness);half3 NormalTS = UnpackNormalScale(SAMPLE_TEXTURE2D(_NormalMap,sampler_NormalMap,UV),_Normal);WorldNormal = normalize(mul(NormalTS,TBN));half Occlusion = SAMPLE_TEXTURE2D(_OcclusionMap,sampler_OcclusionMap,UV).r;Occlusion = lerp(1.0,Occlusion,_OcclusionStrength);float3 DiffuseColor = lerp(BaseColor,float3(0.0,0.0,0.0),Metallic);float3 SpecularColor = lerp(float3(0.04,0.04,0.04),BaseColor,Metallic);Roughness = max(Roughness,0.001f);half3 DirectLighting = DirectLighting_float(DiffuseColor,SpecularColor,Roughness,WorldPos,WorldNormal,ViewDir);half3 IndirectLighting = IndirectLighting_float(DiffuseColor,SpecularColor,Roughness,WorldPos,WorldNormal,ViewDir,Occlusion);half4 color = half4(DirectLighting + IndirectLighting,1.0f);return color;}ENDHLSL}Pass{Name "ShadowCaster"Tags{"LightMode" = "ShadowCaster"}ZWrite OnZTest LEqualColorMask 0Cull[_Cull]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}}FallBack "Hidden/Universal Render Pipeline/FallbackError"
}

效果实测
请添加图片描述

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

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

相关文章

COM组件使用方法

普通COM组件&#xff08;如DLL&#xff09;仅暴露方法/属性接口&#xff0c;而ActiveX控件&#xff08;如OCX&#xff09;需要可视化交互&#xff08;如按钮、表格&#xff09;&#xff0c;需通过 ​​AxInterop​​ 包装器实现宿主环境集成。 项目中引入ActiveX控件流程如下。…

在 Spring Boot 项目中如何使用索引来优化 SQL 查询?

在 Spring Boot 项目中使用索引来优化 SQL 查询是提升数据库性能最常用的方法之一。下面是详细的步骤和实践指南&#xff1a; 核心目标&#xff1a;让数据库能够通过扫描索引&#xff08;小范围、有序的数据结构&#xff09;快速定位到所需数据行&#xff0c;而不是扫描整个表…

Vue3生产环境与Vue Devtools

在 Vue 3 的生产环境中&#xff0c;默认情况下 Vue Devtools 是无法正常使用 的&#xff0c;但开发者可以通过配置强制启用。以下是关键信息总结&#xff1a; &#x1f4cc; 核心结论 默认不可用 Vue 3 生产构建会移除 Devtools 支持以优化性能和安全性。 可强制启用 通过构建…

ARP渗透学习1

ARP协议工作原理 1. 什么是ARP ARP定义: 地址解析协议&#xff08;Address Resolution Protocol&#xff09;&#xff0c;是根据IP地址获取物理地址的一个TCP/IP协议。 2. 工作原理 ARP表: 每台计算机都需要一个ARP表&#xff0c;用来保存IP地址和MAC地址的映射关系。查询过…

甲骨文云2025深度解析:AI驱动的云原生生态与全球化突围

一、战略转型&#xff1a;从数据库巨头到AI云服务先锋 1. 技术重心向AI与云深度迁移 甲骨文在2025年加速向AI原生云架构转型&#xff0c;其核心战略围绕生成式AI与量子计算展开。通过推出Oracle 23ai自治数据库&#xff0c;深度集成AI向量搜索功能&#xff0c;并重构云基础设…

【网络原理】TCP异常处理(二):连接异常

目录 一. 由进程崩溃引起的连接断开 二. 由关机引起的连接断开 三. 由断电引起的连接断开 四. 由网线断开引起的连接断开 一. 由进程崩溃引起的连接断开 在一般情况下&#xff0c;进程无论是正常结束&#xff0c;还是异常崩溃&#xff0c;都会触发回收文件资源&#xff0c;…

想做博闻强记的自己

2025年4月29日&#xff0c;13~25℃&#xff0c;还好 待办&#xff1a; 冶金《物理》期末测试 阅卷&#xff08;冶金《物理》期末测试试卷&#xff09; 重修《物理》《物理2》电子材料归档 规则变更&#xff0c;《高等数学2》期末试卷推倒重来 遇见&#xff1a;直播画面。 感受…

IP属地是实时位置还是自己设置

刷微博、抖音时&#xff0c;评论区总能看到“IP属地”&#xff1f;这个突然冒出来的小标签&#xff0c;让不少网友摸不着头脑&#xff1a;‌IP属地是实时位置&#xff0c;还是可以自己设置&#xff1f;‌别急&#xff0c;今天咱们就来聊聊这个话题&#xff01; 1、什么是IP属地…

水力压裂多裂缝扩展诱发光纤应变演化试验研究

1.概述 本文基于OFDR技术的光纤应变监测方法&#xff0c;监测了真三轴条件下人造岩石试样与页岩的水力压裂试验。结果表明&#xff0c;OFDR技术能以毫米级分辨率实时监测裂缝起裂、扩展及闭合全过程&#xff0c;并建立基于应变演化的裂缝判别准则&#xff0c;为光纤压裂监测的…

4、RabbitMQ的七种工作模式介绍

目录 一、Simple(简单模式) 1.1 概念 1.2 代码实现 消费者 运行结果 二、Work Queue&#xff08;工作队列&#xff09; 2.1 概念 1.2 代码实现 生产者 消费者 运行结果 三、Publish/Subscribe&#xff08;发布/订阅模式&#xff09; 3.1 概念 3.2 代码实现 生产者…

厚铜PCB钻孔工艺全解析:从参数设置到孔壁质量的关键控制点

在现代电子设备中&#xff0c;厚铜PCB&#xff08;印刷电路板&#xff09;扮演着至关重要的角色。它们不仅为电子元件提供了支撑&#xff0c;还实现了电路之间的连接。然而&#xff0c;在生产厚铜PCB时&#xff0c;钻孔是一个关键环节。本文将为您介绍厚铜PCB生产中钻孔的科普知…

缺口拼图,非线性坐标关联

继上一篇文章&#xff0c; 欢迎一起交流探讨 https://t.zsxq.com/GEIze

OTA(Over-The-Air)升级

简介&#xff1a; OTA&#xff08;Over-the-Air&#xff09;是一种通过无线方式进行数据传输和更新的技术&#xff0c;通常用于电子设备&#xff08;如智能手机、汽车、物联网设备等&#xff09;的软件、固件或配置更新。OTA可以在设备与服务器之间进行远程传输&#xff0c;用户…

fastapi和flaskapi有什么区别

FastAPI 和 Flask 都是 Python 的 Web 框架&#xff0c;但设计目标和功能特性有显著差异。以下是它们的核心区别&#xff1a; 1. ‌性能与异步支持‌ ‌FastAPI‌ 基于 ‌Starlette‌&#xff08;高性能异步框架&#xff09;和 ‌Pydantic‌&#xff08;数据校验库&#xff09;…

RCS认证是什么?RCS认证的好处?RCS认证所需要的资料

1. RCS&#xff08;Recycled Claim Standard&#xff09;认证 定义&#xff1a;由 Textile Exchange&#xff08;纺织品交易所&#xff09; 制定的国际标准&#xff0c;用于验证产品中回收材料&#xff08;如再生纤维、塑料、金属等&#xff09;的含量和供应链的可追溯性&…

10 基于Gazebo和Rviz实现导航仿真,包括SLAM建图,地图服务,机器人定位,路径规划

在9中我们已经实现了机器人的模块仿真&#xff0c;现在要在这个基础上实现SLAM建图&#xff0c;地图服务&#xff0c;机器人定位&#xff0c;路径规划 1. 还是在上述机器人的工作空间下&#xff0c;新建功能包&#xff08;nav&#xff09;&#xff0c;导入依赖 gmapping ma…

OpenGL----OpenGL纹理与纹理缓存区

在现代计算机图形学中,纹理(Texture)是一个至关重要的概念。它不仅可以为几何体表面添加细节和真实感,还可以用于实现各种复杂的视觉效果和数据处理。在OpenGL中,纹理的应用范围非常广泛,从基本的颜色映射到高级的阴影映射、环境映射等。本文将深入探讨OpenGL纹理与纹理缓…

Scikit-learn工具介绍与数据集

一、Scikit-learn简介与安装 Scikit-learn是Python中最流行的机器学习库之一&#xff0c;它提供了简单高效的数据挖掘和数据分析工具。 Python语言机器学习工具 Scikit-learn包括许多智能的机器学习算法的实现 Scikit-learn文档完善&#xff0c;容易上手&#xff0c;丰富的A…

Byte-Buddy系列 - 第4讲 byte-buddy无法读取到SpringBoot Jar中的类

目录 一、问题描述二、原因分析三、解决方案1&#xff08;推荐&#xff09;&#xff1a;获取线程上下文中的类加载器扩展 四、解决方案2&#xff1a;自定义SpringBoot类加载器 一、问题描述 在使用Byte-Buddy中的TypePool对类进行扩展后&#xff0c;在本地开发集成环境&#x…

AutogenStudio使用

官网介绍&#xff1a;https://microsoft.github.io/autogen/stable/ Autogen是什么&#xff1f; AutoGen 是由微软开发的一个开源框架&#xff0c;旨在通过 多智能体协作&#xff08;Multi-Agent Collaboration&#xff09; 实现复杂的任务自动化。它的核心思想是让多个 AI 代…