先上图
1.水shader
Shader "Game_XXX/whater" {
Properties {
_WaterTex ("Normal Map (RGB), Foam (A)", 2D) = "white" {}
_WaterTex2 ("Normal Map (RGB), Foam (B)", 2D) = "white" {}
_Tiling ("Wave Scale", Range(0.00025, 0.1)) = 0.25
_WaveSpeed("Wave Speed", Float) = 0.4
_SpecularRatio ("Specular Ratio", Range(10,500)) = 200
_BottomColor("Bottom Color",Color) = (0,0,0,0)
_TopColor("Top Color",Color) = (0,0,0,0)
_Alpha("Alpha",Range(0,1)) = 1
_ReflectionTex("_ReflectionTex", 2D) = "black" {}
_ReflectionLight("ReflectionLight",Range(0,1)) = 0.3
_LightColorSelf ("LightColorSelf",Color) = (1,1,1,1)
_LightDir ("LightDir",vector) = (0,1,0,0)
}
SubShader {
Tags {
"Queue"="Transparent-200"
"RenderType"="Transparent"
"IgnoreProjector" = "True"
"LightMode" = "ForwardBase"
}
LOD 250
Pass{
Lighting On
ZWrite On
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex Vert
#pragma fragment Frag
#include "UnityCG.cginc"
float _Tiling;
float _WaveSpeed;
float _SpecularRatio;
sampler2D _WaterTex;
sampler2D _WaterTex2;
sampler2D _ReflectionTex;
float4 _LightColorSelf;
float4 _LightDir;
float4 _BottomColor;
float4 _TopColor;
float _Alpha;
float _ReflectionLight;
struct v2f
{
float4 position : POSITION;
float3 worldPos : TEXCOORD0;
float3 tilingAndOffset:TEXCOORD2;
float4 screen:TEXCOORD3;
float4 VertColor :TEXCOORD4;
};
v2f Vert(appdata_full v)
{
v2f o;
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
o.position = UnityObjectToClipPos(v.vertex);
//uv动画
o.tilingAndOffset.z =frac( _Time.x * _WaveSpeed);
o.tilingAndOffset.xy = o.worldPos.xz*_Tiling;
o.screen = ComputeScreenPos(o.position);
o.VertColor = v.color;
return o;
}
float4 Frag(v2f i):COLOR
{
float3 lightColor=_LightColorSelf.rgb*2;
//世界视向量
float3 worldView = -normalize(i.worldPos - _WorldSpaceCameraPos);
float2 tiling = i.tilingAndOffset.xy;
//法线采样
float4 N1 = tex2D(_WaterTex, tiling.yx +float2(i.tilingAndOffset.z,0));
float4 N2 = tex2D(_WaterTex2, tiling.yx -float2(i.tilingAndOffset.z,0));
//两个法线相加,转世界空间,这里没有unpack,所以法线贴图不需要转normal 法线贴图为0-1 两张加起来为0-2 将其x2-2,转换为-2 --2然后将其normalize,变成-1到1
//在遇到两张法线的情况下 ,一般将法线相加 再normalize
float3 worldNormal = normalize((N1.xyz+N2.xyz)*2-2);
//以垂直的方向代替灯光 跟法线做点积 得到漫反射强度
float LdotN = dot(worldNormal, float3(0,1,0));
fixed2 uv = i.screen.xy/(i.screen.w+0.0001);
uv.y = 1-uv.y;
fixed4 refTex = tex2D (_ReflectionTex,uv + worldNormal.xy*0.02 );
//这个变量一般在Forward渲染路径下使用,存储的是重要的pixel光源方向,没错,的确是使用w来判断这个光源的类型的,一般和_LightColor0配合使用
//float3 LView=_WorldSpaceLightPos0.xyz;
float3 LView = _LightDir.xyz;
//if(_WorldSpaceLightPos0.w == 0.0){
// L = normalize(_WorldSpaceLightPos0.xyz);
// }
// else{
// L = normalize(_WorldSpaceLightPos0.xyz - i.worldPos);
// }
//根据世界法线 ,世界视向量+光向量 得出高光 系数
float dotSpecular = dot(worldNormal, normalize( worldView+LView));
//控制高光的范围
float3 specularReflection = pow(saturate(dotSpecular), _SpecularRatio);
float4 col;
float fresnel = 0.5*LdotN+0.5;
//根据法线的强度 来确定两种颜色之间的混合 ????
col.rgb = lerp(_BottomColor.xyz, _TopColor.xyz, fresnel);
col.rgb = saturate (LdotN) *col.rgb;
//加上高光
col.rgb += specularReflection;
col.rgb = lerp (col.rgb,refTex.rgb*_ReflectionLight,0.7);
//col.rgb +=refTex.rgb*_ReflectionLight;
//加上灯光颜色
col.rgb*=lightColor;
col.rgb *= i.VertColor.rgb;
//控制透明度
col.a =i.VertColor.a * _Alpha;
return col;
}
ENDCG
}
}
FallBack "Diffuse"
}
2.c# 反射
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteInEditMode]
public class ReflectionSelf : MonoBehaviour {
private Transform refObject;
private Camera m_camera;
public LayerMask reflectionMask;
private RenderTexture m_texture;
void Start () {
refObject = this.GetComponent<Transform>();
GameObject refCramera = new GameObject("refCramera");
m_camera = refCramera.AddComponent<Camera>();
m_texture = new RenderTexture(Screen.width,Screen.height,24);
refCameraSet();
}
/// <summary>
/// 相机位置及方向
/// </summary>
void cameraTrasform()
{
//Position x z 与mainCamera相同 y 到平面的距离与 mainCamera到平面的距离相等
Vector3 p_ref;
Vector3 p_main = Camera.main.transform.position;
Vector3 p_plan = this.transform.position;
float y = p_main.y - p_plan.y;
p_ref.x = p_main.x;
p_ref.y = p_plan.y - y;
p_ref.z = p_main.z;
m_camera.transform.position = p_ref;
//Rotation
Vector3 R_ref;
Vector3 R_main = Camera.main.transform.localEulerAngles;
R_ref.x = -R_main.x;
R_ref.y = R_main.y;
R_ref.z = R_main.z;
m_camera.transform.localEulerAngles = R_ref;
}
/// <summary>
/// 反射相机的设置
/// </summary>
void refCameraSet()
{
m_camera.backgroundColor = Color.black;
m_camera.clearFlags = CameraClearFlags.Skybox;
m_camera.cullingMask = reflectionMask; //图层
m_camera.targetTexture = m_texture;
this.GetComponent<Renderer>().sharedMaterial.SetTexture("_ReflectionTex", m_camera.targetTexture);
}
void Update () {
//相机位置要放在这里,因为要随着主相机一直运动
cameraTrasform();
}
}
3.曾经在官方的demo里看到反射相机的写法 比较复杂 在此也贴出来 用这个脚本的话 需要把水的shader里面屏幕UV的y方向变为正方向
using UnityEngine;
using System.Collections.Generic;
//[ExecuteInEditMode]
public class ReflectionFx : MonoBehaviour
{
public Transform[] reflectiveObjects;
public LayerMask reflectionMask;
public Material[] reflectiveMaterials;
private Transform reflectiveSurfaceHeight;
public Shader replacementShader;
private bool highQuality = false;
public Color clearColor = Color.black;
public System.String reflectionSampler = "_ReflectionTex";
public float clipPlaneOffset = 0.07F;
private Vector3 oldpos = Vector3.zero;
//反射相机
private Camera reflectionCamera;
private Dictionary<Camera, bool> helperCameras = null;
private Texture[] initialReflectionTextures;
public void Start()
{
initialReflectionTextures = new Texture2D[reflectiveMaterials.Length];
for (int i = 0; i < reflectiveMaterials.Length; i++)
{
initialReflectionTextures[i] = reflectiveMaterials[i].GetTexture(reflectionSampler);
}
this.enabled = true;
}
public void OnDisable()
{
if (initialReflectionTextures == null)
return;
// restore initial reflection textures
for (int i = 0; i < reflectiveMaterials.Length; i++)
{
reflectiveMaterials[i].SetTexture(reflectionSampler, initialReflectionTextures[i]);
}
}
//创建新相机
private Camera CreateReflectionCameraFor(Camera cam)
{
//将string类初始化,挂脚本的物体名 + “Reflection” + 输入的cam名字
System.String reflName = gameObject.name + "Reflection" + cam.name;
//Debug.Log("AngryBots: created internal reflection camera " + reflName);
//找到这个名字的物体,并将go 实例化
GameObject go = GameObject.Find(reflName);
if (!go) //如果这个物体不存在
go = new GameObject(reflName, typeof(Camera)); //再重新创建一个新的物体,给他赋上名字和类型
if (!go.GetComponent(typeof(Camera))) //这个物体没有Camera组件
go.AddComponent(typeof(Camera));//给它加上Camera组件
Camera reflectCamera = go.GetComponent<Camera>(); //reflectCamera实例化为这个物体(go)的相机组件
reflectCamera.backgroundColor = clearColor; //相机的背景颜色 把背景色设置为clearColor 就不会遮盖住背后的视图
reflectCamera.clearFlags = CameraClearFlags.Skybox;//清楚标记,SolidColor:屏幕上的任何空的部分将显示当前相机的背景颜色
SetStandardCameraParameter(reflectCamera, reflectionMask);//设置渲染图层
if (!reflectCamera.targetTexture) //如果反射相机没有targetTexture
reflectCamera.targetTexture = CreateTextureFor(cam); //用这个方法创建targetTexture赋给相机的targetTexture
return reflectCamera;
}
public void HighQuality()
{
highQuality = true;
}
//设置反射相机的渲染图层
private void SetStandardCameraParameter(Camera cam, LayerMask mask)
{
cam.backgroundColor = Color.black; //背景色为黑色
cam.enabled = true; //cam为false状态?
cam.cullingMask = reflectionMask; //设置渲染图层
}
//给相机赋上targetTexture
private RenderTexture CreateTextureFor(Camera cam)
{
RenderTextureFormat rtFormat = RenderTextureFormat.RGB565;
if (!SystemInfo.SupportsRenderTextureFormat(rtFormat))
rtFormat = RenderTextureFormat.Default;
float rtSizeMul = highQuality ? 0.75f : 0.5f;
RenderTexture rt = new RenderTexture(Mathf.FloorToInt(cam.pixelWidth * rtSizeMul), Mathf.FloorToInt(cam.pixelHeight * rtSizeMul), 24, rtFormat);
rt.hideFlags = HideFlags.DontSave;
return rt;
}
public void RenderHelpCameras(Camera currentCam)
{
if (null == helperCameras)
helperCameras = new Dictionary<Camera, bool>();
if (!helperCameras.ContainsKey(currentCam))
{
helperCameras.Add(currentCam, false);
}
if (helperCameras[currentCam])
{
return;
}
if (!reflectionCamera)
{
reflectionCamera = CreateReflectionCameraFor(currentCam);
foreach (Material m in reflectiveMaterials)
{
m.SetTexture(reflectionSampler, reflectionCamera.targetTexture);
}
}
RenderReflectionFor(currentCam, reflectionCamera);
helperCameras[currentCam] = true;
}
public void LateUpdate()
{
// find the closest reflective surface and use that as our
// reference for reflection height etc.
//找到最接近的反射面并将其作为我们的
//参考高度等。
Transform closest = null;
float closestDist = Mathf.Infinity;
Vector3 pos = Camera.main.transform.position;
foreach (Transform t in reflectiveObjects)
{
if (t.GetComponent<Renderer>().isVisible)
{
float dist = (pos - t.position).sqrMagnitude;
if (dist < closestDist)
{
closestDist = dist;
closest = t;
}
}
}
if (!closest)
return;
ObjectBeingRendered(closest, Camera.main);
if (null != helperCameras)
helperCameras.Clear();
}
private void ObjectBeingRendered(Transform tr, Camera currentCam)
{
if (null == tr)
return;
reflectiveSurfaceHeight = tr;
RenderHelpCameras(currentCam);
}
private void RenderReflectionFor(Camera cam, Camera reflectCamera)
{
if (!reflectCamera)
return;
SaneCameraSettings(reflectCamera);
reflectCamera.backgroundColor = clearColor;
//GL.SetRevertBackfacing(true);
GL.invertCulling = true;
Transform reflectiveSurface = reflectiveSurfaceHeight;
Vector3 eulerA = cam.transform.eulerAngles;
reflectCamera.transform.eulerAngles = new Vector3(-eulerA.x, eulerA.y, eulerA.z);
reflectCamera.transform.position = cam.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 = cam.transform.position;
Vector3 newpos = reflection.MultiplyPoint(oldpos);
reflectCamera.worldToCameraMatrix = cam.worldToCameraMatrix * reflection;
Vector4 clipPlane = CameraSpacePlane(reflectCamera, pos, normal, 1.0f);
Matrix4x4 projection = cam.projectionMatrix;
projection = CalculateObliqueMatrix(projection, clipPlane);
reflectCamera.projectionMatrix = projection;
reflectCamera.transform.position = newpos;
Vector3 euler = cam.transform.eulerAngles;
reflectCamera.transform.eulerAngles = new Vector3(-euler.x, euler.y, euler.z);
reflectCamera.RenderWithShader(replacementShader, "Reflection");
//GL.SetRevertBackfacing(false);
GL.invertCulling = false;
}
private void SaneCameraSettings(Camera helperCam)
{
helperCam.depthTextureMode = DepthTextureMode.None;
helperCam.backgroundColor = Color.black;
helperCam.clearFlags = CameraClearFlags.Skybox;
helperCam.renderingPath = RenderingPath.Forward;
}
static Matrix4x4 CalculateObliqueMatrix(Matrix4x4 projection, Vector4 clipPlane)
{
Vector4 q = projection.inverse * new Vector4(
sgn(clipPlane.x),
sgn(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;
}
// Helper function for getting the reflection matrix that will be multiplied with camera matrix
//用摄像机矩阵来得到反射矩阵的辅助函数
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;
}
// Extended sign: returns -1, 0 or 1 based on sign of a
//扩展符号:返回-1、0或1,基于a的符号
static float sgn(float a)
{
if (a > 0.0F) return 1.0F;
if (a < 0.0F) return -1.0F;
return 0.0F;
}
// Given position/normal of the plane, calculates plane in camera space.
//给定平面的正/正态,在相机空间计算平面
private 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));
}
}