URP - 水效果Shader

article/2025/7/5 22:31:59

一、水面颜色

利用屏幕深度纹理和水体面片的深度差来设置水面颜色,形成水面的颜色渐变

float4 frag(Varyings i):SV_Target
{//根据深度差设置水面颜色float2 ScreenUV = i.positionCS.xy/_ScreenParams.xy; //屏幕UVfloat depthTex =     SAMPLE_TEXTURE2D(_CameraDepthTexture,sampler_CameraDepthTexture,ScreenUV); //采样深度图depthTex = LinearEyeDepth(depthTex,_ZBufferParams); //将深度纹理的深度值转换到视图空间//(此时水体面片不渲染到深度图中)用深度图中的深度值与水体面片的在视图空间的深度值做差值,两者交接处颜色为0float depthDifference = depthTex+i.positionVS.z; //i.positionVS.z为负值,做差值时需两者相加//_depthIntensity用于调节深度差变化depthDifference *= _depthIntensity; //归一化depthDifference = saturate(depthDifference);//水面颜色float4 WaterColor_M = lerp(_EdgeColor,_CenterColor,depthDifference);return WaterColor_M;
}


 二、水面流动

采样两次流动的法线纹理并进行混合作为水面的流动效果

  • 在材质面板和Pass中分别声明法线纹理

        _NormalTex("NormalTex",2D)="white"{}

        //法线纹理
        TEXTURE2D(_NormalTex);
        SAMPLER(sampler_NormalTex);

  • 定义采样法线纹理所需要的UV,并使该纹理的重复和平铺有效同时使该纹理随时间而流动
o.normalUV.xy = TRANSFORM_TEX(v.normalUV,_NormalTex)-_Time.y*_FlowSpeed*float2(1,1);
//*float2(-1.15,0.8)是使U方向朝负方向流动V方向正常,正常来说是*float2(-1,1),而数值改变是为了让纹理的重复感消失
o.normalUV.zw = TRANSFORM_TEX(v.normalUV,_NormalTex)-_Time.y*_FlowSpeed*float2(-1.15,0.8);
  • 利用两个流动不同方向的normalUV来采样法线纹理,最后将两者混合,做出水面流动的效果
//水面法线
//利用两个流动不同方向的normalUV来采样法线纹理,最后将两者混合,做出水面流动的效果
float4 NormalTex01 = SAMPLE_TEXTURE2D(_NormalTex,sampler_NormalTex,i.normalUV.xy);
float4 NormalTex02 = SAMPLE_TEXTURE2D(_NormalTex,sampler_NormalTex,i.normalUV.zw);
float4 normalTex = NormalTex01*NormalTex02;

Shader"unity/Water02"
{Properties {[Header(Depth)][Space()]_depthIntensity("Depth Intensity",Range(0,0.2))=0.05[Header(WaterColor)][Space()]_CenterColor("Center Color",Color)=(1,1,1,1)_EdgeColor("Edge Color",Color)=(1,1,1,1)[Header(WaterFlowSpeed)][Space()]_FlowSpeed("Water FlowSpeed",float)=1[Header(Normal)][Space()]_NormalTex("NormalTex",2D)="white"{}}SubShader{Tags{"RenderPipeline" = "UniversalPipeline" "Queue"="Transparent" }Pass{HLSLPROGRAM#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Input.hlsl"#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"#pragma vertex vert#pragma fragment frag//深度纹理TEXTURE2D(_CameraDepthTexture);SAMPLER(sampler_CameraDepthTexture);//法线纹理TEXTURE2D(_NormalTex);SAMPLER(sampler_NormalTex);CBUFFER_START(UnityPerMaterial)float4 _EdgeColor,_CenterColor,_NormalTex_ST;float _depthIntensity,_FlowSpeed;CBUFFER_ENDstruct Attributes{float4 positionOS : POSITION;float2 uv : TEXCOORD;float4 normalUV : TEXCOORD1;};struct Varyings{float4 positionCS : SV_POSITION;float3 positionVS : TEXCOORD;float3 positionWS : TEXCOORD1;float2 uv : TEXCOORD2;float4 normalUV : TEXCOORD3;};Varyings vert(Attributes v){Varyings o = (Varyings)0;o.positionCS = TransformObjectToHClip(v.positionOS);o.positionWS = TransformObjectToWorld(v.positionOS);o.positionVS = TransformWorldToView(o.positionWS);o.normalUV.xy = TRANSFORM_TEX(v.normalUV,_NormalTex)-_Time.y*_FlowSpeed*float2(1,1);//*float2(-1.15,0.8)是使U方向朝负方向流动V方向正常,正常来说是*float2(-1,1),而数值改变是为了让纹理的重复感消失o.normalUV.zw = TRANSFORM_TEX(v.normalUV,_NormalTex)-_Time.y*_FlowSpeed*float2(-1.15,0.8);o.uv = v.uv;return o;}float4 frag(Varyings i):SV_Target{//根据深度差设置水面颜色float2 ScreenUV = i.positionCS.xy/_ScreenParams.xy; //屏幕UVfloat depthTex = SAMPLE_TEXTURE2D(_CameraDepthTexture,sampler_CameraDepthTexture,ScreenUV); //采样深度图depthTex = LinearEyeDepth(depthTex,_ZBufferParams); //将深度纹理的深度值转换到视图空间//(此时水体面片不渲染到深度图中)用深度图中的深度值与水体面片的在视图空间的深度值做差值,两者交接处颜色为0float depthDifference = depthTex+i.positionVS.z; //i.positionVS.z为负值,做差值时需两者相加//_depthIntensity用于调节深度差变化depthDifference *= _depthIntensity; //归一化depthDifference = saturate(depthDifference);//水面颜色 (水下颜色可以与焦散相乘)float4 WaterColor_M = lerp(_EdgeColor,_CenterColor,depthDifference);//水面法线//利用两个流动不同方向的normalUV来采样法线纹理,最后将两者混合,做出水面流动的效果float4 NormalTex01 = SAMPLE_TEXTURE2D(_NormalTex,sampler_NormalTex,i.normalUV.xy);float4 NormalTex02 = SAMPLE_TEXTURE2D(_NormalTex,sampler_NormalTex,i.normalUV.zw);float4 normalTex = NormalTex01*NormalTex02;return normalTex;return WaterColor_M;}ENDHLSL}}}

三、水面高光 

float4 Specular =_SpecularColor*_SpecularIntensity*pow(max(0,dot(N,H)),_Shininess);

//H 为半角向量(入射光线和视线之间形成的半角向量)  H = L+V

//水面高光
//利用_NormalMixture来混合模型本身法线和法线纹理
float3 N = normalize(lerp(i.normalWS,normalTex,_NormalMixture));
Light light = GetMainLight();
float3 L = light.direction;
float3 V = normalize(_WorldSpaceCameraPos - i.positionWS);
float3 H = normalize(L+V);
float4 Specular = _SpecularColor * _SpecularIntensity * pow(max(0,dot(N,H)),_Shininess);

Shader"unity/Water02"
{Properties {[Header(Depth)][Space()]_depthIntensity("Depth Intensity",Range(0,0.2))=0.05[Header(WaterColor)][Space()]_CenterColor("Center Color",Color)=(1,1,1,1)_EdgeColor("Edge Color",Color)=(1,1,1,1)[Header(WaterFlowSpeed)][Space()]_FlowSpeed("Water FlowSpeed",float)=1[Header(Normal)][Space()]_NormalTex("NormalTex",2D)="white"{}_NormalMixture("Normal Mixture",float)=1[Header(Specular)][Space()]_SpecularColor("Specular Color",Color)=(1,1,1,1)_SpecularIntensity("Specular Intensity",float)=1_Shininess("Shininess",float)=1}SubShader{Tags{"RenderPipeline" = "UniversalPipeline" "Queue"="Transparent" }Pass{HLSLPROGRAM#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Input.hlsl"#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"#pragma vertex vert#pragma fragment frag//深度纹理TEXTURE2D(_CameraDepthTexture);SAMPLER(sampler_CameraDepthTexture);//法线纹理TEXTURE2D(_NormalTex);SAMPLER(sampler_NormalTex);CBUFFER_START(UnityPerMaterial)float4 _EdgeColor,_CenterColor,_NormalTex_ST,_SpecularColor;float _depthIntensity,_FlowSpeed,_NormalMixture,_Shininess,_SpecularIntensity;CBUFFER_ENDstruct Attributes{float4 positionOS : POSITION;float2 uv : TEXCOORD;float4 normalUV : TEXCOORD1;float3 normalOS : Normal;};struct Varyings{float4 positionCS : SV_POSITION;float3 positionVS : TEXCOORD;float3 positionWS : TEXCOORD1;float2 uv : TEXCOORD2;float4 normalUV : TEXCOORD3;float3 normalWS : TEXCOORD4;};Varyings vert(Attributes v){Varyings o = (Varyings)0;o.positionCS = TransformObjectToHClip(v.positionOS);o.positionWS = TransformObjectToWorld(v.positionOS);o.positionVS = TransformWorldToView(o.positionWS);o.normalUV.xy = TRANSFORM_TEX(v.normalUV,_NormalTex)-_Time.y*_FlowSpeed*float2(1,1);//*float2(-1.15,0.8)是使U方向朝负方向流动V方向正常,正常来说是*float2(-1,1),而数值改变是为了让纹理的重复感消失o.normalUV.zw = TRANSFORM_TEX(v.normalUV,_NormalTex)-_Time.y*_FlowSpeed*float2(-1.15,0.8);o.normalWS = TransformObjectToWorldNormal(v.normalOS);o.uv = v.uv;return o;}float4 frag(Varyings i):SV_Target{//根据深度差设置水面颜色float2 ScreenUV = i.positionCS.xy/_ScreenParams.xy; //屏幕UVfloat depthTex = SAMPLE_TEXTURE2D(_CameraDepthTexture,sampler_CameraDepthTexture,ScreenUV); //采样深度图depthTex = LinearEyeDepth(depthTex,_ZBufferParams); //将深度纹理的深度值转换到视图空间//(此时水体面片不渲染到深度图中)用深度图中的深度值与水体面片的在视图空间的深度值做差值,两者交接处颜色为0float depthDifference = depthTex+i.positionVS.z; //i.positionVS.z为负值,做差值时需两者相加//_depthIntensity用于调节深度差变化depthDifference *= _depthIntensity; //归一化depthDifference = saturate(depthDifference);//水面颜色 (水下颜色可以与焦散相乘)float4 WaterColor_M = lerp(_EdgeColor,_CenterColor,depthDifference);//水面法线//利用两个流动不同方向的normalUV来采样法线纹理,最后将两者混合,做出水面流动的效果float4 NormalTex01 = SAMPLE_TEXTURE2D(_NormalTex,sampler_NormalTex,i.normalUV.xy);float4 NormalTex02 = SAMPLE_TEXTURE2D(_NormalTex,sampler_NormalTex,i.normalUV.zw);float4 normalTex = NormalTex01*NormalTex02;//水面高光//利用_NormalMixture来混合模型本身法线和法线纹理float3 N = normalize(lerp(i.normalWS,normalTex,_NormalMixture));Light light = GetMainLight();float3 L = light.direction;float3 V = normalize(_WorldSpaceCameraPos - i.positionWS);float3 H = normalize(L+V);float4 Specular = _SpecularColor * _SpecularIntensity * pow(max(0,dot(N,H)),_Shininess);float4 c;c = WaterColor_M;c += Specular;return c;//return WaterColor_M;}ENDHLSL}}}

四、水面反射以及菲尼尔

目前有好多种制作水面反射的方法

  1. 利用立方体纹理CubeMap
  2. 平面反射

          

目前还没有搞明白除了立方体纹理之外的其他反射效果如何制作,这里就先使用立方体纹理来制作反射效果吧

  • 首先在材质面板和Pass中定义立方体纹理,然后使用反射向量采样立方体纹理
  • 最后将反射颜色与菲尼尔相乘,使得平行看向水面时,反射率较强,垂直看向水面时,反射率较少
//水面反射以及菲尼尔
//利用_ReflectMixture来控制法线取值,与上一步制作高光的法线值区分
float3 N1 = lerp(i.normalWS,normalTex,_ReflectMixture);
float3 R = reflect(-V,N1); //反射向量
float4 ReflectTex = SAMPLE_TEXTURECUBE(_ReflectTex,sampler_ReflectTex,R);
float Fresnel = pow(max(0,1-dot(N,V)),_FresnelIntensity); //菲尼尔
float4 Reflect = ReflectTex*Fresnel;

Shader"unity/Water02"
{Properties {[Header(Depth)][Space()]_depthIntensity("Depth Intensity",Range(0,0.2))=0.05[Header(WaterColor)][Space()]_CenterColor("Center Color",Color)=(1,1,1,1)_EdgeColor("Edge Color",Color)=(1,1,1,1)[Header(WaterFlowSpeed)][Space()]_FlowSpeed("Water FlowSpeed",float)=1[Header(Normal)][Space()]_NormalTex("NormalTex",2D)="white"{}_NormalMixture("Normal Mixture",float)=1[Header(Specular)][Space()]_SpecularColor("Specular Color",Color)=(1,1,1,1)_SpecularIntensity("Specular Intensity",float)=1_Shininess("Shininess",float)=1[Header(Reflect)][Space()]_ReflectTex("ReflectTex",Cube)="white"{}_ReflectMixture("Reflect Mixture",float)=1_FresnelIntensity("Fresnel Intensity",float)=1}SubShader{Tags{"RenderPipeline" = "UniversalPipeline" "Queue"="Transparent" }Pass{HLSLPROGRAM#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Input.hlsl"#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"#pragma vertex vert#pragma fragment frag//深度纹理TEXTURE2D(_CameraDepthTexture);SAMPLER(sampler_CameraDepthTexture);//法线纹理TEXTURE2D(_NormalTex);SAMPLER(sampler_NormalTex);//反射纹理TEXTURECUBE(_ReflectTex);SAMPLER(sampler_ReflectTex);CBUFFER_START(UnityPerMaterial)float4 _EdgeColor,_CenterColor,_NormalTex_ST,_SpecularColor;float _depthIntensity,_FlowSpeed,_NormalMixture,_Shininess,_SpecularIntensity,_FresnelIntensity,_ReflectMixture;CBUFFER_ENDstruct Attributes{float4 positionOS : POSITION;float2 uv : TEXCOORD;float4 normalUV : TEXCOORD1;float3 normalOS : Normal;};struct Varyings{float4 positionCS : SV_POSITION;float3 positionVS : TEXCOORD;float3 positionWS : TEXCOORD1;float2 uv : TEXCOORD2;float4 normalUV : TEXCOORD3;float3 normalWS : TEXCOORD4;};Varyings vert(Attributes v){Varyings o = (Varyings)0;o.positionCS = TransformObjectToHClip(v.positionOS);o.positionWS = TransformObjectToWorld(v.positionOS);o.positionVS = TransformWorldToView(o.positionWS);o.normalUV.xy = TRANSFORM_TEX(v.normalUV,_NormalTex)-_Time.y*_FlowSpeed*float2(1,1);//*float2(-1.15,0.8)是使U方向朝负方向流动V方向正常,正常来说是*float2(-1,1),而数值改变是为了让纹理的重复感消失o.normalUV.zw = TRANSFORM_TEX(v.normalUV,_NormalTex)-_Time.y*_FlowSpeed*float2(-1.15,0.8);o.normalWS = TransformObjectToWorldNormal(v.normalOS);o.uv = v.uv;return o;}float4 frag(Varyings i):SV_Target{//根据深度差设置水面颜色float2 ScreenUV = i.positionCS.xy/_ScreenParams.xy; //屏幕UVfloat depthTex = SAMPLE_TEXTURE2D(_CameraDepthTexture,sampler_CameraDepthTexture,ScreenUV); //采样深度图depthTex = LinearEyeDepth(depthTex,_ZBufferParams); //将深度纹理的深度值转换到视图空间//(此时水体面片不渲染到深度图中)用深度图中的深度值与水体面片的在视图空间的深度值做差值,两者交接处颜色为0float depthDifference = depthTex+i.positionVS.z; //i.positionVS.z为负值,做差值时需两者相加//_depthIntensity用于调节深度差变化depthDifference *= _depthIntensity; //归一化depthDifference = saturate(depthDifference);//水面颜色 (水下颜色可以与焦散相乘)float4 WaterColor_M = lerp(_EdgeColor,_CenterColor,depthDifference);//水面法线//利用两个流动不同方向的normalUV来采样法线纹理,最后将两者混合,做出水面流动的效果float4 NormalTex01 = SAMPLE_TEXTURE2D(_NormalTex,sampler_NormalTex,i.normalUV.xy);float4 NormalTex02 = SAMPLE_TEXTURE2D(_NormalTex,sampler_NormalTex,i.normalUV.zw);float4 normalTex = NormalTex01*NormalTex02;//水面高光//利用_NormalMixture来混合模型本身法线和法线纹理float3 N = normalize(lerp(i.normalWS,normalTex,_NormalMixture));Light light = GetMainLight();float3 L = light.direction;float3 V = normalize(_WorldSpaceCameraPos - i.positionWS);float3 H = normalize(L+V);float4 Specular = _SpecularColor * _SpecularIntensity * pow(max(0,dot(N,H)),_Shininess);//水面反射以及菲尼尔//利用_ReflectMixture来控制法线取值,与上一步制作高光的法线值区分float3 N1 = lerp(i.normalWS,normalTex,_ReflectMixture);float3 R = reflect(-V,N1); //反射向量float4 ReflectTex = SAMPLE_TEXTURECUBE(_ReflectTex,sampler_ReflectTex,R);float Fresnel = pow(max(0,1-dot(N,V)),_FresnelIntensity); //菲尼尔float4 Reflect = ReflectTex*Fresnel;float4 c;c = WaterColor_M;c += Specular*Reflect;return c;return WaterColor_M;}ENDHLSL}}}

五、水下的折射扭曲

首先定义一个用于扭曲水下的纹理,采样此纹理,利用此纹理来改变屏幕UV,用扭曲后的屏幕UV来采样屏幕纹理

然后可以利用扭曲后的屏幕UV采样深度图,通过深度差来判断深度图中的物体是出于水面上还是水面下,若空间中的物体处于水面上,则采样屏幕纹理时的坐标使用水体面片的屏幕空间坐标

//水下的折射扭曲
//利用水体面片的世界空间下坐标采样用于扭曲水下的纹理
float3 DistortTex = UnpackNormal(SAMPLE_TEXTURE2D(_DistortTex,sampler_DistortTex,i.positionWS.xz*float2(_Refract_X,_Refract_Y)-_Time.y));
//偏移值
float2 offset = DistortTex.xy*_DistortIntensity;
//扭曲UV值
float2 DistortUV = ScreenUV + offset; //_DistortIntensity为扭曲强度值
//利用扭曲后的屏幕UV采样深度图
float DistortDepth = SAMPLE_TEXTURE2D(_CameraDepthTexture,sampler_CameraDepthTexture,DistortUV);
//将深度图中的深度信息转换到视图空间
DistortDepth = LinearEyeDepth(DistortDepth,_ZBufferParams);
//计算深度图和水体面片在视图空间中的深度差(i.positionVS.z为负值,所以两者要相加)
float DistDepthDifferent = saturate(DistortDepth+i.positionVS.z);
//消除水面以上物体的扭曲,因为水面以上物体的深度值小于i.positionVS.z,所以DistDepthDifferent值为负时,使水面以上物体采样屏幕纹理的UV为屏幕UV(不进行扭曲)
if(DistDepthDifferent<=0)
{DistortUV = ScreenUV;}//最后利用上面计算得到的UV来采样屏幕纹理
float4 RefractTex = SAMPLE_TEXTURE2D(_CameraOpaqueTexture,sampler_CameraOpaqueTexture,DistortUV);

之后将折射扭曲与水体颜色值相乘

但是此时还是会有一点小Bug,就是会看到水下物体本身的轮廓

这个问题有会的宝子可以给点建议 

六、水下的焦散 

水下焦散的效果实现和之前学习的深度贴花的实现方法相同,需要利用面片在视图空间下的坐标值求出当前深度图中的像素在视图空间下的坐标值,再将此坐标值转换到世界空间下,用此坐标值作为UV来采样焦散纹理

//水下的焦散
//因为焦散纹理需要处于水底,而我们只能使用水体面片来采样焦散纹理。所以可以求出深度图中的像素位置在世界空间下的值,用此值作为UV来采样焦散纹理
float4 depthVS = 1;
depthVS.z = depthTex;
//求深度图中对应点的xy方向上的值
depthVS.xy = i.positionVS.xy * depthTex / -i.positionVS.z;
//将求得的视图空间下深度图中的位置信息转换到世界空间下
float3 depthWS = mul(unity_CameraToWorld,depthVS).xyz;
//用深度图中像素的在世界空间下位置作为UV来采样焦散纹理
//加depthWS.y的作用是使从上往下看同一个位置上的不同高度的点,在加上不同的高度值,从而使XZ方向的UV值错开从而采样不同的纹理
float2 uv1 = (depthWS.xz * _CausticTex_ST.xy + depthWS.y*_OffsetY)+_Time.y*_FlowSpeed;
float2 uv2 = (depthWS.xz * _CausticTex_ST.xy + depthWS.y*_OffsetY)+_Time.y*_FlowSpeed*float2(-1.15,0.9);
float4 CausticTex01 = SAMPLE_TEXTURE2D(_CausticTex,sampler_CausticTex,uv1);
float4 CausticTex02 = SAMPLE_TEXTURE2D(_CausticTex,sampler_CausticTex,uv2);
float4 CausticTex = min(CausticTex01,CausticTex02)*_CausticIntensity; //_CausticIntensity为焦散的强度值float4 c;
c = WaterColor_M;
c *= RefractTex;  //将折射扭曲与水体颜色相乘
c += Specular*Reflect;
c += CausticTex;
c = saturate(c);
return c;

七、水面上的泡沫

  •  首先需要声明泡沫纹理,然后采样该纹理

        o.uv = TRANSFORM_TEX(v.uv,_FoamTex); //使纹理的重复和平铺有效

        float4 FoamTex = SAMPLE_TEXTURE2D(_FoamTex,sampler_FoamTex,i.uv);

  • 然后利用深度差值和采样泡沫纹理后的值做step()运算
//水面的泡沫
//采样泡沫纹理
float4 FoamTex = SAMPLE_TEXTURE2D(_FoamTex,sampler_FoamTex,i.uv);
//_FoamNoise的值越大,FoamTex的黑色区域越多,后面求得的白色泡沫越少
FoamTex = pow(abs(FoamTex),_FoamNoise);
//step函数 step(a,b) 当a<b时,返回1; 当a>=b时,返回0 (此时靠近交接处的泡沫纹理的白色区域会形成泡沫遮罩,颜色为白色)
float FoamMask = step(depthDifference,FoamTex);
float Foam = FoamMask * _FoamColor;

八、代码部分

Shader"unity/Water02"
{Properties {[Header(Depth)][Space()]_depthIntensity("Depth Intensity",Range(0,0.2))=0.05[Header(WaterColor)][Space()]_CenterColor("Center Color",Color)=(1,1,1,1)_EdgeColor("Edge Color",Color)=(1,1,1,1)[Header(WaterFlowSpeed)][Space()]_FlowSpeed("Water FlowSpeed",float)=1[Header(Foam)][Space()]_FoamTex("FoamTex",2D)="white"{}_FoamColor("Foam Color",Color)=(1,1,1,1)_FoamNoise("FoamNoise",float)=1[Header(Normal)][Space()]_NormalTex("NormalTex",2D)="white"{}_NormalMixture("Normal Mixture",float)=1[Header(Specular)][Space()]_SpecularColor("Specular Color",Color)=(1,1,1,1)_SpecularIntensity("Specular Intensity",float)=1_Shininess("Shininess",float)=1[Header(Reflect)][Space()]_ReflectTex("ReflectTex",Cube)="white"{}_ReflectMixture("Reflect Mixture",float)=1_FresnelIntensity("Fresnel Intensity",float)=1[Header(Refract)][Space()][NoScaleOffset]_DistortTex("DistortTex",2D)="white"{}_Refract_X("Refract_X",float)=1_Refract_Y("Refract_Y",float)=1_DistortIntensity("Distort Intensity",float)=0 //折射纹理的扭曲强度[Header(Caustic)][Space()]_CausticTex("CausticTex",2D)="white"{}_OffsetY("Caustic OffsetY",float)=1_CausticIntensity("Caustic Intensity",float)=1}SubShader{Tags{"RenderPipeline" = "UniversalPipeline" "Queue"="Transparent" //一定要记得添加,要不然采样的屏幕纹理和深度纹理会有错误}// Blend SrcAlpha OneMinusSrcAlphaPass{HLSLPROGRAM#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Input.hlsl"#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"#pragma vertex vert#pragma fragment frag//深度纹理TEXTURE2D(_CameraDepthTexture);SAMPLER(sampler_CameraDepthTexture);//泡沫纹理TEXTURE2D(_FoamTex);SAMPLER(sampler_FoamTex);//法线纹理TEXTURE2D(_NormalTex);SAMPLER(sampler_NormalTex);//反射纹理TEXTURECUBE(_ReflectTex);SAMPLER(sampler_ReflectTex);//折射纹理TEXTURE2D(_DistortTex);SAMPLER(sampler_DistortTex);//屏幕纹理TEXTURE2D(_CameraOpaqueTexture);SAMPLER(sampler_CameraOpaqueTexture);//焦散纹理TEXTURE2D(_CausticTex);SAMPLER(sampler_CausticTex);CBUFFER_START(UnityPerMaterial)float4 _EdgeColor,_CenterColor,_NormalTex_ST,_SpecularColor,_CausticTex_ST,_FoamTex_ST,_FoamColor;float _depthIntensity,_FlowSpeed,_NormalMixture,_Shininess,_SpecularIntensity,_FresnelIntensity,_ReflectMixture,_Refract_X,_Refract_Y,_DistortIntensity,_OffsetY,_CausticIntensity,_FoamNoise;CBUFFER_ENDstruct Attributes{float4 positionOS : POSITION;float2 uv : TEXCOORD;float4 normalUV : TEXCOORD1;float3 normalOS : Normal;};struct Varyings{float4 positionCS : SV_POSITION;float2 uv : TEXCOORD;float3 positionVS : TEXCOORD1;float3 positionWS : TEXCOORD2;float4 normalUV : TEXCOORD3;float3 normalWS : TEXCOORD4;};Varyings vert(Attributes v){Varyings o = (Varyings)0;o.positionCS = TransformObjectToHClip(v.positionOS);o.positionWS = TransformObjectToWorld(v.positionOS);o.positionVS = TransformWorldToView(o.positionWS);o.normalUV.xy = TRANSFORM_TEX(v.normalUV,_NormalTex)+_Time.y*_FlowSpeed*float2(1,1);//*float2(-1.15,0.8)是使U方向朝负方向流动V方向正常,正常来说是*float2(-1,1),而数值改变是为了让纹理的重复感消失o.normalUV.zw = TRANSFORM_TEX(v.normalUV,_NormalTex)+_Time.y*_FlowSpeed*float2(-1.15,0.8);o.normalWS = TransformObjectToWorldNormal(v.normalOS);o.uv = TRANSFORM_TEX(v.uv,_FoamTex)+_Time.y*_FlowSpeed;return o;}float4 frag(Varyings i):SV_Target{//根据深度差设置水面颜色float2 ScreenUV = i.positionCS.xy/_ScreenParams.xy; //屏幕UVfloat depthTex = SAMPLE_TEXTURE2D(_CameraDepthTexture,sampler_CameraDepthTexture,ScreenUV); //采样深度图depthTex = LinearEyeDepth(depthTex,_ZBufferParams); //将深度纹理的深度值转换到视图空间//(此时水体面片不渲染到深度图中)用深度图中的深度值与水体面片的在视图空间的深度值做差值,两者交接处颜色为0float depthDifference = depthTex+i.positionVS.z; //i.positionVS.z为负值,做差值时需两者相加//_depthIntensity用于调节深度差变化depthDifference *= _depthIntensity; //归一化depthDifference = saturate(depthDifference);//水面颜色 (水下颜色可以与焦散相乘)float4 WaterColor_M = lerp(_EdgeColor,_CenterColor,depthDifference);//水面的泡沫//采样泡沫纹理float4 FoamTex = SAMPLE_TEXTURE2D(_FoamTex,sampler_FoamTex,i.uv);//_FoamNoise的值越大,FoamTex的黑色区域越多,后面求得的白色泡沫越少FoamTex = pow(abs(FoamTex),_FoamNoise);//step函数 step(a,b) 当a<b时,返回1; 当a>=b时,返回0 (此时靠近交接处的泡沫纹理的白色区域会形成泡沫遮罩,颜色为白色)float FoamMask = step(depthDifference,FoamTex);float Foam = FoamMask * _FoamColor;//水面法线//利用两个流动不同方向的normalUV来采样法线纹理,最后将两者混合,做出水面流动的效果float4 NormalTex01 = SAMPLE_TEXTURE2D(_NormalTex,sampler_NormalTex,i.normalUV.xy);float4 NormalTex02 = SAMPLE_TEXTURE2D(_NormalTex,sampler_NormalTex,i.normalUV.zw);float4 normalTex = NormalTex01*NormalTex02;//水面高光//利用_NormalMixture来混合模型本身法线和法线纹理float3 N = normalize(lerp(i.normalWS,normalTex,_NormalMixture));Light light = GetMainLight();float3 L = light.direction;float3 V = normalize(_WorldSpaceCameraPos - i.positionWS);float3 H = normalize(L+V);float4 Specular = _SpecularColor * _SpecularIntensity * pow(max(0,dot(N,H)),_Shininess);//水面反射以及菲尼尔//利用_ReflectMixture来控制法线取值,与上一步制作高光的法线值区分float3 N1 = lerp(i.normalWS,normalTex,_ReflectMixture);float3 R = reflect(-V,N1); //反射向量float4 ReflectTex = SAMPLE_TEXTURECUBE(_ReflectTex,sampler_ReflectTex,R);float Fresnel = pow(max(0,1-dot(N,V)),_FresnelIntensity); //菲尼尔float4 Reflect = ReflectTex*Fresnel;//水下的折射扭曲//利用水体面片的世界空间下坐标采样用于扭曲水下的纹理float3 DistortTex = UnpackNormal(SAMPLE_TEXTURE2D(_DistortTex,sampler_DistortTex,i.positionWS.xz*float2(_Refract_X,_Refract_Y)+_Time.y));//偏移值float2 offset = DistortTex.xy*_DistortIntensity;//扭曲UV值float2 DistortUV = ScreenUV + offset; //_DistortIntensity为扭曲强度值//利用扭曲后的屏幕UV采样深度图float DistortDepth = SAMPLE_TEXTURE2D(_CameraDepthTexture,sampler_CameraDepthTexture,DistortUV);//将深度图中的深度信息转换到视图空间DistortDepth = LinearEyeDepth(DistortDepth,_ZBufferParams);//计算深度图和水体面片在视图空间中的深度差(i.positionVS.z为负值,所以两者要相加)float DistDepthDifferent = saturate(DistortDepth+i.positionVS.z);//消除水面以上物体的扭曲,因为水面以上物体的深度值小于i.positionVS.z,所以DistDepthDifferent值为负时,使水面以上物体采样屏幕纹理的UV为屏幕UV(不进行扭曲)if(DistDepthDifferent<=0){DistortUV = ScreenUV;}//最后利用上面计算得到的UV来采样屏幕纹理float4 RefractTex = SAMPLE_TEXTURE2D(_CameraOpaqueTexture,sampler_CameraOpaqueTexture,DistortUV);//水下的焦散//因为焦散纹理需要处于水底,而我们只能使用水体面片来采样焦散纹理。所以可以求出深度图中的像素位置在世界空间下的值,用此值作为UV来采样焦散纹理float4 depthVS = 1;depthVS.z = depthTex;//求深度图中对应点的xy方向上的值depthVS.xy = i.positionVS.xy * depthTex / -i.positionVS.z;//将求得的视图空间下深度图中的位置信息转换到世界空间下float3 depthWS = mul(unity_CameraToWorld,depthVS).xyz;//用深度图中像素的在世界空间下位置作为UV来采样焦散纹理//加depthWS.y的作用是使从上往下看同一个位置上的不同高度的点,在加上不同的高度值,从而使XZ方向的UV值错开从而采样不同的纹理float2 uv1 = (depthWS.xz * _CausticTex_ST.xy + depthWS.y*_OffsetY)-_Time.y*_FlowSpeed;float2 uv2 = (depthWS.xz * _CausticTex_ST.xy + depthWS.y*_OffsetY)-_Time.y*_FlowSpeed*float2(-1.15,0.9);float4 CausticTex01 = SAMPLE_TEXTURE2D(_CausticTex,sampler_CausticTex,uv1);float4 CausticTex02 = SAMPLE_TEXTURE2D(_CausticTex,sampler_CausticTex,uv2);float4 CausticTex = min(CausticTex01,CausticTex02)*_CausticIntensity; //_CausticIntensity为焦散的强度值float4 c;c = WaterColor_M * RefractTex;//将折射扭曲与水体颜色相乘c += Foam;c += Specular*Reflect;c += CausticTex;c = saturate(c);return c;return WaterColor_M;}ENDHLSL}}}

 

屏幕录制 2025-06-02 213020


http://www.hkcw.cn/article/PjuAFRKkCa.shtml

相关文章

Spring之IOC

Spring 一、文档二、依赖注入方式三、注入其他类型3.1 设置null空值:使用null标签3.2 含特殊字符&#xff1a;使用CDATA 四、注入外部bean、内部bean4.1 注入属性中的外部bean4.2 注入属性中的内部bean4.3 注入属性级联赋值 五、注入集合属性5.1 注入集合属性5.2 把集合注入部分…

RAG的ETL Pipeline源码解读

原文链接&#xff1a;SpringAI(GA)&#xff1a;RAG下的ETL源码解读 教程说明 说明&#xff1a;本教程将采用2025年5月20日正式的GA版&#xff0c;给出如下内容 核心功能模块的快速上手教程核心功能模块的源码级解读Spring ai alibaba增强的快速上手教程 源码级解读 版本&a…

github 2FA双重认证丢失解决

文章目录 前言一. 凭借ssh 解锁步骤1.1 要求输入设备码1.2.进入二重验证界面1.3.开始2FA恢复1.4.选择使用ssh验证 二.预防措施2.1 云盘上传git_recover_codes.txt2.2 开启多源FA认证2.2.1 大陆无法使用手机验证码 三.参考资料 前言 场景&#xff1a;没有意识到github recovery …

【Java EE初阶 --- 多线程(初阶)】多线程的实现案例

乐观学习&#xff0c;乐观生活&#xff0c;才能不断前进啊&#xff01;&#xff01;&#xff01; 我的主页&#xff1a;optimistic_chen 我的专栏&#xff1a;c语言 &#xff0c;Java 欢迎大家访问~ 创作不易&#xff0c;大佬们点赞鼓励下吧~ 文章目录 前言单例模式实现单例模式…

BiliNote部署实践

​ 开源地址&#xff1a; https://github.com/JefferyHcool/BiliNote &#x1f680; 快速开始 1. 克隆仓库 git clone https://github.com/JefferyHcool/BiliNote.git cd BiliNote mv .env.example .env2. 启动后端&#xff08;FastAPI&#xff09; cd backend pip insta…

基于回归算法的心理健康预测(EDA + 预测)

心理健康涵盖情感、心理与社会福祉&#xff0c;影响认知、情绪和行为模式&#xff0c;决定压力应对、人际交往及健康决策&#xff0c;且在生命各阶段&#xff08;从童年至成年&#xff09;均至关重要。心理健康与身体健康同为整体健康的核心要素&#xff1a;抑郁会增加糖尿病、…

无他相机:专业摄影,触手可及

在数字摄影时代&#xff0c;手机摄影已成为许多人记录生活、表达创意的重要方式。无他相机正是这样一款专为摄影爱好者设计的相机应用程序&#xff0c;它不仅提供了专业级摄影设备的大部分功能&#xff0c;还通过简洁直观的操作界面&#xff0c;让每一位用户都能轻松上手&#…

Qt/C++编写GB28181服务端工具/绿色版开箱即用/对标wvp-gb28181/实时画面预览/录像回放下载

一、前言说明 使用过不少的gb28181服务端工具&#xff0c;绝大部分都是BS结构的&#xff0c;也就是直接在网页上运行&#xff0c;比如easynvr、liveqing等&#xff0c;也有个知名的开源国标项目叫wvp&#xff0c;总体感觉性能都不如意&#xff0c;理论上来说肯定不如直接CS结构…

ProxyPin抓APK数据包

ProxyPin抓APK数据包&#xff1a; 下载地址&#xff1a;https://github.com/wanghongenpin/proxypin 环境配置&#xff1a; 夜神模拟器&#xff0c;开心消消乐&#xff0c;ProxyPin.apk&#xff0c;ProxyPin.exe 使用步骤&#xff1a; ProxyPin.apk安装https证书&#xff0…

空间智能重塑未来治理

当上海复兴岛的战略空间更新与科创策源功能在时空创新实验基地实现耦合共生&#xff0c;当武汉马拉松的智慧安防系统通过"实景三维城市智眼"构筑起全域智能防线&#xff0c;我们正见证着空间智能技术重构城市治理范式的革命性变革。这场以数据为血脉、算法为神经、模…

FastAPI安全认证:从密码到令牌的魔法之旅

title: FastAPI安全认证:从密码到令牌的魔法之旅 date: 2025/06/02 13:24:43 updated: 2025/06/02 13:24:43 author: cmdragon excerpt: 在FastAPI中实现OAuth2密码流程的认证机制。通过创建令牌端点,用户可以使用用户名和密码获取JWT访问令牌。代码示例展示了如何使用Cry…

Java Script函数

1.认识JS函数 1.1程序中的foo、bar、baz 在学习编程中&#xff0c;你可能会经常看到foo、bar、baz这些名词 它们通常被用来作为函数、变量、文件的名称 目前已经编程了计算机编程的术语一部分 但是它们本身并没有特别的用途和意义 常常被称之为“伪变量”&#xff08;metasynt…

吴恩达MCP课程(5):mcp_chatbot_prompt_resource.py

前提条件&#xff1a; 1、吴恩达MCP课程&#xff08;5&#xff09;&#xff1a;research_server_prompt_resource.py 2、server_config_prompt_resource.json文件 {"mcpServers": {"filesystem": {"command": "npx","args"…

性能测试的概念和场景设计

一、什么是性能 性能的定义&#xff1a;性能是指系统或设备在执行特定任务时的时间效率和资源利用情况。可以从两个主要维度来理解&#xff1a; 时间维度&#xff1a; a、响应时间&#xff1a;指系统从接收用户请求到返回结果所需的时间。 b、吞吐量&#xff1a;指单位时间内…

【Java学习笔记】异常

异常&#xff08;Exception&#xff09; 一、基本介绍 在 Java 程序中&#xff0c;将运行中发生的不正常情况称为 “异常”&#xff0c;开发过程中的语法错误和运行时发生的异常情况是不一样的。 二、异常的分类 1. Error&#xff08;错误&#xff09;&#xff1a;Java 虚拟…

50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | Form Wave(表单label波动效果)

&#x1f4c5; 我们继续 50 个小项目挑战&#xff01;—— FormWave组件 仓库地址&#xff1a;https://github.com/SunACong/50-vue-projects 项目预览地址&#xff1a;https://50-vue-projects.vercel.app/ &#x1f3af; 组件目标 构建一个美观、动态的登录表单&#xff0…

数据库系统概论(十六)数据库安全性(安全标准,控制,视图机制,审计与数据加密)

数据库系统概论&#xff08;十六&#xff09;数据库安全性 前言一、数据库安全性1. 什么是数据库安全性&#xff1f;2. 为何会存在安全问题&#xff1f; 二、安全标准的发展1. 早期的“开拓者”&#xff1a;TCSEC标准2. 走向国际统一&#xff1a;CC标准3. TCSEC和CC标准有什么不…

显示即战略:铁电液晶如何成为 “数字中国” 的 “像素基石”?

一、显示技术&#xff1a;数字时代的核心战略支点 &#xff08;一&#xff09;从 “视觉窗口” 到 “战略基础设施” 在数字经济蓬勃发展的当下&#xff0c;显示技术早已超越了单纯的 “视觉呈现” 范畴&#xff0c;成为连接人与数字世界的关键接口。从智能手机、平板电脑到车…

⚡️ Linux grep 命令参数详解

⚡️ Linux grep 用法及参数详解 &#x1f4d8; 1. grep 简介 grep 是 Linux/Unix 系统中用于文本搜索的命令&#xff0c;其全称为 Global Regular Expression Print&#xff0c;意为全局正则表达式打印器。 它根据给定的 模式&#xff08;pattern&#xff09; 对文件或标准…

Rust 变量与可变性

文章目录 变量与可变性常量遮蔽&#xff08;Shadowing&#xff09; 变量与可变性 Rust中变量默认是不可变的&#xff0c;这是 Rust 鼓励你编写更安全、易于并发代码的众多方式之一。不过&#xff0c;你仍然可以选择让变量可变。让我们来探讨 Rust 为什么鼓励你优先使用不可变性…