游戏开发论坛

 找回密码
 立即注册
搜索
查看: 4472|回复: 1

游戏开发笔记:手机端次时代海水如何实现?

[复制链接]

8717

主题

8783

帖子

1万

积分

版主

Rank: 7Rank: 7Rank: 7

积分
11952
发表于 2018-8-7 09:25:11 | 显示全部楼层 |阅读模式
文/日音

游戏中海水实现后,在商店反响还不错,有人评论说需要增加一个烘焙深度图的工具,想想也有道理,于是就做了一个。原理其实非常简单,就是放一个摄像机照射岛屿,然后用shader将z的值归一化存在贴图中, 然后再将贴图转成png保存。

最重要的部分如下:

  1. Shader "depthShader" {
  2.     Properties {
  3.     }
  4.     SubShader {
  5.         Pass {
  6.             CGPROGRAM
  7. // Upgrade NOTE: excluded shader from DX11 and Xbox360; has structs without semantics (struct v2f members pos1)
  8. #pragma exclude_renderers xbox360
  9.                 #pragma vertex vert
  10.                 #pragma fragment frag
  11.                 #include "UnityCG.cginc"
  12.                 struct appdata {
  13.                     float4 vertex : POSITION;
  14.                 };
  15.                 struct v2f {
  16.                     half4 pos : SV_POSITION;
  17.                     float4 depth : TEXCOORD0;
  18.                 };
  19.                 v2f vert (appdata v) {
  20.                     v2f o;
  21.                     o.pos = UnityObjectToClipPos (v.vertex);
  22.                     o.depth.x = mul(unity_ObjectToWorld,(v.vertex)).y;
  23.                     return o;
  24.                 }
  25.                 float4 frag(v2f i) : COLOR
  26.                 {
  27.                     float d = i.depth.x;
  28. float high = 0;
  29. float low = -2;
  30. float a = (high - d) / (high - low);
  31.                     return float4(1, 0, 0, a);
  32.                 }
  33.             ENDCG
  34.         }
  35.     }
复制代码

非常简单的一个shader,但却非常实用。因为手机上并不支持深度图的直接抓取,所以通过这种方式获取深度图非常适合手机。

而例子我也提供了一个效果对比:

微信图片_20180807091750.jpg

微信图片_20180807091753.jpg

接下来我会写一个运行时烘焙深度图的水,适用任何场景。

先上最终效果:


我开始思考,有没有可能在牺牲部分性能的情况下实现更加贴近pc端表现的海水。

首先,我们要知道目光射入海水的方向:

  1. float3 worldView = (IN.worldPos - _WorldSpaceCameraPos);
复制代码

然后要获得海水的法线:

  1. half4 nmap = (tex2D(_BumpTex, i.worldPos.xz + offset) + tex2D(_BumpTex, half2(-i.worldPos.z, i.worldPos.x) - offset)) * 0.5;
复制代码

这里为了让海水有流动的效果,offset随着时间变化,而取法线贴图则是用了xz和z来出里扰动。

有了法线和眼睛视角,就可以根据菲涅尔公式计算点积:

  1. half fresnel = sqrt(1.0 - dot(-normalize(worldView), worldNormal));
复制代码

这样就有了初步效果:

微信图片_20180807091755.jpg

然后就是海面的高光,我们打算模拟下光照,如下:

  1. half reflectiveFactor = max(0.0, dot(viewdir, reflect(lightDir, worldNormal)));
  2. half shininess = _Strength * 100;
  3. half specularFactor = pow(reflectiveFactor, shininess);
  4.                
  5. half diffuseFactor = max(0.0, dot(worldNormal, lightDir));
  6. color = tex2D(_MainTex, i.texcoord);
  7. color.rgb *= diffuseFactor;
  8. color.rgb = _Specular.rgb * specularFactor;
复制代码

这里我整理下光照的部分,首先我们理解下光照的方向,可以认为应该是负的xyz,这样才像从上面照下来。

于是我把光照设置成-1,-1,0,而计算物体光照原色的时候,利用法线和光照方向的点积,这里要注意,你需要反向一下光照的方向。而计算高光的时候,则是真的用光线的方向了。

这里还有一个非常有意思的点,就是法线里面存的是物体本身的坐标系,我们法线大部分的法线的值都是Z轴朝向的,但水面肯定是朝y轴的才对,所以这里用了一个小技巧,就是把y和z调换。

经过这样的处理,效果变成了这样:

微信图片_20180807091756.jpg

接下来处理海岸的部分,先使用自带的深度图试试:

  1. o.args = ComputeScreenPos(o.pos);
  2. COMPUTE_EYEDEPTH(o.args.z);
  3. half4 foam = (tex2D(_FoamTex, uv1) + tex2D(_FoamTex, uv2)) * 0.5;

  4. half3 worldNormal = (normal.xyz * 2 - 1).xzy;
  5. color = tex2D(_MainTex, i.texcoord);
  6. float depth = tex2Dproj(_CameraDepthTexture, i.args).r;
  7. depth = LinearEyeDepth(depth);
  8. depth = depth - i.args.z;
  9. float depth1 = saturate(depth * _Range.x);
  10. float depth2 = saturate(depth * _Range.x * 0.9);
复制代码

根据视角看过去的深度,计算出深度差。根据深度差,显示出海岸泡沫的效果。这里有一小技巧,我本来使用depth的时候,发现有泡沫的地方和没有泡沫的地方有很明显的分界线,于是用了两层泡沫,第一层浓,第二层淡,弱化泡沫边界。

效果如下:

微信图片_20180807091758.jpg

接下来就是增加光照和深浅变化的控制,这部分就很简单了:

  1. half3 lightDir = _WorldSpaceLightPos0.xyz;
  2.                 // Phong shading model
  3.                 half reflectiveFactor = max(0.0, dot(viewdir, normalize(reflect(-lightDir, worldNormal)))) * 0.995;
  4.                 half shininess = _Strength * 200.0;
  5.                 half specularFactor = pow(reflectiveFactor, shininess);
  6.                 float deep = 1 - saturate(depth1 * _Range.y);
  7.                 float4 deepColor = lerp(_Bright, _Dark, deep * 2)* _Range.y;
  8.                 color.rgb = color.rgb * _Range.z + deepColor * (1 - _Range.z);
复制代码

配了一个血海:

微信图片_20180807091759.jpg

via:游戏扶持by腾讯游戏学院

1

主题

6

帖子

22

积分

注册会员

Rank: 2

积分
22
发表于 2018-8-22 15:44:02 | 显示全部楼层
商店指的Unity Asset Store 吗?里面哪一个产品,我参考一下
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2025-2-24 12:01

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表