Implement per-model color tinting and opacity support where the final color is multiplied by vec4(tint * opacity, opacity). This allows for runtime color modification and transparency effects at the model instance level.
fragColor = vec4(color, baseColorSample.a);- PBR Pipeline: Full physically-based rendering with metallic/roughness workflow
- Multi-light Support: Up to 8 lights with shadow mapping
- Material Textures: BaseColor, Normal, MetallicRoughness, Occlusion, Emissive
- Global Illumination: Hemispheric ambient lighting
- Tone Mapping: Reinhard + gamma correction
// Fragment shader additions (around line 366)
uniform vec3 u_ModelTint; // RGB tint color [0-1]
uniform float u_ModelOpacity; // Opacity multiplier [0-1]// Replace line 636:
// fragColor = vec4(color, baseColorSample.a);
// With tinted and opacity-modified output:
vec3 tintedColor = color * u_ModelTint * u_ModelOpacity;
float finalAlpha = baseColorSample.a * u_ModelOpacity;
fragColor = vec4(tintedColor, finalAlpha);// In types.ts - extend InstanceData
export interface InstanceData {
// ... existing fields
tintColor: [number, number, number]; // RGB tint [0-1]
opacity: number; // Opacity [0-1]
}
// Extend IModel interface
export interface IModel {
// ... existing methods
setTintColor(r: number, g: number, b: number): void;
setOpacity(opacity: number): void;
getTintColor(): [number, number, number];
getOpacity(): number;
}// In GPUResourceManager.ts
export class GPUResourceManager {
// Add method to set tint uniforms
setModelTint(shader: WebGLProgram, tint: [number, number, number], opacity: number): void {
const tintLoc = this.gl.getUniformLocation(shader, 'u_ModelTint');
const opacityLoc = this.gl.getUniformLocation(shader, 'u_ModelOpacity');
if (tintLoc) this.gl.uniform3fv(tintLoc, tint);
if (opacityLoc) this.gl.uniform1f(opacityLoc, opacity);
}
}// In Model.ts
export class Model implements IModel {
// Add getters/setters
get tintColor(): [number, number, number] {
return this._instanceData.tintColor;
}
set tintColor(color: [number, number, number]) {
this._instanceData.tintColor = [
Math.max(0, Math.min(1, color[0])),
Math.max(0, Math.min(1, color[1])),
Math.max(0, Math.min(1, color[2]))
];
this._manager.markInstanceDirty(this.instanceId.id);
}
get opacity(): number {
return this._instanceData.opacity;
}
set opacity(opacity: number) {
this._instanceData.opacity = Math.max(0, Math.min(1, opacity));
this._manager.markInstanceDirty(this.instanceId.id);
}
// Public methods
public setTintColor(r: number, g: number, b: number): void {
this.tintColor = [r, g, b];
}
public setOpacity(opacity: number): void {
this.opacity = opacity;
}
public getTintColor(): [number, number, number] {
return [...this.tintColor];
}
public getOpacity(): number {
return this.opacity;
}
}// In InstanceManager.ts - createInstance method
const instanceData: InstanceData = {
// ... existing fields
tintColor: [1.0, 1.0, 1.0], // Default: no tint (white)
opacity: 1.0 // Default: fully opaque
};// In renderModelInstances method, before drawing
this.gpuResources.setModelTint(shader, instance.tintColor, instance.opacity);// In InstanceManager.ts render method
// Check if any instances have opacity < 1.0
const hasTransparency = Array.from(this.instances.values())
.some(instance => instance.opacity < 1.0);
if (hasTransparency) {
// Enable alpha blending for transparent objects
this.gl.enable(this.gl.BLEND);
this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA);
// Optional: Disable depth writing for transparent objects
// this.gl.depthMask(false);
} else {
this.gl.disable(this.gl.BLEND);
this.gl.depthMask(true);
}// Optional: Sort transparent instances back-to-front
// This would require extending the rendering pipeline
// For now, render in instance order (simpler implementation)// Set red tint at 50% intensity
model.setTintColor(1.0, 0.5, 0.5);
// Set 75% opacity (25% transparent)
model.setOpacity(0.75);
// Property-style access
model.tintColor = [0.2, 0.8, 1.0]; // Light blue tint
model.opacity = 0.5; // 50% transparent// Fade in effect
let opacity = 0.0;
const fadeIn = setInterval(() => {
opacity += 0.02;
model.setOpacity(opacity);
if (opacity >= 1.0) {
clearInterval(fadeIn);
}
}, 16); // 60 FPS
// Damage effect (flash red)
const originalTint = model.getTintColor();
model.setTintColor(1.0, 0.3, 0.3); // Red tint
setTimeout(() => {
model.setTintColor(...originalTint); // Restore
}, 200);// Track if tint uniforms are dirty per instance
interface InstanceData {
// ... existing fields
tintDirty: boolean; // Flag for uniform updates
}- Instances with same tint/opacity can be batched together
- Sort by tint values to minimize uniform changes
- Transparent objects should be rendered after opaque ones
- Tint:
[1.0, 1.0, 1.0](white/no tint) - Opacity:
1.0(fully opaque) - No visual changes for existing models without explicit tint/opacity changes
- Existing models automatically get default values
- No breaking changes to existing API
// Test tint color clamping
model.setTintColor(-0.5, 1.5, 0.5);
expect(model.getTintColor()).toEqual([0.0, 1.0, 0.5]);
// Test opacity clamping
model.setOpacity(1.5);
expect(model.getOpacity()).toBe(1.0);- Solid color models with various tints
- Transparency gradients (opacity 0.0 to 1.0)
- Animated tint changes
- Multiple transparent models with depth
- Per-material tinting: Different tint for diffuse vs emissive
- HDR tinting: Tint values > 1.0 for brightness boost
- Gradient tinting: Vertex-based color variation
- Temporal tinting: Built-in fade/pulse animations
- Conditional compilation based on tint usage
- Pack tint and opacity into single vec4 uniform
- Instance data textures for large model counts
- Shader modifications (uniforms + output)
- TypeScript interfaces (types + GPUResourceManager)
- Model class (properties + methods)
- Instance manager (defaults + rendering integration)
- Transparency support (alpha blending)
- Testing and validation
- Implementation: 4-6 hours
- Testing: 2-3 hours
- Documentation: 1 hour
- Total: 7-10 hours
This plan provides a solid foundation for model-level color tinting and opacity with clean API design and efficient implementation.