UNPKG

playcanvas

Version:

Open-source WebGL/WebGPU 3D engine for the web

244 lines (243 loc) 7.99 kB
import { SEMANTIC_POSITION, SEMANTIC_NORMAL, SEMANTIC_TANGENT, SEMANTIC_TEXCOORD0, SEMANTIC_TEXCOORD1, SEMANTIC_TEXCOORD2, SEMANTIC_TEXCOORD3, SEMANTIC_TEXCOORD4, SEMANTIC_TEXCOORD5, SEMANTIC_TEXCOORD6, SEMANTIC_TEXCOORD7, SEMANTIC_COLOR, SEMANTIC_BLENDINDICES, SEMANTIC_BLENDWEIGHT, SHADERLANGUAGE_WGSL, SHADERLANGUAGE_GLSL, primitiveGlslToWgslTypeMap } from "./constants.js"; import gles3FS from "./shader-chunks/frag/gles3.js"; import gles3VS from "./shader-chunks/vert/gles3.js"; import webgpuFS from "./shader-chunks/frag/webgpu.js"; import webgpuVS from "./shader-chunks/vert/webgpu.js"; import wgslFS from "./shader-chunks/frag/webgpu-wgsl.js"; import wgslVS from "./shader-chunks/vert/webgpu-wgsl.js"; import sharedGLSL from "./shader-chunks/frag/shared.js"; import sharedWGSL from "./shader-chunks/frag/shared-wgsl.js"; import halfTypes from "./shader-chunks/frag/half-types.js"; const _attrib2Semantic = { vertex_position: SEMANTIC_POSITION, vertex_normal: SEMANTIC_NORMAL, vertex_tangent: SEMANTIC_TANGENT, vertex_texCoord0: SEMANTIC_TEXCOORD0, vertex_texCoord1: SEMANTIC_TEXCOORD1, vertex_texCoord2: SEMANTIC_TEXCOORD2, vertex_texCoord3: SEMANTIC_TEXCOORD3, vertex_texCoord4: SEMANTIC_TEXCOORD4, vertex_texCoord5: SEMANTIC_TEXCOORD5, vertex_texCoord6: SEMANTIC_TEXCOORD6, vertex_texCoord7: SEMANTIC_TEXCOORD7, vertex_color: SEMANTIC_COLOR, vertex_boneIndices: SEMANTIC_BLENDINDICES, vertex_boneWeights: SEMANTIC_BLENDWEIGHT }; class ShaderDefinitionUtils { static createDefinition(device, options) { const normalizedOutputTypes = (options2) => { let fragmentOutputTypes = options2.fragmentOutputTypes ?? "vec4"; if (!Array.isArray(fragmentOutputTypes)) { fragmentOutputTypes = [fragmentOutputTypes]; } return fragmentOutputTypes; }; const getDefines = (gpu, gl2, isVertex, options2) => { const deviceIntro = device.isWebGPU ? gpu : gl2; let attachmentsDefine = ""; if (!isVertex) { const fragmentOutputTypes = normalizedOutputTypes(options2); for (let i = 0; i < device.maxColorAttachments; i++) { attachmentsDefine += `#define COLOR_ATTACHMENT_${i} `; const outType = fragmentOutputTypes[i] ?? "vec4"; attachmentsDefine += `#define outType_${i} ${outType} `; } } return attachmentsDefine + deviceIntro; }; const getDefinesWgsl = (isVertex, options2) => { let code = ShaderDefinitionUtils.getWGSLEnables(device, isVertex ? "vertex" : "fragment"); if (!isVertex) { const fragmentOutputTypes = normalizedOutputTypes(options2); for (let i = 0; i < device.maxColorAttachments; i++) { const glslOutType = fragmentOutputTypes[i] ?? "vec4"; const wgslOutType = primitiveGlslToWgslTypeMap.get(glslOutType); code += `alias pcOutType${i} = ${wgslOutType}; `; } } return code; }; const name = options.name ?? "Untitled"; let vertCode; let fragCode; const vertexDefinesCode = ShaderDefinitionUtils.getDefinesCode(device, options.vertexDefines); const fragmentDefinesCode = ShaderDefinitionUtils.getDefinesCode(device, options.fragmentDefines); const wgsl = options.shaderLanguage === SHADERLANGUAGE_WGSL; if (wgsl) { vertCode = ` ${getDefinesWgsl(true, options)} ${vertexDefinesCode} ${halfTypes} ${wgslVS} ${sharedWGSL} ${options.vertexCode} `; fragCode = ` ${getDefinesWgsl(false, options)} ${fragmentDefinesCode} ${halfTypes} ${wgslFS} ${sharedWGSL} ${options.fragmentCode} `; } else { vertCode = `${ShaderDefinitionUtils.versionCode(device) + getDefines(webgpuVS, gles3VS, true, options) + vertexDefinesCode + ShaderDefinitionUtils.precisionCode(device)} ${sharedGLSL} ${ShaderDefinitionUtils.getShaderNameCode(name)} ${options.vertexCode}`; fragCode = `${(options.fragmentPreamble || "") + ShaderDefinitionUtils.versionCode(device) + getDefines(webgpuFS, gles3FS, false, options) + fragmentDefinesCode + ShaderDefinitionUtils.precisionCode(device)} ${sharedGLSL} ${ShaderDefinitionUtils.getShaderNameCode(name)} ${options.fragmentCode}`; } return { name, shaderLanguage: options.shaderLanguage ?? SHADERLANGUAGE_GLSL, attributes: options.attributes, vshader: vertCode, vincludes: options.vertexIncludes, fincludes: options.fragmentIncludes, fshader: fragCode, feedbackVaryings: options.feedbackVaryings, useTransformFeedback: options.useTransformFeedback, meshUniformBufferFormat: options.meshUniformBufferFormat, meshBindGroupFormat: options.meshBindGroupFormat }; } static getWGSLEnables(device, shaderType) { let code = ""; if (device.supportsShaderF16) { code += "enable f16;\n"; } if (shaderType === "fragment" && device.supportsPrimitiveIndex) { code += "enable primitive_index;\n"; } if (device.supportsSubgroups) { code += "enable subgroups;\n"; } if (device.supportsSubgroupId) { code += "requires subgroup_id;\n"; } if (shaderType === "compute" && device.supportsLinearIndexing) { code += "requires linear_indexing;\n"; } if (device.supportsUnrestrictedPointerParameters) { code += "requires unrestricted_pointer_parameters;\n"; } if (device.supportsPointerCompositeAccess) { code += "requires pointer_composite_access;\n"; } if (device.supportsPacked4x8IntegerDotProduct) { code += "requires packed_4x8_integer_dot_product;\n"; } if (device.supportsTextureAndSamplerLet) { code += "requires texture_and_sampler_let;\n"; } return code; } static getDefinesCode(device, defines) { let code = ""; device.capsDefines.forEach((value, key) => { code += `#define ${key} ${value} `; }); code += "\n"; defines?.forEach((value, key) => { code += `#define ${key} ${value} `; }); code += "\n"; return code; } // SpectorJS integration static getShaderNameCode(name) { return `#define SHADER_NAME ${name} `; } static versionCode(device) { return device.isWebGPU ? "#version 450\n" : "#version 300 es\n"; } static precisionCode(device, forcePrecision) { if (forcePrecision && forcePrecision !== "highp" && forcePrecision !== "mediump" && forcePrecision !== "lowp") { forcePrecision = null; } if (forcePrecision) { if (forcePrecision === "highp" && device.maxPrecision !== "highp") { forcePrecision = "mediump"; } if (forcePrecision === "mediump" && device.maxPrecision === "lowp") { forcePrecision = "lowp"; } } const precision = forcePrecision ? forcePrecision : device.precision; const code = ` precision ${precision} float; precision ${precision} int; precision ${precision} usampler2D; precision ${precision} isampler2D; precision ${precision} sampler2DShadow; precision ${precision} samplerCubeShadow; precision ${precision} sampler2DArray; `; return code; } static collectAttributes(vsCode) { const attribs = {}; let attrs = 0; let found = vsCode.indexOf("attribute"); while (found >= 0) { if (found > 0 && vsCode[found - 1] === "/") break; let ignore = false; if (found > 0) { let startOfLine = vsCode.lastIndexOf("\n", found); startOfLine = startOfLine !== -1 ? startOfLine + 1 : 0; const lineStartString = vsCode.substring(startOfLine, found); if (lineStartString.includes("#")) { ignore = true; } } if (!ignore) { const endOfLine = vsCode.indexOf(";", found); const startOfAttribName = vsCode.lastIndexOf(" ", endOfLine); const attribName = vsCode.substring(startOfAttribName + 1, endOfLine); if (attribs[attribName]) { } else { const semantic = _attrib2Semantic[attribName]; if (semantic !== void 0) { attribs[attribName] = semantic; } else { attribs[attribName] = `ATTR${attrs}`; attrs++; } } } found = vsCode.indexOf("attribute", found + 1); } return attribs; } } export { ShaderDefinitionUtils };