playcanvas
Version:
PlayCanvas WebGL game engine
169 lines (166 loc) • 8.63 kB
JavaScript
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 };