Unityshader水体笔记

总结一下最近学习的东西

折射 

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<Renderer>() || !GetComponent<Renderer>().sharedMaterial || !GetComponent<Renderer>().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<Renderer>().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<Camera>();
            _reflCamera.enabled = false;
            _reflCamera.transform.position = transform.position;
            _reflCamera.transform.rotation = transform.rotation;
            _reflCamera.GetComponent<FlareLayer>();
            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<TessellationControlPoint, 3> 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<TessellationControlPoint,3> 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<TessellationControlPoint, 3> 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

  • 5
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值