平面反射

平面反射

GitHub项目地址

平面反射的实现步骤:

1、抓取屏幕图像——渲染纹理RenderTexture+额外摄像机
2、反射矩阵(ReflectMatrix)——获得反射的屏幕图像
3、斜裁剪矩阵(ObliqueMatrix)——裁剪掉反射平面以下的部分
4、高斯模糊——模糊反射的倒影
5、将反射纹理渲染到地面

1、抓取屏幕图像

之前的文章已经提过了,抓取屏幕图像有两种方法:
1、GrabPass
2、渲染纹理RenderTexture+额外摄像机
这里需要使用第二种方法:

private Camera mainCamera, reflectCamera;
private RenderTexture reflectTex;
private Renderer render;
private Material reflectMaterial;
void Start()
{
  mainCamera = Camera.main;
  GameObject go = new GameObject("ReflectCamera", typeof(Camera));
  reflectCamera = go.GetComponent<Camera>();
  reflectCamera.fieldOfView = mainCamera.fieldOfView;
  reflectCamera.aspect = mainCamera.aspect;
  reflectCamera.cullingMask = mainCamera.cullingMask;
  //自己控制渲染的层级
  //reflectCamera.cullingMask = 1 << LayerMask.NameToLayer("Player");
  reflectCamera.enabled = false;

  reflectTex = RenderTexture.GetTemporary(1024, 1024, 24);
  reflectCamera.targetTexture = reflectTex;

  render = GetComponent<Renderer>();
  reflectMaterial = render.sharedMaterial;
}

2、反射矩阵(ReflectMatrix)

顶点反射

反射矩阵

反射矩阵的推导过程https://zhuanlan.zhihu.com/p/92633614

//计算plane平面的反射矩阵
private void CalculateReflectMatrix(ref Matrix4x4 matrix, Vector4 plane)
{
  matrix.m00 = (1F - 2F * plane[0] * plane[0]);
  matrix.m01 = (-2F * plane[0] * plane[1]);
  matrix.m02 = (-2F * plane[0] * plane[2]);
  matrix.m03 = (-2F * plane[3] * plane[0]);

  matrix.m10 = (-2F * plane[1] * plane[0]);
  matrix.m11 = (1F - 2F * plane[1] * plane[1]);
  matrix.m12 = (-2F * plane[1] * plane[2]);
  matrix.m13 = (-2F * plane[3] * plane[1]);

  matrix.m20 = (-2F * plane[2] * plane[0]);
  matrix.m21 = (-2F * plane[2] * plane[1]);
  matrix.m22 = (1F - 2F * plane[2] * plane[2]);
  matrix.m23 = (-2F * plane[3] * plane[2]);
}

3、斜裁剪矩阵(ObliqueMatrix)

需要裁减红色部分

Unity已经为我们提供了获取斜裁减矩阵的方法CalculateObliqueMatrix,我们只需要注意这个方法的参数是视图空间的平面。

//为了反射相机近裁剪平面紧贴我们的反射平面,防止错误渲染
//我们需要重新计算反射相机的投影矩阵,斜裁剪矩阵(ObliqueMatrix)
Vector4 viewPlane = CameraSpacePlane(reflectCamera.worldToCameraMatrix, transform.position, normal);
reflectCamera.projectionMatrix = reflectCamera.CalculateObliqueMatrix(viewPlane);
//计算视图空间的平面
private 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);
}

斜裁剪矩阵的推导过程https://zhuanlan.zhihu.com/p/92633614

4、高斯模糊

高斯模糊我前面的文章已经介绍过了,这里不详细写了。


平面反射+高斯模糊

5、将反射纹理渲染到地面

最后一步,我们需要将获取到的反射纹理渲染到地面。
我们只需要在渲染地面的时候,叠加反射纹理即可。

sampler2D _ReflectTex;
//vert
o.uvReflect = ComputeScreenPos(o.vertex);
//frag
fixed4 reflectCol = tex2Dproj(_ReflectTex, i.uvReflect);