@ifc-lite/renderer 1.26.0 → 1.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/environment.d.ts +76 -0
- package/dist/environment.d.ts.map +1 -0
- package/dist/environment.js +135 -0
- package/dist/environment.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +59 -0
- package/dist/index.js.map +1 -1
- package/dist/pipeline.d.ts +12 -0
- package/dist/pipeline.d.ts.map +1 -1
- package/dist/pipeline.js +45 -2
- package/dist/pipeline.js.map +1 -1
- package/dist/shaders/main.wgsl.d.ts +1 -1
- package/dist/shaders/main.wgsl.d.ts.map +1 -1
- package/dist/shaders/main.wgsl.js +32 -12
- package/dist/shaders/main.wgsl.js.map +1 -1
- package/dist/shaders/sky.wgsl.d.ts +12 -0
- package/dist/shaders/sky.wgsl.d.ts.map +1 -0
- package/dist/shaders/sky.wgsl.js +110 -0
- package/dist/shaders/sky.wgsl.js.map +1 -0
- package/dist/sky-pass.d.ts +49 -0
- package/dist/sky-pass.d.ts.map +1 -0
- package/dist/sky-pass.js +106 -0
- package/dist/sky-pass.js.map +1 -0
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -3
|
@@ -3,5 +3,5 @@
|
|
|
3
3
|
* Features: PBR lighting, section plane clipping, selection highlight,
|
|
4
4
|
* glass fresnel, ACES tone mapping, screen-space edge enhancement.
|
|
5
5
|
*/
|
|
6
|
-
export declare const mainShaderSource = "\n struct Uniforms {\n viewProj: mat4x4<f32>,\n model: mat4x4<f32>,\n baseColor: vec4<f32>,\n metallicRoughness: vec2<f32>, // x = metallic, y = roughness\n _padding1: vec2<f32>,\n sectionPlane: vec4<f32>, // xyz = plane normal, w = plane distance\n flags: vec4<u32>, // x = isSelected, y = sectionEnabled, z = edgeEnabled, w = edgeIntensityMilli\n }\n @binding(0) @group(0) var<uniform> uniforms: Uniforms;\n\n struct VertexInput {\n @location(0) position: vec3<f32>,\n @location(1) normal: vec3<f32>,\n @location(2) entityId: u32,\n }\n\n struct VertexOutput {\n @builtin(position) position: vec4<f32>,\n @location(0) worldPos: vec3<f32>,\n @location(1) normal: vec3<f32>,\n @location(2) @interpolate(flat) entityId: u32,\n @location(3) viewPos: vec3<f32>, // For edge detection\n }\n\n @vertex\n fn vs_main(input: VertexInput, @builtin(instance_index) instanceIndex: u32) -> VertexOutput {\n var output: VertexOutput;\n let worldPos = uniforms.model * vec4<f32>(input.position, 1.0);\n output.position = uniforms.viewProj * worldPos;\n // Anti z-fighting: deterministic depth nudge per entity.\n // Knuth multiplicative hash spreads sequential IDs across 0-255\n // so coplanar faces from different entities always get distinct depths.\n // At 1e-6 per step the max world-space offset is <3mm at 10m \u2014 invisible.\n let zHash = (input.entityId * 2654435761u) & 255u;\n output.position.z *= 1.0 + f32(zHash) * 1e-6;\n output.worldPos = worldPos.xyz;\n output.normal = normalize((uniforms.model * vec4<f32>(input.normal, 0.0)).xyz);\n output.entityId = input.entityId;\n // Store view-space position for edge detection\n output.viewPos = (uniforms.viewProj * worldPos).xyz;\n return output;\n }\n\n // PBR helper functions\n fn fresnelSchlick(cosTheta: f32, F0: vec3<f32>) -> vec3<f32> {\n return F0 + (1.0 - F0) * pow(clamp(1.0 - cosTheta, 0.0, 1.0), 5.0);\n }\n\n fn distributionGGX(NdotH: f32, roughness: f32) -> f32 {\n let a = roughness * roughness;\n let a2 = a * a;\n let NdotH2 = NdotH * NdotH;\n let num = a2;\n let denomBase = (NdotH2 * (a2 - 1.0) + 1.0);\n let denom = 3.14159265 * denomBase * denomBase;\n return num / max(denom, 0.0000001);\n }\n\n fn geometrySchlickGGX(NdotV: f32, roughness: f32) -> f32 {\n let r = (roughness + 1.0);\n let k = (r * r) / 8.0;\n let num = NdotV;\n let denom = NdotV * (1.0 - k) + k;\n return num / max(denom, 0.0000001);\n }\n\n fn geometrySmith(NdotV: f32, NdotL: f32, roughness: f32) -> f32 {\n let ggx2 = geometrySchlickGGX(NdotV, roughness);\n let ggx1 = geometrySchlickGGX(NdotL, roughness);\n return ggx1 * ggx2;\n }\n\n fn encodeId24(id: u32) -> vec4<f32> {\n let r = f32((id >> 16u) & 255u) / 255.0;\n let g = f32((id >> 8u) & 255u) / 255.0;\n let b = f32(id & 255u) / 255.0;\n return vec4<f32>(r, g, b, 1.0);\n }\n\n struct FragmentOutput {\n @location(0) color: vec4<f32>,\n @location(1) objectIdEncoded: vec4<f32>,\n }\n\n @fragment\n fn fs_main(input: VertexOutput) -> FragmentOutput {\n // Section plane clipping - discard fragments ABOVE the plane.\n // flags.y packs two bits: bit 0 = enabled, bit 1 = flipped.\n let sectionEnabled = (uniforms.flags.y & 1u) == 1u;\n if (sectionEnabled) {\n let planeNormal = uniforms.sectionPlane.xyz;\n let planeDistance = uniforms.sectionPlane.w;\n let flipped = (uniforms.flags.y & 2u) == 2u;\n let side = select(1.0, -1.0, flipped);\n let distToPlane = (dot(input.worldPos, planeNormal) - planeDistance) * side;\n if (distToPlane > 0.0) {\n discard;\n }\n }\n\n // Compute normal via derivative-based flat shading.\n //\n // Industry-standard solution for BIM/CAD viewers \u2014 what\n // Three.js (material.flatShading = true), Autodesk Forge,\n // Speckle, and xeokit all do for opaque surfaces. Rationale:\n //\n // * BIM geometry is overwhelmingly flat surfaces (walls,\n // slabs, roofs, beams), and CSG operations (opening\n // subtraction, layer slicing) emit those surfaces as\n // dense strips of coplanar triangles. Per-vertex normal\n // averaging gives a SLIGHTLY-different normal at each\n // vertex due to f32 noise from boolean output; the\n // boundary between strips then reads as a visible darker/\n // brighter scar line \u2014 the horizontal striations on\n // walls, stripes on roofs, visible triangulation reports\n // across every CSG kernel we have tried (legacy BSP,\n // Manifold).\n // * cross(dpdx, dpdy) of world position evaluates to the\n // EXACT face normal in the fragment shader. Every\n // fragment on a flat face \u2014 across an arbitrarily-fine\n // triangulation \u2014 gets the IDENTICAL normal, so coplanar\n // splits become invisible by construction. No CPU-side\n // welding, smooth-grouping, or coplanar-face merging\n // fixes the symptom as cleanly.\n //\n // Trade-off: genuinely curved surfaces (cylinder tessellations,\n // BSpline approximations) shade with visible facets. For BIM\n // that's acceptable \u2014 curved surfaces are < 5 % of typical\n // model triangle count and the faceting matches CAD-tool\n // (Revit, ArchiCAD) on-screen behaviour at default quality.\n //\n // We still fall back to the vertex normal when derivatives\n // are unavailable (extreme polygon degeneracy where dpdx /\n // dpdy collapse to zero \u2014 practically never on real geometry).\n let faceN = cross(dpdx(input.worldPos), dpdy(input.worldPos));\n let fLen2 = dot(faceN, faceN);\n var N: vec3<f32>;\n if (fLen2 > 1e-10) {\n N = faceN * inverseSqrt(fLen2);\n } else {\n // Degenerate derivative \u2014 fall back to the vertex normal\n // if it's populated, else +Y.\n N = input.normal;\n let nLen2 = dot(N, N);\n if (nLen2 > 1e-6) {\n N = N * inverseSqrt(nLen2);\n } else {\n N = vec3<f32>(0.0, 1.0, 0.0);\n }\n }\n\n // Stabilize the SIGN of the derivative face normal with the vertex\n // normal. The screen-space cross product gives the exact face\n // normal DIRECTION for coplanar strips (the scar-line fix), but at\n // grazing angles its SIGN becomes numerically unstable per quad \u2014\n // hemisphere/rim lighting then band-flips across large regions of\n // flat walls/slabs (diagonal lighter/darker bands). The interpolated\n // vertex normal is quad-noise-free, so use it only to orient N.\n // Guard: skip when the vertex normal is missing or nearly\n // perpendicular to the face normal (unreliable witness).\n let vN = input.normal;\n let alignDot = dot(N, vN);\n if (alignDot * alignDot > 0.03 * dot(vN, vN)) {\n N = N * sign(alignDot);\n }\n\n // Enhanced lighting with multiple sources\n let sunLight = normalize(vec3<f32>(0.5, 1.0, 0.3)); // Main directional light\n let fillLight = normalize(vec3<f32>(-0.5, 0.3, -0.3)); // Fill light\n let rimLight = normalize(vec3<f32>(0.0, 0.2, -1.0)); // Rim light for edge definition\n\n // Hemisphere ambient - reduced for less washed-out look\n let skyColor = vec3<f32>(0.3, 0.35, 0.4); // Darker sky\n let groundColor = vec3<f32>(0.15, 0.1, 0.08); // Darker ground\n let hemisphereFactor = N.y * 0.5 + 0.5;\n let ambient = mix(groundColor, skyColor, hemisphereFactor) * 0.25;\n\n // Two-sided sun light so inner faces (I-beam channels) stay visible\n let NdotL = abs(dot(N, sunLight));\n let wrap = 0.3;\n let diffuseSun = max((NdotL + wrap) / (1.0 + wrap), 0.0) * 0.55;\n\n // Fill light - two-sided\n let NdotFill = abs(dot(N, fillLight));\n let diffuseFill = NdotFill * 0.15;\n\n // Rim light for edge definition\n let NdotRim = max(dot(N, rimLight), 0.0);\n let rim = pow(NdotRim, 4.0) * 0.15;\n\n var baseColor = uniforms.baseColor.rgb;\n\n // Detect if the color is close to white/gray (low saturation)\n let baseGray = dot(baseColor, vec3<f32>(0.299, 0.587, 0.114));\n let baseSaturation = length(baseColor - vec3<f32>(baseGray)) / max(baseGray, 0.001);\n let isWhiteish = 1.0 - smoothstep(0.0, 0.3, baseSaturation);\n\n // Darken whites/grays more to reduce washed-out appearance\n baseColor = mix(baseColor, baseColor * 0.7, isWhiteish * 0.4);\n\n // Combine all lighting\n var color = baseColor * (ambient + diffuseSun + diffuseFill + rim);\n\n // flags.x is a bitfield:\n // bit 0 (value 1) = isSelected \u2192 selection-highlight + force opaque\n // bit 1 (value 2) = isOverlay \u2192 color-override pass; preserve\n // baseColor.a (overlay pipeline has\n // src-alpha blending) AND skip the\n // glass-fresnel branch so low-alpha\n // ghost tints don't pick up the\n // near-white reflection tint meant\n // for real glass materials.\n let isSelected = (uniforms.flags.x & 1u) == 1u;\n let isOverlay = (uniforms.flags.x & 2u) == 2u;\n\n // Selection highlight \u2014 a single FLAT selection colour.\n //\n // No lighting-dependent (luminance) or view-dependent (fresnel)\n // term: the selected object must read as one uniform colour, not\n // a shaded/gradient surface. The previous fresnel-glow mix left\n // 80 % of the lit object colour visible at face centres (the\n // green-site / red-roof bleed-through). A constant colour here\n // also stays flat through the downstream tone-mapping, since\n // those are per-pixel functions of a now-constant input.\n if (isSelected) {\n color = vec3<f32>(0.3, 0.6, 1.0);\n }\n\n // Beautiful fresnel effect for transparent materials (glass)\n // Skip when selected \u2014 the glass shine and desaturation wash out the\n // blue highlight, making it appear white instead of blue.\n // Also force alpha to 1.0 for selected objects so the highlight is\n // fully opaque (the selection pipeline has no alpha blending).\n var finalAlpha = select(uniforms.baseColor.a, 1.0, isSelected);\n if (finalAlpha < 0.99 && !isSelected && !isOverlay) {\n // Calculate view direction for fresnel\n let V = normalize(-input.worldPos);\n let NdotV = max(dot(N, V), 0.0);\n\n // Enhanced fresnel effect - stronger at edges (grazing angles)\n // Using Schlick's approximation for realistic glass reflection\n let fresnelPower = 1.5; // Higher = softer edge reflections\n let fresnel = pow(1.0 - NdotV, fresnelPower);\n\n // Glass reflection tint (sky/environment reflection at edges)\n let reflectionTint = vec3<f32>(0.92, 0.96, 1.0); // Cool sky reflection\n let reflectionStrength = fresnel * 0.6; // Strong edge reflections\n\n // Mix in reflection tint at edges\n color = mix(color, color * reflectionTint, reflectionStrength);\n\n // Add realistic glass shine - brighter at edges where light reflects\n let glassShine = fresnel * 0.12;\n color += glassShine;\n\n // Slight desaturation at edges (glass reflects environment, not just color)\n let edgeDesaturation = fresnel * 0.25;\n let gray = dot(color, vec3<f32>(0.299, 0.587, 0.114));\n color = mix(color, vec3<f32>(gray), edgeDesaturation);\n\n // Make glass more transparent (reduce opacity by 30%)\n finalAlpha = finalAlpha * 0.7;\n }\n\n // Exposure adjustment - darken overall\n color *= 0.85;\n\n // Contrast enhancement\n color = (color - 0.5) * 1.15 + 0.5;\n color = max(color, vec3<f32>(0.0));\n\n // Saturation boost - stronger for colored surfaces, less for whites\n let gray = dot(color, vec3<f32>(0.299, 0.587, 0.114));\n let satBoost = mix(1.4, 1.1, isWhiteish); // More saturation for colored surfaces\n color = mix(vec3<f32>(gray), color, satBoost);\n\n // ACES filmic tone mapping\n let a = 2.51;\n let b = 0.03;\n let c = 2.43;\n let d = 0.59;\n let e = 0.14;\n color = clamp((color * (a * color + b)) / (color * (c * color + d) + e), vec3<f32>(0.0), vec3<f32>(1.0));\n\n // Subtle edge enhancement using screen-space derivatives.\n //\n // Use the SHADED normal (face normal from dpdx/dpdy above)\n // for the normal-gradient term, not the interpolated vertex\n // normal \u2014 otherwise we get spurious dark stripes on flat\n // surfaces whose vertex normals carry numerical noise from\n // CSG output (the visible scar-line symptom would just\n // resurface here even after the lit-normal fix). With the\n // face normal, coplanar adjacent triangles agree exactly \u2192\n // zero normal gradient \u2192 no false edge; only the genuine\n // creases between perpendicular faces produce a real\n // gradient and get the intended outline.\n let depthGradient = length(vec2<f32>(\n dpdx(input.viewPos.z),\n dpdy(input.viewPos.z)\n ));\n let normalGradient = length(vec2<f32>(\n length(dpdx(N)),\n length(dpdy(N))\n ));\n\n if (uniforms.flags.z == 1u) {\n // Threshold filters subtle normal discontinuities at internal\n // triangle edges between coplanar entities in the same batch.\n let edgeFactor = smoothstep(0.02, 0.12, depthGradient * 10.0 + normalGradient * 5.0);\n let edgeIntensity = f32(uniforms.flags.w) / 1000.0;\n let edgeDarkenStrength = clamp(0.25 * edgeIntensity, 0.0, 0.85);\n let edgeDarken = mix(1.0, 1.0 - edgeDarkenStrength, edgeFactor);\n color *= edgeDarken;\n }\n\n // Gamma correction\n color = pow(color, vec3<f32>(1.0 / 2.2));\n\n var out: FragmentOutput;\n out.color = vec4<f32>(color, finalAlpha);\n out.objectIdEncoded = encodeId24(input.entityId);\n return out;\n }\n ";
|
|
6
|
+
export declare const mainShaderSource = "\n struct Uniforms {\n viewProj: mat4x4<f32>,\n model: mat4x4<f32>,\n baseColor: vec4<f32>,\n metallicRoughness: vec2<f32>, // x = metallic, y = roughness\n _padding1: vec2<f32>,\n sectionPlane: vec4<f32>, // xyz = plane normal, w = plane distance\n flags: vec4<u32>, // x = isSelected, y = sectionEnabled, z = edgeEnabled, w = edgeIntensityMilli\n }\n @binding(0) @group(0) var<uniform> uniforms: Uniforms;\n\n // Global lighting environment \u2014 one buffer shared by every mesh in\n // the pass (bound once per frame at group(1)). Field packing must\n // match packEnvironmentUniforms() in environment.ts.\n struct Environment {\n sunDirection: vec3<f32>, // unit vector TOWARD the sun\n sunIntensity: f32,\n sunColor: vec3<f32>,\n ambientIntensity: f32,\n skyColor: vec3<f32>, // hemisphere-ambient sky tint\n exposure: f32,\n groundColor: vec3<f32>, // hemisphere-ambient ground tint\n fillIntensity: f32,\n rimIntensity: f32,\n _pad0: f32,\n _pad1: f32,\n _pad2: f32,\n }\n @binding(0) @group(1) var<uniform> env: Environment;\n\n struct VertexInput {\n @location(0) position: vec3<f32>,\n @location(1) normal: vec3<f32>,\n @location(2) entityId: u32,\n }\n\n struct VertexOutput {\n @builtin(position) position: vec4<f32>,\n @location(0) worldPos: vec3<f32>,\n @location(1) normal: vec3<f32>,\n @location(2) @interpolate(flat) entityId: u32,\n @location(3) viewPos: vec3<f32>, // For edge detection\n }\n\n @vertex\n fn vs_main(input: VertexInput, @builtin(instance_index) instanceIndex: u32) -> VertexOutput {\n var output: VertexOutput;\n let worldPos = uniforms.model * vec4<f32>(input.position, 1.0);\n output.position = uniforms.viewProj * worldPos;\n // Anti z-fighting: deterministic depth nudge per entity.\n // Knuth multiplicative hash spreads sequential IDs across 0-255\n // so coplanar faces from different entities always get distinct depths.\n // At 1e-6 per step the max world-space offset is <3mm at 10m \u2014 invisible.\n let zHash = (input.entityId * 2654435761u) & 255u;\n output.position.z *= 1.0 + f32(zHash) * 1e-6;\n output.worldPos = worldPos.xyz;\n output.normal = normalize((uniforms.model * vec4<f32>(input.normal, 0.0)).xyz);\n output.entityId = input.entityId;\n // Store view-space position for edge detection\n output.viewPos = (uniforms.viewProj * worldPos).xyz;\n return output;\n }\n\n // PBR helper functions\n fn fresnelSchlick(cosTheta: f32, F0: vec3<f32>) -> vec3<f32> {\n return F0 + (1.0 - F0) * pow(clamp(1.0 - cosTheta, 0.0, 1.0), 5.0);\n }\n\n fn distributionGGX(NdotH: f32, roughness: f32) -> f32 {\n let a = roughness * roughness;\n let a2 = a * a;\n let NdotH2 = NdotH * NdotH;\n let num = a2;\n let denomBase = (NdotH2 * (a2 - 1.0) + 1.0);\n let denom = 3.14159265 * denomBase * denomBase;\n return num / max(denom, 0.0000001);\n }\n\n fn geometrySchlickGGX(NdotV: f32, roughness: f32) -> f32 {\n let r = (roughness + 1.0);\n let k = (r * r) / 8.0;\n let num = NdotV;\n let denom = NdotV * (1.0 - k) + k;\n return num / max(denom, 0.0000001);\n }\n\n fn geometrySmith(NdotV: f32, NdotL: f32, roughness: f32) -> f32 {\n let ggx2 = geometrySchlickGGX(NdotV, roughness);\n let ggx1 = geometrySchlickGGX(NdotL, roughness);\n return ggx1 * ggx2;\n }\n\n fn encodeId24(id: u32) -> vec4<f32> {\n let r = f32((id >> 16u) & 255u) / 255.0;\n let g = f32((id >> 8u) & 255u) / 255.0;\n let b = f32(id & 255u) / 255.0;\n return vec4<f32>(r, g, b, 1.0);\n }\n\n struct FragmentOutput {\n @location(0) color: vec4<f32>,\n @location(1) objectIdEncoded: vec4<f32>,\n }\n\n @fragment\n fn fs_main(input: VertexOutput) -> FragmentOutput {\n // Section plane clipping - discard fragments ABOVE the plane.\n // flags.y packs two bits: bit 0 = enabled, bit 1 = flipped.\n let sectionEnabled = (uniforms.flags.y & 1u) == 1u;\n if (sectionEnabled) {\n let planeNormal = uniforms.sectionPlane.xyz;\n let planeDistance = uniforms.sectionPlane.w;\n let flipped = (uniforms.flags.y & 2u) == 2u;\n let side = select(1.0, -1.0, flipped);\n let distToPlane = (dot(input.worldPos, planeNormal) - planeDistance) * side;\n if (distToPlane > 0.0) {\n discard;\n }\n }\n\n // Compute normal via derivative-based flat shading.\n //\n // Industry-standard solution for BIM/CAD viewers \u2014 what\n // Three.js (material.flatShading = true), Autodesk Forge,\n // Speckle, and xeokit all do for opaque surfaces. Rationale:\n //\n // * BIM geometry is overwhelmingly flat surfaces (walls,\n // slabs, roofs, beams), and CSG operations (opening\n // subtraction, layer slicing) emit those surfaces as\n // dense strips of coplanar triangles. Per-vertex normal\n // averaging gives a SLIGHTLY-different normal at each\n // vertex due to f32 noise from boolean output; the\n // boundary between strips then reads as a visible darker/\n // brighter scar line \u2014 the horizontal striations on\n // walls, stripes on roofs, visible triangulation reports\n // across every CSG kernel we have tried (legacy BSP,\n // Manifold).\n // * cross(dpdx, dpdy) of world position evaluates to the\n // EXACT face normal in the fragment shader. Every\n // fragment on a flat face \u2014 across an arbitrarily-fine\n // triangulation \u2014 gets the IDENTICAL normal, so coplanar\n // splits become invisible by construction. No CPU-side\n // welding, smooth-grouping, or coplanar-face merging\n // fixes the symptom as cleanly.\n //\n // Trade-off: genuinely curved surfaces (cylinder tessellations,\n // BSpline approximations) shade with visible facets. For BIM\n // that's acceptable \u2014 curved surfaces are < 5 % of typical\n // model triangle count and the faceting matches CAD-tool\n // (Revit, ArchiCAD) on-screen behaviour at default quality.\n //\n // We still fall back to the vertex normal when derivatives\n // are unavailable (extreme polygon degeneracy where dpdx /\n // dpdy collapse to zero \u2014 practically never on real geometry).\n let faceN = cross(dpdx(input.worldPos), dpdy(input.worldPos));\n let fLen2 = dot(faceN, faceN);\n var N: vec3<f32>;\n if (fLen2 > 1e-10) {\n N = faceN * inverseSqrt(fLen2);\n } else {\n // Degenerate derivative \u2014 fall back to the vertex normal\n // if it's populated, else +Y.\n N = input.normal;\n let nLen2 = dot(N, N);\n if (nLen2 > 1e-6) {\n N = N * inverseSqrt(nLen2);\n } else {\n N = vec3<f32>(0.0, 1.0, 0.0);\n }\n }\n\n // Stabilize the SIGN of the derivative face normal with the vertex\n // normal. The screen-space cross product gives the exact face\n // normal DIRECTION for coplanar strips (the scar-line fix), but at\n // grazing angles its SIGN becomes numerically unstable per quad \u2014\n // hemisphere/rim lighting then band-flips across large regions of\n // flat walls/slabs (diagonal lighter/darker bands). The interpolated\n // vertex normal is quad-noise-free, so use it only to orient N.\n // Guard: skip when the vertex normal is missing or nearly\n // perpendicular to the face normal (unreliable witness).\n let vN = input.normal;\n let alignDot = dot(N, vN);\n if (alignDot * alignDot > 0.03 * dot(vN, vN)) {\n N = N * sign(alignDot);\n }\n\n // Lighting environment \u2014 sun/hemisphere/exposure come from the\n // global env uniform (defaults reproduce the historic hardcoded\n // values); fill + rim directions stay fixed in view-agnostic\n // world space as stylistic shaping lights.\n let sunLight = env.sunDirection;\n let fillLight = normalize(vec3<f32>(-0.5, 0.3, -0.3)); // Fill light\n let rimLight = normalize(vec3<f32>(0.0, 0.2, -1.0)); // Rim light for edge definition\n\n // Hemisphere ambient\n let hemisphereFactor = N.y * 0.5 + 0.5;\n let ambient = mix(env.groundColor, env.skyColor, hemisphereFactor) * env.ambientIntensity;\n\n // Two-sided sun light so inner faces (I-beam channels) stay visible\n let NdotL = abs(dot(N, sunLight));\n let wrap = 0.3;\n let diffuseSun = max((NdotL + wrap) / (1.0 + wrap), 0.0) * env.sunIntensity;\n\n // Fill light - two-sided\n let NdotFill = abs(dot(N, fillLight));\n let diffuseFill = NdotFill * env.fillIntensity;\n\n // Rim light for edge definition\n let NdotRim = max(dot(N, rimLight), 0.0);\n let rim = pow(NdotRim, 4.0) * env.rimIntensity;\n\n var baseColor = uniforms.baseColor.rgb;\n\n // Detect if the color is close to white/gray (low saturation)\n let baseGray = dot(baseColor, vec3<f32>(0.299, 0.587, 0.114));\n let baseSaturation = length(baseColor - vec3<f32>(baseGray)) / max(baseGray, 0.001);\n let isWhiteish = 1.0 - smoothstep(0.0, 0.3, baseSaturation);\n\n // Darken whites/grays more to reduce washed-out appearance\n baseColor = mix(baseColor, baseColor * 0.7, isWhiteish * 0.4);\n\n // Combine all lighting\n var color = baseColor * (ambient + env.sunColor * diffuseSun + vec3<f32>(diffuseFill + rim));\n\n // flags.x is a bitfield:\n // bit 0 (value 1) = isSelected \u2192 selection-highlight + force opaque\n // bit 1 (value 2) = isOverlay \u2192 color-override pass; preserve\n // baseColor.a (overlay pipeline has\n // src-alpha blending) AND skip the\n // glass-fresnel branch so low-alpha\n // ghost tints don't pick up the\n // near-white reflection tint meant\n // for real glass materials.\n let isSelected = (uniforms.flags.x & 1u) == 1u;\n let isOverlay = (uniforms.flags.x & 2u) == 2u;\n\n // Selection highlight \u2014 a single FLAT selection colour.\n //\n // No lighting-dependent (luminance) or view-dependent (fresnel)\n // term: the selected object must read as one uniform colour, not\n // a shaded/gradient surface. The previous fresnel-glow mix left\n // 80 % of the lit object colour visible at face centres (the\n // green-site / red-roof bleed-through). A constant colour here\n // also stays flat through the downstream tone-mapping, since\n // those are per-pixel functions of a now-constant input.\n if (isSelected) {\n color = vec3<f32>(0.3, 0.6, 1.0);\n }\n\n // Beautiful fresnel effect for transparent materials (glass)\n // Skip when selected \u2014 the glass shine and desaturation wash out the\n // blue highlight, making it appear white instead of blue.\n // Also force alpha to 1.0 for selected objects so the highlight is\n // fully opaque (the selection pipeline has no alpha blending).\n var finalAlpha = select(uniforms.baseColor.a, 1.0, isSelected);\n if (finalAlpha < 0.99 && !isSelected && !isOverlay) {\n // Calculate view direction for fresnel\n let V = normalize(-input.worldPos);\n let NdotV = max(dot(N, V), 0.0);\n\n // Enhanced fresnel effect - stronger at edges (grazing angles)\n // Using Schlick's approximation for realistic glass reflection\n let fresnelPower = 1.5; // Higher = softer edge reflections\n let fresnel = pow(1.0 - NdotV, fresnelPower);\n\n // Glass reflection tint (sky/environment reflection at edges)\n let reflectionTint = vec3<f32>(0.92, 0.96, 1.0); // Cool sky reflection\n let reflectionStrength = fresnel * 0.6; // Strong edge reflections\n\n // Mix in reflection tint at edges\n color = mix(color, color * reflectionTint, reflectionStrength);\n\n // Add realistic glass shine - brighter at edges where light reflects\n let glassShine = fresnel * 0.12;\n color += glassShine;\n\n // Slight desaturation at edges (glass reflects environment, not just color)\n let edgeDesaturation = fresnel * 0.25;\n let gray = dot(color, vec3<f32>(0.299, 0.587, 0.114));\n color = mix(color, vec3<f32>(gray), edgeDesaturation);\n\n // Make glass more transparent (reduce opacity by 30%)\n finalAlpha = finalAlpha * 0.7;\n }\n\n // Exposure adjustment (historic default 0.85 darkens overall)\n color *= env.exposure;\n\n // Contrast enhancement\n color = (color - 0.5) * 1.15 + 0.5;\n color = max(color, vec3<f32>(0.0));\n\n // Saturation boost - stronger for colored surfaces, less for whites\n let gray = dot(color, vec3<f32>(0.299, 0.587, 0.114));\n let satBoost = mix(1.4, 1.1, isWhiteish); // More saturation for colored surfaces\n color = mix(vec3<f32>(gray), color, satBoost);\n\n // ACES filmic tone mapping\n let a = 2.51;\n let b = 0.03;\n let c = 2.43;\n let d = 0.59;\n let e = 0.14;\n color = clamp((color * (a * color + b)) / (color * (c * color + d) + e), vec3<f32>(0.0), vec3<f32>(1.0));\n\n // Subtle edge enhancement using screen-space derivatives.\n //\n // Use the SHADED normal (face normal from dpdx/dpdy above)\n // for the normal-gradient term, not the interpolated vertex\n // normal \u2014 otherwise we get spurious dark stripes on flat\n // surfaces whose vertex normals carry numerical noise from\n // CSG output (the visible scar-line symptom would just\n // resurface here even after the lit-normal fix). With the\n // face normal, coplanar adjacent triangles agree exactly \u2192\n // zero normal gradient \u2192 no false edge; only the genuine\n // creases between perpendicular faces produce a real\n // gradient and get the intended outline.\n let depthGradient = length(vec2<f32>(\n dpdx(input.viewPos.z),\n dpdy(input.viewPos.z)\n ));\n let normalGradient = length(vec2<f32>(\n length(dpdx(N)),\n length(dpdy(N))\n ));\n\n if (uniforms.flags.z == 1u) {\n // Threshold filters subtle normal discontinuities at internal\n // triangle edges between coplanar entities in the same batch.\n let edgeFactor = smoothstep(0.02, 0.12, depthGradient * 10.0 + normalGradient * 5.0);\n let edgeIntensity = f32(uniforms.flags.w) / 1000.0;\n let edgeDarkenStrength = clamp(0.25 * edgeIntensity, 0.0, 0.85);\n let edgeDarken = mix(1.0, 1.0 - edgeDarkenStrength, edgeFactor);\n color *= edgeDarken;\n }\n\n // Gamma correction\n color = pow(color, vec3<f32>(1.0 / 2.2));\n\n var out: FragmentOutput;\n out.color = vec4<f32>(color, finalAlpha);\n out.objectIdEncoded = encodeId24(input.entityId);\n return out;\n }\n ";
|
|
7
7
|
//# sourceMappingURL=main.wgsl.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.wgsl.d.ts","sourceRoot":"","sources":["../../src/shaders/main.wgsl.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,
|
|
1
|
+
{"version":3,"file":"main.wgsl.d.ts","sourceRoot":"","sources":["../../src/shaders/main.wgsl.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,wzgBAyVtB,CAAC"}
|
|
@@ -18,6 +18,25 @@ export const mainShaderSource = `
|
|
|
18
18
|
}
|
|
19
19
|
@binding(0) @group(0) var<uniform> uniforms: Uniforms;
|
|
20
20
|
|
|
21
|
+
// Global lighting environment — one buffer shared by every mesh in
|
|
22
|
+
// the pass (bound once per frame at group(1)). Field packing must
|
|
23
|
+
// match packEnvironmentUniforms() in environment.ts.
|
|
24
|
+
struct Environment {
|
|
25
|
+
sunDirection: vec3<f32>, // unit vector TOWARD the sun
|
|
26
|
+
sunIntensity: f32,
|
|
27
|
+
sunColor: vec3<f32>,
|
|
28
|
+
ambientIntensity: f32,
|
|
29
|
+
skyColor: vec3<f32>, // hemisphere-ambient sky tint
|
|
30
|
+
exposure: f32,
|
|
31
|
+
groundColor: vec3<f32>, // hemisphere-ambient ground tint
|
|
32
|
+
fillIntensity: f32,
|
|
33
|
+
rimIntensity: f32,
|
|
34
|
+
_pad0: f32,
|
|
35
|
+
_pad1: f32,
|
|
36
|
+
_pad2: f32,
|
|
37
|
+
}
|
|
38
|
+
@binding(0) @group(1) var<uniform> env: Environment;
|
|
39
|
+
|
|
21
40
|
struct VertexInput {
|
|
22
41
|
@location(0) position: vec3<f32>,
|
|
23
42
|
@location(1) normal: vec3<f32>,
|
|
@@ -174,29 +193,30 @@ export const mainShaderSource = `
|
|
|
174
193
|
N = N * sign(alignDot);
|
|
175
194
|
}
|
|
176
195
|
|
|
177
|
-
//
|
|
178
|
-
|
|
196
|
+
// Lighting environment — sun/hemisphere/exposure come from the
|
|
197
|
+
// global env uniform (defaults reproduce the historic hardcoded
|
|
198
|
+
// values); fill + rim directions stay fixed in view-agnostic
|
|
199
|
+
// world space as stylistic shaping lights.
|
|
200
|
+
let sunLight = env.sunDirection;
|
|
179
201
|
let fillLight = normalize(vec3<f32>(-0.5, 0.3, -0.3)); // Fill light
|
|
180
202
|
let rimLight = normalize(vec3<f32>(0.0, 0.2, -1.0)); // Rim light for edge definition
|
|
181
203
|
|
|
182
|
-
// Hemisphere ambient
|
|
183
|
-
let skyColor = vec3<f32>(0.3, 0.35, 0.4); // Darker sky
|
|
184
|
-
let groundColor = vec3<f32>(0.15, 0.1, 0.08); // Darker ground
|
|
204
|
+
// Hemisphere ambient
|
|
185
205
|
let hemisphereFactor = N.y * 0.5 + 0.5;
|
|
186
|
-
let ambient = mix(groundColor, skyColor, hemisphereFactor) *
|
|
206
|
+
let ambient = mix(env.groundColor, env.skyColor, hemisphereFactor) * env.ambientIntensity;
|
|
187
207
|
|
|
188
208
|
// Two-sided sun light so inner faces (I-beam channels) stay visible
|
|
189
209
|
let NdotL = abs(dot(N, sunLight));
|
|
190
210
|
let wrap = 0.3;
|
|
191
|
-
let diffuseSun = max((NdotL + wrap) / (1.0 + wrap), 0.0) *
|
|
211
|
+
let diffuseSun = max((NdotL + wrap) / (1.0 + wrap), 0.0) * env.sunIntensity;
|
|
192
212
|
|
|
193
213
|
// Fill light - two-sided
|
|
194
214
|
let NdotFill = abs(dot(N, fillLight));
|
|
195
|
-
let diffuseFill = NdotFill *
|
|
215
|
+
let diffuseFill = NdotFill * env.fillIntensity;
|
|
196
216
|
|
|
197
217
|
// Rim light for edge definition
|
|
198
218
|
let NdotRim = max(dot(N, rimLight), 0.0);
|
|
199
|
-
let rim = pow(NdotRim, 4.0) *
|
|
219
|
+
let rim = pow(NdotRim, 4.0) * env.rimIntensity;
|
|
200
220
|
|
|
201
221
|
var baseColor = uniforms.baseColor.rgb;
|
|
202
222
|
|
|
@@ -209,7 +229,7 @@ export const mainShaderSource = `
|
|
|
209
229
|
baseColor = mix(baseColor, baseColor * 0.7, isWhiteish * 0.4);
|
|
210
230
|
|
|
211
231
|
// Combine all lighting
|
|
212
|
-
var color = baseColor * (ambient + diffuseSun + diffuseFill + rim);
|
|
232
|
+
var color = baseColor * (ambient + env.sunColor * diffuseSun + vec3<f32>(diffuseFill + rim));
|
|
213
233
|
|
|
214
234
|
// flags.x is a bitfield:
|
|
215
235
|
// bit 0 (value 1) = isSelected → selection-highlight + force opaque
|
|
@@ -272,8 +292,8 @@ export const mainShaderSource = `
|
|
|
272
292
|
finalAlpha = finalAlpha * 0.7;
|
|
273
293
|
}
|
|
274
294
|
|
|
275
|
-
// Exposure adjustment
|
|
276
|
-
color *=
|
|
295
|
+
// Exposure adjustment (historic default 0.85 darkens overall)
|
|
296
|
+
color *= env.exposure;
|
|
277
297
|
|
|
278
298
|
// Contrast enhancement
|
|
279
299
|
color = (color - 0.5) * 1.15 + 0.5;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.wgsl.js","sourceRoot":"","sources":["../../src/shaders/main.wgsl.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG
|
|
1
|
+
{"version":3,"file":"main.wgsl.js","sourceRoot":"","sources":["../../src/shaders/main.wgsl.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAyVzB,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Procedural sky background — a single fullscreen triangle drawn at the far
|
|
3
|
+
* plane (reverse-Z: z = 0) before any geometry. Analytic zenith/horizon/
|
|
4
|
+
* ground gradient plus a sun disc + glow, driven by the same sun direction
|
|
5
|
+
* the geometry lighting uses.
|
|
6
|
+
*
|
|
7
|
+
* Tonemapping note: the geometry shader applies ACES + gamma at the end of
|
|
8
|
+
* its fragment stage (not in a post pass), so the sky must apply the same
|
|
9
|
+
* curve here or it would read as a different "film stock" than the model.
|
|
10
|
+
*/
|
|
11
|
+
export declare const skyShaderSource = "\n struct SkyUniforms {\n camRight: vec3<f32>,\n tanHalfFovX: f32,\n camUp: vec3<f32>,\n tanHalfFovY: f32,\n camForward: vec3<f32>,\n exposure: f32,\n sunDirection: vec3<f32>,\n sunIntensity: f32,\n zenithColor: vec3<f32>,\n _pad0: f32,\n horizonColor: vec3<f32>,\n _pad1: f32,\n groundColor: vec3<f32>,\n _pad2: f32,\n }\n @binding(0) @group(0) var<uniform> sky: SkyUniforms;\n\n struct VertexOutput {\n @builtin(position) position: vec4<f32>,\n @location(0) ndc: vec2<f32>,\n }\n\n @vertex\n fn vs_main(@builtin(vertex_index) vi: u32) -> VertexOutput {\n // Fullscreen triangle: (-1,-1), (3,-1), (-1,3).\n let x = f32((vi << 1u) & 2u) * 2.0 - 1.0;\n let y = f32(vi & 2u) * 2.0 - 1.0;\n var out: VertexOutput;\n // z = 0 is the far plane under reverse-Z, so any geometry drawn\n // later wins the depth test without the sky writing depth.\n out.position = vec4<f32>(x, y, 0.0, 1.0);\n out.ndc = vec2<f32>(x, y);\n return out;\n }\n\n // Same ACES filmic curve as the geometry fragment shader.\n fn acesTonemap(c: vec3<f32>) -> vec3<f32> {\n let a = 2.51;\n let b = 0.03;\n let cc = 2.43;\n let d = 0.59;\n let e = 0.14;\n return clamp((c * (a * c + b)) / (c * (cc * c + d) + e), vec3<f32>(0.0), vec3<f32>(1.0));\n }\n\n struct FragmentOutput {\n @location(0) color: vec4<f32>,\n // Matches the pass's object-id attachment clear value so the sky\n // never registers as a pickable entity.\n @location(1) objectIdEncoded: vec4<f32>,\n }\n\n @fragment\n fn fs_main(input: VertexOutput) -> FragmentOutput {\n // Per-pixel view ray from the camera basis. Used for orthographic\n // cameras too \u2014 parallel rays would collapse the sky to a single\n // colour, so a perspective-style fan reads better in both modes.\n let dir = normalize(\n sky.camForward\n + input.ndc.x * sky.tanHalfFovX * sky.camRight\n + input.ndc.y * sky.tanHalfFovY * sky.camUp\n );\n let elevation = dir.y;\n\n // Horizon \u2192 zenith gradient with a slow falloff near the horizon.\n let zenithMix = pow(clamp(elevation, 0.0, 1.0), 0.45);\n var color = mix(sky.horizonColor, sky.zenithColor, zenithMix);\n\n // Below the horizon: fade quickly into the ground colour.\n let groundMix = smoothstep(0.0, -0.1, elevation);\n color = mix(color, sky.groundColor, groundMix);\n\n // Sun disc + glow. The disc is slightly oversized vs the real\n // ~0.53\u00B0 sun so it stays visible at typical canvas resolutions.\n let cosSun = dot(dir, sky.sunDirection);\n let disc = smoothstep(0.99985, 0.99995, cosSun);\n let glow = pow(max(cosSun, 0.0), 350.0) * 0.5\n + pow(max(cosSun, 0.0), 24.0) * 0.12;\n // Hide the disc once the sun sets, and never draw it on the ground.\n let sunVisible = smoothstep(-0.06, 0.02, sky.sunDirection.y) * (1.0 - groundMix);\n let sunTint = vec3<f32>(1.0, 0.92, 0.78);\n color += (disc * 6.0 + glow) * max(sky.sunIntensity, 0.0) * sunVisible * sunTint;\n\n // Match the geometry pipeline: exposure \u2192 ACES \u2192 gamma.\n color *= sky.exposure;\n color = acesTonemap(color);\n color = pow(color, vec3<f32>(1.0 / 2.2));\n\n var out: FragmentOutput;\n out.color = vec4<f32>(color, 1.0);\n out.objectIdEncoded = vec4<f32>(0.0, 0.0, 0.0, 0.0);\n return out;\n }\n ";
|
|
12
|
+
//# sourceMappingURL=sky.wgsl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sky.wgsl.d.ts","sourceRoot":"","sources":["../../src/shaders/sky.wgsl.ts"],"names":[],"mappings":"AAIA;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe,k0HA+FrB,CAAC"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
/**
|
|
5
|
+
* Procedural sky background — a single fullscreen triangle drawn at the far
|
|
6
|
+
* plane (reverse-Z: z = 0) before any geometry. Analytic zenith/horizon/
|
|
7
|
+
* ground gradient plus a sun disc + glow, driven by the same sun direction
|
|
8
|
+
* the geometry lighting uses.
|
|
9
|
+
*
|
|
10
|
+
* Tonemapping note: the geometry shader applies ACES + gamma at the end of
|
|
11
|
+
* its fragment stage (not in a post pass), so the sky must apply the same
|
|
12
|
+
* curve here or it would read as a different "film stock" than the model.
|
|
13
|
+
*/
|
|
14
|
+
export const skyShaderSource = `
|
|
15
|
+
struct SkyUniforms {
|
|
16
|
+
camRight: vec3<f32>,
|
|
17
|
+
tanHalfFovX: f32,
|
|
18
|
+
camUp: vec3<f32>,
|
|
19
|
+
tanHalfFovY: f32,
|
|
20
|
+
camForward: vec3<f32>,
|
|
21
|
+
exposure: f32,
|
|
22
|
+
sunDirection: vec3<f32>,
|
|
23
|
+
sunIntensity: f32,
|
|
24
|
+
zenithColor: vec3<f32>,
|
|
25
|
+
_pad0: f32,
|
|
26
|
+
horizonColor: vec3<f32>,
|
|
27
|
+
_pad1: f32,
|
|
28
|
+
groundColor: vec3<f32>,
|
|
29
|
+
_pad2: f32,
|
|
30
|
+
}
|
|
31
|
+
@binding(0) @group(0) var<uniform> sky: SkyUniforms;
|
|
32
|
+
|
|
33
|
+
struct VertexOutput {
|
|
34
|
+
@builtin(position) position: vec4<f32>,
|
|
35
|
+
@location(0) ndc: vec2<f32>,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@vertex
|
|
39
|
+
fn vs_main(@builtin(vertex_index) vi: u32) -> VertexOutput {
|
|
40
|
+
// Fullscreen triangle: (-1,-1), (3,-1), (-1,3).
|
|
41
|
+
let x = f32((vi << 1u) & 2u) * 2.0 - 1.0;
|
|
42
|
+
let y = f32(vi & 2u) * 2.0 - 1.0;
|
|
43
|
+
var out: VertexOutput;
|
|
44
|
+
// z = 0 is the far plane under reverse-Z, so any geometry drawn
|
|
45
|
+
// later wins the depth test without the sky writing depth.
|
|
46
|
+
out.position = vec4<f32>(x, y, 0.0, 1.0);
|
|
47
|
+
out.ndc = vec2<f32>(x, y);
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Same ACES filmic curve as the geometry fragment shader.
|
|
52
|
+
fn acesTonemap(c: vec3<f32>) -> vec3<f32> {
|
|
53
|
+
let a = 2.51;
|
|
54
|
+
let b = 0.03;
|
|
55
|
+
let cc = 2.43;
|
|
56
|
+
let d = 0.59;
|
|
57
|
+
let e = 0.14;
|
|
58
|
+
return clamp((c * (a * c + b)) / (c * (cc * c + d) + e), vec3<f32>(0.0), vec3<f32>(1.0));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
struct FragmentOutput {
|
|
62
|
+
@location(0) color: vec4<f32>,
|
|
63
|
+
// Matches the pass's object-id attachment clear value so the sky
|
|
64
|
+
// never registers as a pickable entity.
|
|
65
|
+
@location(1) objectIdEncoded: vec4<f32>,
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
@fragment
|
|
69
|
+
fn fs_main(input: VertexOutput) -> FragmentOutput {
|
|
70
|
+
// Per-pixel view ray from the camera basis. Used for orthographic
|
|
71
|
+
// cameras too — parallel rays would collapse the sky to a single
|
|
72
|
+
// colour, so a perspective-style fan reads better in both modes.
|
|
73
|
+
let dir = normalize(
|
|
74
|
+
sky.camForward
|
|
75
|
+
+ input.ndc.x * sky.tanHalfFovX * sky.camRight
|
|
76
|
+
+ input.ndc.y * sky.tanHalfFovY * sky.camUp
|
|
77
|
+
);
|
|
78
|
+
let elevation = dir.y;
|
|
79
|
+
|
|
80
|
+
// Horizon → zenith gradient with a slow falloff near the horizon.
|
|
81
|
+
let zenithMix = pow(clamp(elevation, 0.0, 1.0), 0.45);
|
|
82
|
+
var color = mix(sky.horizonColor, sky.zenithColor, zenithMix);
|
|
83
|
+
|
|
84
|
+
// Below the horizon: fade quickly into the ground colour.
|
|
85
|
+
let groundMix = smoothstep(0.0, -0.1, elevation);
|
|
86
|
+
color = mix(color, sky.groundColor, groundMix);
|
|
87
|
+
|
|
88
|
+
// Sun disc + glow. The disc is slightly oversized vs the real
|
|
89
|
+
// ~0.53° sun so it stays visible at typical canvas resolutions.
|
|
90
|
+
let cosSun = dot(dir, sky.sunDirection);
|
|
91
|
+
let disc = smoothstep(0.99985, 0.99995, cosSun);
|
|
92
|
+
let glow = pow(max(cosSun, 0.0), 350.0) * 0.5
|
|
93
|
+
+ pow(max(cosSun, 0.0), 24.0) * 0.12;
|
|
94
|
+
// Hide the disc once the sun sets, and never draw it on the ground.
|
|
95
|
+
let sunVisible = smoothstep(-0.06, 0.02, sky.sunDirection.y) * (1.0 - groundMix);
|
|
96
|
+
let sunTint = vec3<f32>(1.0, 0.92, 0.78);
|
|
97
|
+
color += (disc * 6.0 + glow) * max(sky.sunIntensity, 0.0) * sunVisible * sunTint;
|
|
98
|
+
|
|
99
|
+
// Match the geometry pipeline: exposure → ACES → gamma.
|
|
100
|
+
color *= sky.exposure;
|
|
101
|
+
color = acesTonemap(color);
|
|
102
|
+
color = pow(color, vec3<f32>(1.0 / 2.2));
|
|
103
|
+
|
|
104
|
+
var out: FragmentOutput;
|
|
105
|
+
out.color = vec4<f32>(color, 1.0);
|
|
106
|
+
out.objectIdEncoded = vec4<f32>(0.0, 0.0, 0.0, 0.0);
|
|
107
|
+
return out;
|
|
108
|
+
}
|
|
109
|
+
`;
|
|
110
|
+
//# sourceMappingURL=sky.wgsl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sky.wgsl.js","sourceRoot":"","sources":["../../src/shaders/sky.wgsl.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+FxB,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Procedural sky background pass.
|
|
3
|
+
*
|
|
4
|
+
* Draws one fullscreen triangle into the main render pass BEFORE any
|
|
5
|
+
* geometry (the pass's colour attachments are cleared either way, so the
|
|
6
|
+
* sky simply replaces the flat clear colour). Reverse-Z places it at the
|
|
7
|
+
* far plane (z = 0) with depth writes off, so geometry always wins the
|
|
8
|
+
* depth test and transparent surfaces blend over the sky correctly.
|
|
9
|
+
*
|
|
10
|
+
* The pipeline targets must mirror the main pass exactly: colour + object-id
|
|
11
|
+
* attachments and the MSAA sample count. The object-id output is the
|
|
12
|
+
* attachment's clear value, so picking still reads "background" on sky.
|
|
13
|
+
*/
|
|
14
|
+
import type { ResolvedEnvironment } from './environment.js';
|
|
15
|
+
export interface SkyPassFormats {
|
|
16
|
+
colorFormat: GPUTextureFormat;
|
|
17
|
+
objectIdFormat: GPUTextureFormat;
|
|
18
|
+
depthFormat: GPUTextureFormat;
|
|
19
|
+
sampleCount: number;
|
|
20
|
+
}
|
|
21
|
+
export interface SkyCamera {
|
|
22
|
+
/** Camera world position → target direction, unit. */
|
|
23
|
+
forward: [number, number, number];
|
|
24
|
+
/** Screen-right in world space, unit. */
|
|
25
|
+
right: [number, number, number];
|
|
26
|
+
/** Screen-up in world space, unit. */
|
|
27
|
+
up: [number, number, number];
|
|
28
|
+
/** Vertical field of view in radians. */
|
|
29
|
+
fovY: number;
|
|
30
|
+
/** Viewport width / height. */
|
|
31
|
+
aspect: number;
|
|
32
|
+
}
|
|
33
|
+
export declare class SkyPass {
|
|
34
|
+
private device;
|
|
35
|
+
private pipeline;
|
|
36
|
+
private uniformBuffer;
|
|
37
|
+
private bindGroup;
|
|
38
|
+
private scratch;
|
|
39
|
+
private destroyed;
|
|
40
|
+
constructor(device: GPUDevice, formats: SkyPassFormats, shaderSource: string);
|
|
41
|
+
/**
|
|
42
|
+
* Record the sky draw into an already-begun render pass. Callers must
|
|
43
|
+
* re-set their own pipeline + group(0) afterwards (the main loop does
|
|
44
|
+
* this per batch anyway).
|
|
45
|
+
*/
|
|
46
|
+
draw(pass: GPURenderPassEncoder, camera: SkyCamera, env: ResolvedEnvironment): void;
|
|
47
|
+
destroy(): void;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=sky-pass.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sky-pass.d.ts","sourceRoot":"","sources":["../src/sky-pass.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAE5D,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,gBAAgB,CAAC;IAC9B,cAAc,EAAE,gBAAgB,CAAC;IACjC,WAAW,EAAE,gBAAgB,CAAC;IAC9B,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,SAAS;IACxB,sDAAsD;IACtD,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,yCAAyC;IACzC,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,sCAAsC;IACtC,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,+BAA+B;IAC/B,MAAM,EAAE,MAAM,CAAC;CAChB;AAID,qBAAa,OAAO;IAClB,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,QAAQ,CAAoB;IACpC,OAAO,CAAC,aAAa,CAAY;IACjC,OAAO,CAAC,SAAS,CAAe;IAChC,OAAO,CAAC,OAAO,CAAwC;IACvD,OAAO,CAAC,SAAS,CAAS;gBAEd,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM;IA+C5E;;;;OAIG;IACH,IAAI,CAAC,IAAI,EAAE,oBAAoB,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,mBAAmB,GAAG,IAAI;IA2BnF,OAAO,IAAI,IAAI;CAKhB"}
|
package/dist/sky-pass.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
const SKY_UNIFORM_FLOATS = 28; // 7 × vec4 rows = 112 bytes
|
|
5
|
+
export class SkyPass {
|
|
6
|
+
device;
|
|
7
|
+
pipeline;
|
|
8
|
+
uniformBuffer;
|
|
9
|
+
bindGroup;
|
|
10
|
+
scratch = new Float32Array(SKY_UNIFORM_FLOATS);
|
|
11
|
+
destroyed = false;
|
|
12
|
+
constructor(device, formats, shaderSource) {
|
|
13
|
+
this.device = device;
|
|
14
|
+
this.uniformBuffer = device.createBuffer({
|
|
15
|
+
label: 'sky-uniforms',
|
|
16
|
+
size: SKY_UNIFORM_FLOATS * 4,
|
|
17
|
+
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
18
|
+
});
|
|
19
|
+
const bindGroupLayout = device.createBindGroupLayout({
|
|
20
|
+
label: 'sky-bgl',
|
|
21
|
+
entries: [
|
|
22
|
+
{
|
|
23
|
+
binding: 0,
|
|
24
|
+
visibility: GPUShaderStage.FRAGMENT,
|
|
25
|
+
buffer: { type: 'uniform' },
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
});
|
|
29
|
+
this.bindGroup = device.createBindGroup({
|
|
30
|
+
layout: bindGroupLayout,
|
|
31
|
+
entries: [{ binding: 0, resource: { buffer: this.uniformBuffer } }],
|
|
32
|
+
});
|
|
33
|
+
const module = device.createShaderModule({ label: 'sky-shader', code: shaderSource });
|
|
34
|
+
this.pipeline = device.createRenderPipeline({
|
|
35
|
+
label: 'sky-pipeline',
|
|
36
|
+
layout: device.createPipelineLayout({ bindGroupLayouts: [bindGroupLayout] }),
|
|
37
|
+
vertex: { module, entryPoint: 'vs_main' },
|
|
38
|
+
fragment: {
|
|
39
|
+
module,
|
|
40
|
+
entryPoint: 'fs_main',
|
|
41
|
+
targets: [{ format: formats.colorFormat }, { format: formats.objectIdFormat }],
|
|
42
|
+
},
|
|
43
|
+
primitive: { topology: 'triangle-list' },
|
|
44
|
+
depthStencil: {
|
|
45
|
+
format: formats.depthFormat,
|
|
46
|
+
depthWriteEnabled: false,
|
|
47
|
+
// Drawn first into a depth buffer cleared to 0.0 (reverse-Z far),
|
|
48
|
+
// so z = 0 passes everywhere; kept as a real compare (not 'always')
|
|
49
|
+
// in case the draw ever moves after opaque geometry.
|
|
50
|
+
depthCompare: 'greater-equal',
|
|
51
|
+
},
|
|
52
|
+
multisample: { count: formats.sampleCount },
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Record the sky draw into an already-begun render pass. Callers must
|
|
57
|
+
* re-set their own pipeline + group(0) afterwards (the main loop does
|
|
58
|
+
* this per batch anyway).
|
|
59
|
+
*/
|
|
60
|
+
draw(pass, camera, env) {
|
|
61
|
+
if (this.destroyed)
|
|
62
|
+
return;
|
|
63
|
+
const tanHalfFovY = Math.tan(camera.fovY / 2);
|
|
64
|
+
const tanHalfFovX = tanHalfFovY * camera.aspect;
|
|
65
|
+
const u = this.scratch;
|
|
66
|
+
u[0] = camera.right[0];
|
|
67
|
+
u[1] = camera.right[1];
|
|
68
|
+
u[2] = camera.right[2];
|
|
69
|
+
u[3] = tanHalfFovX;
|
|
70
|
+
u[4] = camera.up[0];
|
|
71
|
+
u[5] = camera.up[1];
|
|
72
|
+
u[6] = camera.up[2];
|
|
73
|
+
u[7] = tanHalfFovY;
|
|
74
|
+
u[8] = camera.forward[0];
|
|
75
|
+
u[9] = camera.forward[1];
|
|
76
|
+
u[10] = camera.forward[2];
|
|
77
|
+
u[11] = env.exposure;
|
|
78
|
+
u[12] = env.sunDirection[0];
|
|
79
|
+
u[13] = env.sunDirection[1];
|
|
80
|
+
u[14] = env.sunDirection[2];
|
|
81
|
+
u[15] = env.sunIntensity;
|
|
82
|
+
u[16] = env.sky.zenith[0];
|
|
83
|
+
u[17] = env.sky.zenith[1];
|
|
84
|
+
u[18] = env.sky.zenith[2];
|
|
85
|
+
u[19] = 0;
|
|
86
|
+
u[20] = env.sky.horizon[0];
|
|
87
|
+
u[21] = env.sky.horizon[1];
|
|
88
|
+
u[22] = env.sky.horizon[2];
|
|
89
|
+
u[23] = 0;
|
|
90
|
+
u[24] = env.sky.ground[0];
|
|
91
|
+
u[25] = env.sky.ground[1];
|
|
92
|
+
u[26] = env.sky.ground[2];
|
|
93
|
+
u[27] = 0;
|
|
94
|
+
this.device.queue.writeBuffer(this.uniformBuffer, 0, u);
|
|
95
|
+
pass.setPipeline(this.pipeline);
|
|
96
|
+
pass.setBindGroup(0, this.bindGroup);
|
|
97
|
+
pass.draw(3);
|
|
98
|
+
}
|
|
99
|
+
destroy() {
|
|
100
|
+
if (this.destroyed)
|
|
101
|
+
return;
|
|
102
|
+
this.destroyed = true;
|
|
103
|
+
this.uniformBuffer.destroy();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=sky-pass.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sky-pass.js","sourceRoot":"","sources":["../src/sky-pass.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -130,6 +130,12 @@ export interface VisualEnhancementOptions {
|
|
|
130
130
|
}
|
|
131
131
|
export interface RenderOptions {
|
|
132
132
|
clearColor?: [number, number, number, number];
|
|
133
|
+
/**
|
|
134
|
+
* Global lighting environment (sun direction/colour, hemisphere ambient,
|
|
135
|
+
* exposure, procedural sky). Omitted/empty reproduces the legacy hardcoded
|
|
136
|
+
* look exactly. See {@link import('./environment.js').LightingEnvironment}.
|
|
137
|
+
*/
|
|
138
|
+
environment?: import('./environment.js').LightingEnvironment;
|
|
133
139
|
enableDepthTest?: boolean;
|
|
134
140
|
enableFrustumCulling?: boolean;
|
|
135
141
|
spatialIndex?: import('@ifc-lite/spatial').SpatialIndex;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ifc-lite/renderer",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.27.0",
|
|
4
4
|
"description": "WebGPU renderer for IFC-Lite",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -13,8 +13,7 @@
|
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@ifc-lite/geometry": "^2.6.
|
|
17
|
-
"@ifc-lite/wasm": "^2.7.0",
|
|
16
|
+
"@ifc-lite/geometry": "^2.6.1",
|
|
18
17
|
"@ifc-lite/spatial": "^1.14.8"
|
|
19
18
|
},
|
|
20
19
|
"devDependencies": {
|