Skip to content

Commit

Permalink
Merge pull request #1073 from AnalyticalGraphicsInc/debugfrustum
Browse files Browse the repository at this point in the history
Frustum debugging
  • Loading branch information
bagnell committed Aug 27, 2013
2 parents 2a72fb7 + c0a44fd commit c0a2f87
Show file tree
Hide file tree
Showing 6 changed files with 170 additions and 27 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ var geometry = BoxGeometry.createGeometry(box);
* Added the ability to specify a `minimumTerrainLevel` and `maximumTerrainLevel` when constructing an `ImageryLayer`. The layer will only be shown for terrain tiles within the specified range.
* Added `Math.setRandomNumberSeed` and `Math.nextRandomNumber` for generating repeatable random numbers.
* Added `Color.fromRandom` to generate random and partially random colors.
* Added `Scene.debugShowFrustums` and `Scene.debugFrustumStatistics` for rendering debugging.
* Improved geometry batching performance by moving work to a web worker.
* Improved `WallGeometry` to follow the curvature of the earth.
* Fixed broken surface rendering in Columbus View when using the `EllipsoidTerrainProvider`.
Expand Down
7 changes: 7 additions & 0 deletions Source/Renderer/DrawCommand.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,13 @@ define(function() {
* @see DrawCommand#boundingVolume
*/
this.debugShowBoundingVolume = false;

/**
* @private
*
* Used to implement {@see Scene.debugShowFrustums}.
*/
this.debugOverlappingFrustums = 0;
};

/**
Expand Down
22 changes: 22 additions & 0 deletions Source/Renderer/ShaderProgram.js
Original file line number Diff line number Diff line change
Expand Up @@ -2280,6 +2280,28 @@ define([
this._samplerUniforms = uniforms.samplerUniforms;
this._automaticUniforms = partitionedUniforms.automaticUniforms;
this._manualUniforms = partitionedUniforms.manualUniforms;

/**
* GLSL source for the shader program's vertex shader. This is the version of
* the source provided when the shader program was created, not the final
* source provided to WebGL, which includes Cesium bulit-ins.
*
* @type {String}
*
* @readonly
*/
this.vertexShaderSource = vertexShaderSource;

/**
* GLSL source for the shader program's fragment shader. This is the version of
* the source provided when the shader program was created, not the final
* source provided to WebGL, which includes Cesium bulit-ins.
*
* @type {String}
*
* @readonly
*/
this.fragmentShaderSource = fragmentShaderSource;
};

function extractShaderVersion(source) {
Expand Down
132 changes: 111 additions & 21 deletions Source/Scene/Scene.js
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,42 @@ define([
*/
this.debugCommandFilter = undefined;

/**
* This property is for debugging only; it is not for production use.
* <p>
* When <code>true</code>, commands are shaded based on the frustums they
* overlap. Commands in the closest frustum are tinted red, commands in
* the next closest are green, and commands in the farthest frustum are
* blue. If a command overlaps more than one frustum, the color components
* are combined, e.g., a command overlapping the first two frustums is tinted
* yellow.
* </p>
*
* @type Boolean
*
* @default false
*/
this.debugShowFrustums = false;

/**
* This property is for debugging only; it is not for production use.
* <p>
* When {@see Scene.debugShowFrustums} is <code>true</code>, this contains
* properties with statistics about the number of command execute per frustum.
* <code>totalCommands</code> is the total number of commands executed, ignoring
* overlap. <code>commandsInFrustums</code> is an array with the number of times
* commands are executed redundantly, e.g., how many commands overlap two or
* three frustums.
* </p>
*
* @type Object
*
* @default undefined
*
* @readonly
*/
this.debugFrustumStatistics = undefined;

this._debugSphere = undefined;

// initial guess at frustums.
Expand Down Expand Up @@ -377,8 +413,13 @@ define([
}

function insertIntoBin(scene, command, distance) {
if (scene.debugShowFrustums) {
command.debugOverlappingFrustums = 0;
}

var frustumCommandsList = scene._frustumCommandsList;
var length = frustumCommandsList.length;

for (var i = 0; i < length; ++i) {
var frustumCommands = frustumCommandsList[i];
var curNear = frustumCommands.near;
Expand All @@ -395,25 +436,20 @@ define([
// PERFORMANCE_IDEA: sort bins
frustumCommands.commands[frustumCommands.index++] = command;

if (command.executeInClosestFrustum) {
break;
if (scene.debugShowFrustums) {
command.debugOverlappingFrustums |= (1 << i);
}
}
}

function insertIntoAllBins(scene, command) {
var frustumCommandsList = scene._frustumCommandsList;
var length = frustumCommandsList.length;
for (var i = 0; i < length; ++i) {
var frustumCommands = frustumCommandsList[i];

// PERFORMANCE_IDEA: sort bins
frustumCommands.commands[frustumCommands.index++] = command;

if (command.executeInClosestFrustum) {
break;
}
}

if (scene.debugShowFrustums) {
var cf = scene.debugFrustumStatistics.commandsInFrustums;
cf[command.debugOverlappingFrustums] = defined(cf[command.debugOverlappingFrustums]) ? cf[command.debugOverlappingFrustums] + 1 : 1;
++scene.debugFrustumStatistics.totalCommands;
}
}

var scratchCullingVolume = new CullingVolume();
Expand All @@ -427,9 +463,16 @@ define([
var direction = camera.getDirectionWC();
var position = camera.getPositionWC();

if (scene.debugShowFrustums) {
scene.debugFrustumStatistics = {
totalCommands : 0,
commandsInFrustums : {}
};
}

var frustumCommandsList = scene._frustumCommandsList;
var frustumsLength = frustumCommandsList.length;
for (var n = 0; n < frustumsLength; ++n) {
var numberOfFrustums = frustumCommandsList.length;
for (var n = 0; n < numberOfFrustums; ++n) {
frustumCommandsList[n].index = 0;
}

Expand Down Expand Up @@ -468,15 +511,16 @@ define([
distances = transformedBV.getPlaneDistances(position, direction, distances);
near = Math.min(near, distances.start);
far = Math.max(far, distances.stop);

insertIntoBin(scene, command, distances);
} else {
// Clear commands don't need a bounding volume - just add the clear to all frustums.
// If another command has no bounding volume, though, we need to use the camera's
// worst-case near and far planes to avoid clipping something important.
distances.start = camera.frustum.near;
distances.stop = camera.frustum.far;
undefBV = !(command instanceof ClearCommand);
insertIntoAllBins(scene, command);
}

insertIntoBin(scene, command, distances);
}
}

Expand All @@ -495,19 +539,65 @@ define([
// last frame, else compute the new frustums and sort them by frustum again.
var farToNearRatio = scene.farToNearRatio;
var numFrustums = Math.ceil(Math.log(far / near) / Math.log(farToNearRatio));
if (near !== Number.MAX_VALUE && (numFrustums !== frustumsLength || (frustumCommandsList.length !== 0 &&
(near < frustumCommandsList[0].near || far > frustumCommandsList[frustumsLength - 1].far)))) {
if (near !== Number.MAX_VALUE && (numFrustums !== numberOfFrustums || (frustumCommandsList.length !== 0 &&
(near < frustumCommandsList[0].near || far > frustumCommandsList[numberOfFrustums - 1].far)))) {
updateFrustums(near, far, farToNearRatio, numFrustums, frustumCommandsList);
createPotentiallyVisibleSet(scene, listName);
}
}

function createFrustumDebugFragmentShaderSource(command) {
var fragmentShaderSource = command.shaderProgram.fragmentShaderSource;
var renamedFS = fragmentShaderSource.replace(/void\s+main\s*\(\s*(?:void)?\s*\)/g, 'void czm_frustumDebug_main()');

// Support up to three frustums. If a command overlaps all
// three, it's code is not changed.
var r = (command.debugOverlappingFrustums & (1 << 0)) ? '1.0' : '0.0';
var g = (command.debugOverlappingFrustums & (1 << 1)) ? '1.0' : '0.0';
var b = (command.debugOverlappingFrustums & (1 << 2)) ? '1.0' : '0.0';

var pickMain =
'void main() \n' +
'{ \n' +
' czm_frustumDebug_main(); \n' +
' gl_FragColor.rgb *= vec3(' + r + ', ' + g + ', ' + b + '); \n' +
'}';

return renamedFS + '\n' + pickMain;
}

function executeFrustumDebugCommand(command, context, passState) {
if (defined(command.shaderProgram)) {
// Replace shader for frustum visualization
var sp = command.shaderProgram;
var attributeLocations = {};
var attributes = sp.getVertexAttributes();
for (var a in attributes) {
if (attributes.hasOwnProperty(a)) {
attributeLocations[a] = attributes[a].index;
}
}

command.shaderProgram = context.getShaderCache().getShaderProgram(
sp.vertexShaderSource, createFrustumDebugFragmentShaderSource(command), attributeLocations);

command.execute(context, passState);

command.shaderProgram.release();
command.shaderProgram = sp;
}
}

function executeCommand(command, scene, context, passState) {
if ((defined(scene.debugCommandFilter)) && !scene.debugCommandFilter(command)) {
return;
}

command.execute(context, passState);
if (!scene.debugShowFrustums) {
command.execute(context, passState);
} else {
executeFrustumDebugCommand(command, context, passState);
}

if (command.debugShowBoundingVolume && (defined(command.boundingVolume))) {
// Debug code to draw bounding volume for command. Not optimized!
Expand Down
9 changes: 9 additions & 0 deletions Specs/Renderer/ShaderProgramSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@ defineSuite([
destroyContext(context);
});

it('has vertex and fragment shader source', function() {
var vs = 'void main() { gl_Position = vec4(1.0); }';
var fs = 'void main() { gl_FragColor = vec4(1.0); }';
sp = context.createShaderProgram(vs, fs);

expect(sp.vertexShaderSource).toEqual(vs);
expect(sp.fragmentShaderSource).toEqual(fs);
});

it('has a position vertex attribute', function() {
var vs = 'attribute vec4 position; void main() { gl_Position = position; }';
var fs = 'void main() { gl_FragColor = vec4(1.0); }';
Expand Down
26 changes: 20 additions & 6 deletions Specs/Scene/MultifrustumSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,20 @@ defineSuite([
expect(context.readPixels()).toEqual([255, 255, 255, 255]);
});

it('renders primitive in last frustum with debugShowFrustums', function() {
createBillboards();
var color = new Color(1.0, 1.0, 1.0, 0.0);
billboard0.setColor(color);
billboard1.setColor(color);

scene.debugShowFrustums = true;
scene.initializeFrame();
scene.render();
expect(context.readPixels()).toEqual([0, 0, 255, 255]);
expect(scene.debugFrustumStatistics.totalCommands).toEqual(3);
expect(scene.debugFrustumStatistics.commandsInFrustums).toEqual({ 1 : 1, 2 : 1, 4 : 1});
});

function createPrimitive(bounded, closestFrustum) {
bounded = defaultValue(bounded, true);
closestFrustum = defaultValue(closestFrustum, false);
Expand All @@ -196,12 +210,12 @@ defineSuite([

var that = this;
this._um = {
u_color : function() {
return that.color;
},
u_model : function() {
return that._modelMatrix;
}
u_color : function() {
return that.color;
},
u_model : function() {
return that._modelMatrix;
}
};
};

Expand Down

0 comments on commit c0a2f87

Please sign in to comment.