Unity自定义后处理——用偏导数求图片颜色边缘

大家好,我是阿赵。

继续介绍屏幕后处理效果的做法。这次介绍一下用偏导数求图形边缘的技术。

一、原理介绍

先来看例子吧。

这个例子看起来好像是要给模型描边。之前其实也介绍过很多描边的方法,比如沿着法线方向放大模型,或者用NdotV来求边缘,之类。

不过这篇文章所说的内容,其实和模型描边是没有关系的。因为这是屏幕后处理,他针对的并不是模型,所以也不会有法线方向,有观察空间的计算。用偏导数,求的是一张图片的颜色变化。

简单来说,我们要求的是连续像素点之间的颜色变化。

听起来好像很复杂,不过由于已经提供了现成的方法,所以我们直接用就行了。方法就是ddx和ddy。

ddx是求横向像素之间的变化的,可以理解成是当前像素点和横向前一个像素点颜色的变化。

ddy就是纵向像素之间的变化了。

通过ddx和ddy,我们可以求出一张图片颜色变化比较强烈的一些边缘位置。

当求出了这些范围之后,我们可以给他填充不同的颜色,也可以指定背景色,发挥想象力之后,就可以做出一些有趣的效果了。

二、代码实现

1、C#代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class OutlineCtrl : MonoBehaviour
{
    private Material outlineMat;
    public float lineStrength = 1;
    public Color baseColor = Color.white;
    public Color lineColor = Color.black;
    public float powVal = 1;
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    private void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        if(outlineMat == null)
        {
            outlineMat = new Material(Shader.Find("Hidden/azhaoOutline"));
        }
        outlineMat.SetFloat("_lineStrength", lineStrength);
        outlineMat.SetColor("_baseColor", baseColor);
        outlineMat.SetColor("_lineColor", lineColor);
        outlineMat.SetFloat("_powVal", powVal);
        Graphics.Blit(source, destination,outlineMat);
    }
}

2、Shader

Shader "Hidden/azhaoOutline"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
		_lineStrength("LineStrength",Float) = 1
		_lineColor("LineColor",Color) = (0,0,0,1)
		_baseColor("baseColor", Color) = (1,1,1,0)
		_powVal("powVal",Float) = 1
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
			float _lineStrength;
			float4 _lineColor;
			float3 _baseColor;
			float _powVal;
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                UNITY_TRANSFER_FOG(o,o.vertex);
                return o;
            }

            half4 frag (v2f i) : SV_Target
            {
                // sample the texture
                half4 col = tex2D(_MainTex, i.uv);
				float grayscale = col.r * 0.2126729f + col.g * 0.7151522f + col.b * 0.0721750f;
				grayscale = pow(grayscale, _powVal);
				float ddVal = saturate(ddx(grayscale) + ddy(grayscale))*_lineStrength;
				half3 finalCol =  _baseColor.rgb * (1.0 - ddVal) + _lineColor.rgb * ddVal;
				return half4(finalCol, 1);

            }
            ENDCG
        }
    }
}

三、和原图的叠加

稍微做的一点点扩展,之前描绘出来的是纯背景色和线条色,其实我们也不一定要用纯背景色的,比如把偏导数得到的结果,和原图做叠加,就可以做出类似模型描边的效果。

代码很简单,修改一下shader的片段着色器程序就可以:

half4 frag (v2f i) : SV_Target
{
    // sample the texture
    half4 col = tex2D(_MainTex, i.uv);
	float grayscale = col.r * 0.2126729f + col.g * 0.7151522f + col.b * 0.0721750f;
	grayscale = pow(grayscale, _powVal);
	float ddVal = saturate(ddx(grayscale) + ddy(grayscale))*_lineStrength;
	half3 finalCol = col.rgb*(1 - ddVal) + _lineColor.rgb * ddVal;
	return half4(finalCol, 1);

}

可以看出,描边的效果其实没有使用法线计算那么干净清晰。这是因为偏导数依赖于颜色的变化,越分明的变化,结果是越清晰,然后图片的分辨率如果不够大,得出的效果也会比较的模糊。

不过由于它并不依赖于其他数据,只要有颜色,就能计算,所以在屏幕后处理上,就刚好可以做出一些特殊的效果了。

相关推荐
Artistation Game1 天前
九、怪物行为逻辑
游戏·unity·游戏引擎
百里香酚兰1 天前
【AI学习笔记】基于Unity+DeepSeek开发的一些BUG记录&解决方案
人工智能·学习·unity·大模型·deepseek
妙为1 天前
unreal engine5制作动作类游戏时,我们使用刀剑等武器攻击怪物或敌方单位时,发现攻击特效、伤害等没有触发
游戏·游戏引擎·虚幻·碰撞预设
dangoxiba1 天前
[Unity Demo]从零开始制作空洞骑士Hollow Knight第十三集:制作小骑士的接触地刺复活机制以及完善地图的可交互对象
游戏·unity·visualstudio·c#·游戏引擎
先生沉默先2 天前
使用Materialize制作unity的贴图,Materialize的简单教程,Materialize学习日志
学习·unity·贴图
十画_8243 天前
Visual Studio 小技巧记录
unity·visual studio
red_redemption3 天前
cpp,git,unity学习
git·unity·游戏引擎
tealcwu3 天前
【Unity踩坑】Unity更新Google Play结算库
unity·游戏引擎
先生沉默先3 天前
unity 默认渲染管线材质球的材质通道,材质球的材质通道
unity·游戏引擎·材质
白鹭float.3 天前
【Unity AI】基于 WebSocket 和 讯飞星火大模型
人工智能·websocket·unity