之前学习的东西其实都是为了做自己的个人毕业设计准备的,包括随机地图生成,事件,人物移动,轨道摄像机等等。现在算是有了一个雏形的DEMO了。
基础的射击,武器系统,道具拾取系统等功能也做了出来,敌人追踪AI也添加进去了,只需要往后继续填充素材即可,当然不排除由于架构不够好可能要重构自己代码的过程。

但是这两天我发现一件很严重的事情,我的游戏画面表达极差。什么意思呢?给大家截个图看一看。

unity shader光照 背面不剔除 unity背光面很黑_Processing


unity shader光照 背面不剔除 unity背光面很黑_Processing_02


除了墙体贴图和色彩缺乏之外,这个阴影的质量完全就是没有任何柔化,并且人物进去之后在没有光源的情况下会直接看不到位置。

作为一个注重视觉表达,将来也想兼做TA的程序,这是完全不能容忍的事情。

但是质量很高的渲染我也做不出来,毕竟我的渲染功底还停留在刚会写一些Shader的阶段,所以这里我打算先做一些为了解决这个问题学习到的处理方式,日后学习了更好的渲染方法之后再来继续做场景渲染优化。

我们这里使用的是非URP渲染管线下的Post Processing,安装方式如下:

1.在Package Manager中找到Post Processing 包,将其install

2.创建配置文件Profile

3.创建空物体并且添加【Post Process Volume】组件,并且将配置文件赋值给它

4.勾选IsGlobal全局响应

5.将创建的空物体右上角选为【Post Process Layer】

6.选中主相机并且添加【Post Process Layer】组件,并且选中刚刚的Layer层

7.然后,就可以通过Add effect面板来添加一些常见的屏幕后处理效果了:

unity shader光照 背面不剔除 unity背光面很黑_游戏_03


这些效果都可以在网上找到详细的解释和使用方法,我这里就不多解释了,可以参考下面这个视频

Post Processing讲述

重点在于如何用代码来获取并且控制这些屏幕后处理效果:
(以下笔记摘自上方视频)
我们获取到这个组件的方法是通过得到Profile文件来操纵整个设置的。
(Profile文件会和Volum,即容器里面的所有设置同步更新)

第一种控制方式建立在你拥有一个添加了【Post Process Volume】组件的物体
1.创建PPController脚本并将其赋给添加了【Post Process Volume】组件的空物体。

2.通过得到【Post Process Volume】组件中的Profile组件对应的效果来操纵。

unity shader光照 背面不剔除 unity背光面很黑_Processing_04


unity shader光照 背面不剔除 unity背光面很黑_Time_05


比如上方就是一个按下空格之后随着时间改变的边角压暗效果。

第二种情况建立在你并不拥有这个Volume物体之下的时候:
(这种时候需要创建一个)
关于ScriptableObject的使用

我将视频里所有的代码放在这里,包括实现了下方的动态受击动画:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering.PostProcessing;

public class PPController : MonoBehaviour
{
    private PostProcessVolume volume;
    private Vignette vignette;

    private bool isDamaging;
    private bool isBack;
    [SerializeField] private Color hurtColor;//受击颜色
    [SerializeField] private float hurtSpeed;//受击速度
    private void Start()
    {
        volume = GetComponent<PostProcessVolume>();
        volume.profile.TryGetSettings(out vignette);
    }


    private void Update() {
        if(Input.GetKeyDown(KeyCode.Space))
        {
            isDamaging = true;
        }

        if(isDamaging && isBack == false)
        {
            vignette.intensity.value += hurtSpeed * Time.deltaTime;
            vignette.color.value = hurtColor;

            if(vignette.intensity.value >= 0.5f)//边角压暗上限
            {
                isDamaging = false;
                isBack = true;
            }
        }

        if(isBack)
        {
            vignette.intensity.value -= 3*hurtSpeed * Time.deltaTime;
            if(vignette.intensity.value <=0.05f)//为了避免计算精度问题
            {
                isBack = false;
            }
        }
    
    }
}


#region Have PostProcessVolume
// private PostProcessVolume volume;//体积,这里指的是容器,容器里面含有各个设置
//     private Vignette vignette;//边角压暗效果
//     private Bloom bloom;

//     private bool isFading;//控制渐变时间
//     [SerializeField] private float fadeSpeed = 0.35f;

//     private void Start()
//     {
//         volume = GetComponent<PostProcessVolume>();
//         volume.profile.TryGetSettings(out vignette);//获得边角压暗设置
//         volume.profile.TryGetSettings(out bloom);//获得柔光设置
//     }

//     private void Update()
//     {
//         if(Input.GetKeyDown(KeyCode.Space))
//         {
//             // vignette.enabled.Override(false);//禁用效果
//             // vignette.intensity.value = 1.0f;//FloatPara -> Float
//             // vignette.intensity.Override(1.0f);//重写值为1

//             isFading = true;
//         }

//         if(isFading)
//         {
//             vignette.intensity.value += fadeSpeed * Time.deltaTime;

//             if(vignette.intensity.value >= 0.75f)
//             {
//                 isFading = false;
//             }
//         }

//     }


#endregion

#region Didn't Have PostProcessVolume
//这种方法建立在场景中没有搭载volume组件的时候
    // private PostProcessVolume volume;
    // private Vignette vignette;

    // private void Start()
    // {
    //     //官方文档解释称,如果你想要创建一些对象,而这些对象并不依赖GameObject,不使用GetComponent之类的方法,你可以从ScriptableObject类派生
    //     vignette = ScriptableObject.CreateInstance<Vignette>();//创建vignette类型
    //     vignette.enabled.Override(true);
    //     vignette.intensity.Override(1.0f);

    //     //下面这个方法会在场景中快速创建一个Volume物体出来  参数:渲染层,优先级,配置
    //     volume = PostProcessManager.instance.QuickVolume(gameObject.layer,1,vignette);
    
    // }

    // private void Update() {
    //     vignette.intensity.value = Mathf.Sin(Time.realtimeSinceStartup);
    // }

    // private void OnDestroy() {
    //     //删除Volume 参数:volume物体,是否删除配置文件,是否删除游戏对象
    //     RuntimeUtilities.DestroyVolume(volume,true,true);
    // }

#endregion

unity shader光照 背面不剔除 unity背光面很黑_Time_06


接下来回到正题,我该如何使用这些效果来优化自己的画面呢?

我目前使用了的一系列效果如下:

1.平行光阴影软化,简而言之就是拉低阴影强度,让它柔化一些,不至于阴影内部黑的一塌糊涂

unity shader光照 背面不剔除 unity背光面很黑_unity_07


2.根据障碍物生成的位置来弄一些颜色

我们根据障碍物生成占整个地图的具体位置使用Lerp插值来给这些障碍物赋予颜色

unity shader光照 背面不剔除 unity背光面很黑_游戏_08

效果:

unity shader光照 背面不剔除 unity背光面很黑_Time_09


同样的,我们希望我们的地砖最好也能有相同的表现效果:

使用白灰渐变

unity shader光照 背面不剔除 unity背光面很黑_游戏_10


注:地图不一样是因为每一次的地图都是随机生成的联通地图。3.添加自动的间接光照

上面出现的物体背面全黑的最大原因是因为我这里的间接光照烘焙是默认关闭的。

关闭时:没有间接光照,背光面呈现全黑,并且画面整体偏暗

unity shader光照 背面不剔除 unity背光面很黑_Processing_11


开启:

再Lighting面板下,由于我们的地图是随机生成的,所以无法使用static去控制烘焙。我们这里勾选自动生成【Auto Generate】即可。

unity shader光照 背面不剔除 unity背光面很黑_Time_12


开启后效果:

unity shader光照 背面不剔除 unity背光面很黑_Time_13


注:由于开启Auto Generte非常消耗Unity性能,所以在最终没有做完游戏之前,我会选择关掉。并且如何在随机地图生成的模式下生成光照贴图是我目前的知识盲区,等日后学习到相关知识之后我会再做笔记分享。

4.一系列屏幕后处理效果
包括但不限于开启了轻度边角压暗,环境光遮蔽等效果

5.日后会做出白天黑夜切换的两种地图,凸显灯光效果

今日最后完成效果:

unity shader光照 背面不剔除 unity背光面很黑_游戏_14