欢迎来到我的博客小站。  交流请加我微信好友: studyjava。  也欢迎关注同名公众号:Java学习之道

[转]Unity游戏开发之实现江南水乡效果

  |   0 评论   |   0 浏览

这两天玩了下《江南百景图》,发现里面物体出现的效果很有意思,想了想有大概的实现思路,于是打算在Unity中尝试实现一下。

1.1. 游戏中的效果

仔细看可以观察到,当一些物体进入摄像机视角之后,并不是直接看到这个物体的。二十先看到绘制出轮廓,然后物品的背景色(黑白),再然后才是完整的物品。并且绘制轮廓和背景色出现的时候是类似于消融的效果。如下图顺序的效果。


1.2. 在Unity中模仿的效果

一开始还在想这个轮廓会不会是用描边做的,但是仔细想想应该不是。一方面这种图片的描边应该做不了在图片内部描边,另一方面,放大之后看到轮廓也是不够清晰,估计就是两层图片。

那么整个思路大概分为两部分:

  1. 物体的双层消融效果实现
  2. 消融效果的调用

消融效果实现必然是用到shader。而且这里是2D图像,按道理一般应该用sprite,但是sprite的渲染和平时mesh的有些许不太一样,对此我不太了解。所以我还是用3dMesh的形式,用一个quad显示图片。
首先需要两张图片,一张是轮廓,一张是颜色。分别容噪声去消融。以及顺便控制图片的颜色和透明度。然后用以0-1的值对应整个流程,通过控制这一个值来控制图片的显示。

大体思路如下图:

如何去调用的话,目前有几个想法。

  1. 通过OnBecameVisible和OnBecameInvisible函数让物体在被摄像机看到之后,开始将value值从0到1变化,物体离开摄像机之后从1到0。
  2. 用一张灰度图代表整个地图,在摄像机内的部分缓慢变为1,摄像机外的部分缓慢变成0,然后直接从shader中读取这个灰度图对应坐标的数字用来控制消融。

方法1对于我来说比较容易实现,但是这样子就意味着每一个物体上都需要挂在一个脚本来控制消融。当场景里面有非常多的物体的时候,不知道性能消耗大不大。
方法2的话,自我认为大部分工作都是GPU的,应该效率会高一些。但是我不知道怎么做…

还是用方法1简单做做就好了。

3.1. 图片

首先要有用来显示的图片,分图层画好轮廓和颜色。然后分别导出成png格式的图片。
(不会画画,瞎画的)

3.2. shader

我就直接用ShaderGraph连连看了,比较方便。

有一个点,当Remap节点输入值超过InMinMax范围的时候,输出值也会超出OutMinMax的范围,这不是我想要的。于是稍微封装一下搞了一个MyRemap的SubGraph,如下。

整体节点的连接如下,就不细说了。

大体就是用世界坐标(加上了一个物体坐标,免得消融的时候相邻的物体都太连贯了)作为噪声的uv。通过各种加减乘除以及Remap(即将0-1里面每部分的值重新映射到相应的值上)实现消融、透明度、黑白彩色(或者可以叫饱和度?)的变换。并且颜色和alpha分开控制。

大图不太清晰,下面拆分成几部分放图片。


3.3. 代码

代码比较简单,不多说了。不知道OnBecameVisibleOnBecameInvisible可以去查查API。

控制value值可以向下面一样线性的控制,或者用插值平滑一下。如果用上AnimationCurve也许效果会更好吧。

using UnityEngine;

public class Mix2D : MonoBehaviour
{
    private Material _material;
    private bool _isSeeing;
    private static readonly int Vector1Value = Shader.PropertyToID("Vector1_Value");
    private float _value;
    
    public float speed = 0.3f;
    
    private void Awake()
    {
        _material = GetComponent<MeshRenderer>().material;
    }
    
    private void Update()
    {
        
        _value = Mathf.Clamp01(_isSeeing ? _value + speed * Time.deltaTime : _value - speed * Time.deltaTime * 5);
        
        
        _material.SetFloat(Vector1Value, _value);
    }

    
    private void OnBecameVisible()
    {
        _isSeeing = true;
    }
    private void OnBecameInvisible()
    {
        _isSeeing = false;
    }
}

在完成上面的效果之前,有一个先前的版本。是如下的形式,这样子的话消融的边太硬了。

效果如下。

这个方法只是根据自己的猜想实现的一种方法,和《江南百景图》中的方法不一定相同。
(话说我都做完了才发现,原来游戏中轮廓的线条明显比显示完成之后的线条要粗,也就是说显示完成之后轮廓应该是不在了的。估计是完整的图片并不是像我这里一样只有颜色,不过算了我也懒得改了,反正就是做着玩)

而且我这个将轮廓和颜色混合(当然用透明度做了一些插值,不是直接混合,因为直接加或乘都会让结果颜色改变),效果和直接渲染两个sprite重叠是不一样的。左为两个sprite,右为用我写的shader渲染。但是无伤大雅了吧。也许用多pass渲染会好些。


标题:[转]Unity游戏开发之实现江南水乡效果
作者:shirlnGame
地址:https://www.mmzsblog.cn/articles/2021/05/12/1620781463023.html
-----------------------------
如未加特殊说明,此网站文章均为原创。
网站转载须在文章起始位置标注作者及原文连接,否则保留追究法律责任的权利。
公众号转载请联系网站首页的微信号申请白名单!

个人微信公众号 ↓↓↓                 

微信搜一搜爱上游戏开发