Skip to content

Commit

Permalink
Arnold : Prefer ai:volume over ai:surface for volume objects
Browse files Browse the repository at this point in the history
Fixes #5830.
  • Loading branch information
johnhaddon committed Apr 30, 2024
1 parent 7f39dc3 commit 1353515
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 2 deletions.
2 changes: 2 additions & 0 deletions Changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,15 @@ Improvements
- Added Arnold-specific extension parameters.
- Added parameter tooltips.
- LightEditor : Added columns for Arnold-specific parameters on USD lights.
- Arnold : If it exists, an `ai:volume` attribute is preferred over an `ai:surface` attribute when resolving shaders for volumes.

Fixes
-----

- Viewer : Fixed Cycles shader balls.
- TweakPlug : Fixed incorrect results and potential crashes in list modes.
- USD : Fixed `Unsupported value type "StringData" for parameter "input"` warning when converting `UsdTransform2d` shaders with no `in` connections to Arnold.
- Arnold : Fixed rendering of `ai:volume` shaders loaded from USD (#5830).

API
---
Expand Down
52 changes: 52 additions & 0 deletions python/IECoreArnoldTest/RendererTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -4314,6 +4314,58 @@ def testArnoldOSLWithCode( self ) :

del renderer

def testVolumeShaderAttribute( self ) :

import IECoreVDB

renderer = GafferScene.Private.IECoreScenePreview.Renderer.create(
"Arnold",
GafferScene.Private.IECoreScenePreview.Renderer.RenderType.Batch,
str( self.temporaryDirectory() / "test.ass" )
)
universe = ctypes.cast( renderer.command( "ai:queryUniverse", {} ), ctypes.POINTER( arnold.AtUniverse ) )

for withVolume in ( True, False ) :
for withSurface in ( True, False ) :
for step in ( 0.0, 0.1 ) :
with self.subTest( withVolume = withVolume, withSurface = withSurface, step = step ) :

attributes = IECore.CompoundObject( { "ai:shape:step_size" : IECore.FloatData( step ) } )

if withVolume :
attributes["ai:volume"] = IECoreScene.ShaderNetwork(
shaders = { "shader" : IECoreScene.Shader( "standard_volume" ) },
output = ( "shader", "out" ),
)

if withSurface :
attributes["ai:surface"] = IECoreScene.ShaderNetwork(
shaders = { "shader" : IECoreScene.Shader( "standard_surface" ) },
output = ( "shader", "out" ),
)

attributes = renderer.attributes( attributes )

meshName = f"mesh{withVolume}{withSurface}{step}"
renderer.object( meshName, IECoreScene.MeshPrimitive.createSphere( 1 ), attributes )

vdbName = f"vdb{withVolume}{withSurface}{step}"
renderer.object( vdbName, IECoreVDB.VDBObject(), attributes )

meshShader = arnold.AiNodeGetPtr( arnold.AiNodeLookUpByName( universe, meshName ), "shader" )
self.assertEqual(
arnold.AiNodeEntryGetName( arnold.AiNodeGetNodeEntry( meshShader ) ),
"standard_volume" if ( withVolume and step ) else "standard_surface" if withSurface else "utility"
)

vdbShader = arnold.AiNodeGetPtr( arnold.AiNodeLookUpByName( universe, vdbName ), "shader" )
self.assertEqual(
arnold.AiNodeEntryGetName( arnold.AiNodeGetNodeEntry( vdbShader ) ),
"standard_volume" if withVolume else "standard_surface" if withSurface else "utility"
)

del renderer

def testInternedStringAttributes( self ) :

r = GafferScene.Private.IECoreScenePreview.Renderer.create(
Expand Down
37 changes: 35 additions & 2 deletions src/IECoreArnold/Renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -922,6 +922,7 @@ namespace
bool isConvertedProcedural( const AtNode *node );

IECore::InternedString g_surfaceShaderAttributeName( "surface" );
IECore::InternedString g_volumeShaderAttributeName( "volume" );
IECore::InternedString g_lightShaderAttributeName( "light" );
IECore::InternedString g_doubleSidedAttributeName( "doubleSided" );
IECore::InternedString g_setsAttributeName( "sets" );
Expand Down Expand Up @@ -951,6 +952,7 @@ IECore::InternedString g_volumeVisibilityAutoBumpAttributeName( "ai:autobump_vis
IECore::InternedString g_subsurfaceVisibilityAutoBumpAttributeName( "ai:autobump_visibility:subsurface" );

IECore::InternedString g_arnoldSurfaceShaderAttributeName( "ai:surface" );
IECore::InternedString g_arnoldVolumeShaderAttributeName( "ai:volume" );
IECore::InternedString g_arnoldLightShaderAttributeName( "ai:light" );
IECore::InternedString g_arnoldFilterMapAttributeName( "ai:filtermap" );
IECore::InternedString g_arnoldUVRemapAttributeName( "ai:uv_remap" );
Expand Down Expand Up @@ -1059,6 +1061,13 @@ class ArnoldAttributes : public IECoreScenePreview::Renderer::AttributesInterfac
m_surfaceShader = shaderCache->get( surfaceShaderAttribute, attributes );
}

const IECoreScene::ShaderNetwork *volumeShaderAttribute = attribute<IECoreScene::ShaderNetwork>( g_arnoldVolumeShaderAttributeName, attributes );
volumeShaderAttribute = volumeShaderAttribute ? volumeShaderAttribute : attribute<IECoreScene::ShaderNetwork>( g_volumeShaderAttributeName, attributes );
if( volumeShaderAttribute )
{
m_volumeShader = shaderCache->get( volumeShaderAttribute, attributes );
}

if( auto filterMapAttribute = attribute<IECoreScene::ShaderNetwork>( g_arnoldFilterMapAttributeName, attributes ) )
{
m_filterMap = shaderCache->get( filterMapAttribute, attributes );
Expand Down Expand Up @@ -1375,9 +1384,9 @@ class ArnoldAttributes : public IECoreScenePreview::Renderer::AttributesInterfac

AiNodeSetBool( node, g_matteArnoldString, m_shadingFlags & ArnoldAttributes::Matte );

if( m_surfaceShader && m_surfaceShader->root() )
if( AtNode *shader = preferredShader( geometry ) )
{
AiNodeSetPtr( node, g_shaderArnoldString, m_surfaceShader->root() );
AiNodeSetPtr( node, g_shaderArnoldString, shader );
}
else
{
Expand Down Expand Up @@ -1995,6 +2004,7 @@ class ArnoldAttributes : public IECoreScenePreview::Renderer::AttributesInterfac
h.append( m_sidedness );
h.append( m_shadingFlags );
hashOptional( m_surfaceShader.get(), h );
hashOptional( m_volumeShader.get(), h );
hashOptional( m_filterMap.get(), h );
hashOptional( m_uvRemap.get(), h );
hashOptional( m_lightShader.get(), h );
Expand All @@ -2018,10 +2028,33 @@ class ArnoldAttributes : public IECoreScenePreview::Renderer::AttributesInterfac
h.append( m_sssSetName.c_str() ? m_sssSetName.c_str() : "" );
}

AtNode *preferredShader( const AtNode *geometry ) const
{
if( m_volumeShader )
{
// Prefer the volume shader if we have one, and the geometry is either
// a volume or a shape being rendered as a volume (non-zero step size).
bool preferVolume = AiNodeIs( geometry, g_volumeArnoldString );
if( !preferVolume && AiNodeEntryLookUpParameter( AiNodeGetNodeEntry( geometry ), g_stepSizeArnoldString ) )
{
preferVolume = AiNodeGetFlt( geometry, g_stepSizeArnoldString ) > 0.0f;
}
if( preferVolume )
{
return m_volumeShader->root();
}
}

// Otherwise use the surface shader. We use this even for volume geometry,
// because Gaffer has always assigned volume shaders as `ai:surface`.
return m_surfaceShader ? m_surfaceShader->root() : nullptr;
}

unsigned char m_visibility;
unsigned char m_sidedness;
unsigned char m_shadingFlags;
ArnoldShaderPtr m_surfaceShader;
ArnoldShaderPtr m_volumeShader;
ArnoldShaderPtr m_filterMap;
ArnoldShaderPtr m_uvRemap;
IECoreScene::ConstShaderNetworkPtr m_lightShader;
Expand Down

0 comments on commit 1353515

Please sign in to comment.