diff --git a/lib/flowViewport/usdPlugins/shadersDiscoveryPlugin/shaders/customBasicLightingShader.glslfx b/lib/flowViewport/usdPlugins/shadersDiscoveryPlugin/shaders/customBasicLightingShader.glslfx index 2a255c5b9..f2cb26fa5 100644 --- a/lib/flowViewport/usdPlugins/shadersDiscoveryPlugin/shaders/customBasicLightingShader.glslfx +++ b/lib/flowViewport/usdPlugins/shadersDiscoveryPlugin/shaders/customBasicLightingShader.glslfx @@ -70,6 +70,111 @@ BasicLightingCoeffs computeLightingCoefficients(vec3 L, vec3 N, vec3 V, float ex return coeffs; } +//To support dome lights +struct LightingContributions +{ + vec3 diffuse; + vec3 specular; +}; + +mat4 GetDomeLightTransform(mat4 worldToLightTransform) +{ + // transform from view space to light space + mat4 worldToViewInverse = GetWorldToViewInverseMatrix(); + return worldToLightTransform * worldToViewInverse; +} + +#define PI 3.1415 +vec2 ProjectToLatLong(vec3 sample3D) +{ + // project spherical coord onto latitude-longitude map with + // latitude: +y == pi/2 and longitude: +z == 0, +x == pi/2 + float x = (atan(sample3D.z, sample3D.x) + 0.5 * PI) / (2.0 * PI); + float y = acos(sample3D.y) / PI; + + return vec2(x,y); +} + +LightingContributions +evaluateIndirectLighting( + vec3 diffuseColor, + vec3 specularColor, + vec3 Neye, + vec3 Reye, + float NdotE, + float EdotH, + float ior, + float metallic, + float occlusion, + float roughness, + bool useSpecularWorkflow, + float clearcoatAmount, + vec3 clearcoatColor, + float clearcoatRoughness, + mat4 worldToLightTransform) +{ + LightingContributions indirect; + indirect.diffuse = vec3(0.0); + indirect.specular = vec3(0.0); + +#ifdef HD_HAS_domeLightIrradiance + vec3 F0 = specularColor; + vec3 F90 = vec3(1.0); + vec3 d = diffuseColor; + const float R = (1.0 - ior) / (1.0 + ior); + if (!useSpecularWorkflow) { + vec3 specColor = mix(vec3(1.0), diffuseColor, metallic); + F0 = mix(R * R * specColor, specColor, metallic); + F90 = specColor; + + // For metallic workflows, pure metals have no diffuse + d *= 1.0 - metallic; + } + // Adjust the diffuse so glazing angles have less diffuse + float fresnel = pow(max(0.0, 1.0 - EdotH), 5.0);//SchlickFresnel + vec3 F = mix(F0, F90, fresnel); + d *= (1.0 - F); + + mat4 transformationMatrix = GetDomeLightTransform(worldToLightTransform); + + // Diffuse Component + vec3 dir = normalize((transformationMatrix * vec4(Neye,0.0)).xyz); + vec2 coord = ProjectToLatLong(dir); + vec3 diffuse = HdGet_domeLightIrradiance(coord).rgb; + + // Specular Component + const float MAX_REFLECTION_LOD = + textureQueryLevels(HdGetSampler_domeLightPrefilter()); + float lod = roughness * MAX_REFLECTION_LOD; + vec3 Rdir = normalize((transformationMatrix * vec4(Reye,0.0)).xyz); + vec2 Rcoord = ProjectToLatLong(Rdir); + vec3 prefilter = HdTextureLod_domeLightPrefilter(Rcoord, lod).rgb; + + vec2 brdf = HdGet_domeLightBRDF(vec2(NdotE, roughness)).rg; + + vec3 specular = prefilter * (F * brdf.x + brdf.y); + + // Clearcoat Component + vec3 clearcoat = vec3(0.0); + if (clearcoatAmount > 0.0) { + const vec3 clearcoatF = clearcoatAmount * mix( + R * R * clearcoatColor, // Clearcoat F0 + clearcoatColor, // Clearcoat F90 + fresnel); + lod = clearcoatRoughness * MAX_REFLECTION_LOD; + prefilter = HdTextureLod_domeLightPrefilter(Rcoord, lod).rgb; + clearcoat = prefilter * (clearcoatF * brdf.x + brdf.y); + } + + // Indirect Lighting + indirect.diffuse = (d * diffuse) * occlusion; + indirect.specular = (specular + clearcoat) * occlusion; + +#endif + + return indirect; +} + vec4 surfaceShader(vec4 Peye, vec3 Neye, vec4 color, vec4 patchCoord) { @@ -89,34 +194,65 @@ surfaceShader(vec4 Peye, vec3 Neye, vec4 color, vec4 patchCoord) vec3 n = Neye; vec3 e = normalize(-Peye.xyz); + vec3 directLight = vec3(0.0); + vec3 indirectLight = vec3(0.0);//For dome light float glossiness = 30.0;//Hardcoded + float ior = 1.5; //Hardcoded + float metallic = 0.0;//Hardcoded + float occlusion = 1.0;//Hardcoded + bool useSpecularWorkflow = true;//Hardcoded + float clearcoatAmount = 0.0;//Hardcoded + vec3 clearcoatColor = vec3(1.0);//Hardcoded + float clearcoatRoughness = 0.01;//Hardcoded - //Do basic lighting + //Do lighting (with no shadows) #if NUM_LIGHTS > 0 for (int i = 0; i < NUM_LIGHTS; ++i) { - LightSource light = GetLightSource(i); - if (!light.isIndirectLight) - { - vec4 Plight = light.position; - vec3 l = (Plight.w == 0.0) - ? normalize(Plight.xyz) - : normalize(Plight - Peye).xyz; + LightSource light = GetLightSource(i); + vec4 Plight = light.isIndirectLight //light.isIndirectLight is true for dome light. + ? vec4(0,0,0,1) + : light.position; + + vec3 l = (Plight.w == 0.0) + ? normalize(Plight.xyz) + : normalize(Plight - Peye).xyz; - float atten = lightDistanceAttenuation(Peye, i); - float spot = lightSpotAttenuation(l, i); + float atten = lightDistanceAttenuation(Peye, i); + float spot = lightSpotAttenuation(l, i); - float intensity = 1.0 * atten * spot; + float intensity = 1.0 * atten * spot; - vec3 lightDiffuseIrradiance = intensity * light.diffuse.rgb; - vec3 lightSpecularIrradiance = intensity * light.specular.rgb; + vec3 lightDiffuseIrradiance = intensity * light.diffuse.rgb; + vec3 lightSpecularIrradiance = intensity * light.specular.rgb; - BasicLightingCoeffs coeffs = computeLightingCoefficients(l, n, e, glossiness); - directLight += (light.ambient.rgb *ambientColor + lightDiffuseIrradiance * coeffs.diffuse * diffuseColor + lightSpecularIrradiance * coeffs.specular * specularColor); + BasicLightingCoeffs coeffs = computeLightingCoefficients(l, n, e, glossiness); + + if (light.isIndirectLight) { + float NdotE = max(0.0, dot(n, e)); + vec3 Reye = reflect(-e, n); + + vec3 h = normalize(e + l); + float NdotL = max(0.0, dot(n, l)); + float NdotH = max(0.0, dot(n, h)); + float EdotH = max(0.0, dot(e, h)); + // Calculate the indirect light (DomeLight) + LightingContributions indirectLightContrib = + evaluateIndirectLighting(diffuseColor, specularColor, + Neye, Reye, NdotE, EdotH, ior, metallic, occlusion, + glossiness, useSpecularWorkflow, + clearcoatAmount, clearcoatColor, clearcoatRoughness, + light.worldToLightTransform); + indirectLight += (indirectLightContrib.diffuse * light.diffuse.rgb + + indirectLightContrib.specular * light.specular.rgb); } + else { + // All other light sources contribute to the direct lighting + directLight += (light.ambient.rgb *ambientColor + lightDiffuseIrradiance * coeffs.diffuse * diffuseColor + lightSpecularIrradiance * coeffs.specular * specularColor); + } } #endif - return vec4(directLight, 1.0); + return vec4(directLight+indirectLight, 1.0); } \ No newline at end of file diff --git a/test/lib/mayaUsd/render/mayaToHydra/CustomShadersNodeTest/testCustomShadersNode.png b/test/lib/mayaUsd/render/mayaToHydra/CustomShadersNodeTest/testCustomShadersNode.png deleted file mode 100644 index 1837822c5..000000000 Binary files a/test/lib/mayaUsd/render/mayaToHydra/CustomShadersNodeTest/testCustomShadersNode.png and /dev/null differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/CustomShadersNodeTest/testCustomShadersNodeDefaultLight.png b/test/lib/mayaUsd/render/mayaToHydra/CustomShadersNodeTest/testCustomShadersNodeDefaultLight.png new file mode 100644 index 000000000..49a2d4596 Binary files /dev/null and b/test/lib/mayaUsd/render/mayaToHydra/CustomShadersNodeTest/testCustomShadersNodeDefaultLight.png differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/CustomShadersNodeTest/testCustomShadersNodeDomeLightOnly.png b/test/lib/mayaUsd/render/mayaToHydra/CustomShadersNodeTest/testCustomShadersNodeDomeLightOnly.png new file mode 100644 index 000000000..df9721a4a Binary files /dev/null and b/test/lib/mayaUsd/render/mayaToHydra/CustomShadersNodeTest/testCustomShadersNodeDomeLightOnly.png differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/CustomShadersNodeTest/testCustomShadersNodeUseAllLights.png b/test/lib/mayaUsd/render/mayaToHydra/CustomShadersNodeTest/testCustomShadersNodeUseAllLights.png new file mode 100644 index 000000000..f4781fc76 Binary files /dev/null and b/test/lib/mayaUsd/render/mayaToHydra/CustomShadersNodeTest/testCustomShadersNodeUseAllLights.png differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/testCustomShadersNode.py b/test/lib/mayaUsd/render/mayaToHydra/testCustomShadersNode.py index 7f924399c..bb63a336a 100644 --- a/test/lib/mayaUsd/render/mayaToHydra/testCustomShadersNode.py +++ b/test/lib/mayaUsd/render/mayaToHydra/testCustomShadersNode.py @@ -19,13 +19,18 @@ import mtohUtils import mayaUtils from testUtils import PluginLoaded +import platform class TestCustomShadersNode(mtohUtils.MayaHydraBaseTestCase): #Subclassing mtohUtils.MayaHydraBaseTestCase to be able to call self.assertSnapshotClose # MayaHydraBaseTestCase.setUpClass requirement. _file = __file__ IMAGE_DIFF_FAIL_THRESHOLD = 0.01 - IMAGE_DIFF_FAIL_PERCENT = 0.1 + @property + def IMAGE_DIFF_FAIL_PERCENT(self): + if platform.system() == "Darwin": + return 3 + return 0.2 def test_LoadCustomShaderNode(self): with PluginLoaded('mayaHydraCustomShadersNode'): @@ -33,7 +38,15 @@ def test_LoadCustomShaderNode(self): "testCustomShadersNode", "testCustomShadersNode.ma") cmds.refresh() - self.assertSnapshotClose("testCustomShadersNode.png", self.IMAGE_DIFF_FAIL_THRESHOLD, self.IMAGE_DIFF_FAIL_PERCENT) + self.assertSnapshotClose("testCustomShadersNodeDefaultLight.png", self.IMAGE_DIFF_FAIL_THRESHOLD, self.IMAGE_DIFF_FAIL_PERCENT) + + # Switch the lighting mode to use all scene lights. + cmds.modelEditor(mayaUtils.activeModelPanel(), edit=True, displayLights = 'all') + self.assertSnapshotClose("testCustomShadersNodeUseAllLights.png", self.IMAGE_DIFF_FAIL_THRESHOLD, self.IMAGE_DIFF_FAIL_PERCENT) + + #Remove the direct lighting to check if the dome light works fine + cmds.setAttr("pointLightShape1.intensity", 0); + self.assertSnapshotClose("testCustomShadersNodeDomeLightOnly.png", self.IMAGE_DIFF_FAIL_THRESHOLD, self.IMAGE_DIFF_FAIL_PERCENT) if __name__ == '__main__': fixturesUtils.runTests(globals()) diff --git a/test/testSamples/testCustomShadersNode/UVChecker.png b/test/testSamples/testCustomShadersNode/UVChecker.png new file mode 100644 index 000000000..e5db15464 Binary files /dev/null and b/test/testSamples/testCustomShadersNode/UVChecker.png differ diff --git a/test/testSamples/testCustomShadersNode/testCustomShadersNode.ma b/test/testSamples/testCustomShadersNode/testCustomShadersNode.ma index c4e485997..dbcb7e4ae 100644 --- a/test/testSamples/testCustomShadersNode/testCustomShadersNode.ma +++ b/test/testSamples/testCustomShadersNode/testCustomShadersNode.ma @@ -1,26 +1,28 @@ -//Maya ASCII 2025ff01 scene +//Maya ASCII 2025ff02 scene //Name: testCustomShadersNode.ma -//Last modified: Tue, Dec 10, 2024 02:09:44 PM +//Last modified: Wed, Dec 11, 2024 11:46:10 AM //Codeset: 1252 -requires maya "2025ff01"; +requires maya "2025ff02"; requires -nodeType "MhCustomShaders" "mayaHydraCustomShadersNode" "2025.0"; +requires -nodeType "aiOptions" -nodeType "aiAOVDriver" -nodeType "aiAOVFilter" -nodeType "aiSkyDomeLight" + -nodeType "aiImagerDenoiserOidn" "mtoa" "5.5.0"; currentUnit -l centimeter -a degree -t film; fileInfo "application" "maya"; fileInfo "product" "Maya 2025"; fileInfo "version" "Preview Release"; -fileInfo "cutIdentifier" "202406201527-000000"; +fileInfo "cutIdentifier" "202412031025-000000"; fileInfo "osv" "Windows 10 Enterprise v2009 (Build: 19045)"; -fileInfo "UUID" "1CC4C576-45C2-C419-BB7C-C6ADB74EB77E"; +fileInfo "UUID" "32399C4F-4798-7197-6C92-ABA4785D13A3"; createNode transform -s -n "persp"; rename -uid "CAC40983-46C8-183E-1AFA-C889DB90A237"; setAttr ".v" no; - setAttr ".t" -type "double3" 1.7032043776282506 1.3442620959436657 1.6628472799166649 ; - setAttr ".r" -type "double3" -32.738352729575475 44.999999999961283 -2.2489917831959524e-15 ; + setAttr ".t" -type "double3" 2.3444946207989208 2.6275868051098961 2.2417681041490223 ; + setAttr ".r" -type "double3" -41.138352729575864 45.799999999961258 -6.8431882242206845e-15 ; createNode camera -s -n "perspShape" -p "persp"; rename -uid "98B104E6-4BBD-86B0-9523-75B2BDF52ADE"; setAttr -k off ".v" no; setAttr ".fl" 34.999999999999993; - setAttr ".coi" 2.7290490368539975; + setAttr ".coi" 4.1940870213230212; setAttr ".imn" -type "string" "persp"; setAttr ".den" -type "string" "persp_depth"; setAttr ".man" -type "string" "persp_mask"; @@ -78,21 +80,33 @@ createNode MhCustomShaders -n "MhCustomShaders1" -p "transform1"; rename -uid "2588465F-4F45-D5D0-30EE-079DD68236D8"; setAttr -k off ".v"; setAttr ".col" -type "double3" 1 1 1 ; +createNode transform -n "aiSkyDomeLight1"; + rename -uid "C5F4165C-4AFD-9433-9888-2892633BBCC7"; +createNode aiSkyDomeLight -n "aiSkyDomeLightShape1" -p "aiSkyDomeLight1"; + rename -uid "AA09D07E-4E4A-B07D-8CEA-5AA70457B47E"; + setAttr -k off ".v"; +createNode transform -n "pointLight1"; + rename -uid "5AB2DCD7-4F0C-4E42-65A9-E5824FC99182"; + setAttr ".t" -type "double3" 0.72655050063505378 0.58409642784605986 0 ; +createNode pointLight -n "pointLightShape1" -p "pointLight1"; + rename -uid "61F387C9-49DA-1D37-7F8F-1D80492D0949"; + setAttr -k off ".v"; + setAttr ".us" no; createNode lightLinker -s -n "lightLinker1"; - rename -uid "17FE1D90-4E82-91F9-865C-769041D175BC"; + rename -uid "F9AF3BD0-44AF-9D2E-5E78-A7A859E122BC"; setAttr -s 2 ".lnk"; setAttr -s 2 ".slnk"; createNode shapeEditorManager -n "shapeEditorManager"; - rename -uid "8DB5C160-420F-D5BA-683B-0A89E4280978"; + rename -uid "08EEB963-4DEE-1A77-BA04-5FBBAEBDBDED"; createNode poseInterpolatorManager -n "poseInterpolatorManager"; - rename -uid "D74D9600-4F8B-9358-8CF2-2DBF7BD24F2C"; + rename -uid "3DD30F0A-4745-FDB2-C2AA-1C90F62D90E8"; createNode displayLayerManager -n "layerManager"; - rename -uid "B164B0DD-4EF2-18A1-6FE0-CFBF493D8E6D"; + rename -uid "58745776-4014-38C3-8FAC-AB9D4C406B48"; createNode displayLayer -n "defaultLayer"; rename -uid "E6921E06-44BE-9413-CEDF-B8BE8AAC03F8"; setAttr ".ufem" -type "stringArray" 0 ; createNode renderLayerManager -n "renderLayerManager"; - rename -uid "AA6B9068-483F-5E56-A11F-519EA23FC0CD"; + rename -uid "3B89CDF4-4990-B59E-02ED-CB97E5291EDE"; createNode renderLayer -n "defaultRenderLayer"; rename -uid "DD7E50CB-4068-BEFD-F909-1092A320CBAA"; setAttr ".g" yes; @@ -144,6 +158,25 @@ createNode script -n "sceneConfigurationScriptNode"; rename -uid "6D6880C9-4A2E-EBCD-99E5-47A4F9F4379A"; setAttr ".b" -type "string" "playbackOptions -min 1 -max 120 -ast 1 -aet 200 "; setAttr ".st" 6; +createNode aiOptions -s -n "defaultArnoldRenderOptions"; + rename -uid "897C0433-427B-A1A7-56FF-7180E45E9460"; + setAttr ".version" -type "string" "5.5.0"; +createNode aiAOVFilter -s -n "defaultArnoldFilter"; + rename -uid "939FC437-435A-5D2D-E989-F6B44FC6D83B"; +createNode aiAOVDriver -s -n "defaultArnoldDriver"; + rename -uid "FFF5C067-4E9D-AF13-BE0A-57BE613433BF"; +createNode aiAOVDriver -s -n "defaultArnoldDisplayDriver"; + rename -uid "FAA3D8DB-498D-8F49-E692-9AB5EA825653"; + setAttr ".ai_translator" -type "string" "maya"; + setAttr ".output_mode" 0; +createNode aiImagerDenoiserOidn -s -n "defaultArnoldDenoiser"; + rename -uid "58A6D5FC-429A-C8A7-38B0-92BE87D40FF7"; +createNode file -n "file1"; + rename -uid "73223ABC-4CB5-EDF7-2134-1ABC6D855D11"; + setAttr ".ftn" -type "string" "D:/GIT/maya-hydra-opensource/test/testSamples/testCustomShadersNode/UVChecker.png"; + setAttr ".cs" -type "string" "sRGB"; +createNode place2dTexture -n "place2dTexture1"; + rename -uid "1C593DED-4767-D011-1183-BD90C0A7794B"; select -ne :time1; setAttr ".o" 1; setAttr ".unw" 1; @@ -161,7 +194,11 @@ select -ne :defaultShaderList1; setAttr -s 6 ".s"; select -ne :postProcessList1; setAttr -s 2 ".p"; +select -ne :defaultRenderUtilityList1; select -ne :defaultRenderingList1; +select -ne :lightList1; + setAttr -s 2 ".l"; +select -ne :defaultTextureList1; select -ne :standardSurface1; setAttr ".bc" -type "float3" 0.40000001 0.40000001 0.40000001 ; setAttr ".sr" 0.5; @@ -203,6 +240,8 @@ select -ne :defaultRenderGlobals; setAttr ".dss" -type "string" "standardSurface1"; select -ne :defaultResolution; setAttr ".pa" 1; +select -ne :defaultLightSet; + setAttr -s 2 ".dsm"; select -ne :defaultColorMgtGlobals; setAttr ".cfe" yes; setAttr ".cfp" -type "string" "/OCIO-configs/Maya2022-default/config.ocio"; @@ -215,11 +254,46 @@ select -ne :defaultColorMgtGlobals; select -ne :hardwareRenderGlobals; setAttr ".ctrs" 256; setAttr ".btrs" 512; +connectAttr "file1.oc" "aiSkyDomeLightShape1.sc"; relationship "link" ":lightLinker1" ":initialShadingGroup.message" ":defaultLightSet.message"; relationship "link" ":lightLinker1" ":initialParticleSE.message" ":defaultLightSet.message"; relationship "shadowLink" ":lightLinker1" ":initialShadingGroup.message" ":defaultLightSet.message"; relationship "shadowLink" ":lightLinker1" ":initialParticleSE.message" ":defaultLightSet.message"; connectAttr "layerManager.dli[0]" "defaultLayer.id"; connectAttr "renderLayerManager.rlmi[0]" "defaultRenderLayer.rlid"; +connectAttr ":defaultArnoldDenoiser.msg" ":defaultArnoldRenderOptions.imagers" -na + ; +connectAttr ":defaultArnoldDisplayDriver.msg" ":defaultArnoldRenderOptions.drivers" + -na; +connectAttr ":defaultArnoldFilter.msg" ":defaultArnoldRenderOptions.filt"; +connectAttr ":defaultArnoldDriver.msg" ":defaultArnoldRenderOptions.drvr"; +connectAttr ":defaultColorMgtGlobals.cme" "file1.cme"; +connectAttr ":defaultColorMgtGlobals.cfe" "file1.cmcf"; +connectAttr ":defaultColorMgtGlobals.cfp" "file1.cmcp"; +connectAttr ":defaultColorMgtGlobals.wsn" "file1.ws"; +connectAttr "place2dTexture1.c" "file1.c"; +connectAttr "place2dTexture1.tf" "file1.tf"; +connectAttr "place2dTexture1.rf" "file1.rf"; +connectAttr "place2dTexture1.mu" "file1.mu"; +connectAttr "place2dTexture1.mv" "file1.mv"; +connectAttr "place2dTexture1.s" "file1.s"; +connectAttr "place2dTexture1.wu" "file1.wu"; +connectAttr "place2dTexture1.wv" "file1.wv"; +connectAttr "place2dTexture1.re" "file1.re"; +connectAttr "place2dTexture1.of" "file1.of"; +connectAttr "place2dTexture1.r" "file1.ro"; +connectAttr "place2dTexture1.n" "file1.n"; +connectAttr "place2dTexture1.vt1" "file1.vt1"; +connectAttr "place2dTexture1.vt2" "file1.vt2"; +connectAttr "place2dTexture1.vt3" "file1.vt3"; +connectAttr "place2dTexture1.vc1" "file1.vc1"; +connectAttr "place2dTexture1.o" "file1.uv"; +connectAttr "place2dTexture1.ofs" "file1.fs"; +connectAttr "place2dTexture1.msg" ":defaultRenderUtilityList1.u" -na; connectAttr "defaultRenderLayer.msg" ":defaultRenderingList1.r" -na; +connectAttr "aiSkyDomeLightShape1.ltd" ":lightList1.l" -na; +connectAttr "pointLightShape1.ltd" ":lightList1.l" -na; +connectAttr "file1.msg" ":defaultTextureList1.tx" -na; +connectAttr "aiSkyDomeLight1.iog" ":defaultLightSet.dsm" -na; +connectAttr "pointLight1.iog" ":defaultLightSet.dsm" -na; // End of testCustomShadersNode.ma