总结一下最近学习的东西
GrabPass{}获取水下的贴图 采样添加法线的扰乱
水体密度以水深参考,越深颜色越重
写一个脚本用于获取反射贴图,采样添加法线的扰乱
脚本的主要内容为:
1、获取主相机和水平面,并生成一个和主相机配置相同的反射相机
2、根据主相机位置和水平面得到反射矩阵,计算得到反射相机的位置
3、修改反射相机的世界-相机矩阵
4、修改反射相机的投影矩阵,使其近平面为水平面
5、将结果渲染进贴图
参考:https://catlikecoding.com/unity/tutorials/flow/directional-flow/
1、采样流向纹理,获得该点的流向和流动强度
2、根据流向旋转uv 旋转矩阵如下:float2x2(dir.y, -dir.x, dir.x, dir.y)
3、对采样得到的导数(用于表示法线)也进行相应的旋转
4、各点的流向不连续,导致画面破碎,修改为块状采样流向 一块块的按流向旋转
//掩盖网状感
5、 blend cell :每个cell和右、上、右上的cell以一定权重混合
6、cell采样点移至中心 shift
7、抹除越界采样的线条 uv突然从1调至0导致 方法:当一个cell到边界时权值变为0,用另一个cell掩盖 offset
8、blend graid:以一定偏移(0.25)再采样一次整体,两者混合
1、曲面细分增加顶点
2、Gerstner 波修改顶点
https://catlikecoding.com/unity/tutorials/flow/waves/
深度贴图-自身深度=水深
按水深过渡
获取反射贴图的脚本
[ExecuteInEditMode]
[RequireComponent(typeof(MeshRenderer))]
public class ToolForWater : MonoBehaviour
{
#region 提取反射贴图
public enum TexSize
{
_128 = 128,
_256 = 256,
_512 = 512,
_1024 = 1024
};
public TexSize TextureSize = TexSize._512;
public int PlaneOffset = 0;
public LayerMask ReflecetLayer=-1;
private TexSize oldTexSize;
private RenderTexture _reflectionTexture;
private Camera _mainCamera;
private Camera _reflCamera;
private static bool s_InsideRendering = false;
void Start()
{
_mainCamera = Camera.main;
}
//物体被渲染时会调用该方法
void OnWillRenderObject()
{
if (!enabled || !GetComponent() || !GetComponent().sharedMaterial || !GetComponent().enabled||!_mainCamera)
return;
if (s_InsideRendering) return;
s_InsideRendering = true;//反射相机也可能会渲染到这个水面从而无限递归,通过这个布尔值来确保只会在主相机渲染时调用一次
CreateReflectCamAndTex(_mainCamera);
CloneCameraModes(_mainCamera,_reflCamera);
//获取反射平面
Vector3 pos = transform.position;
Vector3 normal = transform.up;
float D = -Vector3.Dot(pos, normal) - PlaneOffset;
Vector4 reflectPlane=new Vector4(normal.x,normal.y,normal.z,D);
//计算反射矩阵
Matrix4x4 reflection = CalculateReflectionMatrixByPlane(reflectPlane);
Vector3 MainCamPos = _mainCamera.transform.position;
Vector3 ReflCamPos = reflection.MultiplyPoint(MainCamPos);
//设置反射相机的世界-相机矩阵
_reflCamera.worldToCameraMatrix = _mainCamera.worldToCameraMatrix * reflection;
//计算剪裁空间的反射平面
Vector4 clipPlane = CameraSpacePlane(_reflCamera, pos, normal, PlaneOffset);
//设置反射相机的投影矩阵
Matrix4x4 projection=_mainCamera.projectionMatrix;
projection = CalculateProjectionBasePlane(projection, clipPlane);
_reflCamera.projectionMatrix = projection;
//避免渲染到layer=water这一层的物体 避免反射贴图渲染水体本身
_reflCamera.cullingMask = ~(1 << 4) & ReflecetLayer.value;
//渲染反射贴图
_reflCamera.targetTexture = _reflectionTexture;
//反射相机用的是左手坐标系,而常规的相机用的左手坐标系,而且很多计算也反了
GL.invertCulling = true;
_reflCamera.transform.position = ReflCamPos;
Vector3 eular = _mainCamera.transform.eulerAngles;
_reflCamera.transform.eulerAngles=new Vector3(0,eular.y,eular.z);
_reflCamera.Render();
_reflCamera.transform.position=MainCamPos;
GL.invertCulling = false;
//传递反射贴图给需要的材质
Material[] materials = GetComponent().sharedMaterials;
foreach (var m in materials)
{
if (m.HasProperty("_ReflectionTexture"))
{
m.SetTexture("_ReflectionTexture",_reflectionTexture);
}
}
s_InsideRendering = false;
}
//关闭时清除
void OnDisable()
{
if (_reflectionTexture)
{
DestroyImmediate(_reflectionTexture);
_reflectionTexture = null;
}
if (_reflCamera)
{
DestroyImmediate(_reflCamera.gameObject);
_reflCamera = null;
}
}
public void CreateReflectCamAndTex(Camera sourceCam)
{
if (!_reflectionTexture || oldTexSize != TextureSize)
{
if(!_reflectionTexture) DestroyImmediate(_reflectionTexture);
_reflectionTexture=new RenderTexture((int)TextureSize,(int)TextureSize,0);
_reflectionTexture.name = "_ReflectionTex" + GetInstanceID();
_reflectionTexture.isPowerOfTwo = true;
_reflectionTexture.hideFlags = HideFlags.DontSave;
_reflectionTexture.antiAliasing = 4;
_reflectionTexture.anisoLevel = 0;
oldTexSize = TextureSize;
}
if (!_reflCamera)
{
GameObject go=new GameObject("Reflection Camera id" + GetInstanceID() + " for " + _mainCamera.GetInstanceID(), typeof(Camera), typeof(Skybox));
_reflCamera = go.GetComponent();
_reflCamera.enabled = false;
_reflCamera.transform.position = transform.position;
_reflCamera.transform.rotation = transform.rotation;
_reflCamera.GetComponent();
go.hideFlags = HideFlags.HideAndDontSave;
}
}
public static void CloneCameraModes(Camera src, Camera dest)
{
if (dest == null)
return;
dest.clearFlags = src.clearFlags;
dest.backgroundColor = src.backgroundColor;
if (src.clearFlags == CameraClearFlags.Skybox)
{
Skybox sky = src.GetComponent(typeof(Skybox)) as Skybox;
Skybox mysky = dest.GetComponent(typeof(Skybox)) as Skybox;
if (!sky || !sky.material)
{
mysky.enabled = false;
}
else
{
mysky.enabled = true;
mysky.material = sky.material;
}
}
dest.depth = src.depth;
dest.farClipPlane = src.farClipPlane;
dest.nearClipPlane = src.nearClipPlane;
dest.orthographic = src.orthographic;
dest.fieldOfView = src.fieldOfView;
dest.aspect = src.aspect;
dest.orthographicSize = src.orthographicSize;
}
//计算关于反射平面的反射矩阵,来得到主相机的镜像位置位置
/*若平面表示为(nx,ny,nz,d)则关于它的反射矩阵为
|1-2nx*nx,-2ny*nx,-2nz*nx,-2d*nx|
|-2nx*ny,1-2ny*ny,-2nz*ny,-2d*ny|
|-2nx*nz,-2ny*nz,1-2nz*nz,-2d*nz|
| 0, 0, 0, 1|
*/
static Matrix4x4 CalculateReflectionMatrixByPlane(Vector4 plane)
{
Matrix4x4 reflectionMat=new Matrix4x4();
reflectionMat.m00 = 1f - 2f * plane.x * plane.x;
reflectionMat.m01 = -2f * plane.x * plane.y;
reflectionMat.m02 = -2f * plane.x * plane.z;
reflectionMat.m03 = -2f * plane.x * plane.w;
reflectionMat.m10 = -2f * plane.y * plane.x;
reflectionMat.m11 = 1f - 2f * plane.y * plane.y;
reflectionMat.m12 = -2f * plane.y * plane.z;
reflectionMat.m13 = -2f * plane.y * plane.w;
reflectionMat.m20 = -2f * plane.z * plane.x;
reflectionMat.m21 = -2f * plane.z * plane.y;
reflectionMat.m22 = 1f - 2f * plane.z * plane.z;
reflectionMat.m23 = -2f * plane.z * plane.w;
reflectionMat.m30 = 0f;
reflectionMat.m31 = 0f;
reflectionMat.m32 = 0f;
reflectionMat.m33 = 1f;
return reflectionMat;
}
//用于将反射平面转换成在反射相机中的表示
static Vector4 CameraSpacePlane(Camera cam, Vector3 pos, Vector3 normal, float planeOffset)
{
Vector3 wPos = pos + normal * planeOffset;
Matrix4x4 m = cam.worldToCameraMatrix;
Vector3 cPos = m.MultiplyPoint(wPos);
Vector3 cNormal = m.MultiplyVector(normal).normalized;
return new Vector4(cNormal.x,cNormal.y,cNormal.z,-Vector3.Dot(cPos,cNormal));
}
//修改反射相机的投影矩阵,使之以反射面为近平面(这样水面下的东西就不会被渲染进反射贴图)
static Matrix4x4 CalculateProjectionBasePlane(Matrix4x4 projectMat,Vector4 clipPlane)
{
Vector4 pQ=new Vector4(sgn(clipPlane.x),sgn(clipPlane.y),1.0f,1.0f);
Vector3 cQ = projectMat.inverse * pQ;
//矩阵第三行替换为 M3=-2cQ.z*clipPlane/(C·cQ)+<0,0,1,0>
Vector4 c = clipPlane * (-2.0F / (Vector4.Dot(clipPlane, cQ)));
projectMat.m20=c.x+projectMat.m30;
projectMat.m21 = c.y + projectMat.m31;
projectMat.m22 = c.z + projectMat.m32;
projectMat.m23 = c.w + projectMat.m33;
return projectMat;
}
//Mathf.Sign()在a=0时返回1 所以得自己写一个
static float sgn (float a)
{
if (a > 0.0f) return 1.0f;
if (a < 0.0f) return -1.0f;
return 0.0f;
}
#endregion
}
两个shader的包含文件
#ifndef WATER_TOOL_INCLUDE
#define WATER_TOOL_INCLUDE
//-----------------折射和反射------------------------------
//计算折射颜色 默认贴图名为_RefractionTexture
float3 RefractionColor(float4 screenPos,float3 worldNormal)
{
//法线uv偏移 长宽应当适配屏幕
//float2 uvOffset=tangnentNormal.xy*_FlowStrength;
//世界空间改为xz
float2 uvOffset=worldNormal.xz*_FlowStrength;
uvOffset.y*=_CameraDepthTexture_TexelSize.z*abs(_CameraDepthTexture_TexelSize.y);
//齐次除法得到透视的uv坐标
float2 uv=(screenPos.xy+uvOffset)/screenPos.w;
#if UNITY_UV_STARTS_AT_TOP
if (_CameraDepthTexture_TexelSize.y < 0) {
uv.y = 1 - uv.y;
}
#endif
//采样深度 得到深度差
float backgroundDepth=LinearEyeDepth(tex2D(_CameraDepthTexture,uv));
float surfaceDepth=UNITY_Z_0_FAR_FROM_CLIPSPACE(screenPos.z);
float waterDepth=backgroundDepth-surfaceDepth;
//水深为负数表明物体在水面之上,应当抹去法向偏移 重新计算
uvOffset*=saturate(waterDepth);//负数就归0了,顺便水深0-1还可以有个过渡
uv=(screenPos.xy+uvOffset)/screenPos.w;
#if UNITY_UV_STARTS_AT_TOP
if (_CameraDepthTexture_TexelSize.y < 0) {
uv.y = 1 - uv.y;
}
#endif
backgroundDepth=LinearEyeDepth(tex2D(_CameraDepthTexture,uv));
surfaceDepth=UNITY_Z_0_FAR_FROM_CLIPSPACE(screenPos.z);
waterDepth=backgroundDepth-surfaceDepth;
//采样贴图
float3 bgCol=tex2D(_RefractionTexture,uv).rgb;
//根据水深模拟水的折射强度
float fogFactor=exp2(-waterDepth*_WaterDensity);
float3 finalCol=lerp(_WaterColor,bgCol,fogFactor);
return finalCol;
}
//计算反射颜色 默认贴图名为_ReflectionTexture
float3 ReflectionColor(float4 screenPos,float3 worldNormal)
{
//法线uv偏移 长宽应当适配屏幕
//float2 uvOffset=tangnentNormal.xy*_FlowStrength;
//世界空间,扰乱该为xz
float2 uvOffset=worldNormal.xz*_FlowStrength;
uvOffset.y*=_CameraDepthTexture_TexelSize.z*abs(_CameraDepthTexture_TexelSize.y);
//齐次除法得到透视的uv坐标
float2 uv=(screenPos.xy+uvOffset)/screenPos.w;
#if UNITY_UV_STARTS_AT_TOP
if (_CameraDepthTexture_TexelSize.y < 0) {
uv.y = 1 - uv.y;
}
#endif
float3 reflectCol=tex2D(_ReflectionTexture,uv);
return reflectCol;
}
float Fresnel(float F0,float3 viewDir,float3 halfDir)
{//schlick
return F0+(1-F0)*pow((1-dot(viewDir,halfDir)),5);
}
//--------------------------------------------------------------
//-----------------------水体流动--------------------------------
//根据uv采样流向,再根据流向和时间扰乱uv
float2 DirectionalFlowUV(float2 uv,float3 flowVectorAndSpeed,float Tile,float time,out float2x2 roation)
{
//将流体方向转换为旋转矩阵来旋转uv;
float2 dir=normalize(flowVectorAndSpeed.xy);
//用于修改导数,一个点旋转后,导数表示的法线方向应旋转,比如原来是0,1旋转90度后应指向1,0
roation=float2x2(dir.y,dir.x,-dir.x,dir.y);
uv.xy=mul(float2x2(dir.y,-dir.x,dir.x,dir.y),uv);
uv.y-=time*flowVectorAndSpeed.z;
return uv*Tile;
}
//将贴图的导数映射回[-1,1]
float3 UnpackDerivativeHeight (float4 textureData) {
float3 dh = textureData.agb;
dh.xy = dh.xy * 2 - 1;
return dh;
}
//流向贴图每个像素的流向不连续采样的结果是破碎的,因此水面分成多个小块,每一块区域用同一个uv值采样流向
//网格分布 _GridResolution
float3 FlowCell(float2 uv,float2 offset,float time,bool gridB){
float2 shift = 1 - offset;
shift *= 0.5;//让采样在中心点
offset*=0.5;//错开边缘的瑕疵线
if(gridB){
offset+=0.25;
shift-=0.25;
}
//块状uv坐标 流向纹理采样
float2 uvTiled=(floor(uv*_GridResolution+offset)+shift)/_GridResolution;
float3 flow=tex2D(_FlowMap,uvTiled).rgb;
flow.xy=flow.xy*2-1;
flow.z*=_FlowStrength;
float tiling = flow.z *_TilingModulated+ _Tile;//流向越快,缩放越大,波浪越大
//得到经流向扭曲后的uv坐标
float2x2 derivRotation;
//随便加个offset打乱一下重复
float2 uvFlow = DirectionalFlowUV(uv+offset,flow,tiling,time,derivRotation);
//采样主贴图 导数和高
float3 dh = UnpackDerivativeHeight(tex2D(_DerivHeightMap, uvFlow));//这里只能确保采样位置的匹配,不会修改内部的数据
dh.xy=mul(derivRotation,dh.xy);//导数也做对应的旋转
dh *= flow.z * _HeightScaleModulated + _HeightScale;//强度
return dh;
}
//每个cell混合周围cell,得到最终网格值,gridB用于创造一点偏移,从而得到另一个网格结果,混合两者降低边缘瑕疵
float3 FlowGrid(float2 uv,float time,bool gridB)
{
//混合相邻块
float3 dhA=FlowCell(uv,float2(0,0),time,gridB);
float3 dhB=FlowCell(uv,float2(1,0),time,gridB);
float3 dhC=FlowCell(uv,float2(0,1),time,gridB);
float3 dhD=FlowCell(uv,float2(1,1),time,gridB);
//计算混合权值
float2 t=uv*_GridResolution;
if(gridB){t+=0.25;}//偏移第二次网格的位置
t= abs(2 * frac(t) - 1);
float wA = (1 - t.x) * (1 - t.y);
float wB = t.x * (1 - t.y);
float wC = (1 - t.x) * t.y;
float wD = t.x * t.y;
float3 dh=wA*dhA+wB*dhB+wC*dhC+wD*dhD;
return dh;
}
//-----------------------------------------------------
//------------------------波浪------------------------------
float3 GerstnerWave (float4 wave, float wavespeed,float3 p, inout float3 tangent, inout float3 binormal){
float steepness=wave.z;
float wavelength=wave.w;
float k=UNITY_PI*2/(wavelength);
float a=(steepness/k);//通过波长和表面压力配置值来得到amplitude
float c=sqrt(9.8/k);//速度同样借此推导
//偏移与xy方向对齐
float2 d=normalize(wave.xy).xy;
float f=k*(dot(d,p.xz)-c*_Time.y*wavespeed);
float3 offset=float3(
d.x*a*cos(f),
a*sin(f),
d.y*a*cos(f)
);
//计算导数得到切线和次切线并推出法线 _Steepness=k*a
tangent += float3(
-d.x * d.x * (steepness * sin(f)),
d.x * (steepness * cos(f)),
-d.x * d.y * (steepness * sin(f))
);
binormal += float3(
-d.x * d.y * (steepness * sin(f)),
d.y * (steepness * cos(f)),
-d.y * d.y * (steepness * sin(f))
);
return offset;
}
//--------------------------------------------------
//--------------------泡沫----------------------------
float3 FoamColor(float4 screenPos,float2 FoamUV)
{
//未经法线扰乱的深度
float2 uv=(screenPos.xy)/screenPos.w;
#if UNITY_UV_STARTS_AT_TOP
if (_CameraDepthTexture_TexelSize.y < 0) {
uv.y = 1 - uv.y;
}
#endif
float backgroundDepth=LinearEyeDepth(tex2D(_CameraDepthTexture,uv));
float surfaceDepth=UNITY_Z_0_FAR_FROM_CLIPSPACE(screenPos.z);
float waterDepth=backgroundDepth-surfaceDepth;
//一条依附边缘
float factor1=1-smoothstep(_Foam1Range,_Foam1Range+0.2,waterDepth);
//一条移动
float time=-frac(_Time.y*_Foam2Speed);
float offset=time*_Foam2Range;//(0-_Foam2Range)的偏移量
float fade=sin(frac(_Time.y*_Foam2Speed)*3.14);//中间最明显
float factor2=step(_Foam1Range+_Foam2Range+0.12+offset,waterDepth)*step(waterDepth,_Foam1Range+_Foam2Range+0.15+offset);
float factor=factor1+factor2*fade*0.5;
float3 foamCol=factor*tex2D(_FoamTexture,FoamUV*10).r;
return foamCol;
}
//----------------------------------------------------
#endif
#ifndef WATER_TESSELLATION_INCLUDE
#define WATER_TESSELLATION_INCLUDE
TessellationControlPoint WaterTessellationVert(a2v v)
{
TessellationControlPoint o;
o.vertex=v.vertex;
o.normal=v.normal;
o.tangent=v.tangent;
o.texcoord=v.texcoord;
return o;
}
//--------------------壳着色器(决定如何细分)--------------------------
//图元的细分因子
struct TessellationFactors {
float edge[3] : SV_TessFactor;//决定原始三角边细分的段数 例:4=分成四段
float inside : SV_InsideTessFactor;//决定新生成的三角形的细分程度
};
//计算边的细分因子,按视距处理细分 默认存在_TessellationEdgeLength
float TessellationEdgeFactor(float3 p0,float3 p1)
{
float edgeLength = distance(p0, p1);
float3 edgeCenter = (p0 + p1) * 0.5;
float viewDistance = distance(edgeCenter, _WorldSpaceCameraPos);
//_ScreenParams x = 屏幕宽度,y = 屏幕高度,z = 1 + 1.0/屏幕宽度, w = 1 + 1.0/height屏幕高度(指像素数)
return edgeLength*_ScreenParams.y / (_TessellationEdgeLength * viewDistance);
}
//顶点是否在平面下方
bool TriangleIsBelowClipPlane (float3 p0, float3 p1, float3 p2, int planeIndex,float bias)
{ //点乘小于0表示在面下方,等于0在面内,大于0在面上方
//视锥体的6个平面,近平面和远平面可忽视
float4 plane = unity_CameraWorldClipPlanes[planeIndex];
return
dot(float4(p0, 1), plane) < bias &&
dot(float4(p1, 1), plane) < bias &&
dot(float4(p2, 1), plane) < bias;
}
//用于裁减掉视锥体外的不必要的顶点 优化
bool TriangleIsCulled (float3 p0, float3 p1, float3 p2,float bias) {
return
TriangleIsBelowClipPlane(p0, p1, p2, 0,bias) ||
TriangleIsBelowClipPlane(p0, p1, p2, 1,bias) ||
TriangleIsBelowClipPlane(p0, p1, p2, 2,bias) ||
TriangleIsBelowClipPlane(p0, p1, p2, 3,bias);
}
//常量函数
TessellationFactors MyPatchConstantFunction (InputPatch patch) {
TessellationFactors f;
float3 p0 = mul(unity_ObjectToWorld, patch[0].vertex).xyz;
float3 p1 = mul(unity_ObjectToWorld, patch[1].vertex).xyz;
float3 p2 = mul(unity_ObjectToWorld, patch[2].vertex).xyz;
float bias =-0.5;//顶点裁减的偏移值
if (TriangleIsCulled(p0, p1, p2,bias)) {
f.edge[0] = f.edge[1] = f.edge[2] = f.inside = 0;//因子为0时会将原有的顶点也裁去
}
else {
f.edge[0] = TessellationEdgeFactor(p1,p2);//对边
f.edge[1] = TessellationEdgeFactor(p2,p0);;
f.edge[2] = TessellationEdgeFactor(p0,p1);;
f.inside =
(TessellationEdgeFactor(p1, p2) +
TessellationEdgeFactor(p2, p0) +
TessellationEdgeFactor(p0, p1)) * (1 / 3.0);
}
return f;
}
[UNITY_domain("tri")]//表明处理的图元是三角形
[UNITY_outputcontrolpoints(3)]//每个图元输出三个控制点,分别是三角形的每个角
[UNITY_outputtopology("triangle_cw")]//新生成的三角形应当是顺时针的 clockwise
[UNITY_partitioning("fractional_odd")] //细分因子的处理方式 integer 因子取整 fractional_odd 支持小数,奇数间过渡,例(3,5]之间三段变成到五段的过渡,该值域内一直是5段,但是每段的长度逐渐平均,直至标准五段 fractional_even 偶数间过渡
[UNITY_patchconstantfunc("MyPatchConstantFunction")]//常量函数 决定每个图元应该如何细分,不同图元需要细分的程度不固定,所以用一个函数来输出各自的细分因子,该函数每一组块只调用一次
//patch中存储了网格的顶点数据的一个组,3表示三个顶点数据
//虽然是三个一组,但是实际上是为图元的每个顶点都调用一次该函数,id就用于表示一个图元中的某一顶点的序列
TessellationControlPoint WaterHullProgram(InputPatch patch,uint id : SV_OutputControlPointID)
{
return patch[id];
}
//-----------------------------------------------------
//----------------域着色器(为细分完成后的新旧顶点设置数据)-----------------------------
//用于属性插值的宏
#define MY_DOMAIN_PROGRAM_INTERPOLATE(fieldName) data.fieldName = \
patch[0].fieldName * barycentricCoordinates.x + \
patch[1].fieldName * barycentricCoordinates.y + \
patch[2].fieldName * barycentricCoordinates.z;
[UNITY_domain("tri")]
//每个新顶点会调用一次域函数,利用提供的重心坐标来插值新顶点属性
v2f WaterDomainProgram(TessellationFactors factors,OutputPatch patch,float3 barycentricCoordinates : SV_DomainLocation)
{
TessellationControlPoint data;
//新顶点属性的插值
MY_DOMAIN_PROGRAM_INTERPOLATE(vertex)
MY_DOMAIN_PROGRAM_INTERPOLATE(normal)
MY_DOMAIN_PROGRAM_INTERPOLATE(texcoord)
MY_DOMAIN_PROGRAM_INTERPOLATE(tangent)
//为新顶点执行顶点函数
v2f a=waterVert(data);
return a;
}
//----------------------------------------------------------------------------
#endif
shader
Shader "MyWater/CompleteWater"
{
Properties
{
[NoScaleOffset]_ReflectionTexture("反射贴图(From ToolForWater)",2D)="white"{}
_Gloss("Gloss",Range(8,256))=20
[Header(Water Properties)]
_WaterColor("水体颜色",Color)=(1,1,1,1)
_WaterDensity("水体密度",Range(0,2))=0.5
_Fresnel("菲涅尔",Range(0,1))=2
[Header(Water Flow)]
[Toggle(_DUAL_GRID)] _DualGrid ("双重混合", Int) = 0//用toggle特性 名字会变成一个关键字
_DerivHeightMap ("导数 (AG) 高度 (B)", 2D) = "white" {}
[NoScaleOffset]_FlowMap ("水体流向(RG),流速(B),噪声(A)", 2D) = "white" {}
_GridResolution("流向分辨率",Float)=10
_FlowSpeed("流速",Range(0,0.5))=1
_FlowStrength("流动强度",Float)=1
_Tile("Tile",Float)=1//波纹图案大小
_TilingModulated ("Tiling, Modulated", Float) = 1//流速对默认大小的影响程度,一般来说流速越快波纹越细密
_HeightScale ("Height Scale", Float) = 0.25//浪高缩放值
_HeightScaleModulated ("Height Scale, Modulated", Float) = 0.75//随速度递增的高度缩放值
[Header(Water Wave)]
_TessellationEdgeLength("细分边长(值越高精度越低)",Range(5,100))=50//细分段的长度
[Toggle(_WAVE_A_OPEN)]_enableA("开启波A",Int)=0
_WaveA("Wave A:dir,steepness,wavelength",Vector)=(1,0,0.5,10)
_WaveASpeed("speed",Float)=1
[Toggle(_WAVE_B_OPEN)]_enableB("开启波B",Int)=0
_WaveB("Wave B", Vector) = (0,1,0.25,20)
_WaveBSpeed("speed",Float)=1
[Toggle(_WAVE_C_OPEN)]_enableC("开启波C",Int)=0
_WaveC ("Wave C", Vector) = (1,1,0.15,10)
_WaveCSpeed("speed",Float)=1
[Header(Water Foam)]
_FoamTexture("泡沫贴图",2D)="white"{}
_Foam1Range("外围泡沫范围",Range(0,1))=0.5
_Foam2Range("内围泡沫移动范围",Range(0,1))=0.2
_Foam2Speed("内围泡沫移动速度",Float)=1
}
SubShader
{
Tags{"RenderType"="Opaque" "Queue"="Transparent"}
GrabPass{"_RefractionTexture"}
Pass{
CGPROGRAM
#include "UnityCG.cginc"
#include "Lighting.cginc"
#pragma target 4.6//曲面细分可用的级别
#pragma vertex WaterTessellationVert
#pragma hull WaterHullProgram
#pragma domain WaterDomainProgram
#pragma fragment frag
#pragma shader_feature _DUAL_GRID
#pragma shader_feature _WAVE_A_OPEN
#pragma shader_feature _WAVE_B_OPEN
#pragma shader_feature _WAVE_C_OPEN
sampler2D _NormalMap,_FoamTexture;
float4 _MainTex_ST,_FoamTexture_ST;
sampler2D _CameraDepthTexture,_RefractionTexture;
sampler2D _ReflectionTexture;
float4 _CameraDepthTexture_TexelSize;
float _Gloss,_WaterDensity,_Fresnel;
float3 _WaterColor;
sampler2D _DerivHeightMap,_FlowMap;
float4 _DerivHeightMap_ST;
float _FlowSpeed,_FlowStrength,_Tile,_TilingModulated,_HeightScale,_HeightScaleModulated,_GridResolution;
float _TessellationEdgeLength;
float4 _WaveA,_WaveB,_WaveC;
float _WaveASpeed,_WaveBSpeed,_WaveCSpeed;
float _Foam1Range,_Foam2Range,_Foam2Speed;
#include "Assets/WaterNote/Shaders/Library/WaterTool.cginc"
//开启曲面细分使用的顶点函数,只单纯的传递数据,真正对顶点的修改放到Domain中
struct a2v{
float4 vertex:POSITION;
float3 normal:NORMAL;
float4 tangent:TANGENT;
float2 texcoord:TEXCOORD0;
};
//顶点函数传给hull的结构
struct TessellationControlPoint {
float4 vertex : INTERNALTESSPOS;//用这个代替POSITION避免报错
float3 normal : NORMAL;
float4 tangent:TANGENT;
float2 texcoord : TEXCOORD0;
};
struct v2f{
float4 pos:SV_POSITION;
float4 ScreenPos:TEXCOORD1;
float4 uvNF:TEXCOORD3;
//切线到世界矩阵 第四分量存储世界位置
float4 TtoW0 : TEXCOORD4;
float4 TtoW1 : TEXCOORD5;
float4 TtoW2 : TEXCOORD6;
//float3 TangentLightDir:TEXCOORD4;
//float3 TangentViewDir:TEXCOORD5;
};
v2f waterVert(TessellationControlPoint v){
v2f o;
#if defined(_WAVE_A_OPEN)||defined(_WAVE_B_OPEN)||defined(_WAVE_C_OPEN)
float3 gridPoint=v.vertex.xyz;
float3 tangentG=float3(1,0,0);
float3 binormalG=float3(0,0,1);
float3 p=gridPoint;
#if defined(_WAVE_A_OPEN)
p+=GerstnerWave(_WaveA,_WaveASpeed,gridPoint,tangentG,binormalG);
#endif
#if defined(_WAVE_B_OPEN)
p+=GerstnerWave(_WaveB,_WaveBSpeed,gridPoint,tangentG,binormalG);
#endif
#if defined(_WAVE_C_OPEN)
p+=GerstnerWave(_WaveC,_WaveCSpeed,gridPoint,tangentG,binormalG);
#endif
float3 normal=normalize(cross(binormalG,tangentG));
v.vertex.xyz=p;
v.normal=normal;
#endif
o.pos=UnityObjectToClipPos(v.vertex);
o.ScreenPos=ComputeScreenPos(o.pos);//一定要在顶点函数里计算,然后让插值器插值,在片元函数里得到的o.pos可能已经裁减空间的pos了
o.uvNF.xy=TRANSFORM_TEX(v.texcoord,_DerivHeightMap);
o.uvNF.zw=TRANSFORM_TEX(v.texcoord,_FoamTexture);
//对于折射的扰乱我们用的是法向量,如果使用切线空间法向量,那么只有法线贴图会影响扰乱
//所以我们选择世界空间计算光照
//TANGENT_SPACE_ROTATION; //得到切线空间矩阵rotation
//o.TangentLightDir=mul(rotation,ObjSpaceLightDir(v.vertex));
//o.TangentViewDir=mul(rotation,ObjSpaceViewDir(v.vertex));
float3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);
float3 worldNormal = UnityObjectToWorldNormal(v.normal);
float3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w;
float3 worldPos=mul(unity_ObjectToWorld,v.vertex);
o.TtoW0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x,worldPos.x);
o.TtoW1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y,worldPos.y);
o.TtoW2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z,worldPos.z);
return o;
}
#include "Assets/WaterNote/Shaders/Library/WaterTessellation.cginc"
float4 frag(v2f i):SV_Target{
//得用UnityWorldxxxxDir(要求世界空间的顶点位置) 不能用WorldxxxxDir(要求对象空间的顶点位置)
//float3 lightDir=normalize(i.TangentLightDir);
//float3 viewDir=normalize(i.TangentViewDir);
float3 worldPos = float3(i.TtoW0.w,i.TtoW1.w,i.TtoW2.w);
float3 lightDir = normalize(UnityWorldSpaceLightDir(worldPos));
float3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));
float3 halfDir=normalize(lightDir+viewDir);
//扰乱法线的uv,模拟流动效果
float time=_Time.y*_FlowSpeed;
float2 waveUV=i.uvNF.xy;
float3 dh = FlowGrid(waveUV, time, false);
#if defined(_DUAL_GRID)
dh = (dh + FlowGrid(waveUV, time, true)) * 0.5;
#endif
float3 normal=normalize(float3(dh.xy, 1));
//切线空间转至世界空间
normal = normalize(half3(dot(i.TtoW0, normal), dot(i.TtoW1, normal), dot(i.TtoW2, normal)));
float waterDepth;
//采样折射
float3 refrCol=RefractionColor(i.ScreenPos,normal);
//采样反射
float3 reflCol=ReflectionColor(i.ScreenPos,normal);
//泡沫采样
float3 foamCol=FoamColor(i.ScreenPos,i.uvNF.zw);
//漫反射
//float3 diffuse=_LightColor0.rgb*_Color*saturate(dot(normal,lightDir));
//高光
float3 specular=_LightColor0.rgb*pow(saturate(dot(halfDir,normal)),_Gloss)*0.1;
float3 finalCol=lerp(refrCol,reflCol,saturate(Fresnel(_Fresnel,viewDir,lightDir)))+specular+foamCol;
return float4(finalCol,1);
}
ENDCG
}
}
//FallBack "Diffuse"
}
UnityPackage:https://pan.baidu.com/s/1KI8bLcBFF1aTCk70bXZlXg ut5a