Talk is Cheap, Show me your CODE!
环境
Unity : 2018.2.11f1 Pipeline : BRP
SoftParticleCommon.cg
#ifndef __SOFT_PARTICLE_COMMON_H__
#define __SOFT_PARTICLE_COMMON_H__
#include "UnityCG.cginc"
#if defined(_SOFT_PARTICLE_ON)
#if defined(_SOFT_PARTICLE_DEPTH_MAP_DEF_ON)
sampler2D _CameraDepthTexture;
#endif
half _InvFade;
#define SOFT_PARTICLE_V2F(idx) float4 projPos : TEXCOORD##idx;
#define SOFT_PARTICLE_VERT(o) \
o.projPos = ComputeScreenPos(o.vertex); \
COMPUTE_EYEDEPTH(o.projPos.z);
#define SOFT_PARTICLE_VERT1(o, vertex) \
o.projPos = ComputeScreenPos(vertex); \
COMPUTE_EYEDEPTH(o.projPos.z);
#define SOFT_PARTICLE_FRAG_FADE(i, out_v) \
fixed offSP = step(10.0, _InvFade); \
out_v = saturate(_InvFade * (LinearEyeDepth (SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos))) - i.projPos.z)); \
out_v = lerp(out_v, 1.0, offSP);
#else
#define SOFT_PARTICLE_V2F(idx)
#define SOFT_PARTICLE_VERT(o)
#define SOFT_PARTICLE_VERT1(o, vertex)
#define SOFT_PARTICLE_FRAG_FADE(i, out_v) out_v = 1.0;
#endif
#endif
意思有两句 fixed offSP = step(10.0, _InvFade); 和 out_v = lerp(out_v, 1.0, offSP); 是为了避免由些特效同学再制作 UI 特效的时候没效果 UI 特效的 BUG
这里我就不使用变体了,计算量不算大,直接 step, lerp 来伪分支即可
Alpha blend
soft particle fade 主要用于 alpha 通道即可
Shader "cgwell/Alpha Blended"
{
Properties
{
_TintColor ("Tint Color", Color) = (0.5,0.5,0.5,0.5)
_MainTex ("Particle Texture", 2D) = "white" {}
[HideInInspector]_Center ("Center",Vector) = (0,0,0,1)
[HideInInspector]_Scale ("Scale",Vector) = (1,1,1,1)
[HideInInspector]_Normal ("Normal",Vector) = (0,0,1,0)
_InvFade("Soft Particles Factor", Range(0.01,10.0)) = 10.0
}
Category
{
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
Blend SrcAlpha OneMinusSrcAlpha
Cull Off Lighting Off ZWrite Off Fog { Mode Off}
SubShader
{
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
#pragma multi_compile _ _SOFT_PARTICLE_ON
#define _SOFT_PARTICLE_DEPTH_MAP_DEF_ON
#include "UnityCG.cginc"
#include "../../../Shaders/Includes/CG/SoftParticleCommon.cginc"
sampler2D _MainTex;
fixed4 _TintColor;
struct appdata_t
{
float4 vertex : POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
SOFT_PARTICLE_V2F(1)
};
float4 _MainTex_ST;
float4 _Center;
float4 _Scale;
float4 _Normal;
uniform float4x4 _Camera2World;
v2f vert (appdata_t v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.color = v.color;
o.texcoord = TRANSFORM_TEX(v.texcoord,_MainTex);
SOFT_PARTICLE_VERT(o)
return o;
}
fixed4 frag (v2f i) : SV_Target
{
float fade;
SOFT_PARTICLE_FRAG_FADE(i, fade)
fixed4 col = saturate(2.0f * i.color * _TintColor * tex2D(_MainTex, i.texcoord));
col.a *= fade;
return col;
}
ENDCG
}
}
}
}
Additive blend
主要是减少亮度,也就是 RGB 整体降低
Shader "cgwell/Dissolution_Add" {
Properties {
_TintColor ("Diffuse Color", Color) = (0.6985294,0.6985294,0.6985294,1)
_MainTex ("Diffuse Texture", 2D) = "white" {}
_N_mask ("N_mask", Float ) = 0.3
_MaskTexture ("Mask Texture", 2D) = "white" {}
_C_BYcolor ("C_BYcolor", Color) = (1,0,0,1)
_N_BY_QD ("N_BY_QD", Float ) = 3
_N_BY_KD ("N_BY_KD", Float ) = 0.01
[HideInInspector]_Cutoff ("Alpha cutoff", Range(0,1)) = 0.5
_InvFade("Soft Particles Factor", Range(0.01,3.0)) = 10.0
}
SubShader {
Tags {
"IgnoreProjector"="True"
"Queue"="Transparent"
"RenderType"="Transparent"
}
Pass {
Name "FORWARD"
Tags {
"LightMode"="ForwardBase"
}
Blend One One
ZWrite Off
Cull Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "../../../Shaders/Includes/CG/SoftParticleCommon.cginc"
#pragma multi_compile _ _SOFT_PARTICLE_ON
#define _SOFT_PARTICLE_DEPTH_MAP_DEF_ON
#pragma target 3.0
uniform sampler2D _MaskTexture; uniform float4 _MaskTexture_ST;
uniform sampler2D _MainTex; uniform float4 _MainTex_ST;
uniform float4 _TintColor;
uniform float _N_mask;
uniform float _N_BY_KD;
uniform float4 _C_BYcolor;
uniform float _N_BY_QD;
struct VertexInput {
float4 vertex : POSITION;
float2 texcoord0 : TEXCOORD0;
float4 vertexColor : COLOR;
};
struct VertexOutput {
float4 pos : SV_POSITION;
float2 uv0 : TEXCOORD0;
float4 vertexColor : COLOR;
SOFT_PARTICLE_V2F(1)
};
VertexOutput vert (VertexInput v) {
VertexOutput o = (VertexOutput)0;
o.uv0 = v.texcoord0;
o.vertexColor = v.vertexColor;
o.pos = UnityObjectToClipPos(v.vertex );
SOFT_PARTICLE_VERT1(o, o.pos)
return o;
}
float4 frag(VertexOutput i) : COLOR {
float4 _MainTex_var = tex2D(_MainTex,TRANSFORM_TEX(i.uv0, _MainTex));
float vertColMulMask = (i.vertexColor.a * _N_mask);
float maskTexture_r = tex2D(_MaskTexture,TRANSFORM_TEX(i.uv0, _MaskTexture)).r;
float isMaskRGreaterThanVertColMulMask = step(vertColMulMask,maskTexture_r);
float isMaskRNotGreaterThanVertColMulMask = step(maskTexture_r,vertColMulMask);
float vertColMulMaskIsGreaterThan_MaskRPlus_N_BY_KD = step((maskTexture_r+_N_BY_KD),vertColMulMask);
float node_1274 = (isMaskRNotGreaterThanVertColMulMask-vertColMulMaskIsGreaterThan_MaskRPlus_N_BY_KD);
float node_6450 = (isMaskRNotGreaterThanVertColMulMask+node_1274);
float3 node_7666 = ((node_1274*_C_BYcolor.rgb)*_N_BY_QD);
float3 emissive = (_TintColor.a*(((_TintColor.rgb*_MainTex_var.rgb)*node_6450)+node_7666));
float3 finalColor = emissive + (_TintColor.a*node_7666);
float node_6644 = (_TintColor.a*(_MainTex_var.a*node_6450));
fixed4 col = saturate(fixed4(finalColor,node_6644));
float fade;
SOFT_PARTICLE_FRAG_FADE(i, fade)
col *= fade;
return col;
}
ENDCG
}
}
}
因为这些特效之前使用了比较多特效材质的 shader 都是每有这个 _InvFade("Soft Particles Factor", Range(0.01,3.0)) = 0.1 //1.0 属性的,并且优化之前我忘记将默认值线调整到一个看着比较好统一值
然后 _InvFade 都默认是 1.0 了,效果不是很好,如果需要对几千个特效的材质统一处理默认值未 0.1,那么这么多个材质,手动去调整肯定要死人,所以就写个工具批量处理一下即可
UniformSetSoftParticleThreshold.cs
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using UnityEditor;
using UnityEngine;
public class UniformSetSoftParticleThreshold : EditorWindow
{
private static int _InvFade = Shader.PropertyToID("_InvFade");
[MenuItem("Tools/统一调整所有SoftParticleThreshold...")]
public static void _Show()
{
var win = EditorWindow.GetWindow<UniformSetSoftParticleThreshold>();
win.Show();
}
private float uniformValue = 0.1f;
private bool adjusting = false;
private int doingIDX = -1;
private int len = -1;
private bool error = false;
private int handlePerFrame = 5;
private List<string> assetPaths;
private Stopwatch sw;
private void OnEnable()
{
if (assetPaths == null)
{
assetPaths = new List<string>();
}
}
private void OnGUI()
{
uniformValue = EditorGUILayout.FloatField("Uniform SoftParticle Threshold", uniformValue);
const int min = 1, max = 20;
handlePerFrame = EditorGUILayout.IntSlider("Hanlde Per Frame", handlePerFrame, min, max);
var srcEnabled = GUI.enabled;
if (adjusting)
{
GUI.enabled = false;
}
if (GUILayout.Button("Adjust"))
{
var filterPaths = new string[]
{
"Assets/LuaFramework/Effect",
"Assets/LuaFramework/EffectNew"
};
if (assetPaths == null) assetPaths = new List<string>();
else assetPaths.Clear();
foreach (var filterPath in filterPaths)
{
var guids = AssetDatabase.FindAssets("t:Material", new string[] { filterPath });
foreach (var guid in guids)
{
var assetPath = AssetDatabase.GUIDToAssetPath(guid);
assetPaths.Add(assetPath);
}
}
doingIDX = 0;
len = assetPaths.Count;
adjusting = true;
sw = new Stopwatch();
sw.Start();
UnityEngine.Debug.Log("Uniform SoftParticle Threshold Start!");
}
GUI.enabled = srcEnabled;
}
private void Update()
{
if (adjusting && assetPaths != null && assetPaths.Count > 0)
{
if (doingIDX == -1 && len == -1)
{
adjusting = false;
return;
}
if (doingIDX >= len)
{
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
adjusting = false;
sw.Stop();
UnityEngine.Debug.Log(
string.Format("Uniform SoftParticle Threshold Complete! handle:{0}, len:{1}, et:{2}",
doingIDX, len, sw.Elapsed
));
return;
}
var cancel = EditorUtility.DisplayCancelableProgressBar(
"Uniform SoftParticle Threshold",
string.Format("{0}/{1}", (doingIDX + 1), len),
(float)(doingIDX + 1) / len);
if (cancel)
{
adjusting = false;
sw.Stop();
UnityEngine.Debug.Log(
string.Format("Uniform SoftParticle Threshold Cancel! handle:{0}, len:{1}, et:{2}",
doingIDX, len, sw.Elapsed
));
return;
}
var n = handlePerFrame;
while (n-- > 0)
{
if (doingIDX >= len)
return;
var assetPath = assetPaths[doingIDX++];
try
{
var mat = AssetDatabase.LoadAssetAtPath<Material>(assetPath);
if (mat.HasProperty(_InvFade))
{
mat.SetFloat(_InvFade, uniformValue);
}
}
catch (System.Exception er)
{
error = true;
adjusting = false;
sw.Stop();
UnityEngine.Debug.Log(
string.Format("Uniform SoftParticle Threshold error, idx : {0}, len : {1}\nassetPath : {2}\nerror : \n{3}",
doingIDX, len, assetPath, er
));
}
}
}
}
}
CustomGraphicsSettings.cs
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;
public enum QualityLevel
{
Low = 1,
Middle = 2,
High = 3,
Ultra = 4,
}
public class CustomGraphicsSettings
{
public static void InitGraphicSetting()
{
SceneManager.activeSceneChanged -= Instance.OnActiveSceneChanged;
SceneManager.activeSceneChanged += Instance.OnActiveSceneChanged;
SceneManager.sceneUnloaded -= Instance.OnSceneUnloaded;
SceneManager.sceneUnloaded += Instance.OnSceneUnloaded;
Instance.srcShadows = QualitySettings.shadows;
Instance.srcAntiAliasing = QualitySettings.antiAliasing;
var powerSaveMode = PlayerPrefs.GetInt(kPowerSaveModeKey, 0) == 1;
PowerSaveMode = powerSaveMode;
var graphicsQualityLevel = Instance.GetGraphicsQualityLevel();
GraphicsQualityLevel = graphicsQualityLevel;
Instance.DeviceAdapterQualityLevel = GraphicsQualityLevel;
}
public static bool PowerSaveMode
{
get
{
return Application.targetFrameRate < 40;
}
set
{
var cv = PlayerPrefs.GetInt(kPowerSaveModeKey, -1);
var nv = value ? 1 : 0;
if (cv != nv)
{
PlayerPrefs.SetInt(kPowerSaveModeKey, nv);
}
var fps = value ? 30 : 60;
Application.targetFrameRate = fps;
}
}
public static QualityLevel GraphicsQualityLevel
{
get
{
return Instance.GetGraphicsQualityLevel();
}
set
{
if ((int)value != PlayerPrefs.GetInt(kGraphicsQualityLevelKey, -1))
{
PlayerPrefs.SetInt(kGraphicsQualityLevelKey, (int)value);
Instance.SetGraphicsQualityLevel(value);
if (null != onGraphicsQualityLevelChanged)
{
onGraphicsQualityLevelChanged.Invoke(value);
}
}
}
}
private static CustomGraphicsSettings Instance
{
get
{
if (null == instance)
{
instance = new CustomGraphicsSettings();
}
return instance;
}
}
public static Action<QualityLevel> onGraphicsQualityLevelChanged;
private static CustomGraphicsSettings instance = null;
public QualityLevel DeviceAdapterQualityLevel
{
get; private set;
}
public Dictionary<string, CustomLightMapData[]> LightMapRecord
{
get
{
return lightMapRecord;
}
set
{
lightMapRecord = value;
}
}
private int srcAntiAliasing;
private ShadowQuality srcShadows;
private const string kPowerSaveModeKey = "graphics.setting.power.save.mode";
private const string kGraphicsQualityLevelKey = "graphics.setting.quality.level";
private QualityLevel AnalysicDeviceLevel()
{
if (SystemInfo.processorFrequency >= 2500 &&
SystemInfo.processorCount >= 8 &&
SystemInfo.systemMemorySize >= (6 * 1024) &&
SystemInfo.graphicsMemorySize >= (2 * 1024) &&
SystemInfo.graphicsShaderLevel >= 30 &&
SystemInfo.graphicsMultiThreaded &&
SystemInfo.supportsShadows &&
SystemInfo.supportsInstancing &&
SystemInfo.supports32bitsIndexBuffer
)
{
return QualityLevel.Ultra;
}
else if (SystemInfo.processorFrequency >= 2000 &&
SystemInfo.processorCount >= 4 &&
SystemInfo.systemMemorySize >= (4 * 1024) &&
SystemInfo.graphicsMemorySize >= (1 * 1024) &&
SystemInfo.graphicsShaderLevel >= 20
)
{
return QualityLevel.High;
}
else if (SystemInfo.processorFrequency >= 1500 &&
SystemInfo.processorCount >= 2 &&
SystemInfo.systemMemorySize >= (2 * 1024) &&
SystemInfo.graphicsMemorySize >= (512) &&
SystemInfo.graphicsShaderLevel >= 10
)
{
return QualityLevel.Middle;
}
else
{
return QualityLevel.Low;
}
}
private QualityLevel GetGraphicsQualityLevel()
{
var val = PlayerPrefs.GetInt(kGraphicsQualityLevelKey, -1);
if (val == -1)
{
return AnalysicDeviceLevel();
}
else
{
return (QualityLevel)val;
}
}
private void SetGraphicsQualityLevel(QualityLevel level)
{
SetGraphicsTier(level);
SetResolutionAndShaderLOD(level);
SetAntiAliasing(level);
SetLigthMapUsage(level);
SetShadow(level);
SetGlobalKW(level);
}
private void SetLigthMapUsage(QualityLevel level)
{
if (level >= QualityLevel.High)
{
var datas = CreateCurLightMapsData(SceneManager.GetActiveScene().name);
var len = datas != null ? datas.Length : 0;
var newLightMapDatas = new LightmapData[len];
for (int i = 0; i < len; i++)
{
newLightMapDatas[i] = datas[i].CreateLightMapData();
}
LightmapSettings.lightmaps = newLightMapDatas;
}
else
{
LightmapSettings.lightmaps = new LightmapData[] { };
}
}
private void SetAntiAliasing(QualityLevel level)
{
if (level >= QualityLevel.High)
{
QualitySettings.antiAliasing = srcAntiAliasing;
}
else
{
QualitySettings.antiAliasing = 0;
}
}
private void SetGraphicsTier(QualityLevel level)
{
switch(level)
{
case QualityLevel.Low:
case QualityLevel.Middle:
Graphics.activeTier = GraphicsTier.Tier1;
break;
case QualityLevel.High:
Graphics.activeTier = GraphicsTier.Tier2;
break;
case QualityLevel.Ultra:
Graphics.activeTier = GraphicsTier.Tier3;
break;
}
}
private void SetResolutionAndShaderLOD(QualityLevel level)
{
switch (level)
{
case QualityLevel.Low:
QualitySettings.resolutionScalingFixedDPIFactor = 0.75f;
Shader.globalMaximumLOD = 200;
QualitySettings.masterTextureLimit = DeviceAdapterQualityLevel < QualityLevel.High ? 3 : 2;
break;
case QualityLevel.Middle:
QualitySettings.resolutionScalingFixedDPIFactor = 0.85f;
Shader.globalMaximumLOD = 400;
QualitySettings.masterTextureLimit = DeviceAdapterQualityLevel < QualityLevel.High ? 2 : 1;
break;
case QualityLevel.High:
QualitySettings.resolutionScalingFixedDPIFactor = 0.85f;
Shader.globalMaximumLOD = 600;
QualitySettings.masterTextureLimit = 0;
break;
case QualityLevel.Ultra:
QualitySettings.resolutionScalingFixedDPIFactor = 1.00f;
Shader.globalMaximumLOD = 800;
QualitySettings.masterTextureLimit = 0;
break;
}
}
private void SetShadow(QualityLevel level)
{
switch (level)
{
case QualityLevel.Low:
QualitySettings.shadows = ShadowQuality.Disable;
break;
case QualityLevel.Middle:
QualitySettings.shadows = ShadowQuality.Disable;
break;
case QualityLevel.High:
QualitySettings.shadows = srcShadows;
QualitySettings.shadowResolution = ShadowResolution.Low;
QualitySettings.shadowDistance = 70;
break;
case QualityLevel.Ultra:
QualitySettings.shadows = srcShadows;
QualitySettings.shadowResolution = ShadowResolution.High;
QualitySettings.shadowDistance = 100;
break;
}
}
private void SetGlobalKW(QualityLevel level)
{
switch (level)
{
case QualityLevel.Low:
case QualityLevel.Middle:
Shader.DisableKeyword("_SOFT_PARTICLE_ON");
break;
case QualityLevel.High:
case QualityLevel.Ultra:
Shader.EnableKeyword("_SOFT_PARTICLE_ON");
break;
}
}
private Dictionary<string, CustomLightMapData[]> lightMapRecord = new Dictionary<string, CustomLightMapData[]>();
private void OnActiveSceneChanged(Scene from, Scene to)
{
CreateCurLightMapsData(to.name);
var level = GetGraphicsQualityLevel();
SetGraphicsQualityLevel(level);
}
private CustomLightMapData[] CreateCurLightMapsData(string sceneName)
{
CustomLightMapData[] ret;
var srcLightmaps = LightmapSettings.lightmaps;
var srcLightmapsLen = srcLightmaps != null ? srcLightmaps.Length : 0;
var recreate = false;
if (!lightMapRecord.TryGetValue(sceneName, out ret))
{
if (srcLightmapsLen > 0)
{
recreate = true;
}
}
else
{
recreate = ret.Length < srcLightmapsLen;
}
if (recreate)
{
ret = new CustomLightMapData[srcLightmapsLen];
for (int i = 0; i < srcLightmapsLen; i++)
{
ret[i] = new CustomLightMapData();
}
lightMapRecord[sceneName] = ret;
}
if (ret != null)
{
if (srcLightmapsLen > 0)
{
for (int i = 0; i < srcLightmapsLen; i++)
{
ret[i].CopyFrom(srcLightmaps[i]);
}
}
}
return ret;
}
private void OnSceneUnloaded(Scene scene)
{
lightMapRecord.Remove(scene.name);
}
}
查看 SetGlobalKW
private void SetGlobalKW(QualityLevel level)
{
switch (level)
{
case QualityLevel.Low:
case QualityLevel.Middle:
Shader.DisableKeyword("_SOFT_PARTICLE_ON");
break;
case QualityLevel.High:
case QualityLevel.Ultra:
Shader.EnableKeyword("_SOFT_PARTICLE_ON");
break;
}
}
SoftParticleToggle.cs
那么,特效同学在他们的特效开发场景中,看不到软粒子效果 那是因为 shader 变体每有开启,所以需要给他们一个组件来开启或关闭的功能,方便查看软粒子效果
using UnityEngine;
[ExecuteInEditMode]
public class SoftParticleToggle : MonoBehaviour
{
[Header("是否开启软粒子")]
public bool softParticleEnabled = true;
private bool lastSoftParticleEnabled = false;
private Camera _camera;
private void Start()
{
#if UNITY_EDITOR
_camera = GetComponent<Camera>();
_camera.depthTextureMode |= DepthTextureMode.Depth;
#endif
}
private void Update()
{
#if UNITY_EDITOR
if (lastSoftParticleEnabled != softParticleEnabled)
{
lastSoftParticleEnabled = softParticleEnabled;
if (lastSoftParticleEnabled)
{
Shader.EnableKeyword("_SOFT_PARTICLE_ON");
}
else
{
Shader.DisableKeyword("_SOFT_PARTICLE_ON");
}
EnabledSceneViewCamsDepth(lastSoftParticleEnabled);
}
#endif
}
private void EnabledSceneViewCamsDepth(bool enabledDepth)
{
for (var i = 0; i < UnityEditor.SceneView.sceneViews.Count; i++)
{
var scene = UnityEditor.SceneView.sceneViews[i] as UnityEditor.SceneView;
if (scene == null) continue;
EnabledCamDepth(scene.camera, enabledDepth);
}
}
private void EnabledCamDepth(Camera camera, bool enabledDepth)
{
if (enabledDepth)
camera.depthTextureMode |= DepthTextureMode.Depth;
else
camera.depthTextureMode &= ~DepthTextureMode.Depth;
Debug.Log("Camera.gameObject.name" + camera.name + ", depth : " + enabledDepth);
}
}
遇到的坑 _InvFade 默认值修改未 10.0 来避坑
因为各个开启,_InvFade = 0.1 的话,会影响所有特效片段里不透明深度写入的深度图比较近
导致贴 地表水的一些地裂,等 特效的面片看不到效果了
所以后面又将 _InvFade 的默认值修改未 10.0 了
有了 BRP 的思路,再 URP 下实现也是很 easy 的
每天 最便宜的圆珠笔 一练
C 的排线已经乱套。。。 -_-~
乱画排线,排出一个锤子
|