unity 内置渲染管线URP写个平面反射的shader
时间: 2025-04-21 21:13:56 浏览: 29
### 如何在 Unity URP 中创建平面反射 Shader
#### 创建着色器文件
为了实现在Unity Universal Render Pipeline (URP)中的平面反射效果,需要先定义一个自定义的Shader文件。这个Shader应该能够接收来自场景的信息,并基于这些信息计算出正确的反射。
```hlsl
Shader "Custom/PlaneReflection"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_ReflectColor ("Reflection Color", Color) = (1,1,1,0.5)
}
SubShader
{
Tags { "RenderType"="Opaque" }
Pass
{
Name "ForwardLit"
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
struct Attributes
{
float4 positionOS : POSITION;
float3 normalOS : NORMAL;
float2 uv : TEXCOORD0;
};
struct Varyings
{
float4 posHCS : SV_POSITION;
float3 viewDirWS : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float3 normalWS : TEXCOORD2;
float2 uv : TEXCOORD3;
};
sampler2D _CameraReflectionTexture;
float4 _CameraReflectionTexture_ST;
CBUFFER_START(UnityPerMaterial)
float4 _MainTex_TexelSize;
half4 _ReflectColor;
CBUFFER_END
TEXTURE2D(_MainTex);
SAMPLER(sampler_MainTex);
Varyings vert(Attributes input)
{
Varyings output;
VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz);
output.posHCS = vertexInput.positionCS;
output.worldPos = mul(GetObjectToWorldMatrix(), float4(input.positionOS.xyz, 1)).xyz;
output.normalWS = TransformObjectToWorldNormal(input.normalOS);
output.viewDirWS = normalize(WorldSpaceViewDir(output.worldPos));
output.uv = TRANSFORM_TEX(input.uv, _MainTex);
return output;
}
half4 frag(Varyings input) : SV_Target
{
// 计算反射方向
float3 reflectDir = reflect(-input.viewDirWS, input.normalWS);
// 将反射方向转换到屏幕空间坐标系中
float4 clipSpaceReflectedRay = mul(UNITY_MATRIX_VP, float4(reflectDir, 0));
// 获取反射纹理坐标的偏移量
float2 screenUV = input.worldPos.xy * (_ScreenParams.zw - 1.0f) + 0.5f;
float2 reflectionOffset = clipSpaceReflectedRay.xy / clipSpaceReflectedRay.w * 0.5f + 0.5f;
float2 finalUV = screenUV + reflectionOffset;
// 折射处理防止超出边界
finalUV = saturate(finalUV);
// 采样反射贴图并应用颜色混合
half4 col = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, input.uv);
half4 reflCol = SAMPLE_TEXTURE2D(_CameraReflectionTexture, sampler_CameraReflectionTexture, finalUV);
return lerp(col, reflCol * _ReflectColor.a, _ReflectColor.rgb);
}
ENDHLSL
}
}
}
```
上述代码片段展示了如何构建一个基本的平面反射Shader[^1]。注意这里假设已经有一个名为`_CameraReflectionTexture`的纹理作为输入参数传递给Shader程序;这通常通过脚本来完成配置。
#### 编写 C# 脚本以支持反射功能
为了让上面编写的Shader正常工作,还需要编写一段C#脚本来管理摄像机捕捉以及提供必要的数据给Shader使用:
```csharp
using UnityEngine;
using UnityEngine.Rendering.Universal;
public class PlaneReflectionManager : MonoBehaviour
{
public Camera mainCam;
private RenderTexture reflectionRT;
void OnEnable()
{
var volumeStack = VolumeManager.instance.stack;
if (!mainCam.GetComponent<UniversalAdditionalCameraData>().renderingLayerMask.HasFlag(RenderingLayer.Reflection))
{
Debug.LogError("The camera must have the 'Reflection' layer enabled.");
return;
}
CreateReflectionTexture();
}
void Update()
{
if (reflectionRT != null && !reflectionRT.IsCreated())
{
DestroyImmediate(reflectionRT);
CreateReflectionTexture();
}
}
void LateUpdate()
{
if(mainCam == null || reflectionRT == null) return;
Graphics.Blit(null, reflectionRT, mainCam);
}
void CreateReflectionTexture()
{
int resWidth = Screen.width >> 1;
int resHeight = Screen.height >> 1;
reflectionRT = new RenderTexture(resWidth, resHeight, 24, RenderTextureFormat.ARGBHalf);
reflectionRT.enableRandomWrite = true;
reflectionRT.Create();
Shader.SetGlobalTexture("_CameraReflectionTexture", reflectionRT);
}
private void OnDestroy()
{
if (reflectionRT != null)
{
DestroyImmediate(reflectionRT);
}
}
}
```
这段脚本负责初始化和更新用于存储反射图像的`RenderTexture`对象,并将其设置为全局材质属性以便于Shader访问[^2]。
阅读全文
相关推荐

















