参考:https://blog.csdn.net/u010848412/article/details/73520191
LinearEyeDepth:
// Z buffer to linear depth
inline float LinearEyeDepth( float z )
{return 1.0 / (_ZBufferParams.z * z + _ZBufferParams.w);}
LinearEyeDepth 负责把深度纹理的采样结果转换到视角空间下的深度值,也 就是我们上面得到的Z(visw)。而 Linear01Depth 则会返回一个范围在[0, 1]的线性深度值,也就是我们上面得到的Z(01),这两个函数内部使用了内置的_ZBufferParams变量来得到远近裁剪平面的距离。
_ZBufferParams
其中_ZBufferParams的定义如下:
double zc0, zc1;
// OpenGL would be this:
// zc0 = (1.0 - m_FarClip / m_NearClip) / 2.0;
// zc1 = (1.0 + m_FarClip / m_NearClip) / 2.0;
// D3D is this:
zc0 = 1.0 - m_FarClip / m_NearClip;// m_FarClip远裁剪平面
zc1 = m_FarClip / m_NearClip;
// now set _ZBufferParams with (zc0, zc1, zc0/m_FarClip, zc1/m_FarClip);
获取深度纹理:在脚本中设置摄像机的depthTextureMode
当在Shader中访问到深度纹理_CameraDepthTexture 后,我们就可以使用当前像素的纹理坐标对它进行采样。绝大多数情况下,我们直接使用tex2D函数采样即可,但在某些平台上,我们需要一些特殊处理。Unity为我们提供了一个统一的宏SAMPLE_DEPTH_TEXTURE,用来处理这些由于平台差异造成的问题。而我们只需要在Shader中使用SAMPLE_DEPTH_TEXTURE宏对深度纹理进行采样,例如:
其中,i.uv 是一个float2类型的变量,对应了当前像素的纹理坐标。类似的宏还有SAMPLE_DEPTH_TEXTURE_PROJ 和 SAMPLE_DEPTH_TEXTURE_LOD。SAMPLE_DEPTH_TEXTURE_PROJ 宏同样接受两个参数——深度纹理和一个float3或float4类型的纹理坐标,它的内部使用了tex2Dproj这样的函数进行投影纹理采样,纹理坐标的前两个分量首先会除以最后一个分量,再进行纹理采样。如果提供了第四个分量,还会进行一次比较, 通常用于阴影的实现中。SAMPLE_DEPTH_TEXTURE_PROJ 的第二个参数通常是由顶点着色器输出插值而得的屏幕坐标,例如:
其中,i.srcPos 是在顶点着色器中通过调用ComputeScreenPos(o.pos)得到的屏幕坐标。上述这些宏,可以在Unity内置的HLSLSupport.cginc文件中找到。
当通过纹理采样得到深度值后,这些深度值往往是非线性的,这种非线性来自于透视投影使用的裁剪矩阵。
然而,在我们的计算过程中通常是需要线性的深度值,也就是说,我们需要把投影后的深度值变换到线性空间下,例如视角空间下的深度值。
如何由深度纹理中的深度信息计算得到视角空间下的深度值:
我们之前已知,当我们使用透视投影的裁剪矩阵P(clip)对视角空间下的一个顶点进行变换后,裁剪空间下顶点的z和w分量为:
其中,Far 和 Near 分别是远近裁剪平面的距离。然后,我们通过齐次除法就可以得到NDC下的z分量:
之前我们知道,深度纹理中的深度值是 通过下面的公式由NDC计算而得的:
由上面的这些式子,我们可以推导出用d表示而得的Z(visw)的表达式:
由于在Unity使用的视角空间中,摄像机正向对应的z值均为负值,因此为了得到深度值的正数表示,我们需要对上面的结果取反,最后得到的结果如下:
它的取值范围就是视锥体深度范围,即[Near,Far]。如果我们想要得到范围在[0, 1]之间的深度值,只需要把上面得到的结果除以Far即可。这样,0就表示该点与摄像机位于同一位置,1表示该点位于视锥体的远裁剪平面上。结果如下:
Shader "Unlit/UnlitWater"
{
Properties
{
_FoamTex("泡沫贴图(R:海浪泡沫,G:岸边泡沫,B:海浪扰动)", 2D) = "white" {}//
[Normal]_NormalTex("法线贴图", 2D) = "bump" {}//
_WaveTex("海浪贴图(R:海浪遮罩,G:海浪渐变)", 2D) = "white" {}//整体贴图
_Gradient("海水颜色渐变", 2D) = "white" {}//设置海水颜色越接近陆地越发白
_Sky("反射天空盒", cube) = "" {}
[Space]
_WaveParams ("海浪参数(x:海浪范围,y:海浪偏移,z:海浪扰动,w:浪花泡沫扰动)", vector) = (0,0,0,0)
_FoamParams("岸边泡沫参数(x:淡入,y:淡出,z:宽度,w:透明度)", vector) = (0,0,0,0)
_Speed("速度参数(x:风速,y:海浪速度)", vector) = (0,0,0,0)
[Space]
_NormalScale ("法线缩放", range(0, 1)) = 1
_Fresnel("菲涅尔系数", float) = 0
_Specular("Specular", float) = 0
_Gloss("Gloss", float) = 0
_FoamColor ("泡沫颜色", color) = (1,1,1,1)
_SpecColor ("高光颜色", color) = (0.4,0.4,0.4,1)
_LightDir("光照方向", vector) = (0, 0, 0, 0)
}
SubShader
{
Tags { "RenderType" = "Transparent" "Queue" = "Transparent" "IgnoreProjector" = "true" }
LOD 100
Pass
{
blend srcalpha oneminussrcalpha
zwrite off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fog
#pragma target 3.0
#include "UnityCG.cginc"
struct v2f
{
float2 uv_FoamTex : TEXCOORD0;
float2 uv_NormalTex : TEXCOORD1;
UNITY_FOG_COORDS(2)
float4 TW0:TEXCOORD3;
float4 TW1:TEXCOORD4;
float4 TW2:TEXCOORD5;
float4 vertex : SV_POSITION;
float4 color : COLOR;
};
sampler2D _FoamTex;
float4 _FoamTex_ST;
sampler2D _WaveTex;
half4 _Speed;
fixed4 _WaveParams;
half _NormalScale;
half4 _FoamParams;
sampler2D _Gradient;
sampler2D _NormalTex;
float4 _NormalTex_ST;
half _Fresnel;
samplerCUBE _Sky;
half _Specular;
fixed _Gloss;
half4 _LightDir;
half4 _SpecColor;
fixed4 _FoamColor;
v2f vert (appdata_full v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv_FoamTex = TRANSFORM_TEX(v.texcoord, _FoamTex);//海浪顶点
o.uv_NormalTex = TRANSFORM_TEX(v.texcoord, _NormalTex);//法线顶点
//o.uv_WaveTex = TRANSFORM_TEX(v.texcoord, _WaveTex);
UNITY_TRANSFER_FOG(o,o.vertex);
float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);
fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);
fixed tangentSign = v.tangent.w * unity_WorldTransformParams.w;
fixed3 worldBinormal = cross(worldNormal, worldTangent) * tangentSign;
o.TW0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);
o.TW1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);
o.TW2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);
o.color = v.color;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
//采样法线贴图
fixed4 normalCol = (tex2D(_NormalTex, i.uv_NormalTex + fixed2(_Time.x*_Speed.x, 0)) + tex2D(_NormalTex, fixed2(_Time.x*_Speed.x + i.uv_NormalTex.y, i.uv_NormalTex.x))) / 2;
half3 worldNormal = UnpackNormal(normalCol);
//泡沫使用法线贴图的rg进行扰动
half3 foam = tex2D(_FoamTex, i.uv_FoamTex +worldNormal.xy*_WaveParams.w).rgb;
worldNormal = lerp(half3(0, 0, 1), worldNormal, _NormalScale);
worldNormal = normalize(fixed3(dot(i.TW0.xyz, worldNormal), dot(i.TW1.xyz, worldNormal), dot(i.TW2.xyz, worldNormal)));
//根据顶点颜色r通道采样海水渐变
fixed4 col = tex2D(_Gradient, float2(i.color.r, 0.5));
//采样反射天空盒
half3 worldPos = half3(i.TW0.w, i.TW1.w, i.TW2.w);
half3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));
half3 refl = reflect(-viewDir, worldNormal);
half vdn = saturate(pow(dot(viewDir, worldNormal), _Fresnel));
col.rgb = lerp(texCUBE(_Sky, refl), col.rgb, vdn);
//计算海浪和岸边泡沫
fixed wave1 = tex2D(_WaveTex, float2(i.color.r + _WaveParams.y + _WaveParams.x*sin(_Time.x*_Speed.y + _WaveParams.z*foam.b), 0)).r;
fixed wave2 = tex2D(_WaveTex, float2(i.color.r + _WaveParams.y + _WaveParams.x*cos(_Time.x*_Speed.y + _WaveParams.z*foam.b), 0)).r;
fixed waveAlpha = tex2D(_WaveTex, float2(i.color.r, 0)).g;
fixed sfadein = 1 - saturate((_FoamParams.x - i.color.r) / _FoamParams.x);
fixed sfadeout = 1 - saturate((i.color.r - _FoamParams.y) / _FoamParams.z);
col+= (_FoamColor - col)* (wave1 + wave2)*waveAlpha*foam.r*i.color.b;
col += (_FoamColor - col)* sfadein*sfadeout *_FoamParams.w*foam.g*i.color.g;
//计算高光
half3 h = normalize(viewDir - normalize(_LightDir.xyz));
fixed ndh = max(0, dot(worldNormal, h));
col += _Gloss*pow(ndh, _Specular*128.0)*_SpecColor;
// apply fog
UNITY_APPLY_FOG(i.fogCoord, col);
//应用顶点透明度
col.a *= i.color.a;
return col;
}
ENDCG
}
}
}
UNITY_TRANSFER_FOG宏的作用是从顶点着色输出雾数据。
lerp(x,y,s) Returns x + s(y - x).