源代码来自Unity
https://github.com/keijiro/AdamPlaneReflection
参考:
Forward Mapped Planar Mirror Reflections
http://ws.iat.sfu.ca/papers/TR98-026.pdf
cs代码:
using UnityEngine;
using UnityEngine.Rendering;
using System.Collections.Generic;
[ExecuteInEditMode]
public class PlaneReflection : MonoBehaviour {
//分辨率
public enum Dimension {
x128 = 128,
x256 = 256,
x512 = 512,
x1024 = 1024,
x2048 = 2048,
x4096 = 4096,
}
//生成反射贴图的shader
[HideInInspector] public Shader convolveShader;
public Dimension reflectionMapSize = Dimension.x1024;
//反射相机绘制的层
public LayerMask reflectLayerMask = ~0;
//偏移
public float clipPlaneOffset = 0.01f;
//是否剔除天空盒
public bool clipSkyDome;
//反射相机nearPlane farPlane
public float nearPlaneDistance = 0.1f;
public float farPlaneDistance = 25f;
//mip级数偏移
public float mipShift;
//是否生成Depth Tex
public bool useDepth = true;
public float depthScale = 1.25f;
public float depthExponent = 2.25f;
public float depthRayPinchFadeSteps = 4f;
//反射相机是否画阴影
public bool renderShadows = false;
public float shadowDistance = 200f;
public int maxPixelLights = -1;
//clear color
public Color clearColor = Color.gray;
public RenderingPath renderingPath = RenderingPath.UsePlayerSettings;
RenderTexture m_reflectionMap;
RenderTexture m_reflectionDepthMap;
//???
CommandBuffer m_copyDepthCB;
//反射相机
Camera m_reflectionCamera;
Camera m_renderCamera;
Material[] m_materials = new Material[0];
Material m_convolveMaterial;
#if UNITY_EDITOR
//编译器中,如果有改动重新编译该类就执行
void OnValidate() {
OnEnable();
UnityEditor.SceneView.RepaintAll();
}
#endif
///
/// 是否支持该shader
///
///
bool CheckSupport() {
bool supported = true;
if(convolveShader && !convolveShader.isSupported)
supported = false;
return supported;
}
void Awake() {
//初始化
if(!convolveShader)
convolveShader = Shader.Find("Hidden/Volund/Convolve");
if(!m_convolveMaterial)
m_convolveMaterial = new Material(convolveShader);
if(CheckSupport()) {
//初始化反射相机
EnsureReflectionCamera(null);
//初始化反射贴图
EnsureReflectionTexture();
//绘制Depth tex,不需要depth可以不走,多一次blit
EnsureResolveDepthHooks();
}
}
void OnEnable() {
m_materials = GetComponent().sharedMaterials;
if(!m_convolveMaterial)
m_convolveMaterial = new Material(convolveShader);
if(useDepth) {
m_convolveMaterial.EnableKeyword("USE_DEPTH");
m_convolveMaterial.SetFloat("_DepthScale", depthScale);
m_convolveMaterial.SetFloat("_DepthExponent", depthExponent);
} else {
m_convolveMaterial.DisableKeyword("USE_DEPTH");
}
m_convolveMaterial.hideFlags = HideFlags.DontSave | HideFlags.NotEditable;
if(CheckSupport())
EnsureReflectionCamera(null);
}
void OnDisable() {
for(int i = 0, n = m_materials.Length; i < n; ++i)
m_materials[i].DisableKeyword("PLANE_REFLECTION");
}
void OnDestroy() {
if(m_reflectionCamera)
Object.DestroyImmediate(m_reflectionCamera.gameObject);
m_reflectionCamera = null;
if(m_copyDepthCB != null) {
m_copyDepthCB.Release();
m_copyDepthCB = null;
}
Object.DestroyImmediate(m_convolveMaterial);
m_convolveMaterial = null;
Object.DestroyImmediate(m_reflectionMap);
Object.DestroyImmediate(m_reflectionDepthMap);
}
//反射相机渲染之前
public void OnWillRenderObject() {
if(!CheckSupport())
return;
//Debug.LogFormat("OnWillRenderObject: {0} from camera {1} (DepthMode: {2})", name, Camera.current.name, Camera.current.depthTextureMode);
if(Camera.current == Camera.main) {
m_renderCamera = Camera.current;
#if UNITY_EDITOR
} else if(UnityEditor.SceneView.currentDrawingSceneView && UnityEditor.SceneView.currentDrawingSceneView.camera == Camera.current) {
m_renderCamera = Camera.current;
#endif
} else {
return;
}
//初始化相机
m_reflectionCamera = EnsureReflectionCamera(m_renderCamera);
//初始化反射贴图
EnsureReflectionTexture();
//绘制Depth tex,不需要depth可以不走,多一次blit
EnsureResolveDepthHooks();
var reflectionMap0 = m_reflectionMap;
// find the reflection plane: position and normal in world space
Vector3 pos = transform.position;
//反射平面的法线方向
Vector3 normal = transform.up;
// Reflect camera around reflection plane
//距离
float d = -Vector3.Dot (normal, pos) - clipPlaneOffset;
//反射面
Vector4 reflectionPlane = new Vector4 (normal.x, normal.y, normal.z, d);
//反射矩阵
Matrix4x4 reflectionMatrix = Matrix4x4.zero;
//计算反射矩阵
CalculateReflectionMatrix(ref reflectionMatrix, reflectionPlane);
//计算反射相机的位置
Vector3 newpos = reflectionMatrix.MultiplyPoint(m_renderCamera.transform.position);
//世界->相机->反射空间
//反射相机绘制的物体也做反射的旋转&位移变换
m_reflectionCamera.worldToCameraMatrix = m_renderCamera.worldToCameraMatrix * reflectionMatrix;
//设置cull mask
m_reflectionCamera.cullingMask = reflectLayerMask;
//设置camera target为反射贴图
m_reflectionCamera.targetTexture = reflectionMap0;
m_reflectionCamera.transform.position = newpos;
m_reflectionCamera.aspect = m_renderCamera.aspect;
// find the reflection plane: position and normal in world space
Vector3 planePos = transform.position;
Vector3 planeNormal = transform.up;
//?为什么又算一遍
float planeDist = -Vector3.Dot(planeNormal, planePos) - clipPlaneOffset;
/*var*/ reflectionPlane = new Vector4(planeNormal.x, planeNormal.y, planeNormal.z, planeDist);
// reflect the camera about the reflection plane
var srcCamPos = m_renderCamera.transform.position;
//var srcCamPos4 = new Vector4(srcCamPos.x, srcCamPos.y, srcCamPos.z, 1f);
var srcCamRgt = m_renderCamera.transform.right;
var srcCamUp = m_renderCamera.transform.up;
var srcCamFwd = m_renderCamera.transform.forward;
//var reflectedPos = srcCamPos - 2f * Vector4.Dot(reflectionPlane, srcCamPos4) * planeNormal;
//计算反射方向
var reflectedDir = -ReflectVector(planeNormal, srcCamFwd);
//反射相机看向反射方向
m_reflectionCamera.transform.rotation = Quaternion.LookRotation(reflectedDir, srcCamUp);
//只有第一帧会走,画Gizmos用
if(m_reflectionCamera && ssnap) {
sup = m_reflectionCamera.transform.up;
spos = m_reflectionCamera.transform.position;
srot = m_reflectionCamera.transform.rotation;
sfov = m_reflectionCamera.fieldOfView;
sfar = m_reflectionCamera.farClipPlane;
snear = m_reflectionCamera.nearClipPlane;
saspect = m_reflectionCamera.aspect;
ssnap = false;
}
// Setup user defined clip plane instead of oblique frustum
Shader.SetGlobalVector("_PlaneReflectionClipPlane", reflectionPlane);
Shader.EnableKeyword("PLANE_REFLECTION_USER_CLIPPLANE");
var oldShadowDist = QualitySettings.shadowDistance;
if(!renderShadows)
QualitySettings.shadowDistance = 0f;
else if(shadowDistance > 0f)
QualitySettings.shadowDistance = shadowDistance;
var oldPixelLights = QualitySettings.pixelLightCount;
if(maxPixelLights != -1)
QualitySettings.pixelLightCount = maxPixelLights;
//反射平面开启shader宏
for(int i = 0, n = m_materials.Length; i < n; ++i)
m_materials[i].DisableKeyword("PLANE_REFLECTION");
GL.invertCulling = true;
//提前渲染反射相机,渲出反射贴图
m_reflectionCamera.Render();
GL.invertCulling = false;
for(int i = 0, n = m_materials.Length; i < n; ++i)
m_materials[i].EnableKeyword("PLANE_REFLECTION");
if(!renderShadows || shadowDistance > 0f)
QualitySettings.shadowDistance = oldShadowDist;
if(maxPixelLights != -1)
QualitySettings.pixelLightCount = oldPixelLights;
Shader.DisableKeyword("PLANE_REFLECTION_USER_CLIPPLANE");
SetupConvolveParams(srcCamPos, srcCamRgt, srcCamUp, srcCamFwd, reflectionMatrix, planeNormal);
//计算mipmap
Convolve(m_reflectionCamera.targetTexture, m_reflectionDepthMap);
m_reflectionCamera.targetTexture = null;
float mipCount = Mathf.Max(0f, Mathf.Round(Mathf.Log ((float)m_reflectionMap.width, 2f)) - mipShift);
for(int i = 0, n = m_materials.Length; i < n; ++i) {
var m = m_materials[i];
m.SetFloat("_PlaneReflectionLodSteps", mipCount);
m.SetTexture("_PlaneReflection", m_reflectionMap);
}
}
void EnsureReflectionTexture() {
//如果反射贴图分辨率改变了||没有反射贴图,需要创建新的反射贴图
var expectedSize = (int)reflectionMapSize;
if(m_reflectionMap == null || m_reflectionMap.width != expectedSize || ((m_reflectionMap.depth == 0) == (m_reflectionCamera.actualRenderingPath == RenderingPath.Forward))) {
Object.DestroyImmediate(m_reflectionMap);
Object.DestroyImmediate(m_reflectionDepthMap);
m_reflectionMap = new RenderTexture(expectedSize, expectedSize, 16, RenderTextureFormat.ARGBHalf, RenderTextureReadWrite.Linear);
m_reflectionMap.name = "PlaneReflection Full Color";
m_reflectionMap.useMipMap = true;
m_reflectionMap.autoGenerateMips = false;
//因为有mipmap所以Trilinear,不用mipmap可以bilinear
m_reflectionMap.filterMode = FilterMode.Trilinear;
m_reflectionMap.hideFlags = HideFlags.DontSave | HideFlags.NotEditable;
m_reflectionDepthMap = new RenderTexture(expectedSize, expectedSize, 0, RenderTextureFormat.RHalf, RenderTextureReadWrite.Linear);
m_reflectionDepthMap.name = "PlaneReflection Full Depth";
m_reflectionDepthMap.useMipMap = false;
m_reflectionDepthMap.hideFlags = HideFlags.DontSave | HideFlags.NotEditable;
}
}
void EnsureResolveDepthHooks() {
if(m_copyDepthCB == null) {
m_copyDepthCB = new CommandBuffer();
m_copyDepthCB.name = "CopyResolveReflectionDepth";
//走pass2画一张depth texture
m_copyDepthCB.Blit(
new RenderTargetIdentifier(BuiltinRenderTextureType.None),
new RenderTargetIdentifier(m_reflectionDepthMap),
m_convolveMaterial,
2
);
}
if(m_reflectionCamera.commandBufferCount == 0)
m_reflectionCamera.AddCommandBuffer(CameraEvent.AfterEverything, m_copyDepthCB);
}
void SetupConvolveParams(Vector3 camPos, Vector3 camRgt, Vector3 camUp, Vector3 camFwd, Matrix4x4 reflectionMatrix, Vector3 planeNormal) {
camPos = reflectionMatrix.MultiplyPoint(camPos);
camRgt = -ReflectVector(camRgt, planeNormal);
camUp = -ReflectVector(camUp, planeNormal);
camFwd = -ReflectVector(camFwd, planeNormal);
var camNear = m_reflectionCamera.nearClipPlane;
var camFar = m_reflectionCamera.farClipPlane;
var camFov = m_reflectionCamera.fieldOfView;
var camAspect = m_reflectionCamera.aspect;
var frustumCorners = Matrix4x4.identity;
var fovWHalf = camFov * 0.5f;
var tanFov = Mathf.Tan(fovWHalf * Mathf.Deg2Rad);
var toRight = camRgt * camNear * tanFov * camAspect;
var toTop = camUp * camNear * tanFov;
var topLeft = (camFwd * camNear - toRight + toTop);
var camScale = topLeft.magnitude * camFar / camNear;
topLeft.Normalize();
topLeft *= camScale;
Vector3 topRight = camFwd * camNear + toRight + toTop;
topRight.Normalize();
topRight *= camScale;
Vector3 bottomRight = camFwd * camNear + toRight - toTop;
bottomRight.Normalize();
bottomRight *= camScale;
Vector3 bottomLeft = camFwd * camNear - toRight - toTop;
bottomLeft.Normalize();
bottomLeft *= camScale;
frustumCorners.SetRow(0, topLeft);
frustumCorners.SetRow(1, topRight);
frustumCorners.SetRow(2, bottomRight);
frustumCorners.SetRow(3, bottomLeft);
Vector4 camPos4 = new Vector4(camPos.x, camPos.y, camPos.z, 1f);
m_convolveMaterial.SetMatrix("_FrustumCornersWS", frustumCorners);
m_convolveMaterial.SetVector("_CameraWS", camPos4);
var zparams = Vector4.zero;
zparams.y = farPlaneDistance / nearPlaneDistance;
zparams.x = 1f - zparams.y;
zparams.z = zparams.x / farPlaneDistance;
zparams.z = zparams.y / farPlaneDistance;
#if UNITY_5_5_OR_NEWER
//requires version>5.5b10:
if(SystemInfo.usesReversedZBuffer)
{
zparams.y += zparams.x;
zparams.x = -zparams.x;
zparams.w += zparams.z;
zparams.z = -zparams.z;
}
#endif
m_convolveMaterial.SetVector("_PlaneReflectionZParams", zparams);
}
void Convolve(RenderTexture reflectionMap0, RenderTexture reflectionDepth) {
// The simplest and most naive texture convolve the world ever saw. It sorta
// gets the job done, though.
var oldRT = RenderTexture.active;
m_convolveMaterial.SetTexture("_CameraDepthTextureCopy", reflectionDepth);
//生成mipmap
for(int i = 0, n = m_reflectionMap.width; (n >> i) > 1; ++i)
ConvolveStep(i, m_reflectionMap, i, i+1);
RenderTexture.active = oldRT;
}
void ConvolveStep(int step, RenderTexture srcMap, int srcMip, int dstMip) {
var srcSize = m_reflectionMap.width >> srcMip;
var tmp = RenderTexture.GetTemporary(srcSize, srcSize, 0, RenderTextureFormat.ARGBHalf, RenderTextureReadWrite.Linear);
tmp.name = "PlaneReflection Half";
var power = 2048 >> dstMip;
m_convolveMaterial.SetFloat("_CosPower", (float)power / 1000f);
m_convolveMaterial.SetFloat("_SampleMip", (float)srcMip);
m_convolveMaterial.SetFloat("_RayPinchInfluence", Mathf.Clamp01((float)step / depthRayPinchFadeSteps));
Graphics.SetRenderTarget(tmp, 0);
CustomGraphicsBlit(srcMap, m_convolveMaterial, 0);
m_convolveMaterial.SetFloat("_SampleMip", 0f);
Graphics.SetRenderTarget(m_reflectionMap, dstMip);
CustomGraphicsBlit(tmp, m_convolveMaterial, 1);
RenderTexture.ReleaseTemporary(tmp);
}
static void CustomGraphicsBlit(RenderTexture src, Material mat, int pass) {
mat.SetTexture("_MainTex", src);
GL.PushMatrix();
GL.LoadOrtho();
mat.SetPass(pass);
GL.Begin(GL.QUADS);
GL.MultiTexCoord2(0, 0.0f, 0.0f);
GL.Vertex3(0.0f, 0.0f, 3.0f); // BL
GL.MultiTexCoord2(0, 1.0f, 0.0f);
GL.Vertex3(1.0f, 0.0f, 2.0f); // BR
GL.MultiTexCoord2(0, 1.0f, 1.0f);
GL.Vertex3(1.0f, 1.0f, 1.0f); // TR
GL.MultiTexCoord2(0, 0.0f, 1.0f);
GL.Vertex3(0.0f, 1.0f, 0.0f); // TL
GL.End();
GL.PopMatrix();
}
void OnRenderObject() {
if(!CheckSupport())
return;
//Debug.LogFormat("OnRenderObject: {0} from camera {1} (self rendercam: {2})", name, Camera.current.name, m_renderCamera);
if(Camera.current != m_renderCamera) {
} else {
m_renderCamera = null;
}
}
///
/// 初始化反射相机
///
Camera EnsureReflectionCamera(Camera renderCamera) {
if(!m_reflectionCamera) {
//没有相机就生成相机
var goCam = new GameObject(string.Format("#> _Planar Reflection Camera < ({0})", name));
goCam.hideFlags = HideFlags.DontSave | HideFlags.NotEditable | HideFlags.HideInHierarchy;
m_reflectionCamera = goCam.AddComponent();
m_reflectionCamera.enabled = false;
}
if(renderCamera) {
//copy当前渲染相机参数到反射相机
m_reflectionCamera.CopyFrom(renderCamera);
//重设一些参数
// Undo some thing we don't want copied.
m_reflectionCamera.ResetProjectionMatrix(); // definitely don't want to inherit an explicit projection matrix
m_reflectionCamera.renderingPath = renderingPath == RenderingPath.UsePlayerSettings ? m_renderCamera.actualRenderingPath : renderingPath;
m_reflectionCamera.allowHDR = renderCamera.allowHDR;
m_reflectionCamera.rect = new Rect(0f, 0f, 1f, 1f);
} else {
m_reflectionCamera.renderingPath = renderingPath;
}
//设置参数
m_reflectionCamera.backgroundColor = clearColor;
m_reflectionCamera.clearFlags = CameraClearFlags.SolidColor;
m_reflectionCamera.depthTextureMode = useDepth ? DepthTextureMode.Depth : DepthTextureMode.None;
m_reflectionCamera.useOcclusionCulling = false;
m_reflectionCamera.nearClipPlane = nearPlaneDistance;
m_reflectionCamera.farClipPlane = farPlaneDistance + nearPlaneDistance;
return m_reflectionCamera;
}
static Vector3 ReflectVector(Vector3 vec, Vector3 normal) {
return 2f * Vector3.Dot(normal, vec) * normal - vec;
}
static void CalculateReflectionMatrix(ref Matrix4x4 reflectionMat, Vector4 plane) {
reflectionMat.m00 = (1F - 2F*plane[0]*plane[0]);
reflectionMat.m01 = ( - 2F*plane[0]*plane[1]);
reflectionMat.m02 = ( - 2F*plane[0]*plane[2]);
reflectionMat.m03 = ( - 2F*plane[3]*plane[0]);
reflectionMat.m10 = ( - 2F*plane[1]*plane[0]);
reflectionMat.m11 = (1F - 2F*plane[1]*plane[1]);
reflectionMat.m12 = ( - 2F*plane[1]*plane[2]);
reflectionMat.m13 = ( - 2F*plane[3]*plane[1]);
reflectionMat.m20 = ( - 2F*plane[2]*plane[0]);
reflectionMat.m21 = ( - 2F*plane[2]*plane[1]);
reflectionMat.m22 = (1F - 2F*plane[2]*plane[2]);
reflectionMat.m23 = ( - 2F*plane[3]*plane[2]);
reflectionMat.m30 = 0F;
reflectionMat.m31 = 0F;
reflectionMat.m32 = 0F;
reflectionMat.m33 = 1F;
}
public bool ssnap;
Vector3 spos, sup; Quaternion srot;
float sfov, snear, sfar, saspect;
void OnDrawGizmos() {
Gizmos.color = Color.red;
var s = transform.rotation * new Vector3(0.15f, 0.05f, 0.1f);
s.Set(Mathf.Abs(s.x), Mathf.Abs(s.y), s.z = Mathf.Abs(s.z));
Gizmos.DrawCube(transform.position, s);
Gizmos.DrawSphere(transform.position + transform.up * 0.025f, 0.05f);
if(sfov != 0f && snear != 0f && sfar != 0f) {
Gizmos.DrawLine(spos, spos + sup * 0.5f);
Gizmos.matrix = Matrix4x4.TRS(spos, srot, Vector3.one);
Gizmos.DrawFrustum(Vector3.zero, sfov, sfar, snear, saspect);
}
}
}
计算mipmap部分shader:
Shader "Hidden/Volund/Convolve" {
Properties {
_MainTex("Diffuse", 2D) = "white" {}
_DepthScale("DepthScale", Float) = 1.0
_DepthExponent("DepthExponent", Float) = 1.0
}
CGINCLUDE
#pragma only_renderers d3d11
#pragma target 3.0
#include "UnityCG.cginc"
uniform sampler2D _MainTex;
uniform float4 _MainTex_TexelSize;
uniform sampler2D _CameraDepthTexture;
uniform sampler2D _CameraDepthTextureCopy;
uniform float4x4 _FrustumCornersWS;
uniform float4 _CameraWS;
uniform float _DepthScale;
uniform float _DepthExponent;
uniform float _SampleMip;
uniform float _CosPower;
uniform float _RayPinchInfluence;
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float4 interpolatedRay : TEXCOORD1;
float3 interpolatedRayN : TEXCOORD2;
};
v2f vert(appdata_img v) {
v2f o;
half index = v.vertex.z;
v.vertex.z = 0.1;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
o.interpolatedRay = _FrustumCornersWS[(int)index];
o.interpolatedRay.w = index;
o.interpolatedRayN = normalize(o.interpolatedRay.xyz);
return o;
}
uniform float4 _PlaneReflectionClipPlane;
uniform float4 _PlaneReflectionZParams;
float4 frag(v2f i, const float2 dir) {
float4 baseUV;
baseUV.xy = i.uv.xy;
baseUV.z = 0;
baseUV.w = _SampleMip;
#if USE_DEPTH
// float rawDepth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv);
// float depth = 1.f / (rawDepth * _PlaneReflectionZParams.x + _PlaneReflectionZParams.y);
float depth = tex2D(_CameraDepthTextureCopy, i.uv);
float3 wsDir = depth * i.interpolatedRay.xyz;
float4 wsPos = float4(_CameraWS.xyz + wsDir, 1.f);
float pointToPlaneDist = dot(wsPos, _PlaneReflectionClipPlane) / dot(_PlaneReflectionClipPlane.xyz, normalize(i.interpolatedRayN));
float sampleScale1 = saturate(pow(saturate(pointToPlaneDist * _DepthScale), _DepthExponent));
sampleScale1 = max(_RayPinchInfluence, sampleScale1);
#else
float sampleScale1 = 1.f;
#endif
float2 sampleScale = dir * _MainTex_TexelSize.xy * sampleScale1;
float weight = 0.f;
float4 color = 0.f;
float4 uv = baseUV;
for(int i = -32; i <= 32; i += 2) {
float2 off = i * sampleScale;
uv.xy = baseUV.xy + off;
// Kill any samples falling outside of the screen.
// Otherwise, as a bright source pixel touches the edge of the screen, it suddenly
// gets exploded by clamping to have the width/height equal to kernel's radius
// and introduces that much more energy to the result.
if (any(uv.xy < 0.0) || any(uv.xy > 1.0))
continue;
float4 s = tex2Dlod(_MainTex, uv);
float c = clamp(i / 20.f, -1.57f, 1.57f);
float w = pow(max(0.f, cos(c)), _CosPower);
color.rgb += s.rgb * w;
weight += w;
}
return color.rgbb / weight;
}
float4 fragH(v2f i) : COLOR { return frag(i, float2(1.f, 0.f)); }
float4 fragV(v2f i) : COLOR { return frag(i, float2(0.f, 1.f)); }
float fragResolve(v2f i) : SV_Target{
float rawDepth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv);
float depth = 1.f / (rawDepth * _PlaneReflectionZParams.x + _PlaneReflectionZParams.y);
return depth;
}
ENDCG
SubShader {
Cull Off ZTest Always ZWrite Off
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment fragH
#pragma multi_compile _ USE_DEPTH
#pragma multi_compile _ CP0 CP1 CP2 CP3
ENDCG
}
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment fragV
#pragma multi_compile _ USE_DEPTH
#pragma multi_compile _ CP0 CP1 CP2 CP3
ENDCG
}
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment fragResolve
ENDCG
}
}
}
材质shader,读取反射贴图部分:
float mip = pow(1.f - s.oneMinusRoughness, 3.0 / 4.0) * _PlaneReflectionLodSteps;
//裁剪空间位置/屏幕宽高
float2 vpos = i.pos / _ScreenParams.xy;
#ifdef _NORMALMAP
half3 tanNormal = NormalInTangentSpace(i.tex);
vpos.xy += clamp(tanNormal.xy * _PlaneReflectionBumpScale /* * float2(-1.f, -1.f)*/, -_PlaneReflectionBumpClamp, _PlaneReflectionBumpClamp);
#endif
float4 lookup = float4(vpos.x, vpos.y, 0.f, mip);
float4 hdrRefl = tex2Dlod(_PlaneReflection, lookup);
gi.indirect.specular = hdrRefl.rgb * _PlaneReflectionIntensityScale * occlusion;
------Add wolf96 2019/4/1
解决水面下反射问题
利用倾斜裁剪
Vector4 clipPlane = reflectionPlane;
//切换到反射相机空间
clipPlane = m_reflectionCamera.worldToCameraMatrix.inverse.transpose * clipPlane;
Matrix4x4 projectMatrix = m_reflectionCamera.projectionMatrix;
Vector4 q;
q.x = (Mathf.Sign(clipPlane.x) + projectMatrix.m02) / projectMatrix.m00;
q.y = (Mathf.Sign(clipPlane.y) + projectMatrix.m12) / projectMatrix.m11;
q.z = -1;
q.w = (1 + projectMatrix.m22) / projectMatrix.m23;
Vector4 c = clipPlane * (2 / Vector4.Dot(clipPlane, q));
projectMatrix.m20 = c.x;
projectMatrix.m21 = c.y;
projectMatrix.m22 = c.z + 1;
projectMatrix.m23 = c.w;
m_reflectionCamera.projectionMatrix = projectMatrix;
GL.invertCulling = true;
m_reflectionCamera.Render();
GL.invertCulling = false;
参考:3D游戏与计算机图形学中的数学方法 5.6镜像与倾斜裁剪
------Add wolf96 2019/4/25
用斜切裁剪导致farplane也变斜,导致远处剔除的距离变远,画的更多了,可以设置Camera cullingDistance
反射相机的Camera Target和屏幕比例一样比较好,水面上面其实没有画东西