【转载】Unity 使用 RenderTexture 实现实时阴影绘制

585 阅读3分钟

文章来源: 腾讯独立游戏孵化器
原文链接: Unity使用RenderTexture实现实时阴影绘制 | feng

原文写得很好,但是主题格式我不太喜欢,所以我对文章的格式进行了修整,方便自己日后阅读

正文

一般实时阴影主要还在出现在角色、怪物的脚底为了让场景表现的更加逼真,实现起来主要会用到这三种方式:

  1. 脚底放个阴影图片跟着主角动,
  2. 通过摄像机的 RenderRexture 绘制显示,
  3. 直接使用 Unity 的 Projector 组件。

在 2D 场景中,为了方便,游戏中通常会直截了当的放一快灰色背景,实现的效果虽然少了几分逼真性,但也足够用。

但是如果想要在 3D 场景绘制脚底阴影,直接放阴影图就不合适了,为了让阴影效果更加逼真,下面就给大家介绍下使用 RenderTexture 的方式实现实时阴影绘制的效果吧。

基本思路

其实就是多用了一个摄像机,将照相机把你需要照到的主角的内容渲染到一张单独的面片上,这个面片就摆在人脚底作为它的阴影。看起来像简单版的一个小地图:只有主角的小地图

场景中所需要的对象如图,对于 map 节点显示 RenderTexture 的地方只要空的材质球就行了,要呈现摄像机照射的内容。直接用 Plane 对象或 Quad 对象都可以的。

此外,为了让阴影真实点,显示阴影的面片一般是 灰色透明 的,我们还要单独设置下,不然默认摄像机照出来的角色就是我们真实看到的世界:

紧接着还需要一个脚本告诉摄像机把内容写到我们的 map 面片上。

在场景中的 display 节点身上绑定脚本 ShadowScript

public class ShadowScript: MonoBehaviour{
    public GameObject display; // 角色形象
    private GameObject ShadowCamera;
    private GameObject map;
    private RenderTexture mTex = null; // 即为显示阴影的 RenderTexutre
    public int AntiAliasing = 4;
    Transform child;

    // Use this for initialization
    void Start(){
        child = this.transform.FindChild("qiangu1");
        map = child.FindChild("child/map").gameObject;
        ShadowCamera = child.FindChild("child/camera").gameObject;

        if (!display)
            display = this.transform.parent.gameObject;

        mTex = new RenderTexture(2000, 2000, 0);
        mTex.name = "Shadow" + GetInstanceID();

        Camera mCamera = ShadowCamera.GetComponent <Camera> ();
        mCamera.cullingMask = GetLayerMask(display.gameObject.layer);
        mCamera.targetTexture = mTex;
    }

    public LayerMask GetLayerMask(int layer){
        LayerMask mask = 0;
        mask |= 1 << layer;
        return mask;
    }

    // Update is called once per frame
    void Update()if (display != null){
            mTex.anisoLevel = AntiAliasing;
        }
        map.renderer.maternTexture = mTex;
    }
}

很简单吧?跑起来直接就可以看到实时的阴影效果了!

自己给小骨模型做了一个御剑的动画!然而一时找不到好看的剑模型,我只好找了张 jpg 的剑扣一个出来,也是大费周折,不过可以看出来阴影效果比较好,因为阴影作为一个独立的 GameObject 直接少了很多调层级的麻烦,现在是只显示主角阴影的效果。

但是当我把剑贴图所在的面片 layer 调整的也和主角模型一样的时候,发现居然还是看不到剑的阴影

这个时候还得专门针对 map(显示阴影的面片)材质写一个Shader,就不能用默认的 Transparent/Diffuse 了,而且最终剑这种透明贴图的还需要使用 Unlit/Transparent,新添加的 Shader 详细代码如下:

/// 作为带 Alpha 通道的模型贴图的 shader
Shader "MyShader / AlphaSkinShader"{

    Properties{
        _Color("Main Color", Color) = (1, 1, 1, 1)
        _MainTex("Base(RGB)", 2D) = "white"{}
        _Cutoff("Base Alpha cutoff", Range(0, 0.9)) = 0.2
    }

    SubShader{

        Tags{"Queue" = "Transparent"}
        LOD 200
        Lighting Off
        ZWrite Off
        Cull off
        Blend SrcAlpha OneMinusSrcAlpha

        Pass{
            CGPROGRAM

            # pragma vertex vert
            # pragma fragment frag
            # include "UnityCG.cginc"

            struct appdata_t{
                float4 vertex: POSITION;
                float4 color: COLOR;
                float2 texcoord: TEXCOORD;
            };

            struct v2f{
                float4 vertex: SV_POSITION;
                float4 color: COLOR;
                float2 texcoord: TEXCOORD;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            float _Cutoff;

            v2f vert(appdata_t v){
                v2f o;
                o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
                o.color = v.color;
                o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
                return o;
            }

            float4 _Color;
            half4 frag(v2f i): SV_Target{
                half4 col = tex2D(_MainTex, i.texcoord);
                if (col.a < _Cutoff){
                    clip(col.a - _Cutoff);
                }
                else{
                    col.rgb = col.rgb * float3(0, 0, 0);
                    col.rgb = col.rgb + _Color;
                    col.a = _Color.a;
                }
                return col;
            }
            ENDCG
        }
    }

    FallBack "Diffuse"

修改之后靓丽的运行效果: