首先是C#脚本:
using System;
using UnityEngine;
using UnityEngine.Rendering;
public class PlanarReflection : MonoBehaviour
{
[SerializeField]
Color color = Color.black;
[SerializeField]
Texture mainTex;
[SerializeField]
Vector2 texTiling = Vector2.one;
[SerializeField]
Vector2 texOffset = Vector2.zero;
[SerializeField]
[Range(0f, 1f)]
float reflectionFactor = 0.5f;
Camera m_MainCamera;
Camera reflectionCam;
RenderTexture RT_RenderTarget;
Material mat;
void Start()
{
RT_RenderTarget = new RenderTexture(Screen.width, Screen.height, 24);
RT_RenderTarget.antiAliasing = 8;
RT_RenderTarget.name = "RT_Reflection";
mat = new Material(Shader.Find("Hidden/PlanarReflection"));
mat.SetColor("_Color", color);
if (mainTex) mat.SetTexture("_MainTex", mainTex);
mat.SetTextureScale("_MainTex", texTiling);
mat.SetTextureOffset("_MainTex", texOffset);
mat.SetTexture("_ReflectionTex", RT_RenderTarget);
mat.SetFloat("_ReflectionFactor", reflectionFactor);
MeshRenderer render = GetComponent<MeshRenderer>();
render.material = mat;
m_MainCamera = Camera.main;
GameObject objCamReflection = new GameObject("objCamReflection");
reflectionCam = objCamReflection.AddComponent<Camera>();
reflectionCam.CopyFrom(m_MainCamera);
reflectionCam.cameraType = CameraType.Reflection;
reflectionCam.targetTexture = RT_RenderTarget;
}
void OnEnable()
{
RenderPipelineManager.beginCameraRendering += OnRenderReflection;
}
void OnDisable()
{
RenderPipelineManager.beginCameraRendering -= OnRenderReflection;
}
void OnDestroy()
{
RenderPipelineManager.beginCameraRendering -= OnRenderReflection;
}
void OnRenderReflection(ScriptableRenderContext context, Camera camera)
{
if (camera.cameraType == CameraType.Reflection) RenderReflection();
}
void RenderReflection()
{
Vector3 cameraDirectionWorldSpace = m_MainCamera.transform.forward;
Vector3 cameraUpWorldSpace = m_MainCamera.transform.up;
Vector3 cameraPositionWorldSpace = m_MainCamera.transform.position;
Vector3 cameraDirectionPlaneSpace = transform.InverseTransformDirection(cameraDirectionWorldSpace);
Vector3 cameraUpPlaneSpace = transform.InverseTransformDirection(cameraUpWorldSpace);
Vector3 cameraPositionPlaneSpace = transform.InverseTransformPoint(cameraPositionWorldSpace);
cameraDirectionPlaneSpace.y *= -1f;
cameraUpPlaneSpace.y *= -1f;
cameraPositionPlaneSpace.y *= -1f;
cameraDirectionWorldSpace = transform.TransformDirection(cameraDirectionPlaneSpace);
cameraUpWorldSpace = transform.TransformDirection(cameraUpPlaneSpace);
cameraPositionWorldSpace = transform.TransformPoint(cameraPositionPlaneSpace);
reflectionCam.transform.position = cameraPositionWorldSpace;
reflectionCam.transform.LookAt(cameraPositionWorldSpace + cameraDirectionWorldSpace, cameraUpWorldSpace);
Vector4 viewPlane = CameraSpacePlane(reflectionCam.worldToCameraMatrix, transform.position, transform.up);
reflectionCam.projectionMatrix = reflectionCam.CalculateObliqueMatrix(viewPlane);
}
Vector4 CameraSpacePlane(Matrix4x4 worldToCameraMatrix, Vector3 pos, Vector3 normal)
{
Vector3 viewPos = worldToCameraMatrix.MultiplyPoint3x4(pos);
Vector3 viewNormal = worldToCameraMatrix.MultiplyVector(normal).normalized;
float w = -Vector3.Dot(viewPos, viewNormal);
return new Vector4(viewNormal.x, viewNormal.y, viewNormal.z, w);
}
}
其次是ShaderGraph:
其中自定义用到的两个hlsl文件如下:
void GetCrossSampleUVs_float(float4 UV,float2 TexelSize,float OffsetMultiPlier,out float2 UVOriginal,out float2 UVTopRight,out float2 UVBottomLeft,out float2 UVTopLeft,out float2 UVBottomRight)
{
UVOriginal = UV;
UVTopRight = UV.xy + float2(TexelSize.x,TexelSize.y) * OffsetMultiPlier;
UVBottomLeft = UV.xy - float2(TexelSize.x,TexelSize.y) * OffsetMultiPlier;
UVTopLeft = UV.xy + float2(-TexelSize.x,TexelSize.y) * OffsetMultiPlier;
UVBottomRight = UV.xy + float2(TexelSize.x,-TexelSize.y) * OffsetMultiPlier;
}
void ColorAverage4_float(float3 color01,float3 color02,float3 color03,float3 color04,out float3 color)
{
color = (color01 + color02 + color03 + color04) * 0.25;
}
有时候Unity会报类似这种错误:
Screen position out of view frustum (screen pos 2339.000000, 0.000000, 1000.000000) (Camera rect 0 0 2339 1315)
出现这个错误需要满足连个条件,一个是反射摄像机位置在平面上,另一个是反射摄像机与平面平行。由于反射摄像机的参数来源于Camera.main,所以只要让Camera.main的初始位置和朝向不与反射平面位置重叠的同时方向平行就可以了,这个不是个完美的解决方法,但是暂时管用,以后有时间继续研究。