UnityShader_倒影,水波倒影(1)
关于我这几天去哪了,在写一个水面的shader,再加上最近公司事情多,就耽搁了。在网上分析了好几种水面的shader,最后发现大体上就两种,下面就来分析这两种shader。
镜面shader一,利用两个Pass渲染出物体本身及倒影
这类型的shader包括两部分:
1、需要被倒影的物体本身,我命名为MirrorShader
2、被投射的水面shader,我命名为waterShader
Shader "ShaderPath/MirrorShader"//shader的选择路径
{Properties//该Shader可控的属性{_PlaneNormal("地面旋转角度",Vector) = (0,0,0,0) //地面法线_PlanePosition("地面位置",Vector) = (0,0,0,0) //地面位置_GradientTex("GradientTex", 2D) = "white" {}//渐变贴图_DiffuseColor("DiffuseColor",Color) = (1,1,1,1)//漫反射的主色调_SpecularColor("SpecularColor",Color) = (1,1,1,1)//高光反射的主色调_Gloss("Gloss",Range(1,100)) = 2 //光泽度(反光度) 控制高光区域的大小 _MirrorRange("MirrorRange", Range(0, 1)) = 1 // 镜面范围(最大范围,超出该范围就不反射)_MirrorAlpha("MirrorAlpha", Range(0, 1)) = 1 // 镜面图像不透明度_MirrorFadeAlpha("_MirrorFadeAlpha", Range(0,1)) = 0.5 // 镜面范围值边缘位置的不透明度,如果调整为0,意思越接近该最大范围的透明就越接近该值:0}//先用CGINCLUDE ENDCG包裹起来片元顶点着色器,后面看比较直观CGINCLUDE#include "UnityCG.cginc"#include "Lighting.cginc"#include "AutoLight.cginc"struct appdata {float4 vertex : POSITION;float2 uv : TEXCOORD0;float3 normal : NORMAL;};//镜像片元结构体struct v2f_m {float4 pos : SV_POSITION;float2 uv : TEXCOORD0;float4 normal : TEXCOORD1;float4 wPos : TEXCOORD2;float3 lightDir : TEXCOORD3;float3 viewDir : TEXCOORD4;};//主体片元结构体struct v2f{float3 lightDir : TEXCOORD0;float3 viewDir : TEXCOORD1;float3 normal : TEXCOORD2;float2 uv : TEXCOORD3;//用于存储纹理信息float4 pos : SV_POSITION;//每个片元结构体必须有的};float4 _PlaneNormal;float4 _PlanePosition;sampler2D _GradientTex;float4 _GradientTex_ST;//图片的(平铺和偏移系数)如果要使图片的Tilling和Offset生效就必须定义fixed4 _DiffuseColor;fixed4 _SpecularColor;float _Gloss;float _MirrorRange;float _MirrorAlpha;float _MirrorFadeAlpha;v2f vert(appdata v){v2f o;o.pos = UnityObjectToClipPos(v.vertex);//把顶点从模型空间转换到剪裁空间o.uv = TRANSFORM_TEX(v.uv, _GradientTex);//使图片对应的_ST生效,这里就是_GradientTex_ST//要区分UnityWorldSpaceLightDir()传入一个float3的世界坐标 和 WorldSpaceLightDir()传入一个float4的模型的顶点坐标//UnityWorldSpaceViewDir()传入一个float3的世界坐标 WorldSpaceViewDir()传入一个float4的模型的顶点坐标o.lightDir = WorldSpaceLightDir(v.vertex);//获取世界坐标下的光线方向o.viewDir = WorldSpaceViewDir(v.vertex);//获取世界坐标下的观察方向o.normal = normalize(UnityObjectToWorldNormal(v.normal));//将模型的法线转到世界坐标return o;}fixed4 frag(v2f i) : SV_Target//返回一个RGBA到模型上{fixed3 lightDir = normalize(i.lightDir);//归一化fixed3 viewDir = normalize(i.viewDir);//归一化//半罗伯特反射fixed halfLambert = (1 + dot(lightDir,i.normal)) / 2;fixed3 gradient = tex2D(_GradientTex,fixed2(halfLambert + i.uv.x,0)).rgb * _DiffuseColor;//采样_GradientTex里的颜色信息fixed3 diffuse = _LightColor0 * gradient;//Blinn-Phong模型高光 fixed3 halfView = normalize(lightDir + viewDir);fixed3 specular = _LightColor0.rgb * _SpecularColor * pow(saturate(dot(i.normal,halfView)),_Gloss);return fixed4(diffuse + specular,1);}//镜像定点着色器v2f_m Mirrorvert(appdata v){v2f_m o;o.wPos = mul(unity_ObjectToWorld, v.vertex);float3 nn = -_PlaneNormal.xyz; // 地面法线反向,镜像的物体要用float3 dp = o.wPos.xyz - _PlanePosition.xyz; // 平面点与世界空间的点的向量(即:从平面的点指向世界空间点的方向)half nd = dot(_PlaneNormal.xyz, dp); // 计算出点与平面的垂直距离o.wPos.xyz += nn * (nd * 2); // 将垂直距离反向2倍的距离,就是镜像的位置o.pos = mul(unity_MatrixVP, o.wPos); //计算模型当前视图的投影点,更多表查看下面的“UnityShader部分常用内置矩阵表”o.normal.xyz = UnityObjectToWorldNormal(v.normal);fixed t = nd / _MirrorRange; // 将位置与镜面最大范围比利作为fade alpha的插值系数fixed a = lerp(_MirrorAlpha, _MirrorAlpha * _MirrorFadeAlpha, t);o.normal.w = a; // 透明度我们存于o.normal.wo.wPos.w = nd; // 距离存于o.wPos.wo.uv = v.uv;o.lightDir = WorldSpaceLightDir(v.vertex);//获取世界坐标下的光线方向o.viewDir = WorldSpaceViewDir(v.vertex);//获取世界坐标下的观察方向return o;}fixed4 Mirrorfrag(v2f_m i) : SV_Target//返回一个RGBA到模型上{if (i.wPos.w > _MirrorRange) discard; // 超过镜像范围也丢弃if (i.normal.w <= 0) discard; // 透明度为0丢弃float3 dir = i.wPos.xyz - _PlaneNormal.xyz; // 平面与插值点的指向half d = dot(dir, _PlaneNormal.xyz); // 与反向镜面的距离if (d > 0) discard; // 如果超过了平面,那就丢弃fixed3 lightDir = normalize(i.lightDir);//归一化fixed3 viewDir = normalize(i.viewDir);//归一化//半罗伯特反射fixed halfLambert = (1 + dot(lightDir,i.normal)) / 2;//fixed2(halfLambert+i.uv.x,0) x的值必须如上,具体解释看下面,y的值任意,因为图片的颜色值不随y的变化而变化fixed3 gradient = tex2D(_GradientTex,fixed2(halfLambert + i.uv.x,0)).rgb * _DiffuseColor;//采样_GradientTex里的颜色信息fixed3 diffuse = _LightColor0 * gradient;fixed3 halfView = normalize(lightDir + viewDir);fixed3 specular = _LightColor0.rgb * _SpecularColor * pow(saturate(dot(i.normal,halfView)),_Gloss);return fixed4(diffuse + specular , i.normal.w);}ENDCGSubShader//子着色器{Pass{Tags {"LightMode" = "ForwardBase"} //定义该Pass在Unity光照流水线中的角色CGPROGRAM#pragma vertex vert#pragma fragment fragENDCG}Pass{Tags { "Queue" = "Geometry" "RenderType" = "Opaque" }Cull Front //镜像里正反颠倒,此时应该剪裁掉正面ZTest Always //这里必须保持深度测试通过,否则会被水面挡住Blend SrcAlpha OneMinusSrcAlpha //渲染的叠加方式Stencil {Ref 1Comp Equal}Tags {"LightMode" = "ForwardBase"} //与ENDCG相照应,将CG代码包裹CGPROGRAM//顶点函数定义#pragma vertex Mirrorvert //片元函数定义#pragma fragment MirrorfragENDCG}}
}
// jave.lin 2019.08.15
Shader "ShaderPath/WaterShader"
{Properties{_MainTex ("Texture", 2D) = "white" {}_Color ("Color", Color) = (1,1,1,1)}SubShader{Tags { "Queue"="Geometry" "RenderType"="Opaque" }ZWrite OnStencil {Ref 1Comp AlwaysPass Replace}Pass {CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct appdata {float4 vertex : POSITION;float2 uv : TEXCOORD0;};struct v2f {float4 vertex : SV_POSITION;float2 uv : TEXCOORD0;};sampler2D _MainTex;float4 _MainTex_ST;fixed4 _Color;v2f vert (appdata v) {v2f o;o.vertex = UnityObjectToClipPos(v.vertex);o.uv = TRANSFORM_TEX(v.uv, _MainTex);return o;}fixed4 frag (v2f i) : SV_Target {fixed4 col = tex2D(_MainTex, i.uv);return col * _Color;}ENDCG}}
}
效果如下如
UnityShader部分常用内置矩阵表
核心代码详解
1、下面这段代码是镜像呈现的核心代码
float3 nn = -normal; // 法线反向float3 dp = o.wPos.xyz - _PlanePosition.xyz; // 平面点与世界空间的点的向量(即:从平面的点指向世界空间点的方向)half nd = dot(normal, dp); // 计算出点与平面的垂直距离o.wPos.xyz += nn * (nd * 2); // 将垂直距离反向2倍的距离,就是镜像的位置
如下图所示,
平面过点O的法线为normal,
P为模型上的点,P’为镜像点,
dp为向量OP,
|nd| = normal·dp = |normal|·|dp| cosθ = |dp|cosθ
PS:normal为单位向量,θ为normal、dp的夹角
P’ = -normal * (2 * nd )
2、关于模板测试
在MirrorShader和WaterShader里面都有关于模板测试的东西
MirrorShader中
Stencil
{Ref 1Comp Equal
}
WaterShader中
Stencil
{Ref 1Comp AlwaysPass Replace
}
上面两个模板合起来解释就是:
1、在物体上渲染出来的投影设置模板参照值(Ref)为1,并且只要模板参考值相同的就通过模板测试;
2、水面的模板参照值为1,模板测试一律通过,并且只要通过了模板测试和深度测试的像素就直接显示出来
3、总结就是物体倒影和水面的模板参照值相同,并且物体倒影的深度测试也通过,所以物体的倒影能在水面显示
关于模板测试更多的知识查看该链接
PS,注意文中的深度写入,另一个水面在下一篇文章中叙述,不然篇幅太长。
UnityShader_倒影,水波倒影(1)相关推荐
- UnityShader_倒影,水波倒影(代码已更新)(2)
相较于上一个水波倒影,这个水波倒影要更为复杂,但也更为真实,大体思路如下 利用脚本抓取水面的镜像,并在Shader中进行处理 先上个Gif解解馋,代码后面整理好再上传,最近实在事情多,早上优化到晚上的 ...
- 教你使用MATLAB制作水波倒影特效
注: 本文算法参考大佬 grafx 的这篇博客: 图像处理算法之水面倒影特效 由于本文使用MATLAB复现,因此很多语法上会显得比较简洁,同时本博文对原大佬文章部分内容进行了改写,详见本文: 0效果展 ...
- HTML里面设置文本倒影,文字倒影怎么做
现在的文字如果仅仅是在一个平面上,不加任何效果,那就真的是太死板.太单调了,尤其追求艺术的小伙伴们啊,细心的话你就会发现那些神马广告之类的文字都讲究有个性,让人过目不忘的效果,有木有!那么接下来就教大 ...
- android textview 文字倒影,textview 倒影
场景:制造TextView的倒影 制作TextView的倒影 package com.javaeye.graphics; import android.app.Activity; import and ...
- CSS3蒙版/遮罩、倒影
蒙版/遮罩mask 很简单的一个属性,并不是PS里面的那个蒙版 蒙版目前只有-webkit-内核的浏览器支持(谷歌.Safari.新欧朋opera),其他不支持 需要png的透明度遮罩 -webkit ...
- html 字体显示倒影,用CSS3的box-reflect设置文字倒影效果的方法讲解
语法: CSS Code复制内容到剪贴板 box-reflect:none| ? ? =above|below|left|rightright = | =none| | | | | 默认值 ...
- css原地颠倒 h5_H5案例分享:CSS3 reflect倒影
CSS3 reflect倒影 说起倒影效果,在传统网页中,我们只能使用photoshop事先将倒影设计好,然后导入到网页中,这样不但耗费资源,也阻碍了开发的效率.而现在CSS3有一个属性box-ref ...
- Qt 渐变(二): 线性渐变与合成模式实现倒影
文章目录 1.Qt 渐变(二): 线性渐变与合成模式一起,实现倒影 ReflectionPanel.h ReflectionPanel.cpp 1.Qt 渐变(二): 线性渐变与合成模式一起,实现倒影 ...
- UnityShader_泳池实现
实现思路: 1.实现水面抖动: ①利用顶点偏移实现水面的上下波动: ②对Unity自带的CustomRenderTextureUpdateZone生成波纹贴图,并采样实现水纹 2.水面的实现: ①水面 ...
最新文章
- Nginx 虚拟主机配置及负载均衡
- 【错误记录】MAC 存储空间 “其它“ 内容清理
- bash 脚本中激活conda环境_ubuntu18.10目标检测算法环境部署+开机自启动脚本创建screen下的web服务...
- php基础不好,基础不好,问个php类调用的初级问题
- 我们处理了10亿个Java记录的错误-这是导致97%的错误的原因
- mysql group by null_MySQL无GROUP BY直接HAVING返回空的问题分析
- 三七互娱李逸飞:未来将关注元宇宙等新业态 创新构建核心优势
- 安全测试===sqlmap(壹)转载
- ipv6详解_modbus之libmodbus库详解
- efs解密-Advanced EFS Data Recovery2.1-含注册KEY
- 忘记 Apple Watch PIN 码,如何解锁 Apple Watch?
- Jlink 下安装JLINK驱动
- 机器学习导论(五)-神经元网络
- 服务器进pe iso安装系统教程,iso系统怎么安装( pe怎么安装iso原版镜像)
- Golang 标准库 tips之waitgroup详解
- 【JY】STKO助力OpenSEES系列:结构模态分析以及动力特性(MDOF与等效SDOF验证)
- Confusing Problem
- NYOJ-234-DP(吃土豆)
- 【ASP.NET MVC系列】浅谈Google Chrome浏览器(操作篇)(上)
- VMware虚拟机无网络