通过深度图做交互水泡沫
大家好,我是阿赵。
这里做一个有交互效果的水面,物体浸入水面时,会根据物体的形状,有一圈水泡沫的效果,并且水泡沫的形状会跟随这物体变化。由于想做得稍微完整一点,又不想其他效果太强泡沫的风头,所以简单加了一个水面纹理流动的效果,却没有加水的反射高光之类的效果,主要是突出泡沫。
而这篇文章主要想说明的就是怎样获取深度图,和怎样求出模型重叠的部分和重叠的边缘。
一、场景的搭建。
这个场景很简单,只是一个用Cube围起来的水池,然后有一个水面的面片,水里面有一个球和一个Cube立方体。
为了能让最后的泡沫形状变化看得更容易,所以我给球和立方体简单的做了一个动画,球是在水中浮沉,然后立方体是在水面移动。
二、深度图计算
在Unity渲染的过程中,是会产生一张深度图信息的。
通过在Shader里面声明深度图,可以获取得到
//声明获取摄像机深度图
UNITY_DECLARE_DEPTH_TEXTURE(_CameraDepthTexture);
uniform float4 _CameraDepthTexture_TexelSize;
然后因为需要和屏幕渲染的图形深度匹配,所以还需要用屏幕坐标的齐次坐标去采样这张深度图
//计算齐次坐标
float4 screenPosNorm = screenPos / screenPos.w;
screenPosNorm.z = (UNITY_NEAR_CLIP_VALUE >= 0) ? screenPosNorm.z : screenPosNorm.z * 0.5 + 0.5;
//采样深度贴图,由于深度图并不是线性渐变过渡的,所以用LinearEyeDepth转换成线性变化
float screenDepthVal = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, screenPosNorm.xy).r);
由于深度图只需要r通道的信息,所以看起来是红色深浅渐变的一种效果。
忽略了颜色,只看黑白作为深度,会是这样。
说明一下,对于眼睛观察的角度来说,这个深度图是非线性效果,所以不会出现很明显的线性渐变,这里需要使用LinearEyeDepth去把这个深度值转换成线性。
接下来,如果我们用屏幕的齐次坐标和深度相减然后去绝对值,可以求出模型边缘相交的部分
float distanceDepthVal = abs((screenDepthVal - LinearEyeDepth(screenPosNorm.z));
这时候看到黑色的部分就是模型穿插的部分,范围比较大,把整个球和立方体插入水中的部分都看到了。
我们可以通过一个距离值来控制一下黑色的范围:
float distanceDepthVal = abs((screenDepthVal - LinearEyeDepth(screenPosNorm.z)) / (distance));
这时候,就还有刚刚开始相交的小部分地方会有黑色。
由于我们想在边缘叠加东西,所以一般来说会用取反的方法,把黑白翻转,变成这样
然后再给白色的部分叠加泡沫的贴图。我这里就随便给一张噪声图来模拟了泡沫了:
再加上衬托用的水基础波纹的颜色,结果会变成这样:
文章来源:https://www.toymoban.com/news/detail-664695.html
上面计算模型相交的深度渐变的过程,一般成为DepthFade,我把它独立成一个方法,以后有需要就可以复用。其实DepthFade是一种比较常用的技术,所以在ASE之类的编辑器里面,直接就有现成的节点可以用
文章来源地址https://www.toymoban.com/news/detail-664695.html
三、完整Shader:
Shader "azhao/WaterDepth"
{
Properties
{
_distance("distance", Range( 0 , 2)) = 0
_WaterColor("WaterColor", Color) = (0.5471698,0.5471698,0.5471698,0)
_noiseMap("noiseMap", 2D) = "white" {}
_speed("speed", Vector) = (0,0,0,0)
_speed2("speed2", Vector) = (0,0,0,0)
_depthMul("DepthMul", Range( 0 , 2)) = 1
_WaterTex("WaterTex", 2D) = "white" {}
_water1Mul("water1Mul", Range( 0 , 1)) = 0
_water2Mul("water2Mul", Range( 0 , 1)) = 0
_alpha("alpha", Range( 0 , 1)) = 1
}
SubShader
{
Tags { "Queue"="Transparent" }
LOD 100
Blend SrcAlpha OneMinusSrcAlpha
Cull Back
ZWrite Off
Pass
{
Name "Unlit"
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float4 color : COLOR;
float2 uv : TEXCOORD0;
};
struct v2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float4 screenUV : TEXCOORD1;
};
uniform float4 _WaterColor;
uniform sampler2D _WaterTex;
uniform float4 _WaterTex_ST;
uniform float2 _speed;
uniform float _water1Mul;
uniform float2 _speed2;
uniform float _water2Mul;
uniform sampler2D _noiseMap;
uniform float4 _noiseMap_ST;
uniform float _distance;
uniform float _depthMul;
uniform float _alpha;
//声明获取摄像机深度图
UNITY_DECLARE_DEPTH_TEXTURE(_CameraDepthTexture);
uniform float4 _CameraDepthTexture_TexelSize;
//计算相交深度渐变的值
float DepthFade(float4 screenPos, float distance)
{
//计算齐次坐标
float4 screenPosNorm = screenPos / screenPos.w;
screenPosNorm.z = (UNITY_NEAR_CLIP_VALUE >= 0) ? screenPosNorm.z : screenPosNorm.z * 0.5 + 0.5;
//采样深度贴图,由于深度图并不是线性渐变过渡的,所以用LinearEyeDepth转换成线性变化
float screenDepthVal = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, screenPosNorm.xy).r);
//关键的一步,屏幕的齐次坐标的z轴同样转换成线性,然后和深度图的值相减
float distanceDepthVal = abs((screenDepthVal - LinearEyeDepth(screenPosNorm.z)) / (distance));
return distanceDepthVal;
}
v2f vert ( appdata v )
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
//ComputeScreenPos一定要在顶点程序计算
float4 screenPos = ComputeScreenPos(o.pos);
o.screenUV = screenPos;
o.uv = v.uv;
return o;
}
half4 frag (v2f i ) : SV_Target
{
float2 uv_WaterTex = i.uv.xy * _WaterTex_ST.xy + _WaterTex_ST.zw;
float2 uv_noiseMap = i.uv.xy * _noiseMap_ST.xy + _noiseMap_ST.zw;
//输入屏幕UV坐标和距离,计算需要显示的深度值
float distanceDepthVal = DepthFade(i.screenUV, _distance);
//通过深度值计算泡沫显示的范围
float foamVal = (1-saturate(distanceDepthVal)) * _depthMul;
//为了让边缘产生不规则,所以读取了一张噪声图,再乘以上面的泡沫深度值
float4 foamCol = tex2D(_noiseMap, uv_noiseMap) * foamVal;
//为了demo看起来稍微完整而加的2个水波纹叠加,不是重点,不用在意
float4 waveCol1 = tex2D(_WaterTex, (uv_WaterTex + (_speed * _Time.y))) * _water1Mul;
float4 waveCol2 = tex2D(_WaterTex, (uv_WaterTex + (_speed2 * _Time.y)))* _water2Mul;
//最终颜色是水波纹+泡沫
float3 finalRGB = (_WaterColor*(waveCol1 + waveCol2) + foamCol).rgb;
finalRGB = clamp(finalRGB, float3( 0,0,0 ) , float3( 1,1,1 ) );
half4 finalCol =half4( finalRGB, _alpha);
return finalCol;
}
ENDCG
}
}
}
到了这里,关于Unity通过深度图做有交互效果的水泡沫的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!