在线ps手机版网页版seo软文是什么意思
1. 延迟渲染的原理
延迟渲染主要包含了两个Pass。在第一个Pass中,我们不进行任何光照计算,而是仅仅计算哪些片元是可见的,这主要是通过深度缓冲技术来实现,当发现一个片元是可见的,我们就把它的相关信息存储到G缓冲区中。然后,在第二个Pass中,我们利用G缓冲区的各个片元信息,例如表面法线、视角方向、漫反射系数等,进行真正的光照计算。
延迟渲染的过程大致可以用下面的伪代码来描述:
// 第一个 Pass 不进行真正的光照计算
// 仅仅把光照计算需要的信息存储到 G 缓冲中
for (each primitive in this model) {for (each fragment covered by this primitive) {if (failed in depth test) {// 如果没有通过深度测试,说明该片元是不可见的discard;} else {// 如果该片元可见// 就把需要的信息存储到 G 缓冲中writeGBuffer(materialInfo, pos, normal, lightDir, viewDir);}}
}// 利用 G 缓冲中的信息进行真正的光照计算
for (each pixel in the screen) {if (the pixel is valid) {// 如果该像素是有效的// 读取它对应的 G 缓冲中的信息readGBuffer(pixel, materialInfo, pos, normal, lightDir, viewDir);// 根据读取到的信息进行光照计算float4 color = Shading(materialInfo, pos, normal, lightDir, viewDir);// 更新帧缓冲writeFrameBuffer(pixel, color);}
}
Unity 里面的延迟渲染
一、代码结构
Shader "Hidden/Internal-DeferredShading" {Properties {// 光照贴图(用于点光源/聚光灯衰减)_LightTexture0 ("Light Texture", any) = "" {}// 光照方向贴图(用于聚光灯方向衰减)_LightTextureB0 ("Light Direction Texture", 2D) = "" {}// 阴影贴图(来自深度缓冲)_ShadowMapTexture ("Shadow Map", any) = "" {}// 混合模式参数(源/目标混合因子)_SrcBlend ("Source Blend", Float) = 1_DstBlend ("Destination Blend", Float) = 1}SubShader {Pass {// Pass 1: 延迟光照计算(叠加或减法混合)}Pass {// Pass 2: LDR模式最终解码(将对数值转为线性)}Fallback Off // 无备用着色器
}
二、第一个Pass
Pass {ZWrite Off // 关闭深度写入Blend [_SrcBlend] [_DstBlend] // 动态混合模式(HDR时为加法,LDR时为减法)CGPROGRAM#pragma target 3.0 // 要求SM3.0以上#pragma vertex vert_deferred // 使用Unity内置延迟渲染顶点着色器#pragma fragment frag // 片段着色器#pragma multi_compile_lightpass // 多编译光照传递模式#pragma multi_compile ___ UNITY_HDR_ON // 根据是否启用HDR编译不同路径#pragma exclude_renderers nomrt // 排除不支持多渲染目标的平台#include "UnityCG.cginc" // Unity基础CG库#include "UnityDeferredLibrary.cginc" // 延迟渲染专用库#include "UnityPBSLighting.cginc" // 物理渲染光照计算#include "UnityStandardUtils.cginc" // 标准着色器工具#include "UnityGBuffer.cginc" // GBuffer解包#include "UnityStandardBRDF.cginc" // BRDF计算// GBuffer纹理采样器(包含材质信息)sampler2D _CameraGBufferTexture0; // Albedo + 金属度sampler2D _CameraGBufferTexture1; // 法线 + 粗糙度sampler2D _CameraGBufferTexture2; // 前照光 + 漫反射// 计算单个光源的光照贡献half4 CalculateLight (unity_v2f_deferred i) {float3 wpos; // 世界空间坐标float2 uv; // GBuffer纹理坐标float atten, fadeDist;// 光照衰减和雾化距离UnityLight light; // 光照结构体UNITY_INITIALIZE_OUTPUT(UnityLight, light); // 初始化光照结构UnityDeferredCalculateLightParams (i, wpos, uv, light.dir, atten, fadeDist); // 计算光照参数(方向、衰减等)// 设置光照颜色(光源颜色 * 衰减)light.color = _LightColor.rgb * atten;// 从GBuffer解包材质属性 //“Unity Standard Shader 解析(六)之Deferred”里面,可以看到Gbuffer的数据来源half4 gbuffer0 = tex2D(_CameraGBufferTexture0, uv);half4 gbuffer1 = tex2D(_CameraGBufferTexture1, uv);half4 gbuffer2 = tex2D(_CameraGBufferTexture2, uv);UnityStandardData data = UnityStandardDataFromGbuffer(gbuffer0, gbuffer1, gbuffer2); // 转换为标准材质数据结构// 计算视角方向(世界空间)float3 eyeVec = normalize(wpos - _WorldSpaceCameraPos);// 计算非高光部分(1 - 反射率)half oneMinusReflectivity = 1 - SpecularStrength(data.specularColor.rgb);// 环境光(此处设为0,因为延迟渲染通常单独处理)UnityIndirect ind;UNITY_INITIALIZE_OUTPUT(UnityIndirect, ind);ind.diffuse = 0;ind.specular = 0;// 使用标准BRDF计算光照结果return UNITY_BRDF_PBS(data.diffuseColor, // 漫反射颜色data.specularColor, // 高光颜色oneMinusReflectivity, // 非高光部分data.smoothness, // 粗糙度data.normalWorld, // 法线方向-eyeVec, // 视角方向(取反)light, // 光照参数ind // 环境光(未使用));}// 主片段着色器入口#ifdef UNITY_HDR_ONhalf4 frag (unity_v2f_deferred i) : SV_Target {#elsefixed4 frag (unity_v2f_deferred i) : SV_Target {#endifhalf4 c = CalculateLight(i); // 计算光照#ifdef UNITY_HDR_ONreturn c; // HDR模式直接返回线性值#elsereturn exp2(-c); // LDR模式对数编码#endif}ENDCG}
1.顶点着色器
vert_deferred
定义在UnityDeferredLibrary.cginc
里面
// 顶点着色器部分
// 延迟渲染顶点着色器输出结构体
struct unity_v2f_deferred {float4 pos : SV_POSITION; // 裁剪空间顶点坐标float4 uv : TEXCOORD0; // 屏幕空间UV坐标(用于采样GBuffer)float3 ray : TEXCOORD1; // 视线方向向量(用于光照计算)
};// 控制是否使用全屏四边形模式的Shader变量
float _LightAsQuad;// 延迟渲染顶点着色器入口函数
unity_v2f_deferred vert_deferred (float4 vertex : POSITION, float3 normal : NORMAL)
{unity_v2f_deferred o;// 1. 将顶点从物体空间转换为裁剪空间o.pos = UnityObjectToClipPos(vertex);// 2. 计算屏幕空间UV坐标(范围[0,1],保留深度信息)o.uv = ComputeScreenPos(o.pos);// 3. 计算视线方向向量(视图空间下的相机到顶点方向)// UnityObjectToViewPos将顶点转换为视图空间坐标// 乘以float3(-1,-1,1)调整坐标方向(可能与坐标系转换有关)o.ray = UnityObjectToViewPos(vertex) * float3(-1, -1, 1);// 4. 根据_LightAsQuad选择ray的来源:// - 当_LightAsQuad=1时:使用传入的normal参数(实际是相机到近平面角落的向量)// - 当_LightAsQuad=0时:使用顶点计算的rayo.ray = lerp(o.ray, normal, _LightAsQuad);return o;
}
2.UnityDeferredCalculateLightParams
// --------------------------------------------------------
// 核心光照参数计算函数void UnityDeferredCalculateLightParams (unity_v2f_deferred i,out float3 outWorldPos, // 输出世界空间坐标out float2 outUV, // 输出屏幕UV坐标out half3 outLightDir, // 输出光照方向out float outAtten, // 输出光照衰减out float outFadeDist) // 输出阴影衰减距离
{// 1. 计算视线方向(归一化到近裁剪面)i.ray = i.ray * (_ProjectionParams.z / i.ray.z);float2 uv = i.uv.xy / i.uv.w;// 2. 从深度缓冲区重建世界坐标float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, uv);depth = Linear01Depth(depth); // 将深度转换为线性0-1范围float4 vpos = float4(i.ray * depth, 1); // 视图空间坐标float3 wpos = mul(unity_CameraToWorld, vpos).xyz; // 世界空间坐标// 3. 计算阴影衰减距离float fadeDist = UnityComputeShadowFadeDistance(wpos, vpos.z);// 4. 根据光照类型计算光照参数#if defined (SPOT) // 聚光灯float3 tolight = _LightPos.xyz - wpos; // 光源到顶点向量half3 lightDir = normalize(tolight); // 光照方向float4 uvCookie = mul(unity_WorldToLight, float4(wpos,1)); // 光源空间坐标// 聚光灯衰减(带贴图)float atten = tex2Dbias(_LightTexture0, float4(uvCookie.xy / uvCookie.w, 0, -8)).w;atten *= uvCookie.w < 0; // 防止背面采样float att = dot(tolight, tolight) * _LightPos.w; // 距离衰减atten *= tex2D(_LightTextureB0, att.rr).r; // 衰减贴图采样// 计算阴影atten *= UnityDeferredComputeShadow(wpos, fadeDist, uv);#elif defined (DIRECTIONAL) || defined (DIRECTIONAL_COOKIE) // 方向光half3 lightDir = -_LightDir.xyz; // 光方向(反向)float atten = 1.0;// 计算阴影atten *= UnityDeferredComputeShadow(wpos, fadeDist, uv);#if defined (DIRECTIONAL_COOKIE) // 带贴图的方向光// 采样方向光贴图(如天空盒投影)atten *= tex2Dbias(_LightTexture0, float4(mul(unity_WorldToLight, half4(wpos,1)).xy, 0, -8)).w;#endif#elif defined (POINT) || defined (POINT_COOKIE) // 点光源float3 tolight = wpos - _LightPos.xyz; // 顶点到光源向量half3 lightDir = -normalize(tolight); // 光照方向(反向)float att = dot(tolight, tolight) * _LightPos.w; // 距离衰减float atten = tex2D(_LightTextureB0, att.rr).r; // 衰减贴图// 计算阴影atten *= UnityDeferredComputeShadow(tolight, fadeDist, uv);#if defined (POINT_COOKIE) // 带贴图的点光源// 采样立方体阴影贴图atten *= texCUBEbias(_LightTexture0, float4(mul(unity_WorldToLight, half4(wpos,1)).xyz, -8)).w;#endif#else // 无效光照类型half3 lightDir = 0;float atten = 0;#endif// 输出结果outWorldPos = wpos;outUV = uv;outLightDir = lightDir;outAtten = atten;outFadeDist = fadeDist;
}
3,UnityComputeShadowFadeDistance
// 计算阴影衰减距离(用于混合实时阴影和烘焙阴影)
//float3 wpos:顶点的世界空间坐标(通过深度缓冲区重建)。
//float z:顶点的归一化设备坐标(NDC)的Z值(范围 [0,1]),表示顶点到相机的距离。
float UnityComputeShadowFadeDistance(float3 wpos, float z)
{// 1. 计算顶点到阴影衰减中心点的线性距离// unity_ShadowFadeCenterAndType.xyz 是阴影衰减球体的中心点坐标(世界空间)float sphereDist = distance(wpos, unity_ShadowFadeCenterAndType.xyz);// 2. 根据混合因子 lerp 在 z(深度值)和 sphereDist(球体距离)之间插值// unity_ShadowFadeCenterAndType.w 是混合权重(0~1):// - 当为0时,完全使用 z(深度值)作为衰减距离// - 当为1时,完全使用 sphereDist(球体距离)作为衰减距离// - 中间值则线性插值return lerp(z, sphereDist, unity_ShadowFadeCenterAndType.w);
}
4.UnityDeferredComputeShadow
// 计算混合阴影(实时+烘焙)
half UnityDeferredComputeShadow(float3 vec, float fadeDist, float2 uv)
{half fade = UnityComputeShadowFade(fadeDist); // 计算阴影衰减因子half shadowMaskAttenuation = UnityDeferredSampleShadowMask(uv); // 烘焙阴影half realtimeShadowAttenuation = UnityDeferredSampleRealtimeShadow(fade, vec, uv); // 实时阴影// 混合烘焙和实时阴影return UnityMixRealtimeAndBakedShadows(realtimeShadowAttenuation, shadowMaskAttenuation, fade);
}
5.UnityComputeShadowFade
half UnityComputeShadowFade(float fadeDist)
{// 计算衰减因子:将 fadeDist 乘以比例因子(scale),加上偏移因子(offset)// 然后通过 saturate() 将结果限制在 [0, 1] 范围内return saturate(fadeDist * _LightShadowData.z + _LightShadowData.w);
}
6.UnityDeferredSampleShadowMask
// 采样阴影掩码(烘焙阴影)
half UnityDeferredSampleShadowMask(float2 uv)
{half shadowMaskAttenuation = 1.0f;#if defined (SHADOWS_SHADOWMASK)// 从GBuffer的第四通道(_CameraGBufferTexture4)读取阴影掩码half4 shadowMask = tex2D(_CameraGBufferTexture4, uv);// 使用unity_OcclusionMaskSelector计算最终的阴影衰减shadowMaskAttenuation = saturate(dot(shadowMask, unity_OcclusionMaskSelector));#endifreturn shadowMaskAttenuation;
}
7.UnityDeferredSampleRealtimeShadow
// 采样实时阴影(动态阴影)
half UnityDeferredSampleRealtimeShadow(half fade, float3 vec, float2 uv)
{half shadowAttenuation = 1.0f;// 方向光/带贴图的方向光的阴影计算(屏幕空间阴影)#if defined (DIRECTIONAL) || defined (DIRECTIONAL_COOKIE)#if defined(SHADOWS_SCREEN)// 从_ShadowMapTexture采样阴影值shadowAttenuation = tex2D(_ShadowMapTexture, uv).r;#endif#endif// 快速分支优化(Unity 5.6+)#if defined(UNITY_FAST_COHERENT_DYNAMIC_BRANCHING) && defined(SHADOWS_SOFT) && !defined(LIGHTMAP_SHADOW_MIXING)// 当衰减小于阈值时,跳过计算(提升性能)UNITY_BRANCHif (fade < (1.0f - 1e-2f)){#endif// 聚光灯阴影(深度阴影)#if defined(SPOT)#if defined(SHADOWS_DEPTH)// 转换到阴影坐标系并采样float4 shadowCoord = mul(unity_WorldToShadow[0], float4(vec, 1));shadowAttenuation = UnitySampleShadowmap(shadowCoord);#endif#endif// 点光源/带贴图的点光源阴影(立方体贴图阴影)#if defined (POINT) || defined (POINT_COOKIE)#if defined(SHADOWS_CUBE)// 直接采样立方体阴影贴图shadowAttenuation = UnitySampleShadowmap(vec);#endif#endif#if defined(UNITY_FAST_COHERENT_DYNAMIC_BRANCHING) && defined(SHADOWS_SOFT) && !defined(LIGHTMAP_SHADOW_MIXING)}#endifreturn shadowAttenuation;
}
8.UnitySampleShadowmap
// ------------------------------------------------------------------
// Spot 光源阴影采样逻辑
// ------------------------------------------------------------------
// 仅在启用深度阴影(SHADOWS_DEPTH)且光源类型为 Spot(SPOT)时编译此部分
#if defined(SHADOWS_DEPTH) && defined(SPOT)// 阴影采样函数(核心逻辑)inline fixed UnitySampleShadowmap (float4 shadowCoord){#if defined(SHADOWS_SOFT)half shadow = 1; // 默认全亮(无阴影)// 情况1:不支持硬件比较采样器(如部分移动设备或 Xbox360)#if !defined(SHADOWS_NATIVE)float3 coord = shadowCoord.xyz / shadowCoord.w; // 将齐次坐标转换为归一化坐标 [0,1]// 使用四个偏移量进行软阴影采样(PCF)float4 shadowVals;shadowVals.x = SAMPLE_DEPTH_TEXTURE(_ShadowMapTexture, coord + _ShadowOffsets[0].xy);shadowVals.y = SAMPLE_DEPTH_TEXTURE(_ShadowMapTexture, coord + _ShadowOffsets[1].xy);shadowVals.z = SAMPLE_DEPTH_TEXTURE(_ShadowMapTexture, coord + _ShadowOffsets[2].xy);shadowVals.w = SAMPLE_DEPTH_TEXTURE(_ShadowMapTexture, coord + _ShadowOffsets[3].xy);// 比较采样深度与当前坐标深度,计算阴影权重(0为全黑,1为全亮)half4 shadows = (shadowVals < coord.zzzz) ? _LightShadowData.rrrr : 1.0f;shadow = dot(shadows, 0.25f); // 四个采样的平均值#else // 情况2:支持硬件比较采样器(如移动设备)// 使用硬件比较采样器进行高效采样float3 coord = shadowCoord.xyz / shadowCoord.w;half4 shadows;shadows.x = UNITY_SAMPLE_SHADOW(_ShadowMapTexture, coord + _ShadowOffsets[0]);shadows.y = UNITY_SAMPLE_SHADOW(_ShadowMapTexture, coord + _ShadowOffsets[1]);shadows.z = UNITY_SAMPLE_SHADOW(_ShadowMapTexture, coord + _ShadowOffsets[2]);shadows.w = UNITY_SAMPLE_SHADOW(_ShadowMapTexture, coord + _ShadowOffsets[3]);shadow = dot(shadows, 0.25f); // 四个采样的平均值#else // 情况3:其他平台(如PC)// 使用更复杂的3x3采样(PCF3x3)提高阴影质量float3 coord = shadowCoord.xyz / shadowCoord.w;float3 receiverPlaneDepthBias = UnityGetReceiverPlaneDepthBias(coord, 1.0f);shadow = UnitySampleShadowmap_PCF3x3(float4(coord, 1), receiverPlaneDepthBias);#endif// 根据光照阴影强度混合阴影效果shadow = lerp(_LightShadowData.r, 1.0f, shadow);#else // 情况4:硬阴影(非软阴影)// 硬阴影直接采样(1-tap)#if defined(SHADOWS_NATIVE)// 使用硬件比较采样器half shadow = UNITY_SAMPLE_SHADOW_PROJ(_ShadowMapTexture, shadowCoord);shadow = lerp(_LightShadowData.r, 1.0f, shadow);#else// 手动比较深度half shadow = SAMPLE_DEPTH_TEXTURE_PROJ(_ShadowMapTexture, UNITY_PROJ_COORD(shadowCoord)) < (shadowCoord.z / shadowCoord.w) ? _LightShadowData.r : 1.0;#endif#endifreturn shadow;}#endif // #if defined(SHADOWS_DEPTH) && defined(SPOT)
三、第二个Pass
Pass {ZTest Always // 总是通过深度测试Cull Off // 不剔除面ZWrite Off // 关闭深度写入Stencil {// 只渲染非背景区域(通过Stencil缓冲区)ref [_StencilNonBackground] // 参考值来自Unity的Stencil设置readmask [_StencilNonBackground] // 读取掩码compback equal // 后面面的比较函数compfront equal // 前面面的比较函数(修复bug,见注释)}CGPROGRAM#pragma target 3.0#pragma vertex vert#pragma fragment frag#pragma exclude_renderers nomrt#include "UnityCG.cginc"sampler2D _LightBuffer; // 光照缓冲区纹理struct v2f {float4 vertex : SV_POSITION;float2 texcoord : TEXCOORD0;};// 顶点着色器(传递屏幕坐标)v2f vert (float4 vertex : POSITION, float2 texcoord : TEXCOORD0) {v2f o;o.vertex = UnityObjectToClipPos(vertex);o.texcoord = texcoord.xy;#ifdef UNITY_SINGLE_PASS_STEREOo.texcoord = TransformStereoScreenSpaceTex(o.texcoord, 1.0f);#endifreturn o;}// 片段着色器(解码对数值)fixed4 frag (v2f i) : SV_Target {return -log2(tex2D(_LightBuffer, i.texcoord)); // 对数解码}ENDCG}
总结Unity中关于内部延迟渲染(Deferred Shading)的实现,主要包括了原理介绍、代码结构和关键函数的作用。
延迟渲染的原理
延迟渲染通过两个Pass完成:第一个Pass只计算哪些片元是可见的,并将这些片元的相关信息存储到G缓冲区中;第二个Pass利用G缓冲区的信息进行光照计算。这种方法允许在不增加额外复杂度的情况下处理大量光源。
Unity里面的延迟渲染
一、代码结构
- Shader “Hidden/Internal-DeferredShading”:定义了一个隐藏的着色器,用于内部延迟渲染。
- Properties块:定义了各种纹理属性,如光纹理、阴影贴图等。
- SubShader块:包含了具体的渲染过程,分为多个Pass来执行不同的渲染任务。
二、第一个Pass
- ZWrite Off 和 Blend:关闭深度写入并设置混合模式。
- CGPROGRAM块:包含了顶点和片段着色器的实现,使用多编译指令以适应不同平台和配置。
- CalculateLight函数:根据从GBuffer读取的信息计算单个光源对像素颜色的贡献。
- frag函数:主片段着色器入口,根据是否启用高动态范围(HDR)选择不同的返回值处理方式。
关键函数
- vert_deferred:延迟渲染使用的顶点着色器,负责转换坐标系并将必要信息传递给片段着色器。
- UnityDeferredCalculateLightParams:核心光照参数计算函数,根据当前片元的位置和其他信息计算出光照方向、衰减等因素。
- UnityComputeShadowFadeDistance:计算阴影衰减距离,用于混合实时阴影和烘焙阴影。
- UnityDeferredComputeShadow:计算混合阴影效果,结合实时阴影和烘焙阴影。
- UnityComputeShadowFade:计算阴影衰减因子。
- UnityDeferredSampleShadowMask:采样阴影掩码,即烘焙阴影。
- UnityDeferredSampleRealtimeShadow:采样实时阴影,即动态生成的阴影。
- UnitySampleShadowmap:阴影贴图采样逻辑,支持多种情况下的阴影采样,包括软阴影和硬阴影。
第二个Pass
- ZTest Always, Cull Off, ZWrite Off:总是通过深度测试,不剔除面,关闭深度写入。
- Stencil块:仅渲染非背景区域,通过模板缓冲区实现。
- CGPROGRAM块:解码对数值,主要是通过对数运算将HDR颜色值转换为线性空间颜色值。