1 //
2 // Copyright 2019 Pixar
3 //
4 // Licensed under the Apache License, Version 2.0 (the "Apache License")
5 // with the following modification; you may not use this file except in
6 // compliance with the Apache License and the following modification to it:
7 // Section 6. Trademarks. is deleted and replaced with:
8 //
9 // 6. Trademarks. This License does not grant permission to use the trade
10 //    names, trademarks, service marks, or product names of the Licensor
11 //    and its affiliates, except as required to comply with Section 4(c) of
12 //    the License and to reproduce the content of the NOTICE file.
13 //
14 // You may obtain a copy of the Apache License at
15 //
16 //     http://www.apache.org/licenses/LICENSE-2.0
17 //
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the Apache License with the above modification is
20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 // KIND, either express or implied. See the Apache License for the specific
22 // language governing permissions and limitations under the Apache License.
23 //
24 #include "pxr/imaging/hdSt/volumeShader.h"
25 
26 #include "pxr/imaging/hdSt/tokens.h"
27 #include "pxr/imaging/hdSt/volume.h"
28 #include "pxr/imaging/hdSt/field.h"
29 #include "pxr/imaging/hdSt/resourceBinder.h"
30 #include "pxr/imaging/hdSt/textureObject.h"
31 #include "pxr/imaging/hdSt/textureHandle.h"
32 #include "pxr/imaging/hdSt/textureBinder.h"
33 #include "pxr/imaging/hdSt/materialParam.h"
34 #include "pxr/imaging/hdSt/resourceRegistry.h"
35 #include "pxr/imaging/hd/renderDelegate.h"
36 #include "pxr/imaging/hd/vtBufferSource.h"
37 #include "pxr/base/tf/staticTokens.h"
38 
39 PXR_NAMESPACE_OPEN_SCOPE
40 
41 TF_DEFINE_PRIVATE_TOKENS(
42     _tokens,
43     (stepSize)
44     (stepSizeLighting)
45 
46     (sampleDistance)
47     (volumeBBoxInverseTransform)
48     (volumeBBoxLocalMin)
49     (volumeBBoxLocalMax)
50 );
51 
52 
HdSt_VolumeShader(HdRenderDelegate * const renderDelegate)53 HdSt_VolumeShader::HdSt_VolumeShader(HdRenderDelegate * const renderDelegate)
54   : _renderDelegate(renderDelegate),
55     _lastRenderSettingsVersion(0),
56     _stepSize(HdStVolume::defaultStepSize),
57     _stepSizeLighting(HdStVolume::defaultStepSizeLighting),
58     _fillsPointsBar(false)
59 {
60 }
61 
62 
63 HdSt_VolumeShader::~HdSt_VolumeShader() = default;
64 
65 void
AddBindings(HdBindingRequestVector * const customBindings)66 HdSt_VolumeShader::AddBindings(HdBindingRequestVector * const customBindings)
67 {
68     HdSt_MaterialNetworkShader::AddBindings(customBindings);
69     customBindings->push_back(
70         HdBindingRequest(
71             HdBinding::UNIFORM,
72             _tokens->stepSize,
73             HdTypeFloat));
74     customBindings->push_back(
75         HdBindingRequest(
76             HdBinding::UNIFORM,
77             _tokens->stepSizeLighting,
78             HdTypeFloat));
79 }
80 
81 void
BindResources(const int program,HdSt_ResourceBinder const & binder,HdRenderPassState const & state)82 HdSt_VolumeShader::BindResources(const int program,
83                                  HdSt_ResourceBinder const &binder,
84                                  HdRenderPassState const &state)
85 {
86     HdSt_MaterialNetworkShader::BindResources(program, binder, state);
87 
88     const int currentRenderSettingsVersion =
89         _renderDelegate->GetRenderSettingsVersion();
90 
91     if (_lastRenderSettingsVersion != currentRenderSettingsVersion) {
92         _lastRenderSettingsVersion = currentRenderSettingsVersion;
93         _stepSize = _renderDelegate->GetRenderSetting<float>(
94             HdStRenderSettingsTokens->volumeRaymarchingStepSize,
95             HdStVolume::defaultStepSize);
96         _stepSizeLighting = _renderDelegate->GetRenderSetting<float>(
97             HdStRenderSettingsTokens->volumeRaymarchingStepSizeLighting,
98             HdStVolume::defaultStepSizeLighting);
99     }
100 
101     binder.BindUniformf(_tokens->stepSize, 1, &_stepSize);
102     binder.BindUniformf(_tokens->stepSizeLighting, 1, &_stepSizeLighting);
103 }
104 
105 void
UnbindResources(const int program,HdSt_ResourceBinder const & binder,HdRenderPassState const & state)106 HdSt_VolumeShader::UnbindResources(const int program,
107                                    HdSt_ResourceBinder const &binder,
108                                    HdRenderPassState const &state)
109 {
110     HdSt_MaterialNetworkShader::UnbindResources(program, binder, state);
111 }
112 
113 void
SetPointsBar(HdBufferArrayRangeSharedPtr const & pointsBar)114 HdSt_VolumeShader::SetPointsBar(HdBufferArrayRangeSharedPtr const &pointsBar)
115 {
116     _pointsBar = pointsBar;
117 }
118 
119 void
SetFillsPointsBar(const bool fillsPointsBar)120 HdSt_VolumeShader::SetFillsPointsBar(const bool fillsPointsBar)
121 {
122     _fillsPointsBar = fillsPointsBar;
123 }
124 
125 static
126 TfToken
_ConcatFallback(const TfToken & token)127 _ConcatFallback(const TfToken &token)
128 {
129     return TfToken(
130         token.GetString()
131         + HdSt_ResourceBindingSuffixTokens->fallback.GetString());
132 }
133 
134 void
GetParamsAndBufferSpecsForBBoxAndSampleDistance(HdSt_MaterialParamVector * const params,HdBufferSpecVector * const specs)135 HdSt_VolumeShader::GetParamsAndBufferSpecsForBBoxAndSampleDistance(
136     HdSt_MaterialParamVector * const params,
137     HdBufferSpecVector * const specs)
138 {
139     {
140         params->emplace_back(
141             HdSt_MaterialParam::ParamTypeFallback,
142             _tokens->volumeBBoxInverseTransform,
143             VtValue(GfMatrix4d()));
144 
145         static const TfToken sourceName(
146             _ConcatFallback(_tokens->volumeBBoxInverseTransform));
147         specs->emplace_back(
148             sourceName,
149             HdTupleType{HdTypeDoubleMat4, 1});
150     }
151 
152     {
153         params->emplace_back(
154             HdSt_MaterialParam::ParamTypeFallback,
155             _tokens->volumeBBoxLocalMin,
156             VtValue(GfVec3d()));
157 
158         static const TfToken sourceName(
159             _ConcatFallback(_tokens->volumeBBoxLocalMin));
160         specs->emplace_back(
161             sourceName,
162             HdTupleType{HdTypeDoubleVec3, 1});
163     }
164 
165     {
166         params->emplace_back(
167             HdSt_MaterialParam::ParamTypeFallback,
168             _tokens->volumeBBoxLocalMax,
169             VtValue(GfVec3d()));
170 
171         static const TfToken sourceName(
172             _ConcatFallback(_tokens->volumeBBoxLocalMax));
173         specs->emplace_back(
174             sourceName,
175             HdTupleType{HdTypeDoubleVec3, 1});
176     }
177 
178     {
179         params->emplace_back(
180             HdSt_MaterialParam::ParamTypeFallback,
181             _tokens->sampleDistance,
182             VtValue(100000.0f));
183 
184         static const TfToken sourceName(
185             _ConcatFallback(_tokens->sampleDistance));
186         specs->emplace_back(
187             sourceName,
188             HdTupleType{HdTypeFloat, 1});
189     }
190 }
191 
192 void
GetBufferSourcesForBBoxAndSampleDistance(const std::pair<GfBBox3d,float> & bboxAndSampleDistance,HdBufferSourceSharedPtrVector * const sources)193 HdSt_VolumeShader::GetBufferSourcesForBBoxAndSampleDistance(
194     const std::pair<GfBBox3d, float> &bboxAndSampleDistance,
195     HdBufferSourceSharedPtrVector * const sources)
196 {
197     const GfBBox3d &bbox = bboxAndSampleDistance.first;
198     const GfRange3d &range = bbox.GetRange();
199 
200     {
201         static const TfToken sourceName(
202             _ConcatFallback(_tokens->volumeBBoxInverseTransform));
203         sources->push_back(
204             std::make_shared<HdVtBufferSource>(
205                 sourceName,
206                 VtValue(bbox.GetInverseMatrix())));
207     }
208 
209     {
210         static const TfToken sourceName(
211             _ConcatFallback(_tokens->volumeBBoxLocalMin));
212         sources->push_back(
213             std::make_shared<HdVtBufferSource>(
214                 sourceName,
215                 VtValue(GetSafeMin(range))));
216     }
217 
218     {
219         static const TfToken sourceName(
220             _ConcatFallback(_tokens->volumeBBoxLocalMax));
221         sources->push_back(
222             std::make_shared<HdVtBufferSource>(
223                 sourceName,
224                 VtValue(GetSafeMax(range))));
225     }
226 
227     {
228         const float sampleDistance = bboxAndSampleDistance.second;
229 
230         static const TfToken sourceName(
231             _ConcatFallback(_tokens->sampleDistance));
232         sources->push_back(
233             std::make_shared<HdVtBufferSource>(
234                 sourceName,
235                 VtValue(sampleDistance)));
236     }
237 }
238 
239 GfVec3d
GetSafeMin(const GfRange3d & range)240 HdSt_VolumeShader::GetSafeMin(const GfRange3d &range)
241 {
242     if (range.IsEmpty()) {
243         return GfVec3d(0.0, 0.0, 0.0);
244     }
245     return range.GetMin();
246 }
247 
248 GfVec3d
GetSafeMax(const GfRange3d & range)249 HdSt_VolumeShader::GetSafeMax(const GfRange3d &range)
250 {
251     if (range.IsEmpty()) {
252         return GfVec3d(0.0, 0.0, 0.0);
253     }
254     return range.GetMax();
255 }
256 
257 namespace {
258 
259 // Square of length of a 3-vector
260 float
_SqrLengthThreeVector(const double * const v)261 _SqrLengthThreeVector(const double * const v)
262 {
263     return v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
264 }
265 
266 // Assuming the bounding box comes from a grid (range is the
267 // bounding box of active voxels and matrix the grid transform),
268 // compute the distance between samples.
269 //
270 // Note that this assumes that the bounding box transform is an
271 // affine transformation obtained by composing scales with rotation.
272 // (More generally, we would need to take the minimum of the singular
273 // values from the SVD of the 3x3-matrix).
274 float
_ComputeSampleDistance(const GfBBox3d & bbox)275 _ComputeSampleDistance(const GfBBox3d &bbox)
276 {
277     const GfMatrix4d &m = bbox.GetMatrix();
278 
279     // Take minimum of lengths of images of the x-, y-, and z-vector.
280     return sqrt(
281         std::min({ _SqrLengthThreeVector(m[0]),
282                    _SqrLengthThreeVector(m[1]),
283                    _SqrLengthThreeVector(m[2]) }));
284 }
285 
286 // Compute the bounding box and sample distance from all the fields in
287 // this volume.
288 std::pair<GfBBox3d, float>
_ComputeBBoxAndSampleDistance(const HdStShaderCode::NamedTextureHandleVector & textures)289 _ComputeBBoxAndSampleDistance(
290     const HdStShaderCode::NamedTextureHandleVector &textures)
291 {
292     // Computed by combining all bounding boxes.
293     GfBBox3d bbox;
294     // Computes as minimum of all sampling distances.
295     // (Initialized to large value rather than IEEE754-infinity which might
296     // not be converted to correctly to GLSL: if there is
297     // no texture, the ray marcher simply obtains a point outside the
298     // bounding box after one step and stops).
299     float sampleDistance = 1000000.0;
300 
301     for (const HdStShaderCode::NamedTextureHandle &texture : textures) {
302         HdStTextureObjectSharedPtr const &textureObject =
303             texture.handle->GetTextureObject();
304 
305         if (const HdStFieldTextureObject * const fieldTex =
306                 dynamic_cast<HdStFieldTextureObject *>(
307                     textureObject.get())) {
308             const GfBBox3d &fieldBbox = fieldTex->GetBoundingBox();
309             bbox =
310                 GfBBox3d::Combine(bbox, fieldBbox);
311             sampleDistance =
312                 std::min(sampleDistance, _ComputeSampleDistance(fieldBbox));
313         }
314     }
315 
316     return { bbox, sampleDistance };
317 }
318 
319 // Compute 8 vertices of a bounding box.
320 
321 VtValue
_ComputePoints(const GfBBox3d & bbox)322 _ComputePoints(const GfBBox3d &bbox)
323 {
324     VtVec3fArray points(8);
325 
326     size_t i = 0;
327 
328     const GfMatrix4d &transform = bbox.GetMatrix();
329     const GfRange3d &range = bbox.GetRange();
330 
331     // Use vertices of a cube shrunk to point for empty bounding box
332     // (to avoid min and max being large floating point numbers).
333     const GfVec3d min = HdSt_VolumeShader::GetSafeMin(range);
334     const GfVec3d max = HdSt_VolumeShader::GetSafeMax(range);
335 
336     for (const double x : { min[0], max[0] }) {
337         for (const double y : { min[1], max[1] }) {
338             for (const double z : { min[2], max[2] }) {
339                 points[i] = GfVec3f(transform.Transform(GfVec3d(x, y, z)));
340                 i++;
341             }
342         }
343     }
344 
345     return VtValue(points);
346 }
347 
348 }  // end anonymous namespace
349 
350 void
AddResourcesFromTextures(ResourceContext & ctx) const351 HdSt_VolumeShader::AddResourcesFromTextures(ResourceContext &ctx) const
352 {
353     HdBufferSourceSharedPtrVector shaderBarSources;
354 
355     // Fills in sampling transforms for textures.
356     HdSt_TextureBinder::ComputeBufferSources(
357         GetNamedTextureHandles(), &shaderBarSources);
358 
359     if (_fillsPointsBar) {
360         // Compute volume bounding box from field bounding boxes
361         const std::pair<GfBBox3d, float> bboxAndSampleDistance =
362             _ComputeBBoxAndSampleDistance(GetNamedTextureHandles());
363 
364         const GfBBox3d &bbox = bboxAndSampleDistance.first;
365 
366         // Use as points
367         ctx.AddSource(
368             _pointsBar,
369             std::make_shared<HdVtBufferSource>(
370                 HdTokens->points,
371                 _ComputePoints(bbox)));
372 
373         // And let the shader know for raymarching bounds.
374         GetBufferSourcesForBBoxAndSampleDistance(
375             bboxAndSampleDistance, &shaderBarSources);
376     }
377 
378     if (!shaderBarSources.empty()) {
379         ctx.AddSources(GetShaderData(), std::move(shaderBarSources));
380     }
381 }
382 
383 void
SetFieldDescriptors(const HdVolumeFieldDescriptorVector & fieldDescs)384 HdSt_VolumeShader::SetFieldDescriptors(
385     const HdVolumeFieldDescriptorVector & fieldDescs)
386 {
387     _fieldDescriptors = fieldDescs;
388 }
389 
390 void
UpdateTextureHandles(HdSceneDelegate * const sceneDelegate)391 HdSt_VolumeShader::UpdateTextureHandles(
392     HdSceneDelegate * const sceneDelegate)
393 {
394     TRACE_FUNCTION();
395 
396     HdStResourceRegistrySharedPtr const resourceRegistry =
397         std::static_pointer_cast<HdStResourceRegistry>(
398             sceneDelegate->GetRenderIndex().GetResourceRegistry());
399 
400     NamedTextureHandleVector textureHandles = GetNamedTextureHandles();
401 
402     if (!TF_VERIFY(textureHandles.size() == _fieldDescriptors.size())) {
403         return;
404     }
405 
406     // Walk through the vector of named texture handles and field descriptors
407     // simultaneously.
408     for (size_t i = 0; i < textureHandles.size(); i++) {
409         // To allocate the texture and update it in the vector ...
410 
411         // use field descriptor to find field prim, ...
412         const HdVolumeFieldDescriptor &fieldDesc = _fieldDescriptors[i];
413 
414         const HdStField * const fieldPrim =
415             dynamic_cast<HdStField*>(
416                 sceneDelegate->GetRenderIndex().GetBprim(
417                     fieldDesc.fieldPrimType, fieldDesc.fieldId));
418 
419         // ask field prim for texture information, ...
420         const HdStTextureIdentifier &textureId =
421             TF_VERIFY(fieldPrim) ?
422             fieldPrim->GetTextureIdentifier() : HdStTextureIdentifier();
423         const HdTextureType textureType = textureHandles[i].type;
424         const size_t textureMemory =
425             TF_VERIFY(fieldPrim) ?
426             fieldPrim->GetTextureMemory() : 0;
427         static const HdSamplerParameters samplerParams{
428             HdWrapBlack, HdWrapBlack, HdWrapBlack,
429             HdMinFilterLinear, HdMagFilterLinear };
430 
431         // allocate texture handle and assign it.
432         textureHandles[i].handle =
433             resourceRegistry->AllocateTextureHandle(
434                 textureId,
435                 textureType,
436                 samplerParams,
437                 textureMemory,
438                 shared_from_this());
439     }
440 
441     // And update!
442     SetNamedTextureHandles(textureHandles);
443 }
444 
445 
446 PXR_NAMESPACE_CLOSE_SCOPE
447