@kitware/vtk.js 25.3.0 → 25.6.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.
Files changed (38) hide show
  1. package/IO/Core/DataAccessHelper/HtmlDataAccessHelper.js +4 -8
  2. package/IO/Core/DataAccessHelper/HttpDataAccessHelper.js +6 -14
  3. package/IO/Core/DataAccessHelper/JSZipDataAccessHelper.js +60 -57
  4. package/IO/Core/ZipMultiDataSetReader.js +19 -29
  5. package/IO/Core/ZipMultiDataSetWriter.js +7 -23
  6. package/IO/Misc/SkyboxReader.js +67 -75
  7. package/IO/XML/XMLReader.js +2 -2
  8. package/IO/XML/XMLWriter.js +2 -2
  9. package/Interaction/Style/InteractorStyleTrackballCamera.js +16 -0
  10. package/README.md +2 -2
  11. package/Rendering/Core/Actor2D.d.ts +12 -6
  12. package/Rendering/Core/ColorTransferFunction/ColorMaps.d.ts +24 -0
  13. package/Rendering/Core/Property2D.d.ts +1 -1
  14. package/Rendering/Core/Renderer.d.ts +43 -3
  15. package/Rendering/Core/Renderer.js +5 -1
  16. package/Rendering/Core/Texture.d.ts +22 -0
  17. package/Rendering/Core/Texture.js +159 -10
  18. package/Rendering/Core/VolumeProperty.d.ts +4 -4
  19. package/Rendering/Core/VolumeProperty.js +1 -1
  20. package/Rendering/OpenGL/RenderWindow/ContextProxy.js +65 -0
  21. package/Rendering/OpenGL/RenderWindow.js +3 -1
  22. package/Rendering/WebGPU/CellArrayMapper.js +43 -13
  23. package/Rendering/WebGPU/ForwardPass.js +93 -15
  24. package/Rendering/WebGPU/FullScreenQuad.js +2 -1
  25. package/Rendering/WebGPU/OpaquePass.js +1 -1
  26. package/Rendering/WebGPU/OrderIndependentTranslucentPass.js +1 -1
  27. package/Rendering/WebGPU/RenderEncoder.js +9 -5
  28. package/Rendering/WebGPU/RenderWindow.js +15 -13
  29. package/Rendering/WebGPU/Renderer.js +77 -5
  30. package/Rendering/WebGPU/Sampler.js +4 -0
  31. package/Rendering/WebGPU/Texture.js +78 -46
  32. package/Rendering/WebGPU/TextureManager.js +5 -3
  33. package/Rendering/WebGPU/TextureView.js +15 -2
  34. package/Rendering/WebGPU/VolumePass.js +1 -1
  35. package/index.d.ts +1 -0
  36. package/index.js +0 -2
  37. package/package.json +4 -5
  38. package/ThirdParty/index.js +0 -9
@@ -18,7 +18,7 @@ var ScalarMode = vtkMapper.ScalarMode;
18
18
  var CoordinateSystem = vtkProp.CoordinateSystem;
19
19
  var DisplayLocation = vtkProperty2D.DisplayLocation;
20
20
  var vtkWebGPUPolyDataVS = "\n//VTK::Renderer::Dec\n\n//VTK::Color::Dec\n\n//VTK::Normal::Dec\n\n//VTK::TCoord::Dec\n\n//VTK::Select::Dec\n\n//VTK::Mapper::Dec\n\n//VTK::IOStructs::Dec\n\n@vertex\nfn main(\n//VTK::IOStructs::Input\n)\n//VTK::IOStructs::Output\n{\n var output : vertexOutput;\n\n var vertex: vec4<f32> = vertexBC;\n\n //VTK::Color::Impl\n\n //VTK::Normal::Impl\n\n //VTK::TCoord::Impl\n\n //VTK::Select::Impl\n\n //VTK::Position::Impl\n\n return output;\n}\n";
21
- var vtkWebGPUPolyDataFS = "\nstruct PBRData {\n diffuse: vec3<f32>,\n specular: vec3<f32>,\n}\n\n// Dot product with the max already in it\nfn mdot(a: vec3<f32>, b: vec3<f32>) -> f32 {\n return max(0.0, dot(a, b));\n}\n\n// Lambertian diffuse model\nfn lambertDiffuse(base: vec3<f32>, N: vec3<f32>, L: vec3<f32>) -> vec3<f32> {\n var pi: f32 = 3.14159265359; \n var NdotL: f32 = mdot(N, L);\n NdotL = pow(NdotL, 1.5);\n return (base/pi)*NdotL;\n}\n\n// Yasuhiro Fujii improvement on the Oren-Nayar model\n// https://mimosa-pudica.net/improved-oren-nayar.html\n// p is surface color, o is roughness\nfn fujiiOrenNayar(p: vec3<f32>, o: f32, N: vec3<f32>, L: vec3<f32>, V: vec3<f32>) -> vec3<f32> {\n var invpi: f32 = 0.31830988618; // 1/pi\n\n var o2 = o*o;\n var NdotL: f32 = mdot(N, L);\n NdotL = pow(NdotL, 1.5); // Less physically accurate, but hides the \"seams\" between lights better\n\n var NdotV: f32 = mdot(N, V);\n var LdotV: f32 = mdot(L, V);\n\n var s: f32 = LdotV - NdotL*NdotV;\n var t: f32 = mix(1, max(NdotL, NdotV), step(0, s)); // Mix with step is the equivalent of an if statement\n var A: vec3<f32> = 0.5*(o2 / (o2 + 0.33)) + 0.17*p*(o2 / (o2 + 0.13));\n A = invpi*(1 - A);\n var B: f32 = 0.45*(o2 / (o2 + 0.09));\n B = invpi*B;\n\n return p*NdotL*(A + B*(s/t));\n}\n\n// Fresnel portion of BRDF (IOR only, simplified)\nfn schlickFresnelIOR(V: vec3<f32>, N: vec3<f32>, ior: f32, k: f32) -> f32 {\n var NdotV: f32 = mdot(V, N);\n // var R0: f32 = pow((ior - 1.0) / (ior + 1.0), 2); // 1.0 is about the ior of air, and it is assumed that light will be traveling through air\n var F0: f32 = (pow((ior - 1.0), 2) + k*k) / (pow((ior + 1.0), 2) + k*k); // This takes into account the roughness, whic the other one does not\n return F0 + (1 - F0) * pow((1-NdotV), 5); \n}\n\n// Fresnel portion of BRDF (Color ior, better)\nfn schlickFresnelRGB(V: vec3<f32>, N: vec3<f32>, F0: vec3<f32>) -> vec3<f32> {\n var NdotV: f32 = mdot(V, N);\n return F0 + (1 - F0) * pow((1-NdotV), 5); \n}\n\n// Normal portion of BRDF\n// https://learnopengl.com/PBR/Theory\n// Trowbridge-Reitz GGX functions: normal, halfway, roughness^2\nfn trGGX(N: vec3<f32>, H: vec3<f32>, a: f32) -> f32 {\n var pi: f32 = 3.14159265359; \n\n var a2: f32 = a*a;\n var NdotH = mdot(N, H);\n var NdotH2 = NdotH*NdotH;\n \n var denom: f32 = NdotH2 * (a2 - 1.0) + 1.0;\n\n return a2 / max((pi*denom*denom), 0.000001);\n}\n\n// A VERY bad approximation of anisotropy. Real anisotropic calculations require tangent and bitangent\nfn anisotrophicTrGGX(N: vec3<f32>, H: vec3<f32>, O: vec3<f32>, s: f32, a: f32) -> f32 {\n var Op: vec3<f32> = (rendererUBO.WCVCNormals * vec4<f32>(normalize(O) * s, 0.)).xyz;\n\n var ggx1: f32 = trGGX(N + Op*s, H, a);\n var ggx2: f32 = trGGX(N - Op*s, H, a);\n return (0.5 * ggx1 + 0.5 * ggx2);\n}\n\n// Geometry portion of BRDF\nfn schlickGGX(N: vec3<f32>, X: vec3<f32>, k: f32) -> f32 {\n var NdotX = mdot(N, X);\n return NdotX / max(0.000001, (NdotX*(1-k) + k));\n}\n\nfn smithSurfaceRoughness(N: vec3<f32>, V: vec3<f32>, L: vec3<f32>, k: f32) -> f32 {\n var ggx1: f32 = max(0.01, schlickGGX(N, V, k)); // Prevents void zones at the cost of some accuracy\n var ggx2: f32 = schlickGGX(N, L, k);\n return ggx1*ggx2;\n}\n\n// BRDF Combination\nfn cookTorrance(D: f32, F: f32, G: f32, N: vec3<f32>, V: vec3<f32>, L: vec3<f32>) -> f32 {\n var num: f32 = D*F*G;\n var denom: f32 = 4*mdot(V, N)*mdot(L, N);\n\n return num / max(denom, 0.000001);\n}\n\n// Different lighting calculations for different light sources\nfn calcDirectionalLight(N: vec3<f32>, V: vec3<f32>, ior: f32, roughness: f32, metallic: f32, direction: vec3<f32>, color: vec3<f32>, base: vec3<f32>) -> PBRData { \n var L: vec3<f32> = normalize(direction); // Light Vector\n var H: vec3<f32> = normalize(L + V); // Halfway Vector\n\n var alpha = roughness*roughness;\n var k: f32 = alpha*alpha / 2;\n\n var D: f32 = trGGX(N, H, alpha); // Distribution\n // var F: f32 = schlickFresnelIOR(V, N, ior, k); // Fresnel\n var G: f32 = smithSurfaceRoughness(N, V, L, k); // Geometry\n\n var brdf: f32 = cookTorrance(D, 1, G, N, V, L); // Fresnel term is replaced with 1 because it is added later\n var incoming: vec3<f32> = color;\n var angle: f32 = mdot(L, N);\n angle = pow(angle, 1.5);\n\n var specular: vec3<f32> = brdf*incoming*angle;\n // Oren-Nayar gives a clay-like effect when fully rough which some people may not want, so it might be better to give a separate\n // control property for the diffuse vs specular roughness\n var diffuse: vec3<f32> = incoming*fujiiOrenNayar(base, roughness, N, L, V); \n // Stores the specular and diffuse separately to allow for finer post processing\n // Could also be done (propably more properly) with a struct\n var out = PBRData(diffuse, specular);\n \n return out; // Returns angle along with color of light so the final color can be multiplied by angle as well (creates black areas)\n}\n\n// TODO: find some way to reduce the number of arguments going in here\nfn calcPointLight(N: vec3<f32>, V: vec3<f32>, fragPos: vec3<f32>, ior: f32, roughness: f32, metallic: f32, position: vec3<f32>, color: vec3<f32>, base: vec3<f32>) -> PBRData {\n var L: vec3<f32> = normalize(position - fragPos); // Light Vector\n var H: vec3<f32> = normalize(L + V); // Halfway Vector\n var dist = distance(position, fragPos);\n\n var alpha = roughness*roughness;\n var k: f32 = alpha*alpha / 2; // could also be pow(alpha + 1.0, 2) / 8\n\n var D: f32 = trGGX(N, H, alpha); // Distribution\n // var F: f32 = schlickFresnelIOR(V, N, ior, k); // Fresnel\n var G: f32 = smithSurfaceRoughness(N, V, L, k); // Geometry\n\n var brdf: f32 = cookTorrance(D, 1, G, N, V, L); \n var incoming: vec3<f32> = color * (1. / (dist*dist));\n var angle: f32 = mdot(L, N);\n angle = pow(angle, 1.5); // Smoothing factor makes it less accurate, but reduces ugly \"seams\" bewteen light sources\n\n var specular: vec3<f32> = brdf*incoming*angle;\n var diffuse: vec3<f32> = incoming*fujiiOrenNayar(base, roughness, N, L, V);\n\n // Stores the specular and diffuse separately to allow for finer post processing\n // Could also be done (propably more properly) with a struct\n var out = PBRData(diffuse, specular);\n \n return out; // Returns angle along with color of light so the final color can be multiplied by angle as well (creates black areas)\n}\n\n// For a reason unknown to me, spheres dont seem to behave propperly with head-on spot lights\nfn calcSpotLight(N: vec3<f32>, V: vec3<f32>, fragPos: vec3<f32>, ior: f32, roughness: f32, metallic: f32, position: vec3<f32>, direction: vec3<f32>, cones: vec2<f32>, color: vec3<f32>, base: vec3<f32>) -> PBRData {\n var L: vec3<f32> = normalize(position - fragPos);\n var H: vec3<f32> = normalize(L + V); // Halfway Vector\n var dist = distance(position, fragPos);\n\n var alpha = roughness*roughness;\n var k: f32 = alpha*alpha / 2; // could also be pow(alpha + 1.0, 2) / 8\n\n var D: f32 = trGGX(N, H, alpha); // Distribution\n // var F: f32 = schlickFresnelIOR(V, N, ior, k); // Fresnel\n var G: f32 = smithSurfaceRoughness(N, V, L, k); // Geometry\n\n var brdf: f32 = cookTorrance(D, 1, G, N, V, L); \n \n // Cones.x is the inner phi and cones.y is the outer phi\n var theta: f32 = mdot(normalize(direction), L);\n var epsilon: f32 = cones.x - cones.y;\n var intensity: f32 = (theta - cones.y) / epsilon;\n intensity = clamp(intensity, 0.0, 1.0);\n intensity /= dist*dist;\n\n var incoming: vec3<f32> = color * intensity;\n\n var angle: f32 = mdot(L, N);\n angle = pow(angle, 1.5); // Smoothing factor makes it less accurate, but reduces ugly \"seams\" bewteen light sources\n\n var specular: vec3<f32> = brdf*incoming*angle;\n var diffuse: vec3<f32> = incoming*fujiiOrenNayar(base, roughness, N, L, V);\n\n // Stores the specular and diffuse separately to allow for finer post processing\n // Could also be done (propably more properly) with a struct\n var out = PBRData(diffuse, specular);\n \n return out; // Returns angle along with color of light so the final color can be multiplied by angle as well (creates black areas)\n}\n\n// Environment mapping stuff\n// Takes in a vector and converts it to an equivalent coordinate in a rectilinear texture. Should be replaced with cubemaps at some point\nfn vecToRectCoord(dir: vec3<f32>) -> vec2<f32> {\n var tau: f32 = 6.28318530718;\n var out: vec2<f32> = vec2<f32>(0.);\n\n out.x = atan2(dir.z, dir.x) / tau;\n out.x += 0.5;\n\n out.y = (dir.y * .5) + .5;\n\n return out;\n}\n\n//VTK::Renderer::Dec\n\n//VTK::Color::Dec\n\n//VTK::TCoord::Dec\n\n// optional surface normal declaration\n//VTK::Normal::Dec\n\n//VTK::Select::Dec\n\n//VTK::RenderEncoder::Dec\n\n//VTK::Mapper::Dec\n\n//VTK::IOStructs::Dec\n\n@fragment\nfn main(\n//VTK::IOStructs::Input\n)\n//VTK::IOStructs::Output\n{\n var output : fragmentOutput;\n\n // Temporary ambient, diffuse, and opacity\n var ambientColor: vec4<f32> = mapperUBO.AmbientColor;\n var diffuseColor: vec4<f32> = mapperUBO.DiffuseColor;\n var opacity: f32 = mapperUBO.Opacity;\n\n // This should be declared somewhere else\n var _diffuseMap: vec4<f32> = vec4<f32>(1);\n var _roughnessMap: vec4<f32> = vec4<f32>(1);\n var _metallicMap: vec4<f32> = vec4<f32>(1);\n var _normalMap: vec4<f32> = vec4<f32>(0, 0, 1, 0); // normal map was setting off the normal vector detection in fragment\n var _ambientOcclusionMap: vec4<f32> = vec4<f32>(0);\n var _emissionMap: vec4<f32> = vec4<f32>(0);\n\n //VTK::Color::Impl\n\n //VTK::TCoord::Impl\n\n //VTK::Normal::Impl\n\n var computedColor: vec4<f32> = vec4<f32>(diffuseColor.rgb, 1.);\n\n //VTK::Light::Impl\n\n //VTK::Select::Impl\n\n if (computedColor.a == 0.0) { discard; };\n\n //VTK::Position::Impl\n\n //VTK::RenderEncoder::Impl\n\n return output;\n}\n";
21
+ var vtkWebGPUPolyDataFS = "\nstruct PBRData {\n diffuse: vec3<f32>,\n specular: vec3<f32>,\n}\n\n// Dot product with the max already in it\nfn mdot(a: vec3<f32>, b: vec3<f32>) -> f32 {\n return max(0.0, dot(a, b));\n}\n// Dot product with a max in it that does not allow for negative values\n// Physically based rendering is accurate as long as normals are accurate,\n// however this is pretty often not the case. In order to prevent negative\n// values from ruining light calculations and creating zones of zero light,\n// this remapping is used, which smoothly clamps the dot product between\n// zero and one while still maintaining a good amount of accuracy.\nfn cdot(a: vec3<f32>, b: vec3<f32>) -> f32 {\n var d: f32 = max(0.0, dot(a, b));\n d = pow((d + 1) / 2.0, 2.6);\n return d;\n}\n\n// Lambertian diffuse model\nfn lambertDiffuse(base: vec3<f32>, N: vec3<f32>, L: vec3<f32>) -> vec3<f32> {\n var pi: f32 = 3.14159265359; \n var NdotL: f32 = mdot(N, L);\n NdotL = pow(NdotL, 1.5);\n return (base/pi)*NdotL;\n}\n\n// Yasuhiro Fujii improvement on the Oren-Nayar model\n// https://mimosa-pudica.net/improved-oren-nayar.html\n// p is surface color, o is roughness\nfn fujiiOrenNayar(p: vec3<f32>, o: f32, N: vec3<f32>, L: vec3<f32>, V: vec3<f32>) -> vec3<f32> {\n var invpi: f32 = 0.31830988618; // 1/pi\n\n var o2 = o*o;\n var NdotL: f32 = mdot(N, L);\n NdotL = pow(NdotL, 1.5); // Less physically accurate, but hides the \"seams\" between lights better\n\n var NdotV: f32 = mdot(N, V);\n var LdotV: f32 = mdot(L, V);\n\n var s: f32 = LdotV - NdotL*NdotV;\n var t: f32 = mix(1, max(NdotL, NdotV), step(0, s)); // Mix with step is the equivalent of an if statement\n var A: vec3<f32> = 0.5*(o2 / (o2 + 0.33)) + 0.17*p*(o2 / (o2 + 0.13));\n A = invpi*(1 - A);\n var B: f32 = 0.45*(o2 / (o2 + 0.09));\n B = invpi*B;\n\n return p*NdotL*(A + B*(s/t));\n}\n\n// Fresnel portion of BRDF (IOR only, simplified)\nfn schlickFresnelIOR(V: vec3<f32>, N: vec3<f32>, ior: f32, k: f32) -> f32 {\n var NdotV: f32 = mdot(V, N);\n var F0: f32 = (pow((ior - 1.0), 2) + k*k) / (pow((ior + 1.0), 2) + k*k); // This takes into account the roughness, which the other one does not\n return F0 + (1 - F0) * pow((1-NdotV), 5); \n}\n\n// Fresnel portion of BRDF (Color ior, better)\nfn schlickFresnelRGB(V: vec3<f32>, N: vec3<f32>, F0: vec3<f32>) -> vec3<f32> {\n var NdotV: f32 = mdot(V, N);\n return F0 + (1 - F0) * pow((1-NdotV), 5); \n}\n\n// Normal portion of BRDF\n// https://learnopengl.com/PBR/Theory\n// Trowbridge-Reitz GGX functions: normal, halfway, roughness^2\nfn trGGX(N: vec3<f32>, H: vec3<f32>, a: f32) -> f32 {\n var pi: f32 = 3.14159265359; \n\n var a2: f32 = a*a;\n var NdotH = mdot(N, H);\n var NdotH2 = NdotH*NdotH;\n \n var denom: f32 = NdotH2 * (a2 - 1.0) + 1.0;\n\n return a2 / max((pi*denom*denom), 0.000001);\n}\n\n// A VERY bad approximation of anisotropy. Real anisotropic calculations require tangent and bitangent\nfn anisotrophicTrGGX(N: vec3<f32>, H: vec3<f32>, O: vec3<f32>, s: f32, a: f32) -> f32 {\n var Op: vec3<f32> = (rendererUBO.WCVCNormals * vec4<f32>(normalize(O) * s, 0.)).xyz;\n\n var ggx1: f32 = trGGX(N + Op*s, H, a);\n var ggx2: f32 = trGGX(N - Op*s, H, a);\n return (0.5 * ggx1 + 0.5 * ggx2);\n}\n\n// Geometry portion of BRDF\nfn schlickGGX(N: vec3<f32>, X: vec3<f32>, k: f32) -> f32 {\n var NdotX = cdot(N, X);\n return NdotX / max(0.000001, (NdotX*(1-k) + k));\n}\n\nfn smithSurfaceRoughness(N: vec3<f32>, V: vec3<f32>, L: vec3<f32>, k: f32) -> f32 {\n var ggx1: f32 = min(1, schlickGGX(N, V, k));\n var ggx2: f32 = min(1, schlickGGX(N, L, k));\n return ggx1*ggx2;\n}\n\n// BRDF Combination\nfn cookTorrance(D: f32, F: f32, G: f32, N: vec3<f32>, V: vec3<f32>, L: vec3<f32>) -> f32 {\n var num: f32 = D*F*G;\n var denom: f32 = 4*cdot(V, N)*cdot(L, N);\n\n return num / max(denom, 0.000001);\n}\n\n// Different lighting calculations for different light sources\nfn calcDirectionalLight(N: vec3<f32>, V: vec3<f32>, ior: f32, roughness: f32, metallic: f32, direction: vec3<f32>, color: vec3<f32>, base: vec3<f32>) -> PBRData { \n var L: vec3<f32> = normalize(direction); // Light Vector\n var H: vec3<f32> = normalize(L + V); // Halfway Vector\n\n var alpha = roughness*roughness;\n var k: f32 = alpha*alpha / 2;\n\n var D: f32 = trGGX(N, H, alpha); // Distribution\n // var F: f32 = schlickFresnelIOR(V, N, ior, k); // Fresnel\n var G: f32 = smithSurfaceRoughness(N, V, L, k); // Geometry\n\n var brdf: f32 = cookTorrance(D, 1, G, N, V, L); // Fresnel term is replaced with 1 because it is added later\n var incoming: vec3<f32> = color;\n var angle: f32 = mdot(L, N);\n angle = pow(angle, 1.5);\n\n var specular: vec3<f32> = brdf*incoming*angle;\n // Oren-Nayar gives a clay-like effect when fully rough which some people may not want, so it might be better to give a separate\n // control property for the diffuse vs specular roughness\n var diffuse: vec3<f32> = incoming*fujiiOrenNayar(base, roughness, N, L, V); \n // Stores the specular and diffuse separately to allow for finer post processing\n var out = PBRData(diffuse, specular);\n \n return out; // Returns angle along with color of light so the final color can be multiplied by angle as well (creates black areas)\n}\n\n// TODO: find some way to reduce the number of arguments going in here\nfn calcPointLight(N: vec3<f32>, V: vec3<f32>, fragPos: vec3<f32>, ior: f32, roughness: f32, metallic: f32, position: vec3<f32>, color: vec3<f32>, base: vec3<f32>) -> PBRData {\n var L: vec3<f32> = normalize(position - fragPos); // Light Vector\n var H: vec3<f32> = normalize(L + V); // Halfway Vector\n var dist = distance(position, fragPos);\n\n var alpha = roughness*roughness;\n var k: f32 = alpha*alpha / 2; // could also be pow(alpha + 1.0, 2) / 8\n\n var D: f32 = trGGX(N, H, alpha); // Distribution\n // var F: f32 = schlickFresnelIOR(V, N, ior, k); // Fresnel\n var G: f32 = smithSurfaceRoughness(N, V, L, k); // Geometry\n\n var brdf: f32 = cookTorrance(D, 1, G, N, V, L); \n var incoming: vec3<f32> = color * (1. / (dist*dist));\n var angle: f32 = mdot(L, N);\n angle = pow(angle, 1.5); // Smoothing factor makes it less accurate, but reduces ugly \"seams\" bewteen light sources\n\n var specular: vec3<f32> = brdf*incoming*angle;\n var diffuse: vec3<f32> = incoming*fujiiOrenNayar(base, roughness, N, L, V);\n\n // Stores the specular and diffuse separately to allow for finer post processing\n // Could also be done (propably more properly) with a struct\n var out = PBRData(diffuse, specular);\n \n return out; // Returns angle along with color of light so the final color can be multiplied by angle as well (creates black areas)\n}\n\n// For a reason unknown to me, spheres dont seem to behave propperly with head-on spot lights\nfn calcSpotLight(N: vec3<f32>, V: vec3<f32>, fragPos: vec3<f32>, ior: f32, roughness: f32, metallic: f32, position: vec3<f32>, direction: vec3<f32>, cones: vec2<f32>, color: vec3<f32>, base: vec3<f32>) -> PBRData {\n var L: vec3<f32> = normalize(position - fragPos);\n var H: vec3<f32> = normalize(L + V); // Halfway Vector\n var dist = distance(position, fragPos);\n\n var alpha = roughness*roughness;\n var k: f32 = alpha*alpha / 2; // could also be pow(alpha + 1.0, 2) / 8\n\n var D: f32 = trGGX(N, H, alpha); // Distribution\n // var F: f32 = schlickFresnelIOR(V, N, ior, k); // Fresnel\n var G: f32 = smithSurfaceRoughness(N, V, L, k); // Geometry\n\n var brdf: f32 = cookTorrance(D, 1, G, N, V, L); \n \n // Cones.x is the inner phi and cones.y is the outer phi\n var theta: f32 = mdot(normalize(direction), L);\n var epsilon: f32 = cones.x - cones.y;\n var intensity: f32 = (theta - cones.y) / epsilon;\n intensity = clamp(intensity, 0.0, 1.0);\n intensity /= dist*dist;\n\n var incoming: vec3<f32> = color * intensity;\n\n var angle: f32 = mdot(L, N);\n angle = pow(angle, 1.5); // Smoothing factor makes it less accurate, but reduces ugly \"seams\" bewteen light sources\n\n var specular: vec3<f32> = brdf*incoming*angle;\n var diffuse: vec3<f32> = incoming*fujiiOrenNayar(base, roughness, N, L, V);\n\n // Stores the specular and diffuse separately to allow for finer post processing\n // Could also be done (propably more properly) with a struct\n var out = PBRData(diffuse, specular);\n \n return out; // Returns angle along with color of light so the final color can be multiplied by angle as well (creates black areas)\n}\n\n// Environment mapping stuff\n// Takes in a vector and converts it to an equivalent coordinate in a rectilinear texture. Should be replaced with cubemaps at some point\nfn vecToRectCoord(dir: vec3<f32>) -> vec2<f32> {\n var tau: f32 = 6.28318530718;\n var pi: f32 = 3.14159265359;\n var out: vec2<f32> = vec2<f32>(0.0);\n\n out.x = atan2(dir.z, dir.x) / tau;\n out.x += 0.5;\n\n var phix: f32 = length(vec2(dir.x, dir.z));\n out.y = atan2(dir.y, phix) / pi + 0.5;\n\n return out;\n}\n\n//VTK::Renderer::Dec\n\n//VTK::Color::Dec\n\n//VTK::TCoord::Dec\n\n// optional surface normal declaration\n//VTK::Normal::Dec\n\n//VTK::Select::Dec\n\n//VTK::RenderEncoder::Dec\n\n//VTK::Mapper::Dec\n\n//VTK::IOStructs::Dec\n\n@fragment\nfn main(\n//VTK::IOStructs::Input\n)\n//VTK::IOStructs::Output\n{\n var output : fragmentOutput;\n\n // Temporary ambient, diffuse, and opacity\n var ambientColor: vec4<f32> = mapperUBO.AmbientColor;\n var diffuseColor: vec4<f32> = mapperUBO.DiffuseColor;\n var opacity: f32 = mapperUBO.Opacity;\n\n // This should be declared somewhere else\n var _diffuseMap: vec4<f32> = vec4<f32>(1);\n var _roughnessMap: vec4<f32> = vec4<f32>(1);\n var _metallicMap: vec4<f32> = vec4<f32>(1);\n var _normalMap: vec4<f32> = vec4<f32>(0, 0, 1, 0); // normal map was setting off the normal vector detection in fragment\n var _ambientOcclusionMap: vec4<f32> = vec4<f32>(1);\n var _emissionMap: vec4<f32> = vec4<f32>(0);\n\n //VTK::Color::Impl\n\n //VTK::TCoord::Impl\n\n //VTK::Normal::Impl\n\n var computedColor: vec4<f32> = vec4<f32>(diffuseColor.rgb, 1.0);\n\n //VTK::Light::Impl\n\n //VTK::Select::Impl\n\n if (computedColor.a == 0.0) { discard; };\n\n //VTK::Position::Impl\n\n //VTK::RenderEncoder::Impl\n\n return output;\n}\n";
22
22
 
23
23
  function isEdges(hash) {
24
24
  // edge pipelines have "edge" in them
@@ -209,7 +209,7 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
209
209
  code = fDesc.getCode();
210
210
 
211
211
  if (actor.getProperty().getNormalTexture()) {
212
- code = vtkWebGPUShaderCache.substitute(code, '//VTK::Normal::Impl', [' var normal: vec3<f32> = input.normalVC;', ' if (!input.frontFacing) { normal = -normal; }', ' var tangent: vec3<f32> = input.tangentVC;', ' var bitangent: vec3<f32> = input.bitangentVC;', ' var TCVCMatrix: mat3x3<f32> = mat3x3<f32>(', ' tangent.x, bitangent.x, normal.x,', ' tangent.y, bitangent.y, normal.y,', ' tangent.z, bitangent.z, normal.z,', ' );', ' normal = TCVCMatrix * (_normalMap.xyz * 2 - 1);', ' normal = mix(input.normalVC, normal, mapperUBO.NormalStrength);', ' normal = normalize(normal);']).result;
212
+ code = vtkWebGPUShaderCache.substitute(code, '//VTK::Normal::Impl', [' var normal: vec3<f32> = input.normalVC;', ' if (!input.frontFacing) { normal = -normal; }', ' var tangent: vec3<f32> = input.tangentVC;', ' var bitangent: vec3<f32> = input.bitangentVC;', ' var TCVCMatrix: mat3x3<f32> = mat3x3<f32>(', ' tangent.x, bitangent.x, normal.x,', ' tangent.y, bitangent.y, normal.y,', ' tangent.z, bitangent.z, normal.z,', ' );', ' var mappedNormal: vec3<f32> = TCVCMatrix * (_normalMap.xyz * 2 - 1);', ' normal = mix(normal, mappedNormal, mapperUBO.NormalStrength);', ' normal = normalize(normal);']).result;
213
213
  } else {
214
214
  code = vtkWebGPUShaderCache.substitute(code, '//VTK::Normal::Impl', [' var normal: vec3<f32> = input.normalVC;', ' if (!input.frontFacing) { normal = -normal; }', ' normal = normalize(normal);']).result;
215
215
  }
@@ -225,18 +225,30 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
225
225
  if (hash.includes('sel')) return;
226
226
  var vDesc = pipeline.getShaderDescription('vertex');
227
227
  if (!vDesc.hasOutput('vertexVC')) vDesc.addOutput('vec4<f32>', 'vertexVC');
228
+ var renderer = model.WebGPURenderer.getRenderable();
228
229
  var fDesc = pipeline.getShaderDescription('fragment');
229
230
  var code = fDesc.getCode(); // Code that runs if the fragment shader includes normals
230
231
 
231
232
  if (code.includes('var normal:') && model.useRendererMatrix && !isEdges(hash) && !model.is2D && !hash.includes('sel')) {
232
- code = vtkWebGPUShaderCache.substitute(code, '//VTK::Light::Impl', [// Constants
233
+ var _renderer$getEnvironm;
234
+
235
+ var lightingCode = [// Constants
233
236
  ' var pi: f32 = 3.14159265359;', // Vectors needed for light calculations
234
237
  ' var fragPos: vec3<f32> = vec3<f32>(input.vertexVC.xyz);', ' var V: vec3<f32> = mix(normalize(-fragPos), vec3<f32>(0, 0, 1), f32(rendererUBO.cameraParallel)); // View Vector', // Values needed for light calculations
235
238
  ' var baseColor: vec3<f32> = _diffuseMap.rgb * diffuseColor.rgb;', ' var roughness: f32 = max(0.000001, mapperUBO.Roughness * _roughnessMap.r);', // Need to have a different way of sampling greyscale values aside from .r
236
239
  ' var metallic: f32 = mapperUBO.Metallic * _metallicMap.r;', ' var alpha: f32 = roughness*roughness;', ' var ior: f32 = mapperUBO.BaseIOR;', ' var k: f32 = alpha*alpha / 2;', // Split diffuse and specular components
237
240
  ' var diffuse: vec3<f32> = vec3<f32>(0.);', ' var specular: vec3<f32> = vec3<f32>(0.);', ' var emission: vec3<f32> = _emissionMap.rgb * mapperUBO.Emission;', // Summing diffuse and specular components of directional lights
238
241
  ' {', ' var i: i32 = 0;', ' loop {', ' if !(i < rendererUBO.LightCount) { break; }', ' switch (i32(rendererLightSSBO.values[i].LightData.x)) {', ' // Point Light', ' case 0 {', ' var color: vec3<f32> = rendererLightSSBO.values[i].LightColor.rgb * rendererLightSSBO.values[i].LightColor.w;', ' var pos: vec3<f32> = (rendererLightSSBO.values[i].LightPos).xyz;', ' var calculated: PBRData = calcPointLight(normal, V, fragPos, ior, roughness, metallic, pos, color, baseColor);', ' diffuse += max(vec3<f32>(0), calculated.diffuse);', ' specular += max(vec3<f32>(0), calculated.specular);', ' }', ' // Directional light', ' case 1 {', ' var dir: vec3<f32> = (rendererUBO.WCVCNormals * vec4<f32>(normalize(rendererLightSSBO.values[i].LightDir.xyz), 0.)).xyz;', ' dir = normalize(dir);', ' var color: vec3<f32> = rendererLightSSBO.values[i].LightColor.rgb * rendererLightSSBO.values[i].LightColor.w;', ' var calculated: PBRData = calcDirectionalLight(normal, V, ior, roughness, metallic, dir, color, baseColor); // diffuseColor.rgb needs to be fixed with a more dynamic diffuse color', ' diffuse += max(vec3<f32>(0), calculated.diffuse);', ' specular += max(vec3<f32>(0), calculated.specular);', ' }', ' // Spot Light', ' case 2 {', ' var color: vec3<f32> = rendererLightSSBO.values[i].LightColor.rgb * rendererLightSSBO.values[i].LightColor.w;', ' var pos: vec3<f32> = (rendererLightSSBO.values[i].LightPos).xyz;', ' var dir: vec3<f32> = (rendererUBO.WCVCNormals * vec4<f32>(normalize(rendererLightSSBO.values[i].LightDir.xyz), 0.)).xyz;', ' dir = normalize(dir);', ' var cones: vec2<f32> = vec2<f32>(rendererLightSSBO.values[i].LightData.y, rendererLightSSBO.values[i].LightData.z);', ' var calculated: PBRData = calcSpotLight(normal, V, fragPos, ior, roughness, metallic, pos, dir, cones, color, baseColor);', ' diffuse += max(vec3<f32>(0), calculated.diffuse);', ' specular += max(vec3<f32>(0), calculated.specular);', ' }', ' default { continue; }', ' }', ' continuing { i++; }', ' }', ' }', // Final variables for combining specular and diffuse
239
- ' var fresnel: f32 = schlickFresnelIOR(V, normal, ior, k); // Fresnel', ' fresnel = min(1, fresnel);', ' // This could be controlled with its own variable (that isnt base color) for better artistic control', ' var fresnelMetallic: vec3<f32> = schlickFresnelRGB(V, normal, baseColor); // Fresnel for metal, takes color into account', ' var kS: vec3<f32> = mix(vec3<f32>(fresnel), fresnelMetallic, metallic);', ' kS = min(vec3<f32>(1), kS);', ' var kD: vec3<f32> = (1.0 - kS) * (1.0 - metallic);', ' var PBR: vec3<f32> = mapperUBO.DiffuseIntensity*kD*diffuse + kS*specular;', ' PBR += emission;', ' computedColor = vec4<f32>(PBR, mapperUBO.Opacity);']).result;
242
+ ' var fresnel: f32 = schlickFresnelIOR(V, normal, ior, k); // Fresnel', ' fresnel = min(1, fresnel);', ' // This could be controlled with its own variable (that isnt base color) for better artistic control', ' var fresnelMetallic: vec3<f32> = schlickFresnelRGB(V, normal, baseColor); // Fresnel for metal, takes color into account', ' var kS: vec3<f32> = mix(vec3<f32>(fresnel), fresnelMetallic, metallic);', ' kS = min(vec3<f32>(1), kS);', ' var kD: vec3<f32> = (1.0 - kS) * (1.0 - metallic);', ' var PBR: vec3<f32> = mapperUBO.DiffuseIntensity*kD*diffuse + kS*specular;', ' PBR += emission;', ' computedColor = vec4<f32>(PBR, mapperUBO.Opacity);'];
243
+
244
+ if ((_renderer$getEnvironm = renderer.getEnvironmentTexture()) !== null && _renderer$getEnvironm !== void 0 && _renderer$getEnvironm.getImageLoaded()) {
245
+ lightingCode.push(' // To get diffuse IBL, the texture is sampled with normals in worldspace', ' var diffuseIBLCoords: vec3<f32> = (transpose(rendererUBO.WCVCNormals) * vec4<f32>(normal, 1.)).xyz;', ' var diffuseCoords: vec2<f32> = vecToRectCoord(diffuseIBLCoords);', ' // To get specular IBL, the texture is sampled as the worldspace reflection between the normal and view vectors', ' // Reflections are first calculated in viewspace, then converted to worldspace to sample the environment', ' var VreflN: vec3<f32> = normalize(reflect(-V, normal));', ' var reflectionIBLCoords = (transpose(rendererUBO.WCVCNormals) * vec4<f32>(VreflN, 1.)).xyz;', ' var specularCoords: vec2<f32> = vecToRectCoord(reflectionIBLCoords);', ' var diffuseIBL = textureSampleLevel(EnvironmentTexture, EnvironmentTextureSampler, diffuseCoords, rendererUBO.MaxEnvironmentMipLevel);', // Level multiplier should be set by UBO
246
+ ' var level = roughness * rendererUBO.MaxEnvironmentMipLevel;', ' var specularIBL = textureSampleLevel(EnvironmentTexture, EnvironmentTextureSampler, specularCoords, level);', // Manual mip smoothing since not all formats support smooth level sampling
247
+ ' var specularIBLContribution: vec3<f32> = specularIBL.rgb*rendererUBO.BackgroundSpecularStrength;', ' computedColor += vec4<f32>(specularIBLContribution*kS, 0);', ' var diffuseIBLContribution: vec3<f32> = diffuseIBL.rgb*rendererUBO.BackgroundDiffuseStrength;', ' diffuseIBLContribution *= baseColor * _ambientOcclusionMap.rgb;', // Multipy by baseColor may be changed
248
+ ' computedColor += vec4<f32>(diffuseIBLContribution*kD, 0);');
249
+ }
250
+
251
+ code = vtkWebGPUShaderCache.substitute(code, '//VTK::Light::Impl', lightingCode).result;
240
252
  fDesc.setCode(code); // If theres no normals, just set the specular color to be flat
241
253
  } else {
242
254
  code = vtkWebGPUShaderCache.substitute(code, '//VTK::Light::Impl', [' var diffuse: vec3<f32> = diffuseColor.rgb;', ' var specular: vec3<f32> = mapperUBO.SpecularColor.rgb * mapperUBO.SpecularColor.a;', ' computedColor = vec4<f32>(diffuse * _diffuseMap.rgb, mapperUBO.Opacity);']).result;
@@ -287,8 +299,7 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
287
299
  var numComp = vtkWebGPUTypes.getNumberOfComponentsFromBufferFormat(tcoords.getArrayInformation()[0].format);
288
300
  var code = vDesc.getCode();
289
301
  vDesc.addOutput("vec".concat(numComp, "<f32>"), 'tcoordVS');
290
- code = vtkWebGPUShaderCache.substitute(code, '//VTK::TCoord::Impl', [' output.tcoordVS = tcoord;' // Ensure that UV coordinates are always between 0-1
291
- ]).result;
302
+ code = vtkWebGPUShaderCache.substitute(code, '//VTK::TCoord::Impl', [' output.tcoordVS = tcoord;']).result;
292
303
  vDesc.setCode(code);
293
304
  var fDesc = pipeline.getShaderDescription('fragment');
294
305
  code = fDesc.getCode();
@@ -568,7 +579,7 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
568
579
  };
569
580
 
570
581
  publicAPI.updateTextures = function () {
571
- var _model$renderable$get2, _model$renderable2, _actor$getProperty$ge14, _actor$getProperty8, _actor$getProperty$ge15, _actor$getProperty9, _actor$getProperty$ge16, _actor$getProperty10, _actor$getProperty$ge17, _actor$getProperty11, _actor$getProperty$ge18, _actor$getProperty12, _actor$getProperty$ge19, _actor$getProperty13, _renderer$getBackgrou;
582
+ var _model$renderable$get2, _model$renderable2, _actor$getProperty$ge14, _actor$getProperty8, _actor$getProperty$ge15, _actor$getProperty9, _actor$getProperty$ge16, _actor$getProperty10, _actor$getProperty$ge17, _actor$getProperty11, _actor$getProperty$ge18, _actor$getProperty12, _actor$getProperty$ge19, _actor$getProperty13, _renderer$getEnvironm2;
572
583
 
573
584
  // we keep track of new and used textures so
574
585
  // that we can clean up any unused textures so we don't hold onto them
@@ -634,8 +645,8 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
634
645
  textures.push(_pair7);
635
646
  }
636
647
 
637
- if ((_renderer$getBackgrou = renderer.getBackgroundTexture) !== null && _renderer$getBackgrou !== void 0 && _renderer$getBackgrou.call(renderer)) {
638
- var _pair8 = ['Background', renderer.getBackgroundTexture()];
648
+ if ((_renderer$getEnvironm2 = renderer.getEnvironmentTexture) !== null && _renderer$getEnvironm2 !== void 0 && _renderer$getEnvironm2.call(renderer)) {
649
+ var _pair8 = ['Environment', renderer.getEnvironmentTexture()];
639
650
  textures.push(_pair8);
640
651
  }
641
652
 
@@ -671,10 +682,29 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
671
682
  model.textures.push(newTex);
672
683
  model.textureViews.push(tview);
673
684
  var interpolate = srcTexture.getInterpolate() ? 'linear' : 'nearest';
674
- tview.addSampler(model.device, {
675
- minFilter: interpolate,
676
- magFilter: interpolate
677
- });
685
+ var addressMode = null;
686
+ if (!addressMode && srcTexture.getEdgeClamp() && srcTexture.getRepeat()) addressMode = 'mirror-repeat';
687
+ if (!addressMode && srcTexture.getEdgeClamp()) addressMode = 'clamp-to-edge';
688
+ if (!addressMode && srcTexture.getRepeat()) addressMode = 'repeat';
689
+
690
+ if (textureName !== 'Environment') {
691
+ tview.addSampler(model.device, {
692
+ addressModeU: addressMode,
693
+ addressModeV: addressMode,
694
+ addressModeW: addressMode,
695
+ minFilter: interpolate,
696
+ magFilter: interpolate
697
+ });
698
+ } else {
699
+ tview.addSampler(model.device, {
700
+ addressModeU: 'repeat',
701
+ addressModeV: 'clamp-to-edge',
702
+ addressModeW: 'repeat',
703
+ minFilter: interpolate,
704
+ magFilter: interpolate,
705
+ mipmapFilter: 'linear'
706
+ });
707
+ }
678
708
  }
679
709
  }
680
710
  } // remove unused textures
@@ -1,8 +1,14 @@
1
1
  import macro from '../../macros.js';
2
+ import vtkWebGPUFullScreenQuad from './FullScreenQuad.js';
2
3
  import vtkWebGPUOpaquePass from './OpaquePass.js';
3
4
  import vtkWebGPUOrderIndepenentTranslucentPass from './OrderIndependentTranslucentPass.js';
5
+ import vtkWebGPURenderEncoder from './RenderEncoder.js';
4
6
  import vtkWebGPUVolumePass from './VolumePass.js';
5
7
  import vtkRenderPass from '../SceneGraph/RenderPass.js';
8
+ import vtkWebGPUSampler from './Sampler.js';
9
+ import vtkWebGPUTextureView from './TextureView.js';
10
+
11
+ var finalBlitFragTemplate = "\n//VTK::Mapper::Dec\n\n//VTK::TCoord::Dec\n\n//VTK::RenderEncoder::Dec\n\n//VTK::IOStructs::Dec\n\n@fragment\nfn main(\n//VTK::IOStructs::Input\n)\n//VTK::IOStructs::Output\n{\n var output: fragmentOutput;\n\n var computedColor: vec4<f32> = clamp(textureSampleLevel(opaquePassColorTexture, finalPassSampler, input.tcoordVS, 0),vec4<f32>(0.0),vec4<f32>(1.0));\n\n //VTK::RenderEncoder::Impl\n return output;\n}\n"; // ----------------------------------------------------------------------------
6
12
 
7
13
  function vtkForwardPass(publicAPI, model) {
8
14
  // Set our className
@@ -69,24 +75,96 @@ function vtkForwardPass(publicAPI, model) {
69
75
  model.volumePass.setDepthTextureView(model.opaquePass.getDepthTextureView());
70
76
  model.volumePass.setVolumes(model.volumes);
71
77
  model.volumePass.traverse(renNode, viewNode);
72
- }
78
+ } // blit the result into the swap chain
79
+
80
+
81
+ publicAPI.finalPass(viewNode, renNode);
73
82
  }
74
83
  }
75
- } // blit the result into the swap chain
76
-
77
-
78
- var sctex = viewNode.getCurrentTexture();
79
- var optex = model.opaquePass.getColorTexture();
80
- var cmdEnc = viewNode.getCommandEncoder();
81
- cmdEnc.copyTextureToTexture({
82
- texture: optex.getHandle()
83
- }, {
84
- texture: sctex
85
- }, {
86
- width: optex.getWidth(),
87
- height: optex.getHeight(),
88
- depthOrArrayLayers: 1
84
+ }
85
+ };
86
+
87
+ publicAPI.finalPass = function (viewNode, renNode) {
88
+ if (!model._finalBlitEncoder) {
89
+ publicAPI.createFinalBlitEncoder(viewNode);
90
+ }
91
+
92
+ model._finalBlitOutputTextureView.createFromTextureHandle(viewNode.getCurrentTexture(), {
93
+ depth: 1,
94
+ format: viewNode.getPresentationFormat()
95
+ });
96
+
97
+ model._finalBlitEncoder.attachTextureViews();
98
+
99
+ model._finalBlitEncoder.begin(viewNode.getCommandEncoder());
100
+
101
+ renNode.scissorAndViewport(model._finalBlitEncoder);
102
+
103
+ model._fullScreenQuad.prepareAndDraw(model._finalBlitEncoder);
104
+
105
+ model._finalBlitEncoder.end();
106
+ };
107
+
108
+ publicAPI.createFinalBlitEncoder = function (viewNode) {
109
+ model._finalBlitEncoder = vtkWebGPURenderEncoder.newInstance({
110
+ label: 'forwardPassBlit'
111
+ });
112
+
113
+ model._finalBlitEncoder.setDescription({
114
+ colorAttachments: [{
115
+ view: null,
116
+ loadOp: 'load',
117
+ storeOp: 'store'
118
+ }]
119
+ });
120
+
121
+ model._finalBlitEncoder.setPipelineHash('fpf');
122
+
123
+ model._finalBlitEncoder.setPipelineSettings({
124
+ primitive: {
125
+ cullMode: 'none'
126
+ },
127
+ fragment: {
128
+ targets: [{
129
+ format: viewNode.getPresentationFormat(),
130
+ blend: {
131
+ color: {
132
+ srcFactor: 'src-alpha',
133
+ dstFactor: 'one-minus-src-alpha'
134
+ },
135
+ alpha: {
136
+ srcfactor: 'one',
137
+ dstFactor: 'one-minus-src-alpha'
138
+ }
139
+ }
140
+ }]
141
+ }
142
+ });
143
+
144
+ model._fsqSampler = vtkWebGPUSampler.newInstance({
145
+ label: 'finalPassSampler'
146
+ });
147
+
148
+ model._fsqSampler.create(viewNode.getDevice(), {
149
+ minFilter: 'linear',
150
+ magFilter: 'linear'
89
151
  });
152
+
153
+ model._fullScreenQuad = vtkWebGPUFullScreenQuad.newInstance();
154
+
155
+ model._fullScreenQuad.setDevice(viewNode.getDevice());
156
+
157
+ model._fullScreenQuad.setPipelineHash('fpfsq');
158
+
159
+ model._fullScreenQuad.setTextureViews([model.opaquePass.getColorTextureView()]);
160
+
161
+ model._fullScreenQuad.setAdditionalBindables([model._fsqSampler]);
162
+
163
+ model._fullScreenQuad.setFragmentShaderTemplate(finalBlitFragTemplate);
164
+
165
+ model._finalBlitOutputTextureView = vtkWebGPUTextureView.newInstance();
166
+
167
+ model._finalBlitEncoder.setColorTextureView(0, model._finalBlitOutputTextureView);
90
168
  };
91
169
 
92
170
  publicAPI.incrementOpaqueActorCount = function () {
@@ -12,8 +12,9 @@ function vtkWebGPUFullScreenQuad(publicAPI, model) {
12
12
  publicAPI.replaceShaderPosition = function (hash, pipeline, vertexInput) {
13
13
  var vDesc = pipeline.getShaderDescription('vertex');
14
14
  vDesc.addBuiltinOutput('vec4<f32>', '@builtin(position) Position');
15
+ vDesc.addOutput('vec4<f32>', 'vertexVC');
15
16
  var code = vDesc.getCode();
16
- code = vtkWebGPUShaderCache.substitute(code, '//VTK::Position::Impl', ['output.tcoordVS = vec2<f32>(vertexBC.x * 0.5 + 0.5, 1.0 - vertexBC.y * 0.5 - 0.5);', 'output.Position = vec4<f32>(vertexBC, 1.0);']).result;
17
+ code = vtkWebGPUShaderCache.substitute(code, '//VTK::Position::Impl', ['output.tcoordVS = vec2<f32>(vertexBC.x * 0.5 + 0.5, 1.0 - vertexBC.y * 0.5 - 0.5);', 'output.Position = vec4<f32>(vertexBC, 1.0);', 'output.vertexVC = vec4<f32>(vertexBC, 1);']).result;
17
18
  vDesc.setCode(code);
18
19
  };
19
20
 
@@ -27,7 +27,7 @@ function vtkWebGPUOpaquePass(publicAPI, model) {
27
27
  model.colorTexture.create(device, {
28
28
  width: viewNode.getCanvas().width,
29
29
  height: viewNode.getCanvas().height,
30
- format: 'bgra8unorm',
30
+ format: 'rgba16float',
31
31
 
32
32
  /* eslint-disable no-undef */
33
33
 
@@ -184,7 +184,7 @@ function vtkWebGPUOrderIndependentTranslucentPass(publicAPI, model) {
184
184
  },
185
185
  fragment: {
186
186
  targets: [{
187
- format: 'bgra8unorm',
187
+ format: 'rgba16float',
188
188
  blend: {
189
189
  color: {
190
190
  srcFactor: 'src-alpha',
@@ -51,9 +51,11 @@ function vtkWebGPURenderEncoder(publicAPI, model) {
51
51
  console.trace();
52
52
  } else {
53
53
  for (var i = 0; i < model.colorTextureViews.length; i++) {
54
- var fmt = model.colorTextureViews[i].getTexture().getFormat();
54
+ var _model$colorTextureVi;
55
55
 
56
- if (fmt !== pd.fragment.targets[i].format) {
56
+ var fmt = (_model$colorTextureVi = model.colorTextureViews[i].getTexture()) === null || _model$colorTextureVi === void 0 ? void 0 : _model$colorTextureVi.getFormat();
57
+
58
+ if (fmt && fmt !== pd.fragment.targets[i].format) {
57
59
  console.log("mismatched attachments for attachment ".concat(i, " on pipeline ").concat(pd.fragment.targets[i].format, " while encoder has ").concat(fmt));
58
60
  console.trace();
59
61
  }
@@ -65,9 +67,11 @@ function vtkWebGPURenderEncoder(publicAPI, model) {
65
67
  console.log('mismatched depth attachments');
66
68
  console.trace();
67
69
  } else if (model.depthTextureView) {
68
- var dfmt = model.depthTextureView.getTexture().getFormat();
70
+ var _model$depthTextureVi;
71
+
72
+ var dfmt = (_model$depthTextureVi = model.depthTextureView.getTexture()) === null || _model$depthTextureVi === void 0 ? void 0 : _model$depthTextureVi.getFormat();
69
73
 
70
- if (dfmt !== pd.depthStencil.format) {
74
+ if (dfmt && dfmt !== pd.depthStencil.format) {
71
75
  console.log("mismatched depth attachments on pipeline ".concat(pd.depthStencil.format, " while encoder has ").concat(dfmt));
72
76
  console.trace();
73
77
  }
@@ -202,7 +206,7 @@ function extend(publicAPI, model) {
202
206
  },
203
207
  fragment: {
204
208
  targets: [{
205
- format: 'bgra8unorm',
209
+ format: 'rgba16float',
206
210
  blend: {
207
211
  color: {
208
212
  srcFactor: 'src-alpha',
@@ -9,6 +9,7 @@ import vtkWebGPUHardwareSelector from './HardwareSelector.js';
9
9
  import vtkWebGPUViewNodeFactory from './ViewNodeFactory.js';
10
10
  import vtkRenderPass from '../SceneGraph/RenderPass.js';
11
11
  import vtkRenderWindowViewNode from '../SceneGraph/RenderWindowViewNode.js';
12
+ import HalfFloat from '../../Common/Core/HalfFloat.js';
12
13
 
13
14
  var vtkErrorMacro = macro.vtkErrorMacro; // const IS_CHROME = navigator.userAgent.indexOf('Chrome') !== -1;
14
15
 
@@ -67,14 +68,14 @@ function vtkWebGPURenderWindow(publicAPI, model) {
67
68
  publicAPI.recreateSwapChain = function () {
68
69
  if (model.context) {
69
70
  model.context.unconfigure();
70
- var presentationFormat = navigator.gpu.getPreferredCanvasFormat(model.adapter);
71
+ model.presentationFormat = navigator.gpu.getPreferredCanvasFormat(model.adapter);
71
72
  /* eslint-disable no-undef */
72
73
 
73
74
  /* eslint-disable no-bitwise */
74
75
 
75
76
  model.context.configure({
76
77
  device: model.device.getHandle(),
77
- format: presentationFormat,
78
+ format: model.presentationFormat,
78
79
  alphaMode: 'premultiplied',
79
80
  usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_DST,
80
81
  width: model.size[0],
@@ -545,10 +546,10 @@ function vtkWebGPURenderWindow(publicAPI, model) {
545
546
  result = {
546
547
  width: texture.getWidth(),
547
548
  height: texture.getHeight()
548
- }; // must be a multiple of 256 bytes, so 64 texels with rgba8
549
+ }; // must be a multiple of 256 bytes, so 32 texels with rgba16
549
550
 
550
- result.colorBufferWidth = 64 * Math.floor((result.width + 63) / 64);
551
- result.colorBufferSizeInBytes = result.colorBufferWidth * result.height * 4;
551
+ result.colorBufferWidth = 32 * Math.floor((result.width + 31) / 32);
552
+ result.colorBufferSizeInBytes = result.colorBufferWidth * result.height * 8;
552
553
  colorBuffer = vtkWebGPUBuffer.newInstance();
553
554
  colorBuffer.setDevice(device);
554
555
  /* eslint-disable no-bitwise */
@@ -565,7 +566,7 @@ function vtkWebGPURenderWindow(publicAPI, model) {
565
566
  texture: texture.getHandle()
566
567
  }, {
567
568
  buffer: colorBuffer.getHandle(),
568
- bytesPerRow: 4 * result.colorBufferWidth,
569
+ bytesPerRow: 8 * result.colorBufferWidth,
569
570
  rowsPerImage: result.height
570
571
  }, {
571
572
  width: result.width,
@@ -581,7 +582,7 @@ function vtkWebGPURenderWindow(publicAPI, model) {
581
582
 
582
583
  case 14:
583
584
  /* eslint-enable no-undef */
584
- result.colorValues = new Uint8ClampedArray(colorBuffer.getMappedRange().slice());
585
+ result.colorValues = new Uint16Array(colorBuffer.getMappedRange().slice());
585
586
  colorBuffer.unmap(); // repack the array
586
587
 
587
588
  tmparray = new Uint8ClampedArray(result.height * result.width * 4);
@@ -590,10 +591,10 @@ function vtkWebGPURenderWindow(publicAPI, model) {
590
591
  for (x = 0; x < result.width; x++) {
591
592
  doffset = (y * result.width + x) * 4;
592
593
  soffset = (y * result.colorBufferWidth + x) * 4;
593
- tmparray[doffset] = result.colorValues[soffset + 2];
594
- tmparray[doffset + 1] = result.colorValues[soffset + 1];
595
- tmparray[doffset + 2] = result.colorValues[soffset];
596
- tmparray[doffset + 3] = result.colorValues[soffset + 3];
594
+ tmparray[doffset] = 255.0 * HalfFloat.fromHalf(result.colorValues[soffset]);
595
+ tmparray[doffset + 1] = 255.0 * HalfFloat.fromHalf(result.colorValues[soffset + 1]);
596
+ tmparray[doffset + 2] = 255.0 * HalfFloat.fromHalf(result.colorValues[soffset + 2]);
597
+ tmparray[doffset + 3] = 255.0 * HalfFloat.fromHalf(result.colorValues[soffset + 3]);
597
598
  }
598
599
  }
599
600
 
@@ -635,7 +636,8 @@ var DEFAULT_VALUES = {
635
636
  useOffScreen: false,
636
637
  useBackgroundImage: false,
637
638
  nextPropID: 1,
638
- xrSupported: false
639
+ xrSupported: false,
640
+ presentationFormat: null
639
641
  }; // ----------------------------------------------------------------------------
640
642
 
641
643
  function extend(publicAPI, model) {
@@ -671,7 +673,7 @@ function extend(publicAPI, model) {
671
673
  macro.event(publicAPI, model, 'imageReady');
672
674
  macro.event(publicAPI, model, 'initialized'); // Build VTK API
673
675
 
674
- macro.get(publicAPI, model, ['commandEncoder', 'device', 'useBackgroundImage', 'xrSupported']);
676
+ macro.get(publicAPI, model, ['commandEncoder', 'device', 'presentationFormat', 'useBackgroundImage', 'xrSupported']);
675
677
  macro.setGet(publicAPI, model, ['initialized', 'context', 'canvas', 'device', 'renderPasses', 'notifyStartCaptureImage', 'cursor', 'useOffScreen']);
676
678
  macro.setGetArray(publicAPI, model, ['size'], 2); // Object methods
677
679
 
@@ -9,7 +9,12 @@ import vtkWebGPUUniformBuffer from './UniformBuffer.js';
9
9
  import { registerOverride } from './ViewNodeFactory.js';
10
10
 
11
11
  var vtkDebugMacro = vtkDebugMacro$1;
12
- var clearFragTemplate = "\n//VTK::Renderer::Dec\n\n//VTK::Mapper::Dec\n\n//VTK::TCoord::Dec\n\n//VTK::RenderEncoder::Dec\n\n//VTK::IOStructs::Dec\n\n@fragment\nfn main(\n//VTK::IOStructs::Input\n)\n//VTK::IOStructs::Output\n{\n var output: fragmentOutput;\n\n var computedColor: vec4<f32> = mapperUBO.BackgroundColor;\n\n //VTK::RenderEncoder::Impl\n return output;\n}\n"; // Light type index gives either 0, 1, or 2 which indicates what type of light there is.
12
+ var clearFragColorTemplate = "\n//VTK::Renderer::Dec\n\n//VTK::Mapper::Dec\n\n//VTK::TCoord::Dec\n\n//VTK::RenderEncoder::Dec\n\n//VTK::IOStructs::Dec\n\n@fragment\nfn main(\n//VTK::IOStructs::Input\n)\n//VTK::IOStructs::Output\n{\n var output: fragmentOutput;\n\n var computedColor: vec4<f32> = mapperUBO.BackgroundColor;\n\n //VTK::RenderEncoder::Impl\n return output;\n}\n";
13
+ var clearFragTextureTemplate = "\nfn vecToRectCoord(dir: vec3<f32>) -> vec2<f32> {\n var tau: f32 = 6.28318530718;\n var pi: f32 = 3.14159265359;\n var out: vec2<f32> = vec2<f32>(0.0);\n\n out.x = atan2(dir.z, dir.x) / tau;\n out.x += 0.5;\n\n var phix: f32 = length(vec2(dir.x, dir.z));\n out.y = atan2(dir.y, phix) / pi + 0.5;\n\n return out;\n}\n\n//VTK::Renderer::Dec\n\n//VTK::Mapper::Dec\n\n//VTK::TCoord::Dec\n\n//VTK::RenderEncoder::Dec\n\n//VTK::IOStructs::Dec\n\n@fragment\nfn main(\n//VTK::IOStructs::Input\n)\n//VTK::IOStructs::Output\n{\n var output: fragmentOutput;\n\n var tcoord: vec4<f32> = vec4<f32>(input.vertexVC.xy, -1, 1);\n var V: vec4<f32> = normalize(mapperUBO.FSQMatrix * tcoord); // vec2<f32>((input.tcoordVS.x - 0.5) * 2, -(input.tcoordVS.y - 0.5) * 2);\n // textureSampleLevel gets rid of some ugly artifacts\n var background = textureSampleLevel(EnvironmentTexture, EnvironmentTextureSampler, vecToRectCoord(V.xyz), 0);\n var computedColor: vec4<f32> = vec4<f32>(background.rgb, 1);\n\n //VTK::RenderEncoder::Impl\n return output;\n}\n";
14
+
15
+ var _fsqClearMat4 = new Float64Array(16);
16
+
17
+ var _tNormalMat4 = new Float64Array(16); // Light type index gives either 0, 1, or 2 which indicates what type of light there is.
13
18
  // While technically, there are only spot and directional lights, within the CellArrayMapper
14
19
  // there is a third, positional light. It is technically just a variant of a spot light with
15
20
  // a cone angle of 90 or above, however certain calculations can be skipped if it is treated
@@ -19,6 +24,7 @@ var clearFragTemplate = "\n//VTK::Renderer::Dec\n\n//VTK::Mapper::Dec\n\n//VTK::
19
24
  // 1 -> directional light
20
25
  // 2 -> spot light
21
26
 
27
+
22
28
  function getLightTypeIndex(light) {
23
29
  if (light.getPositional()) {
24
30
  if (light.getConeAngle() >= 90) {
@@ -124,6 +130,8 @@ function vtkWebGPURenderer(publicAPI, model) {
124
130
  var utime = model.UBO.getSendTime();
125
131
 
126
132
  if (model._parent.getMTime() > utime || publicAPI.getMTime() > utime || model.camera.getMTime() > utime || model.renderable.getMTime() > utime) {
133
+ var _model$renderable$get;
134
+
127
135
  var keyMats = model.webgpuCamera.getKeyMatrices(publicAPI);
128
136
  model.UBO.setArray('WCVCMatrix', keyMats.wcvc);
129
137
  model.UBO.setArray('SCPCMatrix', keyMats.scpc);
@@ -132,6 +140,9 @@ function vtkWebGPURenderer(publicAPI, model) {
132
140
  model.UBO.setArray('VCPCMatrix', keyMats.vcpc);
133
141
  model.UBO.setArray('WCVCNormals', keyMats.normalMatrix);
134
142
  model.UBO.setValue('LightCount', model.renderable.getLights().length);
143
+ model.UBO.setValue('MaxEnvironmentMipLevel', (_model$renderable$get = model.renderable.getEnvironmentTexture()) === null || _model$renderable$get === void 0 ? void 0 : _model$renderable$get.getMipLevel());
144
+ model.UBO.setValue('BackgroundDiffuseStrength', model.renderable.getEnvironmentTextureDiffuseStrength());
145
+ model.UBO.setValue('BackgroundSpecularStrength', model.renderable.getEnvironmentTextureSpecularStrength());
135
146
  var tsize = publicAPI.getYInvertedTiledSizeAndOrigin();
136
147
  model.UBO.setArray('viewportSize', [tsize.usize, tsize.vsize]);
137
148
  model.UBO.setValue('cameraParallel', model.camera.getParallelProjection());
@@ -162,8 +173,7 @@ function vtkWebGPURenderer(publicAPI, model) {
162
173
 
163
174
  var viewCoordinatePosition = lights[_i].getPosition();
164
175
 
165
- vec3.transformMat4(viewCoordinatePosition, viewCoordinatePosition, keyMats.wcvc); // console.log(viewCoordinatePosition);
166
- // viewCoordinatePosition
176
+ vec3.transformMat4(viewCoordinatePosition, viewCoordinatePosition, keyMats.wcvc); // viewCoordinatePosition
167
177
 
168
178
  lightPosArray[offset] = viewCoordinatePosition[0];
169
179
  lightPosArray[offset + 1] = viewCoordinatePosition[1];
@@ -240,26 +250,85 @@ function vtkWebGPURenderer(publicAPI, model) {
240
250
  };
241
251
 
242
252
  publicAPI.clear = function () {
253
+ var _model$backgroundTex;
254
+
243
255
  if (model.renderable.getTransparent() || model.suppressClear) {
244
256
  return;
245
257
  }
246
258
 
247
- var device = model._parent.getDevice();
259
+ var device = model._parent.getDevice(); // Normal Solid Color
260
+
248
261
 
249
262
  if (!model.clearFSQ) {
250
263
  model.clearFSQ = vtkWebGPUFullScreenQuad.newInstance();
251
264
  model.clearFSQ.setDevice(device);
252
265
  model.clearFSQ.setPipelineHash('clearfsq');
253
- model.clearFSQ.setFragmentShaderTemplate(clearFragTemplate);
266
+ model.clearFSQ.setFragmentShaderTemplate(clearFragColorTemplate);
254
267
  var ubo = vtkWebGPUUniformBuffer.newInstance({
255
268
  label: 'mapperUBO'
256
269
  });
270
+ ubo.addEntry('FSQMatrix', 'mat4x4<f32>');
257
271
  ubo.addEntry('BackgroundColor', 'vec4<f32>');
258
272
  model.clearFSQ.setUBO(ubo);
273
+ model.backgroundTex = model.renderable.getEnvironmentTexture();
274
+ } // Textured Background
275
+
276
+
277
+ if (model.clearFSQ.getPipelineHash() !== 'clearfsqwithtexture' && model.renderable.getUseEnvironmentTextureAsBackground() && (_model$backgroundTex = model.backgroundTex) !== null && _model$backgroundTex !== void 0 && _model$backgroundTex.getImageLoaded()) {
278
+ model.clearFSQ.setFragmentShaderTemplate(clearFragTextureTemplate);
279
+
280
+ var _ubo = vtkWebGPUUniformBuffer.newInstance({
281
+ label: 'mapperUBO'
282
+ });
283
+
284
+ _ubo.addEntry('FSQMatrix', 'mat4x4<f32>');
285
+
286
+ _ubo.addEntry('BackgroundColor', 'vec4<f32>');
287
+
288
+ model.clearFSQ.setUBO(_ubo);
289
+ var environmentTextureHash = device.getTextureManager().getTextureForVTKTexture(model.backgroundTex);
290
+
291
+ if (environmentTextureHash.getReady()) {
292
+ var tview = environmentTextureHash.createView("EnvironmentTexture");
293
+ model.clearFSQ.setTextureViews([tview]);
294
+ model.backgroundTexLoaded = true;
295
+ var interpolate = model.backgroundTex.getInterpolate() ? 'linear' : 'nearest';
296
+ tview.addSampler(device, {
297
+ addressModeU: 'repeat',
298
+ addressModeV: 'clamp-to-edge',
299
+ addressModeW: 'repeat',
300
+ minFilter: interpolate,
301
+ magFilter: interpolate,
302
+ mipmapFilter: 'linear'
303
+ });
304
+ }
305
+
306
+ model.clearFSQ.setPipelineHash('clearfsqwithtexture');
307
+ } else if (model.clearFSQ.getPipelineHash() === 'clearfsqwithtexture' && !model.renderable.getUseEnvironmentTextureAsBackground()) {
308
+ // In case the mode is changed at runtime
309
+ model.clearFSQ = vtkWebGPUFullScreenQuad.newInstance();
310
+ model.clearFSQ.setDevice(device);
311
+ model.clearFSQ.setPipelineHash('clearfsq');
312
+ model.clearFSQ.setFragmentShaderTemplate(clearFragColorTemplate);
313
+
314
+ var _ubo2 = vtkWebGPUUniformBuffer.newInstance({
315
+ label: 'mapperUBO'
316
+ });
317
+
318
+ _ubo2.addEntry('FSQMatrix', 'mat4x4<f32>');
319
+
320
+ _ubo2.addEntry('BackgroundColor', 'vec4<f32>');
321
+
322
+ model.clearFSQ.setUBO(_ubo2);
259
323
  }
260
324
 
325
+ var keyMats = model.webgpuCamera.getKeyMatrices(publicAPI);
261
326
  var background = model.renderable.getBackgroundByReference();
262
327
  model.clearFSQ.getUBO().setArray('BackgroundColor', background);
328
+ mat4.transpose(_tNormalMat4, keyMats.normalMatrix);
329
+ mat4.mul(_fsqClearMat4, keyMats.scvc, keyMats.pcsc);
330
+ mat4.mul(_fsqClearMat4, _tNormalMat4, _fsqClearMat4);
331
+ model.clearFSQ.getUBO().setArray('FSQMatrix', _fsqClearMat4);
263
332
  model.clearFSQ.getUBO().sendIfNeeded(device);
264
333
  model.clearFSQ.prepareAndDraw(model.renderEncoder);
265
334
  };
@@ -395,6 +464,9 @@ function extend(publicAPI, model) {
395
464
  model.UBO.addEntry('WCVCNormals', 'mat4x4<f32>');
396
465
  model.UBO.addEntry('viewportSize', 'vec2<f32>');
397
466
  model.UBO.addEntry('LightCount', 'i32');
467
+ model.UBO.addEntry('MaxEnvironmentMipLevel', 'f32');
468
+ model.UBO.addEntry('BackgroundDiffuseStrength', 'f32');
469
+ model.UBO.addEntry('BackgroundSpecularStrength', 'f32');
398
470
  model.UBO.addEntry('cameraParallel', 'u32'); // SSBO (Light data)
399
471
 
400
472
  model.SSBO = vtkWebGPUStorageBuffer.newInstance({
@@ -12,8 +12,12 @@ function vtkWebGPUSampler(publicAPI, model) {
12
12
  publicAPI.create = function (device) {
13
13
  var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
14
14
  model.device = device;
15
+ model.options.addressModeU = options.addressModeU ? options.addressModeU : 'clamp-to-edge';
16
+ model.options.addressModeV = options.addressModeV ? options.addressModeV : 'clamp-to-edge';
17
+ model.options.addressModeW = options.addressModeW ? options.addressModeW : 'clamp-to-edge';
15
18
  model.options.magFilter = options.magFilter ? options.magFilter : 'nearest';
16
19
  model.options.minFilter = options.minFilter ? options.minFilter : 'nearest';
20
+ model.options.mipmapFilter = options.mipmapFilter ? options.mipmapFilter : 'nearest';
17
21
  model.options.label = model.label;
18
22
  model.handle = model.device.getHandle().createSampler(model.options);
19
23
  model.bindGroupTime.modified();