UNPKG

gpu-curtains

Version:

gpu-curtains is a 3D WebGPU rendering engine. It can be used as a standalone 3D engine, but also includes extra classes focused on mapping 3d objects to DOM elements; It allows users to synchronize values such as position, sizing, or scale between them.

534 lines (533 loc) 18.8 kB
import { Vec3 } from "../../math/Vec3.mjs"; import { isCameraRenderer } from "../../core/renderers/utils.mjs"; import { Vec2 } from "../../math/Vec2.mjs"; import { sRGBToLinear } from "../../math/color-utils.mjs"; import { Mesh } from "../../core/meshes/Mesh.mjs"; import { getVertexShaderCode } from "../../core/shaders/full/vertex/get-vertex-shader-code.mjs"; import { getFragmentShaderCode } from "../../core/shaders/full/fragment/get-fragment-shader-code.mjs"; //#region src/extras/meshes/LitMesh.ts /** * Class used to create a {@link Mesh} with built-in lighting. Shading types include 'Unlit' (no lighting), 'Lambert', 'Phong' and 'PBR'. For a better 'PBR' shading result, you should always use an associated {@link extras/environmentMap/EnvironmentMap.EnvironmentMap | EnvironmentMap}. * * Since the shaders are automatically generated based on the {@link LitMeshMaterialParams | `material`} parameter passed, it is more difficult to tweak them, even tho a few options exist. If you want full control over your shading, consider using a regular {@link Mesh} and writing your own shaders. * * @example * ```javascript * // assume 'renderer' is a valid camera renderer * * const ambientLight = new AmbientLight(renderer, { * intensity: 0.1, * }) * * const directionalLight = new DirectionalLight(renderer, { * position: new Vec3(10), * }) * * // A mesh with 'Lambert' shading * const lambertMesh = new LitMesh(renderer, { * label: 'Mesh with lambert shading', * geometry: new BoxGeometry(), * material: { * shading: 'Lambert', * color: new Vec3(1), * }, * }) * * // A mesh with a base color texture, 'Phong' shading * // and where we modify the output color before the lighting calculations * * // create a base color texture * baseColorTexture = new MediaTexture(renderer, { * label: 'Base color texture', * name: 'baseColorTexture', * format: 'rgba8unorm-srgb', * visibility: ['fragment'], * }) * * // load the image * baseColorTexture.loadImage('./path/to/texture.jpg') * * // create the mesh * const phongMesh = new LitMesh(renderer, { * label: 'Mesh with phong shading', * geometry: new BoxGeometry(), * material: { * shading: 'Phong', * fragmentChunks: { * // applied after having set the color and baseColorTexture to outputColor * // but before lighting calculations * preliminaryContribution: 'outputColor = mix(outputColor, vec4(vec3(modifiedMaterial.color), 1.0), modifiedMaterial.mixValue);' * }, * color: new Vec3(1), * shininess: 60, * baseColorTexture: { * texture: baseColorTexture, * }, * }, * uniforms: { * modifiedMaterial: { * visibility: ['fragment'], * struct: { * color: { * type: 'vec3f', * value: sRGBToLinear(new Vec3(0.5)), // colors need to be in linear space * }, * mixValue: { * type: 'f32', * value: 0.5, * } * }, * }, * }, * }) * ``` */ var LitMesh = class LitMesh extends Mesh { /** * LitMesh constructor * @param renderer - {@link CameraRenderer} object or {@link GPUCurtains} class object used to create this {@link LitMesh}. * @param parameters - {@link LitMeshParameters} used to create this {@link LitMesh}. */ constructor(renderer, parameters = {}) { renderer = isCameraRenderer(renderer, "LitMesh"); let { material, ...defaultParams } = parameters; if (!material) material = {}; let { colorSpace, transmissiveInputColorSpace, transmissiveInputToneMapping, outputColorSpace, flatShading, fragmentOutput } = material; if (!colorSpace) colorSpace = "srgb"; if (!outputColorSpace) outputColorSpace = "srgb"; if (!transmissiveInputColorSpace) transmissiveInputColorSpace = "srgb"; if (transmissiveInputToneMapping === void 0) transmissiveInputToneMapping = "Khronos"; if (!fragmentOutput) fragmentOutput = { struct: [{ type: "vec4f", name: "color" }], output: ` var output: FSOutput; output.color = outputColor; return output;` }; const { shading, additionalVaryings, vertexChunks, fragmentChunks, toneMapping, color, opacity, alphaCutoff, metallic, roughness, normalScale, occlusionIntensity, emissiveIntensity, emissiveColor, specularIntensity, specularColor, shininess, transmission, ior, dispersion, thickness, attenuationDistance, attenuationColor, multiscatterColor, scatterAnisotropy, sheenColor, sheenRoughness, anisotropy, anisotropyVector, clearcoat, clearcoatRoughness, clearcoatNormalScale, iridescence, iridescenceIOR, iridescenceThicknessRange, diffuseTransmission, diffuseTransmissionColor, baseColorTexture, normalTexture, emissiveTexture, occlusionTexture, metallicRoughnessTexture, specularTexture, specularFactorTexture, specularColorTexture, transmissionThicknessTexture, transmissionTexture, thicknessTexture, sheenTexture, sheenColorTexture, sheenRoughnessTexture, anisotropyTexture, clearcoatTexture, clearcoatFactorTexture, clearcoatRoughnessTexture, clearcoatNormalTexture, iridescenceTexture, iridescenceFactorTexture, iridescenceThicknessTexture, diffuseTransmissionTexture, diffuseTransmissionFactorTexture, diffuseTransmissionColorTexture, environmentMap } = material; const materialUniform = LitMesh.getMaterialUniform({ shading, colorSpace, color, opacity, alphaCutoff, metallic, roughness, normalScale, occlusionIntensity, emissiveIntensity, emissiveColor, specularIntensity, specularColor, shininess, transmission, ior, dispersion, thickness, attenuationDistance, attenuationColor, multiscatterColor, scatterAnisotropy, sheenColor, sheenRoughness, anisotropy, anisotropyVector, clearcoat, clearcoatRoughness, clearcoatNormalScale, iridescence, iridescenceIOR, iridescenceThicknessRange, diffuseTransmission, diffuseTransmissionColor, environmentMap }); if (defaultParams.uniforms) defaultParams.uniforms = { ...defaultParams.uniforms, material: materialUniform }; else defaultParams.uniforms = { material: materialUniform }; if (!defaultParams.textures) defaultParams.textures = []; if (!defaultParams.samplers) defaultParams.samplers = []; LitMesh.getMaterialTexturesDescriptors({ shading, baseColorTexture, normalTexture, emissiveTexture, occlusionTexture, metallicRoughnessTexture, specularTexture, specularFactorTexture, specularColorTexture, transmissionThicknessTexture, transmissionTexture, thicknessTexture, sheenTexture, sheenColorTexture, sheenRoughnessTexture, anisotropyTexture, clearcoatTexture, clearcoatFactorTexture, clearcoatRoughnessTexture, clearcoatNormalTexture, iridescenceTexture, iridescenceFactorTexture, iridescenceThicknessTexture, diffuseTransmissionTexture, diffuseTransmissionFactorTexture, diffuseTransmissionColorTexture }).forEach((textureDescriptor) => { if (textureDescriptor.sampler) { if (!defaultParams.samplers.find((s) => s.uuid === textureDescriptor.sampler.uuid)) defaultParams.samplers.push(textureDescriptor.sampler); } defaultParams.textures.push(textureDescriptor.texture); }); const useEnvMap = environmentMap && (shading === "PBR" || !shading); if (useEnvMap) { if (!defaultParams.textures) defaultParams.textures = []; defaultParams.textures = [ ...defaultParams.textures, environmentMap.diffuseTexture, environmentMap.specularTexture ]; if (environmentMap.lutTexture) defaultParams.textures = [...defaultParams.textures, environmentMap.lutTexture]; if (!defaultParams.samplers) defaultParams.samplers = []; defaultParams.samplers = [...defaultParams.samplers, environmentMap.sampler]; } const extensionsUsed = []; let transmissionBackgroundTexture = null; if (parameters.transmissive) { extensionsUsed.push("KHR_materials_transmission"); renderer.createTransmissionTarget(); transmissionBackgroundTexture = { texture: renderer.transmissionTarget.texture, sampler: renderer.transmissionTarget.sampler }; } if (thickness) extensionsUsed.push("KHR_materials_volume"); if (dispersion) extensionsUsed.push("KHR_materials_dispersion"); if (sheenColor || sheenRoughness) extensionsUsed.push("KHR_materials_sheen"); if (anisotropy !== void 0) extensionsUsed.push("KHR_materials_anisotropy"); if (clearcoat) extensionsUsed.push("KHR_materials_clearcoat"); if (iridescence) extensionsUsed.push("KHR_materials_iridescence"); if (diffuseTransmission !== void 0) extensionsUsed.push("KHR_materials_diffuse_transmission"); if (multiscatterColor !== void 0 || scatterAnisotropy !== void 0) extensionsUsed.push("KHR_materials_volume_scatter"); const hasNormal = defaultParams.geometry && defaultParams.geometry.getAttributeByName("normal"); if (defaultParams.geometry && !hasNormal) { defaultParams.geometry.computeGeometry(); flatShading = true; } const vs = LitMesh.getVertexShaderCode({ bindings: defaultParams.bindings, geometry: defaultParams.geometry, chunks: vertexChunks, additionalVaryings }); const cullMode = parameters.cullMode ?? "back"; const fs = LitMesh.getFragmentShaderCode({ shadingModel: shading, outputColorSpace, fragmentOutput, chunks: fragmentChunks, extensionsUsed, receiveShadows: defaultParams.receiveShadows, cullMode, flatShading, toneMapping, transmissiveInputColorSpace, transmissiveInputToneMapping, geometry: defaultParams.geometry, additionalVaryings, materialUniform, baseColorTexture, normalTexture, metallicRoughnessTexture, specularTexture, specularFactorTexture, specularColorTexture, transmissionThicknessTexture, transmissionTexture, thicknessTexture, emissiveTexture, occlusionTexture, sheenTexture, sheenColorTexture, sheenRoughnessTexture, anisotropyTexture, clearcoatTexture, clearcoatFactorTexture, clearcoatRoughnessTexture, clearcoatNormalTexture, iridescenceTexture, iridescenceFactorTexture, iridescenceThicknessTexture, diffuseTransmissionTexture, diffuseTransmissionFactorTexture, diffuseTransmissionColorTexture, transmissionBackgroundTexture, environmentMap }); const shaders = { vertex: { code: vs, entryPoint: "main" }, fragment: { code: fs, entryPoint: "main" } }; super(renderer, { ...defaultParams, shaders }); if (useEnvMap) environmentMap.onRotationAxisChanged(() => { this.uniforms.material.envRotation.value = environmentMap.rotationMatrix; }); } /** * Get the material {@link BufferBindingParams} to build the material uniform. * @param parameters - {@link GetLitMeshMaterialUniform} parameters. * @returns - Material uniform {@link BufferBindingParams}. */ static getMaterialUniform(parameters) { const { shading, colorSpace, color, opacity, alphaCutoff, metallic, roughness, normalScale, occlusionIntensity, emissiveIntensity, emissiveColor, specularIntensity, specularColor, shininess, transmission, ior, dispersion, thickness, attenuationDistance, attenuationColor, multiscatterColor, scatterAnisotropy, sheenColor, sheenRoughness, anisotropy, anisotropyVector, clearcoat, clearcoatRoughness, clearcoatNormalScale, iridescence, iridescenceIOR, iridescenceThicknessRange, diffuseTransmission, diffuseTransmissionColor, environmentMap } = parameters; const baseUniformStruct = { color: { type: "vec3f", value: color !== void 0 ? colorSpace === "srgb" ? sRGBToLinear(color.clone()) : color.clone() : new Vec3(1) }, opacity: { type: "f32", value: opacity !== void 0 ? opacity : 1 }, alphaCutoff: { type: "f32", value: alphaCutoff !== void 0 ? alphaCutoff : .5 }, occlusionIntensity: { type: "f32", value: occlusionIntensity !== void 0 ? occlusionIntensity : 1 }, emissiveIntensity: { type: "f32", value: emissiveIntensity !== void 0 ? emissiveIntensity : 1 }, emissiveColor: { type: "vec3f", value: emissiveColor !== void 0 ? colorSpace === "srgb" ? sRGBToLinear(emissiveColor.clone()) : emissiveColor.clone() : new Vec3() } }; const diffuseUniformStruct = { ...baseUniformStruct, normalScale: { type: "vec2f", value: normalScale !== void 0 ? normalScale : new Vec2(1) } }; const specularUniformStruct = { ...diffuseUniformStruct, specularIntensity: { type: "f32", value: specularIntensity !== void 0 ? specularIntensity : 1 }, specularColor: { type: "vec3f", value: specularColor !== void 0 ? colorSpace === "srgb" ? sRGBToLinear(specularColor.clone()) : specularColor.clone() : new Vec3(1) } }; const phongUniformStruct = { ...specularUniformStruct, shininess: { type: "f32", value: shininess !== void 0 ? shininess : 30 } }; const pbrUniformStruct = { ...specularUniformStruct, metallic: { type: "f32", value: metallic !== void 0 ? metallic : 1 }, roughness: { type: "f32", value: roughness !== void 0 ? roughness : 1 }, transmission: { type: "f32", value: transmission !== void 0 ? transmission : 0 }, ior: { type: "f32", value: ior !== void 0 ? ior : 1.5 }, dispersion: { type: "f32", value: dispersion !== void 0 ? dispersion : 0 }, thickness: { type: "f32", value: thickness !== void 0 ? thickness : 0 }, attenuationDistance: { type: "f32", value: attenuationDistance !== void 0 ? attenuationDistance : Infinity }, attenuationColor: { type: "vec3f", value: attenuationColor !== void 0 ? colorSpace === "srgb" ? sRGBToLinear(attenuationColor.clone()) : attenuationColor.clone() : new Vec3(1) }, multiscatterColor: { type: "vec3f", value: multiscatterColor !== void 0 ? colorSpace === "srgb" ? sRGBToLinear(multiscatterColor.clone()) : multiscatterColor.clone() : new Vec3(0) }, scatterAnisotropy: { type: "f32", value: scatterAnisotropy !== void 0 ? scatterAnisotropy : 0 }, sheenColor: { type: "vec3f", value: sheenColor !== void 0 ? colorSpace === "srgb" ? sRGBToLinear(sheenColor.clone()) : sheenColor.clone() : new Vec3(0) }, sheenRoughness: { type: "f32", value: sheenRoughness !== void 0 ? sheenRoughness : 0 }, anisotropy: { type: "f32", value: anisotropy !== void 0 ? anisotropy : 0 }, anisotropyVector: { type: "vec2f", value: anisotropyVector !== void 0 ? anisotropyVector.clone() : new Vec2(1, 0) }, clearcoat: { type: "f32", value: clearcoat !== void 0 ? clearcoat : 0 }, clearcoatRoughness: { type: "f32", value: clearcoatRoughness !== void 0 ? clearcoatRoughness : 0 }, clearcoatNormalScale: { type: "vec2f", value: clearcoatNormalScale !== void 0 ? clearcoatNormalScale.clone() : new Vec2(1) }, iridescence: { type: "f32", value: iridescence !== void 0 ? iridescence : 0 }, iridescenceIOR: { type: "f32", value: iridescenceIOR !== void 0 ? iridescenceIOR : 1.3 }, iridescenceThicknessRange: { type: "vec2f", value: iridescenceThicknessRange !== void 0 ? iridescenceThicknessRange.clone() : new Vec2(100, 400) }, diffuseTransmission: { type: "f32", value: diffuseTransmission !== void 0 ? diffuseTransmission : 0 }, diffuseTransmissionColor: { type: "vec3f", value: diffuseTransmissionColor !== void 0 ? colorSpace === "srgb" ? sRGBToLinear(diffuseTransmissionColor.clone()) : diffuseTransmissionColor.clone() : new Vec3(1) }, ...environmentMap && { envRotation: { type: "mat3x3f", value: environmentMap.rotationMatrix }, envDiffuseIntensity: { type: "f32", value: environmentMap.options.diffuseIntensity }, envSpecularIntensity: { type: "f32", value: environmentMap.options.specularIntensity } } }; return { visibility: ["fragment"], struct: (() => { switch (shading) { case "Unlit": return baseUniformStruct; case "Lambert": return diffuseUniformStruct; case "Phong": return phongUniformStruct; default: return pbrUniformStruct; } })() }; } /** * Get all the material {@link ShaderTextureDescriptor} as an array. * @param parameters - {@link GetMaterialTexturesDescriptors} parameters. * @returns - Array of {@link ShaderTextureDescriptor} to use. */ static getMaterialTexturesDescriptors(parameters) { const { shading, baseColorTexture, normalTexture, emissiveTexture, occlusionTexture, metallicRoughnessTexture, specularTexture, specularFactorTexture, specularColorTexture, transmissionThicknessTexture, transmissionTexture, thicknessTexture, sheenTexture, sheenColorTexture, sheenRoughnessTexture, anisotropyTexture, clearcoatTexture, clearcoatFactorTexture, clearcoatRoughnessTexture, clearcoatNormalTexture, iridescenceTexture, iridescenceFactorTexture, iridescenceThicknessTexture, diffuseTransmissionTexture, diffuseTransmissionFactorTexture, diffuseTransmissionColorTexture } = parameters; const baseTextures = [ baseColorTexture, emissiveTexture, occlusionTexture ]; const diffuseTextures = [...baseTextures, normalTexture]; const specularTextures = [ ...diffuseTextures, metallicRoughnessTexture, specularTexture, specularFactorTexture, specularColorTexture ]; const pbrTextures = [ ...specularTextures, transmissionThicknessTexture, transmissionTexture, thicknessTexture, sheenTexture, sheenColorTexture, sheenRoughnessTexture, anisotropyTexture, clearcoatTexture, clearcoatFactorTexture, clearcoatRoughnessTexture, clearcoatNormalTexture, iridescenceTexture, iridescenceFactorTexture, iridescenceThicknessTexture, diffuseTransmissionTexture, diffuseTransmissionFactorTexture, diffuseTransmissionColorTexture ]; return (() => { switch (shading) { case "Unlit": return baseTextures; case "Lambert": return diffuseTextures; case "Phong": return specularTextures; default: return pbrTextures; } })().filter(Boolean); } /** * Generate the {@link LitMesh} vertex shader code. * @param parameters - {@link VertexShaderInputParams} used to generate the vertex shader code. * @returns - The vertex shader generated based on the provided parameters. */ static getVertexShaderCode(parameters) { return getVertexShaderCode(parameters); } /** * Generate the {@link LitMesh} fragment shader. * @param parameters - {@link FragmentShaderInputParams} used to build the fragment shader. * @returns - The fragment shader generated based on the provided parameters. */ static getFragmentShaderCode(parameters) { return getFragmentShaderCode(parameters); } }; //#endregion export { LitMesh };