Unity2019实现背景透明桌宠

Unity2019实现背景透明桌宠

其实之前已经有相当多的教程了,比如:

如何使Unity窗口透明置顶,且鼠标可以穿透点击 - 踢踢打踢踢www.cwbeta.com图标

但我在尝试时始终无法使用,为此还花25刀买了插件,最后发现是2019新增的设置导致原本透明的部分变为win10的主题色:

一定要关掉

代码:

using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System;
using UnityEngine;
using System.Diagnostics;

public class WindowManager : MonoBehaviour
{
    private static WindowManager instance;

    public static WindowManager Instance
    {
        get
        {
            return instance;
        }
    }
    // Use this for initialization
    [SerializeField]
    private Material m_Material;
    private struct MARGINS
    {
        public int cxLeftWidth;
        public int cxRightWidth;
        public int cyTopHeight;
        public int cyBottomHeight;
    }
    // Define function signatures to import from Windows APIs
    [DllImport("user32.dll")]
    private static extern IntPtr GetActiveWindow();
    [DllImport("user32.dll")]
    private static extern int SetWindowLong(IntPtr hWnd, int nIndex, uint dwNewLong);
    [DllImport("Dwmapi.dll")]
    private static extern uint DwmExtendFrameIntoClientArea(IntPtr hWnd, ref MARGINS margins);

    public delegate bool WNDENUMPROC(IntPtr hwnd, uint lParam);
    [DllImport("user32.dll", SetLastError = true)]
    public static extern bool EnumWindows(WNDENUMPROC lpEnumFunc, uint lParam);

    [DllImport("user32.dll", SetLastError = true)]
    public static extern IntPtr GetParent(IntPtr hWnd);
    [DllImport("user32.dll")]
    public static extern uint GetWindowThreadProcessId(IntPtr hWnd, ref uint lpdwProcessId);


    [DllImport("kernel32.dll")]  
    public static extern void SetLastError(uint dwErrCode);


    // Definitions of window styles
    const int GWL_STYLE = -16;
    const uint WS_POPUP = 0x80000000;
    const uint WS_VISIBLE = 0x10000000;
    public const int width = 300;
    public const int height = 300;
    void Start()
    {
        SetWindowScreen(width, height);
        var margins = new MARGINS() { cxLeftWidth = -1 };
        var hwnd = GetProcessWnd();
        SetWindowLong(hwnd, GWL_STYLE, WS_POPUP | WS_VISIBLE);
        DwmExtendFrameIntoClientArea(hwnd, ref margins);

    }

    void SetWindowScreen(int width, int height)
    {
        Screen.SetResolution(width, height, false);
    }

    void OnRenderImage(RenderTexture from, RenderTexture to)
    {
        Graphics.Blit(from, to, m_Material);
    }

    public static IntPtr GetProcessWnd()
    {
        IntPtr ptrWnd = IntPtr.Zero;
        uint pid = (uint)Process.GetCurrentProcess().Id;  // 当前进程 ID  

        bool bResult = EnumWindows(new WNDENUMPROC(delegate (IntPtr hwnd, uint lParam)
       {
           uint id = 0;

           if (GetParent(hwnd) == IntPtr.Zero)
           {
               GetWindowThreadProcessId(hwnd, ref id);
               if (id == lParam)    // 找到进程对应的主窗口句柄  
               {
                   ptrWnd = hwnd;   // 把句柄缓存起来  
                   SetLastError(0);    // 设置无错误  
                   return false;   // 返回 false 以终止枚举窗口  
               }
           }

           return true;

       }), pid);
        return (!bResult && Marshal.GetLastWin32Error() == 0) ? ptrWnd : IntPtr.Zero;
    }

}

Shader:

Shader "Custom/MakeTransparent"
{
    Properties
    {
        _MainTex ("Base (RGB)", 2D) = "white" { }
        _TransparentColorKey ("Transparent Color Key", Color) = (0, 1, 0, 1)
        _TransparencyMargin ("Transparency Margin", Float) = 0.01
    }
    SubShader
    {
        Pass
        {
            Tags { "RenderType" = "Opaque" }
            LOD 200
            
            CGPROGRAM
            
            #pragma vertex VertexShaderFunction
            #pragma fragment PixelShaderFunction
            
            #include "UnityCG.cginc"
            
            struct VertexData
            {
                float4 position: POSITION;
                float2 uv: TEXCOORD0;
            };
            
            struct VertexToPixelData
            {
                float4 position: SV_POSITION;
                float2 uv: TEXCOORD0;
            };
            
            VertexToPixelData VertexShaderFunction(VertexData input)
            {
                VertexToPixelData output;
                output.position = UnityObjectToClipPos(input.position);
                output.uv = input.uv;
                return output;
            }
            
            sampler2D _MainTex;
            float3 _TransparentColorKey;
            float _TransparencyMargin;
            
            float4 PixelShaderFunction(VertexToPixelData input): SV_Target
            {
                float4 color = tex2D(_MainTex, input.uv);
                
                float deltaR = abs(color.r - _TransparentColorKey.r);
                float deltaG = abs(color.g - _TransparentColorKey.g);
                float deltaB = abs(color.b - _TransparentColorKey.b);
                
                if (deltaR < _TransparencyMargin && deltaG < _TransparencyMargin && deltaB < _TransparencyMargin)
                {
                    return float4(0.0f, 0.0f, 0.0f, 0.0f);
                }
                
                return color;
            }
            ENDCG
            
        }
    }
}

用法:

  1. 关闭上述设置
  2. 用此Shader创建材质球
  3. 脚本挂在摄像机上并拖入上面的材质球
  4. 保证摄像机的Soild Color与材质球中的颜色一致
  5. 打包运行

本质就是抠图,加上抗锯齿的PP效果挺不错,顺便了解了下Windows的API

至于后续的功能会慢慢更新:表情管理、动作语音管理、Shader等

发布于 2019-11-17

文章被以下专栏收录