最近其实做了好多东西,但是实在是忙啊
没有时间归纳和总结,先把最近做的这个东西拿出来和大家分享
后续逐步把所会的东西一点点分享出来
先放一个效果出来:
法线水最终效果 https://www.zhihu.com/video/1154034073386377216
法线水其实和顶点偏移+曲面细分的波浪水本质是一样的
只是波浪的呈现方式不同,我们可以通过学习法线水的制作方式掌握大致的架构,
然后慢慢升级更多不同的效果
最后,请大家不要只收藏不点赞...
我先把各个要做的功能列出来:
反光波浪 - 法线水顾名思义是用法线做的反光
岸边混合 - 就是根据深度利用地面颜色和水体产生混合,这样会有柔和的边缘混合,所以这里需要用到深度和截屏,
我们这里用Command buffer实现从相机中直接抽取颜色和深度缓冲
岸边波浪 - 这里也是根据深度,叠加一个或者多个波形,产生不断有海水拍打岸边的效果
泡沫 - 但是在某些情况下,是没有波浪的,比如内陆河,那么可能岸边带一点泡沫就够了,所以我们也要支持泡沫
水下折射 - 这个就要利用UV扭曲截屏的图像
基于Command buffer,从相机内抽取深度和颜色 - 大思路是绑定两个Command Buffer到相机上,
设置为全局纹理,是改自一个大哥写的文章,最后我会放出全部代码, 这个方法有一点小问题,
就是在物体比较少的时候,他本身的消耗就比直接取深度大,
因为unity经典管线深度图的获取成本实际上和场景的复杂度有直接关系, 所以也要酌情使用
一些小Trick - 中间会讲一些小Trick,
例如3张跨度比较大的纹理,利用UV缩放让不同距离的过渡更平滑,
一张纹理旋转UV多次采样当多张纹理使用(泡沫),利用插值实现布尔类型的开关
还有一些关于shader格式化的例子
还有哪些可以做的? -
我们这里可以重点说下波浪
法线水其实已经具备了所有的功能
如果我们想要效果更好的水,只需要在这个基础上修改即可,实际上我后面也试了下顶点偏移+曲面细分水,
之后有空在写一篇专门讲高级波浪的文章
顶点偏移+曲面细分水可以尝试用 Gwave波 或者FFT 实现
你只需要去掉法线的部分,改成顶点偏移就好了
而手机上,你根据深度加入一点点顶点偏移,不要曲面细分也是完全的可以的
这样可以做类似"权利游戏"海边波浪起伏的效果
碧蓝航线里那种海面的起伏也差不多可以这样做
而更高级的做法,我觉得最好的是Wave particles, 可以看这篇PPT:
http://advances.realtimerendering.com/s2016/Rendering%20rapids%20in%20Uncharted%204.pptx advances.realtimerendering.com
波浪
法线水嘛,波浪肯定是法线做的,
之后着色因为仅仅是个平面, 所以可以直接用高光+水体颜色
高光可以用Phong实现, 你当然可以用你喜欢的方式实现
但是这里有2个小Trick
首先是缩放,我们用水体的时候,经常会缩放,但是我们并不喜欢水体上的纹理因为缩放拉伸
这个同样作用于制作各种全屏效果,例如迷雾
那我们就可以用这个代码乘以UV来保持缩放比例:
//通过世界到模型空间转换矩阵,拿到基向量的变化,进而得知缩放信息//假如平面被缩放,我们要保证波浪不被跟着拉变形,所以需要这个信息float3 recipObjScale = float3(length(unity_WorldToObject[0].xyz), length(unity_WorldToObject[1].xyz), length(unity_WorldToObject[2].xyz));float3 objScale = 1.0 / recipObjScale;
下面我们先看一张法线图的效果:
左边是着色的, 右边是直接输出法线图的效果,我们想要的表面凹凸已经有了,
但是这样也太直白单调了
所以我们增加2张法线图
得到的结果是这样的:
肯定比上面要好看啦
但是单纯的法线叠加,有个问题,就是在缩放之后,会出现大量的重复感,
例如:
为什么会这样呢?因为我们吧相机拉远之后,tiling的大小并没有变换,
所以我们可以使用插值,根据摄像机距离水面的距离,来缩放uv
结果就可以在远景保持这样的效果:
贴一个完整的代码让大家可以在引擎内看效果,
我加上了详细的注释
之后就不在贴出完整代码了, 完整的代码放到文末
话说知乎的代码编辑器真不好用啊....
法线波浪完整的代码如下:
Shader "Custom/FX/FX-TestNormalWave"
{
Properties
{
[Header(Color)]
_WaterColor("Water Color 浅水区颜色", Color) = (0,0.3411765,0.6235294,1)
_DeepWaterColor("Deep Water Color 深水区颜色", Color) = (0,0.3411765,0.6235294,1)
_DepthTransparency("Depth Transparency 水体透明度", Range(0.5,10)) = 1.5
_Fade("Fade 颜色混合后乘数,控制颜色强度", Range(0.1,5)) = 1
[Space(50)]
[Header(Light)]
_Specular("Specular 高光强度", Range(0, 10)) = 1
_LightWrapping("Light Wrapping", Float) = 0
_Gloss("Gloss 高光范围", Range(0, 1)) = 0.55
[Space(50)]
[Header(Small Waves)]
[NoScaleOffset] _SmallWavesTexture("Small Waves Texture", 2D) = "bump" {}
_SmallWavesTiling("Small Waves Tiling", Float) = 1.5
_SmallWavesSpeed("Small Waves Speed", Float) = 60
_SmallWaveRrefraction("Small Wave Rrefraction", Range(0, 3)) = 1
[Space(50)]
[Header(Medium Waves)]
[NoScaleOffset]_MediumWavesTexture("Medium Waves Texture", 2D) = "bump" {}
_MediumWavesTiling("Medium Waves Tiling", Float) = 3
_MediumWavesSpeed("Medium Waves Speed", Float) = -80
_MediumWaveRefraction("Medium Wave Refraction", Range(0, 3)) = 2
[Space(50)]
[Header(Large Waves)]
[NoScaleOffset]_LargeWavesTexture("Large Waves Texture", 2D) = "bump" {}
_LargeWavesTiling("Large Waves Tiling", Float) = 0.5
_LargeWavesSpeed("Large Waves Speed", Float) = 60
_LargeWaveRefraction("Large Wave Refraction", Range(0, 3)) = 2.5
[Space(50)]
[Header(TilingDistance)]
_MediumTilingDistance("Medium Tiling Distance", Float) = 200
_LongTilingDistance("Long Tiling Distance", Float) = 500
_DistanceTilingFade("Distance Tiling Fade", Float) = 1
}
SubShader
{
Tags {"IgnoreProjector" = "True""Queue" = "Transparent""RenderType" = "Transparent"
}
GrabPass{ "_GrabTexture" }
Pass
{
Tags {"LightMode" = "ForwardBase"
}
Blend SrcAlpha OneMinusSrcAlpha
Cull Off
ZWrite Off
CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"#include "UnityPBSLighting.cginc"#include "UnityStandardBRDF.cginc"#pragma target 3.0
uniform float4 _WaterColor;
uniform float4 _DeepWaterColor;
uniform float _DepthTransparency;
uniform float _Fade;
//自定义全局纹理, 深度和屏幕颜色
//uniform sampler2D_float _LastDepthTexture;
//uniform sampler2D_float _SceneColorTexture;
uniform sampler2D _GrabTexture;
uniform sampler2D_float _CameraDepthTexture;
//波浪相关参数
uniform sampler2D _SmallWavesTexture;
uniform sampler2D _MediumWavesTexture;
uniform sampler2D _LargeWavesTexture;
uniform float _SmallWaveRrefraction;
uniform float _SmallWavesSpeed;
uniform float _SmallWavesTiling;
uniform float _MediumWavesTiling;
uniform float _MediumWavesSpeed;
uniform float _MediumWaveRefraction;
uniform float _LargeWaveRefraction;
uniform float _LargeWavesTiling;
uniform float _LargeWavesSpeed;
//波浪距离
uniform float _MediumTilingDistance;
uniform float _DistanceTilingFade;
uniform float _LongTilingDistance;
//光照
uniform float _Specular;
uniform float _LightWrapping;
uniform float _Gloss;
struct VertexInput
{float4 vertex : POSITION;float3 normal : NORMAL;float4 tangent : TANGENT;float2 texcoord0 : TEXCOORD0;
};
struct Interpolators
{float4 pos : SV_POSITION;float2 uv : TEXCOORD0;float4 posWorld : TEXCOORD1;float3 normalDir : TEXCOORD2;float3 tangentDir : TEXCOORD3;float3 bitangentDir : TEXCOORD4;float4 screenPos : TEXCOORD5;float4 projPos : TEXCOORD6;
};
Interpolators vert (VertexInput v)
{
Interpolators i = (Interpolators)0;
i.uv = v.texcoord0;
i.normalDir = UnityObjectToWorldNormal(v.normal);
i.tangentDir = normalize(mul(unity_ObjectToWorld, float4(v.tangent.xyz, 0.0)).xyz);
i.bitangentDir = normalize(cross(i.normalDir, i.tangentDir) * v.tangent.w);
i.posWorld = mul(unity_ObjectToWorld, v.vertex);
i.pos = UnityObjectToClipPos(v.vertex);
i.projPos = ComputeScreenPos(i.pos);
COMPUTE_EYEDEPTH(i.projPos.z);
i.screenPos = i.pos;return i;
}float3 getWave(sampler2D tex, float3 objScale, Interpolators i,float tiling, float speed, float refraction,float _clampedDistance1, float _clampedDistance2) {
//UV缩放设置float2 scale = objScale.rb*tiling;
//根据时间和速度偏移UVfloat2 _smallWavesPanner = (i.uv + (float3((speed / scale), 0.0) * (_Time.r / 100.0)));float2 wavesUV = _smallWavesPanner * scale;
//采样时, 根据时间偏移量和偏移缩放量采样float3 wavesTex = UnpackNormal(tex2D(tex, wavesUV));
//return wavesTex;
//第二次采样 uv 缩小20倍float3 wavesTex2 = UnpackNormal(tex2D(tex, wavesUV / 20.0));
//类似之前的操作,采样uv被再次缩小,距离也同样float3 wavesTex3 = UnpackNormal(tex2D(tex, wavesUV / 60));
//根据距离利用插值选择合适的UVreturn lerp(float3(0, 0, 1),
lerp(lerp(wavesTex.rgb, wavesTex2.rgb, _clampedDistance1), wavesTex3.rgb, _clampedDistance2),
lerp(lerp(refraction, refraction / 2.0, _clampedDistance1), (refraction / 8), _clampedDistance2));
}
fixed4 frag (Interpolators i) : SV_Target
{
//通过世界到模型空间转换矩阵,拿到基向量的变化,进而得知缩放信息
//假如平面被缩放,我们要保证波浪不被跟着拉变形,所以需要这个信息float3 recipObjScale = float3(length(unity_WorldToObject[0].xyz),
length(unity_WorldToObject[1].xyz), length(unity_WorldToObject[2].xyz));float3 objScale = 1.0 / recipObjScale;#if UNITY_UV_STARTS_AT_TOP // OpenGL 和DX兼容设置float grabSign = -_ProjectionParams.x;#elsefloat grabSign = _ProjectionParams.x;#endif
i.normalDir = normalize(i.normalDir);
i.screenPos = float4(i.screenPos.xy / i.screenPos.w, 0, 0);
i.screenPos.y *= _ProjectionParams.x;
//拿到切线变换,下面合并波浪法线要用float3x3 tangentTransform = float3x3(i.tangentDir, i.bitangentDir, i.normalDir);
//视角方向,就是摄像机方向float3 viewDirection = normalize(_WorldSpaceCameraPos.xyz - i.posWorld.xyz);
//算出距离相机的距离float _distance = distance(i.posWorld.rgb, _WorldSpaceCameraPos);
//如果小于1 这个距离用平方缩小clamp距离float _clampedDistance1 = saturate(pow((_distance / _MediumTilingDistance), _DistanceTilingFade));float _clampedDistance2 = saturate(pow((_distance / _LongTilingDistance), _DistanceTilingFade));
/**小波浪相关设置**/float3 _SmallWaveNormal =
getWave(_SmallWavesTexture, objScale, i, _SmallWavesTiling,
_SmallWavesSpeed, _SmallWaveRrefraction, _clampedDistance1, _clampedDistance2);
/**中级波浪相关设置**/float3 _MediumWaveNormal =
getWave(_MediumWavesTexture, objScale, i,
_MediumWavesTiling, _MediumWavesSpeed, _MediumWaveRefraction, _clampedDistance1, _clampedDistance2);
/**大波浪相关设置**/float3 _LargeWaveNormal =
getWave(_LargeWavesTexture, objScale, i,
_LargeWavesTiling, _LargeWavesSpeed, _LargeWaveRefraction, _clampedDistance1, _clampedDistance2);
//合并波浪运算结果,偏转法线float3 normalWaveLocal = (_SmallWaveNormal + _MediumWaveNormal + _LargeWaveNormal);
//这里可以输出法线看下法线效果
//return float4(normalWaveLocal,1);
//乘以切线空间矩阵,拿到真正的法线float3 normalDirection = normalize(mul(normalWaveLocal, tangentTransform));float3 lightDirection = normalize(_WorldSpaceLightPos0.xyz);float3 lightColor = _LightColor0.rgb;float3 halfDirection = normalize(viewDirection + lightDirection);float attenuation = 1;float3 attenColor = attenuation * _LightColor0.xyz;
// Gloss:float gloss = _Gloss;float specPow = exp2(gloss * 10.0 + 1.0);
// 根据法线算高光Specular:float NdotL = saturate(dot(normalDirection, lightDirection));float3 specularColor = (_Specular*_LightColor0.rgb);float3 directSpecular = attenColor * pow(max(0, dot(halfDirection, normalDirection)), specPow)*specularColor;float3 finalColor = directSpecular+ _WaterColor.rgb;
fixed4 finalRGBA = fixed4(finalColor, 1);return finalRGBA;
}
ENDCG
}
}
}
计算深度 - 实现岸边颜色混合 和 折射
先看下目前的效果:
没有混合,非常生硬
从这里开始我们就开始逐段写出代码
法线完成后,我们就可以考虑根据深度和截屏的颜色来混合岸边
大家大致看下想法,参数我就不贴出来了
/**根据深度和坐标拿到当前的深度差**///屏幕深度float sceneZ = max(0, LinearEyeDepth(UNITY_SAMPLE_DEPTH(tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos)))) - _ProjectionParams.g);float partZ = max(0, i.projPos.z - _ProjectionParams.g);//算出深度差, 这个就可以做很多事,比如岸边,根据深度颜色变化float depthGap = sceneZ - partZ;//深度乘数,用深度差和预设参数运算出一个乘数,方便下面的运算float deepMultiplier = pow(saturate(depthGap / _DepthTransparency), _ShoreFade)*saturate(depthGap / _ShoreTransparency);
然后是折射:
//利用偏移抓取屏幕颜色的截图UV, 实现类似折射的效果float2 sceneUVs = float2(1, grabSign) * i.screenPos.xy * 0.5 + 0.5 + lerp( ((normalWaveLocal.rg*(_MediumWaveRefraction*0.02))*deepMultiplier), float2(0, 0), saturate(pow((distance(i.posWorld.rgb, _WorldSpaceCameraPos) / _RefractionDistance), _RefractionFalloff)));//截屏用在这里float4 sceneColor = tex2D(_GrabTexture, sceneUVs);
然后根据上面深度的内容,我们混合一个岸边的颜色出来:
//根据深度混合Deep color 和 water colorfloat3 _blendWaterColor = saturate(_DeepWaterColor.rgb + sceneColor.rgb * saturate(_Fade - depthGap) * _WaterColor.rgb);
你也可以分辨输出一下看看_blendWaterColor 具体的造型:
最后我们在颜色混合后根据颜色成熟插值一下岸边颜色:
float3 finalColor = directSpecular + _blendWaterColor;fixed4 finalRGBA = fixed4(lerp(sceneColor.rgb, finalColor, deepMultiplier), 1);
此时如果你自己凑了几个参数,你应该实现了这样的效果:
泡沫
泡沫这里有一个小track,就是用一个旋转矩阵,旋转UV可以做出多层叠加一起的效果
和法线贴图一样,我们这里重复采样增加丰富度
然后泡沫的方法如下:
float3 getFoam(Interpolators i, float3 _blendWaterColor,float3 objScale, float depthGap, float3 normalWaveLocal) { //根据波浪法线和屏幕坐标采样, *0.5+0.5是为了归一化 float2 _remap = (i.screenPos.rg + normalWaveLocal.rg)*0.5 + 0.5; float4 _ReflectionTex_var = tex2D(_ReflectionTex, TRANSFORM_TEX(_remap, _ReflectionTex)); float _rotator_ang = 1.5708; float _rotator_spd = 1.0; float _rotator_cos = cos(_rotator_spd*_rotator_ang); float _rotator_sin = sin(_rotator_spd*_rotator_ang); float2 _rotator_piv = float2(0.5, 0.5); //旋转UV, uv * 2D旋转矩阵 float2 _rotator = (mul(i.uv - _rotator_piv, float2x2(_rotator_cos, -_rotator_sin, _rotator_sin, _rotator_cos)) + _rotator_piv); //泡沫贴图的Tiling和物体缩放相乘拿到UV缩放比例 float2 _FoamDivision = objScale.rb*_FoamTiling; //UV根据时间偏移具体量 float3 _foamUVSpeed = (float3(_FoamSpeed / _FoamDivision, 0.0)*(_Time.r / 100.0)); 旋转后的UV + uv偏移量,拿到最终UV float2 _FoamAdd = (_rotator + _foamUVSpeed); UV * 缩放乘数 float2 _foamUV = (_FoamAdd*_FoamDivision); float4 _foamTex1 = tex2D(_FoamTexture, _foamUV); float2 _FoamAdd2 = (i.uv + _foamUVSpeed); float2 _foamUV2 = (_FoamAdd2*_FoamDivision); float4 _foamTex2 = tex2D(_FoamTexture, _foamUV2); float2 _foamUV3 = (_FoamAdd*objScale.rb*_FoamTiling / 3.0); float4 _foamTex3 = tex2D(_FoamTexture, _foamUV3); float2 maxUV = (_FoamAdd2*_foamUV3); float4 _foamTex4 = tex2D(_FoamTexture, maxUV); //根据距离混合泡沫纹理的几种不同的UV float3 blendFoamRGB = lerp((_foamTex1.rgb - _foamTex2.rgb), (_foamTex3.rgb - _foamTex4.rgb), saturate(pow((distance(i.posWorld.rgb, _WorldSpaceCameraPos) / 20), 3))); //去色 float3 foamRGBGray = (dot(blendFoamRGB, float3(0.3, 0.59, 0.11)) - _FoamContrast) / (1.0 - 2 * _FoamContrast); //根据深度混合颜色 float3 foamRGB = foamRGBGray * _FoamColor.rgb *_FoamIntensity; float3 sqrtFoamRGB = (foamRGB*foamRGB); return lerp(_blendWaterColor, sqrtFoamRGB, _FoamVisibility);}
大家可以在最后输出一下泡沫的方法,看下效果
泡沫覆盖了所有的海域,很好,下面我们在海浪上根据深度处理下泡沫的范围就可以做出贴边的海浪了
海浪
海浪我是在网上找一个大佬写的代码,遗憾的时候不太记得是哪里看的了
不过原理也非常简单
用一个纹理作为海浪的范围,然后用一个sin函数不断循环,依旧是两次采样做出叠加的海浪
然后根据深度控制海浪范围海浪
代码如下:
//岸边拍打的海浪相关业务float3 getSurge(Interpolators i,float3 objScale, float depthGap){//缩放UVfloat2 surgeUVScale = objScale.rb / 200;//噪波图fixed4 noiseColor = tex2D(_NoiseTex, i.uv*objScale.rb / 5);//第一个海浪fixed4 surgeColor = tex2D(_SurgeTex, float2(1 - min(_Range, depthGap) / _Range + _SurgeRange * sin(_Time.x*_SurgeSpeed + noiseColor.r*_NoiseRange), 1)*surgeUVScale);surgeColor.rgb *= (1 - (sin(_Time.x*_SurgeSpeed + noiseColor.r*_NoiseRange) + 1) / 2)*noiseColor.r;//第二个海浪fixed4 surgeColor2 = tex2D(_SurgeTex, float2(1 - min(_Range, depthGap) / _Range + _SurgeRange * sin(_Time.x*_SurgeSpeed + _SurgeDelta + noiseColor.r*_NoiseRange) + 0.5, 1)*surgeUVScale);surgeColor2.rgb *= (1 - (sin(_Time.x*_SurgeSpeed + _SurgeDelta + noiseColor.r*_NoiseRange) + 1) / 2)*noiseColor.r;//根据深度控制海浪范围half surgeWave= 1 - min(_Range, depthGap) / _Range;return (surgeColor.rgb + surgeColor2.rgb * _SurgeColor) * surgeWave;}
但是这里还没完, 海浪有两种情况
一种是海边,那就是正常的海浪
但是还有一种是在河流中,那这时候我们只要根据深度输出泡沫而不要海浪
于是我们做一个单选框:
[Toggle]_SurgeType("SurgeType",Float) = 1
在最后颜色混合的时候,根据插值混合下深度或者直接乘波浪
float3 finalColor = directSpecular + _blendWaterColor+ lerp(1-(saturate(depthGap / _FoamBlend)), surgeFinalColor, _SurgeType)* foamColor; //重点在这里
左边是勾上,右边是不勾,不过我适当调整了泡沫的参数,来调整亮度
这样整个海浪我们就都完成了
完整代码和资源
如果你想支持延迟渲染,只要最后改一下各个参数的混合模式就行了
还有比如把float 改成fixed这种事,请大家根据喜好酌情修改吧
我先把相关素材贴上来,当然你可以酌情更换
三张法线:
Shader "Custom/FX/FX-SimpleRiver"{ Properties { [Header(Color)] _WaterColor("Water Color 浅水区颜色", Color) = (0,0.3411765,0.6235294,1) _DeepWaterColor("Deep Water Color 深水区颜色", Color) = (0,0.3411765,0.6235294,1) _DepthTransparency("Depth Transparency 水体透明度", Range(0.5,10)) = 1.5 _Fade("Fade 颜色混合后乘数,控制颜色强度", Range(0.1,5)) = 1 [Space(50)] [Header(Shore)] _ShoreFade("Shore Fade 岸边范围", Float) = 0.3 _ShoreTransparency("Shore Transparency 岸边透明度", Float) = 0.04 [Space(50)] [Header(Light)] _Specular("Specular 高光强度", Range(0, 10)) = 1 _LightWrapping("Light Wrapping", Float) = 0 _Gloss("Gloss 高光范围", Range(0, 1)) = 0.55 [Space(50)] [Header(Small Waves)] [NoScaleOffset] _SmallWavesTexture("Small Waves Texture", 2D) = "bump" {} _SmallWavesTiling("Small Waves Tiling", Float) = 1.5 _SmallWavesSpeed("Small Waves Speed", Float) = 60 _SmallWaveRrefraction("Small Wave Rrefraction", Range(0, 3)) = 1 [Space(50)] [Header(Medium Waves)] [NoScaleOffset]_MediumWavesTexture("Medium Waves Texture", 2D) = "bump" {} _MediumWavesTiling("Medium Waves Tiling", Float) = 3 _MediumWavesSpeed("Medium Waves Speed", Float) = -80 _MediumWaveRefraction("Medium Wave Refraction", Range(0, 3)) = 2 [Space(50)] [Header(Large Waves)] [NoScaleOffset]_LargeWavesTexture("Large Waves Texture", 2D) = "bump" {} _LargeWavesTiling("Large Waves Tiling", Float) = 0.5 _LargeWavesSpeed("Large Waves Speed", Float) = 60 _LargeWaveRefraction("Large Wave Refraction", Range(0, 3)) = 2.5 [Space(50)] [Header(TilingDistance)] _MediumTilingDistance("Medium Tiling Distance", Float) = 200 _LongTilingDistance("Long Tiling Distance", Float) = 500 _DistanceTilingFade("Distance Tiling Fade", Float) = 1 [Space(50)] [Header(Reflections)] _ReflectionIntensity("Reflection Intensity ", Range(0, 1)) = 0.5 [HideInInspector]_ReflectionTex("Reflection Tex", 2D) = "white" {} _RefractionDistance("Refraction Distance", Float) = 10 _RefractionFalloff("Refraction Falloff 水面折射范围", Float) = 1 [Space(50)] [Header(Foam)] //泡沫 [NoScaleOffset]_FoamTexture("Foam Texture", 2D) = "white" {} _FoamTiling("Foam Tiling", Float) = 3 _FoamBlend("Foam Blend", Float) = 0.15 _FoamVisibility("Foam Visibility", Range(0, 1)) = 0.3 _FoamIntensity("Foam Intensity", Float) = 10 _FoamContrast("Foam Contrast", Range(0, 0.5)) = 0.25 _FoamColor("Foam Color", Color) = (0.3823529,0.3879758,0.3879758,1) _FoamSpeed("Foam Speed", Float) = 120 _FoamDistFalloff("Foam Dist. Falloff", Float) = 16 _FoamDistFade("Foam Dist. Fade", Float) = 9.5/**/ //岸边海浪 [Space(50)] [Header(Surge)] [Toggle] _SurgeType("SurgeType",Float) = 1 _Range("Range 海浪的范围", Range(0,5)) = 3 _SurgeColor("Surge Color 海浪颜色", Color) = (0,0.3411765,0.6235294,1) _SurgeTex("Surge Tex 海浪流动贴图", 2D) = "Surge" {} _SurgeSpeed("SurgeSpeed 海浪速度", float) = -12.64 //海浪速度 _SurgeRange("SurgeRange 海浪抖动范围(不是海浪范围)", float) = 0.3 _SurgeDelta("SurgeDelta 对海浪波形压缩", float) = 2.43 _NoiseRange("NoiseRange 噪声扰动半径", float) = 6.43 _NoiseTex("Noise", 2D) = "white" {} //海浪躁波 } SubShader { Tags { "IgnoreProjector" = "True" "Queue" = "Transparent" "RenderType" = "Transparent" } GrabPass{ "_GrabTexture" } Pass { Tags { "LightMode" = "ForwardBase" } Blend SrcAlpha OneMinusSrcAlpha Cull Off ZWrite Off CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" #include "UnityPBSLighting.cginc" #include "UnityStandardBRDF.cginc" #pragma target 3.0 uniform float4 _WaterColor; uniform float4 _DeepWaterColor; uniform float _DepthTransparency; uniform float _Fade; //自定义全局纹理, 深度和屏幕颜色 //uniform sampler2D_float _LastDepthTexture; //uniform sampler2D_float _SceneColorTexture; uniform sampler2D _GrabTexture; uniform sampler2D_float _CameraDepthTexture; //反射相关的 uniform sampler2D _ReflectionTex; uniform float4 _ReflectionTex_ST; uniform float _ReflectionIntensity; uniform fixed _EnableReflections; uniform float _RefractionDistance; uniform float _RefractionFalloff; //波浪相关参数 uniform sampler2D _SmallWavesTexture; uniform sampler2D _MediumWavesTexture; uniform sampler2D _LargeWavesTexture; uniform float _SmallWaveRrefraction; uniform float _SmallWavesSpeed; uniform float _SmallWavesTiling; uniform float _MediumWavesTiling; uniform float _MediumWavesSpeed; uniform float _MediumWaveRefraction; uniform float _LargeWaveRefraction; uniform float _LargeWavesTiling; uniform float _LargeWavesSpeed; //波浪距离 uniform float _MediumTilingDistance; uniform float _DistanceTilingFade; uniform float _LongTilingDistance; //光照 uniform float _Specular; uniform float _LightWrapping; uniform float _Gloss; //岸边 uniform float _ShoreFade; uniform float _ShoreTransparency; //泡沫相关参数 uniform float _FoamBlend; uniform float4 _FoamColor; uniform float _FoamIntensity; uniform float _FoamContrast; uniform sampler2D _FoamTexture; uniform float _FoamSpeed; uniform float _FoamTiling; uniform float _FoamDistFalloff; uniform float _FoamDistFade; uniform float _FoamVisibility; //岸边海浪 uniform float4 _SurgeColor; uniform sampler2D _SurgeTex; uniform float _SurgeSpeed; uniform float _SurgeRange; uniform float _Range; fixed _SurgeDelta; uniform sampler2D _NoiseTex; uniform float _NoiseRange; uniform float _SurgeType; struct VertexInput { float4 vertex : POSITION; float3 normal : NORMAL; float4 tangent : TANGENT; float2 texcoord0 : TEXCOORD0; }; struct Interpolators { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; float4 posWorld : TEXCOORD1; float3 normalDir : TEXCOORD2; float3 tangentDir : TEXCOORD3; float3 bitangentDir : TEXCOORD4; float4 screenPos : TEXCOORD5; float4 projPos : TEXCOORD6; }; Interpolators vert (VertexInput v) { Interpolators i = (Interpolators)0; i.uv = v.texcoord0; i.normalDir = UnityObjectToWorldNormal(v.normal); i.tangentDir = normalize(mul(unity_ObjectToWorld, float4(v.tangent.xyz, 0.0)).xyz); i.bitangentDir = normalize(cross(i.normalDir, i.tangentDir) * v.tangent.w); i.posWorld = mul(unity_ObjectToWorld, v.vertex); i.pos = UnityObjectToClipPos(v.vertex); i.projPos = ComputeScreenPos(i.pos); COMPUTE_EYEDEPTH(i.projPos.z); i.screenPos = i.pos; return i; } float3 getWave(sampler2D tex, float3 objScale, Interpolators i, float tiling, float speed, float refraction, float _clampedDistance1, float _clampedDistance2) { //UV缩放设置 float2 scale = objScale.rb*tiling; //根据时间和速度偏移UV float2 _smallWavesPanner = (i.uv + (float3((speed / scale), 0.0) * (_Time.r / 100.0))); float2 wavesUV = _smallWavesPanner * scale; //采样时, 根据时间偏移量和偏移缩放量采样 float3 wavesTex = UnpackNormal(tex2D(tex, wavesUV)); //第二次采样 uv 缩小20倍 float3 wavesTex2 = UnpackNormal(tex2D(tex, wavesUV / 20.0)); //类似之前的操作,采样uv被再次缩小,距离也同样 float3 wavesTex3 = UnpackNormal(tex2D(tex, wavesUV / 60)); return lerp( float3(0, 0, 1), lerp(lerp(wavesTex.rgb, wavesTex2.rgb, _clampedDistance1), wavesTex3.rgb, _clampedDistance2), lerp(lerp(refraction, refraction / 2.0, _clampedDistance1), (refraction / 8), _clampedDistance2)); } float3 getFoam(Interpolators i, float3 _blendWaterColor,float3 objScale, float depthGap, float3 normalWaveLocal) { //根据波浪法线和屏幕坐标采样, *0.5+0.5是为了归一化 float2 _remap = (i.screenPos.rg + normalWaveLocal.rg)*0.5 + 0.5; float4 _ReflectionTex_var = tex2D(_ReflectionTex, TRANSFORM_TEX(_remap, _ReflectionTex)); float _rotator_ang = 1.5708; float _rotator_spd = 1.0; float _rotator_cos = cos(_rotator_spd*_rotator_ang); float _rotator_sin = sin(_rotator_spd*_rotator_ang); float2 _rotator_piv = float2(0.5, 0.5); //旋转UV, uv * 2D旋转矩阵 float2 _rotator = (mul(i.uv - _rotator_piv, float2x2(_rotator_cos, -_rotator_sin, _rotator_sin, _rotator_cos)) + _rotator_piv); //泡沫贴图的Tiling和物体缩放相乘拿到UV缩放比例 float2 _FoamDivision = objScale.rb*_FoamTiling; //UV根据时间偏移具体量 float3 _foamUVSpeed = (float3(_FoamSpeed / _FoamDivision, 0.0)*(_Time.r / 100.0)); ////旋转后的UV + uv偏移量,拿到最终UV float2 _FoamAdd = (_rotator + _foamUVSpeed); ////UV * 缩放乘数 float2 _foamUV = (_FoamAdd*_FoamDivision); float4 _foamTex1 = tex2D(_FoamTexture, _foamUV); float2 _FoamAdd2 = (i.uv + _foamUVSpeed); float2 _foamUV2 = (_FoamAdd2*_FoamDivision); float4 _foamTex2 = tex2D(_FoamTexture, _foamUV2); float2 _foamUV3 = (_FoamAdd*objScale.rb*_FoamTiling / 3.0); float4 _foamTex3 = tex2D(_FoamTexture, _foamUV3); float2 maxUV = (_FoamAdd2*_foamUV3); float4 _foamTex4 = tex2D(_FoamTexture, maxUV); //根据距离混合泡沫纹理的几种不同的UV float3 blendFoamRGB = lerp((_foamTex1.rgb - _foamTex2.rgb), (_foamTex3.rgb - _foamTex4.rgb), saturate(pow((distance(i.posWorld.rgb, _WorldSpaceCameraPos) / 20), 3))); //去色 float3 foamRGBGray = (dot(blendFoamRGB, float3(0.3, 0.59, 0.11)) - _FoamContrast) / (1.0 - 2 * _FoamContrast); //float depth = (saturate(depthGap / _FoamBlend) - 1.0); //根据深度混合颜色 float3 foamRGB = foamRGBGray * _FoamColor.rgb *_FoamIntensity;// * lerp(1, depth, _FormType) float3 sqrtFoamRGB = (foamRGB*foamRGB); return lerp(_blendWaterColor, sqrtFoamRGB, _FoamVisibility); } //岸边拍打的海浪相关业务 float3 getSurge(Interpolators i,float3 objScale, float depthGap) { //缩放UV float2 surgeUVScale = objScale.rb / 200; //噪波图 fixed4 noiseColor = tex2D(_NoiseTex, i.uv*objScale.rb / 5); //第一个海浪 fixed4 surgeColor = tex2D(_SurgeTex, float2(1 - min(_Range, depthGap) / _Range + _SurgeRange * sin(_Time.x*_SurgeSpeed + noiseColor.r*_NoiseRange), 1)*surgeUVScale); surgeColor.rgb *= (1 - (sin(_Time.x*_SurgeSpeed + noiseColor.r*_NoiseRange) + 1) / 2)*noiseColor.r; //第二个海浪 fixed4 surgeColor2 = tex2D(_SurgeTex, float2(1 - min(_Range, depthGap) / _Range + _SurgeRange * sin(_Time.x*_SurgeSpeed + _SurgeDelta + noiseColor.r*_NoiseRange) + 0.5, 1)*surgeUVScale); surgeColor2.rgb *= (1 - (sin(_Time.x*_SurgeSpeed + _SurgeDelta + noiseColor.r*_NoiseRange) + 1) / 2)*noiseColor.r; //根据深度控制海浪范围 half surgeWave = 1 - min(_Range, depthGap) / _Range; return (surgeColor.rgb + surgeColor2.rgb * _SurgeColor) * surgeWave; } fixed4 frag (Interpolators i) : SV_Target { //通过世界到模型空间转换矩阵,拿到基向量的变化,进而得知缩放信息 //假如平面被缩放,我们要保证波浪不被跟着拉变形,所以需要这个信息 float3 recipObjScale = float3(length(unity_WorldToObject[0].xyz), length(unity_WorldToObject[1].xyz), length(unity_WorldToObject[2].xyz)); float3 objScale = 1.0 / recipObjScale;#if UNITY_UV_STARTS_AT_TOP // OpenGL 和DX兼容设置 float grabSign = -_ProjectionParams.x;#else float grabSign = _ProjectionParams.x;#endif i.normalDir = normalize(i.normalDir); i.screenPos = float4(i.screenPos.xy / i.screenPos.w, 0, 0); i.screenPos.y *= _ProjectionParams.x; //拿到切线变换,下面合并波浪法线要用 float3x3 tangentTransform = float3x3(i.tangentDir, i.bitangentDir, i.normalDir); //视角方向,就是摄像机方向 float3 viewDirection = normalize(_WorldSpaceCameraPos.xyz - i.posWorld.xyz); //算出距离相机的距离 float _distance = distance(i.posWorld.rgb, _WorldSpaceCameraPos); //如果小于1 这个距离用平方缩小clamp距离 float _clampedDistance1 = saturate(pow((_distance / _MediumTilingDistance), _DistanceTilingFade)); float _clampedDistance2 = saturate(pow((_distance / _LongTilingDistance), _DistanceTilingFade)); /**小波浪相关设置**/ float3 _SmallWaveNormal = getWave(_SmallWavesTexture, objScale, i,_SmallWavesTiling, _SmallWavesSpeed, _SmallWaveRrefraction, _clampedDistance1, _clampedDistance2); /**中级波浪相关设置**/ float3 _MediumWaveNormal = getWave(_MediumWavesTexture, objScale, i,_MediumWavesTiling, _MediumWavesSpeed, _MediumWaveRefraction, _clampedDistance1, _clampedDistance2); /**大波浪相关设置**/ float3 _LargeWaveNormal = getWave(_LargeWavesTexture, objScale, i,_LargeWavesTiling, _LargeWavesSpeed, _LargeWaveRefraction, _clampedDistance1, _clampedDistance2); //合并波浪运算结果,偏转法线 float3 normalWaveLocal = (_SmallWaveNormal + _MediumWaveNormal + _LargeWaveNormal); //乘以切线空间矩阵,拿到真正的法线 float3 normalDirection = normalize(mul(normalWaveLocal, tangentTransform)); float3 lightDirection = normalize(_WorldSpaceLightPos0.xyz); float3 lightColor = _LightColor0.rgb; float3 halfDirection = normalize(viewDirection + lightDirection); float attenuation = 1; float3 attenColor = attenuation * _LightColor0.xyz; // Gloss: float gloss = _Gloss; float specPow = exp2(gloss * 10.0 + 1.0); // 根据法线算高光Specular: float NdotL = saturate(dot(normalDirection, lightDirection)); float3 specularColor = (_Specular*_LightColor0.rgb); float3 directSpecular = attenColor * pow(max(0, dot(halfDirection, normalDirection)), specPow)*specularColor; /**根据深度和坐标拿到当前的深度差**/ //屏幕深度 float sceneZ = max(0, LinearEyeDepth(UNITY_SAMPLE_DEPTH(tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos)))) - _ProjectionParams.g); float partZ = max(0, i.projPos.z - _ProjectionParams.g); //深度差可以做很多事,比如岸边,根据深度颜色变化 float depthGap = sceneZ - partZ; float deepMultiplier = pow(saturate(depthGap / _DepthTransparency), _ShoreFade)*saturate(depthGap / _ShoreTransparency); //偏移截屏UV, 实现类似折射的效果 float2 sceneUVs = float2(1, grabSign) * i.screenPos.xy * 0.5 + 0.5 + lerp( ((normalWaveLocal.rg*(_MediumWaveRefraction*0.02))*deepMultiplier), float2(0, 0), saturate(pow((distance(i.posWorld.rgb, _WorldSpaceCameraPos) / _RefractionDistance), _RefractionFalloff))); //截屏用在这里 float4 sceneColor = tex2D(_GrabTexture, sceneUVs); //根据深度混合Deep color 和 water color float3 _blendWaterColor = saturate( _DeepWaterColor.rgb + sceneColor.rgb * saturate(_Fade - depthGap) * _WaterColor.rgb ); //输出一下混合颜色 //return float4(_blendWaterColor,1); /*泡沫*/ float3 foamColor = getFoam(i, _blendWaterColor,objScale, depthGap, normalWaveLocal); /*海浪*/ float3 surgeFinalColor = getSurge(i, objScale, depthGap); //输出一下海浪 //return float4(surgeFinalColor, 1); //输出一下泡沫 //return float4(foamColor,1); float foamDepht = 1 - (saturate(depthGap / _FoamBlend)); //我在这里给了foamDepht一个0.5的乘数用来降低亮度,你可以去掉这个乘数试试效果 //因为海浪和岸边的泡沫需要的亮度值是不同的,海浪需要的更高,为了统一就加了一个0.5 float3 finalColor = directSpecular + _blendWaterColor+ lerp(foamDepht*0.5, surgeFinalColor, _SurgeType)* foamColor; fixed4 finalRGBA = fixed4(lerp(sceneColor.rgb, finalColor, deepMultiplier), 1); return finalRGBA; } ENDCG } }}
CommandBuffer的例子
我之前看一个大佬用CommandBuffer在经典管线下获取深度,来降低DC
BQ哥:unity自定义深度图(drawcall不翻倍使用深度图) zhuanlan.zhihu.com
Unity本身的深度获取方式太2了,当然你要用SRP就省事了,利用MRT就可以
我转念一想...干嘛不把颜色一起拿出来,于是我稍微改了一下,
也象征性的可以支持Post也求教诸位大佬有没有更好的方案
请大家无视Bloom的逗比写法, 比如最后那个:Graphics.Blit(dest,src);
我只是想试试post能不能顺利工作
如果你要应用这个,请把这个脚本挂到相机上
并且注释掉 GrabPass{ "_GrabTexture" }
同时替换水体shader里最上面两个纹理
打开 注释并且替换正文里的纹理引用
//自定义全局纹理, 深度和屏幕颜色uniform sampler2D_float _LastDepthTexture;uniform sampler2D_float _SceneColorTexture;//uniform sampler2D _GrabTexture;//uniform sampler2D_float _CameraDepthTexture;......float sceneZ = max(0, LinearEyeDepth(UNITY_SAMPLE_DEPTH(tex2Dproj(_LastDepthTexture, UNITY_PROJ_COORD(i.projPos)))) - _ProjectionParams.g);......//截屏用在这里float4 sceneColor = tex2D(_SceneColorTexture, sceneUVs);
C#代码如下:
using System.Collections;using System.Collections.Generic;using UnityEngine;using UnityEngine.Profiling;using UnityEngine.Rendering;using UnityEngine.UI;[ExecuteInEditMode][RequireComponent(typeof(Camera))]public class RenderPost2 : MonoBehaviour{ private Camera _Camera; public bool isCustomDepth = true; private void Awake() { _Camera = GetComponent(); //_Camera.depthTextureMode = DepthTextureMode.Depth; //PrintImage.Create(); if (isCustomDepth) { CustomDepth(); } else { _Camera.depthTextureMode = DepthTextureMode.Depth; } //if (PrintImage != null && SourceImage != null) //{ // BlurTexture(SourceImage, _Iterator, _BlurSize, _DownSample, PrintImage); //} } private void OnPreRender() { if (_Camera != null) { //设置各种全局矩阵 Matrix4x4 viewMat = _Camera.worldToCameraMatrix; Matrix4x4 projMat = GL.GetGPUProjectionMatrix(_Camera.projectionMatrix, false); Matrix4x4 viewProjMat = (projMat * viewMat); Shader.SetGlobalMatrix("_ViewProjInv", viewProjMat.inverse); //_width, _height; Shader.SetGlobalFloat("_ScreenWidth", _Camera.pixelWidth); Shader.SetGlobalFloat("_ScreenHight", _Camera.pixelHeight); if (isCustomDepth) { //获取深度 if (colorRT != null && depthRT != null) { _Camera.SetTargetBuffers(colorRT.colorBuffer, depthRT.depthBuffer); } } } } private void Update() { //设置全局纹理 Shader.SetGlobalTexture("_SceneColorTexture", colorTex); Shader.SetGlobalTexture("_LastDepthTexture", depthTex); } private void OnPostRender() { if (isCustomDepth) { //把颜色写回相机目标纹理 if (colorRT != null) { if (getBloomMaterial != null && isBloom) { PostBloom(colorRT); } Graphics.Blit(colorRT, _Camera.targetTexture); } } } #region RenderTexture Depth private CommandBuffer _cbDepth = null; private CommandBuffer _cbColor = null; private RenderTexture depthRT; private RenderTexture colorRT; private RenderTexture depthTex; private RenderTexture colorTex; private void CustomDepth() { //绑定到相机的纹理,要是用延迟渲染 Gbuffer里自带这些信息, 或者SRP里的MRT都可以 depthRT = new RenderTexture(_Camera.pixelWidth, _Camera.pixelHeight, 24, RenderTextureFormat.Depth); depthRT.name = "MainDepthBuffer"; colorRT = new RenderTexture(_Camera.pixelWidth, _Camera.pixelHeight, 24, RenderTextureFormat.RGB111110Float); colorRT.name = "MainColorBuffer"; //最后用下面两个纹理获取具体数据 depthTex = new RenderTexture(_Camera.pixelWidth, _Camera.pixelHeight, 24, RenderTextureFormat.RFloat); depthTex.name = "SceneDepthTex"; colorTex = new RenderTexture(_Camera.pixelWidth, _Camera.pixelHeight, 24, RenderTextureFormat.ARGBFloat); colorTex.name = "SceneColorTex"; //绑定深度CB _cbDepth = new CommandBuffer(); _cbDepth.name = "CommandBuffer_DepthBuffer"; _cbDepth.Blit(depthRT.depthBuffer, depthTex.colorBuffer); _Camera.AddCommandBuffer(CameraEvent.AfterForwardOpaque, _cbDepth); //绑定颜色CB _cbColor = new CommandBuffer(); _cbColor.name = "CommandBuffer_ColorBuffer"; _cbColor.Blit(colorRT.colorBuffer, colorTex.colorBuffer); _Camera.AddCommandBuffer(CameraEvent.AfterForwardOpaque, _cbColor); } #endregion #region Post-Bloom [Header("Post - Bloom")] public bool isBloom; [Range(0, 4)] public int iterations = 3; // Blur spread for each iteration - larger value means more blur [Range(0.2f, 3.0f)] public float blurSpread = 0.6f; [Range(1, 8)] public int bloomDownSample = 2; [Range(0.0f, 4.0f)] public float luminanceThreshold = 0.6f; public Shader bloomShader; private Material bloomMaterial = null; public Material getBloomMaterial { get { bloomMaterial = CheckShaderAndCreateMaterial(bloomShader, bloomMaterial); return bloomMaterial; } } void PostBloom(RenderTexture src) { getBloomMaterial.SetFloat("_LuminanceThreshold", luminanceThreshold); int rtW = src.width / bloomDownSample; int rtH = src.height / bloomDownSample; RenderTexture buffer0 = RenderTexture.GetTemporary(rtW, rtH, 0); buffer0.filterMode = FilterMode.Bilinear; Graphics.Blit(src, buffer0, getBloomMaterial, 0); for (int i = 0; i < iterations; i++) { getBloomMaterial.SetFloat("_BlurSize", 1.0f + i * blurSpread); RenderTexture buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0); // Render the vertical pass Graphics.Blit(buffer0, buffer1, getBloomMaterial, 1); RenderTexture.ReleaseTemporary(buffer0); buffer0 = buffer1; buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0); // Render the horizontal pass Graphics.Blit(buffer0, buffer1, getBloomMaterial, 2); RenderTexture.ReleaseTemporary(buffer0); buffer0 = buffer1; } getBloomMaterial.SetTexture("_Bloom", buffer0); RenderTexture dest = RenderTexture.GetTemporary(src.width, src.height, 0); dest.filterMode = FilterMode.Bilinear; Graphics.Blit(src, dest, getBloomMaterial, 3); Graphics.Blit(dest,src); RenderTexture.ReleaseTemporary(buffer0); RenderTexture.ReleaseTemporary(dest); } #endregion #region util // Called when need to create the material used by this effect protected Material CheckShaderAndCreateMaterial(Shader shader, Material material) { if (shader == null) { return null; } if (shader.isSupported && material && material.shader == shader) return material; if (!shader.isSupported) { return null; } else { material = new Material(shader); material.hideFlags = HideFlags.DontSave; if (material) return material; else return null; } } #endregion}
声明:发布此文是出于传递更多知识以供交流学习之目的。若有来源标注错误或侵犯了您的合法权益,请作者持权属证明与我们联系,我们将及时更正、删除,谢谢。
作者:lingzerg
来源:https://zhuanlan.zhihu.com/p/81672833
More:【微信公众号】 u3dnotes