UnityShader 您所在的位置:网站首页 月亮水中倒影图片 UnityShader

UnityShader

2023-08-23 19:28| 来源: 网络整理| 查看: 265

关于我这几天去哪了,在写一个水面的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.w o.wPos.w = nd; // 距离存于o.wPos.w o.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; // 如果超过了平面,那就丢弃 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); } ENDCG SubShader//子着色器 { Pass{ Tags {"LightMode" = "ForwardBase"} //定义该Pass在Unity光照流水线中的角色 CGPROGRAM #pragma vertex vert #pragma fragment frag ENDCG } Pass { Tags { "Queue" = "Geometry" "RenderType" = "Opaque" } Cull Front //镜像里正反颠倒,此时应该剪裁掉正面 ZTest Always //这里必须保持深度测试通过,否则会被水面挡住 Blend SrcAlpha OneMinusSrcAlpha //渲染的叠加方式 Stencil { Ref 1 Comp Equal } Tags {"LightMode" = "ForwardBase"} //与ENDCG相照应,将CG代码包裹 CGPROGRAM //顶点函数定义 #pragma vertex Mirrorvert //片元函数定义 #pragma fragment Mirrorfrag ENDCG } } } // 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 On Stencil { Ref 1 Comp Always Pass 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 1 Comp Equal } WaterShader中 Stencil { Ref 1 Comp Always Pass Replace }

上面两个模板合起来解释就是: 1、在物体上渲染出来的投影设置模板参照值(Ref)为1,并且只要模板参考值相同的就通过模板测试; 2、水面的模板参照值为1,模板测试一律通过,并且只要通过了模板测试和深度测试的像素就直接显示出来 3、总结就是物体倒影和水面的模板参照值相同,并且物体倒影的深度测试也通过,所以物体的倒影能在水面显示

关于模板测试更多的知识查看该链接

PS,注意文中的深度写入,另一个水面在下一篇文章中叙述,不然篇幅太长。


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

      专题文章
        CopyRight 2018-2019 实验室设备网 版权所有