@hology/core 0.0.247 → 0.0.248
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/gameplay/actors/actor.js +1 -1
- package/dist/gameplay/actors/builtin/components/spline-component.js +1 -1
- package/dist/gameplay/actors/builtin/components/water-post-process-settings.d.ts +24 -0
- package/dist/gameplay/actors/builtin/components/water-post-process-settings.js +4 -0
- package/dist/gameplay/actors/builtin/post-process-volume-actor.d.ts +1 -2
- package/dist/gameplay/actors/builtin/water-spline-volume-actor.d.ts +11 -1
- package/dist/gameplay/actors/builtin/water-spline-volume-actor.js +1 -1
- package/dist/gameplay/actors/builtin/water-volume-actor.d.ts +12 -1
- package/dist/gameplay/actors/builtin/water-volume-actor.js +1 -1
- package/dist/gameplay/actors/camera/camera-component.js +1 -1
- package/dist/gameplay/actors/camera/third-person-camera-component.js +1 -1
- package/dist/gameplay/actors/component.d.ts +0 -1
- package/dist/gameplay/actors/component.js +1 -1
- package/dist/gameplay/actors/internal/component-init.d.ts +1 -1
- package/dist/gameplay/actors/internal/component-init.js +1 -1
- package/dist/gameplay/inject.d.ts +3 -0
- package/dist/gameplay/inject.js +1 -1
- package/dist/gameplay/services/actor-update-registry.d.ts +13 -0
- package/dist/gameplay/services/actor-update-registry.js +4 -0
- package/dist/gameplay/services/render.d.ts +1 -0
- package/dist/gameplay/services/render.js +1 -1
- package/dist/gameplay/services/world.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -1
- package/dist/rendering/webgpu-experimental-backend.js +1 -1
- package/dist/rendering.d.ts +8 -0
- package/dist/rendering.js +1 -1
- package/dist/scene/materializer.js +1 -1
- package/dist/shader/graph/compiler.js +1 -1
- package/dist/shader/graph/model.d.ts +1 -1
- package/dist/shader/graph/registry.js +1 -1
- package/dist/shader/parameter.d.ts +3 -0
- package/dist/shader/post-process-shader.js +1 -1
- package/dist/shader-nodes/depth.d.ts +3 -1
- package/dist/shader-nodes/depth.js +1 -1
- package/dist/shader-nodes/index.d.ts +1 -0
- package/dist/shader-nodes/index.js +1 -1
- package/dist/shader-nodes/water-volume.d.ts +9 -0
- package/dist/shader-nodes/water-volume.js +4 -0
- package/dist/shader-nodes/water.d.ts +12 -0
- package/dist/shader-nodes/water.js +4 -0
- package/dist/test/materializer-dynamic-transform.test.js +1 -1
- package/dist/test/post-process-effect.test.js +1 -1
- package/dist/test/spline-component.test.js +1 -1
- package/dist/test/view-controller-updates.test.d.ts +2 -0
- package/dist/test/view-controller-updates.test.js +4 -0
- package/dist/test/water-spline-volume-actor.test.js +1 -1
- package/package.json +3 -3
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import*as e from"three";import{NodeMaterialWebGpuResolver as t}from"./node-material-webgpu-resolver.js";import{float as i,varyingTransformed as n,vec4 as o}from"three-shader-graph";import{NodeShaderMaterial as r}from"../shader-nodes/index.js";import{sceneMapUniformName as s}from"../shader-nodes/scene-sample.js";import{MATERIAL_FLAG_ALPHA_TEST as a,MATERIAL_FLAG_RECEIVE_DECALS as l}from"../shader-nodes/material-flags.js";import{createMotionVectorOutput as d}from"./motion-vector-node.js";export class ExperimentalWebGpuBackend{constructor(e,t,i,n,o,r,s,a,l,d,c,h,u,p,m,g,f,v,S){this.renderer=e,this.fallbackMaterial=t,this.nodeMaterialResolver=i,this.gBufferFallbackSource=n,this.bloom=o,this.fxaa=r,this.smaa=s,this.gtao=a,this.ssr=l,this.taa=d,this.motionBlur=c,this.volumetricFog=h,this.colorGrading=u,this.depthOfField=p,this.outline=m,this.postProcessEffects=g,this.renderGraphExtensions=f,this.antiAliasingState=v,this.renderScale=1,this.pmremGenerator=null,this.pmremResults=new WeakMap,this.ownedPmremResults=new Set,this.warnedUnsupportedPostProcessStages=new Set,this.canvas=S}static async create(u={}){const p=await import("@hology/webgpu-renderer"),m=document.createElement("canvas");m.dataset.hologyRenderer="webgpu-experimental";const g=new p.WGSLShaderMaterial({vertexShader:c,fragmentShader:h});g.side=p.DoubleSide;const f=p,v=null!=u.motionBlur&&!1!==u.motionBlur,S=!1!==u.gtao&&(null==u.gtao||!1!==u.gtao.temporal),P=!1!==u.volumetricFog&&(null==u.volumetricFog||!1!==u.volumetricFog.temporal),x=null!=(w=u).antiAliasing?w.antiAliasing:null!=w.taa&&!1!==w.taa?"taa":null!=w.smaa&&!1!==w.smaa?"smaa":null!=w.fxaa&&!1!==w.fxaa?"fxaa":!1===w.taa?"off":"taa";var w;const b=null!=u.antiAliasing,y=v||S||P||(b||"taa"===x),F=new t(f,(e,t)=>"opaque"===t?[e.outputColor.xyz.rgba(i(e.alphaTest>0?a:0).add(!1===e.userData.receiveDecals?0:l)),o((e.outputNormal??n.normal).multiplyScalar(.5).addScalar(.5),e.outputRoughness??i(1)),...y?[d(e)]:[]]:void 0),C=new e.MeshStandardMaterial({color:8688803,roughness:1});if(null==F.resolve(C,"opaque"))throw g.dispose(),C.dispose(),new Error("Failed to compile the WebGPU G-buffer fallback material.");const L=!1===u.gtao?null:new p.GTAOEffect({normalSpace:"view",...p.GTAO_PRESETS.high,...u.gtao??{}}),M=!1===u.ssr||null==u.ssr?null:new p.SSREffect({normalSpace:"view",resolutionScale:.5,...u.ssr??{}}),O=!1===u.bloom?null:new p.BloomEffect({threshold:1,strength:.9,radius:.2,...u.bloom??{}}),E=b||!1!==u.fxaa&&"fxaa"===x?new p.FXAAEffect(!1===u.fxaa?{}:u.fxaa):null,G=b||!1!==u.smaa&&"smaa"===x?new p.SMAAEffect(!1===u.smaa?{}:u.smaa):null,R=b||!1!==u.taa&&"taa"===x?new p.TemporalAAEffect({renderScale:1,...!1===u.taa?{}:u.taa}):null,D={mode:x},B=!1===u.motionBlur||null==u.motionBlur?null:new p.MotionBlurEffect(u.motionBlur),A=u.volumetricFog||void 0,T=!1===u.volumetricFog?null:new p.VolumetricFogEffect(void 0,{...p.VOLUMETRIC_FOG_PRESETS.high,maxDistance:64,spatialJitter:!0,scatteringIntensity:Math.PI,...A,temporal:!1!==A?.temporal&&{...p.VOLUMETRIC_FOG_PRESETS.high.temporal,...A?.temporal},blur:!1!==A?.blur&&{...!1===p.VOLUMETRIC_FOG_PRESETS.high.blur?{}:p.VOLUMETRIC_FOG_PRESETS.high.blur,depthAwareUpsample:!0,...A?.blur}}),V=!1===u.colorGrading?null:new p.ColorGradingEffect({enabled:!1,...u.colorGrading??{}}),W=!1===u.depthOfField?null:new p.DepthOfFieldEffect({enabled:!1,...u.depthOfField??{}}),k=!1===u.outline?null:new p.OutlineEffect({enabled:!1,...u.outline??{}}),I=[],N=new WeakMap,U=new Set,j=[],H=(e,t,i)=>{let n=i;const o=I.filter(e=>e.enabled&&e.stage===t).sort((e,t)=>e.priority-t.priority||e.id-t.id);for(const t of o){if(!(t.material instanceof r)){const e=`${t.id}:material`;U.has(e)||(U.add(e),console.warn(`[Hology WebGPU experiment] Skipping custom post-process effect ${t.id}: only NodeShaderMaterial effects are supported.`));continue}try{let i=N.get(t.material);null!=i&&i.version===t.material.version||(i={version:t.material.version,shader:F.compileFullscreen(t.material)},N.set(t.material,i));const o=e.createColorTarget(`hologyCustomPostProcess${t.id}`);e.addNodeFullscreenPass({name:`HologyCustomPostProcess${t.id}`,shader:i.shader,inputOverrides:{[s]:n},outputs:o}),n=o}catch(e){const i=`${t.id}:compile:${t.material.version}`;U.has(i)||(U.add(i),console.warn(`[Hology WebGPU experiment] Skipping custom post-process effect ${t.id}: ${e instanceof Error?e.message:String(e)}`,e))}}return n},$=new p.WebGpuRenderer({canvas:m,fallbackMaterial:g,materialResolver:(e,t)=>F.resolve(e,t),invokeObjectRenderCallbacks:!1,hiZOcclusionCulling:!0,renderGraphProfiling:!1,sampleCount:1,multiDrawIndirect:!1});$.setRenderScale("taa"===x?R?.renderScale??1:1),$.setRenderGraphBuilder((e,t)=>{const i=new p.FrameGraphBuilder(t),n=i.createColorTarget("hologyNormalRoughness",{format:"rgba16float"}),o=y?i.createColorTarget("hologyMotionVectors",{format:"rgba16float"}):null;for(const e of j)"beforeScene"===e.stage&&e(i,i.sceneColor);i.addDirectionalShadowPass(),i.addPointShadowPass(),i.addSpotShadowPass(),i.addDepthPrepass(),i.addHiZPasses(),i.addVisibilityPass(),i.addMaterialPass({name:"OpaqueGBufferPass",outputs:null==o?[i.sceneColor,n]:[i.sceneColor,n,o],drawStage:"opaque",loadDepth:!0}),i.addHiZHistoryPass("OpaqueGBufferPass");let r=i.sceneColor;if(L?.enabled){const e=L.addPasses(i,{depth:i.mainDepth,normal:n,velocity:o??void 0});r=L.addCompositePass(i,r,e)}null!=M&&(r=M.addPasses(i,{sceneColor:r,depth:i.mainDepth,normalRoughness:n}));const s=new Set(j.flatMap(e=>e.getSceneTextureReads?.()??[]));i.addSceneTextureCopyPass(r,{sceneColor:s.has("sceneColorSnapshot"),sceneDepth:s.has("sceneDepthSnapshot")}),i.addLatePass(r);for(const e of j)"beforeScene"!==e.stage&&e(i,r);r=H(i,"beforeFog",r),null!=T&&(r=T.addPasses(i,{sceneColor:r,depth:i.mainDepth,velocity:o??void 0,volumeObjects:t.fogVolumeObjects,lights:t.extractedLights})),null!=B&&null!=o&&(r=B.addPass(i,r,o,i.mainDepth)),null!=O&&(r=O.addPass(i,r)),r=H(i,"beforeOutline",r),null!=k&&(r=k.addPass(i,r)),r=H(i,"beforeDepthOfField",r),null!=W&&(r=W.addPass(i,r,i.mainDepth)),r=H(i,"beforeColorAdjustment",r),null!=V&&(r=V.addPass(i,r)),r=H(i,"beforeAntiAliasing",r);let a="taa"===D.mode&&null!=R?R.addPass(i,r,i.mainDepth,o??void 0):r;"smaa"===D.mode&&null!=G?a=G.addPass(i,a):"fxaa"===D.mode&&null!=E&&(a=E.addPass(i,a)),a=H(i,"beforeOutput",a),i.present(a)});try{return await $.initialize(),new ExperimentalWebGpuBackend($,g,F,C,O,E,G,L,M,R,B,T,V,W,k,I,j,D,m)}catch(e){throw $.destroy(),g.dispose(),F.dispose(),C.dispose(),L?.dispose(),G?.dispose(),B?.dispose(),T?.dispose(),k?.dispose(),e}}static supportsCamera(t){return t instanceof e.PerspectiveCamera||t instanceof e.OrthographicCamera}setSize(e,t,i,n=1){const o=this.canvas.width,r=this.canvas.height,s=Math.min(1,Math.max(.25,n)),a=s!==this.renderScale;this.renderScale=s,this.renderer.setRenderScale(s),this.taa?.setRenderScale(s),this.renderer.setSize(e,t,i),(a||this.canvas.width!==o||this.canvas.height!==r)&&this.resetViewHistory()}initTexture(e){this.renderer.initTexture(e)}setPostProcessEffects(e){this.postProcessEffects.splice(0,this.postProcessEffects.length,...e);for(const t of e){if("beforeLut"!==t.stage)continue;const e=`${t.id}:${t.stage}`;this.warnedUnsupportedPostProcessStages.has(e)||(this.warnedUnsupportedPostProcessStages.add(e),console.warn(`[Hology WebGPU experiment] Skipping custom post-process effect ${t.id}: stage "${t.stage}" has no native WebGPU anchor.`))}}getDevice(){return this.renderer.getDevice()}setRenderGraphExtensions(e){this.renderGraphExtensions.length=0;for(const t of e)this.renderGraphExtensions.push(t)}setEnableOutlines(e){null!=this.outline&&(this.outline.enabled=e)}setDynamicBatchingEnabled(e){this.renderer.setDynamicBatchingEnabled(e)}setSelectedObjects(e){this.outline?.setSelectedObjects(e)}setSelectedObjectInstances(e){this.outline?.setSelectedInstances(e)}setCascadedShadowRanges(e,t=[]){this.renderer.setCascadedShadowRanges(e,t),this.volumetricFog?.setCascadedDirectionalLights(t)}setWorldEnvironment(t){if(null==t)return void this.nodeMaterialResolver.setWorldEnvironment(null);let i=t.source;if(i.mapping!==e.CubeUVReflectionMapping){let e=this.pmremResults.get(i);null==e&&(this.pmremGenerator??(this.pmremGenerator=this.renderer.createPMREMGenerator()),e=this.pmremGenerator.fromEquirectangular(i),this.pmremResults.set(i,e),this.ownedPmremResults.add(e)),i=e.texture}this.nodeMaterialResolver.setWorldEnvironment({texture:i,intensity:t.intensity})}resetViewHistory(){this.renderer.resetViewHistory(),this.taa?.reset(),this.motionBlur?.reset(),this.gtao?.reset(),this.volumetricFog?.reset()}async compile(e,t){await this.renderer.compileAsync(e,t)}createStaticDrawSet(e,t,i=!1){this.renderer.setHiZOcclusionCullingEnabled(!0);const n=this.renderer.createStaticDrawSet(e,{label:t,onUnsupported:"skip",allowUnsortedLateDraws:i});return 0===n.drawCount?(n.dispose(),null):n}render(e,t,i){const n="taa"===this.antiAliasingState.mode?this.taa:null;this.volumetricFog?.setScene(e),this.renderer.setMotionVectorProjection(t.projectionMatrix),this.motionBlur?.prepareCamera(t,this.canvas.width,this.canvas.height,i),this.motionBlur?.requiresRendererHistoryReset()&&(this.renderer.resetViewHistory(),this.gtao?.reset(),this.volumetricFog?.reset()),n?.prepareCamera(t,this.canvas.width,this.canvas.height),this.motionBlur?.setJitterUv(n?.currentJitterUv.x??0,n?.currentJitterUv.y??0);let o=!1;try{this.renderer.render(e,t,i),o=!0}finally{n?.finishCamera(t),o&&this.motionBlur?.commitCamera()}}setMotionBlurOptions(e){null!=this.motionBlur&&Object.assign(this.motionBlur.options,e)}setGTAOOptions(e){this.gtao?.setOptions(e)}setAntiAliasingMode(e){e!==this.antiAliasingState.mode&&("taa"===e&&null==this.taa||"smaa"===e&&null==this.smaa||"fxaa"===e&&null==this.fxaa?console.warn(`[Hology WebGPU experiment] Anti-aliasing mode "${e}" was not initialized. Set webgpuPostProcessing.antiAliasing in the initial rendering configuration to enable runtime switching.`):(this.antiAliasingState.mode=e,this.resetTemporalHistory()))}setGTAOQuality(e){this.gtao?.applyPreset(e)}setVolumetricFogOptions(e){this.volumetricFog?.setOptions(e)}setVolumetricFogQuality(e){this.volumetricFog?.applyPreset(e)}pushMotionBlurOverride(e){return this.motionBlur?.pushOverride(e)??null}invalidateObjectMotion(e){this.renderer.invalidateObjectMotion(e)}getLastFrameProfile(){return this.renderer.getLastFrameProfile()}setPostProcessState(e){null!=this.colorGrading&&(Object.assign(this.colorGrading.options,e??{enabled:!1}),this.colorGrading.options.enabled=!0===e?.enabled),null!=this.depthOfField&&(this.depthOfField.options.enabled=null!=e?.depthFocus||null!=e?.depthAperture||null!=e?.depthMaxBlur,this.depthOfField.options.focus=e?.depthFocus,this.depthOfField.options.aperture=e?.depthAperture,this.depthOfField.options.maxBlur=e?.depthMaxBlur)}setProfilingEnabled(e){this.renderer.setRenderGraphProfilingEnabled(e)}resetTemporalHistory(){this.taa?.reset(),this.motionBlur?.reset(),this.gtao?.reset(),this.volumetricFog?.reset(),this.renderer.resetViewHistory()}inspect(e,t){return inspectWebGpuScene(e,t,this.nodeMaterialResolver)}destroy(){this.nodeMaterialResolver.dispose(),this.gBufferFallbackSource.dispose(),this.gtao?.dispose(),this.smaa?.dispose(),this.motionBlur?.dispose(),this.volumetricFog?.dispose();for(const e of this.ownedPmremResults)e.dispose();this.ownedPmremResults.clear(),this.renderer.destroy(),this.fallbackMaterial.dispose()}}export function inspectWebGpuScene(e,t,i){const n={meshes:0,skinnedMeshes:0,instancedMeshes:0,batchedMeshes:0,sprites:0,points:0,lines:0,missingPositionGeometry:0,transparentMaterials:0,shaderMaterialsReplaced:0,materialsReplaced:0,unsupportedCamera:ExperimentalWebGpuBackend.supportsCamera(t)?0:1};return e.traverse(e=>{if(e.isSprite)return void n.sprites++;if(e.isPoints)return void n.points++;if(e.isLine)return void n.lines++;if(!e.isMesh)return;const t=e;t.isBatchedMesh?n.batchedMeshes++:t.isInstancedMesh?n.instancedMeshes++:t.isSkinnedMesh?n.skinnedMeshes++:n.meshes++,null==t.geometry?.getAttribute("position")&&n.missingPositionGeometry++;const o=Array.isArray(t.material)?t.material:[t.material];for(const e of o){if(null==e)continue;e.transparent&&n.transparentMaterials++;const t=!0===e.isWGSLShaderMaterial||!0===e.isLineMaterial,o=!t&&!0===i?.canResolve(e);t||o||(n.materialsReplaced++,e.isShaderMaterial&&n.shaderMaterialsReplaced++)}}),n}export function formatWebGpuSceneCapabilitySummary(e){return`[Hology WebGPU experiment] ${JSON.stringify(e)}`}const c="\nstruct VertexInput {\n @location(0) position: vec3<f32>,\n @location(1) normal: vec3<f32>,\n @location(2) uv: vec2<f32>,\n @location(3) color: vec4<f32>,\n#ifdef USE_SKINNING\n @location(4) skinIndex: vec4<u32>,\n @location(5) skinWeight: vec4<f32>,\n#endif\n @builtin(instance_index) instanceIndex: u32,\n};\n\nstruct VertexOutput {\n @builtin(position) position: vec4<f32>,\n @location(0) worldPosition: vec3<f32>,\n @location(1) worldNormal: vec3<f32>,\n @location(2) color: vec4<f32>,\n};\n\n@vertex\nfn vsMain(input: VertexInput) -> VertexOutput {\n var output: VertexOutput;\n#ifdef USE_SKINNING\n let localPosition = rendererSkinPosition(input.position, input.skinIndex, input.skinWeight);\n let localNormal = rendererSkinNormal(input.normal, input.skinIndex, input.skinWeight);\n#else\n let localPosition = input.position;\n let localNormal = input.normal;\n#endif\n let world = rendererWorldPosition(localPosition, input.instanceIndex);\n output.position = rendererClipPosition(localPosition, input.instanceIndex);\n output.worldPosition = world.xyz;\n output.worldNormal = rendererWorldNormal(localNormal, input.instanceIndex);\n output.color = input.color * rendererObjectColor(input.instanceIndex);\n return output;\n}\n",h="\nstruct VertexOutput {\n @builtin(position) position: vec4<f32>,\n @location(0) worldPosition: vec3<f32>,\n @location(1) worldNormal: vec3<f32>,\n @location(2) color: vec4<f32>,\n};\n\nfn distanceAttenuation(distanceToLight: f32, cutoffDistance: f32, decayExponent: f32) -> f32 {\n var distanceFalloff = 1.0 / max(pow(distanceToLight, decayExponent), 0.01);\n if (cutoffDistance > 0.0) {\n let ratio = distanceToLight / cutoffDistance;\n let cutoff = clamp(1.0 - ratio * ratio * ratio * ratio, 0.0, 1.0);\n distanceFalloff *= cutoff * cutoff;\n }\n return distanceFalloff;\n}\n\n@fragment\nfn fsMain(input: VertexOutput) -> @location(0) vec4<f32> {\n let normal = normalize(input.worldNormal);\n let normalTint = normal * 0.5 + vec3<f32>(0.5);\n let base = mix(vec3<f32>(0.52, 0.58, 0.64), normalTint, 0.22) * input.color.rgb;\n var lighting = rendererIndirectDiffuse(normal) + vec3<f32>(0.08);\n var directionalLighting = vec3<f32>(0.0);\n let directionalCount = rendererDirectionalLightCount();\n\n for (var index: u32 = 0u; index < directionalCount; index = index + 1u) {\n let lightDirection = rendererDirectionalLightDirectionForLight(index);\n let shadow = rendererSampleDirectionalShadowForLightWithNormal(index, input.worldPosition, normal);\n directionalLighting += rendererDirectionalLightColorForLight(index) * max(dot(normal, lightDirection), 0.0) * shadow;\n }\n if (directionalCount > 0u) {\n lighting += directionalLighting / f32(directionalCount);\n }\n\n for (var index: u32 = 0u; index < rendererPointLightCount(); index = index + 1u) {\n let lightVector = rendererPointLightPositionForLight(index) - input.worldPosition;\n let distanceToLight = length(lightVector);\n let lightDirection = normalize(lightVector);\n let shadow = rendererSamplePointShadowForLightWithNormal(index, input.worldPosition, normal);\n lighting += rendererPointLightColorForLight(index) * max(dot(normal, lightDirection), 0.0) * distanceAttenuation(distanceToLight, rendererPointLightRangeForLight(index), rendererPointLightDecayForLight(index)) * shadow;\n }\n\n for (var index: u32 = 0u; index < rendererSpotLightCount(); index = index + 1u) {\n let lightVector = rendererSpotLightPositionForLight(index) - input.worldPosition;\n let distanceToLight = length(lightVector);\n let lightDirection = normalize(lightVector);\n let angleCos = dot(-lightDirection, rendererSpotLightDirectionForLight(index));\n let cone = smoothstep(rendererSpotLightConeCosForLight(index), rendererSpotLightPenumbraCosForLight(index), angleCos);\n let shadow = rendererSampleSpotShadowForLightWithNormal(index, input.worldPosition, normal);\n lighting += rendererSpotLightColorForLight(index) * max(dot(normal, lightDirection), 0.0) * cone * distanceAttenuation(distanceToLight, rendererSpotLightRangeForLight(index), rendererSpotLightDecayForLight(index)) * shadow;\n }\n\n return vec4<f32>(base * max(lighting, vec3<f32>(0.08)), input.color.a);\n}\n";/*
|
|
1
|
+
import*as e from"three";import{NodeMaterialWebGpuResolver as t}from"./node-material-webgpu-resolver.js";import{float as i,varyingTransformed as n,vec4 as o}from"three-shader-graph";import{NodeShaderMaterial as r}from"../shader-nodes/index.js";import{sceneMapUniformName as s}from"../shader-nodes/scene-sample.js";import{depthUniformName as a}from"../shader-nodes/depth.js";import{MATERIAL_FLAG_ALPHA_TEST as l,MATERIAL_FLAG_RECEIVE_DECALS as d}from"../shader-nodes/material-flags.js";import{createMotionVectorOutput as c}from"./motion-vector-node.js";export class ExperimentalWebGpuBackend{constructor(e,t,i,n,o,r,s,a,l,d,c,h,u,p,m,g,f,v,S){this.renderer=e,this.fallbackMaterial=t,this.nodeMaterialResolver=i,this.gBufferFallbackSource=n,this.bloom=o,this.fxaa=r,this.smaa=s,this.gtao=a,this.ssr=l,this.taa=d,this.motionBlur=c,this.volumetricFog=h,this.colorGrading=u,this.depthOfField=p,this.outline=m,this.postProcessEffects=g,this.renderGraphExtensions=f,this.antiAliasingState=v,this.renderScale=1,this.pmremGenerator=null,this.pmremResults=new WeakMap,this.ownedPmremResults=new Set,this.warnedUnsupportedPostProcessStages=new Set,this.canvas=S}static async create(p={}){const m=await import("@hology/webgpu-renderer"),g=document.createElement("canvas");g.dataset.hologyRenderer="webgpu-experimental";const f=new m.WGSLShaderMaterial({vertexShader:h,fragmentShader:u});f.side=m.DoubleSide;const v=m,S=null!=p.motionBlur&&!1!==p.motionBlur,P=!1!==p.gtao&&(null==p.gtao||!1!==p.gtao.temporal),x=!1!==p.volumetricFog&&(null==p.volumetricFog||!1!==p.volumetricFog.temporal),w=null!=(b=p).antiAliasing?b.antiAliasing:null!=b.taa&&!1!==b.taa?"taa":null!=b.smaa&&!1!==b.smaa?"smaa":null!=b.fxaa&&!1!==b.fxaa?"fxaa":!1===b.taa?"off":"taa";var b;const y=null!=p.antiAliasing,F=S||P||x||(y||"taa"===w),C=new t(v,(e,t)=>"opaque"===t?[e.outputColor.xyz.rgba(i(e.alphaTest>0?l:0).add(!1===e.userData.receiveDecals?0:d)),o((e.outputNormal??n.normal).multiplyScalar(.5).addScalar(.5),e.outputRoughness??i(1)),...F?[c(e)]:[]]:void 0),L=new e.MeshStandardMaterial({color:8688803,roughness:1});if(null==C.resolve(L,"opaque"))throw f.dispose(),L.dispose(),new Error("Failed to compile the WebGPU G-buffer fallback material.");const M=!1===p.gtao?null:new m.GTAOEffect({normalSpace:"view",...m.GTAO_PRESETS.high,...p.gtao??{}}),O=!1===p.ssr||null==p.ssr?null:new m.SSREffect({normalSpace:"view",resolutionScale:.5,...p.ssr??{}}),E=!1===p.bloom?null:new m.BloomEffect({threshold:1,strength:.9,radius:.2,...p.bloom??{}}),G=y||!1!==p.fxaa&&"fxaa"===w?new m.FXAAEffect(!1===p.fxaa?{}:p.fxaa):null,R=y||!1!==p.smaa&&"smaa"===w?new m.SMAAEffect(!1===p.smaa?{}:p.smaa):null,D=y||!1!==p.taa&&"taa"===w?new m.TemporalAAEffect({renderScale:1,...!1===p.taa?{}:p.taa}):null,B={mode:w},A=!1===p.motionBlur||null==p.motionBlur?null:new m.MotionBlurEffect(p.motionBlur),T=p.volumetricFog||void 0,V=!1===p.volumetricFog?null:new m.VolumetricFogEffect(void 0,{...m.VOLUMETRIC_FOG_PRESETS.high,maxDistance:64,spatialJitter:!0,scatteringIntensity:Math.PI,...T,temporal:!1!==T?.temporal&&{...m.VOLUMETRIC_FOG_PRESETS.high.temporal,...T?.temporal},blur:!1!==T?.blur&&{...!1===m.VOLUMETRIC_FOG_PRESETS.high.blur?{}:m.VOLUMETRIC_FOG_PRESETS.high.blur,depthAwareUpsample:!0,...T?.blur}}),W=!1===p.colorGrading?null:new m.ColorGradingEffect({enabled:!1,...p.colorGrading??{}}),k=!1===p.depthOfField?null:new m.DepthOfFieldEffect({enabled:!1,...p.depthOfField??{}}),I=!1===p.outline?null:new m.OutlineEffect({enabled:!1,...p.outline??{}}),N=[],U=new WeakMap,j=new Set,H=[],$=(e,t,i)=>{let n=i;const o=N.filter(e=>e.enabled&&e.stage===t).sort((e,t)=>e.priority-t.priority||e.id-t.id);for(const t of o){if(!(t.material instanceof r)){const e=`${t.id}:material`;j.has(e)||(j.add(e),console.warn(`[Hology WebGPU experiment] Skipping custom post-process effect ${t.id}: only NodeShaderMaterial effects are supported.`));continue}try{let i=U.get(t.material);null!=i&&i.version===t.material.version||(i={version:t.material.version,shader:C.compileFullscreen(t.material)},U.set(t.material,i));const o=e.createColorTarget(`hologyCustomPostProcess${t.id}`);e.addNodeFullscreenPass({name:`HologyCustomPostProcess${t.id}`,shader:i.shader,inputOverrides:{[s]:n},outputs:o}),n=o}catch(e){const i=`${t.id}:compile:${t.material.version}`;j.has(i)||(j.add(i),console.warn(`[Hology WebGPU experiment] Skipping custom post-process effect ${t.id}: ${e instanceof Error?e.message:String(e)}`,e))}}return n},_=new m.WebGpuRenderer({canvas:g,fallbackMaterial:f,materialResolver:(e,t)=>C.resolve(e,t),invokeObjectRenderCallbacks:!1,hiZOcclusionCulling:!0,renderGraphProfiling:!1,sampleCount:1,multiDrawIndirect:!1});_.setRenderScale("taa"===w?D?.renderScale??1:1),_.setRenderGraphBuilder((e,t)=>{const i=new m.FrameGraphBuilder(t),n=i.createColorTarget("hologyNormalRoughness",{format:"rgba16float"}),o=F?i.createColorTarget("hologyMotionVectors",{format:"rgba16float"}):null;for(const e of H)"beforeScene"===e.stage&&e(i,i.sceneColor);i.addDirectionalShadowPass(),i.addPointShadowPass(),i.addSpotShadowPass(),i.addDepthPrepass(),i.addHiZPasses(),i.addVisibilityPass(),i.addMaterialPass({name:"OpaqueGBufferPass",outputs:null==o?[i.sceneColor,n]:[i.sceneColor,n,o],drawStage:"opaque",loadDepth:!0}),i.addHiZHistoryPass("OpaqueGBufferPass");let r=i.sceneColor;if(M?.enabled){const e=M.addPasses(i,{depth:i.mainDepth,normal:n,velocity:o??void 0});r=M.addCompositePass(i,r,e)}null!=O&&(r=O.addPasses(i,{sceneColor:r,depth:i.mainDepth,normalRoughness:n}));const s=new Set(H.flatMap(e=>e.getSceneTextureReads?.()??[]));i.addSceneTextureCopyPass(r,{sceneColor:s.has("sceneColorSnapshot"),sceneDepth:s.has("sceneDepthSnapshot")||N.some(e=>e.enabled&&null!=e.material.uniforms[a])}),r=$(i,"beforeFog",r),i.addLatePass(r);for(const e of H)"beforeScene"!==e.stage&&e(i,r);null!=V&&(r=V.addPasses(i,{sceneColor:r,depth:i.mainDepth,velocity:o??void 0,volumeObjects:t.fogVolumeObjects,lights:t.extractedLights})),null!=A&&null!=o&&(r=A.addPass(i,r,o,i.mainDepth)),null!=E&&(r=E.addPass(i,r)),r=$(i,"beforeOutline",r),null!=I&&(r=I.addPass(i,r)),r=$(i,"beforeDepthOfField",r),null!=k&&(r=k.addPass(i,r,i.mainDepth)),r=$(i,"beforeColorAdjustment",r),null!=W&&(r=W.addPass(i,r)),r=$(i,"beforeAntiAliasing",r);let l="taa"===B.mode&&null!=D?D.addPass(i,r,i.mainDepth,o??void 0):r;"smaa"===B.mode&&null!=R?l=R.addPass(i,l):"fxaa"===B.mode&&null!=G&&(l=G.addPass(i,l)),l=$(i,"beforeOutput",l),i.present(l)});try{return await _.initialize(),new ExperimentalWebGpuBackend(_,f,C,L,E,G,R,M,O,D,A,V,W,k,I,N,H,B,g)}catch(e){throw _.destroy(),f.dispose(),C.dispose(),L.dispose(),M?.dispose(),R?.dispose(),A?.dispose(),V?.dispose(),I?.dispose(),e}}static supportsCamera(t){return t instanceof e.PerspectiveCamera||t instanceof e.OrthographicCamera}setSize(e,t,i,n=1){const o=this.canvas.width,r=this.canvas.height,s=Math.min(1,Math.max(.25,n)),a=s!==this.renderScale;this.renderScale=s,this.renderer.setRenderScale(s),this.taa?.setRenderScale(s),this.renderer.setSize(e,t,i),(a||this.canvas.width!==o||this.canvas.height!==r)&&this.resetViewHistory()}initTexture(e){this.renderer.initTexture(e)}setPostProcessEffects(e){this.postProcessEffects.splice(0,this.postProcessEffects.length,...e);for(const t of e){if("beforeLut"!==t.stage)continue;const e=`${t.id}:${t.stage}`;this.warnedUnsupportedPostProcessStages.has(e)||(this.warnedUnsupportedPostProcessStages.add(e),console.warn(`[Hology WebGPU experiment] Skipping custom post-process effect ${t.id}: stage "${t.stage}" has no native WebGPU anchor.`))}}getDevice(){return this.renderer.getDevice()}setRenderGraphExtensions(e){this.renderGraphExtensions.length=0;for(const t of e)this.renderGraphExtensions.push(t)}setEnableOutlines(e){null!=this.outline&&(this.outline.enabled=e)}setDynamicBatchingEnabled(e){this.renderer.setDynamicBatchingEnabled(e)}setSelectedObjects(e){this.outline?.setSelectedObjects(e)}setSelectedObjectInstances(e){this.outline?.setSelectedInstances(e)}setCascadedShadowRanges(e,t=[]){this.renderer.setCascadedShadowRanges(e,t),this.volumetricFog?.setCascadedDirectionalLights(t)}setWorldEnvironment(t){if(null==t)return void this.nodeMaterialResolver.setWorldEnvironment(null);let i=t.source;if(i.mapping!==e.CubeUVReflectionMapping){let e=this.pmremResults.get(i);null==e&&(this.pmremGenerator??(this.pmremGenerator=this.renderer.createPMREMGenerator()),e=this.pmremGenerator.fromEquirectangular(i),this.pmremResults.set(i,e),this.ownedPmremResults.add(e)),i=e.texture}this.nodeMaterialResolver.setWorldEnvironment({texture:i,intensity:t.intensity})}resetViewHistory(){this.renderer.resetViewHistory(),this.taa?.reset(),this.motionBlur?.reset(),this.gtao?.reset(),this.volumetricFog?.reset()}async compile(e,t){await this.renderer.compileAsync(e,t)}createStaticDrawSet(e,t,i=!1){this.renderer.setHiZOcclusionCullingEnabled(!0);const n=this.renderer.createStaticDrawSet(e,{label:t,onUnsupported:"skip",allowUnsortedLateDraws:i});return 0===n.drawCount?(n.dispose(),null):n}render(e,t,i){const n="taa"===this.antiAliasingState.mode?this.taa:null;this.volumetricFog?.setScene(e),this.renderer.setMotionVectorProjection(t.projectionMatrix),this.motionBlur?.prepareCamera(t,this.canvas.width,this.canvas.height,i),this.motionBlur?.requiresRendererHistoryReset()&&(this.renderer.resetViewHistory(),this.gtao?.reset(),this.volumetricFog?.reset()),n?.prepareCamera(t,this.canvas.width,this.canvas.height),this.motionBlur?.setJitterUv(n?.currentJitterUv.x??0,n?.currentJitterUv.y??0);let o=!1;try{this.renderer.render(e,t,i),o=!0}finally{n?.finishCamera(t),o&&this.motionBlur?.commitCamera()}}setMotionBlurOptions(e){null!=this.motionBlur&&Object.assign(this.motionBlur.options,e)}setGTAOOptions(e){this.gtao?.setOptions(e)}setAntiAliasingMode(e){e!==this.antiAliasingState.mode&&("taa"===e&&null==this.taa||"smaa"===e&&null==this.smaa||"fxaa"===e&&null==this.fxaa?console.warn(`[Hology WebGPU experiment] Anti-aliasing mode "${e}" was not initialized. Set webgpuPostProcessing.antiAliasing in the initial rendering configuration to enable runtime switching.`):(this.antiAliasingState.mode=e,this.resetTemporalHistory()))}setGTAOQuality(e){this.gtao?.applyPreset(e)}setVolumetricFogOptions(e){this.volumetricFog?.setOptions(e)}setVolumetricFogQuality(e){this.volumetricFog?.applyPreset(e)}pushMotionBlurOverride(e){return this.motionBlur?.pushOverride(e)??null}invalidateObjectMotion(e){this.renderer.invalidateObjectMotion(e)}getLastFrameProfile(){return this.renderer.getLastFrameProfile()}setPostProcessState(e){null!=this.colorGrading&&(Object.assign(this.colorGrading.options,e??{enabled:!1}),this.colorGrading.options.enabled=!0===e?.enabled),null!=this.depthOfField&&(this.depthOfField.options.enabled=null!=e?.depthFocus||null!=e?.depthAperture||null!=e?.depthMaxBlur,this.depthOfField.options.focus=e?.depthFocus,this.depthOfField.options.aperture=e?.depthAperture,this.depthOfField.options.maxBlur=e?.depthMaxBlur)}setProfilingEnabled(e){this.renderer.setRenderGraphProfilingEnabled(e)}resetTemporalHistory(){this.taa?.reset(),this.motionBlur?.reset(),this.gtao?.reset(),this.volumetricFog?.reset(),this.renderer.resetViewHistory()}inspect(e,t){return inspectWebGpuScene(e,t,this.nodeMaterialResolver)}destroy(){this.nodeMaterialResolver.dispose(),this.gBufferFallbackSource.dispose(),this.gtao?.dispose(),this.smaa?.dispose(),this.motionBlur?.dispose(),this.volumetricFog?.dispose();for(const e of this.ownedPmremResults)e.dispose();this.ownedPmremResults.clear(),this.renderer.destroy(),this.fallbackMaterial.dispose()}}export function inspectWebGpuScene(e,t,i){const n={meshes:0,skinnedMeshes:0,instancedMeshes:0,batchedMeshes:0,sprites:0,points:0,lines:0,missingPositionGeometry:0,transparentMaterials:0,shaderMaterialsReplaced:0,materialsReplaced:0,unsupportedCamera:ExperimentalWebGpuBackend.supportsCamera(t)?0:1};return e.traverse(e=>{if(e.isSprite)return void n.sprites++;if(e.isPoints)return void n.points++;if(e.isLine)return void n.lines++;if(!e.isMesh)return;const t=e;t.isBatchedMesh?n.batchedMeshes++:t.isInstancedMesh?n.instancedMeshes++:t.isSkinnedMesh?n.skinnedMeshes++:n.meshes++,null==t.geometry?.getAttribute("position")&&n.missingPositionGeometry++;const o=Array.isArray(t.material)?t.material:[t.material];for(const e of o){if(null==e)continue;e.transparent&&n.transparentMaterials++;const t=!0===e.isWGSLShaderMaterial||!0===e.isLineMaterial,o=!t&&!0===i?.canResolve(e);t||o||(n.materialsReplaced++,e.isShaderMaterial&&n.shaderMaterialsReplaced++)}}),n}export function formatWebGpuSceneCapabilitySummary(e){return`[Hology WebGPU experiment] ${JSON.stringify(e)}`}const h="\nstruct VertexInput {\n @location(0) position: vec3<f32>,\n @location(1) normal: vec3<f32>,\n @location(2) uv: vec2<f32>,\n @location(3) color: vec4<f32>,\n#ifdef USE_SKINNING\n @location(4) skinIndex: vec4<u32>,\n @location(5) skinWeight: vec4<f32>,\n#endif\n @builtin(instance_index) instanceIndex: u32,\n};\n\nstruct VertexOutput {\n @builtin(position) position: vec4<f32>,\n @location(0) worldPosition: vec3<f32>,\n @location(1) worldNormal: vec3<f32>,\n @location(2) color: vec4<f32>,\n};\n\n@vertex\nfn vsMain(input: VertexInput) -> VertexOutput {\n var output: VertexOutput;\n#ifdef USE_SKINNING\n let localPosition = rendererSkinPosition(input.position, input.skinIndex, input.skinWeight);\n let localNormal = rendererSkinNormal(input.normal, input.skinIndex, input.skinWeight);\n#else\n let localPosition = input.position;\n let localNormal = input.normal;\n#endif\n let world = rendererWorldPosition(localPosition, input.instanceIndex);\n output.position = rendererClipPosition(localPosition, input.instanceIndex);\n output.worldPosition = world.xyz;\n output.worldNormal = rendererWorldNormal(localNormal, input.instanceIndex);\n output.color = input.color * rendererObjectColor(input.instanceIndex);\n return output;\n}\n",u="\nstruct VertexOutput {\n @builtin(position) position: vec4<f32>,\n @location(0) worldPosition: vec3<f32>,\n @location(1) worldNormal: vec3<f32>,\n @location(2) color: vec4<f32>,\n};\n\nfn distanceAttenuation(distanceToLight: f32, cutoffDistance: f32, decayExponent: f32) -> f32 {\n var distanceFalloff = 1.0 / max(pow(distanceToLight, decayExponent), 0.01);\n if (cutoffDistance > 0.0) {\n let ratio = distanceToLight / cutoffDistance;\n let cutoff = clamp(1.0 - ratio * ratio * ratio * ratio, 0.0, 1.0);\n distanceFalloff *= cutoff * cutoff;\n }\n return distanceFalloff;\n}\n\n@fragment\nfn fsMain(input: VertexOutput) -> @location(0) vec4<f32> {\n let normal = normalize(input.worldNormal);\n let normalTint = normal * 0.5 + vec3<f32>(0.5);\n let base = mix(vec3<f32>(0.52, 0.58, 0.64), normalTint, 0.22) * input.color.rgb;\n var lighting = rendererIndirectDiffuse(normal) + vec3<f32>(0.08);\n var directionalLighting = vec3<f32>(0.0);\n let directionalCount = rendererDirectionalLightCount();\n\n for (var index: u32 = 0u; index < directionalCount; index = index + 1u) {\n let lightDirection = rendererDirectionalLightDirectionForLight(index);\n let shadow = rendererSampleDirectionalShadowForLightWithNormal(index, input.worldPosition, normal);\n directionalLighting += rendererDirectionalLightColorForLight(index) * max(dot(normal, lightDirection), 0.0) * shadow;\n }\n if (directionalCount > 0u) {\n lighting += directionalLighting / f32(directionalCount);\n }\n\n for (var index: u32 = 0u; index < rendererPointLightCount(); index = index + 1u) {\n let lightVector = rendererPointLightPositionForLight(index) - input.worldPosition;\n let distanceToLight = length(lightVector);\n let lightDirection = normalize(lightVector);\n let shadow = rendererSamplePointShadowForLightWithNormal(index, input.worldPosition, normal);\n lighting += rendererPointLightColorForLight(index) * max(dot(normal, lightDirection), 0.0) * distanceAttenuation(distanceToLight, rendererPointLightRangeForLight(index), rendererPointLightDecayForLight(index)) * shadow;\n }\n\n for (var index: u32 = 0u; index < rendererSpotLightCount(); index = index + 1u) {\n let lightVector = rendererSpotLightPositionForLight(index) - input.worldPosition;\n let distanceToLight = length(lightVector);\n let lightDirection = normalize(lightVector);\n let angleCos = dot(-lightDirection, rendererSpotLightDirectionForLight(index));\n let cone = smoothstep(rendererSpotLightConeCosForLight(index), rendererSpotLightPenumbraCosForLight(index), angleCos);\n let shadow = rendererSampleSpotShadowForLightWithNormal(index, input.worldPosition, normal);\n lighting += rendererSpotLightColorForLight(index) * max(dot(normal, lightDirection), 0.0) * cone * distanceAttenuation(distanceToLight, rendererSpotLightRangeForLight(index), rendererSpotLightDecayForLight(index)) * shadow;\n }\n\n return vec4<f32>(base * max(lighting, vec3<f32>(0.08)), input.color.a);\n}\n";/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
package/dist/rendering.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { CSM } from "three/examples/jsm/csm/CSM.js";
|
|
|
6
6
|
import { GTAOPass } from "three/examples/jsm/postprocessing/GTAOPass.js";
|
|
7
7
|
import { OutlinePass } from './utils/three/outline-pass.js';
|
|
8
8
|
import { type PostProcessSettings, type PostProcessVolume } from "./gameplay/actors/builtin/post-process-volume-actor.js";
|
|
9
|
+
import type { WaterPostProcessVolume } from './gameplay/actors/builtin/components/water-post-process-settings.js';
|
|
9
10
|
import { type PostProcessEffect, type PostProcessEffectOptions } from "./rendering/post-process-effect.js";
|
|
10
11
|
import { Cluster } from "./rendering/cluster-generator.js";
|
|
11
12
|
import { LightVolume } from "./rendering/light-probes/light-volume-capture.js";
|
|
@@ -163,6 +164,10 @@ export declare class RenderingView {
|
|
|
163
164
|
get fpsCap(): number | null;
|
|
164
165
|
set fpsCap(fps: number | null | undefined);
|
|
165
166
|
postProcessVolumes: PostProcessVolume[];
|
|
167
|
+
private readonly waterPostProcessVolumes;
|
|
168
|
+
private readonly underwaterPostProcessEffects;
|
|
169
|
+
private activeUnderwaterPostProcessEffect;
|
|
170
|
+
private activeWaterPostProcessVolume;
|
|
166
171
|
postProcessSettings: PostProcessSettings;
|
|
167
172
|
baseToneMapping: THREE.ToneMapping;
|
|
168
173
|
baseToneMappingExposure: number;
|
|
@@ -244,6 +249,9 @@ export declare class RenderingView {
|
|
|
244
249
|
private previousPresentationPixelRatio;
|
|
245
250
|
resizeRender(force?: boolean): void;
|
|
246
251
|
addPostProcessVolume(volume: PostProcessVolume): void;
|
|
252
|
+
addWaterPostProcessVolume(volume: WaterPostProcessVolume): void;
|
|
253
|
+
removeWaterPostProcessVolume(volume: WaterPostProcessVolume): void;
|
|
254
|
+
private updateUnderwaterPostProcessing;
|
|
247
255
|
removePostProcessVolume(volume: PostProcessVolume): void;
|
|
248
256
|
addPostProcessEffect(source: PostProcessMaterialSource, options?: PostProcessEffectOptions): PostProcessEffect;
|
|
249
257
|
private syncExperimentalWebGpuPostProcessEffects;
|