// 着色程序id
var nextShaderProgramId = 0;
/**
* @private
*/
// 着色程序
function ShaderProgram(options) {
// 顶点、像所着色程序
var vertexShaderText = options.vertexShaderText;
var fragmentShaderText = options.fragmentShaderText;
if (typeof spector !== "undefined") {
// The #line statements common in Cesium shaders interfere with the ability of the
// SpectorJS to show errors on the correct line. So remove them when SpectorJS
// is active.
// 注释
vertexShaderText = vertexShaderText.replace(/^#line/gm, "//#line");
fragmentShaderText = fragmentShaderText.replace(/^#line/gm, "//#line");
}
// vs和fs中都有uniform但是精度不同
var modifiedFS = handleUniformPrecisionMismatches(
vertexShaderText,
fragmentShaderText
);
// 上下文谢谢
this._gl = options.gl;
// 编译是否错误的日志
this._logShaderCompilation = options.logShaderCompilation;
// gl的扩展功能,用来获取shader中的源码字符串
this._debugShaders = options.debugShaders;
// 属性和位置
this._attributeLocations = options.attributeLocations;
// 着色程序
this._program = undefined;
// 顶点属性数量
this._numberOfVertexAttributes = undefined;
// 顶点属性对象
this._vertexAttributes = undefined;
// uniform与对应名称的对象
this._uniformsByName = undefined;
// uniform数组
this._uniforms = undefined;
// 自动uniform对象
this._automaticUniforms = undefined;
// 手动uniform对象
this._manualUniforms = undefined;
// 修改过的uniform名字
this._duplicateUniformNames = modifiedFS.duplicateUniformNames;
this._cachedShader = undefined; // Used by ShaderCache
/**
* @private
*/
// 最大纹理单元索引
this.maximumTextureUnitIndex = undefined;
// 顶点着色器源码和处理过的代码
this._vertexShaderSource = options.vertexShaderSource;
this._vertexShaderText = options.vertexShaderText;
// 像素着色器源码和处理过的代码
this._fragmentShaderSource = options.fragmentShaderSource;
this._fragmentShaderText = modifiedFS.fragmentShaderText;
/**
* @private
*/
// 程序id全局唯一
this.id = nextShaderProgramId++;
}
// 缓存中获取着色程序(如果已经存在,不存在就创建)
ShaderProgram.fromCache = function (options) {
options = defaultValue(options, defaultValue.EMPTY_OBJECT);
//>>includeStart('debug', pragmas.debug);
Check.defined("options.context", options.context);
//>>includeEnd('debug');
// 缓存中获取着色程序(如果已经存在,不存在就创建)
return options.context.shaderCache.getShaderProgram(options);
};
// 替换着色程序
ShaderProgram.replaceCache = function (options) {
options = defaultValue(options, defaultValue.EMPTY_OBJECT);
//>>includeStart('debug', pragmas.debug);
Check.defined("options.context", options.context);
//>>includeEnd('debug');
return options.context.shaderCache.replaceShaderProgram(options);
};
Object.defineProperties(ShaderProgram.prototype, {
/**
* GLSL source for the shader program's vertex shader.
* @memberof ShaderProgram.prototype
*
* @type {ShaderSource}
* @readonly
*/
vertexShaderSource: {
get: function () {
return this._vertexShaderSource;
},
},
/**
* GLSL source for the shader program's fragment shader.
* @memberof ShaderProgram.prototype
*
* @type {ShaderSource}
* @readonly
*/
// 得到像素着色器
fragmentShaderSource: {
get: function () {
return this._fragmentShaderSource;
},
},
// 得到顶点属性
vertexAttributes: {
get: function () {
initialize(this);
return this._vertexAttributes;
},
},
// 得到顶点属性数量
numberOfVertexAttributes: {
get: function () {
initialize(this);
return this._numberOfVertexAttributes;
},
},
// 得到uniforms
allUniforms: {
get: function () {
initialize(this);
return this._uniformsByName;
},
},
});
// 查找glsl中的uniform变量
function extractUniforms(shaderText) {
var uniformNames = [];
var uniformLines = shaderText.match(/uniform.*?(?![^{]*})(?=[=\[;])/g);
if (defined(uniformLines)) {
var len = uniformLines.length;
for (var i = 0; i < len; i++) {
var line = uniformLines[i].trim();
var name = line.slice(line.lastIndexOf(" ") + 1);
uniformNames.push(name);
}
}
return uniformNames;
}
function handleUniformPrecisionMismatches(
vertexShaderText,
fragmentShaderText
) {
// If a uniform exists in both the vertex and fragment shader but with different precision qualifiers,
// give the fragment shader uniform a different name. This fixes shader compilation errors on devices
// that only support mediump in the fragment shader.
/*
如果统一存在于顶点和片段着色器中,但具有不同的精度限定符,请为片段着色器统一指定不同的名称。这修复了在片段着色器中
仅支持mediump的设备上的着色器编译错误。
*/
var duplicateUniformNames = {};
// 不支持高精度
if (!ContextLimits.highpFloatSupported || !ContextLimits.highpIntSupported) {
var i, j;
var uniformName;
var duplicateName;
// 查找glsl中的uniform变量
var vertexShaderUniforms = extractUniforms(vertexShaderText);
var fragmentShaderUniforms = extractUniforms(fragmentShaderText);
var vertexUniformsCount = vertexShaderUniforms.length;
var fragmentUniformsCount = fragmentShaderUniforms.length;
// 查重
for (i = 0; i < vertexUniformsCount; i++) {
for (j = 0; j < fragmentUniformsCount; j++) {
// 顶点着色器和像素着色器中都存在这个uniform
if (vertexShaderUniforms[i] === fragmentShaderUniforms[j]) {
// 获取值
uniformName = vertexShaderUniforms[i];
// 名字上拼接中精度前缀czm_mediump_
duplicateName = "czm_mediump_" + uniformName;
// Update fragmentShaderText with renamed uniforms
// 替换shader中的相关字符
var re = new RegExp(uniformName + "\\b", "g");
fragmentShaderText = fragmentShaderText.replace(re, duplicateName);
// 存储对应关系
duplicateUniformNames[duplicateName] = uniformName;
}
}
}
}
// 返回新的shader和
return {
fragmentShaderText: fragmentShaderText,
duplicateUniformNames: duplicateUniformNames,
};
}
// 调试信息前缀,glsl错误前缀
var consolePrefix = "[Cesium WebGL] ";
// 创建着色器、连接到着色程序
function createAndLinkProgram(gl, shader) {
// 顶点着色器代码
var vsSource = shader._vertexShaderText;
// 像素着色器代码
var fsSource = shader._fragmentShaderText;
// 创建、编译顶点着色器
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vsSource);
gl.compileShader(vertexShader);
// 创建、编译像素着色器
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fsSource);
gl.compileShader(fragmentShader);
//创建着色程序、绑定着色器
var program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
// 删除着色器
gl.deleteShader(vertexShader);
gl.deleteShader(fragmentShader);
// 属性位置信息
var attributeLocations = shader._attributeLocations;
if (defined(attributeLocations)) {
// 遍历属性位置信息
for (var attribute in attributeLocations) {
if (attributeLocations.hasOwnProperty(attribute)) {
// 绑定属性位置信息
gl.bindAttribLocation(
program, // 着色程序
attributeLocations[attribute], // 属性位置(layout(1))
attribute // 属性名 (position:)
);
}
}
}
// 连接程序
gl.linkProgram(program);
var log;
// 获取连接状态
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
// glsl扩展的调试信息,用来获取着色器源码
var debugShaders = shader._debugShaders;
// For performance, only check compile errors if there is a linker error.
// 提高执行效率,只是检查连接状态
// 得到像素着色器错误
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
// 获取错误日志
log = gl.getShaderInfoLog(fragmentShader);
// 错误日志
console.error(consolePrefix + "Fragment shader compile log: " + log);
// 定义了调试信息
if (defined(debugShaders)) {
// 获取着色器源码
var fragmentSourceTranslation = debugShaders.getTranslatedShaderSource(
fragmentShader
);
// 打印像素着色器源码
if (fragmentSourceTranslation !== "") {
console.error(
consolePrefix +
"Translated fragment shader source:\n" +
fragmentSourceTranslation
);
} else {
console.error(consolePrefix + "Fragment shader translation failed.");
}
}
// 删除着色程序
gl.deleteProgram(program);
// 异常中断
throw new RuntimeError(
"Fragment shader failed to compile. Compile log: " + log
);
}
// 得到顶点着色器错误
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
// 获取错误日志
log = gl.getShaderInfoLog(vertexShader);
// 错误日志
console.error(consolePrefix + "Vertex shader compile log: " + log);
// 定义了调试信息
if (defined(debugShaders)) {
// 获取着色器源码
var vertexSourceTranslation = debugShaders.getTranslatedShaderSource(
vertexShader
);
// 打印像素着色器源码
if (vertexSourceTranslation !== "") {
console.error(
consolePrefix +
"Translated vertex shader source:\n" +
vertexSourceTranslation
);
} else {
console.error(consolePrefix + "Vertex shader translation failed.");
}
}
// 删除着色程序
gl.deleteProgram(program);
// 异常中断
throw new RuntimeError(
"Vertex shader failed to compile. Compile log: " + log
);
}
// 得到着色程序错误
log = gl.getProgramInfoLog(program);
// 打印日志
console.error(consolePrefix + "Shader program link log: " + log);
if (defined(debugShaders)) {
// 打印顶点着色器源码
console.error(
consolePrefix +
"Translated vertex shader source:\n" +
debugShaders.getTranslatedShaderSource(vertexShader)
);
// 打印像素着色器源码
console.error(
consolePrefix +
"Translated fragment shader source:\n" +
debugShaders.getTranslatedShaderSource(fragmentShader)
);
}
// 删除着色程序
gl.deleteProgram(program);
// 异常中断
throw new RuntimeError("Program failed to link. Link log: " + log);
}
// 着色器编译日志是否启用
var logShaderCompilation = shader._logShaderCompilation;
// 得到顶点着色器编译日志
if (logShaderCompilation) {
log = gl.getShaderInfoLog(vertexShader);
if (defined(log) && log.length > 0) {
console.log(consolePrefix + "Vertex shader compile log: " + log);
}
}
// 得到像素着色器编译日志
if (logShaderCompilation) {
log = gl.getShaderInfoLog(fragmentShader);
if (defined(log) && log.length > 0) {
console.log(consolePrefix + "Fragment shader compile log: " + log);
}
}
// 得到着色程序编译日志
if (logShaderCompilation) {
log = gl.getProgramInfoLog(program);
if (defined(log) && log.length > 0) {
console.log(consolePrefix + "Shader program link log: " + log);
}
}
// 返回着色程序
return program;
}
// 查找顶点属性
function findVertexAttributes(gl, program, numberOfAttributes) {
var attributes = {};
for (var i = 0; i < numberOfAttributes; ++i) {
// 获取顶点属性对象(包括类型、名称等)
var attr = gl.getActiveAttrib(program, i);
// 获取顶点属性的位置
var location = gl.getAttribLocation(program, attr.name);
// 保存顶点属性信息
attributes[attr.name] = {
name: attr.name, // 属性名称
type: attr.type, // 属性类型
index: location, // 属性位置
};
}
return attributes;
}
// 动态查找着色程序中的uniform
function findUniforms(gl, program) {
var uniformsByName = {};
var uniforms = [];
var samplerUniforms = [];
// 获取着色程序中的活动的uniform数量
var numberOfUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
// 遍历uniform数量下的uniform数据
for (var i = 0; i < numberOfUniforms; ++i) {
// 遍历uniform(包括名称、类型等信息)
var activeUniform = gl.getActiveUniform(program, i);
var suffix = "[0]";
var uniformName = // 分离出uniform的名字,如果是数组,去掉数组[0]子串
activeUniform.name.indexOf(
suffix,
activeUniform.name.length - suffix.length
) !== -1
? activeUniform.name.slice(0, activeUniform.name.length - 3) // 是数组,返回字串
: activeUniform.name; // 不是数组,返回名称
// Ignore GLSL built-in uniforms returned in Firefox.
// 忽略内建的uniform,在火狐浏览器中存在这中情况
if (uniformName.indexOf("gl_") !== 0) { // 不是开头位置
if (activeUniform.name.indexOf("[") < 0) { // 不是数组
// Single uniform
// 单个uniform,获取位置
var location = gl.getUniformLocation(program, uniformName);
// IE 11.0.9 needs this check since getUniformLocation can return null
// if the uniform is not active (e.g., it is optimized out). Looks like
// getActiveUniform() above returns uniforms that are not actually active.
if (location !== null) { // 位置不是null
// 创建各种uniform,uniform封装了很多的gl相关操作
var uniform = createUniform(gl, activeUniform, uniformName, location);
// 将封装的uniform列表保存起来
uniformsByName[uniformName] = uniform;
uniforms.push(uniform);
// 采样器存在,另外保存
if (uniform._setSampler) {
samplerUniforms.push(uniform);
}
}
} else {// 是数组
// Uniform array
var uniformArray;
var locations;
var value;
var loc;
// On some platforms - Nexus 4 in Firefox for one - an array of sampler2D ends up being represented
// as separate uniforms, one for each array element. Check for and handle that case.
var indexOfBracket = uniformName.indexOf("[");
if (indexOfBracket >= 0) {
// We're assuming the array elements show up in numerical order - it seems to be true.
uniformArray = uniformsByName[uniformName.slice(0, indexOfBracket)];
// Nexus 4 with Android 4.3 needs this check, because it reports a uniform
// with the strange name webgl_3467e0265d05c3c1[1] in our globe surface shader.
if (!defined(uniformArray)) {
continue;
}
locations = uniformArray._locations;
// On the Nexus 4 in Chrome, we get one uniform per sampler, just like in Firefox,
// but the size is not 1 like it is in Firefox. So if we push locations here,
// we'll end up adding too many locations.
if (locations.length <= 1) {
value = uniformArray.value;
loc = gl.getUniformLocation(program, uniformName);
// Workaround for IE 11.0.9. See above.
if (loc !== null) {
locations.push(loc);
value.push(gl.getUniform(program, loc));
}
}
} else {
locations = [];
for (var j = 0; j < activeUniform.size; ++j) {
loc = gl.getUniformLocation(program, uniformName + "[" + j + "]");
// Workaround for IE 11.0.9. See above.
if (loc !== null) {
locations.push(loc);
}
}
uniformArray = createUniformArray(
gl,
activeUniform,
uniformName,
locations
);
uniformsByName[uniformName] = uniformArray;
uniforms.push(uniformArray);
if (uniformArray._setSampler) {
samplerUniforms.push(uniformArray);
}
}
}
}
}
// 返回unifrom相关数据
return {
uniformsByName: uniformsByName,
uniforms: uniforms,
samplerUniforms: samplerUniforms,
};
}
// 分离手动、自动uniform
function partitionUniforms(shader, uniforms) {
// 自动对象、手动对象
var automaticUniforms = [];
var manualUniforms = [];
// 遍历uniform
for (var uniform in uniforms) {
// uniform名称对应的自身属性
if (uniforms.hasOwnProperty(uniform)) {
// uniform名称对应的对象
var uniformObject = uniforms[uniform];
// uniform名称
var uniformName = uniform;
// if it's a duplicate uniform, use its original name so it is updated correctly
// 如果是重复的uniform,请使用其原始名称,以便正确更新
var duplicateUniform = shader._duplicateUniformNames[uniformName]; // 别名
if (defined(duplicateUniform)) {
uniformObject.name = duplicateUniform; // 别名(位置对应上就可以了)
uniformName = duplicateUniform; // 别名
}
// 自动uniform还是手动uniform,自动的应该是内置的(模型、视图、投影等uniform矩阵),不用自己创建,手动的是自己创建的uniform
var automaticUniform = AutomaticUniforms[uniformName]; // AutomaticUniforms大写字母开头
if (defined(automaticUniform)) {
// 将uniform对象放到自动数组中
automaticUniforms.push({
uniform: uniformObject,
automaticUniform: automaticUniform,
});
} else {
// 将uniform对象放到手动数组中
manualUniforms.push(uniformObject);
}
}
}
// 返回自动对象和手动对象
return {
automaticUniforms: automaticUniforms,
manualUniforms: manualUniforms,
};
}
function setSamplerUniforms(gl, program, samplerUniforms) {
// 启动着色程序
gl.useProgram(program);
var textureUnitIndex = 0;
var length = samplerUniforms.length;
for (var i = 0; i < length; ++i) {
// 根据采样器数量设置采样器索引
textureUnitIndex = samplerUniforms[i]._setSampler(textureUnitIndex);
}
// 禁用着色程序
gl.useProgram(null);
return textureUnitIndex;
}
// 初始化着色程序
function initialize(shader) {
if (defined(shader._program)) {
return;
}
// 重新初始化着色程序
reinitialize(shader);
}
// 重新初始化
function reinitialize(shader) {
// 老的着色程序
var oldProgram = shader._program;
var gl = shader._gl;
// 创建并且连接着色器
var program = createAndLinkProgram(gl, shader, shader._debugShaders);
// 得到活动的顶点属性数量
var numberOfVertexAttributes = gl.getProgramParameter(
program,
gl.ACTIVE_ATTRIBUTES // 活动的属性数量
);
// 查找uniform相关结构(包括,位置,创建对应的uniform类型对象)
var uniforms = findUniforms(gl, program);
// 分离手动自动uniform
var partitionedUniforms = partitionUniforms(shader, uniforms.uniformsByName);
// 新的着色程序
shader._program = program;
// 顶点属性数量
shader._numberOfVertexAttributes = numberOfVertexAttributes;
// 查找顶点属性
shader._vertexAttributes = findVertexAttributes(
gl,
program,
numberOfVertexAttributes
);
// uniform的名称-对象
shader._uniformsByName = uniforms.uniformsByName;
// uniform数组
shader._uniforms = uniforms.uniforms;
// 设置手动、自动uniform
shader._automaticUniforms = partitionedUniforms.automaticUniforms;
shader._manualUniforms = partitionedUniforms.manualUniforms;
// 设置采样器绑定的纹理单元
shader.maximumTextureUnitIndex = setSamplerUniforms(
gl,
program,
uniforms.samplerUniforms // 采样器对象数组
);
// 删除老的着色对象
if (oldProgram) {
shader._gl.deleteProgram(oldProgram);
}
// If SpectorJS is active, add the hook to make the shader editor work.
// 支持SpectorJS调试
// https://github.com/BabylonJS/Spector.js/blob/master/documentation/extension.md#shader-editor
if (typeof spector !== "undefined") {
shader._program.__SPECTOR_rebuildProgram = function (
vertexSourceCode, // The new vertex shader source
fragmentSourceCode, // The new fragment shader source
onCompiled, // Callback triggered by your engine when the compilation is successful. It needs to send back the new linked program.
onError // Callback triggered by your engine in case of error. It needs to send the WebGL error to allow the editor to display the error in the gutter.
) {
var originalVS = shader._vertexShaderText;
var originalFS = shader._fragmentShaderText;
// SpectorJS likes to replace `!=` with `! =` for unknown reasons,
// and that causes glsl compile failures. So fix that up.
var regex = / ! = /g;
shader._vertexShaderText = vertexSourceCode.replace(regex, " != ");
shader._fragmentShaderText = fragmentSourceCode.replace(regex, " != ");
try {
reinitialize(shader);
onCompiled(shader._program);
} catch (e) {
shader._vertexShaderText = originalVS;
shader._fragmentShaderText = originalFS;
// Only pass on the WebGL error:
var errorMatcher = /(?:Compile|Link) error: ([^]*)/;
var match = errorMatcher.exec(e.message);
if (match) {
onError(match[1]);
} else {
onError(e.message);
}
}
};
}
}
// 绑定着色程序
ShaderProgram.prototype._bind = function () {
// 还没有初始化时,先初始化
initialize(this);
// 启用着色程序
this._gl.useProgram(this._program);
};
// 向glsl中设置uniform数据
ShaderProgram.prototype._setUniforms = function (
uniformMap, // 命令中的uniform
uniformState, // 上下文中的uniform
validate
) {
var len;
var i;
if (defined(uniformMap)) {
// 遍历手动uniform对象,将数据保存
var manualUniforms = this._manualUniforms;
len = manualUniforms.length;
for (i = 0; i < len; ++i) {
var mu = manualUniforms[i];
// 将命令中的uniform数据添加到uniform接口中,用于后续传到gpu中
try {
mu.value = uniformMap[mu.name]();
}catch(e) {
console.log(mu.name);
}
}
}
// 遍历自动uniform对象,将数据保存
var automaticUniforms = this._automaticUniforms;
len = automaticUniforms.length;
for (i = 0; i < len; ++i) {
var au = automaticUniforms[i];
// 将context上下文中的uniform数据添加
au.uniform.value = au.automaticUniform.getValue(uniformState);
}
///
// It appears that assigning the uniform values above and then setting them here
// (which makes the GL calls) is faster than removing this loop and making
// the GL calls above. I suspect this is because each GL call pollutes the
// L2 cache making our JavaScript and the browser/driver ping-pong cache lines.
// _uniforms是uniform数组,将数据传入到gpu中
var uniforms = this._uniforms;
len = uniforms.length;
for (i = 0; i < len; ++i) {
uniforms[i].set();
}
// 有效性验证
if (validate) {
var gl = this._gl;
var program = this._program;
// 验证着色程序是否有效
gl.validateProgram(program);
//>>includeStart('debug', pragmas.debug);
if (!gl.getProgramParameter(program, gl.VALIDATE_STATUS)) {
throw new DeveloperError(
"Program validation failed. Program info log: " +
gl.getProgramInfoLog(program)
);
}
//>>includeEnd('debug');
}
};
// 是否销毁
ShaderProgram.prototype.isDestroyed = function () {
return false;
};
// 销毁
ShaderProgram.prototype.destroy = function () {
this._cachedShader.cache.releaseShaderProgram(this);
return undefined;
};
// 最终销毁
ShaderProgram.prototype.finalDestroy = function () {
this._gl.deleteProgram(this._program);
return destroyObject(this);
};
export default ShaderProgram;