playcanvas
Version:
PlayCanvas WebGL game engine
421 lines (418 loc) • 19.6 kB
JavaScript
import { SHADER_FORWARD, SPRITE_RENDERMODE_SLICED, SPRITE_RENDERMODE_TILED, FRESNEL_SCHLICK, BLEND_NONE, DITHER_NONE, DITHER_BAYER8 } from '../../constants.js';
import { ShaderPass } from '../../shader-pass.js';
import { LitShader } from './lit-shader.js';
import { ChunkBuilder } from '../chunk-builder.js';
import { ChunkUtils } from '../chunk-utils.js';
import { StandardMaterialOptions } from '../../materials/standard-material-options.js';
import { LitOptionsUtils } from './lit-options-utils.js';
import { ShaderGenerator } from './shader-generator.js';
import { ShaderUtils } from '../../../platform/graphics/shader-utils.js';
import { SHADERTAG_MATERIAL } from '../../../platform/graphics/constants.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);
}
var _matTex2D = [];
var buildPropertiesList = (options)=>{
return Object.keys(options).filter((key)=>key !== 'litOptions').sort();
};
class ShaderGeneratorStandard extends ShaderGenerator {
generateKey(options) {
var props;
if (options === this.optionsContextMin) {
if (!this.propsMin) this.propsMin = buildPropertiesList(options);
props = this.propsMin;
} else if (options === this.optionsContext) {
if (!this.props) this.props = buildPropertiesList(options);
props = this.props;
} else {
props = buildPropertiesList(options);
}
var definesHash = ShaderGenerator.definesHash(options.defines);
var key = "standard:\n" + definesHash + "\n" + props.map((prop)=>prop + options[prop]).join('\n') + LitOptionsUtils.generateKey(options.litOptions);
return key;
}
_getUvSourceExpression(transformPropName, uVPropName, options) {
var transformId = options[transformPropName];
var uvChannel = options[uVPropName];
var isMainPass = options.litOptions.pass === SHADER_FORWARD;
var expression;
if (isMainPass && options.litOptions.nineSlicedMode === SPRITE_RENDERMODE_SLICED) {
expression = 'nineSlicedUv';
} else if (isMainPass && options.litOptions.nineSlicedMode === SPRITE_RENDERMODE_TILED) {
expression = 'nineSlicedUv';
} else {
if (transformId === 0) {
expression = "vUv" + uvChannel;
} else {
expression = "vUV" + uvChannel + "_" + transformId;
}
if (options.heightMap && transformPropName !== 'heightMapTransform') {
expression += ' + dUvOffset';
}
}
return expression;
}
_addMapDef(name, enabled) {
return enabled ? "#define " + name + "\n" : "#undef " + name + "\n";
}
_addMapDefs(float, color, vertex, map, invert) {
return this._addMapDef('MAPFLOAT', float) + this._addMapDef('MAPCOLOR', color) + this._addMapDef('MAPVERTEX', vertex) + this._addMapDef('MAPTEXTURE', map) + this._addMapDef('MAPINVERT', invert);
}
_addMap(propName, chunkName, options, chunks, mapping, encoding) {
if (encoding === void 0) encoding = null;
var mapPropName = "" + propName + "Map";
var uVPropName = "" + mapPropName + "Uv";
var identifierPropName = "" + mapPropName + "Identifier";
var transformPropName = "" + mapPropName + "Transform";
var channelPropName = "" + mapPropName + "Channel";
var vertexColorChannelPropName = "" + propName + "VertexColorChannel";
var tintPropName = "" + propName + "Tint";
var vertexColorPropName = "" + propName + "VertexColor";
var detailModePropName = "" + propName + "Mode";
var invertName = "" + propName + "Invert";
var tintOption = options[tintPropName];
var vertexColorOption = options[vertexColorPropName];
var textureOption = options[mapPropName];
var textureIdentifier = options[identifierPropName];
var detailModeOption = options[detailModePropName];
var subCode = chunks[chunkName];
if (textureOption) {
var uv = this._getUvSourceExpression(transformPropName, uVPropName, options);
subCode = subCode.replace(/\$UV/g, uv).replace(/\$CH/g, options[channelPropName]);
if (mapping && subCode.search(/\$SAMPLER/g) !== -1) {
var samplerName = "texture_" + mapPropName;
var alias = mapping[textureIdentifier];
if (alias) {
samplerName = alias;
} else {
mapping[textureIdentifier] = samplerName;
}
subCode = subCode.replace(/\$SAMPLER/g, samplerName);
}
if (encoding) {
if (options[channelPropName] === 'aaa') {
subCode = subCode.replace(/\$DECODE/g, 'passThrough');
} else {
subCode = subCode.replace(/\$DECODE/g, ChunkUtils.decodeFunc(encoding));
}
if (subCode.indexOf('$texture2DSAMPLE')) {
var decodeTable = {
linear: 'texture2D',
srgb: 'texture2DSRGB',
rgbm: 'texture2DRGBM',
rgbe: 'texture2DRGBE'
};
subCode = subCode.replace(/\$texture2DSAMPLE/g, decodeTable[encoding] || 'texture2D');
}
}
}
if (vertexColorOption) {
subCode = subCode.replace(/\$VC/g, options[vertexColorChannelPropName]);
}
if (detailModeOption) {
subCode = subCode.replace(/\$DETAILMODE/g, detailModeOption);
}
var isFloatTint = !!(tintOption & 1);
var isVecTint = !!(tintOption & 2);
var invertOption = !!options[invertName];
subCode = this._addMapDefs(isFloatTint, isVecTint, vertexColorOption, textureOption, invertOption) + subCode;
return subCode.replace(/\$/g, '');
}
_correctChannel(p, chan, _matTex2D) {
if (_matTex2D[p] > 0) {
if (_matTex2D[p] < chan.length) {
return chan.substring(0, _matTex2D[p]);
} else if (_matTex2D[p] > chan.length) {
var str = chan;
var chr = str.charAt(str.length - 1);
var addLen = _matTex2D[p] - str.length;
for(var i = 0; i < addLen; i++)str += chr;
return str;
}
return chan;
}
}
createVertexShader(litShader, options) {
var useUv = [];
var useUnmodifiedUv = [];
var mapTransforms = [];
var maxUvSets = 2;
for(var p in _matTex2D){
var mapName = "" + p + "Map";
if (options["" + p + "VertexColor"]) {
var colorChannelName = "" + p + "VertexColorChannel";
options[colorChannelName] = this._correctChannel(p, options[colorChannelName], _matTex2D);
}
if (options[mapName]) {
var channelName = "" + mapName + "Channel";
var transformName = "" + mapName + "Transform";
var uvName = "" + mapName + "Uv";
options[uvName] = Math.min(options[uvName], maxUvSets - 1);
options[channelName] = this._correctChannel(p, options[channelName], _matTex2D);
var uvSet = options[uvName];
useUv[uvSet] = true;
useUnmodifiedUv[uvSet] = useUnmodifiedUv[uvSet] || options[mapName] && !options[transformName];
if (options[transformName]) {
mapTransforms.push({
name: p,
id: options[transformName],
uv: options[uvName]
});
}
}
}
if (options.forceUv1) {
useUv[1] = true;
useUnmodifiedUv[1] = useUnmodifiedUv[1] !== undefined ? useUnmodifiedUv[1] : true;
}
litShader.generateVertexShader(useUv, useUnmodifiedUv, mapTransforms);
}
createShaderDefinition(device, options) {
var shaderPassInfo = ShaderPass.get(device).getByIndex(options.litOptions.pass);
var isForwardPass = shaderPassInfo.isForward;
var litShader = new LitShader(device, options.litOptions);
this.createVertexShader(litShader, options);
var textureMapping = {};
options.litOptions.fresnelModel = options.litOptions.fresnelModel === 0 ? FRESNEL_SCHLICK : options.litOptions.fresnelModel;
var decl = new ChunkBuilder();
var code = new ChunkBuilder();
var func = new ChunkBuilder();
var args = new ChunkBuilder();
var lightingUv = '';
if (options.litOptions.nineSlicedMode === SPRITE_RENDERMODE_TILED) {
decl.append('const float textureBias = -1000.0;');
} else {
decl.append('uniform float textureBias;');
}
if (isForwardPass) {
if (options.heightMap) {
decl.append('vec2 dUvOffset;');
code.append(this._addMap('height', 'parallaxPS', options, litShader.chunks, textureMapping));
func.append('getParallax();');
}
if (options.litOptions.blendType !== BLEND_NONE || options.litOptions.alphaTest || options.litOptions.alphaToCoverage || options.litOptions.opacityDither !== DITHER_NONE) {
decl.append('float dAlpha;');
code.append(this._addMap('opacity', 'opacityPS', options, litShader.chunks, textureMapping));
func.append('getOpacity();');
args.append('litArgs_opacity = dAlpha;');
if (options.litOptions.alphaTest) {
code.append(litShader.chunks.alphaTestPS);
func.append('alphaTest(dAlpha);');
}
var opacityDither = options.litOptions.opacityDither;
if (opacityDither !== DITHER_NONE) {
if (opacityDither === DITHER_BAYER8) {
decl.append(litShader.chunks.bayerPS);
}
decl.append("#define DITHER_" + opacityDither.toUpperCase() + "\n");
decl.append(litShader.chunks.opacityDitherPS);
func.append('opacityDither(dAlpha, 0.0);');
}
} else {
decl.append('float dAlpha = 1.0;');
}
if (litShader.needsNormal) {
if (options.normalMap || options.clearCoatNormalMap) {
code.append(options.packedNormal ? litShader.chunks.normalXYPS : litShader.chunks.normalXYZPS);
if (!options.litOptions.hasTangents) {
var baseName = options.normalMap ? 'normalMap' : 'clearCoatNormalMap';
lightingUv = this._getUvSourceExpression("" + baseName + "Transform", "" + baseName + "Uv", options);
}
}
decl.append('vec3 dNormalW;');
code.append(this._addMap('normalDetail', 'normalDetailMapPS', options, litShader.chunks, textureMapping));
code.append(this._addMap('normal', 'normalMapPS', options, litShader.chunks, textureMapping));
func.append('getNormal();');
args.append('litArgs_worldNormal = dNormalW;');
}
if (litShader.needsSceneColor) {
decl.append('uniform sampler2D uSceneColorMap;');
}
if (litShader.needsScreenSize) {
decl.append('uniform vec4 uScreenSize;');
}
if (litShader.needsTransforms) {
decl.append('uniform mat4 matrix_viewProjection;');
decl.append('uniform mat4 matrix_model;');
}
if (options.diffuseDetail || options.aoDetail) {
code.append(litShader.chunks.detailModesPS);
}
decl.append('vec3 dAlbedo;');
if (options.diffuseDetail) {
code.append(this._addMap('diffuseDetail', 'diffuseDetailMapPS', options, litShader.chunks, textureMapping, options.diffuseDetailEncoding));
}
code.append(this._addMap('diffuse', 'diffusePS', options, litShader.chunks, textureMapping, options.diffuseEncoding));
func.append('getAlbedo();');
args.append('litArgs_albedo = dAlbedo;');
if (options.litOptions.useRefraction) {
decl.append('float dTransmission;');
code.append(this._addMap('refraction', 'transmissionPS', options, litShader.chunks, textureMapping));
func.append('getRefraction();');
args.append('litArgs_transmission = dTransmission;');
decl.append('float dThickness;');
code.append(this._addMap('thickness', 'thicknessPS', options, litShader.chunks, textureMapping));
func.append('getThickness();');
args.append('litArgs_thickness = dThickness;');
if (options.litOptions.dispersion) {
args.append('litArgs_dispersion = material_dispersion;');
}
}
if (options.litOptions.useIridescence) {
decl.append('float dIridescence;');
code.append(this._addMap('iridescence', 'iridescencePS', options, litShader.chunks, textureMapping));
func.append('getIridescence();');
args.append('litArgs_iridescence_intensity = dIridescence;');
decl.append('float dIridescenceThickness;');
code.append(this._addMap('iridescenceThickness', 'iridescenceThicknessPS', options, litShader.chunks, textureMapping));
func.append('getIridescenceThickness();');
args.append('litArgs_iridescence_thickness = dIridescenceThickness;');
}
if (litShader.lighting && options.litOptions.useSpecular || litShader.reflections) {
decl.append('vec3 dSpecularity;');
decl.append('float dGlossiness;');
if (options.litOptions.useSheen) {
decl.append('vec3 sSpecularity;');
code.append(this._addMap('sheen', 'sheenPS', options, litShader.chunks, textureMapping, options.sheenEncoding));
func.append('getSheen();');
args.append('litArgs_sheen_specularity = sSpecularity;');
decl.append('float sGlossiness;');
code.append(this._addMap('sheenGloss', 'sheenGlossPS', options, litShader.chunks, textureMapping));
func.append('getSheenGlossiness();');
args.append('litArgs_sheen_gloss = sGlossiness;');
}
if (options.litOptions.useMetalness) {
decl.append('float dMetalness;');
code.append(this._addMap('metalness', 'metalnessPS', options, litShader.chunks, textureMapping));
func.append('getMetalness();');
args.append('litArgs_metalness = dMetalness;');
decl.append('float dIor;');
code.append(this._addMap('ior', 'iorPS', options, litShader.chunks, textureMapping));
func.append('getIor();');
args.append('litArgs_ior = dIor;');
}
if (options.litOptions.useSpecularityFactor) {
decl.append('float dSpecularityFactor;');
code.append(this._addMap('specularityFactor', 'specularityFactorPS', options, litShader.chunks, textureMapping));
func.append('getSpecularityFactor();');
args.append('litArgs_specularityFactor = dSpecularityFactor;');
}
if (options.useSpecularColor) {
code.append(this._addMap('specular', 'specularPS', options, litShader.chunks, textureMapping, options.specularEncoding));
} else {
code.append('void getSpecularity() { dSpecularity = vec3(1); }');
}
code.append(this._addMap('gloss', 'glossPS', options, litShader.chunks, textureMapping));
func.append('getGlossiness();');
func.append('getSpecularity();');
args.append('litArgs_specularity = dSpecularity;');
args.append('litArgs_gloss = dGlossiness;');
} else {
decl.append('vec3 dSpecularity = vec3(0.0);');
decl.append('float dGlossiness = 0.0;');
}
if (options.aoDetail) {
code.append(this._addMap('aoDetail', 'aoDetailMapPS', options, litShader.chunks, textureMapping));
}
if (options.aoMap || options.aoVertexColor || options.useAO) {
decl.append('float dAo;');
code.append(this._addMap('ao', 'aoPS', options, litShader.chunks, textureMapping));
func.append('getAO();');
args.append('litArgs_ao = dAo;');
}
decl.append('vec3 dEmission;');
code.append(this._addMap('emissive', 'emissivePS', options, litShader.chunks, textureMapping, options.emissiveEncoding));
func.append('getEmission();');
args.append('litArgs_emission = dEmission;');
if (options.litOptions.useClearCoat) {
decl.append('float ccSpecularity;');
decl.append('float ccGlossiness;');
decl.append('vec3 ccNormalW;');
code.append(this._addMap('clearCoat', 'clearCoatPS', options, litShader.chunks, textureMapping));
code.append(this._addMap('clearCoatGloss', 'clearCoatGlossPS', options, litShader.chunks, textureMapping));
code.append(this._addMap('clearCoatNormal', 'clearCoatNormalPS', options, litShader.chunks, textureMapping));
func.append('getClearCoat();');
func.append('getClearCoatGlossiness();');
func.append('getClearCoatNormal();');
args.append('litArgs_clearcoat_specularity = ccSpecularity;');
args.append('litArgs_clearcoat_gloss = ccGlossiness;');
args.append('litArgs_clearcoat_worldNormal = ccNormalW;');
}
if (options.lightMap || options.lightVertexColor) {
var lightmapDir = options.dirLightMap && options.litOptions.useSpecular;
var lightmapChunkPropName = lightmapDir ? 'lightmapDirPS' : 'lightmapSinglePS';
decl.append('vec3 dLightmap;');
if (lightmapDir) {
decl.append('vec3 dLightmapDir;');
}
code.append(this._addMap('light', lightmapChunkPropName, options, litShader.chunks, textureMapping, options.lightMapEncoding));
func.append('getLightMap();');
args.append('litArgs_lightmap = dLightmap;');
if (lightmapDir) {
args.append('litArgs_lightmapDir = dLightmapDir;');
}
}
} else {
var opacityShadowDither = options.litOptions.opacityShadowDither;
if (options.litOptions.alphaTest || opacityShadowDither) {
decl.append('float dAlpha;');
code.append(this._addMap('opacity', 'opacityPS', options, litShader.chunks, textureMapping));
func.append('getOpacity();');
args.append('litArgs_opacity = dAlpha;');
if (options.litOptions.alphaTest) {
code.append(litShader.chunks.alphaTestPS);
func.append('alphaTest(dAlpha);');
}
if (opacityShadowDither !== DITHER_NONE) {
if (opacityShadowDither === DITHER_BAYER8) {
decl.append(litShader.chunks.bayerPS);
}
decl.append("#define DITHER_" + opacityShadowDither.toUpperCase() + "\n");
decl.append(litShader.chunks.opacityDitherPS);
func.append('opacityDither(dAlpha, 0.0);');
}
}
}
decl.append(litShader.chunks.litShaderArgsPS);
code.append("\n void evaluateFrontend() {\n " + func.code + "\n " + args.code + "\n }\n ");
for(var texture in textureMapping){
decl.append("uniform sampler2D " + textureMapping[texture] + ";");
}
litShader.generateFragmentShader(decl.code, code.code, lightingUv);
var includes = new Map(Object.entries(_extends({}, Object.getPrototypeOf(litShader.chunks), litShader.chunks, options.litOptions.chunks)));
var vDefines = litShader.vDefines;
options.defines.forEach((value, key)=>vDefines.set(key, value));
var fDefines = litShader.fDefines;
options.defines.forEach((value, key)=>fDefines.set(key, value));
var definition = ShaderUtils.createDefinition(device, {
name: 'StandardShader',
attributes: litShader.attributes,
vertexCode: litShader.vshader,
fragmentCode: litShader.fshader,
vertexIncludes: includes,
fragmentIncludes: includes,
fragmentDefines: fDefines,
vertexDefines: vDefines
});
if (litShader.shaderPassInfo.isForward) {
definition.tag = SHADERTAG_MATERIAL;
}
return definition;
}
constructor(...args){
super(...args), this.optionsContext = new StandardMaterialOptions(), this.optionsContextMin = new StandardMaterialOptions();
}
}
var standard = new ShaderGeneratorStandard();
export { _matTex2D, standard };