700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > Unity3D简单的水面shader实现

Unity3D简单的水面shader实现

时间:2023-10-30 01:51:23

相关推荐

Unity3D简单的水面shader实现

根据这位大佬的文章我进行了实现,并且对代码的部分做了注释方便大家理解。

文章指路:/o83290102o5/article/details/120296344

实现大概分为四个部分:

1. 用顶点坐标变化来模拟水面波浪的起伏

//实现水面的波浪的变化//计算顶点的实时高度,计算的xz平面,用波的震动函数来计算实时的高度fixed height = sin(_Time.y * _WaveSpeed + v.vertex.z * _WaveGap + v.vertex.x) * _WaveHeight;//用计算得到的实时高度在法线的方向上进行变化v.vertex.xyz += v.normal * height;

2. 用深度差值来实现不同深度水面的不同颜色

//通过深度差值计算不同深度的水面的颜色//获取到深度纹理的深度值,也就是非透明物体渲染进行深度测试所更新存放的最接近摄像头的距离float depth1 = LinearEyeDepth(tex2D(_CameraDepthTexture,IN.screenPos.xy).r);//获取到该点实际的深度值,也就是水面的深度值,LinearEyeDepth用于转换成线性深度float depth2 = LinearEyeDepth(IN.screenPos.z);//取差值的绝对值得到深度差,深度差值越大,说明离水面越深,也就是颜色越深float distance = abs(depth1-depth2);//根据差值与阙值的乘积,来计算不同深度的水体的颜色,阙值越大,越容易靠近1,则颜色会越深fixed4 waterColor = lerp(_EdgeColor,_Color,saturate(distance*_EdgeThreshold));

3. 结合差值实现水面与物体相加的边缘的泡沫效果

//边缘泡沫的实现//通过对纹理坐标的变化从而获得不同时刻的泡沫的变化,纹理坐标的变化也就是不同时刻采样得到的颜色是变化的,//用fixed2(1,1)是为了对xy坐标同时进行变化float4 foamColor = tex2D(_FoamTex,(IN.uv_FoamTex+fixed2(1,1)*_Time.y*_FoamSpeed));//将深度差值限制在[0,_FoamThreshold]得到formDegreefixed formDegree = clamp(distance,0,_FoamThreshold);//将formDegree限制在[0,1]formDegree = formDegree/_FoamThreshold;//如果差值越靠近_FoamThreshold阙值则泡沫的效果越不明显,也就是在深度低的地方泡沫效果越明显,这样可以近似得到一个边缘的泡沫效果fixed4 formColor = lerp(foamColor,fixed4(0,0,0,0),formDegree)*_FoamColorScale;

4. 利用法线纹理实现水面波光粼粼的效果

//水面波光粼粼的效果//结合时间计算偏移量,结合波浪的速度使得波光粼粼的变化更加真实自然,speed并非是速度而是偏移量float2 speed = _Time.x*float2(_WaveSpeed,_WaveSpeed)*_NormalSpeed;//对法线贴图进行两次采样,最后将采样结果相加得到最终的法线,如果单单取一个方向的话,会使得波光的变化只朝着一个方向,这不符合预期效果fixed3 bump1 = UnpackNormal(tex2D(_NormalTex,IN.screenPos.xy+speed)).rgb;fixed3 bump2 = UnpackNormal(tex2D(_NormalTex,IN.screenPos.xy-speed)).rgb;fixed3 bump = normalize(bump1+bump2);//法线乘以变化的大小进行变化bump.xy *= _NormalScale;//归一化bump = normalize(bump);//法线赋值o.Normal = bump;//法线(提供偏移方向)乘以_Distortion(偏移大小)得到该点采样倒影纹理的偏移大小,乘以倒影纹理的纹素大小,得到实际偏移float2 offset = bump.xy*_Distortion*_RefractionTex_TexelSize.xy;//在采样的时候加上实际偏移对倒影的部分进行采样,得到变化的倒影效果,倒影纹理是抓取的屏幕图像的纹理fixed3 refrCol = tex2D(_RefractionTex,IN.screenPos.xy+offset).rgb;

综合代码:

Shader "Custom/simpleWater"{Properties{_Color ("Color", Color) = (0,0,1,1)//深层水体的颜色_EdgeColor ("EdgeColor", Color) = (0,1,1,1)//水面的颜色_EdgeThreshold ("EdgeThreshold", float) =0.1//深度阈值,用于判定水体不同深度,从而产生不同的水体颜色效果_WaveHeight ("WaveHeight", float) = 1//波浪的高度_WaveSpeed ("WaveSpeed", float) = 1//波浪传播的速度_WaveGap ("WaveGap", float) = 1//波浪的间隔_FoamTex ("FoamTex", 2D) = "white"//边缘泡沫的纹理_FoamThreshold ("FoamThreshold", float) =0.1//泡沫边缘的阈值_FoamColorScale ("FoamColorScale", float) =0.2//边缘的颜色的饱和度,饱和度越高颜色越深_FoamSpeed ("FoamSpeed", float) =0.1//泡沫纹理的大小_NormalTex ("NormalTex", 2D) = "white"//法线纹理_NormalScale ("NormalScale", float) = 1//法线的变化幅度_NormalSpeed ("NormalSpeed", float) = 1//法线的变化速度_Distortion("Distortion",float) = 1//倒影的偏移大小}SubShader{//设置渲染类型是透明的,渲染队列在非透明和透明测试之后,也就是非透明的物体渲染了之后,半透明的水体需要混合之前渲染的颜色,并且忽略别的物体的阴影影响Tags { "RenderType"="Transparent" "Queue"="Transparent" "IgnoreProjector"="True" }LOD 200//抓取屏幕图像存储到_RefractionTex纹理中GrabPass{"_RefractionTex"}CGPROGRAM//声明表面着色器的函数名和光照函数:Standard,透明度的函数,以及顶线修改函数#pragma surface surf Standard alpha:fade keepalpha fullforwardshadows vertex:vertexDataFunc#pragma target 3.0//表面着色器的输入结构体struct Input{float2 uv_FoamTex;//泡沫纹理float4 screenPos;//顶点坐标};sampler2D _CameraDepthTexture;//深度纹理fixed4 _Color;fixed4 _EdgeColor;float _WaveHeight;float _WaveSpeed;float _WaveGap;float _EdgeThreshold;float _FoamThreshold;sampler2D _FoamTex;float _FoamColorScale;sampler2D _NormalTex;float _NormalScale;float _NormalSpeed;float _FoamSpeed;float _Distortion;sampler2D _RefractionTex;//倒影纹理float4 _RefractionTex_TexelSize;//倒影纹理纹素大小void vertexDataFunc(inout appdata_full v, out Input o){//对o中的参数进行初始化UNITY_INITIALIZE_OUTPUT(Input, o);//实现水面的波浪的变化//计算顶点的实时高度,计算的xz平面,用波的震动函数来计算实时的高度fixed height = sin(_Time.y * _WaveSpeed + v.vertex.z * _WaveGap + v.vertex.x) * _WaveHeight;//用计算得到的实时高度在法线的方向上进行变化v.vertex.xyz += v.normal * height;}void surf (Input IN, inout SurfaceOutputStandard o){//坐标其次变IN.screenPos.xyz /= IN.screenPos.w;//通过深度差值计算不同深度的水面的颜色//获取到深度纹理的深度值,也就是非透明物体渲染进行深度测试所更新存放的最接近摄像头的距离float depth1 = LinearEyeDepth(tex2D(_CameraDepthTexture,IN.screenPos.xy).r);//获取到该点实际的深度值,也就是水面的深度值,LinearEyeDepth用于转换成线性深度float depth2 = LinearEyeDepth(IN.screenPos.z);//取差值的绝对值得到深度差,深度差值越大,说明离水面越深,也就是颜色越深float distance = abs(depth1-depth2);//根据差值与阙值的乘积,来计算不同深度的水体的颜色,阙值越大,越容易靠近1,则颜色会越深fixed4 waterColor = lerp(_EdgeColor,_Color,saturate(distance*_EdgeThreshold));//边缘泡沫的实现//通过对纹理坐标的变化从而获得不同时刻的泡沫的变化,纹理坐标的变化也就是不同时刻采样得到的颜色是变化的,//用fixed2(1,1)是为了对xy坐标同时进行变化float4 foamColor = tex2D(_FoamTex,(IN.uv_FoamTex+fixed2(1,1)*_Time.y*_FoamSpeed));//将深度差值限制在[0,_FoamThreshold]得到formDegreefixed formDegree = clamp(distance,0,_FoamThreshold);//将formDegree限制在[0,1]formDegree = formDegree/_FoamThreshold;//如果差值越靠近_FoamThreshold阙值则泡沫的效果越不明显,也就是在深度低的地方泡沫效果越明显,这样可以近似得到一个边缘的泡沫效果fixed4 formColor = lerp(foamColor,fixed4(0,0,0,0),formDegree)*_FoamColorScale;//水面波光粼粼的效果//结合时间计算偏移量,结合波浪的速度使得波光粼粼的变化更加真实自然,speed并非是速度而是偏移量float2 speed = _Time.x*float2(_WaveSpeed,_WaveSpeed)*_NormalSpeed;//对法线贴图进行两次采样,最后将采样结果相加得到最终的法线,如果单单取一个方向的话,会使得波光的变化只朝着一个方向,这不符合预期效果fixed3 bump1 = UnpackNormal(tex2D(_NormalTex,IN.screenPos.xy+speed)).rgb;fixed3 bump2 = UnpackNormal(tex2D(_NormalTex,IN.screenPos.xy-speed)).rgb;fixed3 bump = normalize(bump1+bump2);//法线乘以变化的大小进行变化bump.xy *= _NormalScale;//归一化bump = normalize(bump);//法线赋值o.Normal = bump;//法线(提供偏移方向)乘以_Distortion(偏移大小)得到该点采样倒影纹理的偏移大小,乘以倒影纹理的纹素大小,得到实际偏移float2 offset = bump.xy*_Distortion*_RefractionTex_TexelSize.xy;//在采样的时候加上实际偏移对倒影的部分进行采样,得到变化的倒影效果,倒影纹理是抓取的屏幕图像的纹理fixed3 refrCol = tex2D(_RefractionTex,IN.screenPos.xy+offset).rgb;//环境光等于水体的颜色乘以倒影的颜色加上泡沫的颜色//水体的颜色用的是各个颜色通道的比例,而倒影的则是255数值的颜色值,所以二者相乘,而泡沫是255颜色值所以是相加o.Albedo = waterColor.rgb*refrCol+formColor.rgb;//透明度为水体的透明度o.Alpha=waterColor.a;}ENDCG}FallBack "Diffuse"}

最后,我有一个点不能理解的就是,在计算法线纹理偏移量的时候,是乘以了_Time.x也就是t/20,这时间会随着运行的时间而不断变大,那么偏移量也会随着时间不断变大,但是代码中并没有对偏移量进行什么限制,那么为什么不会出现在采样的时候,纹理坐标偏移之后超出范围呢?不知道是我理解错误还是什么概念没有理解到位,请各位大佬帮忙指正。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。