UnityShader(五)

编程入门 行业动态 更新时间:2024-10-25 10:30:04

<a href=https://www.elefans.com/category/jswz/34/1711619.html style=UnityShader(五)"/>

UnityShader(五)

这次要用表面着色器实现一个水的特效。先翻到最下边看代码,看不懂再看下面的解释。

首先第一步要实现水的深浅判断,实现深水区和浅水区的区分。

这里需要用到深度图的概念。不去说太多概念,只去说怎么实现的,首先我们的水面是在地面上的,我们可以利用深度图,计算得到人眼到地面的距离depth,人眼到水面的距离记作IN.Proj.z

可以看的出来,当我们看深水区的时候,depth-IN.Proj.z的差值会比浅水区大,所以可以通过这个差值的大小去判断深水区和浅水区。这个IN就是我这个水面的其中某一块着色面片表面着色器的输入结构体值。depth可以通过调一系列api得到,里面涉及到很多空间变换,不仔细去讲了,只说大体思路。

然后,在判断完水的深浅以后,我们要通过设置一个深水色和一个浅水色,将我们的depth-IN.Proj.z映射到[0,1]中,这样通过对每个面片设置一个lerp差值,lerp(_WaterShallowColor,_WaterDeepColor, min(_DepthRange, deltaDepth)/_DepthRange),就可以得到整片水域的颜色深浅过度,而这个映射的过程,我们可以通过设置另外一个变量_DepthRange实现。

min(_DepthRange, deltaDepth)/_DepthRange,这个意思就是,当我水很深的时候,min(_DepthRange, deltaDepth)=_DepthRange,整体得1,所以在lerp函数中取的是深水色,当我水很浅的时候,min(_DepthRange, deltaDepth)=deltaDepth,lerp的第三个参数就成了deltaDepth/_DepthRange,因为水很浅,所以这个数接近0,颜色是接近浅水色,如果水的深浅介于深水和浅水之间,那么颜色也介于深水色和潜水色之间。但是这样深水和浅水虽然区分开了,但是都是纯色的,没有立体效果。

第二步通过对法线贴图采样,实现水的立体效果。

下面的代码是说,本来把法线贴图假设是第i,j个面片采样到我当前模型的面片,但是现在不是采样到我当前面片, 而是到了x方向加上_WaterSpeed * _Time.x这个值的面片,先不管_Time.x会动,因为uv的范围都是0-1,所以本质上是在0-1的范围内,采样的数量少了,因为是本来是法线0-1对应模型0-1,现在相当于同样一个点采样的距离大了,假设法线0-1对应模型0-1.4,模型1-1.4的不会显示,所以相当于是原来的0.6倍。

  float4 bumpOffset1 = tex2D(_NormalTex, IN.uv_NormalTex + float2(_WaterSpeed * _Time.x, 0));

剩下三个计算也是同理,只不过需要注意,_Time.x是会变的,这样子,本来法线贴图假设是第i,j个面片采样到模型的m,n个面片,一段时间后,就是对应到了模型第m + k,n个面片,所有面片都这样映射,相当于整体是移动了,也就是有了大波浪效果,汹涌澎湃。
  float4 bumpOffset2 = tex2D(_NormalTex, float2(1 - IN.uv_NormalTex.y, IN.uv_NormalTex.x) + float2(_WaterSpeed * _Time.x, 0));
  float4 offsetColor = (bumpOffset1 + bumpOffset2)/2;
  float2 offset = UnpackNormal(offsetColor).xy * _Refract;
  float4 bumpColor1 = tex2D(_NormalTex, IN.uv_NormalTex + offset + float2(_WaterSpeed * _Time.x, 0));
  float4 bumpColor2 = tex2D(_NormalTex, float2(1 - IN.uv_NormalTex.y, IN.uv_NormalTex.x) + offset + float2(_WaterSpeed * _Time.x, 0));


 第三步是要实现波纹,主要对浅水区,波纹的实现是要对WaveTex贴图进行采样。

WaveTex贴图就是一个黑白交替的图,白色表示有波纹,黑色表示没有,但它是0-1,对应模型0-1,但其实我们是需要将一个更大范围的x映射到一个更小范围的WaveTex贴图上的,此时就可以用到GPUGem里讲的正弦波模拟水面波纹了           

首先要对波形图和噪声图进行采样,对波形图进行采样两次,是为了产生两道波纹,采样的时候使用噪声图是为了产生随机的效果,对波形图采样要采用上面的正弦波的方式去采,这样就有一个波浪的效果了

            fixed4 waveColor = tex2D(_WaveTex,float2(waveA + _WaveRange * sin(_Time.x * _WaveSpeed + noiseColor.r) , 1) + offset);
            fixed4 waveColor2 = tex2D(_WaveTex,float2(waveA + _WaveRange * sin(_Time.x * _WaveSpeed + _WaveDelta + noiseColor.r) , 1) + offset);

Shader "Custom/MyWater2"
{Properties{_Color ("Color", Color) = (1,1,1,1)_MainTex ("Albedo (RGB)", 2D) = "white" {}_WaterShallowColor("WaterShallowColor", Color) = (1,1,1,1)_WaterDeepColor("WaterDeepColor", Color) = (1,1,1,1)_TranAmount("TranAmount", Range(0,100)) = 0.5_DepthRange("DepthRange",float) = 1_NormalTex("Normal",2D) = "bump"{}_WaterSpeed("WaterSpeed",float) = 5//控制法线的密集程度_Refract("Refract",float) = 0.5_Specular("Specular",float) = 1_Gloss("Gloss",float) = 0.5_SpecularColor("_SpecularColor", Color) = (1,1,1,1)//波浪效果需要先对波浪图采样,噪声图是为了产生随机效果_WaveTex("WaveTex",2D) = "white"{}_NoiseTex("NoiseTex",2D) = "white"{}_WaveSpeed("WaveSpeed",float) = 1_WaveRange("WaveRange",float) = 0.5_WaveRangeA("WaveRangeA",float) = 1_WaveDelta("WaveDelta", float) = 0.5} SubShader{Tags { "RenderType"="Transparent" "Queue"="Transparent"  }LOD 200//水是透明的,所以关闭ZWriteZWrite offCGPROGRAM// Physically based Standard lighting model, and enable shadows on all light types#pragma surface surf WaterLight vertex:vert alpha noshadow// Use shader model 3.0 target, to get nicer looking lighting#pragma target 3.0//加float是为了增加精度sampler2D_float _CameraDepthTexture;sampler2D _MainTex;fixed4 _WaterShallowColor;fixed4 _WaterDeepColor;half _TranAmount;half _DepthRange;sampler2D _NormalTex;half _WaterSpeed;sampler2D _WaveTex;sampler2D _NoiseTex;float _WaveSpeed;float _WaveRange;//这两个控制波浪的范围float _WaveRangeA;float _WaveDelta;struct Input{float2 uv_MainTex;float4 proj;float2 uv_NormalTex;float2 uv_WaveTex;float2 uv_NoiseTex;};half _Glossiness;half _Metallic;fixed4 _Color;half _Refract;half _Specular;half _Gloss;fixed4 _SpecularColor;// Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.// See .html for more information about instancing.// #pragma instancing_options assumeuniformscalingUNITY_INSTANCING_BUFFER_START(Props)// put more per-instance properties hereUNITY_INSTANCING_BUFFER_END(Props)fixed4 LightingWaterLight(SurfaceOutput s, fixed3 lightDir, half3 viewDir, fixed atten){float diffuseFactor = max(0,dot(normalize(lightDir),s.Normal));half3 halfDir = normalize(lightDir + viewDir);float nh = max(0,dot(halfDir,s.Normal));float spec = pow(nh, s.Specular * 128) * s.Gloss;fixed4 c;c.rgb = (s.Albedo * _LightColor0.rgb * diffuseFactor + _SpecularColor.rgb * spec * _LightColor0.rgb) * atten;c.a = s.Alpha + spec * _SpecularColor.a;return c;}void vert(inout appdata_full v,out Input i){UNITY_INITIALIZE_OUTPUT(Input, i);i.proj = ComputeScreenPos(UnityObjectToClipPos(v.vertex));COMPUTE_EYEDEPTH(i.proj.z);}void surf (Input IN, inout SurfaceOutput o){//关键点是深度图判断深水和浅水,法线移动实现波浪效果//tex2Dproj(_CameraDepthTexture, IN.proj)=tex2D(_CameraDepthTexture, IN.proj.xy/IN.proj.w)//对深度图采样,采样屏幕空间//它的uv表示当前模型在顶点着色器画到屏幕后的坐标//采样后的每个变量都是一样的,取r即可//越近深度图上对应的区域越红(深度值接近1),越远则越黑(深度值接近0)。也就是说深度值从0到1代表的是从远到近。half depth = LinearEyeDepth(tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(IN.proj)).r);//depth 就相当于是这样计算出来的//SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(IN.proj));//UNITY_PROJ_COORD(IN.proj)是返回的采样后的纹理坐标,大多数平台就是返回的IN.proj,所以写不写区别不大//摄像机深度贴图的深度值减去地面模型面片的深度值half deltaDepth = depth - IN.proj.z;fixed4 c = lerp(_WaterShallowColor,_WaterDeepColor, min(_DepthRange, deltaDepth)/_DepthRange);//对法线贴图采样float4 bumpOffset1 = tex2D(_NormalTex, IN.uv_NormalTex + float2(_WaterSpeed * _Time.x, 0));float4 bumpOffset2 = tex2D(_NormalTex, float2(1 - IN.uv_NormalTex.y, IN.uv_NormalTex.x) + float2(_WaterSpeed * _Time.x, 0));float4 offsetColor = (bumpOffset1 + bumpOffset2)/2;float2 offset = UnpackNormal(offsetColor).xy * _Refract;float4 bumpColor1 = tex2D(_NormalTex, IN.uv_NormalTex + offset + float2(_WaterSpeed * _Time.x, 0));float4 bumpColor2 = tex2D(_NormalTex, float2(1 - IN.uv_NormalTex.y, IN.uv_NormalTex.x) + offset + float2(_WaterSpeed * _Time.x, 0));//波浪 跟水的深浅相反即可 min(_DepthRange, deltaDepth)/_DepthRange接近1的地方,应该无波浪//因为波浪要出现在水的边缘,min(_DepthRange, deltaDepth)/_DepthRange接近1的地方 表示deltadepth很大,水很深half waveA = 1 - min(_WaveRangeA,deltaDepth)/_WaveRangeA;fixed4 noiseColor = tex2D(_NoiseTex,IN.uv_NoiseTex);//sin函数来对波纹图采样,产生流动效果fixed4 waveColor = tex2D(_WaveTex,float2(waveA + _WaveRange * sin(_Time.x * _WaveSpeed + noiseColor.r) , 1) + offset);fixed4 waveColor2 = tex2D(_WaveTex,float2(waveA + _WaveRange * sin(_Time.x * _WaveSpeed + _WaveDelta + noiseColor.r) , 1) + offset);o.Albedo = c.rgb + (waveColor.rgb + waveColor2.rgb) * waveA;o.Normal = UnpackNormal((bumpColor1 + bumpColor2)/2).xyz;o.Gloss = _Gloss;o.Specular = _Specular;o.Alpha = min(_TranAmount,deltaDepth) / _TranAmount;}ENDCG}FallBack "Diffuse"
}

法线贴图,噪声图,波纹图网盘链接

链接:=jk3l 
提取码:jk3l

更多推荐

UnityShader(五)

本文发布于:2023-11-16 01:20:37,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1611167.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:UnityShader

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!