Skip to content

Commit 730d833

Browse files
authored
Merge pull request #51 from microsoft/feature/light-visibility
feat: add hidden property to light options and update light handling in plot
2 parents 63a68eb + 0aa3140 commit 730d833

4 files changed

Lines changed: 219 additions & 158 deletions

File tree

core/src/light.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export type LightType = typeof LightType[keyof typeof LightType];
2020

2121
export interface ILightOptions {
2222
color?: Vector3; // RGB
23+
hidden?: boolean; // Hidden from primary rays (default true)
2324
}
2425

2526
export interface IRectLightOptions extends ILightOptions {
@@ -97,9 +98,12 @@ export interface IProjectorLightOptions extends ILightOptions {
9798
export abstract class Light {
9899
protected _color: Vector3; // RGB
99100
public get color(): Vector3 { return this._color; }
101+
protected _hidden: boolean;
102+
public get hidden(): boolean { return this._hidden; }
100103

101104
constructor(options: ILightOptions) {
102105
this._color = options.color || [1, 1, 1]; // Default to white
106+
this._hidden = options.hidden != undefined ? options.hidden : true;
103107
}
104108

105109
protected _directionToRotation(direction: Vector3, rotationQuaternion: Quaternion) {
@@ -121,6 +125,7 @@ export abstract class Light {
121125

122126
public toBuffer(buffer: LightBufferData, index: number) {
123127
buffer.setColor(index, this._color);
128+
buffer.setHidden(index, this._hidden ? 1 : 0);
124129
}
125130
}
126131

@@ -437,9 +442,17 @@ export class ProjectorLight extends Light {
437442
// | F32 | F32 | F32 | F32 |
438443
// | 00000000 | 00000000 | 00000000 | 00000000 | 00000000 | 00000000 | 00000000 | 00000000 | 00000000 | 00000000 | 00000000 | 00000000 | 00000000 | 00000000 | 00000000 | 00000000 |
439444
// ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
445+
//
446+
// ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
447+
// | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 |
448+
// | hidden | | | | | | | | | | | | |
449+
// | | | | | | | | | | | | | |
450+
// | F32 | | | | | | | | | | | | |
451+
// | 00000000 | 00000000 | 00000000 | 00000000 | 00000000 | 00000000 | 00000000 | 00000000 | 00000000 | 00000000 | 00000000 | 00000000 | 00000000 | 00000000 | 00000000 | 00000000 |
452+
// ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
440453

441454
export class LightBufferData extends Float32Array {
442-
public static readonly SIZE = 144 / 4;
455+
public static readonly SIZE = 160 / 4;
443456

444457
public readonly ROTATION_OFFSET = 0 / 4;
445458
public readonly CENTER_OFFSET = 16 / 4;
@@ -455,6 +468,7 @@ export class LightBufferData extends Float32Array {
455468
public readonly TEXTURE_COORDS_OFFSET = 96 / 4;
456469
public readonly TEXTURE_OFFSET_OFFSET = 112 / 4;
457470
public readonly TEXTURE_SCALE_OFFSET = 128 / 4;
471+
public readonly HIDDEN_OFFSET = 144 / 4;
458472

459473
constructor(count: number) {
460474
super(count * LightBufferData.SIZE);
@@ -618,4 +632,11 @@ export class LightBufferData extends Float32Array {
618632
this[offset + 3] = value[3];
619633
}
620634

635+
public getHidden(index: number) {
636+
return this[LightBufferData.SIZE * index + this.HIDDEN_OFFSET];
637+
}
638+
public setHidden(index: number, value: number) {
639+
this[LightBufferData.SIZE * index + this.HIDDEN_OFFSET] = value;
640+
}
641+
621642
}

renderers/webgpuraytrace/src/shaders/pathtrace.ts

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,12 @@ struct Light { // -------------------------
104104
color2: vec3<f32>, // 80* 16 12
105105
nearPlane: f32, // 92 4 4
106106
texCoords: vec4<f32>, // 96* 16 16
107-
texOffset: vec4<f32>, // 112* 16 12
108-
texScale: vec4<f32>, // 128* 16 12
107+
texOffset: vec4<f32>, // 112* 16 16
108+
texScale: vec4<f32>, // 128* 16 16
109+
hidden: f32, // 144* 16 4
110+
_padding: f32, // 148 4 12
109111
} // -------------------------
110-
// 16 144
112+
// 16 160
111113
112114
// offset align size
113115
struct Hittable { // -------------------------
@@ -1214,11 +1216,15 @@ fn hitYzRect(id: u32, ray: Ray, tMin: f32, tMax: f32, hitRecord: ptr<function, H
12141216
return true;
12151217
}
12161218
1217-
fn hitLights(ray: Ray, hitRecord: ptr<function, HitRecord>, seed: ptr<function, u32>) -> vec3<f32> {
1219+
fn hitLights(ray: Ray, hitRecord: ptr<function, HitRecord>, seed: ptr<function, u32>, depth: u32) -> vec3<f32> {
12181220
var hit: bool;
12191221
var color = vec3<f32>(0f, 0f, 0f);
12201222
for (var i: u32 = 0u; i < arrayLength(&lightBuffer.lights); i++) {
12211223
let light = lightBuffer.lights[i];
1224+
1225+
// Skip hidden lights on primary rays
1226+
if (depth == 0u && light.hidden > 0f) { continue; }
1227+
12221228
let lightTypeId = u32(light.typeId);
12231229
12241230
// TODO: Split into separate direct and indirect functions
@@ -1237,7 +1243,10 @@ fn hitLights(ray: Ray, hitRecord: ptr<function, HitRecord>, seed: ptr<function,
12371243
if (hitSpotLight(i, ray, &color, hitRecord, seed)) { hit = true; }
12381244
}
12391245
1240-
// Indirect lighting
1246+
// Indirect lighting (area lights)
1247+
case 1u: {
1248+
if (hitDiskLight(i, ray, &color, hitRecord)) { hit = true; }
1249+
}
12411250
case 5u: {
12421251
if (hitRectLight(i, ray, &color, hitRecord)) { hit = true; }
12431252
}
@@ -1253,6 +1262,8 @@ fn hitLights(ray: Ray, hitRecord: ptr<function, HitRecord>, seed: ptr<function,
12531262
return max(uniforms.ambientColor, color);
12541263
}
12551264
1265+
// No light hit: background for primary rays, ambient for bounced rays
1266+
if (depth == 0u) { return uniforms.backgroundColor.xyz; }
12561267
return uniforms.ambientColor;
12571268
}
12581269
@@ -1275,6 +1286,25 @@ fn hitRectLight(id: u32, ray: Ray, color: ptr<function, vec3<f32>>, hitRecord: p
12751286
return true;
12761287
}
12771288
1289+
fn hitDiskLight(id: u32, ray: Ray, color: ptr<function, vec3<f32>>, hitRecord: ptr<function, HitRecord>) -> bool {
1290+
let disk = &lightBuffer.lights[id];
1291+
let center = (*disk).center;
1292+
let rotation = (*disk).rotation;
1293+
let invRotation = conjugate(rotation);
1294+
var rotatedRay: Ray;
1295+
rotatedRay.origin = rotateQuat(ray.origin - center, invRotation) + center;
1296+
rotatedRay.direction = rotateQuat(ray.direction, invRotation);
1297+
if (dot(rotatedRay.direction, vec3<f32>(0f, 0f, 1f)) > 0f) { return false; } // Front face only
1298+
let oc = rotatedRay.origin - center;
1299+
let t = -oc.z / rotatedRay.direction.z;
1300+
if (t < 0f) { return false; }
1301+
let p = oc + t * rotatedRay.direction;
1302+
let radius = (*disk).size.x * 0.5f;
1303+
if (dot(p.xy, p.xy) > radius * radius) { return false; }
1304+
*color += (*disk).color;
1305+
return true;
1306+
}
1307+
12781308
fn hitSphereLight(id: u32, ray: Ray, color: ptr<function, vec3<f32>>, hitRecord: ptr<function, HitRecord>) -> bool {
12791309
let sphere = &lightBuffer.lights[id];
12801310
let radius = (*sphere).size.x * 0.5f;
@@ -1632,14 +1662,11 @@ fn rayColor(ray: ptr<function, Ray>, seed: ptr<function, u32>) -> vec3<f32> {
16321662
}
16331663
else {
16341664
// Miss
1635-
if (depth > 0u) {
1636-
// Bounced ray: sample direct lights
1637-
return hitLights(*ray, &hitRecord, seed) * color;
1638-
}
1639-
else {
1640-
// Primary ray: background only (lights hidden on first bounce)
1641-
return uniforms.backgroundColor.xyz;
1665+
let lightColor = hitLights(*ray, &hitRecord, seed, depth);
1666+
if (depth == 0u) {
1667+
return lightColor;
16421668
}
1669+
return lightColor * color;
16431670
}
16441671
}
16451672
}

spec/src/light.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import { Plot } from "./plot.js";
77

88
export class Light {
99
public name: string;
10+
public visible: boolean;
11+
public hidden: boolean;
1012

1113
public type: string;
1214
public position: Core.Vector3;
@@ -37,6 +39,8 @@ export class Light {
3739
const light = new Light();
3840
light.type = json.type;
3941
light.name = json.name;
42+
light.visible = json.visible != undefined ? json.visible : true;
43+
light.hidden = json.hidden != undefined ? json.hidden : true;
4044

4145
// Group for signal parsing
4246
const group = plot.root;

0 commit comments

Comments
 (0)