源码学习网 首页 编程学园 Unity3d教程 查看内容

建筑场景的阴影实现一

2019-7-16 22:00| 发布者: opiye| 查看: 84| 评论: 0

摘要: 首先看下我们的效果需求,我们是一款SLG游戏,主城中的建筑都是3D的模型,玩家到后期主城内会有上百个建筑,而且建筑都是可以让玩家随意摆放的。我们的建筑要实现自身的阴影和在地面上的投影。如下图:一般我们在游 ...
腾讯云服务器秒杀

首先看下我们的效果需求,我们是一款SLG游戏,主城中的建筑都是3D的模型,玩家到后期主城内会有上百个建筑,而且建筑都是可以让玩家随意摆放的。我们的建筑要实现自身的阴影和在地面上的投影。如下图:

一般我们在游戏中要实现建筑的阴影有两种方案:1.灯光的实时阴影2.使用Lightmap烘焙阴影信息。

方案1在移动端上使用的很少,如果没有实时光影的变化,类似天气系统之类的需求,基本上是不会使用的。要多一个pass去计算阴影,相当于场景中的三角面数翻倍。

方案2是目前大多数移动端上使用的,开销的话只是用第二套UV去采样一次Lightmap贴图而已。但是这种方法烘焙完Lightmap之后就不能修改了,而我们的建筑是可以让玩家随意摆放的,所以这个方案也并不适用于我们。

我们实现的方案也是和Lightmap类似,都是用模型的第二套UV去采样一张光影贴图。只是我们不在Unity里面去做Lightmap烘焙,而是在三维模型制作的时候就把阴影烘焙在贴图的Alpha通道里面。

整个阴影效果分两部分,自身阴影和地面的投影。自身阴影的实现可以直接画在Color贴图上,也就是在制作贴图的时候就把光影关系画出来。但是这样要做到统一光影强弱和后期修改起来都比较麻烦,而且我们为了节省资源很多建筑的Color贴图都是共用的。比如说A建筑和B建筑的墙用的都是同样的一块贴图,但是A建筑可能是在暗面,而B建筑可能是在亮面,所以这个就没有办法直接把阴影画在Color贴图上面了。第二部分是地面的投影,有些游戏建筑下面会加一个地块,建筑的投影就是直接投射在自带的地块上,这样的话可以直接当做自阴影来一起处理,我们的建筑是没有那个自带地块的,所以要在单独建一个面片的模型来接收地面的投影。下面我们来看具体的实现方法。

自身阴影的实现:

我以前是做影视的所以更习惯使用Maya,当然在Max里面也是一样的方法。模型导入之后会有一套之前制作贴图时候的UV,这套UV是用来采样Color的,我们不能修改。

给这个模型新建一套UV,然后用自动映射将模型每个面的UV都展开,不能有重叠。

接下来我们就打好灯光来烘焙阴影,这里我们只单独烘焙阴影所以材质球上不需要贴上Color贴图,直接白模+主光源+AO渲染就好了。离线渲染器也可以选择自己喜欢用的习惯的,我这里用的是Redshift,刚好之前体验过一下这款GPU离线渲染器,效果和效率都还不错。

然后在将这个阴影效果烘焙到模型的第二套UV上,千万不要烘焙到第一套UV上了。

这个操作是可以批量执行的,当所有的建筑模型都做完了可以全部摆在场景里面打好统一的灯光一键批量烘焙。烘焙后的贴图如下:

在用同样的方法把地面投影也烘焙到接收的面片上,接收的面片就不需要第二套UV了,直接烘焙在第一套默认的UV是就行了。

我们在吧贴图导入Unity之前,先在PS里面整合一下贴图,把模型的烘焙的阴影贴图放在Color贴图的Alpha通道里面。

地面投射的这张阴影贴图我们其实要的是黑色的投影部分,所以我们在PS里面把这张图反转一下,用黑色的部分来作为Alpha通道,这样还可以控制强弱和颜色。

把模型和底下接收阴影的面片一起导出FBX,接下来我们开始在Unity里面写shader来使用这个阴影贴图。

Shader部分的处理很简单,用两套UV采样下贴图然后做乘法计算就行了。

Shader "Custom/Building/Building_SelfShadow"{    Properties    {        _Color("MainColor", Color) = (1,1,1,1)        //RGB存储的是模型的颜色贴图,Alpha贴图储存的是自身的阴影        _MainTexture("MainTexture", 2D) = "white" {}        //用一个参数来控制阴影的强弱        _SelfShadow("SelfShadow",Range(0, 1)) = 0.5    }    SubShader    {        Tags { "RenderType"="Opaque" }        Cull Back        LOD 100        Pass        {            Tags { "LightMode" = "ForwardBase" }            CGPROGRAM            #pragma vertex vert            #pragma fragment frag            #pragma fragmentoption ARB_precision_hint_fastest            #include "UnityCG.cginc"            struct appdata            {                float4 vertex : POSITION;                //获取模型的第一套UV                float2 texcoord : TEXCOORD0;                //获取模型的第二套UV                float2 texcoord1 : TEXCOORD1;            };            struct v2f            {                float4 pos : SV_POSITION;                //用来接收传递模型的两套UV                float4 uv : TEXCOORD0;            };            sampler2D _MainTexture;            fixed _SelfShadow;            fixed4 _Color;            v2f vert (appdata v)            {                v2f o;                o.pos = UnityObjectToClipPos(v.vertex);                //xy的分量存储模型的第一套UV                o.uv.xy = v.texcoord;                //zw的分量存储模型的第二套UV                o.uv.zw = v.texcoord1;                return o;            }            fixed4 frag (v2f i) : SV_Target            {                //用第一套UV采样贴图的RGB得到Color值                fixed3 texCol = tex2D(_MainTexture, i.uv.xy).rgb;                //用第二套UV采样贴图的Alpha得到阴影值                fixed shadow = tex2D(_MainTexture, i.uv.zw).a;                //Color和Shadow相乘,用SelfShadow参数来控制阴影的强度,值为0时没有阴影                fixed3 outputColor = texCol * (shadow * _SelfShadow + (1 - _SelfShadow)) * _Color.rgb;                return fixed4(outputColor, 1);            }            ENDCG        }    }        Fallback "Mobile/Unlit"}

地面投影的Shader

Shader "Custom/Building/Building_Shadow"{    Properties    {        //Color的RGB值控制投影的颜色,Alpha值控制投影的强弱        _Color("MainColor", Color) = (1,1,1,1)        _MainTex("MainTexture", 2D) = "white" {}    }    SubShader    {        Tags         {             "Queue" = "Transparent"            "IgnoreProjector" = "True"            "RenderType" = "Transparent"        }        Cull Back        LOD 100        Pass        {            Tags { "LightMode" = "ForwardBase" }            ZWrite Off            Blend SrcAlpha OneMinusSrcAlpha                        CGPROGRAM            #pragma vertex vert            #pragma fragment frag            #pragma fragmentoption ARB_precision_hint_fastest                        #include "UnityCG.cginc"            struct appdata            {                float4 vertex : POSITION;                float2 texcoord : TEXCOORD0;            };            struct v2f            {                float4 pos : SV_POSITION;                float2 uv : TEXCOORD0;            };            sampler2D _MainTex;            fixed4 _Color;            v2f vert (appdata v)            {                v2f o;                o.pos = UnityObjectToClipPos(v.vertex);                o.uv = v.texcoord;                return o;            }            fixed4 frag (v2f i) : SV_Target            {                           fixed4 col = _Color;                col.a *= tex2D(_MainTex, i.uv).r;                return col;            }            ENDCG        }    }}

这样就是建筑的阴影实现方案,下面一篇文章会介绍下在项目中实际运用的一些优化和流程工具的开发。




来源网址:https://zhuanlan.zhihu.com/p/72357414

鲜花

握手

雷人

路过

鸡蛋