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+ks⋅4(N⋅L)(N⋅V)D⋅G⋅F
将 G 4 ( N ⋅ L ) ( N ⋅ V ) \frac{G}{4 (\mathbf{N} \cdot \mathbf{L}) (\mathbf{N} \cdot \mathbf{V})} 4(N⋅L)(N⋅V)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⋅(D⋅V⋅F)⋅π
其中 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)+(SpecularColor⋅D(x)⋅V(x)⋅F(x)⋅(N⋅L)⋅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"
}
效果实测
相关文章:
Unity PBR基础知识
PBR原理 基于物理的渲染(Physically Based Rendering,PBR)是指使用基于物理原理和微平面理论建模的着色/光照模型,以及使用从现实中测量的表面参数来准确表示真实世界材质的渲染理念。 PBR基础理念 微平面理论(Micr…...
智慧交警系统架构设计方案
一、引言:智慧交警为何成为城市治理的刚需? 当前,中国城市化进程加速,汽车保有量激增,交通拥堵、事故频发、执法效率不足等问题日益突出。传统交通管理依赖人力巡查与分散系统,已难以应对复杂需求。智慧交…...
NOC科普一
拓扑结构 NoC里Router之间的link链路连接可以定义成不同的结构以改变通信测量和简化片上通信结构。 (a)Ring:环形,每个router都有2个相邻节点,虽然部署和故障排除相对容易,但主要缺点是其通信的距离也即环…...
Linux CentOS 7 安装Apache 部署html页面
*、使用yum包管理器安装Apache。运行以下命令: sudo yum install httpd *、启动Apache服务 sudo systemctl start httpd *、设置Apache服务开机自启 sudo systemctl enable httpd *、验证Apache是否运行 sudo systemctl status httpd 或者,通过浏…...
人工智能在医疗行业的应用和发展前景
人工智能在医疗行业的应用和发展前景 引言 在科技日新月异的今天,人工智能(Artificial Intelligence,AI)已然成为全球最具潜力与影响力的技术之一。医疗行业,作为关乎人类健康与生命的关键领域,正迅速成为人工智能应用的热门阵地。人工智能在医疗领域的应用,不仅为解决…...
vue3+Nest.js项目 部署阿里云
可以先参考之前的vue3express部署的文章 vue3viteexpressmongoDB上线(新手向)_vue3 vite express-CSDN博客 区别在于express和数据库 前端前往上面文章查看 1.nest.js部署 首先,把nest.js中相关的文件打包 除去依赖(node_modules)上传到服…...
phpstudy修改Apache端口号
1. 修改Listen.conf文件 本地phpstudy安装目录: 2.其他问题 ① 修改httpd.conf不起作用 ② 直接通过控制面板配置好像有延迟缓存...
JSON-RPC 2.0 规范中文版——无状态轻量级远程过程调用协议
前言 JSON-RPC是一种简单、轻量且无状态的远程过程调用(RPC)协议,它允许不同系统通过标准化的数据格式进行通信。自2010年由JSON-RPC工作组发布以来,已成为众多应用中实现远程交互的基础协议之一。本规范主要表达了JSON-RPC 2.0版…...
DeepSeek+Dify之七借助API和Trae完成demo
DeepSeek+Dify之六通过API调用工作流 文章目录 背景准备资料1、借助Trae来创建demo2、前后端主要代码3、测试demo4、完整项目背景 在软件开发与项目实践领域,常常需要借助各种工具与技术来快速搭建可运行的示例项目,以验证思路、展示功能或进行技术探索。本文聚焦于借助 Tra…...
C++ 红黑树
上一节我介绍了二叉搜索树家族的AVL树,这里我们来介绍二叉搜索树家族的另一个成员,也是使用最广泛的成员。 1.AVL树与红黑树的区别 平衡性质 AVL 树:是严格的平衡二叉树,要求任意节点的左右子树高度差的绝对值不超过 1ÿ…...
学习海康VisionMaster之线圆测量
一:进一步学习了 今天学习下VisionMaster中的线圆测量:核心就是坐标点到直线的距离量测 1:什么是线圆测量? 工业自动化中很常见的应用尺寸测量,需要量测一个零件的外形尺寸,其中一项如果是需要测量圆心到直…...
Uniapp:置顶
目录 一、出现场景二、效果展示三、具体使用一、出现场景 在项目的开发过程中,我们经常会用到置顶的功能,比如说从页面的最下方滑动到最上面太慢了,这个时候我们就可以使用置顶功能。 二、效果展示 三、具体使用 参数名类型必填说明scrollTopNumber否滚动到页面的目标位置…...
UDP数据报和TCP流套接字编程
文章目录 UDP数据报套接字编程1.DatagramSocket类2.DatagramPacket类3. InetSocketAddress类构建服务端和客户端 TCP流套接字编程1. ServerSocket类2.Socket类构建服务端和客户端 扩展对话形式简易的字典多线程实现线程池实现 UDP数据报套接字编程 1.DatagramSocket类 Datagr…...
某建筑石料用灰岩矿自动化监测
1. 项目简介 某建材有限公司成立于2012年,是一家集矿山开采、石料生产及销售为一体的建筑材料生产企业,拥有两条年产500万吨的环保型精品骨料生产线,各类工程机械 30 多台套,运输车辆50多辆。公司坚持生态优先,以高质…...
C++11 的编译器支持
C11 主要功能特性一览 特性描述提案GCCClangMSVCApple ClangEDG eccpIntel CNvidia HPC C (ex PGI)*Nvidia nvccCrayEmbarcadero C BuilderIBM Open XL C for AIXIBM Open XL C for z/OSIBM XL CSun/Oracle CHP aCCDigital Mars C核心功能右值引用 (T&&)支持移动语义和…...
20250429 垂直地表发射激光测量偏转可以验证相对性原理吗
垂直地表发射激光测量偏转可以验证相对性原理吗 垂直地表发射激光测量偏转可以在一定条件下用于检验广义相对论中的等效原理和引力对光传播的影响,但要说直接验证整个相对性原理(狭义广义)是不准确的。我们可以逐步分析这个问题:…...
Makefile 在 ARM MCU 开发中的编译与链接参数详解与实践
内容大纲 引言 一、预处理与宏定义 头文件搜索路径:-I 宏定义:-D 二、编译器选项(CFLAGS) 架构与指令集:-mcpu、-mthumb 优化与调试:-Os、-O2、-g 警告与错误:-Wall、-Werror 代码剥离:-ffunction-sections、-fdata-sections 其他常用选项 三、链接器选项(LDFLAGS) 链…...
AimRT 从零到一:官方示例精讲 —— 四、logger示例.md
logger示例 官方仓库:logger 配置文件(configuration_logger.yaml) 依据官方示例项目结构自行编写YAML配置文件: # 基础信息 base_info:project_name: Logger # 项目名称build_mode_tags: ["EXAMPLE", "SIMULATION", "TE…...
mybatis传递多个不同类型的参数到mapper xml文件
在业务中查询某张表时需要设置多个查询条件,并且还要根据id列表进行权限过滤,这时推荐采用Map<String, Object>作为参数进行查询,因为:Object可以设置成不同的类型,比如:List<Integer> ids&…...
信创开发中的数据库详解:国产替代背景下的技术生态与实践指南
🧑 博主简介:CSDN博客专家、CSDN平台优质创作者,高级开发工程师,数学专业,10年以上C/C, C#, Java等多种编程语言开发经验,拥有高级工程师证书;擅长C/C、C#等开发语言,熟悉Java常用开…...
【Linux】第十三章 访问Linux文件系统
目录 1. 存储设备是什么?怎么理解分区和格式化? 2. 文件系统是什么? 3. 挂载是什么?挂载点是什么? 4. 怎么理解块设备? 5. 在SATA附加存储中,第一磁盘上的第一个分区和第二磁盘的第二个分区…...
多态与虚函数
在 C 中,virtual 关键字用于实现多态性(Polymorphism),这是面向对象编程(OOPP)的核心特性之一。多态性允许你编写通用的代码,该代码可以操作不同类型的对象,而这些对象可以有不同的内…...
Spring Boot - 配置管理与自动化配置进阶
Spring Boot 的配置管理和自动化配置是其核心特性之一,能够显著提升开发效率和应用灵活性。本文将深入探讨以下内容: 1、配置管理:多环境配置与优先级解析。 2、自动化配置:自定义 Spring Boot Starter 与 spring.factories 扩展…...
第六章 QT基础:7、Qt中多线程的使用
在进行桌面应用程序开发时,假设应用程序需要处理比较复杂的逻辑,如果只有一个线程去处理,就会导致窗口卡顿,无法处理用户的相关操作。 这种情况下,需要使用多线程: 主线程处理窗口事件和控件更新子线程进…...
前端Vue3 + 后端Spring Boot,前端取消请求后端处理逻辑分析
在 Vue3 Spring Boot 的技术栈下,前端取消请求后,后端是否继续执行业务逻辑的答案仍然是 取决于请求处理的阶段 和 Spring Boot 的实现方式。以下是结合具体技术的详细分析: 1. 请求未到达 Spring Boot 场景:前端通过 AbortContr…...
ShaderToy学习笔记 05.3D旋转
1. 3D旋转 1.1. 汇制立方体 由于立方体没有旋转,所以正对着看过去时,看起来是正方形的,所以需要旋转一下,才能看到立方体的样子。 常见几何体的SDF BOX 的SDF为 float sdBox( vec3 p, vec3 b ) {vec3 q abs(p) - b;return len…...
编程日志4.24
栈的链表基础表示结构 #include<iostream> #include<stdexcept> using namespace std; //模板声明,表明Stack类是一个通用的模板,可以用于存储任何类型的元素T template<typename T> //栈的声明 //Stack类的声明,表示一…...
通信设备制造数字化转型中的创新模式与实践探索
在数字化浪潮下,通信设备制造企业积极探索创新模式,推动数字化转型,以提升竞争力和适应市场变化。 在生产模式创新方面,企业引入工业互联网平台,实现设备互联互通与生产过程智能化监控。通过在生产设备上安装传感器&a…...
同一个路由器接口eth0和ppp0什么不同?
答案摘自 百度知道, eth0是以太网接口,是表示以太网连接的物理接口,路由器可能会有不止一个以太网接口,因此可能会eth0,eht1之类的。 ppp0是经以太网接口PPP拨号时创建的链路接口,用以建PPP拨号连接的&am…...
零训练成本优化LLM: 11种LLM权重合并策略原理与MergeKit实战配置
随着大语言模型的快速发展,如何在不消耗大量计算资源的情况下优化模型性能成为业界关注焦点。模型权重合并技术提供了一种零训练成本的高效解决方案,能够智能整合多个专业微调模型的优势,无需额外训练即可显著提升性能表现。本文系统剖析11种…...
基于tabula对pdf中多个excel进行识别并转换成word中的优化(五)
优化地方:处理合并的单元格内容。 1、修改为stream"complex" 2、增加换行符f"{table_data[i - 1][j]}\n{table_data[i][j]}".strip() 一、pdf中excel样例 二、完整代码 import tabula import numpy as np from docx import Document from docx…...
QT中的网络编程
Qt中的网络编程是通过封装操作系统的API进行实现的 C标准库中,并没有提供网络编程的封装接口 进行网络编程时本质是在编写应用层代码,需要传输层提供支持 传输层最核心的协议为UDP/TCP 使用Qt网络编程的API时,需要在.pro文件中添加network模块…...
0.5 像素边框实现
0.5 像素边框怎么实现 文章目录 0.5 像素边框怎么实现方法 1:使用 transform: scale() 缩放(推荐)方法 2:直接使用 0.5px 边框(部分浏览器支持)方法 3:使用 box-shadow 模拟边框方法 4ÿ…...
【Vagrant+VirtualBox创建自动化虚拟环境】Ansible测试Playbook
文章目录 Vagrant安装vagrant安装 VirtualBox如何使用 Ansible安装AnsiblePlaybook测试创建hosts文件创建setup.yml文件 Vagrant Vagrant是一个基于Ruby的工具,用于创建和部署虚拟化开发环境。它使用Oracle的开源VirtualBox虚拟化系统,使用 Chef创建自动…...
“连接世界的桥梁:深入理解计算机网络应用层”
一、引言 当你浏览网页、发送邮件、聊天或观看视频时,这一切都离不开计算机网络中的应用层(Application Layer)。 应用层是网络协议栈的最顶层,直接为用户的各种应用程序提供服务。它为用户进程之间建立通信桥梁,屏蔽了…...
Vulkan与OpenGL的对比
传统图形API与现代图形API 传统图形API指的是OpenGL/DirectX11这类简单易用、驱动托管严重的图形接口;而现代图形API则指的是Vulkan/Metal/DirectX12这类使用复杂、暴露更多底层硬件功能来显式控制的弱驱动设计的图形接口。 现代图形API与传统图形API相比ÿ…...
海外社交App的Web3革命:去中心化社交与Token经济实战指南
一、Web3社交的核心组件:从身份到经济的重构 去中心化身份(DID)技术栈:Ceramic IDX协议构建链上身份图谱代码示例:javascript// 创建DID const ceramic new CeramicClient() const did new DID({ provider: cerami…...
凯撒密码算法的实现
在密码学里,凯撒密码(也叫恺撒密码、移位密码、恺撒代码或者恺撒移位)是一种简单且广为人知的加密技术。它属于替换密码的一种,在这种加密方式中,明文中的每个字母都会被替换成字母表中往后移动固定位数的字母。例如&a…...
Chrome的插件扩展程序安装目录是什么?在哪个文件夹?
目录 前提 直接复制到浏览器中打开 Mac下Chrome extension 安装路径 最近换了mac pro用起来虽然方便,但是对常用的一些使用方法还是不熟悉。这不为了找到mac上chrome插件的安装路径在哪里,花费了不少时间。我想应用有不少像小编一样刚刚使用mac的小白…...
C++23中的std::forward_like:完美转发的增强
文章目录 一、背景与动机(一)完美转发的局限性(二)std::forward_like的提出 二、std::forward_like的设计与实现(一)基本语法(二)实现原理(三)与std::forward…...
AI与软件测试的未来:如何利用智能自动化改变测试流程
用工作流生成测试用例和自动化测试脚本! 随着人工智能(AI)技术的迅猛发展,软件测试作为软件开发生命周期中的关键环节,正在经历一场前所未有的变革。传统的测试方法已经无法满足现代快速迭代和持续交付的需求ÿ…...
React Native 动态切换主题
React Native 动态切换主题 创建主题配置和上下文创建主题化高阶组件主应用组件主屏幕组件(类组件形式) 创建主题配置和上下文 // ThemeContext.jsimport React, { Component, createContext } from react;import { Appearance, AsyncStorage } from rea…...
得物 小程序 6宫格 分析
声明 本文章中所有内容仅供学习交流使用,不用于其他任何目的,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关! 逆向过程 部分python代码 if result …...
PocketFlow一个最小的Agent框架
1、背景 PocketFlow 2、使用 python 的版本需要python3.10 在cookbook中有如何使用的说明,如图所示 在tuils.py中修改代码 def call_llm(messages):# client OpenAI(api_keyos.environ.get("OPENAI_API_KEY", "your-api-key"))client Op…...
Objective-C Block 底层原理深度解析
Objective-C Block 底层原理深度解析 1. Block 是什么? 1.1 Block 的本质 Block 是 Objective-C 中的特殊对象,实现了匿名函数的功能 通过 isa 指针继承自 NSObject,可以响应(如 copy、retain、release)等内存管理方…...
AlDente Pro for Mac电脑 充电限制保护工具 安装教程【简单,轻松上手】
AlDente Pro for Mac电脑 充电限制保护工具 安装教程【简单,轻松上手】 AlDente Pro for Mac,是一款充电限制保护工具,是可以限制最大充电百分比来保护电池的工具。锂离子和聚合物电池(如 MacBook 中的电池)在40&…...
Linux systemd 从理论到实践:现代系统管理的核心工具
文章目录 引言:为什么需要 systemd?第一部分:systemd 核心理论1.1 systemd 的设计哲学1.2 核心组件1.3 单元文件(Unit File)结构 第二部分:实战操作指南2.1 基础命令2.2 服务管理高级操作2.3 日志管理&…...
分享一个移动端项目模板:React-Umi4-mobile
分享一个移动端项目模板:React-Umi4-mobile 大家好,今天想和大家分享一个我最近做的移动端项目模板 React-Umi4-mobile。 模板的主要内容 这个模板主要包括: 基于 Umi 4 框架使用了 antd-mobile 组件库配置了 px 自动转 vw(基…...
Tailwind CSS 响应式设计解析(含示例)
本文内容: Tailwindcss V4 中如何使用响应式设计功能,包括默认断点、自定义断点、断点范围控制以及容器查询的各种技巧,帮助你在不离开 HTML 的前提下优雅构建响应式页面。 🌟 默认断点用法(移动优先) Tail…...
ElasticSearch入门
1 elasticsearch概述 1.1 elasticsearch 简介 官网: https://www.elastic.co/ ElasticSearch是一个基于 Lucene 的搜索服务器,基于RESTful web接口。Elasticsearch是用Java开发的,开源的企业级搜索引擎。 Elastic官方宣布Elasticsearch进入Version 8…...