UNPKG

playcanvas

Version:

PlayCanvas WebGL game engine

169 lines (166 loc) 8.63 kB
import { Shader } from '../../platform/graphics/shader.js'; import { ShaderUtils } from '../../platform/graphics/shader-utils.js'; import { shaderChunks } from './chunks/chunks.js'; import { getProgramLibrary } from './get-program-library.js'; import { Debug } from '../../core/debug.js'; import { ShaderGenerator } from './programs/shader-generator.js'; import { ShaderPass } from '../shader-pass.js'; function _extends() { _extends = Object.assign || function(target) { for(var i = 1; i < arguments.length; i++){ var source = arguments[i]; for(var key in source){ if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } /** * @import { GraphicsDevice } from '../../platform/graphics/graphics-device.js' * @import { ShaderProcessorOptions } from '../../platform/graphics/shader-processor-options.js' * @import { CameraShaderParams } from '../camera-shader-params.js' * @import { Material, ShaderVariantParams } from '../materials/material.js' */ /** * Create a shader from named shader chunks. * * @param {GraphicsDevice} device - The graphics device. * @param {string} vsName - The vertex shader chunk name. * @param {string} fsName - The fragment shader chunk name. * @param {boolean | Record<string, boolean | string | string[]>} [useTransformFeedback] - Whether * to use transform feedback. Defaults to false. * @param {object} [shaderDefinitionOptions] - Additional options that will be added to the shader * definition. * @param {boolean} [shaderDefinitionOptions.useTransformFeedback] - Whether to use transform * feedback. Defaults to false. * @param {string | string[]} [shaderDefinitionOptions.fragmentOutputTypes] - Fragment shader * output types, which default to vec4. Passing a string will set the output type for all color * attachments. Passing an array will set the output type for each color attachment. * @see ShaderUtils.createDefinition * @returns {Shader} The newly created shader. * @category Graphics */ function createShader(device, vsName, fsName, useTransformFeedback, shaderDefinitionOptions) { if (useTransformFeedback === void 0) useTransformFeedback = false; if (shaderDefinitionOptions === void 0) shaderDefinitionOptions = {}; // Normalize arguments to allow passing shaderDefinitionOptions as the 6th argument if (typeof useTransformFeedback === 'boolean') { shaderDefinitionOptions.useTransformFeedback = useTransformFeedback; } else if (typeof useTransformFeedback === 'object') { shaderDefinitionOptions = _extends({}, shaderDefinitionOptions, useTransformFeedback); } return new Shader(device, ShaderUtils.createDefinition(device, _extends({}, shaderDefinitionOptions, { name: vsName + "_" + fsName, vertexCode: shaderChunks[vsName], fragmentCode: shaderChunks[fsName] }))); } /** * Create a shader from the supplied source code. Note that this function adds additional shader * blocks to both vertex and fragment shaders, which allow the shader to use more features and * compile on both WebGL and WebGPU. Specifically, these blocks are added, and should not be * part of provided vsCode and fsCode: shader version, shader precision, commonly used extensions. * * @param {GraphicsDevice} device - The graphics device. * @param {string} vsCode - The vertex shader code. * @param {string} fsCode - The fragment shader code. * @param {string} uniqueName - Unique name for the shader. If a shader with this name already * exists, it will be returned instead of a new shader instance. * @param {Object<string, string>} [attributes] - Object detailing the mapping of vertex shader * attribute names to semantics SEMANTIC_*. This enables the engine to match vertex buffer data as * inputs to the shader. Defaults to undefined, which generates the default attributes. * @param {boolean | Record<string, boolean | string | string[]>} [useTransformFeedback] - Whether * to use transform feedback. Defaults to false. * @param {object} [shaderDefinitionOptions] - Additional options that will be added to the shader * definition. * @param {boolean} [shaderDefinitionOptions.useTransformFeedback] - Whether to use transform * feedback. Defaults to false. * @param {string | string[]} [shaderDefinitionOptions.fragmentOutputTypes] - Fragment shader * output types, which default to vec4. Passing a string will set the output type for all color * attachments. Passing an array will set the output type for each color attachment. * @see ShaderUtils.createDefinition * @returns {Shader} The newly created shader. * @category Graphics */ function createShaderFromCode(device, vsCode, fsCode, uniqueName, attributes, useTransformFeedback, shaderDefinitionOptions) { if (useTransformFeedback === void 0) useTransformFeedback = false; if (shaderDefinitionOptions === void 0) shaderDefinitionOptions = {}; // the function signature has changed, fail if called incorrectly Debug.assert(typeof attributes !== 'boolean'); // Normalize arguments to allow passing shaderDefinitionOptions as the 6th argument if (typeof useTransformFeedback === 'boolean') { shaderDefinitionOptions.useTransformFeedback = useTransformFeedback; } else if (typeof useTransformFeedback === 'object') { shaderDefinitionOptions = _extends({}, shaderDefinitionOptions, useTransformFeedback); } var programLibrary = getProgramLibrary(device); var shader = programLibrary.getCachedShader(uniqueName); if (!shader) { shader = new Shader(device, ShaderUtils.createDefinition(device, _extends({}, shaderDefinitionOptions, { name: uniqueName, vertexCode: vsCode, fragmentCode: fsCode, attributes: attributes }))); programLibrary.setCachedShader(uniqueName, shader); } return shader; } class ShaderGeneratorPassThrough extends ShaderGenerator { generateKey(options) { return this.key; } createShaderDefinition(device, options) { return this.shaderDefinition; } constructor(key, shaderDefinition){ super(); this.key = key; this.shaderDefinition = shaderDefinition; } } /** * Process shader using shader processing options, utilizing cache of the ProgramLibrary * * @param {Shader} shader - The shader to be processed. * @param {ShaderProcessorOptions} processingOptions - The shader processing options. * @returns {Shader} The processed shader. */ function processShader(shader, processingOptions) { Debug.assert(shader); var shaderDefinition = shader.definition; var _shaderDefinition_name; // 'shader' generator for a material - simply return existing shader definition. Use generator and getProgram // to allow for shader processing to be cached var name = (_shaderDefinition_name = shaderDefinition.name) != null ? _shaderDefinition_name : 'shader'; // unique name based of the shader id var key = name + "-id-" + shader.id; var materialGenerator = new ShaderGeneratorPassThrough(key, shaderDefinition); // temporarily register the program generator var libraryModuleName = 'shader'; var library = getProgramLibrary(shader.device); Debug.assert(!library.isRegistered(libraryModuleName)); library.register(libraryModuleName, materialGenerator); // generate shader variant - its the same shader, but with different processing options var variant = library.getProgram(libraryModuleName, {}, processingOptions); // unregister it again library.unregister(libraryModuleName); return variant; } /** * Create a map of defines used for shader generation for a material. * * @param {Material} material - The material to create the shader defines for. * @param {ShaderVariantParams} params - The shader variant parameters. * @returns {Map<string, string>} The map of shader defines. * @ignore */ var getCoreDefines = (material, params)=>{ // merge both maps, with camera shader params taking precedence var defines = new Map(material.defines); params.cameraShaderParams.defines.forEach((value, key)=>defines.set(key, value)); // add pass defines var shaderPassInfo = ShaderPass.get(params.device).getByIndex(params.pass); shaderPassInfo.defines.forEach((value, key)=>defines.set(key, value)); return defines; }; export { createShader, createShaderFromCode, getCoreDefines, processShader };