diff --git a/models/spectral-light.usda b/models/spectral-light.usda new file mode 100644 index 00000000..cad56719 --- /dev/null +++ b/models/spectral-light.usda @@ -0,0 +1,218 @@ +#usda 1.0 +( + doc = """LTE SpectralAPI Light Source Examples + Demonstrates wavelength-dependent light emission using the wavelength: namespace. + See doc/lte_spectral_api.md for specification. + + Examples include: + - D65 standard illuminant (daylight) + - D50 standard illuminant (horizon light) + - Illuminant A (incandescent) + - Custom spectral emission (warm LED) + - Fluorescent light (F2) + """ + metersPerUnit = 1 + upAxis = "Y" + customLayerData = { + string unitForWavelength = "nanometers" + } +) + +def Xform "Lights" +{ + # D65 Daylight using illuminant preset + def DistantLight "D65_Sunlight" ( + doc = "CIE Standard Illuminant D65 (noon daylight, 6504K)" + ) + { + float inputs:intensity = 1.0 + color3f inputs:color = (1.0, 1.0, 1.0) + float3 xformOp:rotateXYZ = (-45, 30, 0) + uniform token[] xformOpOrder = ["xformOp:rotateXYZ"] + + # LTE SpectralAPI: D65 illuminant preset + # Empty samples with preset metadata + float2[] wavelength:emission = [] ( + customData = { + string illuminantPreset = "d65" + } + ) + } + + # D50 Horizon light using illuminant preset + def DistantLight "D50_HorizonLight" ( + doc = "CIE Standard Illuminant D50 (horizon daylight, 5003K)" + ) + { + float inputs:intensity = 0.8 + color3f inputs:color = (1.0, 0.95, 0.9) + float3 xformOp:rotateXYZ = (-15, -60, 0) + uniform token[] xformOpOrder = ["xformOp:rotateXYZ"] + + # LTE SpectralAPI: D50 illuminant preset + float2[] wavelength:emission = [] ( + customData = { + string illuminantPreset = "d50" + } + ) + } + + # Illuminant A (incandescent/tungsten) + def SphereLight "IncandescentBulb" ( + doc = "CIE Standard Illuminant A (incandescent, 2856K)" + ) + { + float inputs:intensity = 500 + color3f inputs:color = (1.0, 0.85, 0.65) + float inputs:radius = 0.05 + double3 xformOp:translate = (2, 3, 1) + uniform token[] xformOpOrder = ["xformOp:translate"] + + # LTE SpectralAPI: Illuminant A preset + float2[] wavelength:emission = [] ( + customData = { + string illuminantPreset = "a" + } + ) + } + + # Custom warm LED spectrum + def RectLight "WarmLED" ( + doc = "Custom warm white LED spectral power distribution" + ) + { + float inputs:intensity = 200 + color3f inputs:color = (1.0, 0.9, 0.75) + float inputs:width = 0.5 + float inputs:height = 0.5 + double3 xformOp:translate = (-2, 3, 0) + float3 xformOp:rotateXYZ = (90, 0, 0) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:rotateXYZ"] + + # LTE SpectralAPI: Custom LED SPD + # Blue pump peak + phosphor emission + float2[] wavelength:emission = [ + (380, 0.02), (400, 0.05), (420, 0.15), (440, 0.45), + (450, 0.85), (455, 1.00), (460, 0.90), (470, 0.40), + (480, 0.25), (500, 0.35), (520, 0.50), (540, 0.65), + (560, 0.80), (580, 0.90), (600, 0.88), (620, 0.75), + (640, 0.58), (660, 0.40), (680, 0.25), (700, 0.15), + (720, 0.08), (740, 0.04), (760, 0.02), (780, 0.01) + ] ( + customData = { + string interpolation = "linear" + } + ) + } + + # F2 Cool White Fluorescent + def RectLight "FluorescentPanel" ( + doc = "CIE Fluorescent Illuminant F2 (cool white fluorescent)" + ) + { + float inputs:intensity = 150 + color3f inputs:color = (1.0, 1.0, 0.95) + float inputs:width = 1.2 + float inputs:height = 0.3 + double3 xformOp:translate = (0, 4, 0) + float3 xformOp:rotateXYZ = (90, 0, 0) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:rotateXYZ"] + + # LTE SpectralAPI: F2 fluorescent preset + float2[] wavelength:emission = [] ( + customData = { + string illuminantPreset = "f2" + } + ) + } + + # Explicit D65 SPD (relative spectral power distribution) + def DistantLight "D65_Explicit" ( + doc = "D65 with explicit SPD data (normalized at 560nm)" + ) + { + float inputs:intensity = 0.5 + color3f inputs:color = (1.0, 1.0, 1.0) + float3 xformOp:rotateXYZ = (-60, 90, 0) + uniform token[] xformOpOrder = ["xformOp:rotateXYZ"] + + # LTE SpectralAPI: Explicit D65 relative SPD + # Values normalized so 560nm = 100.0 + float2[] wavelength:emission = [ + (380, 49.98), (390, 52.31), (400, 54.65), (410, 68.70), + (420, 82.75), (430, 87.12), (440, 91.49), (450, 92.46), + (460, 93.43), (470, 90.06), (480, 86.68), (490, 95.77), + (500, 104.86), (510, 110.94), (520, 117.01), (530, 117.41), + (540, 117.81), (550, 116.34), (560, 100.00), (570, 108.40), + (580, 103.90), (590, 104.05), (600, 100.00), (610, 96.33), + (620, 95.79), (630, 88.69), (640, 90.01), (650, 89.60), + (660, 87.70), (670, 83.29), (680, 83.70), (690, 80.03), + (700, 80.21), (710, 82.28), (720, 78.28), (730, 69.72), + (740, 71.61), (750, 74.35), (760, 61.60), (770, 69.89), (780, 75.09) + ] ( + customData = { + string interpolation = "linear" + } + ) + } + + # Monochromatic sodium lamp + def SphereLight "SodiumLamp" ( + doc = "Low-pressure sodium lamp (nearly monochromatic at 589nm)" + ) + { + float inputs:intensity = 300 + color3f inputs:color = (1.0, 0.83, 0.0) + float inputs:radius = 0.1 + double3 xformOp:translate = (3, 2.5, -2) + uniform token[] xformOpOrder = ["xformOp:translate"] + + # LTE SpectralAPI: Narrow-band sodium emission + # Dominant 589nm D-line + float2[] wavelength:emission = [ + (380, 0.0), (500, 0.0), (580, 0.01), (585, 0.10), + (587, 0.50), (589, 1.00), (591, 0.50), (593, 0.10), + (600, 0.01), (700, 0.0), (780, 0.0) + ] ( + customData = { + string interpolation = "linear" + } + ) + } +} + +def Xform "Geometry" +{ + # Test spheres to see lighting + def Sphere "TestSphere1" + { + double radius = 0.3 + double3 xformOp:translate = (-1.5, 0.3, 0) + uniform token[] xformOpOrder = ["xformOp:translate"] + } + + def Sphere "TestSphere2" + { + double radius = 0.3 + double3 xformOp:translate = (0, 0.3, 0) + uniform token[] xformOpOrder = ["xformOp:translate"] + } + + def Sphere "TestSphere3" + { + double radius = 0.3 + double3 xformOp:translate = (1.5, 0.3, 0) + uniform token[] xformOpOrder = ["xformOp:translate"] + } + + # Ground plane + def Mesh "Ground" + { + int[] faceVertexCounts = [4] + int[] faceVertexIndices = [0, 1, 2, 3] + point3f[] points = [(-5, 0, -5), (5, 0, -5), (5, 0, 5), (-5, 0, 5)] + normal3f[] primvars:normals = [(0, 1, 0), (0, 1, 0), (0, 1, 0), (0, 1, 0)] ( + interpolation = "vertex" + ) + } +} diff --git a/models/spectral-material.usda b/models/spectral-material.usda new file mode 100644 index 00000000..3a5b0415 --- /dev/null +++ b/models/spectral-material.usda @@ -0,0 +1,231 @@ +#usda 1.0 +( + doc = """LTE SpectralAPI Material Examples + Demonstrates wavelength-dependent material properties using the wavelength: namespace. + See doc/lte_spectral_api.md for specification. + + Examples include: + - Spectral reflectance (wavelength:reflectance) + - Spectral IOR with various interpolation methods + - Sellmeier coefficients for glass dispersion + """ + metersPerUnit = 1 + upAxis = "Y" + customLayerData = { + string unitForWavelength = "nanometers" + } +) + +def Scope "Materials" +{ + # Basic spectral material with reflectance data + def Material "SpectralRedMaterial" + { + token outputs:surface.connect = + + def Shader "PBRShader" + { + uniform token info:id = "UsdPreviewSurface" + color3f inputs:diffuseColor = (0.8, 0.1, 0.1) + float inputs:roughness = 0.4 + float inputs:metallic = 0.0 + float inputs:ior = 1.5 + token outputs:surface + + # LTE SpectralAPI: Spectral reflectance + # Red-shifted reflectance curve + float2[] wavelength:reflectance = [ + (380, 0.05), (400, 0.05), (420, 0.05), (440, 0.05), + (460, 0.06), (480, 0.07), (500, 0.08), (520, 0.10), + (540, 0.15), (560, 0.25), (580, 0.45), (600, 0.65), + (620, 0.78), (640, 0.85), (660, 0.88), (680, 0.90), + (700, 0.91), (720, 0.92), (740, 0.92), (760, 0.93), (780, 0.93) + ] ( + customData = { + string interpolation = "linear" + } + ) + } + } + + # Spectral material with IOR dispersion (crown glass) + def Material "SpectralGlassMaterial" + { + token outputs:surface.connect = + + def Shader "PBRShader" + { + uniform token info:id = "UsdPreviewSurface" + color3f inputs:diffuseColor = (1.0, 1.0, 1.0) + float inputs:roughness = 0.0 + float inputs:metallic = 0.0 + float inputs:opacity = 0.1 + float inputs:ior = 1.52 + token outputs:surface + + # LTE SpectralAPI: Spectral IOR (BK7 Crown Glass) + # IOR varies with wavelength (dispersion) + float2[] wavelength:ior = [ + (380, 1.5308), (400, 1.5253), (420, 1.5214), + (440, 1.5183), (460, 1.5158), (480, 1.5137), + (500, 1.5120), (520, 1.5105), (540, 1.5092), + (560, 1.5080), (580, 1.5070), (600, 1.5061), + (620, 1.5053), (640, 1.5046), (660, 1.5039), + (680, 1.5033), (700, 1.5028), (720, 1.5023), + (740, 1.5018), (760, 1.5014), (780, 1.5010) + ] ( + customData = { + string interpolation = "linear" + } + ) + } + } + + # Spectral material with Sellmeier IOR (fused silica) + def Material "SpectralFusedSilicaMaterial" + { + token outputs:surface.connect = + + def Shader "PBRShader" + { + uniform token info:id = "UsdPreviewSurface" + color3f inputs:diffuseColor = (1.0, 1.0, 1.0) + float inputs:roughness = 0.0 + float inputs:metallic = 0.0 + float inputs:opacity = 0.05 + float inputs:ior = 1.458 + token outputs:surface + + # LTE SpectralAPI: Sellmeier coefficients for fused silica + # n^2 = 1 + B1*l^2/(l^2-C1) + B2*l^2/(l^2-C2) + B3*l^2/(l^2-C3) + # where l is wavelength in micrometers + float2[] wavelength:ior = [ + (0.6961663, 0.0684043), # (B1, C1) - C1 in um^2 + (0.4079426, 0.1162414), # (B2, C2) + (0.8974794, 9.896161) # (B3, C3) + ] ( + customData = { + string interpolation = "sellmeier" + string unitForWavelength = "micrometers" + } + ) + } + } + + # Spectral material with green foliage reflectance + def Material "SpectralFoliageMaterial" + { + token outputs:surface.connect = + + def Shader "PBRShader" + { + uniform token info:id = "UsdPreviewSurface" + color3f inputs:diffuseColor = (0.2, 0.5, 0.15) + float inputs:roughness = 0.6 + float inputs:metallic = 0.0 + token outputs:surface + + # LTE SpectralAPI: Typical green foliage reflectance + # Shows chlorophyll absorption (low blue/red, high green/NIR) + float2[] wavelength:reflectance = [ + (380, 0.03), (400, 0.04), (420, 0.04), (440, 0.05), + (460, 0.05), (480, 0.06), (500, 0.08), (520, 0.12), + (540, 0.18), (560, 0.20), (580, 0.15), (600, 0.10), + (620, 0.08), (640, 0.07), (660, 0.06), (680, 0.08), + (700, 0.35), (720, 0.45), (740, 0.48), (760, 0.50), (780, 0.52) + ] ( + customData = { + string interpolation = "linear" + } + ) + } + } + + # Gold metal with spectral reflectance + def Material "SpectralGoldMaterial" + { + token outputs:surface.connect = + + def Shader "PBRShader" + { + uniform token info:id = "UsdPreviewSurface" + color3f inputs:diffuseColor = (1.0, 0.84, 0.0) + float inputs:roughness = 0.2 + float inputs:metallic = 1.0 + token outputs:surface + + # LTE SpectralAPI: Gold reflectance spectrum + # High reflectance in red/IR, absorption in blue/green + float2[] wavelength:reflectance = [ + (380, 0.35), (400, 0.38), (420, 0.38), (440, 0.37), + (460, 0.36), (480, 0.36), (500, 0.42), (520, 0.62), + (540, 0.82), (560, 0.90), (580, 0.93), (600, 0.95), + (620, 0.96), (640, 0.97), (660, 0.97), (680, 0.98), + (700, 0.98), (720, 0.98), (740, 0.99), (760, 0.99), (780, 0.99) + ] ( + customData = { + string interpolation = "linear" + } + ) + } + } +} + +def Xform "Geometry" +{ + # Sphere with red spectral material + def Sphere "RedSphere" + { + double radius = 0.5 + double3 xformOp:translate = (-2, 0.5, 0) + uniform token[] xformOpOrder = ["xformOp:translate"] + rel material:binding = + } + + # Sphere with glass material (IOR dispersion) + def Sphere "GlassSphere" + { + double radius = 0.5 + double3 xformOp:translate = (-1, 0.5, 0) + uniform token[] xformOpOrder = ["xformOp:translate"] + rel material:binding = + } + + # Sphere with fused silica (Sellmeier) + def Sphere "SilicaSphere" + { + double radius = 0.5 + double3 xformOp:translate = (0, 0.5, 0) + uniform token[] xformOpOrder = ["xformOp:translate"] + rel material:binding = + } + + # Sphere with foliage material + def Sphere "FoliageSphere" + { + double radius = 0.5 + double3 xformOp:translate = (1, 0.5, 0) + uniform token[] xformOpOrder = ["xformOp:translate"] + rel material:binding = + } + + # Sphere with gold material + def Sphere "GoldSphere" + { + double radius = 0.5 + double3 xformOp:translate = (2, 0.5, 0) + uniform token[] xformOpOrder = ["xformOp:translate"] + rel material:binding = + } + + # Ground plane + def Mesh "Ground" + { + int[] faceVertexCounts = [4] + int[] faceVertexIndices = [0, 1, 2, 3] + point3f[] points = [(-5, 0, -5), (5, 0, -5), (5, 0, 5), (-5, 0, 5)] + normal3f[] primvars:normals = [(0, 1, 0), (0, 1, 0), (0, 1, 0), (0, 1, 0)] ( + interpolation = "vertex" + ) + } +} diff --git a/models/spectral-scene.usda b/models/spectral-scene.usda new file mode 100644 index 00000000..db20443a --- /dev/null +++ b/models/spectral-scene.usda @@ -0,0 +1,234 @@ +#usda 1.0 +( + doc = """LTE SpectralAPI Complete Scene Example + Demonstrates a complete scene with spectral materials and lights. + Useful for testing spectral rendering pipelines. + + Scene: Glass prism demonstrating dispersion under D65 daylight + """ + metersPerUnit = 1 + upAxis = "Y" + customLayerData = { + string unitForWavelength = "nanometers" + } +) + +def Xform "Scene" +{ + def Scope "Materials" + { + # Diamond with high dispersion (Sellmeier coefficients) + def Material "DiamondMaterial" + { + token outputs:surface.connect = + + def Shader "Shader" + { + uniform token info:id = "UsdPreviewSurface" + color3f inputs:diffuseColor = (1.0, 1.0, 1.0) + float inputs:roughness = 0.0 + float inputs:metallic = 0.0 + float inputs:opacity = 0.02 + float inputs:ior = 2.417 + token outputs:surface + + # LTE SpectralAPI: Diamond Sellmeier coefficients + # Very high dispersion (fire) + float2[] wavelength:ior = [ + (0.4083, 0.0), # (B1, C1) C in um^2 + (0.0, 0.0), # (B2, C2) + (4.3356, 0.01064) # (B3, C3) + ] ( + customData = { + string interpolation = "sellmeier" + string unitForWavelength = "micrometers" + } + ) + } + } + + # White diffuse reference + def Material "WhiteDiffuse" + { + token outputs:surface.connect = + + def Shader "Shader" + { + uniform token info:id = "UsdPreviewSurface" + color3f inputs:diffuseColor = (0.9, 0.9, 0.9) + float inputs:roughness = 1.0 + float inputs:metallic = 0.0 + token outputs:surface + + # LTE SpectralAPI: Near-perfect white reflectance + float2[] wavelength:reflectance = [ + (380, 0.88), (400, 0.89), (420, 0.89), (440, 0.90), + (460, 0.90), (480, 0.90), (500, 0.90), (520, 0.90), + (540, 0.90), (560, 0.90), (580, 0.90), (600, 0.90), + (620, 0.90), (640, 0.90), (660, 0.90), (680, 0.90), + (700, 0.90), (720, 0.90), (740, 0.90), (760, 0.90), (780, 0.90) + ] ( + customData = { + string interpolation = "linear" + } + ) + } + } + + # Water material + def Material "WaterMaterial" + { + token outputs:surface.connect = + + def Shader "Shader" + { + uniform token info:id = "UsdPreviewSurface" + color3f inputs:diffuseColor = (0.8, 0.9, 1.0) + float inputs:roughness = 0.0 + float inputs:metallic = 0.0 + float inputs:opacity = 0.1 + float inputs:ior = 1.333 + token outputs:surface + + # LTE SpectralAPI: Water IOR (slight dispersion) + float2[] wavelength:ior = [ + (380, 1.3435), (400, 1.3420), (420, 1.3408), + (440, 1.3397), (460, 1.3388), (480, 1.3380), + (500, 1.3373), (520, 1.3367), (540, 1.3362), + (560, 1.3357), (580, 1.3353), (600, 1.3349), + (620, 1.3346), (640, 1.3343), (660, 1.3340), + (680, 1.3337), (700, 1.3335), (720, 1.3333), + (740, 1.3331), (760, 1.3329), (780, 1.3327) + ] ( + customData = { + string interpolation = "linear" + } + ) + } + } + + # Copper metal + def Material "CopperMaterial" + { + token outputs:surface.connect = + + def Shader "Shader" + { + uniform token info:id = "UsdPreviewSurface" + color3f inputs:diffuseColor = (0.95, 0.64, 0.54) + float inputs:roughness = 0.3 + float inputs:metallic = 1.0 + token outputs:surface + + # LTE SpectralAPI: Copper reflectance + float2[] wavelength:reflectance = [ + (380, 0.30), (400, 0.32), (420, 0.33), (440, 0.35), + (460, 0.38), (480, 0.42), (500, 0.50), (520, 0.58), + (540, 0.66), (560, 0.74), (580, 0.82), (600, 0.88), + (620, 0.92), (640, 0.94), (660, 0.95), (680, 0.96), + (700, 0.97), (720, 0.97), (740, 0.98), (760, 0.98), (780, 0.98) + ] ( + customData = { + string interpolation = "linear" + } + ) + } + } + } + + def Scope "Lights" + { + # Main D65 sunlight + def DistantLight "Sun" + { + float inputs:intensity = 1.5 + color3f inputs:color = (1.0, 1.0, 1.0) + float3 xformOp:rotateXYZ = (-45, 30, 0) + uniform token[] xformOpOrder = ["xformOp:rotateXYZ"] + + float2[] wavelength:emission = [] ( + customData = { + string illuminantPreset = "d65" + } + ) + } + + # Fill light (warmer) + def DistantLight "FillLight" + { + float inputs:intensity = 0.3 + color3f inputs:color = (1.0, 0.95, 0.85) + float3 xformOp:rotateXYZ = (-20, -60, 0) + uniform token[] xformOpOrder = ["xformOp:rotateXYZ"] + + float2[] wavelength:emission = [] ( + customData = { + string illuminantPreset = "d50" + } + ) + } + } + + def Xform "Objects" + { + # Diamond gem + def Mesh "Diamond" + { + # Simplified diamond shape (octahedron) + int[] faceVertexCounts = [3, 3, 3, 3, 3, 3, 3, 3] + int[] faceVertexIndices = [ + 0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 1, + 5, 2, 1, 5, 3, 2, 5, 4, 3, 5, 1, 4 + ] + point3f[] points = [ + (0, 0.5, 0), + (0.35, 0, 0.35), (0.35, 0, -0.35), + (-0.35, 0, -0.35), (-0.35, 0, 0.35), + (0, -0.3, 0) + ] + double3 xformOp:translate = (0, 0.5, 0) + uniform token[] xformOpOrder = ["xformOp:translate"] + rel material:binding = + } + + # Water droplet + def Sphere "WaterDrop" + { + double radius = 0.2 + double3 xformOp:translate = (-1, 0.2, 0.5) + uniform token[] xformOpOrder = ["xformOp:translate"] + rel material:binding = + } + + # Copper sphere + def Sphere "CopperSphere" + { + double radius = 0.25 + double3 xformOp:translate = (1, 0.25, 0) + uniform token[] xformOpOrder = ["xformOp:translate"] + rel material:binding = + } + + # Ground plane + def Mesh "Ground" + { + int[] faceVertexCounts = [4] + int[] faceVertexIndices = [0, 1, 2, 3] + point3f[] points = [(-3, 0, -3), (3, 0, -3), (3, 0, 3), (-3, 0, 3)] + normal3f[] primvars:normals = [(0, 1, 0), (0, 1, 0), (0, 1, 0), (0, 1, 0)] ( + interpolation = "vertex" + ) + rel material:binding = + } + } + + def Camera "MainCamera" + { + float focalLength = 35 + float horizontalAperture = 36 + float verticalAperture = 24 + double3 xformOp:translate = (0, 1.5, 4) + float3 xformOp:rotateXYZ = (-15, 0, 0) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:rotateXYZ"] + } +}