Unity Water Shader

上图是一个物体浸入水中的效果
原理
我们使用相机渲染的整个场景的深度图减去需要忽略的模型的深度,这里忽略的是图中蓝色部分,就保留了其他的深度值。
用到Main Camera渲染的深度贴图: sampler2D _CameraDepthTexture; //在shader中声明,需要设置相机开启渲染深度图;深度图中记录的深度值:深度纹理中每个像素所记录的深度值是从0 到1 非线性分布的。精度通常是 24 位或16 位,这主要取决于所使用的深度缓冲区。当读取深度纹理时,我们可以得到一个0-1范围内的高精度值。如果你需要获取到达相机的距离或者其他线性关系的值,那么你需要手动计算它。
在顶点着色器中获取顶点在屏幕空间的位置,用做采样深度图的uv坐标:
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.scrPos = ComputeScreenPos(o.pos);//将返回片段着色器的屏幕位置
COMPUTE_EYEDEPTH(o.scrPos.z);//计算顶点摄像机空间的深度:距离裁剪平面的距离,线性变化;
在像素着色器中采样Main Camera渲染的深度贴图(摄像机能看到的整个场景的深度图):
//计算当前像素深度
float depth= tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(i.scrPos)).r;//深度值 [0,1]
depth = LinearEyeDepth(depth);//深度根据相机的裁剪范围的值[0.3,1000],是将经过透视投影变换的深度值还原了
将此图的值构建出灰度图,输出查看深度图效果: return float4(depth-999,depth-999,depth-999,); 可以看出深度图中,没有被遮挡的部分并没有表现出线性渐变,说明区域的值被默认填充为最大的深度,我并没有找到相关的文件说明,有朋友找到,麻烦私信下。

现在我们测试下顶点在屏幕空间的深度值(仅仅是计算需要计算的模型顶点的深度,与摄像机的深度图有区别),该值被顶点着色器记录在o.srcPos.z中,将值在像素着色器中输出,查看效果: return float4(i.scrPos.z,i.scrPos.z,i.scrPos.z,);

(上图)想象沿着摄像机的方向,该值呈现出线性分布,说明是该点到近裁减面(以近裁剪面)的距离,假如摄像机裁剪空间是[0.3,10],那么该值在大于等于1的时候呈现出白色,在[0,1]区间内是灰度图。
我们将顶点的深度值作为通道输出: return float4(i.scrPos.z,i.scrPos.z,i.scrPos.z,i.scrPos.z);
Tags {"Queue" = "Transparent"}
Blend SrcAlpha OneMinusSrcAlpha

这个样子,我们就实现根据摄像机与顶点之间的深度值变化,实现与视角有关的透明效果,深度值在[0,1]范围内,则会透明,大于1则会呈现出材质颜色;
现在我们将我们转化过的深度图深度值(线性分布)与顶点深度相减: depth -= i.scrPos.z;

为什么其他地方白色相减不等于黑色?

个人认为:在深度图中,没有被遮挡的地方被默认填充为最大深度1,转化为距离相机的线性距离时,也被转化为最大距离,例1000。这个值再减去模型的深度时,结果远大于模型深度,用颜色显示出来,即为白色。
现在将深度值输出到像素着色器的rgba通道:查看效果: return float4(depth,depth,depth, depth); 与模型相交部分变成了透明。

接下来,我们使用变量ColorDepth控制深度值,并增加2个颜色,来表现深度的范围:
Properties
{ _Color0("Water Color",Color) = (,,,)//水的颜色
_Color1("Water Depth",Color) = (,,,)//水的深度的颜色
_Alpha("Alpha",Range( ,))= //水面的正题透明度
_ColorDepth("ColorDepth",Range( ,))= //水的深度
}
//计算水的透明度: 使用深度值
float alpha = saturate( _Alpha*depth); //计算颜色深度:
float colDepth = saturate(_ColorDepth*depth);
colDepth = -colDepth;
colDepth = lerp(colDepth, colDepth*colDepth*colDepth, 0.5);//调整深度,看个人喜好 half3 col;
col.rgb = lerp(_Color0.rgb, _Color1.rgb, colDepth); return float4(col.rgb, alpha );

在物体遮挡处,蓝色越深,表示越深,天蓝色,是海水颜色。现在除了物体遮挡处的深度表现正确,其他地方审定其实不正确。我们在水下加一块地皮:

是不是有感觉了,我们再调节下颜色等参数:

源代码:
Shader "JQM/DepthTest_1"
{
Properties
{ _Color0("Water Color",Color) = (,,,)//水的颜色
_Color1("Water Depth",Color) = (,,,)//水的深度的颜色
_Alpha("Alpha",Range( ,))= //水面的正题透明度
_ColorDepth("ColorDepth",Range( ,))= //水的深度
}
SubShader
{
Tags {"Queue" = "Transparent"} Pass
{
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag #include "UnityCG.cginc" struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
}; struct VertexOutput
{
float2 uv : TEXCOORD0;
float4 pos : SV_POSITION;
float4 scrPos : TEXCOORD1;
}; float4 _Color0;
float4 _Color1;
float _Alpha;//水的透明度
float _ColorDepth; sampler2D _CameraDepthTexture; VertexOutput vert (appdata v)
{
VertexOutput o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.scrPos = ComputeScreenPos(o.pos);//将返回片段着色器的屏幕位置
COMPUTE_EYEDEPTH(o.scrPos.z);//计算顶点摄像机空间的深度:距离裁剪平面的距离
return o;
} fixed4 frag (VertexOutput i) : COLOR
{
//计算当前像素深度
float depth= tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(i.scrPos)).r;//UNITY_PROJ_COORD:深度值 [0,1]
depth = LinearEyeDepth(depth);//深度根据相机的裁剪范围的值[0.3,1000],是将经过透视投影变换的深度值还原了
depth -= i.scrPos.z;
//计算水的透明度: 使用深度值
float alpha = saturate( _Alpha*depth); //计算颜色深度:
float colDepth = saturate(_ColorDepth*depth);
colDepth = -colDepth;
colDepth = lerp(colDepth, colDepth*colDepth*colDepth, 0.5);//调整深度,看个人喜好 half3 col;
col.rgb = lerp(_Color0.rgb, _Color1.rgb, colDepth); return float4(col.rgb, alpha );
}
ENDCG
}
}
}
现在基本完成了物体浸入水中的效果:
接下来,我还需要什么效果呢?
1. UV偏移的动态水面;
2. 反射;
3. 折射;
关于相机透明度问题

深度增加,透明度降低。
Unity Water Shader的更多相关文章
- 【淡墨Unity3D Shader计划】五 圣诞用品: Unity在Shader三种形式的控制&混合操作编译
本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/42060963 作者:毛星云(浅墨) ...
- unity, water cube
<纪念碑谷>里有一关开始是一个宝箱展开后里面有一个water cube,其中还有小鱼在游.如下截图: 因为我们知道<纪念碑谷>是unity做的,而现在正开始学unity,所以也 ...
- Unity Built-in Shader详解二
这次主要讨论Unity自带的Shader中Transparent Shader Family 这个家族的Shader一共7种,原理和类型与Normal中的上差不多,只不过这些Shader是用在半透明或 ...
- 对Unity一个Shader编译Bug的分析(Unrecognized sampler 'samplerunity_lightmap)
写在前面 Unity的用户量越来越大,越来越有钱,这几年摊子也铺的越来越大,所以各个版本总是有很多Bug.对于一些Bug官方在ReleaseNote里的说明是很不详细的,而对于一些渲染相关的Bug,有 ...
- 关于Unity中Shader的使用
在游戏的开发过程中,程序员不太会自己去写shader,因为写起来很麻烦,而且只有Unity会报错,编译器也没有什么提示. 通常是拿别人的shader改一改,当然,程序员还是要能看懂和会一点shader ...
- 关于Unity中Shader的基础认识
Shader也叫着色器,是Unity里面比较难的一个点,网上有很多别人写好的shader,我们可以下载下来用或者修改学习. Shader可以做出很多非常不错的效果,因为它是插在渲染管道里面的程序,一来 ...
- 关于Unity中Shader的内置值
Unity provides a handful of builtin values for your shaders: things like current object's transforma ...
- Unity的shader学习2
下面继续看基于surface的shader代码,基本与Vertex&Fragment shader差不多,只是不能写pass,然后只需要声明surface函数,就能处理所有的事情. Shade ...
- Unity的shader学习1
先来看一段我们项目常见的Shader代码,这个是Vertex&Fragment shader,目前已经不常用了,不过还是适合我们理解一些基础知识和理解一些比较老的shader 代码.下次我们再 ...
随机推荐
- MVC、MVP和MVVM的更简单易懂的理解
本篇转自网络: 一.MVC MVC模式的意思是,软件可以分成三个部分. 视图(View):用户界面. 控制器(Controller):业务逻辑 模型(Model):数据保存 各部分之间的通信方式如下. ...
- Windows下安装Ubuntu16.04双系统
ROS需要在Ubuntu系统上开发,虚拟机跑Ubuntu开发ROS容易出现各种各样的问题,所以需要安装Ubuntu16.04双系统.笔者也是一步步按着网上的帖子来,由于网上的教程都不是最新的而且有的也 ...
- ROS学习笔记七:在ROS中使用USB摄像头
下面是一些USB摄像头的驱动(大多数摄像头都支持uvc标准): 1 使用软件库里的uvc-camera功能包 1.1 检查摄像头 lsusb ----------------------------- ...
- shell 调试 `<<' is not matched
我的这段脚本,验证数据库连接是否正常: #! /bin/sh...while ..do....sqlplus $user/ $passwd@$sid <<!quit;! ... 单独执行 ...
- 给定一个整数 n,返回 n! 结果尾数中零的数量。
示例 1: 输入: 3 输出: 0 解释: 3! = 6, 尾数中没有零. 示例 2: 输入: 5 输出: 1 解释: 5! = 120, 尾数中有 1 个零. 代码部分 class Solution ...
- Ref 和 Out 区别(演练代码)
一.代码 今天就总结Ref和Out 的总结,这东西,也是经常面试过程中,笔试经常考的,比如:请简述Ref和Out 的区别,或者通过一段代码让你计算这过程的结果.... Out代码实例::: stati ...
- poj3050 Hopscotch
思路: 水题. 实现: #include <iostream> #include <cstdio> #include <set> using namespace s ...
- jquery 实现 点击把数据移动右侧 点击再次移回到左侧
2018年第一发 希望新的一年和大家一下学习更多知识 JS://把数据左边挪到了右边,再从右边移动回来function moveOption(e1, e2){ $("#" ...
- IIS 安装了.net framework 4.0/4.5 却找不到相应应用程序池
通常情况下是因为没注册造成的,有些安装包会自己帮你注册上有些不会,感觉略坑. 注册方法:在计算机中点击 开始菜单–>运行 拷贝以下内容运行一下即可. C:\WINDOWS\Microsoft.N ...
- Node.js——防盗链
防盗链可以通过判断请求头中携带的referrer是否属于本域名