You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
435 lines
11 KiB
JavaScript
435 lines
11 KiB
JavaScript
import defined from "../Core/defined.js";
|
|
import DrawCommand from "../Renderer/DrawCommand.js";
|
|
import RenderState from "../Renderer/RenderState.js";
|
|
import ShaderSource from "../Renderer/ShaderSource.js";
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
function DerivedCommand() {}
|
|
|
|
var fragDepthRegex = /\bgl_FragDepthEXT\b/;
|
|
var discardRegex = /\bdiscard\b/;
|
|
|
|
function getDepthOnlyShaderProgram(context, shaderProgram) {
|
|
var shader = context.shaderCache.getDerivedShaderProgram(
|
|
shaderProgram,
|
|
"depthOnly"
|
|
);
|
|
if (!defined(shader)) {
|
|
var attributeLocations = shaderProgram._attributeLocations;
|
|
var fs = shaderProgram.fragmentShaderSource;
|
|
|
|
var i;
|
|
var writesDepthOrDiscards = false;
|
|
var sources = fs.sources;
|
|
var length = sources.length;
|
|
for (i = 0; i < length; ++i) {
|
|
if (fragDepthRegex.test(sources[i]) || discardRegex.test(sources[i])) {
|
|
writesDepthOrDiscards = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
var usesLogDepth = false;
|
|
var defines = fs.defines;
|
|
length = defines.length;
|
|
for (i = 0; i < length; ++i) {
|
|
if (defines[i] === "LOG_DEPTH") {
|
|
usesLogDepth = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
var source;
|
|
if (!writesDepthOrDiscards && !usesLogDepth) {
|
|
source =
|
|
"void main() \n" + "{ \n" + " gl_FragColor = vec4(1.0); \n" + "} \n";
|
|
fs = new ShaderSource({
|
|
sources: [source],
|
|
});
|
|
} else if (!writesDepthOrDiscards && usesLogDepth) {
|
|
source =
|
|
"#ifdef GL_EXT_frag_depth \n" +
|
|
"#extension GL_EXT_frag_depth : enable \n" +
|
|
"#endif \n\n" +
|
|
"void main() \n" +
|
|
"{ \n" +
|
|
" gl_FragColor = vec4(1.0); \n" +
|
|
" czm_writeLogDepth(); \n" +
|
|
"} \n";
|
|
fs = new ShaderSource({
|
|
defines: ["LOG_DEPTH"],
|
|
sources: [source],
|
|
});
|
|
}
|
|
|
|
shader = context.shaderCache.createDerivedShaderProgram(
|
|
shaderProgram,
|
|
"depthOnly",
|
|
{
|
|
vertexShaderSource: shaderProgram.vertexShaderSource,
|
|
fragmentShaderSource: fs,
|
|
attributeLocations: attributeLocations,
|
|
}
|
|
);
|
|
}
|
|
|
|
return shader;
|
|
}
|
|
|
|
function getDepthOnlyRenderState(scene, renderState) {
|
|
var cache = scene._depthOnlyRenderStateCache;
|
|
var depthOnlyState = cache[renderState.id];
|
|
if (!defined(depthOnlyState)) {
|
|
var rs = RenderState.getState(renderState);
|
|
rs.depthMask = true;
|
|
rs.colorMask = {
|
|
red: false,
|
|
green: false,
|
|
blue: false,
|
|
alpha: false,
|
|
};
|
|
|
|
depthOnlyState = RenderState.fromCache(rs);
|
|
cache[renderState.id] = depthOnlyState;
|
|
}
|
|
|
|
return depthOnlyState;
|
|
}
|
|
|
|
DerivedCommand.createDepthOnlyDerivedCommand = function (
|
|
scene,
|
|
command,
|
|
context,
|
|
result
|
|
) {
|
|
// For a depth only pass, we bind a framebuffer with only a depth attachment (no color attachments),
|
|
// do not write color, and write depth. If the fragment shader doesn't modify the fragment depth
|
|
// or discard, the driver can replace the fragment shader with a pass-through shader. We're unsure if this
|
|
// actually happens so we modify the shader to use a pass-through fragment shader.
|
|
|
|
if (!defined(result)) {
|
|
result = {};
|
|
}
|
|
|
|
var shader;
|
|
var renderState;
|
|
if (defined(result.depthOnlyCommand)) {
|
|
shader = result.depthOnlyCommand.shaderProgram;
|
|
renderState = result.depthOnlyCommand.renderState;
|
|
}
|
|
|
|
result.depthOnlyCommand = DrawCommand.shallowClone(
|
|
command,
|
|
result.depthOnlyCommand
|
|
);
|
|
|
|
if (!defined(shader) || result.shaderProgramId !== command.shaderProgram.id) {
|
|
result.depthOnlyCommand.shaderProgram = getDepthOnlyShaderProgram(
|
|
context,
|
|
command.shaderProgram
|
|
);
|
|
result.depthOnlyCommand.renderState = getDepthOnlyRenderState(
|
|
scene,
|
|
command.renderState
|
|
);
|
|
result.shaderProgramId = command.shaderProgram.id;
|
|
} else {
|
|
result.depthOnlyCommand.shaderProgram = shader;
|
|
result.depthOnlyCommand.renderState = renderState;
|
|
}
|
|
|
|
return result;
|
|
};
|
|
|
|
var writeLogDepthRegex = /\s+czm_writeLogDepth\(/;
|
|
var vertexlogDepthRegex = /\s+czm_vertexLogDepth\(/;
|
|
var extensionRegex = /\s*#extension\s+GL_EXT_frag_depth\s*:\s*enable/;
|
|
|
|
function getLogDepthShaderProgram(context, shaderProgram) {
|
|
var shader = context.shaderCache.getDerivedShaderProgram(
|
|
shaderProgram,
|
|
"logDepth"
|
|
);
|
|
if (!defined(shader)) {
|
|
var attributeLocations = shaderProgram._attributeLocations;
|
|
var vs = shaderProgram.vertexShaderSource.clone();
|
|
var fs = shaderProgram.fragmentShaderSource.clone();
|
|
|
|
vs.defines = defined(vs.defines) ? vs.defines.slice(0) : [];
|
|
vs.defines.push("LOG_DEPTH");
|
|
fs.defines = defined(fs.defines) ? fs.defines.slice(0) : [];
|
|
fs.defines.push("LOG_DEPTH");
|
|
|
|
var i;
|
|
var logMain;
|
|
var writesLogDepth = false;
|
|
var sources = vs.sources;
|
|
var length = sources.length;
|
|
for (i = 0; i < length; ++i) {
|
|
if (vertexlogDepthRegex.test(sources[i])) {
|
|
writesLogDepth = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!writesLogDepth) {
|
|
for (i = 0; i < length; ++i) {
|
|
sources[i] = ShaderSource.replaceMain(sources[i], "czm_log_depth_main");
|
|
}
|
|
|
|
logMain =
|
|
"\n\n" +
|
|
"void main() \n" +
|
|
"{ \n" +
|
|
" czm_log_depth_main(); \n" +
|
|
" czm_vertexLogDepth(); \n" +
|
|
"} \n";
|
|
sources.push(logMain);
|
|
}
|
|
|
|
sources = fs.sources;
|
|
length = sources.length;
|
|
|
|
writesLogDepth = false;
|
|
for (i = 0; i < length; ++i) {
|
|
if (writeLogDepthRegex.test(sources[i])) {
|
|
writesLogDepth = true;
|
|
}
|
|
}
|
|
// This define indicates that a log depth value is written by the shader but doesn't use czm_writeLogDepth.
|
|
if (fs.defines.indexOf("LOG_DEPTH_WRITE") !== -1) {
|
|
writesLogDepth = true;
|
|
}
|
|
|
|
var addExtension = true;
|
|
for (i = 0; i < length; ++i) {
|
|
if (extensionRegex.test(sources[i])) {
|
|
addExtension = false;
|
|
}
|
|
}
|
|
|
|
var logSource = "";
|
|
if (addExtension) {
|
|
logSource +=
|
|
"#ifdef GL_EXT_frag_depth \n" +
|
|
"#extension GL_EXT_frag_depth : enable \n" +
|
|
"#endif \n\n";
|
|
}
|
|
|
|
if (!writesLogDepth) {
|
|
for (i = 0; i < length; i++) {
|
|
sources[i] = ShaderSource.replaceMain(sources[i], "czm_log_depth_main");
|
|
}
|
|
|
|
logSource +=
|
|
"\n" +
|
|
"void main() \n" +
|
|
"{ \n" +
|
|
" czm_log_depth_main(); \n" +
|
|
" czm_writeLogDepth(); \n" +
|
|
"} \n";
|
|
}
|
|
|
|
sources.push(logSource);
|
|
|
|
shader = context.shaderCache.createDerivedShaderProgram(
|
|
shaderProgram,
|
|
"logDepth",
|
|
{
|
|
vertexShaderSource: vs,
|
|
fragmentShaderSource: fs,
|
|
attributeLocations: attributeLocations,
|
|
}
|
|
);
|
|
}
|
|
|
|
return shader;
|
|
}
|
|
|
|
DerivedCommand.createLogDepthCommand = function (command, context, result) {
|
|
if (!defined(result)) {
|
|
result = {};
|
|
}
|
|
|
|
var shader;
|
|
if (defined(result.command)) {
|
|
shader = result.command.shaderProgram;
|
|
}
|
|
|
|
result.command = DrawCommand.shallowClone(command, result.command);
|
|
|
|
if (!defined(shader) || result.shaderProgramId !== command.shaderProgram.id) {
|
|
result.command.shaderProgram = getLogDepthShaderProgram(
|
|
context,
|
|
command.shaderProgram
|
|
);
|
|
result.shaderProgramId = command.shaderProgram.id;
|
|
} else {
|
|
result.command.shaderProgram = shader;
|
|
}
|
|
|
|
return result;
|
|
};
|
|
|
|
function getPickShaderProgram(context, shaderProgram, pickId) {
|
|
var shader = context.shaderCache.getDerivedShaderProgram(
|
|
shaderProgram,
|
|
"pick"
|
|
);
|
|
if (!defined(shader)) {
|
|
var attributeLocations = shaderProgram._attributeLocations;
|
|
var fs = shaderProgram.fragmentShaderSource;
|
|
|
|
var sources = fs.sources;
|
|
var length = sources.length;
|
|
|
|
var newMain =
|
|
"void main() \n" +
|
|
"{ \n" +
|
|
" czm_non_pick_main(); \n" +
|
|
" if (gl_FragColor.a == 0.0) { \n" +
|
|
" discard; \n" +
|
|
" } \n" +
|
|
" gl_FragColor = " +
|
|
pickId +
|
|
"; \n" +
|
|
"} \n";
|
|
var newSources = new Array(length + 1);
|
|
for (var i = 0; i < length; ++i) {
|
|
newSources[i] = ShaderSource.replaceMain(sources[i], "czm_non_pick_main");
|
|
}
|
|
newSources[length] = newMain;
|
|
fs = new ShaderSource({
|
|
sources: newSources,
|
|
defines: fs.defines,
|
|
});
|
|
shader = context.shaderCache.createDerivedShaderProgram(
|
|
shaderProgram,
|
|
"pick",
|
|
{
|
|
vertexShaderSource: shaderProgram.vertexShaderSource,
|
|
fragmentShaderSource: fs,
|
|
attributeLocations: attributeLocations,
|
|
}
|
|
);
|
|
}
|
|
|
|
return shader;
|
|
}
|
|
|
|
function getPickRenderState(scene, renderState) {
|
|
var cache = scene.picking.pickRenderStateCache;
|
|
var pickState = cache[renderState.id];
|
|
if (!defined(pickState)) {
|
|
var rs = RenderState.getState(renderState);
|
|
rs.blending.enabled = false;
|
|
|
|
// Turns on depth writing for opaque and translucent passes
|
|
// Overlapping translucent geometry on the globe surface may exhibit z-fighting
|
|
// during the pick pass which may not match the rendered scene. Once
|
|
// terrain is on by default and ground primitives are used instead
|
|
// this will become less of a problem.
|
|
rs.depthMask = true;
|
|
|
|
pickState = RenderState.fromCache(rs);
|
|
cache[renderState.id] = pickState;
|
|
}
|
|
|
|
return pickState;
|
|
}
|
|
|
|
DerivedCommand.createPickDerivedCommand = function (
|
|
scene,
|
|
command,
|
|
context,
|
|
result
|
|
) {
|
|
if (!defined(result)) {
|
|
result = {};
|
|
}
|
|
|
|
var shader;
|
|
var renderState;
|
|
if (defined(result.pickCommand)) {
|
|
shader = result.pickCommand.shaderProgram;
|
|
renderState = result.pickCommand.renderState;
|
|
}
|
|
|
|
result.pickCommand = DrawCommand.shallowClone(command, result.pickCommand);
|
|
|
|
if (!defined(shader) || result.shaderProgramId !== command.shaderProgram.id) {
|
|
result.pickCommand.shaderProgram = getPickShaderProgram(
|
|
context,
|
|
command.shaderProgram,
|
|
command.pickId
|
|
);
|
|
result.pickCommand.renderState = getPickRenderState(
|
|
scene,
|
|
command.renderState
|
|
);
|
|
result.shaderProgramId = command.shaderProgram.id;
|
|
} else {
|
|
result.pickCommand.shaderProgram = shader;
|
|
result.pickCommand.renderState = renderState;
|
|
}
|
|
|
|
return result;
|
|
};
|
|
|
|
function getHdrShaderProgram(context, shaderProgram) {
|
|
var shader = context.shaderCache.getDerivedShaderProgram(
|
|
shaderProgram,
|
|
"HDR"
|
|
);
|
|
if (!defined(shader)) {
|
|
var attributeLocations = shaderProgram._attributeLocations;
|
|
var vs = shaderProgram.vertexShaderSource.clone();
|
|
var fs = shaderProgram.fragmentShaderSource.clone();
|
|
|
|
vs.defines = defined(vs.defines) ? vs.defines.slice(0) : [];
|
|
vs.defines.push("HDR");
|
|
fs.defines = defined(fs.defines) ? fs.defines.slice(0) : [];
|
|
fs.defines.push("HDR");
|
|
|
|
shader = context.shaderCache.createDerivedShaderProgram(
|
|
shaderProgram,
|
|
"HDR",
|
|
{
|
|
vertexShaderSource: vs,
|
|
fragmentShaderSource: fs,
|
|
attributeLocations: attributeLocations,
|
|
}
|
|
);
|
|
}
|
|
|
|
return shader;
|
|
}
|
|
|
|
DerivedCommand.createHdrCommand = function (command, context, result) {
|
|
if (!defined(result)) {
|
|
result = {};
|
|
}
|
|
|
|
var shader;
|
|
if (defined(result.command)) {
|
|
shader = result.command.shaderProgram;
|
|
}
|
|
|
|
result.command = DrawCommand.shallowClone(command, result.command);
|
|
|
|
if (!defined(shader) || result.shaderProgramId !== command.shaderProgram.id) {
|
|
result.command.shaderProgram = getHdrShaderProgram(
|
|
context,
|
|
command.shaderProgram
|
|
);
|
|
result.shaderProgramId = command.shaderProgram.id;
|
|
} else {
|
|
result.command.shaderProgram = shader;
|
|
}
|
|
|
|
return result;
|
|
};
|
|
export default DerivedCommand;
|