Skip to content

Commit

Permalink
Arnold ShaderNetworkAlgo : Set osl.code parameter first
Browse files Browse the repository at this point in the history
Otherwise the other parameters we want to set don't exist yet. This didn't occur for any shaders authored in Gaffer, because there we would use the OSLCode node to author a generic shader that comes in via the `isOSLShader` code path. What is being fixed here is Arnold `osl` shaders coming in via USD after being authored somewhere like Houdini.
  • Loading branch information
johnhaddon committed Apr 3, 2024
1 parent 5b195df commit 2f8f0a9
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 0 deletions.
1 change: 1 addition & 0 deletions Changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Fixes
- Arnold :
- Fixed rendering of shaders imported from HtoA via USD.
- Fixed USD export of shaders to use `outputs:out` instead of `outputs:DEFAULT_OUTPUT`.
- Fixed rendering of `osl` shaders using the `code` parameter.
- GafferTest, GafferImageTest : Fixed import of these modules if the `Gaffer` module had not been imported previously.
- SceneAlgo : Fixed potential shutdown crashes caused by the adaptor registry [^1].
- Dispatcher : Fixed shutdown crashes caused by Python slots connected to the dispatch signals [^1].
Expand Down
54 changes: 54 additions & 0 deletions python/IECoreArnoldTest/RendererTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -4254,6 +4254,60 @@ def testOSLOutParameter( self ) :
input = arnold.AiNodeGetLink( surfaceShader, "base_color" )
self.assertIn( "colorSwitch", arnold.AiNodeGetName( input ) )

def testArnoldOSLWithCode( self ) :

renderer = GafferScene.Private.IECoreScenePreview.Renderer.create(
"Arnold",
GafferScene.Private.IECoreScenePreview.Renderer.RenderType.Batch
)

# This test exercises a problem that would occur if we tried to set
# Arnold's `param_*` attributes before setting the `code` attribute.
# Due to InternedString ordering rules, the likelihood of visiting the
# parameters in that order is increased by constructing the `param_colorIn`
# string first.
IECore.InternedString( "param_colorIn" )

renderer.object(
"testPlane",
IECoreScene.MeshPrimitive.createPlane( imath.Box2f( imath.V2f( -1 ), imath.V2f( 1 ) ) ),
renderer.attributes( IECore.CompoundObject( {
"ai:surface" : IECoreScene.ShaderNetwork(
shaders = {
"osl" : IECoreScene.Shader(
"osl", "ai:shader",
{
"code" : """
shader myConstant(
color colorIn = 0,
output color colorOut = 0
)
{
colorOut = colorIn;
}
""",
"param_colorIn" : imath.Color3f( 0, 1, 0 ),
}
),
},
output = "osl"
)
} ) ),
)

universe = ctypes.cast( renderer.command( "ai:queryUniverse", {} ), ctypes.POINTER( arnold.AtUniverse ) )

shader = arnold.AiNodeGetPtr( arnold.AiNodeLookUpByName( universe, "testPlane" ), "shader" )
self.assertEqual( arnold.AiNodeEntryGetName( arnold.AiNodeGetNodeEntry( shader ) ), "osl" )

# If we have set things in the right order, then `param_colorIn` will exist and have the right value.
self.assertEqual( arnold.AiNodeGetRGB( shader, "param_colorIn" ), arnold.AtRGB( 0, 1, 0 ) )
# And it won't have been declared as a user parameter, because setting `code` declares it as a built-in
# parameter instead.
self.assertIsNone( arnold.AiNodeLookUpUserParameter( shader, "param_colorIn" ) )

del renderer

def testInternedStringAttributes( self ) :

r = GafferScene.Private.IECoreScenePreview.Renderer.create(
Expand Down
14 changes: 14 additions & 0 deletions src/IECoreArnold/ShaderNetworkAlgo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ using namespace IECoreArnold;
namespace
{

const AtString g_codeArnoldString( "code" );
const AtString g_emptyArnoldString( "" );
const AtString g_outputArnoldString( "output" );
const AtString g_shaderNameArnoldString( "shadername" );
Expand Down Expand Up @@ -109,6 +110,19 @@ AtNode *convertWalk( const ShaderNetwork::Parameter &outputParameter, const IECo
AtString( shader->getName().c_str() ),
AtString( nodeName.c_str() )
);
if( node && AiNodeEntryGetNameAtString( AiNodeGetNodeEntry( node ) ) == g_oslArnoldString )
{
// Need to set the `code` parameter on `osl` shaders _before_ we try to set
// the other parameters, because otherwise they don't exist yet.
// Note : This code path is different to the `isOSLShader` one. This code path
// deals with shaders authored explicitly to use Arnold's `osl` shader node (which
// therefore wouldn't work in other renderers). The `isOSLShader` code path deals
// with generically-authored OSL shaders that we are translating to Arnold.
if( auto *code = shader->parametersData()->member<StringData>( "code" ) )
{
AiNodeSetStr( node, g_codeArnoldString, AtString( code->readable().c_str() ) );
}
}
}

if( !node )
Expand Down

0 comments on commit 2f8f0a9

Please sign in to comment.