Add LTE SpectralAPI example USDA files

Example files demonstrating wavelength-dependent material and light properties:

- spectral-material.usda: Materials with wavelength:reflectance and wavelength:ior
  (red, glass, fused silica with Sellmeier, foliage, gold)

- spectral-light.usda: Light sources with wavelength:emission
  (D65, D50, A, F2 illuminant presets, custom LED, sodium lamp)

- spectral-scene.usda: Complete scene with diamond dispersion, water, copper
  under D65/D50 lighting

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Syoyo Fujita
2025-11-30 02:33:13 +09:00
parent b94d202097
commit 99109c75e4
3 changed files with 683 additions and 0 deletions

218
models/spectral-light.usda Normal file
View File

@@ -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"
)
}
}

View File

@@ -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 = </Materials/SpectralRedMaterial/PBRShader.outputs:surface>
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 = </Materials/SpectralGlassMaterial/PBRShader.outputs:surface>
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 = </Materials/SpectralFusedSilicaMaterial/PBRShader.outputs:surface>
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 = </Materials/SpectralFoliageMaterial/PBRShader.outputs:surface>
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 = </Materials/SpectralGoldMaterial/PBRShader.outputs:surface>
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 = </Materials/SpectralRedMaterial>
}
# 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 = </Materials/SpectralGlassMaterial>
}
# 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 = </Materials/SpectralFusedSilicaMaterial>
}
# 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 = </Materials/SpectralFoliageMaterial>
}
# 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 = </Materials/SpectralGoldMaterial>
}
# 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"
)
}
}

234
models/spectral-scene.usda Normal file
View File

@@ -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 = </Scene/Materials/DiamondMaterial/Shader.outputs:surface>
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 = </Scene/Materials/WhiteDiffuse/Shader.outputs:surface>
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 = </Scene/Materials/WaterMaterial/Shader.outputs:surface>
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 = </Scene/Materials/CopperMaterial/Shader.outputs:surface>
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 = </Scene/Materials/DiamondMaterial>
}
# 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 = </Scene/Materials/WaterMaterial>
}
# Copper sphere
def Sphere "CopperSphere"
{
double radius = 0.25
double3 xformOp:translate = (1, 0.25, 0)
uniform token[] xformOpOrder = ["xformOp:translate"]
rel material:binding = </Scene/Materials/CopperMaterial>
}
# 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 = </Scene/Materials/WhiteDiffuse>
}
}
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"]
}
}