效果:
网格脚本生成的,参考:Unity Shader - Noise 噪点图 - 实现简单山脉
参考:Unity Shader - 使用Noise噪点图生成简单山脉(使用tex2Dlod控制顶点高度)
我使用了:大波浪与小波浪的叠加
float centerH = sin(_Time.y * _BigWaveLen + v.uv.x + v.uv.y) * _BigWaveAmplitude;
centerH += tex2Dlod(_MainTex, float4(v.uv + float2(_Time.x * _SmallWaveSpeedX, _Time.x * _SmallWaveSpeedY), 0, 0)).r * _SmallWaveAmplitude;
v.vertex.y = centerH;
着了个色调,并对uv滚动动画
fixed4 col = tex2D(_MainTex, i.uv + float2(_Time.y * _BigWaveLen * _UVSpeed, 0));
col.rgb *= _MainColor.rgb;
这个参考了unity内置的shader中,处理软粒子的思路:。
buffViewZ
。fragViewZ
。delta = buffViewZ - fragViewZ
的深度插值,来控制水体的alpha透视,或lerp的颜色过渡。combined.rgb + _ShallowColor.rgb * (_ShallowColor.a * 2)
)到浅水着色变量的颜色来过渡(combined.rgb
)。combined.rgb = lerp(combined.rgb + _ShallowColor.rgb * (_ShallowColor.a * 2), combined.rgb, fade);
_ShallowToDeepTex
的纹理然后:var shallowToDeepCol = tex2D(_ShallowToDeepTex, i.uv);
combined.rgb = lerp(combined.rgb + shallowToDeepCol.rgb * (shallowToDeepCol.a * 2), combined.rgb, fade);
下面是参考的:unity内置的shader软粒子代码:
// Soft particles fragment function
#if defined(SOFTPARTICLES_ON) && defined(_FADING_ON)
#define fragSoftParticles(i) \
if (SOFT_PARTICLE_NEAR_FADE > 0.0 || SOFT_PARTICLE_INV_FADE_DISTANCE > 0.0) \
{ \
float sceneZ = LinearEyeDepth (SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.projectedPosition))); \
float fade = saturate (SOFT_PARTICLE_INV_FADE_DISTANCE * ((sceneZ - SOFT_PARTICLE_NEAR_FADE) - i.projectedPosition.z)); \
ALBEDO_MUL *= fade; \
}
#else
#define fragSoftParticles(i)
#endif
下面我们的实现代码
// vert
#if DEEP_EFF
o.projPos = ComputeScreenPos (o.pos);
COMPUTE_EYEDEPTH(o.projPos.z);
#endif
// frag
#if DEEP_EFF // 水深效果
// 深度效果
// 这里参考unity内置的shader中,实现软例子:Soft particle的写法
// 与背景深度远时,alpha比较高,里的近则alha低
// 先是获取深度图的view space下的z值: buffViewZ
float buffViewZ = LinearEyeD、epth (SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos)));
// 再是获取片段的view space的z值:fragViewZ
float fragViewZ = i.projPos.z;
// 然后去他们之间的距离值做alpha的控制,与颜色lerp的变化
float fade = saturate ((buffViewZ-fragViewZ) * _DeepFactor);
combined.rgb = lerp(combined.rgb + _ShallowColor.rgb * (_ShallowColor.a * 2), combined.rgb, fade);
combined.a *= fade;
#endif
材质中暴露了一个DeepFactor
参数,可以用于控制水深的系数。如下:
也暴露了一个浅水的颜色色调着色:
这个就是传统的经验光照模型:ambient + diffuse(HalfLambert) + specular(blinn phong)
// frag
#if LIGHTING_ON // 光照效果
// ambient
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
// diffuse
half3 L = normalize(_WorldSpaceLightPos0.xyz);
half3 N = normalize(i.normal);
half LdotN = dot(L, N) * 0.5 + 0.5;
fixed3 diffuse = col.rgb * LdotN;
// specular
half3 specular = 0;
half3 V = normalize(_WorldSpaceCameraPos.xyz - i.wPos);
half3 H = normalize(L + V);
half HdotN = max(0, dot(H, N)); // blinn-phone
specular = _LightColor0.rgb * pow(HdotN, _SpecularGlossy * 100) * _SpecularIntensity;
// combined color
fixed4 combined = fixed4(ambient + diffuse + specular, col.a);
#else // LIGHTING_OFF
fixed4 combined = col;
#endif
参考:Unity Shader - 简单山脉 - 顶点着色器重构法线,这里不说了,参考的文章说得很清楚的。
#if REBUILD_NORMAL // 重构法线,参考:https://blog.csdn.net/linjf520/article/details/104859710
// reconstruct normals
// 这个4x4数据也可以通过外部传入,可以节省顶点着色器ALU的计算量与L1 caches缓存量。
const float4x4 offset_xz = {
{+1,+0, /* gap **/ +1,-1}, // 右下
{+0,-1, /* gap **/ -1,-1}, // 左下
{-1,+0, /* gap **/ -1,+1}, // 左上
{+0,+1, /* gap **/ +1,+1} // 右上
};
// 默认向量也可以外部传入,因为上面的默认法线是可以调整的
// 下面我讲默认法线初始化为:up
float3 sumNormal = float3(0, 1, 0);
float4 uv4 = 0;
for (int i = 0; i < 4; i++) {
// 获取偏移数据
float4 uvs_offset = offset_xz[i];
// 获取偏移数据分别的高度
half h1 =
sin(_Time.y * _BigWaveLen + v.uv.x + uvs_offset.x * _MainTex_TexelSize.x + v.uv.y + uvs_offset.y * _MainTex_TexelSize.y) * _BigWaveAmplitude;
uv4 = float4(v.uv + uvs_offset.xy * _MainTex_TexelSize.xy + float2(_Time.x * _SmallWaveSpeedX, _Time.x * _SmallWaveSpeedY), 0, 0);
h1 += tex2Dlod(_MainTex, uv4).r * _SmallWaveAmplitude;
half h2 = sin(_Time.y * _BigWaveLen + v.uv.x + uvs_offset.z * _MainTex_TexelSize.x + v.uv.y + uvs_offset.w * _MainTex_TexelSize.y) * _BigWaveAmplitude;
uv4 = float4(v.uv + uvs_offset.zw * _MainTex_TexelSize.xy + float2(_Time.x * _SmallWaveSpeedX, _Time.x * _SmallWaveSpeedY), 0, 0);
h2 += tex2Dlod(_MainTex, uv4).r * _SmallWaveAmplitude;
// 根据偏移方向,高度重构从当前顶点,指向附近偏移点的高度两个向量
float3 dir1 = float3(uvs_offset.x * _GridGap, h1 - centerH, uvs_offset.y * _GridGap);
float3 dir2 = float3(uvs_offset.z * _GridGap, h2 - centerH, uvs_offset.w * _GridGap);
// 根据平面两个向量(两个向量可以确定一个平面,如同:TBN当中的TB两个切向量)
// 叉乘得到平面的法线向量
float3 newNormal = (cross(dir1, dir2));
// 累加到混合向量中
sumNormal += newNormal;
}
// 均值混合
sumNormal /= 5;
o.normal = UnityObjectToWorldNormal(sumNormal);
#else // REBUILD_NORMAL off
o.normal = UnityObjectToWorldNormal(v.normal);
#endif
unity底层在处理:相机的正交与透视投影模式下,分别对_CameraDepthTexture有不一样的编码过程(如果你使用的是_CameraDepthNormalsTexture就没这个问题),具体,可以参考我之前写过的一篇文章:Unity Shader - 获取BuiltIn深度纹理和自定义深度纹理的数据-只看:注意Unity正交相机中的深度纹理的编码 部分就好了。
因为我们使用的是_CameraDepthTexture,所以我们需要处理一下读取深度值的逻辑:
// frag
// jave.lin 2020.03.15
#if DEEP_EFF || FOAM
// unity_OrthoParams变量在:UnityShaderVariables.cginc有定义:
// x = orthographic camera's width
// y = orthographic camera's height
// z = unused
// w = 1.0 if camera is ortho, 0.0 if perspective
// float4 unity_OrthoParams;
float linearEyeDepth = 0;
float linear01Depth = 0;
// ortho - 处理正交的
if (unity_OrthoParams.w == 1) {
float depth = SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos));
#if defined(UNITY_REVERSED_Z) // 正交需要处理这个宏定义,透视不用,估计后面unity版本升级后会处理正交的这个宏定义处理吧
depth = 1 - depth;
#endif
/* Linear01Depth的逆运算
// Z buffer to linear 0..1 depth
inline float Linear01Depth( float z )
{
return 1.0 / (_ZBufferParams.x * z + _ZBufferParams.y);
}
Linear01Depth = 1.0 / (_ZBufferParams.x * z + _ZBufferParams.y);
(_ZBufferParams.x * z + _ZBufferParams.y) * Linear01Depth = 1
(_ZBufferParams.x * z + _ZBufferParams.y) = 1/Linear01Depth
(_ZBufferParams.x * z) = 1/Linear01Depth - _ZBufferParams.y
z = (1/Linear01Depth - _ZBufferParams.y) / _ZBufferParams.x
Linear01Depth == ndcZ
z = (1/ndcZ - _ZBufferParams.y) / _ZBufferParams.x
*/
linearEyeDepth = LinearEyeDepth (/*to ndcZ**/(1.0/depth - _ZBufferParams.y) /_ZBufferParams.x);
linear01Depth = (depth);
} else { // perspective - 处理透视的
float depth = SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos));
linearEyeDepth = LinearEyeDepth (depth);
linear01Depth = Linear01Depth(depth);
}
#endif
#if DEEP_EFF // 水深效果
// 深度效果
// 这里参考unity内置的shader中,实现软例子:Soft particle的写法
// 与背景深度远时,alpha比较高,里的近则alha低
// 先是获取深度图的view space下的z值: buffViewZ
// float buffViewZ = LinearEyeDepth (SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos)));
float buffViewZ = linearEyeDepth;
// 再是获取片段的view space的z值:fragViewZ
float fragViewZ = i.projPos.z;
// 然后去他们之间的距离值做alpha的控制,与颜色lerp的变化
float fade = saturate ((buffViewZ-fragViewZ) * _DeepFactor);
combined.rgb = lerp(combined.rgb + _ShallowColor.rgb * (_ShallowColor.a * 2), combined.rgb, fade);
combined.a *= fade;
#endif
注意上面代码中,i.projPos.z
,可以改写为使用i.vertex.w
即可,因为在project matrix中,m[4,3] == -1
,就将model view space下的pos.w=-pos.z
了。
正交下图效果:
后面有空再处理
backup : UnityShader_SimpleWater_ShallowToDeepEffect_2018.3.0f2