diff --git a/Changes.md b/Changes.md index 4c749ceafaf..f077bac68da 100644 --- a/Changes.md +++ b/Changes.md @@ -19,6 +19,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]. diff --git a/python/IECoreArnoldTest/RendererTest.py b/python/IECoreArnoldTest/RendererTest.py index 46a065defca..a8f637fc15d 100644 --- a/python/IECoreArnoldTest/RendererTest.py +++ b/python/IECoreArnoldTest/RendererTest.py @@ -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( diff --git a/src/IECoreArnold/ShaderNetworkAlgo.cpp b/src/IECoreArnold/ShaderNetworkAlgo.cpp index 33e18e1ffdf..f4ae09c14e6 100644 --- a/src/IECoreArnold/ShaderNetworkAlgo.cpp +++ b/src/IECoreArnold/ShaderNetworkAlgo.cpp @@ -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" ); @@ -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( "code" ) ) + { + AiNodeSetStr( node, g_codeArnoldString, AtString( code->readable().c_str() ) ); + } + } } if( !node )