@@ -4,169 +4,161 @@ This page contains some samples for advanced use-cases, like custom texture conv
44
55## Custom Texture Converters
66
7- The texture converter in ` CapturePipeline<T>.TextureConverter ` allows you to easily change the conversion compute shader to custom
8- ones. All you have to do is set ` CapturePipeline<T>.TextureConverter.Shader ` to your shader. You can also change the compute shader
9- for all new capture sessions by changing ` UCameraManager.YUVToRGBAComputeShader ` .
7+ The texture converter in ` CapturePipeline<T>.Converter ` allows you to easily change the conversion compute shader to custom
8+ ones. All you have to do is assign a new ` ComputeShaderKernel ` to ` Converter.ShaderKernel ` . ` ComputeShaderKernel ` references
9+ the compute shader and the name of the shader kernel to use.
10+
11+ You can also change the compute shader for all new capture sessions by changing ` QuestCameraManager.ConversionKernel ` .
1012
1113For example, the following compute shader ignores the U and V values of the YUV stream to provide a Luminance-only image:
1214
1315``` hlsl
1416#pragma kernel CSMain
1517
16- // Input buffers (read-only)
18+ // ---------------- YUVConverter *required* parameters ----------------
19+
20+ // Camera frame
1721ByteAddressBuffer YBuffer;
1822ByteAddressBuffer UBuffer;
1923ByteAddressBuffer VBuffer;
2024
21- // Row strides
22- uint YRowStride;
23- uint UVRowStride;
25+ cbuffer StrideParams {
26+ // Row strides
27+ uint YRowStride;
28+ uint UVRowStride;
2429
25- // Pixel strides
26- uint UVPixelStride;
27-
28- // Image dimensions
29- uint TargetWidth;
30- uint TargetHeight;
30+ // Pixel strides
31+ uint UVPixelStride;
32+ uint StrideParamsPadding;
33+ }
3134
32- // Output texture (read-write)
35+ uniform uint OutputTextureWidth;
36+ uniform uint OutputTextureHeight;
3337RWTexture2D<float4> OutputTexture;
3438
35- // Helper function to get a byte from a ByteAddressBuffer.
36- // buffer: The ByteAddressBuffer.
37- // byteIndex: The *byte* index (offset) into the buffer.
38- uint GetByteFromBuffer(ByteAddressBuffer buffer, uint byteIndex)
39- {
40- // Calculate the 32-bit word offset (each word is 4 bytes).
41- uint wordOffset = byteIndex / 4;
42-
43- // Load the 32-bit word containing the byte.
44- uint word = buffer.Load(wordOffset * 4); // MUST multiply by 4 for ByteAddressBuffer.Load()
39+ // ---------------- End of required parameters ----------------
4540
46- // Calculate the byte position *within* the word (0, 1, 2, or 3).
47- uint byteInWord = byteIndex % 4;
41+ // Converts byte array indexing to ByteAddressBuffer indexing and returns the value
42+ uint GetByteFromBuffer(const ByteAddressBuffer buffer, const uint byteIndex) {
43+
44+ const uint word = buffer.Load(byteIndex & ~3);
45+ const uint byteInWord = byteIndex & 3;
4846
49- // Extract the correct byte using bit shifts and masking.
5047 return (word >> (byteInWord * 8)) & 0xFF;
5148}
5249
5350[numthreads(8, 8, 1)]
54- void CSMain(uint3 id : SV_DispatchThreadID)
55- {
56- if (id.x >= TargetWidth || id.y >= TargetHeight )
51+ void CSMain(uint3 id : SV_DispatchThreadID) {
52+
53+ if (id.x >= OutputTextureWidth || id.y >= OutputTextureHeight )
5754 return;
58-
55+
5956 // The YUV stream is flipped, so we have to un-flip it.
60- uint flippedY = TargetHeight - 1 - id.y;
57+ const uint flippedY = OutputTextureHeight - 1 - id.y;
6158
6259 // Index of Y value in buffer.
63- uint yIndex = flippedY * YRowStride + id.x;
64- uint yValue = GetByteFromBuffer(YBuffer, yIndex);
60+ const uint yIndex = flippedY * YRowStride + id.x;
61+
62+ // Get the Y (luminance) value.
63+ const uint y = GetByteFromBuffer(YBuffer, yIndex);
6564
66- float3 luminance = float3(yValue, yValue, yValue) / 255.0;
67- OutputTexture[id.xy] = float4(luminance.rgb, 1.0);
65+ // Convert the value from 0-255 to 0-1 and set the output texture.
66+ const float3 color = float3(y, y, y) / 255.0;
67+ OutputTexture[id.xy] = float4(color, 1.0);
6868}
6969```
7070
71- ## Multiple Streams From One Camera
71+ ## Multiple Streams from One Camera
7272
7373By adding multiple texture converters to the same request, you can emulate the effect of having more than one image stream from a
7474single camera. For example, you can have one converter stream the camera image as-is, and another streaming with a simple Sepia
7575post-processing effect:
7676
77- ``` csharp
78- // Create a capture session with the camera, at the chosen resolution.
79- CapturePipeline < ContinuousCaptureSession > capturePipeline = camera .CreateContinuousCaptureSession (highestResolution );
80- if (capturePipeline == null .. .
81-
82- yield return capturePipeline .CaptureSession .WaitForInitialization ();
83-
84- // Check if it opened successfully.
85- if (capturePipeline .CaptureSession .CurrentState .. .
77+ > [ !WARNING]
78+ > This is not the best way to do this! Creating multiple ` YUVConverter ` s duplicates almost all conversion resources for each instance!
79+ >
80+ > Each ` YUVConverter ` allocates its own:
81+ > - CPU-side ` NativeArray ` copy buffers
82+ > - GPU-side ` GraphicsBuffer ` s
83+ > - Output ` RenderTexture `
84+ > - Conversion ` CommandBuffer `
85+ >
86+ > The CPU-side copy buffers and GPU-side graphics buffers are each approximately the size of one frame of
87+ > YUV image data. The output ` RenderTexture ` size depends on the selected texture format, but is usually
88+ > larger than an equivalent YUV texture.
89+ >
90+ > Please implement a custom converter which shares the above data between different consumers.
8691
87- // Set the image texture.
88- _rawImage .texture = capturePipeline .TextureConverter .FrameRenderTexture ;
92+ ``` csharp
93+ // Create the session.
94+ _session = _camera .CreateContinuousSession (highestResolution , CaptureTemplate .Preview , useCase );
95+ if (! await _session .WaitForInitializationAsync ())
96+ {
97+ await _session .DisposeAsync ();
98+ await _camera .DisposeAsync ();
99+ return ;
100+ }
89101
90- // Create a new YUVToRGBAConverter .
91- YUVToRGBAConverter secondary = new YUVToRGBAConverter (highestResolution );
102+ // Primary converter, defaults to the shader set in QuestCameraManager .
103+ _primaryConverter = new YUVConverter (highestResolution );
92104
93- // Assign it a different shader .
94- secondary . Shader = _postProcessShader ;
105+ // Secondary converter with the sepia effect, ComputeShaderKernel uses "CSMain" by default .
106+ _secondaryConverter = new YUVConverter ( highestResolution , new ComputeShaderKernel ( _sepiaShader )) ;
95107
96- // Link the capture session and the converter.
97- capturePipeline .CaptureSession .OnFrameReady += secondary .OnFrameReady ;
108+ // Register converters to session.
109+ _session .NativeProxy .OnFrameReady += _primaryConverter .OnFrameReady ;
110+ _session .NativeProxy .OnFrameReady += _secondaryConverter .OnFrameReady ;
98111
99- // Set the second image to the post processed RenderTexture.
100- _rawImagePostProcessed .texture = secondary . FrameRenderTexture ;
112+ _rawImagePrimary . texture = _primaryConverter . Texture ;
113+ _rawImageSecondary .texture = _secondaryConverter . Texture ;
101114```
102115
103116### YUV To RGBA Converter With Sepia Effect
104117
105118``` hlsl
106119#pragma kernel CSMain
107120
108- // Input buffers (read-only)
109121ByteAddressBuffer YBuffer;
110122ByteAddressBuffer UBuffer;
111123ByteAddressBuffer VBuffer;
112124
113- // Row strides
114- uint YRowStride ;
115- uint UVRowStride ;
116-
117- // Pixel strides
118- uint UVPixelStride ;
125+ cbuffer StrideParams {
126+ uint YRowStride;
127+ uint UVRowStride;
119128
120- // Image dimensions
121- uint TargetWidth ;
122- uint TargetHeight ;
129+ uint UVPixelStride;
130+ uint StrideParamsPadding ;
131+ }
123132
124- // Output texture (read-write)
133+ uniform uint OutputTextureWidth;
134+ uniform uint OutputTextureHeight;
125135RWTexture2D<float4> OutputTexture;
126136
127- // Helper function to get a byte from a ByteAddressBuffer.
128- // buffer: The ByteAddressBuffer.
129- // byteIndex: The *byte* index (offset) into the buffer.
130- uint GetByteFromBuffer (ByteAddressBuffer buffer , uint byteIndex )
131- {
132- // Calculate the 32-bit word offset (each word is 4 bytes).
133- uint wordOffset = byteIndex / 4 ;
134-
135- // Load the 32-bit word containing the byte.
136- uint word = buffer .Load (wordOffset * 4 ); // MUST multiply by 4 for ByteAddressBuffer.Load()
137-
138- // Calculate the byte position *within* the word (0, 1, 2, or 3).
139- uint byteInWord = byteIndex % 4 ;
137+ uint GetByteFromBuffer(const ByteAddressBuffer buffer, const uint byteIndex) {
138+
139+ const uint word = buffer.Load(byteIndex & ~3);
140+ const uint byteInWord = byteIndex & 3;
140141
141- // Extract the correct byte using bit shifts and masking.
142142 return (word >> (byteInWord * 8)) & 0xFF;
143143}
144144
145145[numthreads(8, 8, 1)]
146- void CSMain (uint3 id : SV_DispatchThreadID )
147- {
148- if (id .x >= TargetWidth || id .y >= TargetHeight )
146+ void CSMain(uint3 id : SV_DispatchThreadID) {
147+
148+ if (id.x >= OutputTextureWidth || id.y >= OutputTextureHeight )
149149 return;
150-
151- // The YUV stream is flipped, so we have to un-flip it.
152- uint flippedY = TargetHeight - 1 - id .y ;
153150
154- // Index of Y value in buffer.
155- uint yIndex = flippedY * YRowStride + id .x ;
156- uint yValue = GetByteFromBuffer (YBuffer , yIndex );
157-
158- float3 luminance = float3 (yValue , yValue , yValue ) / 255 . 0 ;
151+ const uint flippedY = OutputTextureHeight - 1 - id.y;
152+ const uint yIndex = flippedY * YRowStride + id.x;
159153
160- // --- Post-processing (Sepia Tone) ---
161- float4 color = float4 ( luminance . rgb , 1 . 0 ) ;
154+ const uint y = GetByteFromBuffer(YBuffer, yIndex);
155+ const float3 color = float3(y, y, y) / 255.0 ;
162156
163- // Simple Sepia. Could also do a vignette, bloom, etc. here.
164- float4 sepiaColor ;
157+ float3 sepiaColor;
165158 sepiaColor.r = dot(color.rgb, float3(0.393, 0.769, 0.189));
166159 sepiaColor.g = dot(color.rgb, float3(0.349, 0.686, 0.168));
167160 sepiaColor.b = dot(color.rgb, float3(0.272, 0.534, 0.131));
168- sepiaColor .a = 1 . 0 ;
169161
170- OutputTexture [id .xy ] = sepiaColor ;
162+ OutputTexture[id.xy] = float4( sepiaColor, 1.0) ;
171163}
172164```
0 commit comments