UnityURP 扭曲屏幕效果实现
- 前言
- 项目
- 下载URPGrabPass
- 空间扭曲着色器实现
- 添加可视化控制
- 创建材质球并设置
- 补充
- 粒子使用步骤
- CustomData映射
- 移动设备优化
- 鸣谢
前言
在Unity的Universal Render Pipeline (URP) 中,传统的GrabPass
功能被移除,借助URPGrabPass工具可以实现。
项目
下载URPGrabPass
下载地址:https://github.com/Haruma-K/URPGrabPass
下载完成后解压放入工程
URPGrabPass通过ScriptableRendererFeature机制,在渲染管线中插入自定义的Pass来捕获屏幕颜色纹理。
插件提供两种渲染时机:
AfterOpaques
: 在不透明物体渲染后捕获AfterTransparents
: 在透明物体渲染后捕获插件自动将捕获的屏幕纹理以
_GrabbedTexture
的名称传递给着色器,只需在着色器中声明即可使用:sampler2D _GrabbedTexture;
记得在渲染管线中添加RendererFeature
空间扭曲着色器实现
Shader "Demo/SpatialFracture"
{Properties{_DisplacementTex("Displacement Texture", 2D) = "gray" {}_FractureIntensity("Fracture Intensity", Range(0.0, 2.0)) = 0.5_DistortionStrength("Distortion Strength", Range(0.0, 1.0)) = 0.3_FractureScale("Fracture Scale", Range(0.1, 5.0)) = 1.0_TimeSpeed("Time Speed", Range(0.0, 5.0)) = 1.0_RefractiveIndex("Refractive Index", Range(0.0, 1.0)) = 0.67[Toggle] _DebugDisplacement("Debug Displacement", Float) = 0[Toggle] _UseCustomData("Use Custom Data", Float) = 0}SubShader{Tags{"RenderType" = "Transparent" "RenderPipeline" = "UniversalRenderPipeline""Queue" = "Transparent"}Pass{Tags{"LightMode" = "UseColorTexture"}HLSLPROGRAM#pragma vertex vert#pragma fragment frag#pragma shader_feature _DEBUGDISPLACEMENT_ON#pragma shader_feature _USECUSTOMDATA_ON#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"struct Attributes{float4 positionOS : POSITION;float2 uv : TEXCOORD0;half3 normal : NORMAL;#ifdef _USECUSTOMDATA_ONfloat4 customData : TEXCOORD1;#endif};struct Varyings{float4 positionHCS : SV_POSITION;float2 uv : TEXCOORD0;half2 samplingViewportPos : TEXCOORD1;float3 worldPos : TEXCOORD2;#ifdef _USECUSTOMDATA_ONfloat4 customData : TEXCOORD3;#endif};sampler2D _GrabbedTexture;sampler2D _DisplacementTex;float4 _DisplacementTex_ST;float _FractureIntensity;float _DistortionStrength;float _FractureScale;float _TimeSpeed;float _RefractiveIndex;Varyings vert(Attributes IN){Varyings OUT;OUT.positionHCS = TransformObjectToHClip(IN.positionOS);OUT.uv = TRANSFORM_TEX(IN.uv, _DisplacementTex);OUT.worldPos = TransformObjectToWorld(IN.positionOS);// Calculate base screen positionfloat4 screenPos = ComputeScreenPos(OUT.positionHCS);OUT.samplingViewportPos = screenPos.xy / screenPos.w;#ifdef _USECUSTOMDATA_ONOUT.customData = IN.customData;#endifreturn OUT;}half4 frag(Varyings IN) : SV_Target{float2 uv = IN.uv;float time = _Time.y * _TimeSpeed;// Get parameters from CustomData or use defaults#ifdef _USECUSTOMDATA_ONfloat fractureIntensity = IN.customData.x;float distortionStrength = IN.customData.y;float fractureScale = IN.customData.z;float refractiveIndex = IN.customData.w;#elsefloat fractureIntensity = _FractureIntensity;float distortionStrength = _DistortionStrength;float fractureScale = _FractureScale;float refractiveIndex = _RefractiveIndex;#endif// Sample displacement texture with animationfloat2 animatedUV = uv * fractureScale + float2(time * 0.1, time * 0.05);float4 displacement = tex2D(_DisplacementTex, animatedUV);#ifdef _DEBUGDISPLACEMENT_ONreturn half4(displacement.rgb, 1.0);#endif// Convert displacement to offsetfloat2 totalDisplacement = (displacement.rg - 0.5) * fractureIntensity * distortionStrength;// Apply refractive distortion based on displacementfloat3 viewDir = normalize(IN.worldPos - _WorldSpaceCameraPos.xyz);float displacementIntensity = length(displacement.rg - 0.5);float2 refractiveOffset = viewDir.xy * refractiveIndex * displacementIntensity * 0.05;// Combine distortionsfloat2 finalUV = IN.samplingViewportPos + totalDisplacement + refractiveOffset;// Sample the grabbed texture with distorted coordinateshalf4 grabbedColor = tex2D(_GrabbedTexture, finalUV);return grabbedColor;}ENDHLSL}}
}
添加可视化控制
搭配可视化操作更佳
using UnityEngine;public class SpatialFractureShaderHelper : MonoBehaviour
{[Header("Shader Control")]public Material targetMaterial;[Header("Debug Options 调试模式,只展示置换贴图")]public bool debugDisplacement = false;[Header("使用ParticleSystem传入CustomData")]public bool useCustomData = false;[Header("Manual Parameter Control")][Range(0f, 2f)]public float fractureIntensity = 0.5f;[Range(0f, 1f)]public float distortionStrength = 0.3f;[Range(0.1f, 5f)]public float fractureScale = 1f;[Range(0f, 1f)]public float refractiveIndex = 0.67f;void Update(){if (targetMaterial == null) return;// 设置关键字SetKeyword("_DEBUGDISPLACEMENT_ON", debugDisplacement);SetKeyword("_USECUSTOMDATA_ON", useCustomData);// 如果不使用CustomData,则设置材质参数if (!useCustomData){targetMaterial.SetFloat("_FractureIntensity", fractureIntensity);targetMaterial.SetFloat("_DistortionStrength", distortionStrength);targetMaterial.SetFloat("_FractureScale", fractureScale);targetMaterial.SetFloat("_RefractiveIndex", refractiveIndex);}}void SetKeyword(string keyword, bool enabled){if (enabled)targetMaterial.EnableKeyword(keyword);elsetargetMaterial.DisableKeyword(keyword);}
}
创建材质球并设置
补充
粒子使用步骤
- 在粒子系统中启用"Custom Vertex Streams"
- 添加"Custom1.xyzw"到vertex streams列表
- 在CustomData模块中设置参数值
- 在材质上启用"Use Custom Data"选项
CustomData映射
Custom1.x
→ 破裂强度 (Fracture Intensity)Custom1.y
→ 扭曲强度 (Distortion Strength)Custom1.z
→ 破裂缩放 (Fracture Scale)Custom1.w
→ 折射指数 (Refractive Index)
移动设备优化
#ifdef _MOBILEOPTIMIZATION_ON
// 简化版:直接使用位移作为折射偏移
half2 refractiveOffset = (displacement.rg - 0.5h) * refractiveIndex * 0.05h;
#else
// 完整版:基于视角方向的真实折射
half3 viewDir = normalize(IN.worldPos - _WorldSpaceCameraPos.xyz);
half displacementIntensity = length(displacement.rg - 0.5h);
half2 refractiveOffset = viewDir.xy * refractiveIndex * displacementIntensity * 0.05h;
#endif
鸣谢
URPGrabPass
Claude Code