本项目以Morgan为中心,处理这个角色需要将 VFX Graph 的功能发挥到极致。
您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 

282 行
10 KiB

using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering;
using UnityEngine.VFX;
[ExecuteAlways]
public class SpawnTexturesRenderer : MonoBehaviour
{
[Header("Input Sets")]
public TargetSet[] targetSets;
[Header("Output Options")]
public TextureSize rtSize = TextureSize.x1024;
//--------------------------------------------------
public enum TextureSize { x256 = 256, x512 = 512, x1024 = 1024, x2048 = 2048 }
[System.Serializable]
public struct MeshBinding
{
public Renderer renderer;
public Mesh sourceMesh;
public Vector2Int subMeshRange;
public Vector3Int objectIds;
}
[System.Serializable]
public struct TargetSet
{
public VisualEffect[] visualEffects;
public MeshBinding[] meshBindings;
public Texture sharedAlbedo;
public string rtBindingSuffix;
}
public void RefreshGeneratedData()
{
OnDisable();
OnEnable();
foreach (var targetSet in targetSets)
foreach (var visualEffect in targetSet.visualEffects)
visualEffect.Reinit();
}
//--------------------------------------------------
static readonly int s_spObjectId = Shader.PropertyToID("_STRObjectID");
static readonly int s_spSpawnCountProperty = Shader.PropertyToID("spawn_count");
static readonly int s_spSpawnUVProperty = Shader.PropertyToID("spawn_uv");
static readonly int s_spSpawnUVSizeProperty = Shader.PropertyToID("spawn_uv_size");
static readonly string[] s_AttributeNames = { "position", "normals", "tangents", "vertex_color", "object_id", "albedo" };
static readonly GraphicsFormat[] s_AttributesFormats = { GraphicsFormat.R32G32B32A32_SFloat, GraphicsFormat.R16G16B16A16_SFloat, GraphicsFormat.R16G16B16A16_SFloat, GraphicsFormat.R8G8B8A8_UNorm, GraphicsFormat.R32_SFloat };
static readonly int s_AttributesCountOutput = s_AttributeNames.Length;
static readonly int s_AttributesCountWriteable = s_AttributesCountOutput - 1;
enum Attributes { Position, Normals, Tangents, Colors, Id, Albedo, Count}
enum ShaderPass { Static = 0, Transform = 1 }
struct TargetState
{
public Texture[] BindTextures;
public RenderTexture[] RenderTextures;
public RenderTargetIdentifier[] RTIsStatic;
public RenderTargetIdentifier[] RTIsTransform;
public struct InstanceState
{
public int ParticleCount;
public RenderTexture SpawnUV;
}
public InstanceState[] InstanceStates;
}
// Propagated from default meta assignment
[HideInInspector][SerializeField] Shader shader = null;
CommandBuffer m_Commands;
Material m_Material;
TargetState[] m_TargetStates;
void OnEnable()
{
m_Commands = new CommandBuffer {name = "SpawnTexturesRenderer"};
m_Material = new Material(shader) {name = "SpawnTexturesRenderer", hideFlags = HideFlags.DontSave};
CreateTargetStates();
InitializeOnce();
RenderPipelineManager.beginFrameRendering += BeginFrameRendering;
}
// private bool once;
// void Update()
// {
// if (!once)
// {
// Debug.Log($"{Time.frameCount}: Update");
// once = true;
// RefreshGeneratedData();
// }
// }
void OnDisable()
{
RenderPipelineManager.beginFrameRendering -= BeginFrameRendering;
ReleaseTargetStates();
SafeDestroy(m_Material);
m_Commands.Dispose();
}
void SafeDestroy(Object o) { if (Application.isPlaying) Destroy(o); else DestroyImmediate(o); }
void InitializeOnce()
{
ScheduleTargetSets(true);
Graphics.ExecuteCommandBuffer(m_Commands);
m_Commands.Clear();
for (var i = 0; i < targetSets.Length; ++i)
{
ref var targetState = ref m_TargetStates[i];
targetState.InstanceStates = new TargetState.InstanceState[targetSets[i].visualEffects.Length];
for (var j = 0; j < targetSets[i].visualEffects.Length; ++j)
{
var visualEffect = targetSets[i].visualEffects[j];
if (visualEffect.TryGetComponent<SpawnCompactor>(out var spawnCompactor))
{
var objectIds = targetState.BindTextures[(int) Attributes.Id];
spawnCompactor.Generate(objectIds, out var particleCount, out var compactRt);
targetState.InstanceStates[j] = new TargetState.InstanceState
{
ParticleCount = particleCount,
SpawnUV = compactRt
};
}
}
AssignOutputs(targetSets[i], targetState);
#if UNITY_EDITOR
foreach (var visualEffect in targetSets[i].visualEffects)
PrefabUtility.RecordPrefabInstancePropertyModifications(visualEffect);
#endif
}
}
void CreateTargetStates()
{
m_TargetStates = new TargetState[targetSets.Length];
for (var i = 0; i < targetSets.Length; ++i)
m_TargetStates[i] = CreateTargetState(targetSets[i]);
}
void AssignOutputs(in TargetSet targetSet, in TargetState targetState)
{
for (var i = 0; i < targetSet.visualEffects.Length; ++i)
{
var visualEffect = targetSet.visualEffects[i];
for (var j = 0; j < s_AttributesCountOutput; ++j)
{
if (targetState.BindTextures[j] != null)
{
var attribName = s_AttributeNames[j] + targetSet.rtBindingSuffix;
if (visualEffect.HasTexture(attribName))
visualEffect.SetTexture(attribName, targetState.BindTextures[j]);
}
}
var instanceState = targetState.InstanceStates[i];
if (instanceState.SpawnUV != null)
{
if(visualEffect.HasUInt(s_spSpawnCountProperty))
visualEffect.SetUInt(s_spSpawnCountProperty, (uint)instanceState.ParticleCount);
if(visualEffect.HasTexture(s_spSpawnUVProperty))
visualEffect.SetTexture(s_spSpawnUVProperty, instanceState.SpawnUV);
if(visualEffect.HasUInt(s_spSpawnUVSizeProperty))
visualEffect.SetUInt(s_spSpawnUVSizeProperty, (uint)instanceState.SpawnUV.width);
}
}
}
void ReleaseTargetStates()
{
foreach (var targetState in m_TargetStates)
ReleaseTargetState(targetState);
m_TargetStates = null;
}
TargetState CreateTargetState(in TargetSet targetSet)
{
var rtCount = s_AttributesCountWriteable;
var rts = new RenderTexture[s_AttributesCountOutput];
var rtisEverything = new RenderTargetIdentifier[rtCount];
var textures = new Texture[s_AttributesCountOutput];
for(var i = 0; i < rtCount; ++i)
{
textures[i] = rts[i] = new RenderTexture((int)rtSize, (int)rtSize, 0, s_AttributesFormats[i]) {
hideFlags = HideFlags.DontSave, filterMode = FilterMode.Point , name = s_AttributeNames[i] + targetSet.rtBindingSuffix
};
rtisEverything[i] = new RenderTargetIdentifier(rts[i]);
}
textures[(int) Attributes.Albedo] = targetSet.sharedAlbedo;
var rtisStatic = new[] {rtisEverything[(int)Attributes.Colors], rtisEverything[(int)Attributes.Id]};
var rtisTransform = new[] {rtisEverything[(int)Attributes.Position], rtisEverything[(int)Attributes.Normals], rtisEverything[(int)Attributes.Tangents]};
return new TargetState {BindTextures = textures, RenderTextures = rts, RTIsStatic = rtisStatic, RTIsTransform = rtisTransform};
}
void ReleaseTargetState(TargetState targetState)
{
foreach (var rt in targetState.RenderTextures)
SafeDestroy(rt);
}
void BeginFrameRendering(ScriptableRenderContext context, Camera[] _)
{
ScheduleTargetSets();
context.ExecuteCommandBuffer(m_Commands);
context.Submit();
m_Commands.Clear();
}
void ScheduleTargetSets(bool updateStatic = false)
{
for (int i = 0, n = targetSets.Length; i < n; ++i)
{
if(updateStatic)
ScheduleTargetSetStatic(targetSets[i], m_TargetStates[i], m_Material, m_Commands);
ScheduleTargetSet(updateStatic, targetSets[i], m_TargetStates[i], m_Material, m_Commands);
}
}
static void ScheduleTargetSet(bool updateStatic, in TargetSet targetSet, in TargetState targetState, Material mat, CommandBuffer cmd)
{
cmd.SetRenderTarget(targetState.RTIsTransform, targetState.RTIsTransform[0]);
if (updateStatic)
cmd.ClearRenderTarget(false, true, Color.clear);
foreach (var meshBinding in targetSet.meshBindings)
for (int i = meshBinding.subMeshRange.x, n = meshBinding.subMeshRange.y; i < n; ++i)
cmd.DrawRenderer(meshBinding.renderer, mat, i, (int)ShaderPass.Transform);
}
static void ScheduleTargetSetStatic(in TargetSet targetSet, in TargetState targetState, Material mat, CommandBuffer cmd)
{
cmd.SetRenderTarget(targetState.RTIsStatic, targetState.RTIsStatic[0]);
cmd.ClearRenderTarget(false, true, Color.clear);
foreach (var meshBinding in targetSet.meshBindings)
{
for (int i = meshBinding.subMeshRange.x, n = meshBinding.subMeshRange.y; i < n; ++i)
{
cmd.SetGlobalInt(s_spObjectId, meshBinding.objectIds[i]);
if (meshBinding.sourceMesh != null)
cmd.DrawMesh(meshBinding.sourceMesh, Matrix4x4.identity, mat, i, (int)ShaderPass.Static);
else
cmd.DrawRenderer(meshBinding.renderer, mat, i, (int)ShaderPass.Static);
}
}
}
}