diff --git a/Changes.md b/Changes.md index 4f64bc6dafb..39fea6f82ee 100644 --- a/Changes.md +++ b/Changes.md @@ -24,6 +24,7 @@ Improvements - CurvesPrimitive : Added `Pinned` wrap mode in addition to the existing `Periodic` and `NonPeriodic` modes. This conveniently interpolates CatmullRom and BSpline curves to their endpoints automatically, without manual management of duplicate endpoints or "phantom vertices". - SceneReader, SceneWriter : Added support for pinned UsdGeomBasisCurves. +- Cycles ShaderNetworkAlgo : Added support for USDPreviewSurface normal inputs. Fixes ----- diff --git a/python/GafferCyclesTest/IECoreCyclesPreviewTest/ShaderNetworkAlgoTest.py b/python/GafferCyclesTest/IECoreCyclesPreviewTest/ShaderNetworkAlgoTest.py index 4bd040fea3b..134e4ea3ac3 100644 --- a/python/GafferCyclesTest/IECoreCyclesPreviewTest/ShaderNetworkAlgoTest.py +++ b/python/GafferCyclesTest/IECoreCyclesPreviewTest/ShaderNetworkAlgoTest.py @@ -864,6 +864,41 @@ def testConvertUSDPrimvarReader( self ) : self.assertEqual( len( reader.parameters ), 1 ) self.assertEqual( reader.parameters["attribute"].value, "test" ) + def testConvertUSDNormal( self ) : + + network = IECoreScene.ShaderNetwork( + shaders = { + "previewSurface" : IECoreScene.Shader( + "UsdPreviewSurface", "surface" + ) + }, + output = "previewSurface", + ) + + network.addShader( "texture", IECoreScene.Shader( "UsdUVTexture" ) ) + network.addConnection( ( ( "texture", "color" ), ( "previewSurface", "normal" ) ) ) + + convertedNetwork = network.copy() + IECoreCycles.ShaderNetworkAlgo.convertUSDShaders( convertedNetwork ) + + self.assertEqual( len( convertedNetwork ), 4 ) + + normalize = convertedNetwork.getShader( "previewSurfaceNormalize" ) + self.assertEqual( normalize.parameters["math_type"].value, "normalize" ) + + self.assertEqual( + convertedNetwork.input( ( "previewSurface", "normal" ) ), + ( "previewSurfaceNormalize", "vector" ), + ) + self.assertEqual( + convertedNetwork.input( ( "previewSurfaceNormalize", "value1" ) ), + ( "previewSurfaceNormalMap", "normal" ), + ) + self.assertEqual( + convertedNetwork.input( ( "previewSurfaceNormalMap", "color" ) ), + ( "texture", "color" ), + ) + def __assertShadersEqual( self, shader1, shader2, message = None ) : self.assertEqual( shader1.name, shader2.name, message ) diff --git a/src/GafferCycles/IECoreCyclesPreview/ShaderNetworkAlgo.cpp b/src/GafferCycles/IECoreCyclesPreview/ShaderNetworkAlgo.cpp index 570191e45e4..1a065c45195 100644 --- a/src/GafferCycles/IECoreCyclesPreview/ShaderNetworkAlgo.cpp +++ b/src/GafferCycles/IECoreCyclesPreview/ShaderNetworkAlgo.cpp @@ -1314,9 +1314,20 @@ void IECoreCycles::ShaderNetworkAlgo::convertUSDShaders( ShaderNetwork *shaderNe newShader->parameters()[g_alphaParameter] = new FloatData( opacity ); // Normal. - /// \todo Convert normal parameters once we have a solution for Cycles' - /// need for tangents to be provided for the correct use of normal maps. - removeInput( shaderNetwork, { handle, g_normalParameter } ); + if( const ShaderNetwork::Parameter normalInput = shaderNetwork->input( { handle, g_normalParameter } ) ) + { + ShaderPtr normalmapShader = new Shader( "normal_map", "cycles:shader" ); + const InternedString normalmapHandle = shaderNetwork->addShader( handle.string() + "NormalMap", std::move( normalmapShader ) ); + // ALab has perfect examples where it makes invalid normals from scale/bias tweaks, + // so if we don't normalise the output Cycles will hard-crash. + ShaderPtr normalizeShader = new Shader( "vector_math", "cycles:shader" ); + normalizeShader->parameters()[g_mathTypeParameter] = new StringData( "normalize" ); + const InternedString normalizeHandle = shaderNetwork->addShader( handle.string() + "Normalize", std::move( normalizeShader ) ); + shaderNetwork->addConnection( ShaderNetwork::Connection( normalInput, { normalmapHandle, g_colorParameter } ) ); + shaderNetwork->addConnection( ShaderNetwork::Connection( { normalmapHandle, g_normalParameter }, { normalizeHandle, g_value1Parameter } ) ); + shaderNetwork->removeConnection( ShaderNetwork::Connection( normalInput, { handle, g_normalParameter } ) ); + shaderNetwork->addConnection( ShaderNetwork::Connection( { normalizeHandle, g_vectorParameter }, { handle, g_normalParameter } ) ); + } // Remove occlusion. removeInput( shaderNetwork, { handle, g_occlusionParameter } );