1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
|
/*
Udon# Linear Pipeline Controller
This script manages a linear chain of image effects. It takes a source texture
and processes it through a sequence of materials, storing the result of each
step in a corresponding RenderTexture.
Setup:
1. Create an empty GameObject in your scene.
2. Add an UdonBehaviour component to it.
3. Create this Udon# script in your project and assign it to the UdonBehaviour.
4. Create all the Materials and RenderTextures you need for your pipeline.
5. In the Inspector, assign the Source Input, Materials, and Output RenderTextures.
- The size of the Effect Materials and Pipeline Outputs arrays MUST be the same.
- The order of materials and textures in the arrays determines the pipeline order.
6. Choose your desired execution mode (Run On Start, Run Continuously).
*/
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
[UdonBehaviourSyncMode(BehaviourSyncMode.None)]
public class LinearPipeline : UdonSharpBehaviour
{
[Header("Pipeline Assets")]
[Tooltip("The initial texture to start the pipeline with.")]
public Texture sourceInput;
[Tooltip("The materials to apply in sequence. The order matters.")]
public Material[] effectMaterials;
[Tooltip("The RenderTextures to store the output of each step. MUST be the same size as the materials array.")]
public RenderTexture[] pipelineOutputs;
[Header("Execution Mode")]
[Tooltip("If true, the pipeline will run once when the world loads.")]
public bool runOnStart = true;
[Tooltip("If true, the pipeline will run every frame. Use with caution, can be performance-intensive.")]
public bool runContinuously = false;
void Start()
{
_ConfigurePipeline();
if (runOnStart)
{
_RunPipeline();
}
}
void Update()
{
if (runContinuously)
{
_RunPipeline();
}
}
/// <summary>
/// Configure materials for the pipeline.
/// </summary>
void _ConfigurePipeline()
{
if (effectMaterials == null || effectMaterials.Length == 0)
{
Debug.LogError("[LinearUdonPipeline] No Effect Materials have been assigned!", this);
return;
}
if (pipelineOutputs == null || pipelineOutputs.Length == 0)
{
Debug.LogError("[LinearUdonPipeline] No Pipeline Outputs have been assigned!", this);
return;
}
if (effectMaterials.Length != pipelineOutputs.Length)
{
Debug.LogError("[LinearUdonPipeline] The number of materials does not match the number of output textures!", this);
return;
}
// Configure materials - set _MainTex to appropriate source
for (int i = 0; i < effectMaterials.Length; i++)
{
if (effectMaterials[i] != null)
{
if (i == 0)
{
// First material uses the source input
effectMaterials[i].SetTexture("_MainTex", sourceInput);
}
else
{
// Subsequent materials use the previous output as input
effectMaterials[i].SetTexture("_MainTex", pipelineOutputs[i - 1]);
}
}
}
}
/// <summary>
/// This public method can be called by other Udon scripts or UI events to run the pipeline.
/// </summary>
public void _RunPipeline()
{
// --- Pre-flight Checks ---
if (sourceInput == null)
{
Debug.LogError("[LinearUdonPipeline] Source Input is not assigned!", this);
return;
}
if (effectMaterials == null || effectMaterials.Length == 0)
{
Debug.LogError("[LinearUdonPipeline] No Effect Materials have been assigned!", this);
return;
}
if (pipelineOutputs == null || pipelineOutputs.Length == 0)
{
Debug.LogError("[LinearUdonPipeline] No Pipeline Outputs have been assigned!", this);
return;
}
if (effectMaterials.Length != pipelineOutputs.Length)
{
Debug.LogError("[LinearUdonPipeline] The number of materials does not match the number of output textures!", this);
return;
}
// --- Run Pipeline ---
// 1. First Blit: From the main source to the first texture in our chain.
VRCGraphics.Blit(sourceInput, pipelineOutputs[0], effectMaterials[0], -1);
// 2. Loop through the rest of the chain.
for (int i = 1; i < effectMaterials.Length; i++)
{
// The source for this step is the output from the previous step.
Texture sourceForThisStep = pipelineOutputs[i - 1];
// The destination is the current output texture.
RenderTexture destForThisStep = pipelineOutputs[i];
// The material for this step.
Material materialForThisStep = effectMaterials[i];
VRCGraphics.Blit(sourceForThisStep, destForThisStep, materialForThisStep, -1);
}
}
}
|