shader海水笔记

参考: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

  1. camera.depthTextureMode = DepthTextureMode.Depth;  
在Shader中通过声明_CameraDepthTexture变量来访问它。

  1. camera.depthTextureMode = DepthTextureMode.DepthNormals;  

当在Shader中访问到深度纹理_CameraDepthTexture 后,我们就可以使用当前像素的纹理坐标对它进行采样。绝大多数情况下,我们直接使用tex2D函数采样即可,但在某些平台上,我们需要一些特殊处理。Unity为我们提供了一个统一的宏SAMPLE_DEPTH_TEXTURE,用来处理这些由于平台差异造成的问题。而我们只需要在Shader中使用SAMPLE_DEPTH_TEXTURE宏对深度纹理进行采样,例如:

  1. float d = SMAPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv);  

其中,i.uv 是一个float2类型的变量,对应了当前像素的纹理坐标。类似的宏还有SAMPLE_DEPTH_TEXTURE_PROJ 和 SAMPLE_DEPTH_TEXTURE_LODSAMPLE_DEPTH_TEXTURE_PROJ 宏同样接受两个参数——深度纹理和一个float3或float4类型的纹理坐标,它的内部使用了tex2Dproj这样的函数进行投影纹理采样,纹理坐标的前两个分量首先会除以最后一个分量,再进行纹理采样。如果提供了第四个分量,还会进行一次比较, 通常用于阴影的实现中。SAMPLE_DEPTH_TEXTURE_PROJ 的第二个参数通常是由顶点着色器输出插值而得的屏幕坐标,例如:

  1. float d = SMAPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.srcPos));  

其中,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
		}
	}
}


  • Standard Surface Shader标准表面着色器
  • Unlit Shader 无灯光着色器
  • Image Effect Shader 图像特效着色器 

UNITY_TRANSFER_FOG宏的作用是从顶点着色输出雾数据。

lerp(x,y,s)  Returns x + s(y - x).

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值