(1)效果图:(录屏效果惨不忍睹)
(2)思路图:
代码逻辑如下:
注释在代码中
Shader "Water_Code"
{
Properties
{
_WaterNormal ("_WaterNormal", 2D) = "white" {
}
_ReflectionTex("ReflectionTex",2D) = "white"{
}
_NormalTilling("_NormalTilling",float) = 8
_NoiseIntensity("_NoiseIntensity",float) = 1
_WaterSpeed("_WaterSpeed",float) = 6
_WaterNoise("_WaterNoise",float) = 1
_SpecTint("_SpecTint",color) = (1,1,1,1)
_SpecSmoothness("_SpecSmoothness",range(0,1)) = 0.01
_SpecIntensity("_SpecIntensity",float) = 1
_SpecStart("_SpecStart",float) = 200
_SpecEnd("_SpecEnd",float) = 0
_UnderWaterTex("_UnderWaterTex",2D) = "white"{
}
_UnderWaterTilling("_UnderWaterTilling",float) = 4
_WaterDepth("_WaterDepth",float) = 1
}
SubShader
{
Tags {
"RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
// #include "UnityShaderVariables.cginc"
// #include "UnityStandardUtils.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
float4 tangent : TANGENT;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 pos : SV_POSITION;
float3 worldPos : TEXCOORD1;
float3 worldTangent : TEXCOORD2;
float3 worldBinormal : TEXCOORD3;
float3 worldNormal : TEXCOORD4;
float4 screenPos : TEXCOORD5;
};
sampler2D _WaterNormal,_ReflectionTex,_UnderWaterTex;
float _WaterSpeed,_NormalTilling,_NoiseIntensity, _WaterNoise;
float _SpecSmoothness,_SpecIntensity;
float4 _SpecTint;
float _SpecEnd,_SpecStart;
float _UnderWaterTilling,_WaterDepth;
v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldPos = mul(unity_ObjectToWorld,v.vertex);
o.worldNormal = normalize(UnityObjectToWorldNormal(v.normal));
o.worldTangent = normalize(UnityObjectToWorldNormal(v.tangent));
o.worldBinormal =normalize(cross(o.worldTangent,o.worldNormal) * v.tangent.w);
o.screenPos = ComputeScreenPos(o.pos);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
/// 计算出法线相关的数据, 最后采样同一张normalMap 两次 ,混合 得到新的法线
float3 worldPos = i.worldPos;
float2 uv = worldPos.xz / _NormalTilling + (_Time.y * 0.1f * _WaterSpeed);
float2 uv1 = worldPos.xz / _NormalTilling * 1.5f + (_Time.y * 0.1f * _WaterSpeed * -1.0);
//float3 worldNormal = UnpackScaleNormal( tex2D(_WaterNormal,uv),_NoiseIntensity);
float3 worldNormal = UnpackNormal( tex2D(_WaterNormal,uv));
worldNormal.xy *= _NoiseIntensity;
worldNormal.z = sqrt(1.0 - dot(worldNormal.xy,worldNormal.xy));
float3 worldNormal1 = UnpackNormal( tex2D(_WaterNormal,uv1));
worldNormal1 *= _NoiseIntensity;
worldNormal1.z = sqrt(1.0 - dot(worldNormal1.xy,worldNormal1.xy));
worldNormal += worldNormal1;
worldNormal *= 0.5;
worldNormal.z = sqrt(1.0 - dot(worldNormal.xy,worldNormal.xy));
worldNormal = normalize(mul(float3x3(i.worldTangent,i.worldBinormal,i.worldNormal),worldNormal));
用法线扰动 计算反射的效果 后面除以裁剪空间坐标的w: 点到相机的距离; 距离越远 法线的效果越弱, 远处的波动越小
float2 UVOffset_reflectTex = worldNormal.xz / (i.pos.w + 1) * _WaterNoise;
float4 screenPos = i.screenPos;
float4 tempPos = screenPos / screenPos.w;
float4 reflectColor = tex2D( _ReflectionTex, tempPos.xy + UVOffset_reflectTex);
计算高光颜色
float3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos));
float3 worldLightDir = normalize(UnityWorldSpaceLightDir(worldPos));
float3 halfDir = normalize(worldLightDir + worldViewDir);
float ndoth = dot(halfDir,worldNormal);
float specTerm = pow(saturate(ndoth),_SpecSmoothness * 256.0);
float4 specColor = specTerm * _SpecTint * _SpecIntensity;
// 高光的颜色 随着离摄像机的远近 做强度控制 近处强 , 远处弱
float3 worldCameraPos = _WorldSpaceCameraPos;
float dis = distance(worldPos,worldCameraPos);
float specReflectIntensityMask = (_SpecEnd - dis) / (_SpecEnd - _SpecStart);
float specMask = saturate(specReflectIntensityMask);
specColor = specColor * specMask;
float3 tangentViewDir = mul(worldViewDir,float3x3(i.worldTangent,i.worldBinormal,i.worldNormal));
/ 水下的处理
float2 paralaxOffset = ParallaxOffset( 0 , _WaterDepth , tangentViewDir );
float2 uv_UnderWater = worldPos.xz / _UnderWaterTilling + (worldNormal.xy * 0.1) + paralaxOffset;
float4 underWaterColor = tex2D(_UnderWaterTex,uv_UnderWater);
/ 用菲涅尔 控制反射和折射的比率
float fresnal = 1 - saturate(dot(worldNormal,worldViewDir));
float4 refCol = lerp(underWaterColor,reflectColor,fresnal);
float4 endCol = refCol + specColor;
return endCol;
}
ENDCG
}
}
}
PlanarReflection的逻辑,后面加入了模糊处理, 用commandBuffer实现的, 没有用后处理
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
[ExecuteInEditMode]
public class PlanarReflection_16 : MonoBehaviour {
public LayerMask _reflectionMask = -1;
public bool _reflectSkybox = false;
public float _clipPlaneOffset = 0.07F;
//反射图属性名
const string _reflectionTex = "_ReflectionTex";
Camera _reflectionCamera;
Vector3 _oldpos;
RenderTexture _bluredReflectionTexture;
Material _sharedMaterial;
//模糊效果相关参数
public bool _blurOn = true;
[Range(0.0f, 5.0f)]
public float _blurSize = 1;
[Range(0, 10)]
public int _blurIterations = 2;
[Range(1.0f, 4.0f)]
public float _downsample = 1;
//记录上述模糊参数,用于判断参数是否发生变化
private bool _oldBlurOn;
private float _oldBlurSize;
private int _oldBlurIterations;
private float _oldDownsample;
//模糊shader
private Shader _blurShader;
private Material _blurMaterial;
//用来判断当前是否正在渲染反射图
private static bool _insideRendering;
Material BlurMaterial {
get {
if (_blurMaterial == null) {
_blurMaterial = new Material(_blurShader);
return _blurMaterial;
}
return _blurMaterial;
}
}
void Awake() {
_oldBlurOn = _blurOn;
_oldBlurSize = _blurSize;
_oldBlurIterations = _blurIterations;
_oldDownsample = _downsample;
}
void Start() {
_sharedMaterial = GetComponent<MeshRenderer>().sharedMaterial;
_blurShader = Shader.Find("Hidden/KawaseBlur");
if (_blurShader == null)
Debug.LogError("缺少Hidden/KawaseBlur Shader");
}
bool _blurParamChanged;
void Update()
{
if (_blurParamChanged)
{
_oldBlurOn = _blurOn;
_oldBlurSize = _blurSize;
_oldBlurIterations = _blurIterations;
_oldDownsample = _downsample;
}
if (_blurOn != _oldBlurOn || _blurSize != _oldBlurSize || _blurIterations != _oldBlurIterations || _downsample!= _oldDownsample)
{
_blurParamChanged = true;
}
}
//创建反射用的摄像机
Camera CreateReflectionCamera(Camera cam) {
//生成Camera
String reflName = gameObject.name + "Reflection" + cam.name;
GameObject go = new GameObject(reflName);
go.hideFlags = HideFlags.DontSave;
//go.hideFlags = HideFlags.HideAndDontSave;
Camera reflectCamera = go.AddComponent<Camera>();
//设置反射相机的参数
HoldCameraSettings(reflectCamera);
//创建RT并绑定Camera
if (!reflectCamera.targetTexture) {
reflectCamera.targetTexture = CreateTexture(cam);
}
return reflectCamera;
}
//设置反射相机的参数
void HoldCameraSettings(Camera heplerCam)
{
heplerCam.backgroundColor = Color.black;
heplerCam.clearFlags = _reflectSkybox ? CameraClearFlags.Skybox : CameraClearFlags.SolidColor;
heplerCam.renderingPath = RenderingPath.Forward;
heplerCam.cullingMask = _reflectionMask;
heplerCam.allowMSAA = false;
heplerCam.enabled = false;
}
//创建RT
RenderTexture CreateTexture(Camera sourceCam) {
int width = Mathf.RoundToInt(Screen.width / _downsample);
int height = Mathf.RoundToInt(Screen.height / _downsample);
RenderTextureFormat formatRT = sourceCam.allowHDR ? RenderTextureFormat.DefaultHDR : RenderTextureFormat.Default;
RenderTexture rt = new RenderTexture(width, height, 24, formatRT);
rt.hideFlags = HideFlags.DontSave;
return rt;
}
//内置回调函数,物体渲染之前会先调用该函数
void OnWillRenderObject() {
Camera currentCam = Camera.current;
if (currentCam == null) {
return;
}
#if !UNITY_EDITOR
if (!currentCam.gameObject.CompareTag("MainCamera"))
return;
#endif
if (_insideRendering) {
return;
}
_insideRendering = true;
if (_reflectionCamera == null) {
_reflectionCamera = CreateReflectionCamera(currentCam);
}
//渲染反射图
RenderReflection(currentCam, _reflectionCamera);
//是否对反射图进行模糊
if (_reflectionCamera && _sharedMaterial) {
if (_blurOn) {
if (_bluredReflectionTexture == null)
_bluredReflectionTexture = CreateTexture(currentCam);
PostProcessTexture(currentCam, _reflectionCamera.targetTexture, _bluredReflectionTexture);
_sharedMaterial.SetTexture(_reflectionTex, _bluredReflectionTexture);
}
else {
_sharedMaterial.SetTexture(_reflectionTex, _reflectionCamera.targetTexture);
}
}
_insideRendering = false;
}
//调用反射相机,渲染反射图
void RenderReflection(Camera currentCam, Camera reflectCamera) {
if (reflectCamera == null) {
Debug.LogError("反射Camera无效");
return;
}
if (_sharedMaterial && !_sharedMaterial.HasProperty(_reflectionTex))
{
Debug.LogError("Shader中缺少_ReflectionTex属性");
return;
}
//保持反射相机的参数
HoldCameraSettings(reflectCamera);
if (_reflectSkybox) {
if (currentCam.gameObject.GetComponent(typeof(Skybox))) {
Skybox sb = (Skybox)reflectCamera.gameObject.GetComponent(typeof(Skybox));
if (!sb) {
sb = (Skybox)reflectCamera.gameObject.AddComponent(typeof(Skybox));
}
sb.material = ((Skybox)currentCam.GetComponent(typeof(Skybox))).material;
}
}
bool isInvertCulling = GL.invertCulling;
GL.invertCulling = true;
Transform reflectiveSurface = this.transform; //waterHeight;
Vector3 eulerA = currentCam.transform.eulerAngles;
reflectCamera.transform.eulerAngles = new Vector3(-eulerA.x, eulerA.y, eulerA.z);
reflectCamera.transform.position = currentCam.transform.position;
Vector3 pos = reflectiveSurface.transform.position;
pos.y = reflectiveSurface.position.y;
Vector3 normal = reflectiveSurface.transform.up;
float d = -Vector3.Dot(normal, pos) - _clipPlaneOffset;
Vector4 reflectionPlane = new Vector4(normal.x, normal.y, normal.z, d);
Matrix4x4 reflection = Matrix4x4.zero;
reflection = CalculateReflectionMatrix(reflection, reflectionPlane);
_oldpos = currentCam.transform.position;
Vector3 newpos = reflection.MultiplyPoint(_oldpos);
reflectCamera.worldToCameraMatrix = currentCam.worldToCameraMatrix * reflection;
Vector4 clipPlane = CameraSpacePlane(reflectCamera, pos, normal, 1.0f);
Matrix4x4 projection = currentCam.projectionMatrix;
projection = CalculateObliqueMatrix(projection, clipPlane);
reflectCamera.projectionMatrix = projection;
reflectCamera.transform.position = newpos;
Vector3 euler = currentCam.transform.eulerAngles;
reflectCamera.transform.eulerAngles = new Vector3(-euler.x, euler.y, euler.z);
reflectCamera.Render();
GL.invertCulling = isInvertCulling;
}
static Matrix4x4 CalculateObliqueMatrix(Matrix4x4 projection, Vector4 clipPlane) {
Vector4 q = projection.inverse * new Vector4(
Mathf.Sign(clipPlane.x),
Mathf.Sign(clipPlane.y),
1.0F,
1.0F
);
Vector4 c = clipPlane * (2.0F / (Vector4.Dot(clipPlane, q)));
// third row = clip plane - fourth row
projection[2] = c.x - projection[3];
projection[6] = c.y - projection[7];
projection[10] = c.z - projection[11];
projection[14] = c.w - projection[15];
return projection;
}
static Matrix4x4 CalculateReflectionMatrix(Matrix4x4 reflectionMat, Vector4 plane) {
reflectionMat.m00 = (1.0F - 2.0F * plane[0] * plane[0]);
reflectionMat.m01 = (-2.0F * plane[0] * plane[1]);
reflectionMat.m02 = (-2.0F * plane[0] * plane[2]);
reflectionMat.m03 = (-2.0F * plane[3] * plane[0]);
reflectionMat.m10 = (-2.0F * plane[1] * plane[0]);
reflectionMat.m11 = (1.0F - 2.0F * plane[1] * plane[1]);
reflectionMat.m12 = (-2.0F * plane[1] * plane[2]);
reflectionMat.m13 = (-2.0F * plane[3] * plane[1]);
reflectionMat.m20 = (-2.0F * plane[2] * plane[0]);
reflectionMat.m21 = (-2.0F * plane[2] * plane[1]);
reflectionMat.m22 = (1.0F - 2.0F * plane[2] * plane[2]);
reflectionMat.m23 = (-2.0F * plane[3] * plane[2]);
reflectionMat.m30 = 0.0F;
reflectionMat.m31 = 0.0F;
reflectionMat.m32 = 0.0F;
reflectionMat.m33 = 1.0F;
return reflectionMat;
}
Vector4 CameraSpacePlane(Camera cam, Vector3 pos, Vector3 normal, float sideSign) {
Vector3 offsetPos = pos + normal * _clipPlaneOffset;
Matrix4x4 m = cam.worldToCameraMatrix;
Vector3 cpos = m.MultiplyPoint(offsetPos);
Vector3 cnormal = m.MultiplyVector(normal).normalized * sideSign;
return new Vector4(cnormal.x, cnormal.y, cnormal.z, -Vector3.Dot(cpos, cnormal));
}
//对反射图进行图像处理(利用command buffer实现)
private Dictionary<Camera, CommandBuffer> _cameras = new Dictionary<Camera, CommandBuffer>();
void PostProcessTexture(Camera cam, RenderTexture source, RenderTexture dest)
{
//参数有变化需要刷新commandbuffer
if (_blurParamChanged)
{
if (_cameras.ContainsKey(cam))
cam.RemoveCommandBuffer(CameraEvent.BeforeForwardOpaque, _cameras[cam]);
_cameras.Remove(cam);
}
//已经设置了commandbuffer就不用再执行了
if (_cameras.ContainsKey(cam))
return;
CommandBuffer buf = new CommandBuffer();
buf.name = "Blur Reflection Texture";
_cameras[cam] = buf;
float width = source.width;
float height = source.height;
int rtW = Mathf.RoundToInt(width / _downsample);
int rtH = Mathf.RoundToInt(height / _downsample);
int blurredID = Shader.PropertyToID("_Temp1");
int blurredID2 = Shader.PropertyToID("_Temp2");
buf.GetTemporaryRT(blurredID, rtW, rtH, 0, FilterMode.Bilinear, source.format);
buf.GetTemporaryRT(blurredID2, rtW, rtH, 0, FilterMode.Bilinear, source.format);
buf.Blit((Texture)source, blurredID);
for (int i = 0; i < _blurIterations; i++)
{
float iterationOffs = (i * 1.0f);
buf.SetGlobalFloat("_Offset", iterationOffs / _downsample + _blurSize);
buf.Blit(blurredID, blurredID2, BlurMaterial, 0);
buf.Blit(blurredID2, blurredID, BlurMaterial, 0);
}
buf.Blit(blurredID, dest);
buf.ReleaseTemporaryRT(blurredID);
buf.ReleaseTemporaryRT(blurredID2);
cam.AddCommandBuffer(CameraEvent.BeforeForwardOpaque, buf);
}
}