Skip to content

Commit a7b215d

Browse files
committed
updated VRCLightVolumes to 2.0.0-dev.7
1 parent e638f4e commit a7b215d

File tree

1 file changed

+148
-109
lines changed

1 file changed

+148
-109
lines changed

Packages/sh.orels.shaders.generator/Runtime/Sources/Modules/VRCLightVolumes.orlsource

Lines changed: 148 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,9 @@
9797

9898
float4 _UdonPointLightVolumeDirection[128];
9999

100-
float2 _UdonPointLightVolumeCustomID[128];
100+
float3 _UdonPointLightVolumeCustomID[128];
101101

102-
float _UdonAreaLightBrightnessCutoff;
102+
float _UdonLightBrightnessCutoff;
103103

104104
float _UdonLightVolumeOcclusionCount;
105105

@@ -261,19 +261,6 @@
261261
return LV_SAMPLE(_UdonPointLightVolumeTexture, uvid);
262262
}
263263

264-
float LV_ComputeAreaLightSquaredBoundingSphere(float width, float height, float minSolidAngle) {
265-
float A = width * height;
266-
float w2 = width * width;
267-
float h2 = height * height;
268-
float B = 0.25 * (w2 + h2);
269-
float t = tan(0.25 * minSolidAngle);
270-
float T = t * t;
271-
float TB = T * B;
272-
float discriminant = sqrt(TB * TB + 4.0 * T * A * A);
273-
float d2 = (discriminant - TB) * 0.125 / T;
274-
return d2;
275-
}
276-
277264
float4 LV_ProjectQuadLightIrradianceSH(float3 shadingPosition, float3 lightVertices[4]) {
278265
// Transform the vertices into local space centered on the shading position,
279266
// project, the polygon onto the unit sphere.
@@ -355,27 +342,20 @@
355342
return float4(l1x, l1y, l1z, l0);
356343
}
357344

358-
void LV_QuadLight(float3 worldPos, float3 centroidPos, float4 rotationQuat, float2 size, float3 color, float occlusion, inout float3 L0, inout float3 L1r, inout float3 L1g, inout float3 L1b, inout uint count) {
345+
void LV_QuadLight(float3 worldPos, float3 centroidPos, float4 rotationQuat, float2 size, float3 color, float sqMaxDist, float occlusion, inout float3 L0, inout float3 L1r, inout float3 L1g, inout float3 L1b, inout uint count) {
359346

360-
float2 halfSize = size * 0.5f;
361347
float3 lightToWorldPos = worldPos - centroidPos;
362348

363-
// Get normal to cull the light early
349+
// Normal culling
364350
float3 normal = LV_MultiplyVectorByQuaternion(float3(0, 0, 1), rotationQuat);
365351
[branch] if (dot(normal, lightToWorldPos) < 0.0) return;
366-
367-
// Calculate the bounding sphere of the area light given the cutoff irradiance
368-
// The irradiance of an emitter at a point is assuming normal incidence is irradiance over radiance.
369-
float minSolidAngle = min(abs(_UdonAreaLightBrightnessCutoff * rcp(max(color.r, max(color.g, color.b)))), LV_PI2);
370-
371-
float sqMaxDist = LV_ComputeAreaLightSquaredBoundingSphere(size.x, size.y, minSolidAngle);
372-
float sqCutoffDist = sqMaxDist - dot(lightToWorldPos, lightToWorldPos);
373-
[branch] if (sqCutoffDist < 0) return;
374352

375353
// Attenuate the light based on distance to the bounding sphere, so we don't get hard seam at the edge.
376-
color.rgb *= saturate(sqCutoffDist / sqMaxDist);
377-
354+
float sqCutoffDist = sqMaxDist - dot(lightToWorldPos, lightToWorldPos);
355+
color.rgb *= saturate(sqCutoffDist / sqMaxDist) * LV_PI;
356+
378357
// Compute the vertices of the quad
358+
float2 halfSize = size * 0.5f;
379359
float3 xAxis = LV_MultiplyVectorByQuaternion(float3(1, 0, 0), rotationQuat);
380360
float3 yAxis = cross(normal, xAxis);
381361
float3 verts[4];
@@ -392,16 +372,6 @@
392372
float lenL1 = length(areaLightSH.xyz);
393373
if (lenL1 > areaLightSH.w)
394374
areaLightSH.xyz *= areaLightSH.w / lenL1;
395-
396-
// Accumulate SH coefficients
397-
//float3 l0 = areaLightSH.w * color.rgb * occlusion;
398-
//float3 l1 = areaLightSH.xyz * occlusion;
399-
//float3 stp = step(l0, 0);
400-
401-
//L0 = lerp(L0 + l0, L0 * saturate(1 + l0), stp);
402-
//L1r = lerp(L1r + l1 * color.r, L1r * saturate(1 + l0), stp);
403-
//L1g = lerp(L1g + l1 * color.g, L1g * saturate(1 + l0), stp);
404-
//L1b = lerp(L1b + l1 * color.b, L1b * saturate(1 + l0), stp);
405375

406376
L0 += areaLightSH.w * color.rgb * occlusion;
407377
L1r += areaLightSH.xyz * color.r * occlusion;
@@ -411,111 +381,186 @@
411381
count++;
412382
}
413383

414-
void LV_PointLight(uint id, float3 worldPos, float occlusion, inout float3 L0, inout float3 L1r, inout float3 L1g, inout float3 L1b, inout uint count) {
415-
416-
// Light position and inversed squared range
417-
float4 pos = _UdonPointLightVolumePosition[id];
418-
float invSqRange = abs(pos.w); // Sign of range defines if it's point light (positive) or a spot light (negative)
384+
float3 LV_PointLightAttenuation(float sqdist, float sqlightSize, float3 color, float brightnessCutoff, float sqMaxDist) {
385+
float mask = saturate(1 - sqdist / sqMaxDist);
386+
return mask * mask * color * sqlightSize / (sqdist + sqlightSize);
387+
}
388+
389+
float LV_PointLightSolidAngle(float sqdist, float sqlightSize) {
390+
return saturate(sqrt(sqdist / (sqlightSize + sqdist)));
391+
}
392+
393+
void LV_SphereLight(float3 worldPos, float3 centerPos, float sqlightSize, float3 color, float occlusion, float sqMaxDist, inout float3 L0, inout float3 L1r, inout float3 L1g, inout float3 L1b, inout uint count) {
419394

420-
float3 dir = pos.xyz - worldPos;
421-
float sqlen = max(dot(dir, dir), 1e-6);
422-
float invSqLen = rcp(sqlen);
395+
float3 dir = centerPos - worldPos;
396+
float sqdist = max(dot(dir, dir), 1e-6);
397+
float3 att = LV_PointLightAttenuation(sqdist, sqlightSize, color, _UdonLightBrightnessCutoff, sqMaxDist);
423398

424-
float4 color = _UdonPointLightVolumeColor[id]; // Color, angle
399+
float3 l0 = att * occlusion;
400+
float3 l1 = normalize(dir) * LV_PointLightSolidAngle(sqdist, sqlightSize);
401+
402+
L0 += l0;
403+
L1r += l0.r * l1;
404+
L1g += l0.g * l1;
405+
L1b += l0.b * l1;
406+
count++;
407+
408+
}
409+
410+
void LV_SphereSpotLight(float3 worldPos, float3 centerPos, float sqlightSize, float3 color, float3 lightDir, float cosAngle, float coneFalloff, float occlusion, float sqMaxDist, inout float3 L0, inout float3 L1r, inout float3 L1g, inout float3 L1b, inout uint count) {
411+
412+
float3 dir = centerPos - worldPos;
413+
float sqdist = max(dot(dir, dir), 1e-6);
414+
float3 dirN = normalize(dir);
415+
416+
float spotMask = dot(lightDir, -dirN) - cosAngle;
417+
if (spotMask < 0) return; // Culling by spot angle
425418

426-
bool isSpotLight = pos.w < 0;
427-
bool isPointLight = !isSpotLight && color.w <= 1.5f;
419+
float3 att = LV_PointLightAttenuation(sqdist, sqlightSize, color, _UdonLightBrightnessCutoff, sqMaxDist);
420+
421+
float smoothedCone = LV_Smoothstep01(saturate(spotMask * coneFalloff));
422+
float3 l0 = att * occlusion * smoothedCone;
423+
float3 l1 = dirN * LV_PointLightSolidAngle(sqdist, sqlightSize * saturate(1 - cosAngle));
424+
425+
L0 += l0;
426+
L1r += l0.r * l1;
427+
L1g += l0.g * l1;
428+
L1b += l0.b * l1;
429+
count++;
430+
431+
}
432+
433+
void LV_SphereSpotLightCookie(float3 worldPos, float3 centerPos, float sqlightSize, float3 color, float4 lightRot, float tanAngle, uint customId, float occlusion, float sqMaxDist, inout float3 L0, inout float3 L1r, inout float3 L1g, inout float3 L1b, inout uint count) {
428434

429-
// Culling spotlight by radius
430-
if ((isSpotLight || isPointLight) && invSqLen < invSqRange ) return;
435+
float3 dir = centerPos - worldPos;
436+
float sqdist = max(dot(dir, dir), 1e-6);
437+
float3 dirN = normalize(dir);
431438

432-
float angle = color.w;
433-
float4 ldir = _UdonPointLightVolumeDirection[id]; // Dir + falloff or Rotation
434-
float coneFalloff = ldir.w;
435-
int customId = (int) _UdonPointLightVolumeCustomID[id].x; // Custom Texture ID
439+
float3 localDir = LV_MultiplyVectorByQuaternion(-dirN, lightRot);
440+
if (localDir.z <= 0.0) return; // Culling by direction
436441

437-
float3 dirN = dir * rsqrt(sqlen);
438-
float dirRadius = sqlen * invSqRange;
442+
float2 uv = localDir.xy * rcp(localDir.z * tanAngle);
443+
if (abs(uv.x) > 1.0 || abs(uv.y) > 1.0) return; // Culling by UV
439444

440-
float3 att = color.rgb; // Light attenuation
445+
float3 att = LV_PointLightAttenuation(sqdist, sqlightSize, color, _UdonLightBrightnessCutoff, sqMaxDist);
446+
447+
uint id = (uint) _UdonPointLightVolumeCubeCount * 5 - customId - 1;
448+
float3 uvid = float3(uv * 0.5 + 0.5, id);
449+
float angleSize = saturate(rsqrt(1 + tanAngle * tanAngle));
450+
float4 cookie = LV_SAMPLE(_UdonPointLightVolumeTexture, uvid);
451+
452+
float3 l0 = att * occlusion * cookie.rgb * cookie.a;
453+
float3 l1 = dirN * LV_PointLightSolidAngle(sqdist, sqlightSize * (1 - angleSize));
454+
455+
L0 += l0;
456+
L1r += l0.r * l1;
457+
L1g += l0.g * l1;
458+
L1b += l0.b * l1;
459+
count++;
460+
461+
}
462+
463+
void LV_PointLight(uint id, float3 worldPos, float4 occlusion, inout float3 L0, inout float3 L1r, inout float3 L1g, inout float3 L1b, inout uint count) {
464+
465+
// IDs and range data
466+
float3 customID_data = _UdonPointLightVolumeCustomID[id];
467+
int shadowId = (int) customID_data.y; // Shadowmask id
468+
int customId = (int) customID_data.x; // Custom Texture ID
469+
float sqrRange = customID_data.z; // Squared culling distance
470+
471+
float4 pos = _UdonPointLightVolumePosition[id]; // Light position and inversed squared range
472+
float3 dir = pos.xyz - worldPos;
473+
float sqlen = max(dot(dir, dir), 1e-6);
474+
[branch] // Early distance based culling
475+
if (sqlen > sqrRange) return;
476+
477+
// Processing lights occlusion
478+
float lightOcclusion = 1;
479+
[branch]
480+
if (_UdonLightVolumeOcclusionCount != 0 && shadowId >= 0) {
481+
lightOcclusion = dot(1, float4(shadowId == 0, shadowId == 1, shadowId == 2, shadowId == 3) * occlusion);
482+
}
483+
484+
float4 color = _UdonPointLightVolumeColor[id]; // Color, angle
441485

442-
if (isSpotLight) { // It is a spot light
486+
if (pos.w < 0) { // It is a spot light
487+
488+
float angle = color.w;
489+
float4 ldir = _UdonPointLightVolumeDirection[id]; // Dir + falloff or Rotation
443490

444491
if (customId > 0) { // If it uses Attenuation LUT
445492

493+
float invSqRange = abs(pos.w); // Sign of range defines if it's point light (positive) or a spot light (negative)
494+
float3 dirN = dir * rsqrt(sqlen);
495+
float dirRadius = sqlen * invSqRange;
446496
float spotMask = dot(ldir.xyz, -dirN) - angle;
447-
if(spotMask < 0) return;
497+
if(spotMask < 0) return; // Spot cone based culling
448498
float spot = 1 - saturate(spotMask * rcp(1 - angle));
449499
uint id = (uint) _UdonPointLightVolumeCubeCount * 5 + customId - 1;
450500
float3 uvid = float3(sqrt(float2(spot, dirRadius)), id);
451-
att *= LV_SAMPLE(_UdonPointLightVolumeTexture, uvid).xyz;
501+
float3 att = color.rgb * LV_SAMPLE(_UdonPointLightVolumeTexture, uvid).xyz;
502+
503+
L0 += att * lightOcclusion;
504+
L1r += dirN * att.r * lightOcclusion;
505+
L1g += dirN * att.g * lightOcclusion;
506+
L1b += dirN * att.b * lightOcclusion;
507+
508+
count++;
452509

453510
} else if (customId < 0) { // If uses cookie
454511

455-
float3 localDir = LV_MultiplyVectorByQuaternion(-dirN, ldir);
456-
if (localDir.z <= 0.0) return;
457-
float2 uv = localDir.xy * rcp(localDir.z * angle); // Here angle is tan(angle)
458-
if (abs(uv.x) > 1.0 || abs(uv.y) > 1.0) return;
459-
uint id = (uint) _UdonPointLightVolumeCubeCount * 5 - customId - 1;
460-
float3 uvid = float3(uv * 0.5 + 0.5, id);
461-
att *= saturate((1 - dirRadius) * rcp(dirRadius * 60 + 1.732f)) * LV_SAMPLE(_UdonPointLightVolumeTexture, uvid).xyz;
512+
LV_SphereSpotLightCookie(worldPos, pos.xyz, -pos.w, color.rgb, ldir, angle, customId, lightOcclusion, sqrRange, L0, L1r, L1g, L1b, count);
462513

463514
} else { // If it uses default parametric attenuation
464515

465-
float spotMask = dot(ldir.xyz, -dirN) - angle;
466-
if(spotMask < 0) return;
467-
att *= saturate((1 - dirRadius) * rcp(dirRadius * 60 + 1.732f)) * LV_Smoothstep01(saturate(spotMask * coneFalloff));
516+
LV_SphereSpotLight(worldPos, pos.xyz, -pos.w, color.rgb, ldir.xyz, angle, ldir.w, lightOcclusion, sqrRange, L0, L1r, L1g, L1b, count);
468517

469518
}
470519

471-
} else if (isPointLight) { // It is a point light
520+
} else if (color.w <= 1.5f) { // It is a point light
472521

473522
if (customId < 0) { // If it uses a cubemap
474523

524+
float4 ldir = _UdonPointLightVolumeDirection[id]; // Dir + falloff or Rotation
525+
float3 dirN = dir * rsqrt(sqlen);
475526
uint id = -customId - 1; // Cubemap ID starts from zero and should not take in count texture array slices count.
476-
att *= saturate((1 - dirRadius) * rcp(dirRadius * 60 + 1.732f)) * LV_SampleCubemapArray(id, LV_MultiplyVectorByQuaternion(dirN, ldir)).xyz;
477-
527+
float3 cubeColor = LV_SampleCubemapArray(id, LV_MultiplyVectorByQuaternion(dirN, ldir)).xyz;
528+
float3 l0 = 0, l1r = 0, l1g = 0, l1b = 0;
529+
LV_SphereLight(worldPos, pos.xyz, pos.w, color.rgb, lightOcclusion, sqrRange, l0, l1r, l1g, l1b, count);
530+
L0 += l0 * cubeColor;
531+
L1r += l1r * cubeColor.r;
532+
L1g += l1g * cubeColor.g;
533+
L1b += l1b * cubeColor.b;
534+
478535
} else if (customId > 0) { // Using LUT
479536

537+
float invSqRange = abs(pos.w); // Sign of range defines if it's point light (positive) or a spot light (negative)
538+
float3 dirN = dir * rsqrt(sqlen);
539+
float dirRadius = sqlen * invSqRange;
480540
uint id = (uint) _UdonPointLightVolumeCubeCount * 5 + customId;
481541
float3 uvid = float3(sqrt(float2(0, dirRadius)), id);
482-
att *= LV_SAMPLE(_UdonPointLightVolumeTexture, uvid).xyz;
542+
float3 att = color.rgb * LV_SAMPLE(_UdonPointLightVolumeTexture, uvid).xyz;
543+
544+
L0 += att * lightOcclusion;
545+
L1r += dirN * att.r * lightOcclusion;
546+
L1g += dirN * att.g * lightOcclusion;
547+
L1b += dirN * att.b * lightOcclusion;
548+
549+
count++;
483550

484551
} else { // If it uses default parametric attenuation
485552

486-
att *= saturate((1 - dirRadius) * rcp(dirRadius * 60 + 1.732f));
553+
LV_SphereLight(worldPos, pos.xyz, pos.w, color.rgb, lightOcclusion, sqrRange, L0, L1r, L1g, L1b, count);
487554

488555
}
489556

490557
} else { // It is an area light
491-
492-
// Area light is defined by centroid, rotation and size
493-
float3 centroidPos = pos.xyz;
494-
float4 rotationQuat = ldir;
495-
float2 size = float2(pos.w, color.w - 2.0f);
496558

497-
LV_QuadLight(worldPos, centroidPos, rotationQuat, size, color.rgb, occlusion, L0, L1r, L1g, L1b, count);
498-
return;
559+
float4 ldir = _UdonPointLightVolumeDirection[id]; // Dir + falloff or Rotation
560+
LV_QuadLight(worldPos, pos.xyz, ldir, float2(pos.w, color.w - 2.0f), color.rgb, sqrRange, lightOcclusion, L0, L1r, L1g, L1b, count);
499561

500562
}
501563

502-
// Accumulate SH coefficients
503-
//float3 l0 = att * occlusion;
504-
//float3 l1 = dirN * occlusion;
505-
//float3 stp = step(l0, 0);
506-
507-
//L0 = lerp(L0 + l0, L0 * saturate(1 + l0), stp);
508-
//L1r = lerp(L1r + l1 * att.r, L1r * saturate(1 + l0), stp);
509-
//L1g = lerp(L1g + l1 * att.g, L1g * saturate(1 + l0), stp);
510-
//L1b = lerp(L1b + l1 * att.b, L1b * saturate(1 + l0), stp);
511-
512-
L0 += att * occlusion;
513-
L1r += dirN * att.r * occlusion;
514-
L1g += dirN * att.g * occlusion;
515-
L1b += dirN * att.b * occlusion;
516-
517-
count++;
518-
519564
}
520565

521566
void LV_SampleLightVolumeTex(float3 uvw0, float3 uvw1, float3 uvw2, out float3 L0, out float3 L1r, out float3 L1g, out float3 L1b) {
@@ -648,13 +693,7 @@
648693

649694
[loop]
650695
for (uint pid = 0; pid < pointCount && pcount < maxOverdraw; pid++) {
651-
float lightOcclusion = 1;
652-
float shadowId = _UdonPointLightVolumeCustomID[pid].y;
653-
[branch]
654-
if (_UdonLightVolumeOcclusionCount != 0 && shadowId >= 0) {
655-
lightOcclusion = dot(1, float4(shadowId == 0, shadowId == 1, shadowId == 2, shadowId == 3) * occlusion);
656-
}
657-
LV_PointLight(pid, worldPos, lightOcclusion, L0, L1r, L1g, L1b, pcount);
696+
LV_PointLight(pid, worldPos, occlusion, L0, L1r, L1g, L1b, pcount);
658697
}
659698

660699
}
@@ -878,7 +917,7 @@
878917
float3 a = coloredSpecs + specs * L0;
879918
float3 b = coloredSpecs * 3;
880919

881-
return max(lerp(a, b, smoothness), 0.0);
920+
return max(lerp(a, b, smoothness) * 0.5f, 0.0);
882921

883922
}
884923

@@ -898,7 +937,7 @@
898937

899938
float spec = LV_DistributionGGX(nh, roughExp);
900939

901-
return max(spec * L0 * f0, 0.0) * 3;
940+
return max(spec * L0 * f0, 0.0) * 1.5f;
902941

903942
}
904943

0 commit comments

Comments
 (0)