折射

应用场景

  1. 水池
  2. 玻璃
  3. 热空气(热扭曲)

GrabPass + CubeMap

《Unity3D Shader入门精要》第十章介绍了,模拟实现折射效果有两种

  1. 使用cubemap: 我认为太麻烦.需要的贴图太多。也没有找到其他合适的使用场景,遂搁置。
  2. 使用GrabPass抓屏: 是一种比较简单也好理解的方式,抓屏,然后对抓到的贴图的坐标进行偏移。

详情可以参考 《Unity3D Shader入门精要》第十章

URP 下的热扭曲(Opaque)

按照以往的把build-in升级到urp渲染管线,只需要改改Tag、变量名字和类型。 谁知道程序一直报错。

URP摒弃了GrabPass, 找到了可以使用 _CameraOpaqueTexture 来获取不透明物体的绘制。

UPR管线设置,勾选 Opaque Texture

有了如下代码:

代码
Shader "URP/DistortionOpaque" 
{
	Properties {
		_NoiseTex ("Noise Texture (RG)", 2D) = "white" {}
		_HeatTime  ("Heat Time", range (0,1)) = 0.1
		_HeatForce  ("Heat Force", range (0,0.1)) = 0.008
	}

	SubShader {
		Tags { "Queue" = "Transparent" "RenderType" = "Transparent" "RenderPipeline"="UniversalPipeline"}
		Blend SrcAlpha OneMinusSrcAlpha
		//AlphaTest Greater .01
		Cull Off 
		Lighting Off 
		//ZTest Off
		ZWrite Off
		Pass {
			Tags { "LightMode" = "UniversalForward"}
			
			HLSLPROGRAM
				#pragma vertex vert
				#pragma fragment frag
				#pragma fragmentoption ARB_precision_hint_fastest

				#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
				#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

				struct appdata_t {
					float4 vertex : POSITION;
					float4 color : COLOR;
					float2 texcoord: TEXCOORD0;
				};

				struct v2f {
					float4 vertex : POSITION;
					float4 uvgrab : TEXCOORD0;
					float2 uvmain : TEXCOORD1;
				};

				float _HeatForce;
				float _HeatTime;
				float4 _NoiseTex_ST;
				sampler2D _NoiseTex;
				SAMPLER(_CameraOpaqueTexture);

				v2f vert (appdata_t v)
				{
					v2f o;
					VertexPositionInputs vertexInput = GetVertexPositionInputs(v.vertex.xyz);
					o.vertex = vertexInput.positionCS;

					#if UNITY_UV_STARTS_AT_TOP
					float scale = -1.0;
					#else
					float scale = 1.0;
					#endif

					o.uvmain = TRANSFORM_TEX( v.texcoord, _NoiseTex);
					return o;
				}

				half4 frag( v2f i ) : SV_Target
				{
					//noise effect
					half4 offsetColor1 = tex2D(_NoiseTex, i.uvmain + _Time.xz*_HeatTime);
					half4 offsetColor2 = tex2D(_NoiseTex, i.uvmain - _Time.yx*_HeatTime);
					half distortX = ((offsetColor1.r + offsetColor2.r) - 1) * _HeatForce;
					half distorty = ((offsetColor1.g + offsetColor2.g) - 1) * _HeatForce;

					half2 screenUV = (i.vertex.xy / _ScreenParams.xy)+ float2(distortX, distorty);

					half4 col = tex2D(_CameraOpaqueTexture, screenUV);
					col.a = 1.0f;

					return col;
				}
			ENDHLSL
		}
	}
}

URP 下的热扭曲(Sprite)

  • _CameraColorTexture   是场景渲染后生成的纹理截图,
  • _CameraOpaqueTexture  是在不透明物体渲染后截图,所以截取不到透明物体。

上面的方法是不支持半透明物体的,但是我现在有个需求是需要支持 Sprite, Sprite是作为一个半透明物体,无法使用上面的方法。

网上解决抓取半透明物体的方法是使用 _AfterPostProcessTexture。 思路如下:

  • 利用RendererFeatures新建一个渲染时机 ,并新建一种LightMode Tags = Grab的类型.
  • 这样所有Tags是Grab的shader都会在后期处理完成之后在渲染。

设置管线

  1. 双击 Renderer List -> 0

  2. 添加 Renderer Feature

  • Name: Render Object 名字
  • Event: AfterRenderingPostProcessing 获取屏幕渲染出的一张图
  • Queue & Layer Mask: 设置渲染所有层和叠加模式
  • LightMode Tags: 设置标签

设置相机

  1. 创建一个子相机,用来获取主相机画面

  2. 子相机设置

  3. 主相机设置

  • 勾选 Post Processing
  • Stack添加子相机

在FrameDebug里我们可以看到俩个摄像机,第二个摄像机就是看到前面渲染效果

设置shader

复制上文中的shader, 然后修改贴图

SAMPLER(_AfterPostProcessTexture);

修改获取贴图纹理

half4 col = tex2D(_AfterPostProcessTexture, screenUV);

做了如上修改后发现Unity中game显示黑屏了。

在Fram Debugger 中, Grab渲染层级下并没有参数 _AfterPostProcessTexture. 反而看到的是 _CameraColorAttachmentA, 在shader代码中相应的位置修改成后_CameraColorAttachmentA,发现能正常显示了。

  • 我看到修改成_SourceTex和_CameraColorAttachmentA都能正常显示。

完整代码如下:

代码
Shader "URP/Distortion" 
{
	Properties {
		_NoiseTex ("Noise Texture (RG)", 2D) = "white" {}
		_HeatTime  ("Heat Time", range (0,1)) = 0.1
		_HeatForce  ("Heat Force", range (0,0.1)) = 0.008
	}

	SubShader {
		Tags { "Queue" = "Transparent" "RenderType" = "Transparent" "RenderPipeline"="UniversalPipeline"}
		Blend SrcAlpha OneMinusSrcAlpha
		//AlphaTest Greater .01
		Cull Off 
		//Lighting Off 
		//ZTest Off
		ZWrite Off
		Pass {
			//Tags { "LightMode" = "UniversalForward"}
			Tags { "LightMode" = "Grab"}
			
			HLSLPROGRAM
				#pragma vertex vert
				#pragma fragment frag
				#pragma fragmentoption ARB_precision_hint_fastest

				#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
				#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

				struct appdata_t {
					float4 vertex : POSITION;
					float4 color : COLOR;
					float2 texcoord: TEXCOORD0;
				};

				struct v2f {
					float4 vertex : POSITION;
					float4 uvgrab : TEXCOORD0;
					float2 uvmain : TEXCOORD1;
				};

				float _HeatForce;
				float _HeatTime;
				float4 _NoiseTex_ST;
				sampler2D _NoiseTex;
				//SAMPLER(_CameraOpaqueTexture);
				//SAMPLER(_AfterPostProcessTexture);
				//SAMPLER(_CameraColorTexture);
				//SAMPLER(_CameraColorAttachmentA);
				SAMPLER(_SourceTex);

				v2f vert (appdata_t v)
				{
					v2f o;
					VertexPositionInputs vertexInput = GetVertexPositionInputs(v.vertex.xyz);
					o.vertex = vertexInput.positionCS;

					#if UNITY_UV_STARTS_AT_TOP
					float scale = -1.0;
					#else
					float scale = 1.0;
					#endif

					o.uvmain = TRANSFORM_TEX( v.texcoord, _NoiseTex);
					return o;
				}

				half4 frag( v2f i ) : SV_Target
				{
					//noise effect
					half4 offsetColor1 = tex2D(_NoiseTex, i.uvmain + _Time.xz*_HeatTime);
					half4 offsetColor2 = tex2D(_NoiseTex, i.uvmain - _Time.yx*_HeatTime);
					half distortX = ((offsetColor1.r + offsetColor2.r) - 1) * _HeatForce;
					half distorty = ((offsetColor1.g + offsetColor2.g) - 1) * _HeatForce;

					half2 screenUV = (i.vertex.xy / _ScreenParams.xy)+ float2(distortX, distorty);

					half4 col = tex2D(_SourceTex, screenUV);
					col.a = 1.0f;

					return col;
				}
			ENDHLSL
		}
	}
}

参考

--完--