Add UI controls for PBR debugging features

Integrate Material Override, Validation, and Split View systems into the MaterialX demo GUI.

## UI Additions

### AOV Visualization Dropdown
- Added 7 new Priority 1 AOV modes:
  * UV Layout Overlay (grid + seam detection)
  * Ambient Occlusion
  * Anisotropy (brushed metal)
  * Sheen (fabric materials)
  * Iridescence (thin-film effects)
  * Normal Quality Check (green=valid, red=error)
  * Shader Error Detection (NaN/Inf/range checking)

### Material Override Folder
- Enable/Disable toggle
- Roughness override slider (0-1)
- Metalness override slider (0-1)
- Disable Normal Maps checkbox
- Disable All Textures checkbox
- Preset dropdown (6 presets: Mirror, Matte, White Clay, etc.)
- Reset All Overrides button

### Material Validation Folder
- Validate Now button (runs 12 validation rules)
- Error/Warning/Info count displays
- Auto-validate on Load checkbox
- Logs detailed report to console

### Split View Compare Folder
- Enable Split View toggle
- Split Mode dropdown (Vertical, Horizontal, Diagonal)
- Split Position slider (adjustable divider)
- Secondary View dropdown (Albedo, Normals, Roughness, etc.)

## Technical Changes

### materialx.js
- Import new modules (material-override, material-validator, split-view-comparison)
- Add 4 new GUI folders with 24 total controls
- Update animate() to support split view rendering
- Add auto-validation on material load
- Expose debugging tools to window object

### materialx.html
- Add 3 script tags to import PBR debugging modules

## Features Now Accessible
-  7 Advanced AOV modes via dropdown
-  Material property overrides (roughness, metalness, textures)
-  6 Material override presets (Mirror, Matte, White Clay, etc.)
-  Material validation with 12 rules (energy conservation, IOR, colorspace)
-  Split view comparison (3 modes: vertical, horizontal, diagonal)
-  Auto-validation on scene load

## Documentation
- Added UI-INTEGRATION-SUMMARY.md with testing checklist and usage guide

🚧 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-21 02:27:16 +09:00
parent 40e9cccf4e
commit 5d9b10d973
3 changed files with 584 additions and 7 deletions

View File

@@ -0,0 +1,347 @@
# UI Integration Summary - PBR Debugging Tools
## Overview
This document summarizes the UI integration of the Priority 1 PBR debugging features into the MaterialX web demo.
**Date**: 2025-01-21
**Status**: ✅ Complete
---
## Changes Made
### 1. materialx.js Updates
**Import Additions** (lines 42-49):
```javascript
import {
applyMaterialOverrides,
resetMaterialOverrides,
applyOverridePreset,
OVERRIDE_PRESETS
} from './material-override.js';
import { MaterialValidator } from './material-validator.js';
import { SplitViewComparison, COMPARISON_PRESETS } from './split-view-comparison.js';
```
**AOV Dropdown Enhancements** (lines 3010-3044):
Added 7 new AOV visualization modes to the dropdown:
- `UV Layout Overlay` - Grid lines + seam detection
- `Ambient Occlusion` - AO map visualization
- `Anisotropy` - Brushed metal direction/strength
- `Sheen` - Fabric material sheen
- `Iridescence` - Thin-film effects
- `Normal Quality Check` - Validates normal maps (green=valid, red=error)
- `Shader Error Detection` - Detects NaN/Inf/range errors
**Material Override Controls** (lines 3048-3116):
```javascript
const overrideFolder = gui.addFolder('Material Override');
```
- Enable/Disable toggle
- Roughness override slider (0-1)
- Metalness override slider (0-1)
- Disable Normal Maps checkbox
- Disable All Textures checkbox
- Preset dropdown:
- Base Color Only
- Normals Only
- Flat Shading
- Mirror
- Matte
- White Clay
- Reset All Overrides button
**Material Validation Controls** (lines 3118-3148):
```javascript
const validationFolder = gui.addFolder('Material Validation');
```
- Validate Now button (runs 12 validation rules)
- Error count display (read-only)
- Warning count display (read-only)
- Info count display (read-only)
- Auto-validate on Load checkbox
**Split View Comparison Controls** (lines 3150-3236):
```javascript
const splitViewFolder = gui.addFolder('Split View Compare');
```
- Enable Split View toggle
- Split Mode dropdown:
- Vertical (Left/Right)
- Horizontal (Top/Bottom)
- Diagonal
- Split Position slider (0-1)
- Secondary View dropdown:
- Material (Original)
- Albedo
- Normals (World)
- Roughness
- Metalness
- UV Layout
**Animation Loop Update** (lines 5453-5467):
```javascript
// Check if split view comparison is enabled
if (window.splitViewComparison && window.splitViewComparison.getState().active) {
window.splitViewComparison.render();
} else {
// Normal rendering...
}
```
**Auto-Validation** (lines 3500-3510):
Auto-validates materials when loading if "Auto-validate on Load" is enabled.
**Window Exports** (lines 5484-5491):
```javascript
window.applyMaterialOverrides = applyMaterialOverrides;
window.resetMaterialOverrides = resetMaterialOverrides;
window.applyOverridePreset = applyOverridePreset;
window.MaterialValidator = MaterialValidator;
window.SplitViewComparison = SplitViewComparison;
window.COMPARISON_PRESETS = COMPARISON_PRESETS;
window.OVERRIDE_PRESETS = OVERRIDE_PRESETS;
```
---
### 2. materialx.html Updates
**Script Tag Additions** (lines 1208-1211):
```html
<!-- PBR Debugging Tools (must load before main script) -->
<script type="module" src="material-override.js"></script>
<script type="module" src="material-validator.js"></script>
<script type="module" src="split-view-comparison.js"></script>
```
---
## Feature Access Guide
### How to Use Each Feature
#### 1. Advanced AOV Modes
1. Load a USD file with PBR materials
2. Open the **"AOV Visualization"** folder in the right panel
3. Select from the "AOV Mode" dropdown:
- **UV Layout Overlay**: Shows UV grid + seams (red highlights)
- **Ambient Occlusion**: Visualizes AO maps in grayscale
- **Anisotropy**: Direction as hue, strength as brightness
- **Sheen**: Shows fabric sheen color + roughness
- **Iridescence**: R=strength, G=thickness, B=IOR
- **Normal Quality Check**: Green=valid, Yellow=warning, Red=error
- **Shader Error Detection**: Magenta=NaN, Yellow=Inf, Orange=high values
#### 2. Material Override System
1. Open the **"Material Override"** folder in the right panel
2. Enable "Enable Overrides" checkbox
3. Adjust sliders or toggle checkboxes:
- **Roughness Override**: Force all materials to specific roughness
- **Metalness Override**: Force all materials to specific metalness
- **Disable Normal Maps**: Remove normal mapping globally
- **Disable All Textures**: Show only material constant values
4. Or select a preset from **"Apply Preset"** dropdown:
- **Base Color Only**: Textures off, neutral roughness/metalness
- **Normals Only**: Gray base color, only normals visible
- **Flat Shading**: Disable normal maps
- **Mirror**: Roughness=0, Metalness=1
- **Matte**: Roughness=1, Metalness=0
- **White Clay**: Material preview style (gray, no textures)
5. Click **"Reset All Overrides"** to restore original materials
#### 3. Material Validation
1. Open the **"Material Validation"** folder in the right panel
2. Click **"🔍 Validate Scene"** to run validation
3. View results:
- **Errors** (red): Critical issues (wrong colorspace, physically impossible values)
- **Warnings** (yellow): Best practice violations (energy conservation, unusual IOR)
- **Info** (blue): Suggestions (missing normal maps, intermediate metalness)
4. Check browser console for detailed report
5. Enable **"Auto-validate on Load"** to run validation automatically when loading files
**Validation Rules** (12 total):
- Energy conservation (baseColor * metalness ≤ 1.0)
- IOR range (1.0-3.0)
- Metallic IOR (complex IOR for metals)
- Texture power-of-two dimensions
- Base color colorspace (sRGB required)
- Normal map colorspace (Linear required)
- Data texture colorspace (roughness/metalness/AO must be Linear)
- Missing normal map suggestions
- Zero roughness warnings (perfect mirrors rare in reality)
- Intermediate metalness warnings (should be 0 or 1)
- Bright base color warnings (dielectrics usually <0.9)
- Dark base color for metals (metals are usually bright)
#### 4. Split View Comparison
1. Open the **"Split View Compare"** folder in the right panel
2. Enable **"Enable Split View"** checkbox
3. Configure split:
- **Split Mode**: Choose Vertical, Horizontal, or Diagonal
- **Split Position**: Adjust divider position (0.0-1.0)
- **Secondary View**: Choose what to show on the right/bottom:
- Material (Original): Normal rendering
- Albedo: Base color only
- Normals (World): World-space normals
- Roughness: Grayscale roughness
- Metalness: Grayscale metalness
- UV Layout: UV grid overlay
4. Drag the split position slider to compare different regions
5. Disable to return to normal single-view rendering
**Use Cases**:
- Compare final render vs. albedo
- Compare with vs. without normal maps
- Compare metallic vs. dielectric materials
- Compare different roughness values
- Inspect UV layout overlaid on render
---
## Files Modified
1. **web/js/materialx.js**
- Added 3 imports for new modules
- Extended AOV dropdown with 7 new modes
- Added Material Override folder with controls
- Added Material Validation folder with controls
- Added Split View Compare folder with controls
- Updated animate() to support split view rendering
- Added auto-validation on material load
- Exposed new classes to window object
2. **web/js/materialx.html**
- Added 3 script tags to import new modules
---
## Testing Checklist
To verify the integration works correctly:
- [ ] Load a USD file with PBR materials
- [ ] Open AOV dropdown and test new modes (UV Layout, AO, Anisotropy, etc.)
- [ ] Open Material Override folder
- [ ] Enable overrides and adjust roughness slider
- [ ] Enable overrides and adjust metalness slider
- [ ] Toggle "Disable Normal Maps"
- [ ] Toggle "Disable All Textures"
- [ ] Try each preset (Mirror, Matte, White Clay, etc.)
- [ ] Click "Reset All Overrides"
- [ ] Open Material Validation folder
- [ ] Click "Validate Scene" button
- [ ] Check console for validation report
- [ ] Verify error/warning/info counts update
- [ ] Enable "Auto-validate on Load" and reload a file
- [ ] Open Split View Compare folder
- [ ] Enable split view
- [ ] Try Vertical split mode
- [ ] Try Horizontal split mode
- [ ] Try Diagonal split mode
- [ ] Adjust split position slider
- [ ] Change Secondary View to Albedo
- [ ] Change Secondary View to UV Layout
- [ ] Disable split view
---
## Known Limitations
1. **Split View Diagonal Mode**: Stencil buffer drawing not fully implemented (placeholder)
2. **Material Override**: Doesn't override emission color (only roughness/metalness/base color)
3. **Validation**: Runs on current scene state (not original USD data)
4. **Auto-Validation**: Only triggers on material load, not on manual edits
---
## Architecture Notes
### Module Dependencies
```
materialx.js
├─ material-override.js (exports functions + OVERRIDE_PRESETS)
├─ material-validator.js (exports MaterialValidator class)
└─ split-view-comparison.js (exports SplitViewComparison class + COMPARISON_PRESETS)
```
### Global Scope Exports
All debugging tools are exported to `window` object for easy access:
- `window.MaterialValidator`
- `window.SplitViewComparison`
- `window.applyMaterialOverrides(scene, overrides)`
- `window.resetMaterialOverrides(scene)`
- `window.applyOverridePreset(scene, presetName)`
### GUI Integration Pattern
All features use **lil-gui** folders:
1. Create params object with initial values
2. Add controllers (sliders, dropdowns, buttons)
3. Attach onChange handlers to update scene
4. Call `gui.controllersRecursive().forEach(c => c.updateDisplay())` to refresh
---
## Performance Notes
- **AOV Modes**: Replace all materials with custom shaders (may be slow for large scenes)
- **Material Override**: Stores original properties in Map (low overhead)
- **Validation**: Iterates all meshes/materials (O(n) complexity)
- **Split View**: Renders scene twice per frame (2x render cost)
For large scenes (>1000 meshes):
- Keep AOV modes closed when not in use
- Disable split view when not comparing
- Run validation manually instead of auto-validate
---
## Future Enhancements
Remaining Priority 1 features not yet implemented:
1. **Texture Channel Inspector** (pending)
- Histogram visualization
- Per-channel statistics
- Issue detection (all zeros, clamped values)
UI improvements:
- Draggable split view divider with mouse
- Validation results panel (instead of console-only)
- Material override color picker for base color
- Split view quick presets (Final vs Albedo, With vs Without Normals)
---
## Commit Summary
**Added**:
- Material Override UI controls (folder with 7 controls)
- Material Validation UI controls (folder with 5 displays)
- Split View Comparison UI controls (folder with 4 controls)
- 7 new AOV modes to dropdown (UV Layout, AO, Anisotropy, Sheen, Iridescence, Normal Quality, Shader Error)
- Auto-validation on material load
- Split view rendering in animation loop
- Module imports and window exports
**Modified**:
- `web/js/materialx.js` (+250 lines)
- `web/js/materialx.html` (+4 lines)
**Total Lines Added**: ~254 lines of UI integration code
---
## Related Documentation
- [PBR-DEBUGGING-STATUS.md](./PBR-DEBUGGING-STATUS.md) - Implementation status tracking
- [README-pbr-debugging-tools.md](./README-pbr-debugging-tools.md) - User guide for all debugging tools
- [material-override.js](./material-override.js) - Material override implementation
- [material-validator.js](./material-validator.js) - Validation system implementation
- [split-view-comparison.js](./split-view-comparison.js) - Split view implementation
---
**Last Updated**: 2025-01-21
**Status**: ✅ Ready for testing and user feedback

View File

@@ -1205,6 +1205,11 @@
}
</script>
<!-- PBR Debugging Tools (must load before main script) -->
<script type="module" src="material-override.js"></script>
<script type="module" src="material-validator.js"></script>
<script type="module" src="split-view-comparison.js"></script>
<!-- Main application script as module -->
<script type="module" src="materialx.js"></script>
</body>

View File

@@ -39,6 +39,14 @@ import {
handleMaterialPropertyPickerClick,
resizeMaterialPropertyTargets
} from './material-property-picker.js';
import {
applyMaterialOverrides,
resetMaterialOverrides,
applyOverridePreset,
OVERRIDE_PRESETS
} from './material-override.js';
import { MaterialValidator } from './material-validator.js';
import { SplitViewComparison, COMPARISON_PRESETS } from './split-view-comparison.js';
// Embedded default OpenPBR scene (simple sphere with material)
const EMBEDDED_USDA_SCENE = `#usda 1.0
@@ -3016,6 +3024,7 @@ function setupGUI() {
'Binormals': AOV_MODES.BINORMALS,
'UV Coords 0': AOV_MODES.TEXCOORD_0,
'UV Coords 1': AOV_MODES.TEXCOORD_1,
'UV Layout Overlay': AOV_MODES.UV_LAYOUT,
'World Position': AOV_MODES.POSITION_WORLD,
'View Position': AOV_MODES.POSITION_VIEW,
'Depth': AOV_MODES.DEPTH,
@@ -3027,6 +3036,13 @@ function setupGUI() {
'Coat': AOV_MODES.COAT,
'Transmission': AOV_MODES.TRANSMISSION,
'Emissive': AOV_MODES.EMISSIVE,
'Ambient Occlusion': AOV_MODES.AO,
'Anisotropy': AOV_MODES.ANISOTROPY,
'Sheen': AOV_MODES.SHEEN,
'Iridescence': AOV_MODES.IRIDESCENCE,
'─── Quality Check ───': '',
'Normal Quality Check': AOV_MODES.NORMAL_QUALITY,
'Shader Error Detection': AOV_MODES.SHADER_ERROR,
'─── Utility ───': '',
'Material ID': AOV_MODES.MATERIAL_ID
}).name('AOV Mode').onChange(value => {
@@ -3036,6 +3052,188 @@ function setupGUI() {
});
aovFolder.close();
// Material Override System
const overrideFolder = gui.addFolder('Material Override');
const overrideParams = {
enabled: false,
roughness: null,
metalness: null,
disableNormalMaps: false,
disableAllTextures: false,
preset: 'none',
reset: function() {
window.resetMaterialOverrides(scene);
overrideParams.enabled = false;
overrideParams.roughness = null;
overrideParams.metalness = null;
overrideParams.disableNormalMaps = false;
overrideParams.disableAllTextures = false;
overrideParams.preset = 'none';
gui.controllersRecursive().forEach(c => c.updateDisplay());
}
};
overrideFolder.add(overrideParams, 'enabled').name('Enable Overrides').onChange(value => {
if (!value) {
window.resetMaterialOverrides(scene);
}
});
overrideFolder.add(overrideParams, 'roughness', 0, 1, 0.01).name('Roughness Override').onChange(value => {
if (overrideParams.enabled && value !== null) {
window.applyMaterialOverrides(scene, { roughness: value });
}
});
overrideFolder.add(overrideParams, 'metalness', 0, 1, 0.01).name('Metalness Override').onChange(value => {
if (overrideParams.enabled && value !== null) {
window.applyMaterialOverrides(scene, { metalness: value });
}
});
overrideFolder.add(overrideParams, 'disableNormalMaps').name('Disable Normal Maps').onChange(value => {
if (overrideParams.enabled) {
window.applyMaterialOverrides(scene, { disableNormalMaps: value });
}
});
overrideFolder.add(overrideParams, 'disableAllTextures').name('Disable All Textures').onChange(value => {
if (overrideParams.enabled) {
window.applyMaterialOverrides(scene, { disableAllTextures: value });
}
});
overrideFolder.add(overrideParams, 'preset', {
'None': 'none',
'Base Color Only': 'BASE_COLOR_ONLY',
'Normals Only': 'NORMALS_ONLY',
'Flat Shading': 'FLAT_SHADING',
'Mirror': 'MIRROR',
'Matte': 'MATTE',
'White Clay': 'WHITE_CLAY'
}).name('Apply Preset').onChange(value => {
if (value !== 'none') {
overrideParams.enabled = true;
window.applyOverridePreset(scene, value);
gui.controllersRecursive().forEach(c => c.updateDisplay());
}
});
overrideFolder.add(overrideParams, 'reset').name('Reset All Overrides');
overrideFolder.close();
// Material Validation System
const validationFolder = gui.addFolder('Material Validation');
const validationParams = {
autoValidate: false,
errorCount: 0,
warningCount: 0,
infoCount: 0,
validateNow: function() {
const validator = new window.MaterialValidator();
const results = validator.validateScene(scene);
validationParams.errorCount = results.totalErrors;
validationParams.warningCount = results.totalWarnings;
validationParams.infoCount = results.totalInfo;
console.log(validator.generateReport(results));
validator.logResults(results);
updateStatus(`Validation: ${results.totalErrors} errors, ${results.totalWarnings} warnings`,
results.totalErrors > 0 ? 'error' : 'success');
gui.controllersRecursive().forEach(c => c.updateDisplay());
}
};
validationFolder.add(validationParams, 'validateNow').name('🔍 Validate Scene');
validationFolder.add(validationParams, 'errorCount').name('Errors').listen().disable();
validationFolder.add(validationParams, 'warningCount').name('Warnings').listen().disable();
validationFolder.add(validationParams, 'infoCount').name('Info').listen().disable();
validationFolder.add(validationParams, 'autoValidate').name('Auto-validate on Load');
validationFolder.close();
// Split View Comparison System
const splitViewFolder = gui.addFolder('Split View Compare');
const splitViewParams = {
enabled: false,
mode: 'vertical',
position: 0.5,
secondaryAOV: AOV_MODES.ALBEDO,
enable: function() {
if (!window.splitViewComparison) {
window.splitViewComparison = new window.SplitViewComparison(renderer, scene, camera);
}
window.splitViewComparison.enable();
window.splitViewComparison.setSplitMode(splitViewParams.mode);
window.splitViewComparison.setSplitPosition(splitViewParams.position);
// Apply AOV to secondary scene
if (splitViewParams.secondaryAOV !== AOV_MODES.NONE) {
window.splitViewComparison.secondaryScene.traverse(obj => {
if (obj.isMesh && obj.material) {
const aovMaterial = createAOVMaterial(obj.material, splitViewParams.secondaryAOV);
if (aovMaterial) {
obj.material = aovMaterial;
}
}
});
}
splitViewParams.enabled = true;
updateStatus('Split view comparison enabled', 'success');
},
disable: function() {
if (window.splitViewComparison) {
window.splitViewComparison.disable();
splitViewParams.enabled = false;
updateStatus('Split view comparison disabled', 'success');
}
}
};
splitViewFolder.add(splitViewParams, 'enabled').name('Enable Split View').onChange(value => {
if (value) {
splitViewParams.enable();
} else {
splitViewParams.disable();
}
});
splitViewFolder.add(splitViewParams, 'mode', {
'Vertical (Left/Right)': 'vertical',
'Horizontal (Top/Bottom)': 'horizontal',
'Diagonal': 'diagonal'
}).name('Split Mode').onChange(value => {
if (window.splitViewComparison && splitViewParams.enabled) {
window.splitViewComparison.setSplitMode(value);
}
});
splitViewFolder.add(splitViewParams, 'position', 0, 1, 0.01).name('Split Position').onChange(value => {
if (window.splitViewComparison && splitViewParams.enabled) {
window.splitViewComparison.setSplitPosition(value);
}
});
splitViewFolder.add(splitViewParams, 'secondaryAOV', {
'Material (Original)': AOV_MODES.NONE,
'Albedo': AOV_MODES.ALBEDO,
'Normals (World)': AOV_MODES.NORMALS_WORLD,
'Roughness': AOV_MODES.ROUGHNESS,
'Metalness': AOV_MODES.METALNESS,
'UV Layout': AOV_MODES.UV_LAYOUT
}).name('Secondary View').onChange(value => {
if (window.splitViewComparison && splitViewParams.enabled) {
// Re-enable to apply new AOV
splitViewParams.disable();
splitViewParams.enable();
}
});
splitViewFolder.close();
}
// Load USD file
@@ -3298,6 +3496,18 @@ async function loadMaterials() {
}
console.log(`Successfully loaded ${materials.length} materials`);
// Auto-validate materials if enabled
if (gui) {
const validationController = gui.controllers.find(c => c.property === 'autoValidate');
if (validationController && validationController.object.autoValidate) {
// Run validation automatically
const validator = new MaterialValidator();
const results = validator.validateScene(scene);
console.log('Auto-validation results:');
validator.logResults(results);
}
}
}
// Create Three.js material from OpenPBR/UsdPreviewSurface data
@@ -5250,14 +5460,20 @@ function animate() {
boundingBoxHelper.update();
}
// Use composer if any post-processing is enabled (false color or custom ACES)
const useComposer = showingFalseColor ||
(toneMappingType === 'aces13' || toneMappingType === 'aces20');
if (useComposer && composer) {
composer.render();
// Check if split view comparison is enabled
if (window.splitViewComparison && window.splitViewComparison.getState().active) {
// Split view renders both scenes
window.splitViewComparison.render();
} else {
renderer.render(scene, camera);
// Use composer if any post-processing is enabled (false color or custom ACES)
const useComposer = showingFalseColor ||
(toneMappingType === 'aces13' || toneMappingType === 'aces20');
if (useComposer && composer) {
composer.render();
} else {
renderer.render(scene, camera);
}
}
}
@@ -5275,6 +5491,15 @@ window.loadHDRTextureForMaterial = loadHDRTextureForMaterial;
window.exportSelectedMaterialJSON = exportSelectedMaterialJSON;
window.exportSelectedMaterialMTLX = exportSelectedMaterialMTLX;
// Expose PBR debugging tools to global scope
window.applyMaterialOverrides = applyMaterialOverrides;
window.resetMaterialOverrides = resetMaterialOverrides;
window.applyOverridePreset = applyOverridePreset;
window.MaterialValidator = MaterialValidator;
window.SplitViewComparison = SplitViewComparison;
window.COMPARISON_PRESETS = COMPARISON_PRESETS;
window.OVERRIDE_PRESETS = OVERRIDE_PRESETS;
// Import MaterialX XML file and apply to selected object
async function importMaterialXFile() {
const input = document.createElement('input');