UNPKG

@luma.gl/shadertools

Version:

Shader module system for luma.gl

247 lines (214 loc) 6.69 kB
import { VERTEX_SHADER, FRAGMENT_SHADER } from './constants'; import { resolveModules } from './resolve-modules'; import { getPlatformShaderDefines, getVersionDefines } from './platform-defines'; import injectShader, { DECLARATION_INJECT_MARKER } from './inject-shader'; import transpileShader from './transpile-shader'; import { assert } from '../utils'; const INJECT_SHADER_DECLARATIONS = "\n\n".concat(DECLARATION_INJECT_MARKER, "\n\n"); const SHADER_TYPE = { [VERTEX_SHADER]: 'vertex', [FRAGMENT_SHADER]: 'fragment' }; const FRAGMENT_SHADER_PROLOGUE = "precision highp float;\n\n"; export function assembleShaders(gl, opts) { const { vs, fs } = opts; const modules = resolveModules(opts.modules || []); return { gl, vs: assembleShader(gl, Object.assign({}, opts, { source: vs, type: VERTEX_SHADER, modules })), fs: assembleShader(gl, Object.assign({}, opts, { source: fs, type: FRAGMENT_SHADER, modules })), getUniforms: assembleGetUniforms(modules) }; } function assembleShader(gl, _ref) { let { id, source, type, modules, defines = {}, hookFunctions = [], inject = {}, transpileToGLSL100 = false, prologue = true, log } = _ref; assert(typeof source === 'string', 'shader source must be a string'); const isVertex = type === VERTEX_SHADER; const sourceLines = source.split('\n'); let glslVersion = 100; let versionLine = ''; let coreSource = source; if (sourceLines[0].indexOf('#version ') === 0) { glslVersion = 300; versionLine = sourceLines[0]; coreSource = sourceLines.slice(1).join('\n'); } else { versionLine = "#version ".concat(glslVersion); } const allDefines = {}; modules.forEach(module => { Object.assign(allDefines, module.getDefines()); }); Object.assign(allDefines, defines); let assembledSource = prologue ? "".concat(versionLine, "\n").concat(getShaderName({ id, source, type }), "\n").concat(getShaderType({ type }), "\n").concat(getPlatformShaderDefines(gl), "\n").concat(getVersionDefines(gl, glslVersion, !isVertex), "\n").concat(getApplicationDefines(allDefines), "\n").concat(isVertex ? '' : FRAGMENT_SHADER_PROLOGUE, "\n") : "".concat(versionLine, "\n"); const hookFunctionMap = normalizeHookFunctions(hookFunctions); const hookInjections = {}; const declInjections = {}; const mainInjections = {}; for (const key in inject) { const injection = typeof inject[key] === 'string' ? { injection: inject[key], order: 0 } : inject[key]; const match = key.match(/^(v|f)s:(#)?([\w-]+)$/); if (match) { const hash = match[2]; const name = match[3]; if (hash) { if (name === 'decl') { declInjections[key] = [injection]; } else { mainInjections[key] = [injection]; } } else { hookInjections[key] = [injection]; } } else { mainInjections[key] = [injection]; } } for (const module of modules) { if (log) { module.checkDeprecations(coreSource, log); } const moduleSource = module.getModuleSource(type, glslVersion); assembledSource += moduleSource; const injections = module.injections[type]; for (const key in injections) { const match = key.match(/^(v|f)s:#([\w-]+)$/); if (match) { const name = match[2]; const injectionType = name === 'decl' ? declInjections : mainInjections; injectionType[key] = injectionType[key] || []; injectionType[key].push(injections[key]); } else { hookInjections[key] = hookInjections[key] || []; hookInjections[key].push(injections[key]); } } } assembledSource += INJECT_SHADER_DECLARATIONS; assembledSource = injectShader(assembledSource, type, declInjections); assembledSource += getHookFunctions(hookFunctionMap[type], hookInjections); assembledSource += coreSource; assembledSource = injectShader(assembledSource, type, mainInjections); assembledSource = transpileShader(assembledSource, transpileToGLSL100 ? 100 : glslVersion, isVertex); return assembledSource; } function assembleGetUniforms(modules) { return function getUniforms(opts) { const uniforms = {}; for (const module of modules) { const moduleUniforms = module.getUniforms(opts, uniforms); Object.assign(uniforms, moduleUniforms); } return uniforms; }; } function getShaderType(_ref2) { let { type } = _ref2; return "\n#define SHADER_TYPE_".concat(SHADER_TYPE[type].toUpperCase(), "\n"); } function getShaderName(_ref3) { let { id, source, type } = _ref3; const injectShaderName = id && typeof id === 'string' && source.indexOf('SHADER_NAME') === -1; return injectShaderName ? "\n#define SHADER_NAME ".concat(id, "_").concat(SHADER_TYPE[type], "\n\n") : ''; } function getApplicationDefines() { let defines = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; let count = 0; let sourceText = ''; for (const define in defines) { if (count === 0) { sourceText += '\n// APPLICATION DEFINES\n'; } count++; const value = defines[define]; if (value || Number.isFinite(value)) { sourceText += "#define ".concat(define.toUpperCase(), " ").concat(defines[define], "\n"); } } if (count === 0) { sourceText += '\n'; } return sourceText; } function getHookFunctions(hookFunctions, hookInjections) { let result = ''; for (const hookName in hookFunctions) { const hookFunction = hookFunctions[hookName]; result += "void ".concat(hookFunction.signature, " {\n"); if (hookFunction.header) { result += " ".concat(hookFunction.header); } if (hookInjections[hookName]) { const injections = hookInjections[hookName]; injections.sort((a, b) => a.order - b.order); for (const injection of injections) { result += " ".concat(injection.injection, "\n"); } } if (hookFunction.footer) { result += " ".concat(hookFunction.footer); } result += '}\n'; } return result; } function normalizeHookFunctions(hookFunctions) { const result = { vs: {}, fs: {} }; hookFunctions.forEach(hook => { let opts; if (typeof hook !== 'string') { opts = hook; hook = opts.hook; } else { opts = {}; } hook = hook.trim(); const [stage, signature] = hook.split(':'); const name = hook.replace(/\(.+/, ''); result[stage][name] = Object.assign(opts, { signature }); }); return result; } //# sourceMappingURL=assemble-shaders.js.map