From c6c2d03cb135bce09ae3bdf8c4bd9a5b0cc66a05 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Fri, 20 Feb 2015 20:07:56 -0500 Subject: [PATCH 01/12] Ground primitive WIP. --- Source/Core/PolygonGeometry.js | 217 +------- Source/Core/PolygonGeometryLibrary.js | 242 ++++++++- Source/Core/SphericalExtent.js | 289 +++++++++++ Source/Renderer/AutomaticUniforms.js | 21 + Source/Renderer/Context.js | 1 + Source/Renderer/RenderState.js | 2 +- Source/Renderer/UniformState.js | 75 +++ Source/Scene/GroundPolygon.js | 627 +++++++++++++++++++++++ Source/Scene/PolygonOnTerrain.js | 705 ++++++++++++++++++++++++++ Source/Shaders/ShadowVolumeFS.glsl | 138 +++++ Source/Shaders/ShadowVolumeVS.glsl | 25 + 11 files changed, 2124 insertions(+), 218 deletions(-) create mode 100644 Source/Core/SphericalExtent.js create mode 100644 Source/Scene/GroundPolygon.js create mode 100644 Source/Scene/PolygonOnTerrain.js create mode 100644 Source/Shaders/ShadowVolumeFS.glsl create mode 100644 Source/Shaders/ShadowVolumeVS.glsl diff --git a/Source/Core/PolygonGeometry.js b/Source/Core/PolygonGeometry.js index 915c81832ea7..81167788ec16 100644 --- a/Source/Core/PolygonGeometry.js +++ b/Source/Core/PolygonGeometry.js @@ -20,9 +20,7 @@ define([ './Matrix3', './PolygonGeometryLibrary', './PolygonPipeline', - './PrimitiveType', './Quaternion', - './Queue', './VertexFormat', './WindingOrder' ], function( @@ -46,9 +44,7 @@ define([ Matrix3, PolygonGeometryLibrary, PolygonPipeline, - PrimitiveType, Quaternion, - Queue, VertexFormat, WindingOrder) { "use strict"; @@ -89,55 +85,6 @@ define([ return result; } - var createGeometryFromPositionsPositions = []; - - function createGeometryFromPositions(ellipsoid, positions, granularity, perPositionHeight) { - var tangentPlane = EllipsoidTangentPlane.fromPoints(positions, ellipsoid); - var positions2D = tangentPlane.projectPointsOntoPlane(positions, createGeometryFromPositionsPositions); - - var originalWindingOrder = PolygonPipeline.computeWindingOrder2D(positions2D); - if (originalWindingOrder === WindingOrder.CLOCKWISE) { - positions2D.reverse(); - positions.reverse(); - } - - var indices = PolygonPipeline.triangulate(positions2D); - /* If polygon is completely unrenderable, just use the first three vertices */ - if (indices.length < 3) { - indices = [0, 1, 2]; - } - - var geo; - if (!perPositionHeight) { - geo = PolygonPipeline.computeSubdivision(ellipsoid, positions, indices, granularity); - } else { - var length = positions.length; - var flattenedPositions = new Array(length * 3); - var index = 0; - for ( var i = 0; i < length; i++) { - var p = positions[i]; - flattenedPositions[index++] = p.x; - flattenedPositions[index++] = p.y; - flattenedPositions[index++] = p.z; - } - geo = new Geometry({ - attributes : { - position : new GeometryAttribute({ - componentDatatype : ComponentDatatype.DOUBLE, - componentsPerAttribute : 3, - values : flattenedPositions - }) - }, - indices : indices, - primitiveType : PrimitiveType.TRIANGLES - }); - } - - return new GeometryInstance({ - geometry : geo - }); - } - var scratchBoundingRectangle = new BoundingRectangle(); var scratchPosition = new Cartesian3(); var scratchNormal = new Cartesian3(); @@ -329,114 +276,10 @@ define([ return geometry; } - var computeWallIndicesSubdivided = []; - - function computeWallIndices(positions, ellipsoid, granularity, perPositionHeight){ - var edgePositions; - var topEdgeLength; - var i; - var p1; - var p2; - - var length = positions.length; - var index = 0; - - if (!perPositionHeight) { - var minDistance = CesiumMath.chordLength(granularity, ellipsoid.maximumRadius); - - var numVertices = 0; - for (i = 0; i < length; i++) { - numVertices += PolygonGeometryLibrary.subdivideLineCount(positions[i], positions[(i + 1) % length], minDistance); - } - - topEdgeLength = (numVertices + length) * 3; - edgePositions = new Array(topEdgeLength * 2); - for (i = 0; i < length; i++) { - p1 = positions[i]; - p2 = positions[(i + 1) % length]; - - var tempPositions = PolygonGeometryLibrary.subdivideLine(p1, p2, minDistance, computeWallIndicesSubdivided); - var tempPositionsLength = tempPositions.length; - for (var j = 0; j < tempPositionsLength; ++j, ++index) { - edgePositions[index] = tempPositions[j]; - edgePositions[index + topEdgeLength] = tempPositions[j]; - } - - edgePositions[index] = p2.x; - edgePositions[index + topEdgeLength] = p2.x; - ++index; - - edgePositions[index] = p2.y; - edgePositions[index + topEdgeLength] = p2.y; - ++index; - - edgePositions[index] = p2.z; - edgePositions[index + topEdgeLength] = p2.z; - ++index; - } - } else { - topEdgeLength = length * 3 * 2; - edgePositions = new Array(topEdgeLength * 2); - for (i = 0; i < length; i++) { - p1 = positions[i]; - p2 = positions[(i + 1) % length]; - edgePositions[index] = edgePositions[index + topEdgeLength] = p1.x; - ++index; - edgePositions[index] = edgePositions[index + topEdgeLength] = p1.y; - ++index; - edgePositions[index] = edgePositions[index + topEdgeLength] = p1.z; - ++index; - edgePositions[index] = edgePositions[index + topEdgeLength] = p2.x; - ++index; - edgePositions[index] = edgePositions[index + topEdgeLength] = p2.y; - ++index; - edgePositions[index] = edgePositions[index + topEdgeLength] = p2.z; - ++index; - } - } - - length = edgePositions.length; - var indices = IndexDatatype.createTypedArray(length / 3, length - positions.length * 6); - var edgeIndex = 0; - length /= 6; - - for (i = 0; i < length; i++) { - var UL = i; - var UR = UL + 1; - var LL = UL + length; - var LR = LL + 1; - - p1 = Cartesian3.fromArray(edgePositions, UL * 3, p1Scratch); - p2 = Cartesian3.fromArray(edgePositions, UR * 3, p2Scratch); - if (Cartesian3.equalsEpsilon(p1, p2, CesiumMath.EPSILON14)) { - continue; - } - - indices[edgeIndex++] = UL; - indices[edgeIndex++] = LL; - indices[edgeIndex++] = UR; - indices[edgeIndex++] = UR; - indices[edgeIndex++] = LL; - indices[edgeIndex++] = LR; - } - - return new Geometry({ - attributes : new GeometryAttributes({ - position : new GeometryAttribute({ - componentDatatype : ComponentDatatype.DOUBLE, - componentsPerAttribute : 3, - values : edgePositions - }) - }), - indices : indices, - primitiveType : PrimitiveType.TRIANGLES - }); - } - var createGeometryFromPositionsExtrudedPositions = []; function createGeometryFromPositionsExtruded(ellipsoid, positions, granularity, hierarchy, perPositionHeight) { - var topGeo = createGeometryFromPositions(ellipsoid, positions, granularity, perPositionHeight).geometry; + var topGeo = PolygonGeometryLibrary.createGeometryFromPositions(ellipsoid, positions, granularity, perPositionHeight); var edgePoints = topGeo.attributes.position.values; var indices = topGeo.indices; @@ -489,7 +332,7 @@ define([ outerRing.reverse(); } - var wallGeo = computeWallIndices(outerRing, ellipsoid, granularity, perPositionHeight); + var wallGeo = PolygonGeometryLibrary.computeWallGeometry(outerRing, ellipsoid, granularity, perPositionHeight); geos.walls.push(new GeometryInstance({ geometry : wallGeo })); @@ -506,7 +349,7 @@ define([ hole.reverse(); } - wallGeo = computeWallIndices(hole, ellipsoid, granularity); + wallGeo = PolygonGeometryLibrary.computeWallGeometry(hole, ellipsoid, granularity); geos.walls.push(new GeometryInstance({ geometry : wallGeo })); @@ -812,50 +655,9 @@ define([ var topAndBottom; var outerPositions; - // create from a polygon hierarchy - // Algorithm adapted from http://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf - var polygons = []; - var queue = new Queue(); - queue.enqueue(polygonHierarchy); - polygonHierarchy = []; - var i; - while (queue.length !== 0) { - var outerNode = queue.dequeue(); - var outerRing = outerNode.positions; - var holes = outerNode.holes; - outerRing = PolygonPipeline.removeDuplicates(outerRing); - if (outerRing.length < 3) { - continue; - } - - var numChildren = defined(holes) ? holes.length : 0; - var polygonHoles = []; - for (i = 0; i < numChildren; i++) { - var hole = holes[i]; - hole.positions = PolygonPipeline.removeDuplicates(hole.positions); - if (hole.positions.length < 3) { - continue; - } - polygonHoles.push(hole.positions); - - var numGrandchildren = 0; - if (defined(hole.holes)) { - numGrandchildren = hole.holes.length; - } - - for ( var j = 0; j < numGrandchildren; j++) { - queue.enqueue(hole.holes[j]); - } - } - - polygonHierarchy.push({ - outerRing : outerRing, - holes : polygonHoles - }); - - var combinedPolygon = polygonHoles.length > 0 ? PolygonPipeline.eliminateHoles(outerRing, polygonHoles) : outerRing; - polygons.push(combinedPolygon); - } + var results = PolygonGeometryLibrary.polygonsFromHierarchy(polygonHierarchy); + var hierarchy = results.hierarchy; + var polygons = results.polygons; if (polygons.length === 0) { return undefined; @@ -865,10 +667,11 @@ define([ var geometry; var geometries = []; + var i; if (extrude) { for (i = 0; i < polygons.length; i++) { - geometry = createGeometryFromPositionsExtruded(ellipsoid, polygons[i], granularity, polygonHierarchy[i], perPositionHeight); + geometry = createGeometryFromPositionsExtruded(ellipsoid, polygons[i], granularity, hierarchy[i], perPositionHeight); topAndBottom = geometry.topAndBottom; topAndBottom.geometry = PolygonGeometryLibrary.scaleToGeodeticHeightExtruded(topAndBottom.geometry, height, extrudedHeight, ellipsoid, perPositionHeight); topAndBottom.geometry = computeAttributes(vertexFormat, topAndBottom.geometry, outerPositions, ellipsoid, stRotation, true, false); @@ -884,7 +687,9 @@ define([ } } else { for (i = 0; i < polygons.length; i++) { - geometry = createGeometryFromPositions(ellipsoid, polygons[i], granularity, perPositionHeight); + geometry = new GeometryInstance({ + geometry : PolygonGeometryLibrary.createGeometryFromPositions(ellipsoid, polygons[i], granularity, perPositionHeight) + }); geometry.geometry = PolygonPipeline.scaleToGeodeticHeight(geometry.geometry, height, ellipsoid, !perPositionHeight); geometry.geometry = computeAttributes(vertexFormat, geometry.geometry, outerPositions, ellipsoid, stRotation, false, false); geometries.push(geometry); diff --git a/Source/Core/PolygonGeometryLibrary.js b/Source/Core/PolygonGeometryLibrary.js index f3014bbe82ce..855b73d66810 100644 --- a/Source/Core/PolygonGeometryLibrary.js +++ b/Source/Core/PolygonGeometryLibrary.js @@ -1,14 +1,36 @@ /*global define*/ define([ './Cartesian3', + './ComponentDatatype', './defaultValue', './defined', - './Ellipsoid' + './Ellipsoid', + './EllipsoidTangentPlane', + './Geometry', + './GeometryAttribute', + './GeometryAttributes', + './IndexDatatype', + './Math', + './PolygonPipeline', + './PrimitiveType', + './Queue', + './WindingOrder' ], function( Cartesian3, + ComponentDatatype, defaultValue, defined, - Ellipsoid) { + Ellipsoid, + EllipsoidTangentPlane, + Geometry, + GeometryAttribute, + GeometryAttributes, + IndexDatatype, + CesiumMath, + PolygonPipeline, + PrimitiveType, + Queue, + WindingOrder) { "use strict"; /** @@ -109,9 +131,6 @@ define([ return [distanceScratch.x, distanceScratch.y, distanceScratch.z]; } - /** - * @private - */ PolygonGeometryLibrary.subdivideLineCount = function(p0, p1, minDistance) { var distance = Cartesian3.distance(p0, p1); var n = distance / minDistance; @@ -119,9 +138,6 @@ define([ return Math.pow(2, countDivide); }; - /** - * @private - */ PolygonGeometryLibrary.subdivideLine = function(p0, p1, minDistance, result) { var numVertices = PolygonGeometryLibrary.subdivideLineCount(p0, p1, minDistance); var length = Cartesian3.distance(p0, p1); @@ -149,9 +165,7 @@ define([ var scaleToGeodeticHeightN2 = new Cartesian3(); var scaleToGeodeticHeightP1 = new Cartesian3(); var scaleToGeodeticHeightP2 = new Cartesian3(); - /** - * @private - */ + PolygonGeometryLibrary.scaleToGeodeticHeightExtruded = function(geometry, maxHeight, minHeight, ellipsoid, perPositionHeight) { ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84); @@ -188,5 +202,211 @@ define([ return geometry; }; + PolygonGeometryLibrary.polygonsFromHierarchy = function(polygonHierarchy) { + // create from a polygon hierarchy + // Algorithm adapted from http://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf + var polygons = []; + var hierarchy = []; + + var queue = new Queue(); + queue.enqueue(polygonHierarchy); + + while (queue.length !== 0) { + var outerNode = queue.dequeue(); + var outerRing = outerNode.positions; + var holes = outerNode.holes; + + outerRing = PolygonPipeline.removeDuplicates(outerRing); + if (outerRing.length < 3) { + continue; + } + + var numChildren = defined(holes) ? holes.length : 0; + var polygonHoles = []; + + for (var i = 0; i < numChildren; i++) { + var hole = holes[i]; + hole.positions = PolygonPipeline.removeDuplicates(hole.positions); + if (hole.positions.length < 3) { + continue; + } + polygonHoles.push(hole.positions); + + var numGrandchildren = 0; + if (defined(hole.holes)) { + numGrandchildren = hole.holes.length; + } + + for ( var j = 0; j < numGrandchildren; j++) { + queue.enqueue(hole.holes[j]); + } + } + + hierarchy.push({ + outerRing : outerRing, + holes : polygonHoles + }); + + var combinedPolygon = polygonHoles.length > 0 ? PolygonPipeline.eliminateHoles(outerRing, polygonHoles) : outerRing; + polygons.push(combinedPolygon); + } + + return { + hierarchy : hierarchy, + polygons : polygons + }; + }; + + var createGeometryFromPositionsPositions = []; + + PolygonGeometryLibrary.createGeometryFromPositions = function(ellipsoid, positions, granularity, perPositionHeight) { + var tangentPlane = EllipsoidTangentPlane.fromPoints(positions, ellipsoid); + var positions2D = tangentPlane.projectPointsOntoPlane(positions, createGeometryFromPositionsPositions); + + var originalWindingOrder = PolygonPipeline.computeWindingOrder2D(positions2D); + if (originalWindingOrder === WindingOrder.CLOCKWISE) { + positions2D.reverse(); + positions.reverse(); + } + + var indices = PolygonPipeline.triangulate(positions2D); + /* If polygon is completely unrenderable, just use the first three vertices */ + if (indices.length < 3) { + indices = [0, 1, 2]; + } + + if (perPositionHeight) { + var length = positions.length; + var flattenedPositions = new Array(length * 3); + var index = 0; + for ( var i = 0; i < length; i++) { + var p = positions[i]; + flattenedPositions[index++] = p.x; + flattenedPositions[index++] = p.y; + flattenedPositions[index++] = p.z; + } + + return new Geometry({ + attributes : { + position : new GeometryAttribute({ + componentDatatype : ComponentDatatype.DOUBLE, + componentsPerAttribute : 3, + values : flattenedPositions + }) + }, + indices : indices, + primitiveType : PrimitiveType.TRIANGLES + }); + } + + return PolygonPipeline.computeSubdivision(ellipsoid, positions, indices, granularity); + }; + + var computeWallIndicesSubdivided = []; + var p1Scratch = new Cartesian3(); + var p2Scratch = new Cartesian3(); + + PolygonGeometryLibrary.computeWallGeometry = function(positions, ellipsoid, granularity, perPositionHeight) { + var edgePositions; + var topEdgeLength; + var i; + var p1; + var p2; + + var length = positions.length; + var index = 0; + + if (!perPositionHeight) { + var minDistance = CesiumMath.chordLength(granularity, ellipsoid.maximumRadius); + + var numVertices = 0; + for (i = 0; i < length; i++) { + numVertices += PolygonGeometryLibrary.subdivideLineCount(positions[i], positions[(i + 1) % length], minDistance); + } + + topEdgeLength = (numVertices + length) * 3; + edgePositions = new Array(topEdgeLength * 2); + for (i = 0; i < length; i++) { + p1 = positions[i]; + p2 = positions[(i + 1) % length]; + + var tempPositions = PolygonGeometryLibrary.subdivideLine(p1, p2, minDistance, computeWallIndicesSubdivided); + var tempPositionsLength = tempPositions.length; + for (var j = 0; j < tempPositionsLength; ++j, ++index) { + edgePositions[index] = tempPositions[j]; + edgePositions[index + topEdgeLength] = tempPositions[j]; + } + + edgePositions[index] = p2.x; + edgePositions[index + topEdgeLength] = p2.x; + ++index; + + edgePositions[index] = p2.y; + edgePositions[index + topEdgeLength] = p2.y; + ++index; + + edgePositions[index] = p2.z; + edgePositions[index + topEdgeLength] = p2.z; + ++index; + } + } else { + topEdgeLength = length * 3 * 2; + edgePositions = new Array(topEdgeLength * 2); + for (i = 0; i < length; i++) { + p1 = positions[i]; + p2 = positions[(i + 1) % length]; + edgePositions[index] = edgePositions[index + topEdgeLength] = p1.x; + ++index; + edgePositions[index] = edgePositions[index + topEdgeLength] = p1.y; + ++index; + edgePositions[index] = edgePositions[index + topEdgeLength] = p1.z; + ++index; + edgePositions[index] = edgePositions[index + topEdgeLength] = p2.x; + ++index; + edgePositions[index] = edgePositions[index + topEdgeLength] = p2.y; + ++index; + edgePositions[index] = edgePositions[index + topEdgeLength] = p2.z; + ++index; + } + } + + length = edgePositions.length; + var indices = IndexDatatype.createTypedArray(length / 3, length - positions.length * 6); + var edgeIndex = 0; + length /= 6; + + for (i = 0; i < length; i++) { + var UL = i; + var UR = UL + 1; + var LL = UL + length; + var LR = LL + 1; + + p1 = Cartesian3.fromArray(edgePositions, UL * 3, p1Scratch); + p2 = Cartesian3.fromArray(edgePositions, UR * 3, p2Scratch); + if (Cartesian3.equalsEpsilon(p1, p2, CesiumMath.EPSILON14)) { + continue; + } + + indices[edgeIndex++] = UL; + indices[edgeIndex++] = LL; + indices[edgeIndex++] = UR; + indices[edgeIndex++] = UR; + indices[edgeIndex++] = LL; + indices[edgeIndex++] = LR; + } + + return new Geometry({ + attributes : new GeometryAttributes({ + position : new GeometryAttribute({ + componentDatatype : ComponentDatatype.DOUBLE, + componentsPerAttribute : 3, + values : edgePositions + }) + }), + indices : indices, + primitiveType : PrimitiveType.TRIANGLES + }); + }; + return PolygonGeometryLibrary; }); \ No newline at end of file diff --git a/Source/Core/SphericalExtent.js b/Source/Core/SphericalExtent.js new file mode 100644 index 000000000000..501768804f0a --- /dev/null +++ b/Source/Core/SphericalExtent.js @@ -0,0 +1,289 @@ +/*global define*/ +define([ + './Cartesian2', + './defaultValue', + './defined', + './DeveloperError', + './Math' + ], function( + Cartesian2, + defaultValue, + defined, + DeveloperError, + CesiumMath) { + "use strict"; + + var SphericalExtent = function() { + this.minimumLatitude = undefined; + this.minimumLongitude = undefined; + this.latitudeExtent = undefined; + this.longitudeExtent = undefined; + }; + + function longitude(extent, v0, v1) { + var lessThan180Degs = extent.lessThan180Degs; + var eastNormal = extent.eastNormal; + var westNormal = extent.westNormal; + var east = extent.east; + var west = extent.west; + + // + // Longitude + // + // If the point is inside the extent, do nothing + // + var tempVec = Cartesian2.clone(v1); + var dotEast = Cartesian2.dot(eastNormal, tempVec); + var dotWest = Cartesian2.dot(westNormal, tempVec); + if (lessThan180Degs) + { + if (dotEast >= -CesiumMath.EPSILON5 && dotWest >= -CesiumMath.EPSILON5) + { + return false; + } + } + else if (!(dotEast < CesiumMath.EPSILON5 && dotWest < CesiumMath.EPSILON5)) + { + return false; + } + + // + // Determine if the line is going more east or more west and assign + // the approriate east or west vector + // + var eastDir = Cartesian2.fromElements(-v0.y, v0.x); + Cartesian2.normalize(eastDir, eastDir); + var diff = Cartesian2.subtract(v1, v0, new Cartesian2()); + var dot = Cartesian2.dot(eastDir, diff); + if (-CesiumMath.EPSILON5 <= dot && dot <= CesiumMath.EPSILON5) + { + // + // It is not obvious if the line is more east or west, so ignore this line + // + extent.previousVertexProcessed = false; + return true; + } + if (dot > CesiumMath.EPSILON5) + { + Cartesian2.clone(v1, east); + Cartesian2.normalize(east, east); + Cartesian2.fromElements(east.y, -east.x, eastNormal); + } + else // if (dot < -CesiumMath.EPSILON5) + { + Cartesian2.clone(v1, west); + Cartesian2.normalize(west, west); + Cartesian2.fromElements(-west.y, west.x, westNormal); + } + + // + // Determine if the extents are less than or equal to 180 degs + // + var crossZ = (east.x * west.y) - (east.y * west.x); + if (!(-CesiumMath.EPSILON5 < crossZ && crossZ < CesiumMath.EPSILON5)) + { + extent.lessThanEqual180Degs = crossZ < 0.0; + } + else + { + Cartesian2.fromElements(-east.y, east.x, eastDir); + diff = Cartesian2.subtract(west, east, diff); + extent.lessThanEqual180Degs = Cartesian2.dot(eastDir, diff) < 0.0; + } + return true; + } + + function initialize(extent, v0, v1) { + // + // This code expects v0 and v1 to be connected + // + // Latitude + // + var zOverXY = v0.z >= 0.0 ? 1.0 : -1.0; + var xYMagSquared = Cartesian2.magnitudeSquared(v0); + if (xYMagSquared !== 0.0) + { + zOverXY *= (v0.z * v0.z) / xYMagSquared; + } + else + { + zOverXY *= Number.MAX_VALUE; + } + extent.minZOverXY = zOverXY; + extent.maxZOverXY = zOverXY; + zOverXY = v1.z >= 0.0 ? 1.0 : -1.0; + xYMagSquared = Cartesian2.magnitudeSquared(v1); + if (xYMagSquared !== 0.0) + { + zOverXY *= (v1.z * v1.z) / xYMagSquared; + } + else + { + zOverXY *= Number.MAX_VALUE; + } + if (zOverXY < extent.minZOverXY) + { + extent.minZOverXY = zOverXY; + } + else if (zOverXY > extent.maxZOverXY) + { + extent.maxZOverXY = zOverXY; + } + + var east = extent.east; + var west = extent.west; + + var eastNormal = extent.eastNormal; + var westNormal = extent.westNormal; + + // + // Longitude + // + // Determine if the line is going more east or more west and assign + // the default east and west vectors; it does not matter if the dot + // product is zero + // + var eastDir = Cartesian2.fromElements(-v0.y, v0.x); + Cartesian2.normalize(eastDir, eastDir); + var diff = Cartesian2.subtract(v1, v0, new Cartesian2()); + if (Cartesian2.dot(eastDir, diff) > 0.0) + { + Cartesian2.clone(v1, east); + Cartesian2.clone(v0, west); + } + else + { + Cartesian2.clone(v0, east); + Cartesian2.clone(v1, west); + } + Cartesian2.normalize(east, east); + Cartesian2.normalize(west, west); + Cartesian2.fromElements(east.y, -east.x, eastNormal); + Cartesian2.fromElements(-west.y, west.x, westNormal); + + // + // The first points cannot be separate by more that 180 degs + // + extent.lessThanEqual180Degs = true; + extent.previousVertexProcessed = true; + } + + function expand(extent, v0, v1) { + // + // This code expects v0 and v1 to be connected + // + // Latitude + // + var zOverXY = v1.z >= 0.0 ? 1.0 : -1.0; + var xYMagSquared = Cartesian2.magnitudeSquared(v1); + if (xYMagSquared !== 0.0) + { + zOverXY *= (v1.z * v1.z) / xYMagSquared; + } + else + { + zOverXY *= Number.MAX_VALUE; + } + if (zOverXY < extent._minZOverXY) + { + extent._minZOverXY = zOverXY; + } + else if (zOverXY > extent._maxZOverXY) + { + extent._maxZOverXY = zOverXY; + } + + // + // Longitude + // + if (!extent._previousVertexProcessed) + { + extent._previousVertexProcessed = true; + if (!longitude(extent, v1, v0) || !extent._previousVertexProcessed) + { + return; + } + } + longitude(extent, v0, v1); + } + + var state = { + minZOverXY : 0.0, + maxZOverXY : 0.0, + east : new Cartesian2(), + west : new Cartesian2(), + eastNormal : new Cartesian2(), + westNormal : new Cartesian2(), + lessThanEqual180Degs : true, + previousVertexProcessed : true + }; + + SphericalExtent.fromPositions = function(positions, result) { + if (!defined(result)) { + result = new SphericalExtent(); + } + + initialize(state, positions[0], positions[1]); + + var length = positions.length; + for (var i = 1; i < length; ++i) { + expand(state, positions[i], positions[(i + 1) % length]); + } + + // + // Latitude + // + var minLat; + if (state.minZOverXY === Number.MAX_VALUE) + { + minLat = CesiumMath.PI_OVER_TWO; + } + else if (state.minZOverXY === -Number.MAX_VALUE) + { + minLat = -CesiumMath.PI_OVER_TWO; + } + else if (state.minZOverXY > 0.0) + { + minLat = Math.atan(Math.sqrt(state.minZOverXY)); + } + else + { + minLat = -Math.atan(Math.sqrt(-state.minZOverXY)); + } + var maxLat; + if (state.maxZOverXY === Number.MAX_VALUE) + { + maxLat = CesiumMath.PI_OVER_TWO; + } + else if (state.maxZOverXY === -Number.MAX_VALUE) + { + maxLat = -CesiumMath.PI_OVER_TWO; + } + else if (state.maxZOverXY > 0.0) + { + maxLat = Math.atan(Math.sqrt(state.maxZOverXY)); + } + else + { + maxLat = -Math.atan(Math.sqrt(-state.maxZOverXY)); + } + + result.minimumLatitude = minLat; + result.latitudeExtent = maxLat - minLat; + + // + // Longitude + // + var west = state.west; + var east = state.east; + + var westAzimuth = Math.atan2(west.y, west.x); + result.minimumLongitude = westAzimuth; + var eastAzimuth = Math.atan2(east.y, east.x); + result.longitudeExtent = eastAzimuth >= westAzimuth ? eastAzimuth - westAzimuth : eastAzimuth - westAzimuth + CesiumMath.TWO_PI; + + return result; + }; + + return SphericalExtent; +}); diff --git a/Source/Renderer/AutomaticUniforms.js b/Source/Renderer/AutomaticUniforms.js index 7fd47ce0bcfa..2547a22432a9 100644 --- a/Source/Renderer/AutomaticUniforms.js +++ b/Source/Renderer/AutomaticUniforms.js @@ -653,6 +653,27 @@ define([ } }), + /** + * An automatic GLSL uniform representing a 4x4 model-view-projection transformation matrix that + * transforms model coordinates, relative to the primitive center, to eye coordinates. + * + * @alias czm_modelViewProjectionRelativeToPrimitiveCenterInverseTranspose + * @glslUniform + * + * @example + * // GLSL declaration + * uniform mat4 czm_modelViewProjectionRelativeToPrimitiveCenterInverseTranspose; + * + * @see czm_modelViewProjectionRelativeToEye + */ + czm_modelViewProjectionRelativeToPrimitiveCenterInverseTranspose : new AutomaticUniform({ + size : 1, + datatype : WebGLRenderingContext.FLOAT_MAT4, + getValue : function(uniformState) { + return uniformState.modelViewProjectionRelativeToPrimitiveCenterInverseTranspose; + } + }), + /** * An automatic GLSL uniform representing a 4x4 transformation matrix that * transforms from eye coordinates to model coordinates. diff --git a/Source/Renderer/Context.js b/Source/Renderer/Context.js index 4a861c00ffd1..f6433b432a68 100644 --- a/Source/Renderer/Context.js +++ b/Source/Renderer/Context.js @@ -1831,6 +1831,7 @@ define([ //>>includeEnd('debug'); context._us.model = defaultValue(drawCommand.modelMatrix, Matrix4.IDENTITY); + context._us.boundingVolume = drawCommand.boundingVolume; var sp = defaultValue(shaderProgram, drawCommand.shaderProgram); sp._setUniforms(drawCommand.uniformMap, context._us, context.validateShaderProgram); diff --git a/Source/Renderer/RenderState.js b/Source/Renderer/RenderState.js index 3e3e360d7055..424587d566ba 100644 --- a/Source/Renderer/RenderState.js +++ b/Source/Renderer/RenderState.js @@ -77,7 +77,7 @@ define([ (stencilOperation === WebGLRenderingContext.INCR) || (stencilOperation === WebGLRenderingContext.DECR) || (stencilOperation === WebGLRenderingContext.INVERT) || - (stencilOperation === WebGLRenderingContext.INCREMENT_WRAP) || + (stencilOperation === WebGLRenderingContext.INCR_WRAP) || (stencilOperation === WebGLRenderingContext.DECR_WRAP)); } diff --git a/Source/Renderer/UniformState.js b/Source/Renderer/UniformState.js index ee19c6d39bb7..41c31beb5514 100644 --- a/Source/Renderer/UniformState.js +++ b/Source/Renderer/UniformState.js @@ -43,6 +43,8 @@ define([ this._globeDepthTexture = undefined; + this._boundingVolume = undefined; + this._model = Matrix4.clone(Matrix4.IDENTITY); this._view = Matrix4.clone(Matrix4.IDENTITY); this._inverseView = Matrix4.clone(Matrix4.IDENTITY); @@ -88,6 +90,9 @@ define([ this._modelViewRelativeToEyeDirty = true; this._modelViewRelativeToEye = new Matrix4(); + this._modelViewProjectionRelativeToPrimitiveCenterInverseTransposeDirty = true; + this._modelViewProjectionRelativeToPrimitiveCenterInverseTranspose = new Matrix4(); + this._inverseModelViewDirty = true; this._inverseModelView = new Matrix4(); @@ -216,6 +221,21 @@ define([ } }, + /** + * @memberof UniformState.prototype + * @type {BoundingSphere|undefined} + */ + boundingVolume : { + get : function() { + return this._boundingVolume; + }, + set : function(volume) { + this._boundingVolume = volume; + + this._modelViewProjectionRelativeToPrimitiveCenterInverseTransposeDirty = true; + } + }, + /** * @memberof UniformState.prototype * @type {Matrix4} @@ -240,6 +260,7 @@ define([ this._modelViewProjectionDirty = true; this._inverseModelViewProjectionDirty = true; this._modelViewProjectionRelativeToEyeDirty = true; + this._modelViewProjectionRelativeToPrimitiveCenterInverseTransposeDirty = true; this._modelViewInfiniteProjectionDirty = true; this._normalDirty = true; this._inverseNormalDirty = true; @@ -550,6 +571,19 @@ define([ } }, + /** + * Model-view-projection relative to the primitive center inverse transpose matrix. + * + * @memberof UniformState.prototype + * @type {Matrix4} + */ + modelViewProjectionRelativeToPrimitiveCenterInverseTranspose : { + get : function() { + cleanModelViewProjectionRelativeToPrimitiveCenterInverseTranspose(this); + return this._modelViewProjectionRelativeToPrimitiveCenterInverseTranspose; + } + }, + /** * @memberof UniformState.prototype * @type {Matrix4} @@ -778,6 +812,7 @@ define([ uniformState._viewProjectionDirty = true; uniformState._modelViewProjectionDirty = true; uniformState._modelViewProjectionRelativeToEyeDirty = true; + uniformState._modelViewProjectionRelativeToPrimitiveCenterInverseTransposeDirty = true; uniformState._modelViewInfiniteProjectionDirty = true; uniformState._normalDirty = true; uniformState._inverseNormalDirty = true; @@ -798,6 +833,7 @@ define([ uniformState._viewProjectionDirty = true; uniformState._modelViewProjectionDirty = true; uniformState._modelViewProjectionRelativeToEyeDirty = true; + uniformState._modelViewProjectionRelativeToPrimitiveCenterInverseTransposeDirty = true; } function setInfiniteProjection(uniformState, matrix) { @@ -1023,6 +1059,45 @@ define([ } } + function cleanModelViewProjectionRelativeToPrimitiveCenterInverseTranspose(uniformState) { + if (uniformState._modelViewProjectionRelativeToPrimitiveCenterInverseTransposeDirty) { + uniformState._modelViewProjectionRelativeToPrimitiveCenterInverseTransposeDirty = false; + + var mvpRTP = uniformState._modelViewProjectionRelativeToPrimitiveCenterInverseTranspose; + var boundingVolume = uniformState._boundingVolume; + if (!defined(boundingVolume)) { + Matrix4.clone(uniformState.inverseModelViewProjection, mvpRTP); + Matrix4.transpose(mvpRTP, mvpRTP); + } else { + var mv = uniformState.modelView; + var center = boundingVolume.center; + + center = Matrix4.multiplyByPoint(mv, center, new Cartesian3()); + + mvpRTP[0] = mv[0]; + mvpRTP[1] = mv[1]; + mvpRTP[2] = mv[2]; + mvpRTP[3] = mv[3]; + mvpRTP[4] = mv[4]; + mvpRTP[5] = mv[5]; + mvpRTP[6] = mv[6]; + mvpRTP[7] = mv[7]; + mvpRTP[8] = mv[8]; + mvpRTP[9] = mv[9]; + mvpRTP[10] = mv[10]; + mvpRTP[11] = mv[11]; + mvpRTP[12] = center.x; + mvpRTP[13] = center.y; + mvpRTP[14] = center.z; + mvpRTP[15] = mv[15]; + + Matrix4.multiply(uniformState.projection, mvpRTP, mvpRTP); + Matrix4.inverse(mvpRTP, mvpRTP); + Matrix4.transpose(mvpRTP, mvpRTP); + } + } + } + function cleanModelViewInfiniteProjection(uniformState) { if (uniformState._modelViewInfiniteProjectionDirty) { uniformState._modelViewInfiniteProjectionDirty = false; diff --git a/Source/Scene/GroundPolygon.js b/Source/Scene/GroundPolygon.js new file mode 100644 index 000000000000..2f09d4541f62 --- /dev/null +++ b/Source/Scene/GroundPolygon.js @@ -0,0 +1,627 @@ +/*global define*/ +define([ + '../Core/BoundingSphere', + '../Core/Cartesian2', + '../Core/Cartesian3', + '../Core/Cartesian4', + '../Core/ComponentDatatype', + '../Core/defaultValue', + '../Core/defined', + '../Core/Ellipsoid', + '../Core/EllipsoidTangentPlane', + '../Core/EncodedCartesian3', + '../Core/IndexDatatype', + '../Core/Math', + '../Core/Matrix4', + '../Core/PolygonGeometryLibrary', + '../Core/PolygonPipeline', + '../Core/PrimitiveType', + '../Core/SphericalExtent', + '../Core/WindingOrder', + '../Renderer/BufferUsage', + '../Renderer/DrawCommand', + '../Shaders/ShadowVolumeFS', + '../Shaders/ShadowVolumeVS', + './BlendingState', + './CullFace', + './DepthFunction', + './Pass', + './StencilFunction', + './StencilOperation' + ], function( + BoundingSphere, + Cartesian2, + Cartesian3, + Cartesian4, + ComponentDatatype, + defaultValue, + defined, + Ellipsoid, + EllipsoidTangentPlane, + EncodedCartesian3, + IndexDatatype, + CesiumMath, + Matrix4, + PolygonGeometryLibrary, + PolygonPipeline, + PrimitiveType, + SphericalExtent, + WindingOrder, + BufferUsage, + DrawCommand, + ShadowVolumeFS, + ShadowVolumeVS, + BlendingState, + CullFace, + DepthFunction, + Pass, + StencilFunction, + StencilOperation) { + "use strict"; + + var GroundPolygon = function(options) { + options = defaultValue(options, defaultValue.EMPTY_OBJECT); + + var ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84); + var granularity = defaultValue(options.granularity, CesiumMath.RADIANS_PER_DEGREE); + var polygonHierarchy = options.polygonHierarchy; + + this._ellipsoid = ellipsoid; + this._granularity = granularity; + this._polygonHierarchy = polygonHierarchy; + + this._boundingSphere = undefined; + + this._northPlane = new Cartesian4(); + this._southPlane = new Cartesian4(); + this._eastPlane = new Cartesian4(); + this._westPlane = new Cartesian4(); + + this._sinCosDeltas = new Cartesian4(); + this._centerAzimuthAndInverseDeltas = new Cartesian4(); + + this._centerAzimuthFromWest = 0.0; + + this._va = undefined; + this._sp = undefined; + this._rs = undefined; + + var that = this; + this._uniformMap = { + centralBodyMinimumAltitude : function() { + return -8500.0; + }, + LODNegativeToleranceOverDistance : function() { + return -0.01; + }, + northPlane : function() { + return that._northPlane; + }, + southPlane : function() { + return that._southPlane; + }, + eastPlane : function() { + return that._eastPlane; + }, + westPlane : function() { + return that._westPlane; + }, + sinCosDeltas : function () { + return that._sinCosDeltas; + }, + centerAzimuthAndInverseDeltas : function () { + return that._centerAzimuthAndInverseDeltas; + }, + centerAzimuthFromWest : function () { + return that._centerAzimuthFromWest; + } + }; + + this._stencilPassCommand = undefined; + this._colorPassCommand = undefined; + }; + + var attributeLocations = { + positionHigh : 0, + positionLow : 1, + normal : 2 + }; + + function getSurfaceDelta(ellipsoid, granularity) { + var refDistance = ellipsoid.maximumRadius; + return refDistance - (refDistance * Math.cos(granularity / 2.0)); + } + + var scratchPosition = new Cartesian3(); + var scratchNormal = new Cartesian3(); + var scratchDeltaNormal = new Cartesian3(); + + // TODO: More than one level of the polygon hierarchy + function createShadowVolume(polygon, context) { + var polygonHierarchy = polygon._polygonHierarchy; + var ellipsoid = polygon._ellipsoid; + var granularity = polygon._granularity; + + var results = PolygonGeometryLibrary.polygonsFromHierarchy(polygonHierarchy); + var positions = results.polygons[0]; + var hierarchy = results.hierarchy[0]; + + var bottomCap = PolygonGeometryLibrary.createGeometryFromPositions(ellipsoid, positions, granularity, false); + var bottomPositions = bottomCap.attributes.position.values; + var numBottomCapVertices = bottomPositions.length / 3; + var numCapVertices = numBottomCapVertices + numBottomCapVertices; + var bottomIndices = bottomCap.indices; + var numBottomIndices = bottomIndices.length; + var numCapIndices = numBottomIndices + numBottomIndices; + + var outerRing = hierarchy.outerRing; + var tangentPlane = EllipsoidTangentPlane.fromPoints(outerRing, ellipsoid); + var positions2D = tangentPlane.projectPointsOntoPlane(outerRing); + + var windingOrder = PolygonPipeline.computeWindingOrder2D(positions2D); + if (windingOrder === WindingOrder.CLOCKWISE) { + outerRing.reverse(); + } + + var wall = PolygonGeometryLibrary.computeWallGeometry(outerRing, ellipsoid, granularity, false); + var walls = [wall]; + var numWallVertices = wall.attributes.position.values.length / 3; + var numWallIndices = wall.indices.length; + + var holes = hierarchy.holes; + var i; + var j; + + for (i = 0; i < holes.length; i++) { + var hole = holes[i]; + + tangentPlane = EllipsoidTangentPlane.fromPoints(hole, ellipsoid); + positions2D = tangentPlane.projectPointsOntoPlane(hole); + + windingOrder = PolygonPipeline.computeWindingOrder2D(positions2D); + if (windingOrder === WindingOrder.COUNTER_CLOCKWISE) { + hole.reverse(); + } + + walls.push(PolygonGeometryLibrary.computeWallGeometry(hole, ellipsoid, granularity, false)); + numWallVertices += wall.attributes.position.values.length / 3; + numWallIndices += wall.indices.length; + walls.push(wall); + } + + var maxAlt = 8500.0; // TODO: get max alt of terrain + var surfaceDelta = getSurfaceDelta(ellipsoid, granularity); + var upDelta = maxAlt + surfaceDelta; + + var numVertices = numCapVertices + numWallVertices; + + var vbPositions = new Float32Array(numVertices * 3 * 2); + var vbNormals = new Float32Array(numVertices * 3); + + var position; + var normal; + var topPosition; + + var index = 0; + var normalIndex = 0; + + for (i = 0; i < numBottomCapVertices * 3; i += 3) { + position = Cartesian3.unpack(bottomPositions, i, scratchPosition); + ellipsoid.scaleToGeodeticSurface(position, position); + normal = ellipsoid.geodeticSurfaceNormal(position, scratchNormal); + + topPosition = Cartesian3.multiplyByScalar(normal, upDelta, scratchDeltaNormal); + Cartesian3.add(position, topPosition, topPosition); + + EncodedCartesian3.writeElements(position, vbPositions, index); + EncodedCartesian3.writeElements(topPosition, vbPositions, index + 6); + index += 12; + + Cartesian3.pack(normal, vbNormals, normalIndex); + Cartesian3.pack(Cartesian3.ZERO, vbNormals, normalIndex + 3); + normalIndex += 6; + } + + var numWalls = walls.length; + var wallPositions; + var wallLength; + + for (i = 0; i < numWalls; ++i) { + wall = walls[i]; + wallPositions = wall.attributes.position.values; + wallLength = wallPositions.length / 2; + + for (j = 0; j < wallLength; j += 3) { + position = Cartesian3.unpack(wallPositions, j, scratchPosition); + ellipsoid.scaleToGeodeticSurface(position, position); + normal = ellipsoid.geodeticSurfaceNormal(position, scratchNormal); + + topPosition = Cartesian3.multiplyByScalar(normal, upDelta, scratchDeltaNormal); + Cartesian3.add(position, topPosition, topPosition); + + EncodedCartesian3.writeElements(topPosition, vbPositions, index); + EncodedCartesian3.writeElements(position, vbPositions, index + wallLength * 2); + index += 6; + + Cartesian3.pack(Cartesian3.ZERO, vbNormals, normalIndex); + Cartesian3.pack(normal, vbNormals, normalIndex + wallLength); + normalIndex += 3; + } + + index += wallLength * 2; + normalIndex += wallLength; + } + + var numIndices = numCapIndices + numWallIndices; + var ibIndices = IndexDatatype.createTypedArray(numVertices, numIndices); + + var i0; + var i1; + var i2; + + index = 0; + for (i = 0; i < numBottomIndices; i += 3) { + i0 = bottomIndices[i] * 2; + i1 = bottomIndices[i + 1] * 2; + i2 = bottomIndices[i + 2] * 2; + + ibIndices[index++] = i2; + ibIndices[index++] = i1; + ibIndices[index++] = i0; + } + + for (i = 0; i < numBottomIndices; i += 3) { + i0 = bottomIndices[i] * 2; + i1 = bottomIndices[i + 1] * 2; + i2 = bottomIndices[i + 2] * 2; + + ibIndices[index++] = i0 + 1; + ibIndices[index++] = i1 + 1; + ibIndices[index++] = i2 + 1; + } + + var offset = numCapVertices; + for (i = 0; i < numWalls; ++i) { + wall = walls[i]; + var wallIndices = wall.indices; + wallLength = wallIndices.length; + + for (j = 0; j < wallLength; ++j) { + ibIndices[index++] = wallIndices[j] + offset; + } + offset += wall.attributes.position.values.length / 3; + } + + var positionBuffer = context.createVertexBuffer(vbPositions, BufferUsage.STATIC_DRAW); + var normalBuffer = context.createVertexBuffer(vbNormals, BufferUsage.STATIC_DRAW); + + var indexDatatype = (ibIndices.BYTES_PER_ELEMENT === 2) ? IndexDatatype.UNSIGNED_SHORT : IndexDatatype.UNSIGNED_INT; + var indexBuffer = context.createIndexBuffer(ibIndices, BufferUsage.STATIC_DRAW, indexDatatype); + + var attributes = [{ + index : attributeLocations.positionHigh, + vertexBuffer : positionBuffer, + componentsPerAttribute : 3, + componentDatatype : ComponentDatatype.FLOAT, + offsetInBytes : 0, + strideInBytes : ComponentDatatype.getSizeInBytes(ComponentDatatype.FLOAT) * 3 * 2 + }, { + index : attributeLocations.positionLow, + vertexBuffer : positionBuffer, + componentsPerAttribute : 3, + componentDatatype : ComponentDatatype.FLOAT, + offsetInBytes : ComponentDatatype.getSizeInBytes(ComponentDatatype.FLOAT) * 3, + strideInBytes : ComponentDatatype.getSizeInBytes(ComponentDatatype.FLOAT) * 3 * 2 + }, { + index : attributeLocations.normal, + vertexBuffer : normalBuffer, + componentsPerAttribute : 3, + componentDatatype : ComponentDatatype.FLOAT + }]; + + polygon._va = context.createVertexArray(attributes, indexBuffer); + + polygon._bottomCapOffset = 0; + polygon._bottomCapCount = numBottomIndices; + polygon._topCapOffset = numBottomIndices; + polygon._topCapCount = numBottomIndices; + polygon._wallOffset = numCapIndices; + polygon._wallCount = numWallIndices; + + polygon._boundingSphere = BoundingSphere.fromPoints(outerRing); + } + + function latitudePlane(north, theta, xy, z, center, plane) { + var normal = scratchNormal; + if (xy !== 0.0) + { + var tempVec0 = Cartesian3.fromElements(Math.cos(theta)* xy, Math.sin(theta) * xy, z); + var tempVec1 = Cartesian3.fromElements(tempVec0.y, -tempVec0.x, 0.0); + if (north) + { + Cartesian3.cross(tempVec0, tempVec1, normal); + } + else + { + Cartesian3.cross(tempVec1, tempVec0, normal); + } + } + else + { + normal.x = center.x; + normal.y = center.y; + normal.z = 0.0; + } + Cartesian3.normalize(normal, normal); + plane.x = normal.x; + plane.y = normal.y; + plane.z = normal.z; + plane.w = Cartesian3.dot(normal, center); + } + + var scratchPlane = new Cartesian4(); + var scratchPlaneRotated = new Cartesian4(); + + function computeTextureCoordinates(polygon) { + var polygonHierarchy = polygon._polygonHierarchy; + var outerRing = polygonHierarchy.positions; + + var center = polygon._boundingSphere.center; + + var sphericalExtent = SphericalExtent.fromPositions(outerRing); + var minimumLatitude = sphericalExtent.minimumLatitude; + var minimumLongitude = sphericalExtent.minimumLongitude; + var latitudeExtent = sphericalExtent.latitudeExtent; + var longitudeExtent = sphericalExtent.longitudeExtent; + + // + // West plane + // + var westPlane = polygon._westPlane; + westPlane.x = -Math.sin(minimumLongitude); + westPlane.y = Math.cos(minimumLongitude); + westPlane.z = 0.0; + westPlane.w = (westPlane.x * center.x) + (westPlane.y * center.y) + (westPlane.z * center.z); + + // + // East plane + // + var eastPlane = polygon._eastPlane; + var tempDouble = minimumLongitude + longitudeExtent; + eastPlane.x = Math.sin(tempDouble); + eastPlane.y = -Math.cos(tempDouble); + eastPlane.z = 0.0; + eastPlane.w = (eastPlane.x * center.x) + (eastPlane.y * center.y) + (eastPlane.z * center.z); + + // + // Sin and cos lon/lat deltas + // + var sinCosDeltas = polygon._sinCosDeltas; + sinCosDeltas.x = Math.sin(longitudeExtent); + sinCosDeltas.y = Math.cos(longitudeExtent); + sinCosDeltas.z = Math.sin(latitudeExtent); + sinCosDeltas.w = Math.cos(latitudeExtent); + + // + // Center azimuth + // + var centerAzimuth = Cartesian2.fromElements(center.x, center.y); + Cartesian2.normalize(centerAzimuth, centerAzimuth); + var centerAzimuthAndInverseDeltas = polygon._centerAzimuthAndInverseDeltas; + centerAzimuthAndInverseDeltas.x = centerAzimuth.x; + centerAzimuthAndInverseDeltas.y = centerAzimuth.y; + + // + // Inverse deltas + // + /* + if (volue is medium or small) + { + // + // When both extents are sufficiently small, the shader approximates arctan and + // tan to be the same; due to differences, the extents must be increased slightly + // + centerAzimuthAndInverseDeltas.z = 1.0 / Math.tan(longitudeExtent); + centerAzimuthAndInverseDeltas.w = 1.0 / Math.tan(latitudeExtent); + var azimuthXY = Cartesian2.fromElements(centerAzimuthAndInverseDeltas.x, centerAzimuthAndInverseDeltas.y); + Cartesian2.normalize(azimuthXY, azimuthXY); + var westXY = Cartesian2.fromElements(westPlane.y, -westPlane.x); + Cartesian2.normalize(westXY, westXY); + polygon._centerAzimuthFromWest = Math.acos(Cartesian2.dot(azimuthXY, westXY)); + } + else + { + */ + centerAzimuthAndInverseDeltas.z = 1.0 / longitudeExtent; + centerAzimuthAndInverseDeltas.w = 1.0 / latitudeExtent; + polygon._centerAzimuthFromWest = 0.0; + //} + + // + // North/South plane - for small volumes the north and south planes are calculated + // once. They are approximations that improve frame rate at the slight cost + // of visual quality. For larger volumes, the pixel shader calculates the + // planes on the fly using data calculated below. This is slower but is + // necessary to preserve visual quality. + // + // North plane + // + var northPlane = polygon._northPlane; + var centerAzimuthAngle = Math.atan2(centerAzimuth.y, centerAzimuth.x); + var z = Math.sin(minimumLatitude + latitudeExtent); + var xy = 1.0 - z * z; + xy = (xy < 0.0) ? 0.0 : Math.sqrt(xy); + + var plane = scratchPlane; + var planeRotated180Degs = scratchPlaneRotated; + latitudePlane(true, centerAzimuthAngle, xy, z, center, plane); + + /* + if (volume is small) + { + northPlane.x = plane.x; + northPlane.y = plane.y; + northPlane.z = plane.z; + northPlane.w = (northPlane.x * center.x) + (northPlane.y * center.y) + (northPlane.z * center.z); + } + else + { + */ + latitudePlane(true, centerAzimuthAngle + Math.PI, xy, z, center, planeRotated180Degs); + xy = 1.0 - plane.z * plane.z; + xy = (xy > 0.0) ? Math.sqrt(xy) : 0.0; + if (minimumLatitude + latitudeExtent < 0.0) + { + xy = -xy; + } + northPlane.x = plane.z; + northPlane.y = xy; + northPlane.z = planeRotated180Degs.w; + northPlane.w = plane.w - planeRotated180Degs.w; + //} + + // + // South plane + // + var southPlane = polygon._southPlane; + z = Math.sin(minimumLatitude); + xy = 1.0 - z * z; + xy = (xy < 0.0) ? 0.0 : Math.sqrt(xy); + latitudePlane(false, centerAzimuthAngle, xy, z, center, plane); + /* + if (volume is small) + { + southPlane.x = plane.x; + southPlane.y = plane.y; + southPlane.z = plane.z; + southPlane.w = (southPlane.x * center.x) + (southPlane.y * center.y) + (southPlane.z * center.z); + } + else + { + */ + latitudePlane(false, centerAzimuthAngle + Math.PI, xy, z, center, planeRotated180Degs); + xy = 1.0 - plane.z * plane.z; + xy = (xy > 0.0) ? Math.sqrt(xy) : 0.0; + if (minimumLatitude > 0.0) + { + xy = -xy; + } + southPlane.x = plane.z; + southPlane.y = xy; + southPlane.z = planeRotated180Degs.w; + southPlane.w = plane.w - planeRotated180Degs.w; + //} + } + + GroundPolygon.prototype.update = function(context, frameState, commandList) { + // TODO: determin if supported + if (!defined(context.uniformState.globeDepthTexture)) { + return; + } + + if (!defined(this._va)) { + createShadowVolume(this, context); + computeTextureCoordinates(this); + } + + if (!defined(this._sp)) { + this._sp = context.createShaderProgram(ShadowVolumeVS, ShadowVolumeFS, attributeLocations); + } + + if (!defined(this._stencilPassCommand)) { + var stencilPassRenderState = context.createRenderState({ + colorMask : { + red : false, + green : false, + blue : false, + alpha : false + }, + stencilTest : { + enabled : true, + frontFunction : StencilFunction.ALWAYS, + frontOperation : { + fail : StencilOperation.KEEP, + zFail : StencilOperation.KEEP, + zPass : StencilOperation.INCREMENT_WRAP + }, + backFunction : StencilFunction.ALWAYS, + backOperation : { + fail : StencilOperation.KEEP, + zFail : StencilOperation.KEEP, + zPass : StencilOperation.DECREMENT_WRAP + }, + reference : 0, + mask : ~0 + }, + depthTest : { + enabled : true + }, + depthMask : false + }); + + this._stencilPassCommand = new DrawCommand({ + primitiveType : PrimitiveType.TRIANGLES, + //offset : this._topCapOffset, + //count : this._topCapCount + this._wallCount, + vertexArray : this._va, + renderState : stencilPassRenderState, + shaderProgram : this._sp, + uniformMap : this._uniformMap, + boundingVolume : this._boundingSphere, + owner : this, + modelMatrix : Matrix4.IDENTITY, + pass : Pass.OPAQUE + }); + + var coloRenderState = context.createRenderState({ + stencilTest : { + enabled : true, + frontFunction : StencilFunction.NOT_EQUAL, + frontOperation : { + fail : StencilOperation.KEEP, + zFail : StencilOperation.KEEP, + zPass : StencilOperation.DECREMENT + }, + backFunction : StencilFunction.NOT_EQUAL, + backOperation : { + fail : StencilOperation.KEEP, + zFail : StencilOperation.KEEP, + zPass : StencilOperation.DECREMENT + }, + reference : 0, + mask : ~0 + }, + cull : { + enabled : true, + face : CullFace.BACK + }, + depthTest : { + enabled : false + }, + depthMask : false, + blending : BlendingState.ALPHA_BLEND + }); + + this._colorPassCommand = new DrawCommand({ + primitiveType : PrimitiveType.TRIANGLES, + //offset : this._topCapOffset, + //count : this._topCapCount + this._wallCount, + vertexArray : this._va, + renderState : coloRenderState, + shaderProgram : this._sp, + uniformMap : this._uniformMap, + boundingVolume : this._boundingSphere, + owner : this, + modelMatrix : Matrix4.IDENTITY, + pass : Pass.OPAQUE + }); + } + + var pass = frameState.passes; + if (pass.render) { + commandList.push(this._stencilPassCommand, this._colorPassCommand); + } + }; + + return GroundPolygon; +}); diff --git a/Source/Scene/PolygonOnTerrain.js b/Source/Scene/PolygonOnTerrain.js new file mode 100644 index 000000000000..7df673831efd --- /dev/null +++ b/Source/Scene/PolygonOnTerrain.js @@ -0,0 +1,705 @@ +/*global define*/ +define([ + '../Core/BoundingSphere', + '../Core/Cartesian2', + '../Core/Cartesian3', + '../Core/Cartesian4', + '../Core/ComponentDatatype', + '../Core/defaultValue', + '../Core/defined', + '../Core/Ellipsoid', + '../Core/EllipsoidTangentPlane', + '../Core/EncodedCartesian3', + '../Core/IndexDatatype', + '../Core/Math', + '../Core/Matrix4', + '../Core/PolygonGeometryLibrary', + '../Core/PolygonPipeline', + '../Core/PrimitiveType', + '../Core/SphericalExtent', + '../Core/WindingOrder', + '../Renderer/BufferUsage', + '../Renderer/DrawCommand', + '../Shaders/ShadowVolumeFS', + '../Shaders/ShadowVolumeVS', + './BlendingState', + './CullFace', + './DepthFunction', + './Pass', + './StencilFunction', + './StencilOperation' + ], function( + BoundingSphere, + Cartesian2, + Cartesian3, + Cartesian4, + ComponentDatatype, + defaultValue, + defined, + Ellipsoid, + EllipsoidTangentPlane, + EncodedCartesian3, + IndexDatatype, + CesiumMath, + Matrix4, + PolygonGeometryLibrary, + PolygonPipeline, + PrimitiveType, + SphericalExtent, + WindingOrder, + BufferUsage, + DrawCommand, + ShadowVolumeFS, + ShadowVolumeVS, + BlendingState, + CullFace, + DepthFunction, + Pass, + StencilFunction, + StencilOperation) { + "use strict"; + + var PolygonOnTerrain = function(options) { + options = defaultValue(options, defaultValue.EMPTY_OBJECT); + + var ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84); + var granularity = defaultValue(options.granularity, CesiumMath.RADIANS_PER_DEGREE); + var polygonHierarchy = options.polygonHierarchy; + + this._ellipsoid = ellipsoid; + this._granularity = granularity; + this._polygonHierarchy = polygonHierarchy; + + this._boundingSphere = undefined; + + this._northPlane = new Cartesian4(); + this._southPlane = new Cartesian4(); + this._eastPlane = new Cartesian4(); + this._westPlane = new Cartesian4(); + + this._sinCosDeltas = new Cartesian4(); + this._centerAzimuthAndInverseDeltas = new Cartesian4(); + + this._centerAzimuthFromWest = 0.0; + + this._va = undefined; + this._sp = undefined; + this._rs = undefined; + + this._depthTexture = undefined; + + var that = this; + this._uniformMap = { + centralBodyMinimumAltitude : function() { + return -8500.0; + }, + LODNegativeToleranceOverDistance : function() { + return -0.01; + }, + northPlane : function() { + return that._northPlane; + }, + southPlane : function() { + return that._southPlane; + }, + eastPlane : function() { + return that._eastPlane; + }, + westPlane : function() { + return that._westPlane; + }, + sinCosDeltas : function () { + return that._sinCosDeltas; + }, + centerAzimuthAndInverseDeltas : function () { + return that._centerAzimuthAndInverseDeltas; + }, + centerAzimuthFromWest : function () { + return that._centerAzimuthFromWest; + }, + czm_centralBodyDepthTexture : function() { + return that._depthTexture; + } + }; + + this._zFailCommand = undefined; + this._zPassCommand = undefined; + this._colorInsideSphereCommand = undefined; + this._colorOutsideSphereCommand = undefined; + + this._scene = options.scene; + }; + + var attributeLocations = { + positionHigh : 0, + positionLow : 1, + normal : 2 + }; + + function getSurfaceDelta(ellipsoid, granularity) { + var refDistance = ellipsoid.maximumRadius; + return refDistance - (refDistance * Math.cos(granularity / 2.0)); + } + + var scratchPosition = new Cartesian3(); + var scratchNormal = new Cartesian3(); + var scratchDeltaNormal = new Cartesian3(); + + // TODO: More than one level of the polygon hierarchy + function createShadowVolume(polygon, context) { + var polygonHierarchy = polygon._polygonHierarchy; + var ellipsoid = polygon._ellipsoid; + //var granularity = polygon._granularity; + var granularity = CesiumMath.PI_OVER_FOUR; + + var results = PolygonGeometryLibrary.polygonsFromHierarchy(polygonHierarchy); + var positions = results.polygons[0]; + var hierarchy = results.hierarchy[0]; + + var bottomCap = PolygonGeometryLibrary.createGeometryFromPositions(ellipsoid, positions, granularity, false); + var bottomPositions = bottomCap.attributes.position.values; + var numBottomCapVertices = bottomPositions.length / 3; + var numCapVertices = numBottomCapVertices + numBottomCapVertices; + var bottomIndices = bottomCap.indices; + var numBottomIndices = bottomIndices.length; + var numCapIndices = numBottomIndices + numBottomIndices; + + var outerRing = hierarchy.outerRing; + var tangentPlane = EllipsoidTangentPlane.fromPoints(outerRing, ellipsoid); + var positions2D = tangentPlane.projectPointsOntoPlane(outerRing); + + var windingOrder = PolygonPipeline.computeWindingOrder2D(positions2D); + if (windingOrder === WindingOrder.CLOCKWISE) { + outerRing.reverse(); + } + + var wall = PolygonGeometryLibrary.computeWallGeometry(outerRing, ellipsoid, granularity, false); + var walls = [wall]; + var numWallVertices = wall.attributes.position.values.length / 3; + var numWallIndices = wall.indices.length; + + var holes = hierarchy.holes; + var i; + var j; + + for (i = 0; i < holes.length; i++) { + var hole = holes[i]; + + tangentPlane = EllipsoidTangentPlane.fromPoints(hole, ellipsoid); + positions2D = tangentPlane.projectPointsOntoPlane(hole); + + windingOrder = PolygonPipeline.computeWindingOrder2D(positions2D); + if (windingOrder === WindingOrder.COUNTER_CLOCKWISE) { + hole.reverse(); + } + + wall = PolygonGeometryLibrary.computeWallGeometry(hole, ellipsoid, granularity, false); + numWallVertices += wall.attributes.position.values.length / 3; + numWallIndices += wall.indices.length; + walls.push(wall); + } + + var maxAlt = 8500.0; // TODO: get max alt of terrain + var surfaceDelta = getSurfaceDelta(ellipsoid, granularity); + var upDelta = maxAlt + surfaceDelta; + + var numVertices = numCapVertices + numWallVertices; + + var vbPositions = new Float32Array(numVertices * 3 * 2); + var vbNormals = new Float32Array(numVertices * 3); + + var position; + var normal; + var topPosition; + + var index = 0; + var normalIndex = 0; + + for (i = 0; i < numBottomCapVertices * 3; i += 3) { + position = Cartesian3.unpack(bottomPositions, i, scratchPosition); + ellipsoid.scaleToGeodeticSurface(position, position); + normal = ellipsoid.geodeticSurfaceNormal(position, scratchNormal); + + topPosition = Cartesian3.multiplyByScalar(normal, upDelta, scratchDeltaNormal); + Cartesian3.add(position, topPosition, topPosition); + + EncodedCartesian3.writeElements(position, vbPositions, index); + EncodedCartesian3.writeElements(topPosition, vbPositions, index + 6); + index += 12; + + Cartesian3.pack(normal, vbNormals, normalIndex); + Cartesian3.pack(Cartesian3.ZERO, vbNormals, normalIndex + 3); + normalIndex += 6; + } + + var numWalls = walls.length; + var wallPositions; + var wallLength; + + for (i = 0; i < numWalls; ++i) { + wall = walls[i]; + wallPositions = wall.attributes.position.values; + wallLength = wallPositions.length / 2; + + for (j = 0; j < wallLength; j += 3) { + position = Cartesian3.unpack(wallPositions, j, scratchPosition); + ellipsoid.scaleToGeodeticSurface(position, position); + normal = ellipsoid.geodeticSurfaceNormal(position, scratchNormal); + + topPosition = Cartesian3.multiplyByScalar(normal, upDelta, scratchDeltaNormal); + Cartesian3.add(position, topPosition, topPosition); + + EncodedCartesian3.writeElements(topPosition, vbPositions, index); + EncodedCartesian3.writeElements(position, vbPositions, index + wallLength * 2); + index += 6; + + Cartesian3.pack(Cartesian3.ZERO, vbNormals, normalIndex); + Cartesian3.pack(normal, vbNormals, normalIndex + wallLength); + normalIndex += 3; + } + + index += wallLength * 2; + normalIndex += wallLength; + } + + var numIndices = numCapIndices + numWallIndices; + var ibIndices = IndexDatatype.createTypedArray(numVertices, numIndices); + + var i0; + var i1; + var i2; + + index = 0; + for (i = 0; i < numBottomIndices; i += 3) { + i0 = bottomIndices[i] * 2; + i1 = bottomIndices[i + 1] * 2; + i2 = bottomIndices[i + 2] * 2; + + ibIndices[index++] = i2; + ibIndices[index++] = i1; + ibIndices[index++] = i0; + } + + for (i = 0; i < numBottomIndices; i += 3) { + i0 = bottomIndices[i] * 2; + i1 = bottomIndices[i + 1] * 2; + i2 = bottomIndices[i + 2] * 2; + + ibIndices[index++] = i0 + 1; + ibIndices[index++] = i1 + 1; + ibIndices[index++] = i2 + 1; + } + + var offset = numCapVertices; + for (i = 0; i < numWalls; ++i) { + wall = walls[i]; + var wallIndices = wall.indices; + wallLength = wallIndices.length; + + for (j = 0; j < wallLength; ++j) { + ibIndices[index++] = wallIndices[j] + offset; + } + offset += wall.attributes.position.values.length / 3; + } + + var positionBuffer = context.createVertexBuffer(vbPositions, BufferUsage.STATIC_DRAW); + var normalBuffer = context.createVertexBuffer(vbNormals, BufferUsage.STATIC_DRAW); + + var indexDatatype = (ibIndices.BYTES_PER_ELEMENT === 2) ? IndexDatatype.UNSIGNED_SHORT : IndexDatatype.UNSIGNED_INT; + var indexBuffer = context.createIndexBuffer(ibIndices, BufferUsage.STATIC_DRAW, indexDatatype); + + var attributes = [{ + index : attributeLocations.positionHigh, + vertexBuffer : positionBuffer, + componentsPerAttribute : 3, + componentDatatype : ComponentDatatype.FLOAT, + offsetInBytes : 0, + strideInBytes : ComponentDatatype.getSizeInBytes(ComponentDatatype.FLOAT) * 3 * 2 + }, { + index : attributeLocations.positionLow, + vertexBuffer : positionBuffer, + componentsPerAttribute : 3, + componentDatatype : ComponentDatatype.FLOAT, + offsetInBytes : ComponentDatatype.getSizeInBytes(ComponentDatatype.FLOAT) * 3, + strideInBytes : ComponentDatatype.getSizeInBytes(ComponentDatatype.FLOAT) * 3 * 2 + }, { + index : attributeLocations.normal, + vertexBuffer : normalBuffer, + componentsPerAttribute : 3, + componentDatatype : ComponentDatatype.FLOAT + }]; + + polygon._va = context.createVertexArray(attributes, indexBuffer); + + polygon._bottomCapOffset = 0; + polygon._bottomCapCount = numBottomIndices; + polygon._topCapOffset = numBottomIndices; + polygon._topCapCount = numBottomIndices; + polygon._wallOffset = numCapIndices; + polygon._wallCount = numWallIndices; + + polygon._boundingSphere = BoundingSphere.fromPoints(outerRing); + } + + function latitudePlane(north, theta, xy, z, center, plane) { + var normal = scratchNormal; + if (xy !== 0.0) + { + var tempVec0 = Cartesian3.fromElements(Math.cos(theta)* xy, Math.sin(theta) * xy, z); + var tempVec1 = Cartesian3.fromElements(tempVec0.y, -tempVec0.x, 0.0); + if (north) + { + Cartesian3.cross(tempVec0, tempVec1, normal); + } + else + { + Cartesian3.cross(tempVec1, tempVec0, normal); + } + } + else + { + normal.x = center.x; + normal.y = center.y; + normal.z = 0.0; + } + Cartesian3.normalize(normal, normal); + plane.x = normal.x; + plane.y = normal.y; + plane.z = normal.z; + plane.w = Cartesian3.dot(normal, center); + } + + var scratchPlane = new Cartesian4(); + var scratchPlaneRotated = new Cartesian4(); + + function computeTextureCoordinates(polygon) { + var polygonHierarchy = polygon._polygonHierarchy; + var outerRing = polygonHierarchy.positions; + + var center = polygon._boundingSphere.center; + + var sphericalExtent = SphericalExtent.fromPositions(outerRing); + var minimumLatitude = sphericalExtent.minimumLatitude; + var minimumLongitude = sphericalExtent.minimumLongitude; + var latitudeExtent = sphericalExtent.latitudeExtent; + var longitudeExtent = sphericalExtent.longitudeExtent; + + // + // West plane + // + var westPlane = polygon._westPlane; + westPlane.x = -Math.sin(minimumLongitude); + westPlane.y = Math.cos(minimumLongitude); + westPlane.z = 0.0; + westPlane.w = (westPlane.x * center.x) + (westPlane.y * center.y) + (westPlane.z * center.z); + + // + // East plane + // + var eastPlane = polygon._eastPlane; + var tempDouble = minimumLongitude + longitudeExtent; + eastPlane.x = Math.sin(tempDouble); + eastPlane.y = -Math.cos(tempDouble); + eastPlane.z = 0.0; + eastPlane.w = (eastPlane.x * center.x) + (eastPlane.y * center.y) + (eastPlane.z * center.z); + + // + // Sin and cos lon/lat deltas + // + var sinCosDeltas = polygon._sinCosDeltas; + sinCosDeltas.x = Math.sin(longitudeExtent); + sinCosDeltas.y = Math.cos(longitudeExtent); + sinCosDeltas.z = Math.sin(latitudeExtent); + sinCosDeltas.w = Math.cos(latitudeExtent); + + // + // Center azimuth + // + var centerAzimuth = Cartesian2.fromElements(center.x, center.y); + Cartesian2.normalize(centerAzimuth, centerAzimuth); + var centerAzimuthAndInverseDeltas = polygon._centerAzimuthAndInverseDeltas; + centerAzimuthAndInverseDeltas.x = centerAzimuth.x; + centerAzimuthAndInverseDeltas.y = centerAzimuth.y; + + // + // Inverse deltas + // + /* + if (volue is medium or small) + { + // + // When both extents are sufficiently small, the shader approximates arctan and + // tan to be the same; due to differences, the extents must be increased slightly + // + centerAzimuthAndInverseDeltas.z = 1.0 / Math.tan(longitudeExtent); + centerAzimuthAndInverseDeltas.w = 1.0 / Math.tan(latitudeExtent); + var azimuthXY = Cartesian2.fromElements(centerAzimuthAndInverseDeltas.x, centerAzimuthAndInverseDeltas.y); + Cartesian2.normalize(azimuthXY, azimuthXY); + var westXY = Cartesian2.fromElements(westPlane.y, -westPlane.x); + Cartesian2.normalize(westXY, westXY); + polygon._centerAzimuthFromWest = Math.acos(Cartesian2.dot(azimuthXY, westXY)); + } + else + { + */ + centerAzimuthAndInverseDeltas.z = 1.0 / longitudeExtent; + centerAzimuthAndInverseDeltas.w = 1.0 / latitudeExtent; + polygon._centerAzimuthFromWest = 0.0; + //} + + // + // North/South plane - for small volumes the north and south planes are calculated + // once. They are approximations that improve frame rate at the slight cost + // of visual quality. For larger volumes, the pixel shader calculates the + // planes on the fly using data calculated below. This is slower but is + // necessary to preserve visual quality. + // + // North plane + // + var northPlane = polygon._northPlane; + var centerAzimuthAngle = Math.atan2(centerAzimuth.y, centerAzimuth.x); + var z = Math.sin(minimumLatitude + latitudeExtent); + var xy = 1.0 - z * z; + xy = (xy < 0.0) ? 0.0 : Math.sqrt(xy); + + var plane = scratchPlane; + var planeRotated180Degs = scratchPlaneRotated; + latitudePlane(true, centerAzimuthAngle, xy, z, center, plane); + + /* + if (volume is small) + { + northPlane.x = plane.x; + northPlane.y = plane.y; + northPlane.z = plane.z; + northPlane.w = (northPlane.x * center.x) + (northPlane.y * center.y) + (northPlane.z * center.z); + } + else + { + */ + latitudePlane(true, centerAzimuthAngle + Math.PI, xy, z, center, planeRotated180Degs); + xy = 1.0 - plane.z * plane.z; + xy = (xy > 0.0) ? Math.sqrt(xy) : 0.0; + if (minimumLatitude + latitudeExtent < 0.0) + { + xy = -xy; + } + northPlane.x = plane.z; + northPlane.y = xy; + northPlane.z = planeRotated180Degs.w; + northPlane.w = plane.w - planeRotated180Degs.w; + //} + + // + // South plane + // + var southPlane = polygon._southPlane; + z = Math.sin(minimumLatitude); + xy = 1.0 - z * z; + xy = (xy < 0.0) ? 0.0 : Math.sqrt(xy); + latitudePlane(false, centerAzimuthAngle, xy, z, center, plane); + /* + if (volume is small) + { + southPlane.x = plane.x; + southPlane.y = plane.y; + southPlane.z = plane.z; + southPlane.w = (southPlane.x * center.x) + (southPlane.y * center.y) + (southPlane.z * center.z); + } + else + { + */ + latitudePlane(false, centerAzimuthAngle + Math.PI, xy, z, center, planeRotated180Degs); + xy = 1.0 - plane.z * plane.z; + xy = (xy > 0.0) ? Math.sqrt(xy) : 0.0; + if (minimumLatitude > 0.0) + { + xy = -xy; + } + southPlane.x = plane.z; + southPlane.y = xy; + southPlane.z = planeRotated180Degs.w; + southPlane.w = plane.w - planeRotated180Degs.w; + //} + } + + PolygonOnTerrain.prototype.update = function(context, frameState, commandList) { + var fbo = this._scene._oit._opaqueFBO; + if (!defined(fbo)) { + return; + } + + if (!defined(this._va)) { + createShadowVolume(this, context); + computeTextureCoordinates(this); + } + + if (!defined(this._sp)) { + this._sp = context.createShaderProgram(ShadowVolumeVS, ShadowVolumeFS, attributeLocations); + } + + if (!defined(this._depthTexture)) { + this._depthTexture = fbo.depthStencilTexture; + } + + if (!defined(this._zFailCommand)) { + var disableColorWrites = { + red : false, + green : false, + blue : false, + alpha : false + }; + + var zFailRenderState = context.createRenderState({ + colorMask : disableColorWrites, + stencilTest : { + enabled : true, + frontFunction : StencilFunction.ALWAYS, + frontOperation : { + fail : StencilOperation.KEEP, + zFail : StencilOperation.DECREMENT_WRAP, + zPass : StencilOperation.KEEP + }, + backFunction : StencilFunction.ALWAYS, + backOperation : { + fail : StencilOperation.KEEP, + zFail : StencilOperation.INCREMENT_WRAP, + zPass : StencilOperation.KEEP + }, + reference : 0, + mask : ~0 + }, + depthTest : { + enabled : true + }, + depthMask : false + }); + + this._zFailCommand = new DrawCommand({ + primitiveType : PrimitiveType.TRIANGLES, + vertexArray : this._va, + renderState : zFailRenderState, + shaderProgram : this._sp, + uniformMap : this._uniformMap, + boundingVolume : this._boundingSphere, + owner : this, + modelMatrix : Matrix4.IDENTITY, + pass : Pass.OPAQUE + }); + + var zPassRenderState = context.createRenderState({ + colorMask : disableColorWrites, + stencilTest : { + enabled : true, + frontFunction : StencilFunction.ALWAYS, + frontOperation : { + fail : StencilOperation.KEEP, + zFail : StencilOperation.KEEP, + zPass : StencilOperation.INCREMENT_WRAP + }, + backFunction : StencilFunction.ALWAYS, + backOperation : { + fail : StencilOperation.KEEP, + zFail : StencilOperation.KEEP, + zPass : StencilOperation.DECREMENT_WRAP + }, + reference : 0, + mask : ~0 + }, + depthTest : { + enabled : true + }, + depthMask : false + }); + + this._zPassCommand = new DrawCommand({ + primitiveType : PrimitiveType.TRIANGLES, + offset : this._topCapOffset, + count : this._topCapCount + this._wallCount, + vertexArray : this._va, + renderState : zPassRenderState, + shaderProgram : this._sp, + uniformMap : this._uniformMap, + boundingVolume : this._boundingSphere, + owner : this, + modelMatrix : Matrix4.IDENTITY, + pass : Pass.OPAQUE + }); + + var colorStencilTest = { + enabled : true, + frontFunction : StencilFunction.NOT_EQUAL, + frontOperation : { + fail : StencilOperation.KEEP, + zFail : StencilOperation.KEEP, + zPass : StencilOperation.DECREMENT + }, + backFunction : StencilFunction.NOT_EQUAL, + backOperation : { + fail : StencilOperation.KEEP, + zFail : StencilOperation.KEEP, + zPass : StencilOperation.DECREMENT + }, + reference : 0, + mask : ~0 + }; + + var colorInsideSphereRenderState = context.createRenderState({ + stencilTest : colorStencilTest, + depthTest : { + enabled : false + }, + depthMask : false, + blending : BlendingState.ALPHA_BLEND + }); + + this._colorInsideCommand = new DrawCommand({ + primitiveType : PrimitiveType.TRIANGLES, + vertexArray : this._va, + renderState : colorInsideSphereRenderState, + shaderProgram : this._sp, + uniformMap : this._uniformMap, + boundingVolume : this._boundingSphere, + owner : this, + modelMatrix : Matrix4.IDENTITY, + pass : Pass.OPAQUE + }); + + var colorOutsideSphereRenderState = context.createRenderState({ + stencilTest : colorStencilTest, + cull : { + enabled : true, + face : CullFace.BACK + }, + depthTest : { + enabled : false + }, + depthMask : false, + blending : BlendingState.ALPHA_BLEND + }); + + this._colorOutsideSphereCommand = new DrawCommand({ + primitiveType : PrimitiveType.TRIANGLES, + offset : this._topCapOffset, + count : this._topCapCount + this._wallCount, + vertexArray : this._va, + renderState : colorOutsideSphereRenderState, + shaderProgram : this._sp, + uniformMap : this._uniformMap, + boundingVolume : this._boundingSphere, + owner : this, + modelMatrix : Matrix4.IDENTITY, + pass : Pass.OPAQUE + }); + } + + var pass = frameState.passes; + if (pass.render) { + // intersects near/far plane: z-fail else z-pass + // inside bounding sphere : colorInsideSphere commands else color outside + commandList.push(this._zPassCommand, this._colorInsideCommand); + } + }; + + return PolygonOnTerrain; +}); diff --git a/Source/Shaders/ShadowVolumeFS.glsl b/Source/Shaders/ShadowVolumeFS.glsl new file mode 100644 index 000000000000..a811f982cf1b --- /dev/null +++ b/Source/Shaders/ShadowVolumeFS.glsl @@ -0,0 +1,138 @@ +// +// The west longitude plane relative to the center of the mesh +// +uniform vec4 westPlane; +// +// The west longitude plane relative to the center of the mesh +// +uniform vec4 eastPlane; +// +// Center azimuth and deltas: +// x = center azimuth x +// y = center azimuth y +// z = 1.0 / longitude delta +// w = 1.0 / latitude delta +// +uniform vec4 centerAzimuthAndInverseDeltas; +// +// Sin and cos deltas: +// x = sin(longitude delta) +// y = cos(longitude delta) +// z = sin(latitude delta) +// w = cos(latitude delta) +// +uniform vec4 sinCosDeltas; +// +// North plane: +// x = normal C magnitude +// y = normal AB magnitude +// z = minimum D +// w = maximum D - minimum D +// +uniform vec4 northPlane; +// +// South plane: +// x = normal C magnitude +// y = normal AB magnitude +// z = minimum D +// w = maximum D - minimum D +// +uniform vec4 southPlane; + +void main(void) +{ + // + // Get the depth value of the terrain for the current fragment. + // Note that this is almost certainly different then the depth + // value of the shadow volume, which is why the depth texture + // is used in the first place. + // + vec2 depthTextureCoord; + depthTextureCoord.x = (gl_FragCoord.x - czm_viewport.x) / czm_viewport.z; + depthTextureCoord.y = (gl_FragCoord.y - czm_viewport.y) / czm_viewport.w; + vec4 terrainDepth = texture2D(czm_globeDepthTexture, depthTextureCoord); + // + // Convert to normalized device coordinates. That is + // a cube from [-1, -1, -1] to [1, 1, 1]. + // + vec4 ndcCoord; + ndcCoord.xy = (2.0 * depthTextureCoord.xy) - 1.0; + ndcCoord.z = (2.0 * terrainDepth.r) - 1.0; + ndcCoord.w = 1.0; + // + // Use the Inverse ModelView Projection Matrix to convert from NDC to CBF + // + vec4 cbfPosition = ndcCoord * czm_modelViewProjectionRelativeToPrimitiveCenterInverseTranspose; + // + // Reverse perspective divide + // + float w = 1.0 / cbfPosition.w; + cbfPosition.xyz *= w; + cbfPosition.w = 1.0; + // + // West is 0.0 and east is 1.0 + // + float tangent = 0.0; + float bitangent = 1.0; + float atan1 = 0.0; + float u = 0.0; + float d1 = dot(westPlane, cbfPosition); + if (d1 != 0.0) + { + float tempFloat = (dot(eastPlane, cbfPosition) / d1) + sinCosDeltas.y; + if (tempFloat != 0.0) + { + tangent = sinCosDeltas.x / tempFloat; + bitangent = 1.0; + } + else + { + tangent = 1.0; + bitangent = 0.0; + } + atan1 = atan(sinCosDeltas.x / tempFloat); + atan1 += (step(atan1, 0.0) + step(d1, 0.0)) * czm_pi; + u = atan1 * centerAzimuthAndInverseDeltas.z; + } + // + // Calculate the az vector for this pixel + // + vec2 azVec = normalize(vec2(westPlane.xy * tangent) + (vec2(westPlane.y, -westPlane.x) * bitangent)); + if ((atan1 >= czm_piOverTwo) && (atan1 <= czm_threePiOver2)) + { + azVec = -azVec; + } + // + // The following commented out code might perform better than the above if statement. It + // is slower on a GeForce6. + // + // float theSign = sign(atan1 - czm_piOverTwo) * sign(atan1 - czm_threePiOver2); + // azVec *= theSign; + // + // Calculate the north and south planes + // + float mrDead = (dot(centerAzimuthAndInverseDeltas.xy, azVec) + 1.0) * 0.5; + vec4 southPlane = vec4(azVec.xy * southPlane.y, southPlane.x, southPlane.z + (mrDead * southPlane.w)); + vec4 northPlane = vec4(azVec.xy * northPlane.y, northPlane.x, northPlane.z + (mrDead * northPlane.w)); + // + // South is 0.0 and north is 1.0 + // + float v = 0.0; + d1 = dot(southPlane, cbfPosition); + if (d1 != 0.0) + { + atan1 = atan(sinCosDeltas.z, (dot(northPlane, cbfPosition) / d1) + sinCosDeltas.w); + atan1 += (step(atan1, 0.0) + step(d1, 0.0)) * czm_pi; + v = atan1 * centerAzimuthAndInverseDeltas.w; + } + + vec2 textureCoordinates = vec2(u, v); + + gl_FragColor = vec4(1.0, 1.0, 0.0, 0.5); + + //float depth = terrainDepth.r; + //gl_FragColor = vec4(vec3(depth), 1.0); + + //gl_FragColor = vec4(textureCoordinates, 0.0, 0.5); + //writeDepthClampedToFarPlane(); +} \ No newline at end of file diff --git a/Source/Shaders/ShadowVolumeVS.glsl b/Source/Shaders/ShadowVolumeVS.glsl new file mode 100644 index 000000000000..f0a0b288640c --- /dev/null +++ b/Source/Shaders/ShadowVolumeVS.glsl @@ -0,0 +1,25 @@ +attribute vec3 positionHigh; +attribute vec3 positionLow; +attribute vec3 normal; + +uniform float centralBodyMinimumAltitude; +uniform float LODNegativeToleranceOverDistance; + +void main() { + vec4 position = czm_translateRelativeToEye(positionHigh, positionLow); + + // + // Make sure the vertex is moved down far enough to cover the central body + // + float delta = min(centralBodyMinimumAltitude, LODNegativeToleranceOverDistance * length(position.xyz)); + + // + // Move vertex down. This is not required if it belongs to a top + // cap or top of the wall, in which case it was already moved up just + // once on the CPU so the normal will be (0, 0, 0). + // + // Moving the vertex down is a function of the view parameters so + // it is done here to avoid buring CPU time. + // + gl_Position = czm_modelViewProjectionRelativeToEye * (position + vec4(normal * delta, 0)); +} \ No newline at end of file From 11770c18a10bbda842815c3d0a9196605ecb79f1 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Tue, 24 Feb 2015 15:29:20 -0500 Subject: [PATCH 02/12] Clamp positions to near plane. Shadow volume sides that are entirely culled by the near plane is a WIP. --- Source/Scene/GroundPolygon.js | 8 +- Source/Scene/PolygonOnTerrain.js | 705 ----------------------------- Source/Scene/Scene.js | 1 + Source/Shaders/ShadowVolumeFS.glsl | 18 +- Source/Shaders/ShadowVolumeVS.glsl | 61 ++- 5 files changed, 79 insertions(+), 714 deletions(-) delete mode 100644 Source/Scene/PolygonOnTerrain.js diff --git a/Source/Scene/GroundPolygon.js b/Source/Scene/GroundPolygon.js index 2f09d4541f62..e29c74d1d4c0 100644 --- a/Source/Scene/GroundPolygon.js +++ b/Source/Scene/GroundPolygon.js @@ -514,7 +514,7 @@ define([ } GroundPolygon.prototype.update = function(context, frameState, commandList) { - // TODO: determin if supported + // TODO: determine if supported if (!defined(context.uniformState.globeDepthTexture)) { return; } @@ -561,8 +561,6 @@ define([ this._stencilPassCommand = new DrawCommand({ primitiveType : PrimitiveType.TRIANGLES, - //offset : this._topCapOffset, - //count : this._topCapCount + this._wallCount, vertexArray : this._va, renderState : stencilPassRenderState, shaderProgram : this._sp, @@ -596,7 +594,7 @@ define([ face : CullFace.BACK }, depthTest : { - enabled : false + enabled : true }, depthMask : false, blending : BlendingState.ALPHA_BLEND @@ -604,8 +602,6 @@ define([ this._colorPassCommand = new DrawCommand({ primitiveType : PrimitiveType.TRIANGLES, - //offset : this._topCapOffset, - //count : this._topCapCount + this._wallCount, vertexArray : this._va, renderState : coloRenderState, shaderProgram : this._sp, diff --git a/Source/Scene/PolygonOnTerrain.js b/Source/Scene/PolygonOnTerrain.js deleted file mode 100644 index 7df673831efd..000000000000 --- a/Source/Scene/PolygonOnTerrain.js +++ /dev/null @@ -1,705 +0,0 @@ -/*global define*/ -define([ - '../Core/BoundingSphere', - '../Core/Cartesian2', - '../Core/Cartesian3', - '../Core/Cartesian4', - '../Core/ComponentDatatype', - '../Core/defaultValue', - '../Core/defined', - '../Core/Ellipsoid', - '../Core/EllipsoidTangentPlane', - '../Core/EncodedCartesian3', - '../Core/IndexDatatype', - '../Core/Math', - '../Core/Matrix4', - '../Core/PolygonGeometryLibrary', - '../Core/PolygonPipeline', - '../Core/PrimitiveType', - '../Core/SphericalExtent', - '../Core/WindingOrder', - '../Renderer/BufferUsage', - '../Renderer/DrawCommand', - '../Shaders/ShadowVolumeFS', - '../Shaders/ShadowVolumeVS', - './BlendingState', - './CullFace', - './DepthFunction', - './Pass', - './StencilFunction', - './StencilOperation' - ], function( - BoundingSphere, - Cartesian2, - Cartesian3, - Cartesian4, - ComponentDatatype, - defaultValue, - defined, - Ellipsoid, - EllipsoidTangentPlane, - EncodedCartesian3, - IndexDatatype, - CesiumMath, - Matrix4, - PolygonGeometryLibrary, - PolygonPipeline, - PrimitiveType, - SphericalExtent, - WindingOrder, - BufferUsage, - DrawCommand, - ShadowVolumeFS, - ShadowVolumeVS, - BlendingState, - CullFace, - DepthFunction, - Pass, - StencilFunction, - StencilOperation) { - "use strict"; - - var PolygonOnTerrain = function(options) { - options = defaultValue(options, defaultValue.EMPTY_OBJECT); - - var ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84); - var granularity = defaultValue(options.granularity, CesiumMath.RADIANS_PER_DEGREE); - var polygonHierarchy = options.polygonHierarchy; - - this._ellipsoid = ellipsoid; - this._granularity = granularity; - this._polygonHierarchy = polygonHierarchy; - - this._boundingSphere = undefined; - - this._northPlane = new Cartesian4(); - this._southPlane = new Cartesian4(); - this._eastPlane = new Cartesian4(); - this._westPlane = new Cartesian4(); - - this._sinCosDeltas = new Cartesian4(); - this._centerAzimuthAndInverseDeltas = new Cartesian4(); - - this._centerAzimuthFromWest = 0.0; - - this._va = undefined; - this._sp = undefined; - this._rs = undefined; - - this._depthTexture = undefined; - - var that = this; - this._uniformMap = { - centralBodyMinimumAltitude : function() { - return -8500.0; - }, - LODNegativeToleranceOverDistance : function() { - return -0.01; - }, - northPlane : function() { - return that._northPlane; - }, - southPlane : function() { - return that._southPlane; - }, - eastPlane : function() { - return that._eastPlane; - }, - westPlane : function() { - return that._westPlane; - }, - sinCosDeltas : function () { - return that._sinCosDeltas; - }, - centerAzimuthAndInverseDeltas : function () { - return that._centerAzimuthAndInverseDeltas; - }, - centerAzimuthFromWest : function () { - return that._centerAzimuthFromWest; - }, - czm_centralBodyDepthTexture : function() { - return that._depthTexture; - } - }; - - this._zFailCommand = undefined; - this._zPassCommand = undefined; - this._colorInsideSphereCommand = undefined; - this._colorOutsideSphereCommand = undefined; - - this._scene = options.scene; - }; - - var attributeLocations = { - positionHigh : 0, - positionLow : 1, - normal : 2 - }; - - function getSurfaceDelta(ellipsoid, granularity) { - var refDistance = ellipsoid.maximumRadius; - return refDistance - (refDistance * Math.cos(granularity / 2.0)); - } - - var scratchPosition = new Cartesian3(); - var scratchNormal = new Cartesian3(); - var scratchDeltaNormal = new Cartesian3(); - - // TODO: More than one level of the polygon hierarchy - function createShadowVolume(polygon, context) { - var polygonHierarchy = polygon._polygonHierarchy; - var ellipsoid = polygon._ellipsoid; - //var granularity = polygon._granularity; - var granularity = CesiumMath.PI_OVER_FOUR; - - var results = PolygonGeometryLibrary.polygonsFromHierarchy(polygonHierarchy); - var positions = results.polygons[0]; - var hierarchy = results.hierarchy[0]; - - var bottomCap = PolygonGeometryLibrary.createGeometryFromPositions(ellipsoid, positions, granularity, false); - var bottomPositions = bottomCap.attributes.position.values; - var numBottomCapVertices = bottomPositions.length / 3; - var numCapVertices = numBottomCapVertices + numBottomCapVertices; - var bottomIndices = bottomCap.indices; - var numBottomIndices = bottomIndices.length; - var numCapIndices = numBottomIndices + numBottomIndices; - - var outerRing = hierarchy.outerRing; - var tangentPlane = EllipsoidTangentPlane.fromPoints(outerRing, ellipsoid); - var positions2D = tangentPlane.projectPointsOntoPlane(outerRing); - - var windingOrder = PolygonPipeline.computeWindingOrder2D(positions2D); - if (windingOrder === WindingOrder.CLOCKWISE) { - outerRing.reverse(); - } - - var wall = PolygonGeometryLibrary.computeWallGeometry(outerRing, ellipsoid, granularity, false); - var walls = [wall]; - var numWallVertices = wall.attributes.position.values.length / 3; - var numWallIndices = wall.indices.length; - - var holes = hierarchy.holes; - var i; - var j; - - for (i = 0; i < holes.length; i++) { - var hole = holes[i]; - - tangentPlane = EllipsoidTangentPlane.fromPoints(hole, ellipsoid); - positions2D = tangentPlane.projectPointsOntoPlane(hole); - - windingOrder = PolygonPipeline.computeWindingOrder2D(positions2D); - if (windingOrder === WindingOrder.COUNTER_CLOCKWISE) { - hole.reverse(); - } - - wall = PolygonGeometryLibrary.computeWallGeometry(hole, ellipsoid, granularity, false); - numWallVertices += wall.attributes.position.values.length / 3; - numWallIndices += wall.indices.length; - walls.push(wall); - } - - var maxAlt = 8500.0; // TODO: get max alt of terrain - var surfaceDelta = getSurfaceDelta(ellipsoid, granularity); - var upDelta = maxAlt + surfaceDelta; - - var numVertices = numCapVertices + numWallVertices; - - var vbPositions = new Float32Array(numVertices * 3 * 2); - var vbNormals = new Float32Array(numVertices * 3); - - var position; - var normal; - var topPosition; - - var index = 0; - var normalIndex = 0; - - for (i = 0; i < numBottomCapVertices * 3; i += 3) { - position = Cartesian3.unpack(bottomPositions, i, scratchPosition); - ellipsoid.scaleToGeodeticSurface(position, position); - normal = ellipsoid.geodeticSurfaceNormal(position, scratchNormal); - - topPosition = Cartesian3.multiplyByScalar(normal, upDelta, scratchDeltaNormal); - Cartesian3.add(position, topPosition, topPosition); - - EncodedCartesian3.writeElements(position, vbPositions, index); - EncodedCartesian3.writeElements(topPosition, vbPositions, index + 6); - index += 12; - - Cartesian3.pack(normal, vbNormals, normalIndex); - Cartesian3.pack(Cartesian3.ZERO, vbNormals, normalIndex + 3); - normalIndex += 6; - } - - var numWalls = walls.length; - var wallPositions; - var wallLength; - - for (i = 0; i < numWalls; ++i) { - wall = walls[i]; - wallPositions = wall.attributes.position.values; - wallLength = wallPositions.length / 2; - - for (j = 0; j < wallLength; j += 3) { - position = Cartesian3.unpack(wallPositions, j, scratchPosition); - ellipsoid.scaleToGeodeticSurface(position, position); - normal = ellipsoid.geodeticSurfaceNormal(position, scratchNormal); - - topPosition = Cartesian3.multiplyByScalar(normal, upDelta, scratchDeltaNormal); - Cartesian3.add(position, topPosition, topPosition); - - EncodedCartesian3.writeElements(topPosition, vbPositions, index); - EncodedCartesian3.writeElements(position, vbPositions, index + wallLength * 2); - index += 6; - - Cartesian3.pack(Cartesian3.ZERO, vbNormals, normalIndex); - Cartesian3.pack(normal, vbNormals, normalIndex + wallLength); - normalIndex += 3; - } - - index += wallLength * 2; - normalIndex += wallLength; - } - - var numIndices = numCapIndices + numWallIndices; - var ibIndices = IndexDatatype.createTypedArray(numVertices, numIndices); - - var i0; - var i1; - var i2; - - index = 0; - for (i = 0; i < numBottomIndices; i += 3) { - i0 = bottomIndices[i] * 2; - i1 = bottomIndices[i + 1] * 2; - i2 = bottomIndices[i + 2] * 2; - - ibIndices[index++] = i2; - ibIndices[index++] = i1; - ibIndices[index++] = i0; - } - - for (i = 0; i < numBottomIndices; i += 3) { - i0 = bottomIndices[i] * 2; - i1 = bottomIndices[i + 1] * 2; - i2 = bottomIndices[i + 2] * 2; - - ibIndices[index++] = i0 + 1; - ibIndices[index++] = i1 + 1; - ibIndices[index++] = i2 + 1; - } - - var offset = numCapVertices; - for (i = 0; i < numWalls; ++i) { - wall = walls[i]; - var wallIndices = wall.indices; - wallLength = wallIndices.length; - - for (j = 0; j < wallLength; ++j) { - ibIndices[index++] = wallIndices[j] + offset; - } - offset += wall.attributes.position.values.length / 3; - } - - var positionBuffer = context.createVertexBuffer(vbPositions, BufferUsage.STATIC_DRAW); - var normalBuffer = context.createVertexBuffer(vbNormals, BufferUsage.STATIC_DRAW); - - var indexDatatype = (ibIndices.BYTES_PER_ELEMENT === 2) ? IndexDatatype.UNSIGNED_SHORT : IndexDatatype.UNSIGNED_INT; - var indexBuffer = context.createIndexBuffer(ibIndices, BufferUsage.STATIC_DRAW, indexDatatype); - - var attributes = [{ - index : attributeLocations.positionHigh, - vertexBuffer : positionBuffer, - componentsPerAttribute : 3, - componentDatatype : ComponentDatatype.FLOAT, - offsetInBytes : 0, - strideInBytes : ComponentDatatype.getSizeInBytes(ComponentDatatype.FLOAT) * 3 * 2 - }, { - index : attributeLocations.positionLow, - vertexBuffer : positionBuffer, - componentsPerAttribute : 3, - componentDatatype : ComponentDatatype.FLOAT, - offsetInBytes : ComponentDatatype.getSizeInBytes(ComponentDatatype.FLOAT) * 3, - strideInBytes : ComponentDatatype.getSizeInBytes(ComponentDatatype.FLOAT) * 3 * 2 - }, { - index : attributeLocations.normal, - vertexBuffer : normalBuffer, - componentsPerAttribute : 3, - componentDatatype : ComponentDatatype.FLOAT - }]; - - polygon._va = context.createVertexArray(attributes, indexBuffer); - - polygon._bottomCapOffset = 0; - polygon._bottomCapCount = numBottomIndices; - polygon._topCapOffset = numBottomIndices; - polygon._topCapCount = numBottomIndices; - polygon._wallOffset = numCapIndices; - polygon._wallCount = numWallIndices; - - polygon._boundingSphere = BoundingSphere.fromPoints(outerRing); - } - - function latitudePlane(north, theta, xy, z, center, plane) { - var normal = scratchNormal; - if (xy !== 0.0) - { - var tempVec0 = Cartesian3.fromElements(Math.cos(theta)* xy, Math.sin(theta) * xy, z); - var tempVec1 = Cartesian3.fromElements(tempVec0.y, -tempVec0.x, 0.0); - if (north) - { - Cartesian3.cross(tempVec0, tempVec1, normal); - } - else - { - Cartesian3.cross(tempVec1, tempVec0, normal); - } - } - else - { - normal.x = center.x; - normal.y = center.y; - normal.z = 0.0; - } - Cartesian3.normalize(normal, normal); - plane.x = normal.x; - plane.y = normal.y; - plane.z = normal.z; - plane.w = Cartesian3.dot(normal, center); - } - - var scratchPlane = new Cartesian4(); - var scratchPlaneRotated = new Cartesian4(); - - function computeTextureCoordinates(polygon) { - var polygonHierarchy = polygon._polygonHierarchy; - var outerRing = polygonHierarchy.positions; - - var center = polygon._boundingSphere.center; - - var sphericalExtent = SphericalExtent.fromPositions(outerRing); - var minimumLatitude = sphericalExtent.minimumLatitude; - var minimumLongitude = sphericalExtent.minimumLongitude; - var latitudeExtent = sphericalExtent.latitudeExtent; - var longitudeExtent = sphericalExtent.longitudeExtent; - - // - // West plane - // - var westPlane = polygon._westPlane; - westPlane.x = -Math.sin(minimumLongitude); - westPlane.y = Math.cos(minimumLongitude); - westPlane.z = 0.0; - westPlane.w = (westPlane.x * center.x) + (westPlane.y * center.y) + (westPlane.z * center.z); - - // - // East plane - // - var eastPlane = polygon._eastPlane; - var tempDouble = minimumLongitude + longitudeExtent; - eastPlane.x = Math.sin(tempDouble); - eastPlane.y = -Math.cos(tempDouble); - eastPlane.z = 0.0; - eastPlane.w = (eastPlane.x * center.x) + (eastPlane.y * center.y) + (eastPlane.z * center.z); - - // - // Sin and cos lon/lat deltas - // - var sinCosDeltas = polygon._sinCosDeltas; - sinCosDeltas.x = Math.sin(longitudeExtent); - sinCosDeltas.y = Math.cos(longitudeExtent); - sinCosDeltas.z = Math.sin(latitudeExtent); - sinCosDeltas.w = Math.cos(latitudeExtent); - - // - // Center azimuth - // - var centerAzimuth = Cartesian2.fromElements(center.x, center.y); - Cartesian2.normalize(centerAzimuth, centerAzimuth); - var centerAzimuthAndInverseDeltas = polygon._centerAzimuthAndInverseDeltas; - centerAzimuthAndInverseDeltas.x = centerAzimuth.x; - centerAzimuthAndInverseDeltas.y = centerAzimuth.y; - - // - // Inverse deltas - // - /* - if (volue is medium or small) - { - // - // When both extents are sufficiently small, the shader approximates arctan and - // tan to be the same; due to differences, the extents must be increased slightly - // - centerAzimuthAndInverseDeltas.z = 1.0 / Math.tan(longitudeExtent); - centerAzimuthAndInverseDeltas.w = 1.0 / Math.tan(latitudeExtent); - var azimuthXY = Cartesian2.fromElements(centerAzimuthAndInverseDeltas.x, centerAzimuthAndInverseDeltas.y); - Cartesian2.normalize(azimuthXY, azimuthXY); - var westXY = Cartesian2.fromElements(westPlane.y, -westPlane.x); - Cartesian2.normalize(westXY, westXY); - polygon._centerAzimuthFromWest = Math.acos(Cartesian2.dot(azimuthXY, westXY)); - } - else - { - */ - centerAzimuthAndInverseDeltas.z = 1.0 / longitudeExtent; - centerAzimuthAndInverseDeltas.w = 1.0 / latitudeExtent; - polygon._centerAzimuthFromWest = 0.0; - //} - - // - // North/South plane - for small volumes the north and south planes are calculated - // once. They are approximations that improve frame rate at the slight cost - // of visual quality. For larger volumes, the pixel shader calculates the - // planes on the fly using data calculated below. This is slower but is - // necessary to preserve visual quality. - // - // North plane - // - var northPlane = polygon._northPlane; - var centerAzimuthAngle = Math.atan2(centerAzimuth.y, centerAzimuth.x); - var z = Math.sin(minimumLatitude + latitudeExtent); - var xy = 1.0 - z * z; - xy = (xy < 0.0) ? 0.0 : Math.sqrt(xy); - - var plane = scratchPlane; - var planeRotated180Degs = scratchPlaneRotated; - latitudePlane(true, centerAzimuthAngle, xy, z, center, plane); - - /* - if (volume is small) - { - northPlane.x = plane.x; - northPlane.y = plane.y; - northPlane.z = plane.z; - northPlane.w = (northPlane.x * center.x) + (northPlane.y * center.y) + (northPlane.z * center.z); - } - else - { - */ - latitudePlane(true, centerAzimuthAngle + Math.PI, xy, z, center, planeRotated180Degs); - xy = 1.0 - plane.z * plane.z; - xy = (xy > 0.0) ? Math.sqrt(xy) : 0.0; - if (minimumLatitude + latitudeExtent < 0.0) - { - xy = -xy; - } - northPlane.x = plane.z; - northPlane.y = xy; - northPlane.z = planeRotated180Degs.w; - northPlane.w = plane.w - planeRotated180Degs.w; - //} - - // - // South plane - // - var southPlane = polygon._southPlane; - z = Math.sin(minimumLatitude); - xy = 1.0 - z * z; - xy = (xy < 0.0) ? 0.0 : Math.sqrt(xy); - latitudePlane(false, centerAzimuthAngle, xy, z, center, plane); - /* - if (volume is small) - { - southPlane.x = plane.x; - southPlane.y = plane.y; - southPlane.z = plane.z; - southPlane.w = (southPlane.x * center.x) + (southPlane.y * center.y) + (southPlane.z * center.z); - } - else - { - */ - latitudePlane(false, centerAzimuthAngle + Math.PI, xy, z, center, planeRotated180Degs); - xy = 1.0 - plane.z * plane.z; - xy = (xy > 0.0) ? Math.sqrt(xy) : 0.0; - if (minimumLatitude > 0.0) - { - xy = -xy; - } - southPlane.x = plane.z; - southPlane.y = xy; - southPlane.z = planeRotated180Degs.w; - southPlane.w = plane.w - planeRotated180Degs.w; - //} - } - - PolygonOnTerrain.prototype.update = function(context, frameState, commandList) { - var fbo = this._scene._oit._opaqueFBO; - if (!defined(fbo)) { - return; - } - - if (!defined(this._va)) { - createShadowVolume(this, context); - computeTextureCoordinates(this); - } - - if (!defined(this._sp)) { - this._sp = context.createShaderProgram(ShadowVolumeVS, ShadowVolumeFS, attributeLocations); - } - - if (!defined(this._depthTexture)) { - this._depthTexture = fbo.depthStencilTexture; - } - - if (!defined(this._zFailCommand)) { - var disableColorWrites = { - red : false, - green : false, - blue : false, - alpha : false - }; - - var zFailRenderState = context.createRenderState({ - colorMask : disableColorWrites, - stencilTest : { - enabled : true, - frontFunction : StencilFunction.ALWAYS, - frontOperation : { - fail : StencilOperation.KEEP, - zFail : StencilOperation.DECREMENT_WRAP, - zPass : StencilOperation.KEEP - }, - backFunction : StencilFunction.ALWAYS, - backOperation : { - fail : StencilOperation.KEEP, - zFail : StencilOperation.INCREMENT_WRAP, - zPass : StencilOperation.KEEP - }, - reference : 0, - mask : ~0 - }, - depthTest : { - enabled : true - }, - depthMask : false - }); - - this._zFailCommand = new DrawCommand({ - primitiveType : PrimitiveType.TRIANGLES, - vertexArray : this._va, - renderState : zFailRenderState, - shaderProgram : this._sp, - uniformMap : this._uniformMap, - boundingVolume : this._boundingSphere, - owner : this, - modelMatrix : Matrix4.IDENTITY, - pass : Pass.OPAQUE - }); - - var zPassRenderState = context.createRenderState({ - colorMask : disableColorWrites, - stencilTest : { - enabled : true, - frontFunction : StencilFunction.ALWAYS, - frontOperation : { - fail : StencilOperation.KEEP, - zFail : StencilOperation.KEEP, - zPass : StencilOperation.INCREMENT_WRAP - }, - backFunction : StencilFunction.ALWAYS, - backOperation : { - fail : StencilOperation.KEEP, - zFail : StencilOperation.KEEP, - zPass : StencilOperation.DECREMENT_WRAP - }, - reference : 0, - mask : ~0 - }, - depthTest : { - enabled : true - }, - depthMask : false - }); - - this._zPassCommand = new DrawCommand({ - primitiveType : PrimitiveType.TRIANGLES, - offset : this._topCapOffset, - count : this._topCapCount + this._wallCount, - vertexArray : this._va, - renderState : zPassRenderState, - shaderProgram : this._sp, - uniformMap : this._uniformMap, - boundingVolume : this._boundingSphere, - owner : this, - modelMatrix : Matrix4.IDENTITY, - pass : Pass.OPAQUE - }); - - var colorStencilTest = { - enabled : true, - frontFunction : StencilFunction.NOT_EQUAL, - frontOperation : { - fail : StencilOperation.KEEP, - zFail : StencilOperation.KEEP, - zPass : StencilOperation.DECREMENT - }, - backFunction : StencilFunction.NOT_EQUAL, - backOperation : { - fail : StencilOperation.KEEP, - zFail : StencilOperation.KEEP, - zPass : StencilOperation.DECREMENT - }, - reference : 0, - mask : ~0 - }; - - var colorInsideSphereRenderState = context.createRenderState({ - stencilTest : colorStencilTest, - depthTest : { - enabled : false - }, - depthMask : false, - blending : BlendingState.ALPHA_BLEND - }); - - this._colorInsideCommand = new DrawCommand({ - primitiveType : PrimitiveType.TRIANGLES, - vertexArray : this._va, - renderState : colorInsideSphereRenderState, - shaderProgram : this._sp, - uniformMap : this._uniformMap, - boundingVolume : this._boundingSphere, - owner : this, - modelMatrix : Matrix4.IDENTITY, - pass : Pass.OPAQUE - }); - - var colorOutsideSphereRenderState = context.createRenderState({ - stencilTest : colorStencilTest, - cull : { - enabled : true, - face : CullFace.BACK - }, - depthTest : { - enabled : false - }, - depthMask : false, - blending : BlendingState.ALPHA_BLEND - }); - - this._colorOutsideSphereCommand = new DrawCommand({ - primitiveType : PrimitiveType.TRIANGLES, - offset : this._topCapOffset, - count : this._topCapCount + this._wallCount, - vertexArray : this._va, - renderState : colorOutsideSphereRenderState, - shaderProgram : this._sp, - uniformMap : this._uniformMap, - boundingVolume : this._boundingSphere, - owner : this, - modelMatrix : Matrix4.IDENTITY, - pass : Pass.OPAQUE - }); - } - - var pass = frameState.passes; - if (pass.render) { - // intersects near/far plane: z-fail else z-pass - // inside bounding sphere : colorInsideSphere commands else color outside - commandList.push(this._zPassCommand, this._colorInsideCommand); - } - }; - - return PolygonOnTerrain; -}); diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index 869add3ace6a..89a43eb7d093 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -238,6 +238,7 @@ define([ this._clearColorCommand = new ClearCommand({ color : new Color(), + stencil : 0.0, owner : this }); this._depthClearCommand = new ClearCommand({ diff --git a/Source/Shaders/ShadowVolumeFS.glsl b/Source/Shaders/ShadowVolumeFS.glsl index a811f982cf1b..ee301c148e50 100644 --- a/Source/Shaders/ShadowVolumeFS.glsl +++ b/Source/Shaders/ShadowVolumeFS.glsl @@ -1,3 +1,7 @@ +#ifdef GL_EXT_frag_depth +#extension GL_EXT_frag_depth : enable +#endif + // // The west longitude plane relative to the center of the mesh // @@ -39,8 +43,18 @@ uniform vec4 northPlane; // uniform vec4 southPlane; +varying float v_z; + +void czm_writeDepthClampedToFarPlane() +{ + // That is really 1/w + //gl_FragDepthEXT = min(v_z * gl_FragCoord.w, 1.0); + //gl_FragDepthEXT = clamp(v_z * gl_FragCoord.w, 0.0, 1.0); +} + void main(void) { + /* // // Get the depth value of the terrain for the current fragment. // Note that this is almost certainly different then the depth @@ -127,6 +141,7 @@ void main(void) } vec2 textureCoordinates = vec2(u, v); + */ gl_FragColor = vec4(1.0, 1.0, 0.0, 0.5); @@ -134,5 +149,6 @@ void main(void) //gl_FragColor = vec4(vec3(depth), 1.0); //gl_FragColor = vec4(textureCoordinates, 0.0, 0.5); - //writeDepthClampedToFarPlane(); + + czm_writeDepthClampedToFarPlane(); } \ No newline at end of file diff --git a/Source/Shaders/ShadowVolumeVS.glsl b/Source/Shaders/ShadowVolumeVS.glsl index f0a0b288640c..9082f1795f4e 100644 --- a/Source/Shaders/ShadowVolumeVS.glsl +++ b/Source/Shaders/ShadowVolumeVS.glsl @@ -5,7 +5,53 @@ attribute vec3 normal; uniform float centralBodyMinimumAltitude; uniform float LODNegativeToleranceOverDistance; -void main() { +varying float v_z; + +vec4 czm_depthClampNearFarPlane(vec4 vertexInClipCoordinates) +{ + v_z = (0.5 * (vertexInClipCoordinates.z / vertexInClipCoordinates.w) + 0.5) * vertexInClipCoordinates.w; + vertexInClipCoordinates.z = max(min(vertexInClipCoordinates.z, vertexInClipCoordinates.w), -vertexInClipCoordinates.w); + return vertexInClipCoordinates; +} + +vec4 clipPointToNearPlane(vec3 p0, vec3 p1) +{ + vec3 p1ToP0 = p1 - p0; + float magnitude = length(p1ToP0); + vec3 direction = normalize(p1ToP0); + float endPoint0Distance = -(czm_entireFrustum.x + p0.z); + float denominator = -direction.z; + + bool culledByNearPlane = false; + + if (endPoint0Distance < 0.0 && abs(denominator) < czm_epsilon7) + { + culledByNearPlane = true; + } + else if (endPoint0Distance < 0.0 && abs(denominator) > czm_epsilon7) + { + // t = (-plane distance - dot(plane normal, ray origin)) / dot(plane normal, ray direction) + float t = (czm_entireFrustum.x + p0.z) / denominator; + if (t < 0.0 || t > magnitude) + { + culledByNearPlane = true; + } + else + { + p0 = p0 + t * direction; + //clipped = true; + } + } + + if (culledByNearPlane) { + //p0.z = min(p0.z, -czm_entireFrustum.x); + } + + return czm_projection * vec4(p0, 1.0); +} + +void main() +{ vec4 position = czm_translateRelativeToEye(positionHigh, positionLow); // @@ -21,5 +67,16 @@ void main() { // Moving the vertex down is a function of the view parameters so // it is done here to avoid buring CPU time. // - gl_Position = czm_modelViewProjectionRelativeToEye * (position + vec4(normal * delta, 0)); + + vec3 eyePosition = (czm_modelViewRelativeToEye * position).xyz; + vec3 movedPosition = (czm_modelViewRelativeToEye * (position + vec4(normal * delta, 0.0))).xyz; + + if (all(equal(normal, vec3(0.0)))) + { + gl_Position = clipPointToNearPlane(eyePosition, movedPosition); + } + else + { + gl_Position = clipPointToNearPlane(movedPosition, eyePosition); + } } \ No newline at end of file From fe5fab10d124eb309bd8589002f089b46679aa6e Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Wed, 18 Mar 2015 16:14:57 -0400 Subject: [PATCH 03/12] Declare commands and properly release shaders. --- Source/Scene/GlobeDepth.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Source/Scene/GlobeDepth.js b/Source/Scene/GlobeDepth.js index d11812411f5a..fb389df53d8b 100644 --- a/Source/Scene/GlobeDepth.js +++ b/Source/Scene/GlobeDepth.js @@ -29,6 +29,10 @@ define([ this.framebuffer = undefined; this._copyDepthFramebuffer = undefined; + this._clearColorCommand = undefined; + this._copyColorCommand = undefined; + this._copyDepthCommand = undefined; + var supported = updateFramebuffers(this, context); if (supported) { updateCopyCommands(this, context); @@ -220,6 +224,10 @@ define([ GlobeDepth.prototype.destroy = function() { destroyTextures(this); destroyFramebuffers(this); + + this._copyColorCommand.shaderProgram = defined(this._copyColorCommand.shaderProgram) && this._copyColorCommand.shaderProgram.destroy(); + this._copyDepthCommand.shaderProgram = defined(this._copyDepthCommand.shaderProgram) && this._copyDepthCommand.shaderProgram.destroy(); + return destroyObject(this); }; From 5d408bff55d8e110619a6ff771141c18c733f45d Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Wed, 18 Mar 2015 16:16:56 -0400 Subject: [PATCH 04/12] Update CHANGES.md. --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index 42b89bf483c9..b51b48266b98 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -13,6 +13,7 @@ Change Log * Fixed an issue with transparency. [#2572](https://github.com/AnalyticalGraphicsInc/cesium/issues/2572) * Fixed improper handling of null values when loading `GeoJSON` data. * Cesium is now tested using Jasmine 2.2.0. +* Removed `Scene.fxaaOrderIndependentTranslucency`. Use `Scene.fxaa` which is now `true` by default. ### 1.7.1 - 2015-03-06 From 4d72baf9bdc095cb7736c1f2efb5e3c1b2dc0f93 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Tue, 24 Mar 2015 14:56:02 -0400 Subject: [PATCH 05/12] Clip polygon to the near plane of the first frustum WIP. Added debug command for the volume and to pause the camera for clipping. --- Source/Scene/GlobeDepth.js | 1 + Source/Scene/GroundPolygon.js | 82 ++++++++++++++++++++++++++++-- Source/Shaders/ShadowVolumeVS.glsl | 41 ++++++++++++++- 3 files changed, 119 insertions(+), 5 deletions(-) diff --git a/Source/Scene/GlobeDepth.js b/Source/Scene/GlobeDepth.js index 444d35c887d5..7b2c040548bd 100644 --- a/Source/Scene/GlobeDepth.js +++ b/Source/Scene/GlobeDepth.js @@ -163,6 +163,7 @@ define([ if (!defined(globeDepth._clearColorCommand)) { globeDepth._clearColorCommand = new ClearCommand({ color : new Color(0.0, 0.0, 0.0, 0.0), + stencil : 0.0, owner : globeDepth }); } diff --git a/Source/Scene/GroundPolygon.js b/Source/Scene/GroundPolygon.js index e29c74d1d4c0..bfa7a2b09394 100644 --- a/Source/Scene/GroundPolygon.js +++ b/Source/Scene/GroundPolygon.js @@ -20,6 +20,7 @@ define([ '../Core/WindingOrder', '../Renderer/BufferUsage', '../Renderer/DrawCommand', + '../Renderer/ShaderSource', '../Shaders/ShadowVolumeFS', '../Shaders/ShadowVolumeVS', './BlendingState', @@ -49,6 +50,7 @@ define([ WindingOrder, BufferUsage, DrawCommand, + ShaderSource, ShadowVolumeFS, ShadowVolumeVS, BlendingState, @@ -86,6 +88,18 @@ define([ this._sp = undefined; this._rs = undefined; + this.debugVolume = defaultValue(options.debugVolume, false); + this._debugVolumeCommand = undefined; + + this.debugPauseCamera = false; + this._debugPauseCamera = this.debugPauseCamera; + + this._debugCameraPosition = new Cartesian3(); + this._debugCameraDirection = new Cartesian3(); + + this._cameraPosition = undefined; + this._cameraDirection = undefined; + var that = this; this._uniformMap = { centralBodyMinimumAltitude : function() { @@ -114,6 +128,12 @@ define([ }, centerAzimuthFromWest : function () { return that._centerAzimuthFromWest; + }, + u_cameraPosition : function() { + return that._cameraPosition; + }, + u_cameraDirection : function() { + return that._cameraDirection; } }; @@ -571,7 +591,7 @@ define([ pass : Pass.OPAQUE }); - var coloRenderState = context.createRenderState({ + var colorRenderState = context.createRenderState({ stencilTest : { enabled : true, frontFunction : StencilFunction.NOT_EQUAL, @@ -603,7 +623,7 @@ define([ this._colorPassCommand = new DrawCommand({ primitiveType : PrimitiveType.TRIANGLES, vertexArray : this._va, - renderState : coloRenderState, + renderState : colorRenderState, shaderProgram : this._sp, uniformMap : this._uniformMap, boundingVolume : this._boundingSphere, @@ -613,9 +633,65 @@ define([ }); } + if (this.debugVolume && !defined(this._debugVolumeCommand)) { + var pauseRS = context.createRenderState({ + cull : { + enabled : false + }, + depthTest : { + enabled : true + }, + depthMask : false, + blending : BlendingState.ALPHA_BLEND + }); + + var pauseVS = new ShaderSource({ + sources : [ShadowVolumeVS], + defined : ['DEBUG_PAUSE'] + }); + var pauseFS = new ShaderSource({ + sources : [ShadowVolumeFS] + }); + var sp = context.createShaderProgram(pauseVS, pauseFS, attributeLocations); + + this._debugVolumeCommand = new DrawCommand({ + primitiveType : PrimitiveType.TRIANGLES, + vertexArray : this._va, + renderState : pauseRS, + shaderProgram : sp, + uniformMap : this._uniformMap, + boundingVolume : this._boundingSphere, + owner : this, + modelMatrix : Matrix4.IDENTITY, + pass : Pass.OPAQUE + }); + } + + var camera = frameState.camera; + + if (this.debugPauseCamera !== this._debugPauseCamera) { + if (this.debugPauseCamera) { + Cartesian3.clone(camera.position, this._debugCameraPosition); + Cartesian3.clone(camera.direction, this._debugCameraDirection); + } + this._debugPauseCamera = this.debugPauseCamera; + } + + if (this.debugPauseCamera) { + this._cameraPosition = this._debugCameraPosition; + this._cameraDirection = this._debugCameraDirection; + } else { + this._cameraPosition = camera.position; + this._cameraDirection = camera.direction; + } + var pass = frameState.passes; if (pass.render) { - commandList.push(this._stencilPassCommand, this._colorPassCommand); + if (this.debugVolume) { + commandList.push(this._debugVolumeCommand); + } else { + commandList.push(this._stencilPassCommand, this._colorPassCommand); + } } }; diff --git a/Source/Shaders/ShadowVolumeVS.glsl b/Source/Shaders/ShadowVolumeVS.glsl index 9082f1795f4e..5a9dd4d965db 100644 --- a/Source/Shaders/ShadowVolumeVS.glsl +++ b/Source/Shaders/ShadowVolumeVS.glsl @@ -14,6 +14,38 @@ vec4 czm_depthClampNearFarPlane(vec4 vertexInClipCoordinates) return vertexInClipCoordinates; } +uniform vec3 u_cameraPosition; +uniform vec3 u_cameraDirection; + +vec4 clipPointToNearPlane(vec3 p0, vec3 p1) +{ + vec3 cameraPosition = u_cameraPosition; + vec3 cameraDirection = -u_cameraDirection; + float near = czm_entireFrustum.x + 1.0; + + //cameraPosition += cameraDirection * near; + + vec3 origin = p0 + cameraPosition; + vec3 diff = p1 + cameraPosition - origin; + vec3 direction = normalize(diff); + float magnitude = length(diff); + + vec3 planeNormal = cameraDirection; + float planeDistance = -dot(planeNormal, cameraPosition); + + float denominator = dot(planeNormal, direction); + + if (abs(denominator) > czm_epsilon6) { + float t = (-planeDistance - dot(planeNormal, origin)) / denominator; + if (t >= 0.0 && t <= magnitude) { + return czm_modelViewProjection * vec4(origin + t * direction, 1.0); + } + } // else segment is parallel to plane (handle culling); + + return czm_modelViewProjectionRelativeToEye * vec4(p0, 1.0); +} + +/* vec4 clipPointToNearPlane(vec3 p0, vec3 p1) { vec3 p1ToP0 = p1 - p0; @@ -49,6 +81,7 @@ vec4 clipPointToNearPlane(vec3 p0, vec3 p1) return czm_projection * vec4(p0, 1.0); } +*/ void main() { @@ -68,8 +101,9 @@ void main() // it is done here to avoid buring CPU time. // - vec3 eyePosition = (czm_modelViewRelativeToEye * position).xyz; - vec3 movedPosition = (czm_modelViewRelativeToEye * (position + vec4(normal * delta, 0.0))).xyz; + /* + vec3 eyePosition = position.xyz; + vec3 movedPosition = eyePosition + normal * delta; if (all(equal(normal, vec3(0.0)))) { @@ -79,4 +113,7 @@ void main() { gl_Position = clipPointToNearPlane(movedPosition, eyePosition); } + */ + + gl_Position = czm_modelViewProjectionRelativeToEye * (position + vec4(normal * delta, 0.0)); } \ No newline at end of file From 36b6772f9146697a86a218cf49b5749c243388d8 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Tue, 24 Mar 2015 17:08:40 -0400 Subject: [PATCH 06/12] More ground polygon WIP. Only extrude vertices in vertex shader. Vertex shader now properly finds intersections with the near plane. --- Source/Scene/GroundPolygon.js | 23 +++++++++++------------ Source/Shaders/ShadowVolumeFS.glsl | 8 ++++---- Source/Shaders/ShadowVolumeVS.glsl | 16 +++++++++------- 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/Source/Scene/GroundPolygon.js b/Source/Scene/GroundPolygon.js index bfa7a2b09394..523ea56c88d8 100644 --- a/Source/Scene/GroundPolygon.js +++ b/Source/Scene/GroundPolygon.js @@ -229,16 +229,14 @@ define([ position = Cartesian3.unpack(bottomPositions, i, scratchPosition); ellipsoid.scaleToGeodeticSurface(position, position); normal = ellipsoid.geodeticSurfaceNormal(position, scratchNormal); - - topPosition = Cartesian3.multiplyByScalar(normal, upDelta, scratchDeltaNormal); - Cartesian3.add(position, topPosition, topPosition); + Cartesian3.multiplyByScalar(normal, upDelta, normal); EncodedCartesian3.writeElements(position, vbPositions, index); - EncodedCartesian3.writeElements(topPosition, vbPositions, index + 6); + EncodedCartesian3.writeElements(position, vbPositions, index + 6); index += 12; - Cartesian3.pack(normal, vbNormals, normalIndex); - Cartesian3.pack(Cartesian3.ZERO, vbNormals, normalIndex + 3); + Cartesian3.pack(Cartesian3.ZERO, vbNormals, normalIndex); + Cartesian3.pack(normal, vbNormals, normalIndex + 3); normalIndex += 6; } @@ -255,16 +253,14 @@ define([ position = Cartesian3.unpack(wallPositions, j, scratchPosition); ellipsoid.scaleToGeodeticSurface(position, position); normal = ellipsoid.geodeticSurfaceNormal(position, scratchNormal); + Cartesian3.multiplyByScalar(normal, upDelta, normal); - topPosition = Cartesian3.multiplyByScalar(normal, upDelta, scratchDeltaNormal); - Cartesian3.add(position, topPosition, topPosition); - - EncodedCartesian3.writeElements(topPosition, vbPositions, index); + EncodedCartesian3.writeElements(position, vbPositions, index); EncodedCartesian3.writeElements(position, vbPositions, index + wallLength * 2); index += 6; - Cartesian3.pack(Cartesian3.ZERO, vbNormals, normalIndex); - Cartesian3.pack(normal, vbNormals, normalIndex + wallLength); + Cartesian3.pack(normal, vbNormals, normalIndex); + Cartesian3.pack(Cartesian3.ZERO, vbNormals, normalIndex + wallLength); normalIndex += 3; } @@ -349,6 +345,9 @@ define([ polygon._wallCount = numWallIndices; polygon._boundingSphere = BoundingSphere.fromPoints(outerRing); + + // TODO: Correct bounding sphere + polygon._boundingSphere.radius = upDelta; } function latitudePlane(north, theta, xy, z, center, plane) { diff --git a/Source/Shaders/ShadowVolumeFS.glsl b/Source/Shaders/ShadowVolumeFS.glsl index ee301c148e50..63304eff84e2 100644 --- a/Source/Shaders/ShadowVolumeFS.glsl +++ b/Source/Shaders/ShadowVolumeFS.glsl @@ -1,6 +1,6 @@ -#ifdef GL_EXT_frag_depth -#extension GL_EXT_frag_depth : enable -#endif +//#ifdef GL_EXT_frag_depth +//#extension GL_EXT_frag_depth : enable +//#endif // // The west longitude plane relative to the center of the mesh @@ -150,5 +150,5 @@ void main(void) //gl_FragColor = vec4(textureCoordinates, 0.0, 0.5); - czm_writeDepthClampedToFarPlane(); + //czm_writeDepthClampedToFarPlane(); } \ No newline at end of file diff --git a/Source/Shaders/ShadowVolumeVS.glsl b/Source/Shaders/ShadowVolumeVS.glsl index 5a9dd4d965db..ea399f9f7010 100644 --- a/Source/Shaders/ShadowVolumeVS.glsl +++ b/Source/Shaders/ShadowVolumeVS.glsl @@ -17,6 +17,7 @@ vec4 czm_depthClampNearFarPlane(vec4 vertexInClipCoordinates) uniform vec3 u_cameraPosition; uniform vec3 u_cameraDirection; +/* vec4 clipPointToNearPlane(vec3 p0, vec3 p1) { vec3 cameraPosition = u_cameraPosition; @@ -38,16 +39,20 @@ vec4 clipPointToNearPlane(vec3 p0, vec3 p1) if (abs(denominator) > czm_epsilon6) { float t = (-planeDistance - dot(planeNormal, origin)) / denominator; if (t >= 0.0 && t <= magnitude) { - return czm_modelViewProjection * vec4(origin + t * direction, 1.0); + //return czm_modelViewProjection * vec4(origin + t * direction, 1.0); + return vec4(vec3(0.0), 1.0); } } // else segment is parallel to plane (handle culling); return czm_modelViewProjectionRelativeToEye * vec4(p0, 1.0); } +*/ -/* vec4 clipPointToNearPlane(vec3 p0, vec3 p1) { + p0 = (czm_modelViewRelativeToEye * vec4(p0, 1.0)).xyz; + p1 = (czm_modelViewRelativeToEye * vec4(p1, 1.0)).xyz; + vec3 p1ToP0 = p1 - p0; float magnitude = length(p1ToP0); vec3 direction = normalize(p1ToP0); @@ -81,7 +86,6 @@ vec4 clipPointToNearPlane(vec3 p0, vec3 p1) return czm_projection * vec4(p0, 1.0); } -*/ void main() { @@ -90,7 +94,7 @@ void main() // // Make sure the vertex is moved down far enough to cover the central body // - float delta = min(centralBodyMinimumAltitude, LODNegativeToleranceOverDistance * length(position.xyz)); + float delta = 1.0;//min(centralBodyMinimumAltitude, LODNegativeToleranceOverDistance * length(position.xyz)); // // Move vertex down. This is not required if it belongs to a top @@ -101,7 +105,6 @@ void main() // it is done here to avoid buring CPU time. // - /* vec3 eyePosition = position.xyz; vec3 movedPosition = eyePosition + normal * delta; @@ -113,7 +116,6 @@ void main() { gl_Position = clipPointToNearPlane(movedPosition, eyePosition); } - */ - gl_Position = czm_modelViewProjectionRelativeToEye * (position + vec4(normal * delta, 0.0)); + //gl_Position = czm_modelViewProjectionRelativeToEye * (position + vec4(normal * delta, 0.0)); } \ No newline at end of file From 0790da03c33f4eefa1b217b2afaae132a2959d0e Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Tue, 24 Mar 2015 17:14:58 -0400 Subject: [PATCH 07/12] Fix blinking polygon by slightly offsetting intersection point from the near plane. --- Source/Shaders/ShadowVolumeVS.glsl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/Shaders/ShadowVolumeVS.glsl b/Source/Shaders/ShadowVolumeVS.glsl index ea399f9f7010..742c682d3fce 100644 --- a/Source/Shaders/ShadowVolumeVS.glsl +++ b/Source/Shaders/ShadowVolumeVS.glsl @@ -75,6 +75,7 @@ vec4 clipPointToNearPlane(vec3 p0, vec3 p1) } else { + t += 0.01; p0 = p0 + t * direction; //clipped = true; } @@ -110,7 +111,8 @@ void main() if (all(equal(normal, vec3(0.0)))) { - gl_Position = clipPointToNearPlane(eyePosition, movedPosition); + //gl_Position = clipPointToNearPlane(eyePosition, movedPosition); + gl_Position = czm_modelViewProjectionRelativeToEye * position; } else { From f0c8e24321d9c6fb80562c5f0801a4893d7d1ffb Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Wed, 25 Mar 2015 14:03:25 -0400 Subject: [PATCH 08/12] Fix multi-frustum artifact on ground polygons volume. --- Source/Scene/GroundPolygon.js | 8 +++++--- Source/Scene/Pass.js | 9 +++++---- Source/Scene/Scene.js | 21 ++++++++++++++++----- Source/Shaders/ShadowVolumeVS.glsl | 11 +++++++---- 4 files changed, 33 insertions(+), 16 deletions(-) diff --git a/Source/Scene/GroundPolygon.js b/Source/Scene/GroundPolygon.js index 523ea56c88d8..64c0a59a129c 100644 --- a/Source/Scene/GroundPolygon.js +++ b/Source/Scene/GroundPolygon.js @@ -228,6 +228,7 @@ define([ for (i = 0; i < numBottomCapVertices * 3; i += 3) { position = Cartesian3.unpack(bottomPositions, i, scratchPosition); ellipsoid.scaleToGeodeticSurface(position, position); + normal = ellipsoid.geodeticSurfaceNormal(position, scratchNormal); Cartesian3.multiplyByScalar(normal, upDelta, normal); @@ -252,6 +253,7 @@ define([ for (j = 0; j < wallLength; j += 3) { position = Cartesian3.unpack(wallPositions, j, scratchPosition); ellipsoid.scaleToGeodeticSurface(position, position); + normal = ellipsoid.geodeticSurfaceNormal(position, scratchNormal); Cartesian3.multiplyByScalar(normal, upDelta, normal); @@ -587,7 +589,7 @@ define([ boundingVolume : this._boundingSphere, owner : this, modelMatrix : Matrix4.IDENTITY, - pass : Pass.OPAQUE + pass : Pass.GROUND }); var colorRenderState = context.createRenderState({ @@ -628,7 +630,7 @@ define([ boundingVolume : this._boundingSphere, owner : this, modelMatrix : Matrix4.IDENTITY, - pass : Pass.OPAQUE + pass : Pass.GROUND }); } @@ -662,7 +664,7 @@ define([ boundingVolume : this._boundingSphere, owner : this, modelMatrix : Matrix4.IDENTITY, - pass : Pass.OPAQUE + pass : Pass.GROUND }); } diff --git a/Source/Scene/Pass.js b/Source/Scene/Pass.js index 4469bbd6c019..60be1a2d6741 100644 --- a/Source/Scene/Pass.js +++ b/Source/Scene/Pass.js @@ -12,13 +12,14 @@ define([ */ var Pass = { GLOBE : 0, - OPAQUE : 1, + GROUND : 1, + OPAQUE : 2, // Commands are executed in order by pass up to the translucent pass. // Translucent geometry needs special handling (sorting/OIT). Overlays // are also special (they're executed last, they're not sorted by frustum). - TRANSLUCENT : 2, - OVERLAY : 3, - NUMBER_OF_PASSES : 4 + TRANSLUCENT : 3, + OVERLAY : 4, + NUMBER_OF_PASSES : 5 }; return freezeObject(Pass); diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index c94100f52a8e..f6186c644850 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -1295,10 +1295,9 @@ define([ frustum.near = frustumCommands.near; frustum.far = frustumCommands.far; - if (index !== 0) { - // Avoid tearing artifacts between adjacent frustums - frustum.near *= 0.99; - } + // Avoid tearing artifacts between adjacent frustums + var overlappedNear = index !== 0 ? frustum.near * 0.99 : frustum.near; + frustum.near = overlappedNear; us.updateFrustum(frustum); clearDepth.execute(context, passState); @@ -1311,9 +1310,21 @@ define([ scene._globeDepth.executeCopyDepth(context, passState); + frustum.near = frustumCommands.near; + us.updateFrustum(frustum); + + commands = frustumCommands.commands[Pass.GROUND]; + length = frustumCommands.indices[Pass.GROUND]; + for (j = 0; j < length; ++j) { + executeCommand(commands[j], scene, context, passState); + } + + frustum.near = overlappedNear; + us.updateFrustum(frustum); + // Execute commands in order by pass up to the translucent pass. // Translucent geometry needs special handling (sorting/OIT). - var startPass = Pass.GLOBE + 1; + var startPass = Pass.GROUND + 1; var endPass = Pass.TRANSLUCENT; for (var pass = startPass; pass < endPass; ++pass) { commands = frustumCommands.commands[pass]; diff --git a/Source/Shaders/ShadowVolumeVS.glsl b/Source/Shaders/ShadowVolumeVS.glsl index 742c682d3fce..cea5f13d21c8 100644 --- a/Source/Shaders/ShadowVolumeVS.glsl +++ b/Source/Shaders/ShadowVolumeVS.glsl @@ -39,8 +39,7 @@ vec4 clipPointToNearPlane(vec3 p0, vec3 p1) if (abs(denominator) > czm_epsilon6) { float t = (-planeDistance - dot(planeNormal, origin)) / denominator; if (t >= 0.0 && t <= magnitude) { - //return czm_modelViewProjection * vec4(origin + t * direction, 1.0); - return vec4(vec3(0.0), 1.0); + return czm_modelViewProjection * vec4(origin + t * direction, 1.0); } } // else segment is parallel to plane (handle culling); @@ -75,7 +74,7 @@ vec4 clipPointToNearPlane(vec3 p0, vec3 p1) } else { - t += 0.01; + t += 0.001; p0 = p0 + t * direction; //clipped = true; } @@ -112,7 +111,11 @@ void main() if (all(equal(normal, vec3(0.0)))) { //gl_Position = clipPointToNearPlane(eyePosition, movedPosition); - gl_Position = czm_modelViewProjectionRelativeToEye * position; + //gl_Position = czm_modelViewProjectionRelativeToEye * position; + + eyePosition = (czm_modelViewRelativeToEye * position).xyz; + eyePosition.z = min(eyePosition.z, -czm_entireFrustum.x); + gl_Position = czm_projection * vec4(eyePosition, 1.0); } else { From 94e13362a78b61bd5f374ab49b3e55efb7f6274f Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Wed, 25 Mar 2015 15:39:13 -0400 Subject: [PATCH 09/12] Clean up intersection function to make it more clear and add comments. --- Source/Shaders/ShadowVolumeFS.glsl | 10 ++++----- Source/Shaders/ShadowVolumeVS.glsl | 36 +++++++++++++++++++----------- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/Source/Shaders/ShadowVolumeFS.glsl b/Source/Shaders/ShadowVolumeFS.glsl index 63304eff84e2..e9124a70ae42 100644 --- a/Source/Shaders/ShadowVolumeFS.glsl +++ b/Source/Shaders/ShadowVolumeFS.glsl @@ -1,6 +1,6 @@ -//#ifdef GL_EXT_frag_depth -//#extension GL_EXT_frag_depth : enable -//#endif +#ifdef GL_EXT_frag_depth +#extension GL_EXT_frag_depth : enable +#endif // // The west longitude plane relative to the center of the mesh @@ -48,7 +48,7 @@ varying float v_z; void czm_writeDepthClampedToFarPlane() { // That is really 1/w - //gl_FragDepthEXT = min(v_z * gl_FragCoord.w, 1.0); + gl_FragDepthEXT = min(v_z * gl_FragCoord.w, 1.0); //gl_FragDepthEXT = clamp(v_z * gl_FragCoord.w, 0.0, 1.0); } @@ -150,5 +150,5 @@ void main(void) //gl_FragColor = vec4(textureCoordinates, 0.0, 0.5); - //czm_writeDepthClampedToFarPlane(); + czm_writeDepthClampedToFarPlane(); } \ No newline at end of file diff --git a/Source/Shaders/ShadowVolumeVS.glsl b/Source/Shaders/ShadowVolumeVS.glsl index cea5f13d21c8..86d99fc268b2 100644 --- a/Source/Shaders/ShadowVolumeVS.glsl +++ b/Source/Shaders/ShadowVolumeVS.glsl @@ -10,7 +10,7 @@ varying float v_z; vec4 czm_depthClampNearFarPlane(vec4 vertexInClipCoordinates) { v_z = (0.5 * (vertexInClipCoordinates.z / vertexInClipCoordinates.w) + 0.5) * vertexInClipCoordinates.w; - vertexInClipCoordinates.z = max(min(vertexInClipCoordinates.z, vertexInClipCoordinates.w), -vertexInClipCoordinates.w); + vertexInClipCoordinates.z = min(vertexInClipCoordinates.z, vertexInClipCoordinates.w); return vertexInClipCoordinates; } @@ -52,36 +52,43 @@ vec4 clipPointToNearPlane(vec3 p0, vec3 p1) p0 = (czm_modelViewRelativeToEye * vec4(p0, 1.0)).xyz; p1 = (czm_modelViewRelativeToEye * vec4(p1, 1.0)).xyz; - vec3 p1ToP0 = p1 - p0; - float magnitude = length(p1ToP0); - vec3 direction = normalize(p1ToP0); - float endPoint0Distance = -(czm_entireFrustum.x + p0.z); + vec3 diff = p1 - p0; + float magnitude = length(diff); + vec3 direction = normalize(diff); float denominator = -direction.z; + float near = czm_entireFrustum.x; + bool behindPlane = -(near + p0.z) < 0.0; bool culledByNearPlane = false; - if (endPoint0Distance < 0.0 && abs(denominator) < czm_epsilon7) + if (behindPlane && abs(denominator) < czm_epsilon7) { + // point is behind and parallel to the near plane culledByNearPlane = true; } - else if (endPoint0Distance < 0.0 && abs(denominator) > czm_epsilon7) + else if (behindPlane && abs(denominator) > czm_epsilon7) { + // find intersection of ray and near plane // t = (-plane distance - dot(plane normal, ray origin)) / dot(plane normal, ray direction) - float t = (czm_entireFrustum.x + p0.z) / denominator; + float t = (near + p0.z) / denominator; if (t < 0.0 || t > magnitude) { + // entire segment is behind the near plane culledByNearPlane = true; } else { + // compute intersection with plane slightly offset + // to prevent precision artifacts t += 0.001; p0 = p0 + t * direction; - //clipped = true; } } if (culledByNearPlane) { - //p0.z = min(p0.z, -czm_entireFrustum.x); + // the segment is behind the near plane. push to near plane and + // slightly offset to prevent precision artifacts + p0.z = min(p0.z, -(near + 0.001)); } return czm_projection * vec4(p0, 1.0); @@ -114,12 +121,15 @@ void main() //gl_Position = czm_modelViewProjectionRelativeToEye * position; eyePosition = (czm_modelViewRelativeToEye * position).xyz; - eyePosition.z = min(eyePosition.z, -czm_entireFrustum.x); - gl_Position = czm_projection * vec4(eyePosition, 1.0); + //eyePosition.z = min(eyePosition.z, -czm_entireFrustum.x); + //eyePosition.z = max(eyePosition.z, -czm_currentFrustum.y); + //gl_Position = czm_projection * vec4(eyePosition, 1.0); + gl_Position = czm_depthClampNearFarPlane(czm_projection * vec4(eyePosition, 1.0)); } else { - gl_Position = clipPointToNearPlane(movedPosition, eyePosition); + //gl_Position = clipPointToNearPlane(movedPosition, eyePosition); + gl_Position = czm_depthClampNearFarPlane(clipPointToNearPlane(movedPosition, eyePosition)); } //gl_Position = czm_modelViewProjectionRelativeToEye * (position + vec4(normal * delta, 0.0)); From 3a96b6160da361b0f9f5649a39b521b5325f8c3f Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Fri, 27 Mar 2015 13:55:33 -0400 Subject: [PATCH 10/12] Some shader clean up and fixing a volume artifact WIP. --- Source/Shaders/ShadowVolumeFS.glsl | 2 +- Source/Shaders/ShadowVolumeVS.glsl | 94 +++++++++++------------------- 2 files changed, 35 insertions(+), 61 deletions(-) diff --git a/Source/Shaders/ShadowVolumeFS.glsl b/Source/Shaders/ShadowVolumeFS.glsl index e9124a70ae42..ee301c148e50 100644 --- a/Source/Shaders/ShadowVolumeFS.glsl +++ b/Source/Shaders/ShadowVolumeFS.glsl @@ -48,7 +48,7 @@ varying float v_z; void czm_writeDepthClampedToFarPlane() { // That is really 1/w - gl_FragDepthEXT = min(v_z * gl_FragCoord.w, 1.0); + //gl_FragDepthEXT = min(v_z * gl_FragCoord.w, 1.0); //gl_FragDepthEXT = clamp(v_z * gl_FragCoord.w, 0.0, 1.0); } diff --git a/Source/Shaders/ShadowVolumeVS.glsl b/Source/Shaders/ShadowVolumeVS.glsl index 86d99fc268b2..53a440ec5750 100644 --- a/Source/Shaders/ShadowVolumeVS.glsl +++ b/Source/Shaders/ShadowVolumeVS.glsl @@ -9,46 +9,17 @@ varying float v_z; vec4 czm_depthClampNearFarPlane(vec4 vertexInClipCoordinates) { - v_z = (0.5 * (vertexInClipCoordinates.z / vertexInClipCoordinates.w) + 0.5) * vertexInClipCoordinates.w; - vertexInClipCoordinates.z = min(vertexInClipCoordinates.z, vertexInClipCoordinates.w); + //v_z = (0.5 * (vertexInClipCoordinates.z / vertexInClipCoordinates.w) + 0.5) * vertexInClipCoordinates.w; + //vertexInClipCoordinates.z = min(vertexInClipCoordinates.z, vertexInClipCoordinates.w); return vertexInClipCoordinates; } -uniform vec3 u_cameraPosition; -uniform vec3 u_cameraDirection; - -/* -vec4 clipPointToNearPlane(vec3 p0, vec3 p1) +vec4 clipPointToPlane(vec3 p0, vec3 p1, bool nearPlane) { - vec3 cameraPosition = u_cameraPosition; - vec3 cameraDirection = -u_cameraDirection; - float near = czm_entireFrustum.x + 1.0; - - //cameraPosition += cameraDirection * near; - - vec3 origin = p0 + cameraPosition; - vec3 diff = p1 + cameraPosition - origin; - vec3 direction = normalize(diff); - float magnitude = length(diff); - - vec3 planeNormal = cameraDirection; - float planeDistance = -dot(planeNormal, cameraPosition); - - float denominator = dot(planeNormal, direction); - - if (abs(denominator) > czm_epsilon6) { - float t = (-planeDistance - dot(planeNormal, origin)) / denominator; - if (t >= 0.0 && t <= magnitude) { - return czm_modelViewProjection * vec4(origin + t * direction, 1.0); - } - } // else segment is parallel to plane (handle culling); + float planeDistance = nearPlane ? czm_entireFrustum.x : czm_entireFrustum.y; + //float offset = nearPlane ? 0.001 : -0.001; + float offset = 0.001; - return czm_modelViewProjectionRelativeToEye * vec4(p0, 1.0); -} -*/ - -vec4 clipPointToNearPlane(vec3 p0, vec3 p1) -{ p0 = (czm_modelViewRelativeToEye * vec4(p0, 1.0)).xyz; p1 = (czm_modelViewRelativeToEye * vec4(p1, 1.0)).xyz; @@ -56,39 +27,39 @@ vec4 clipPointToNearPlane(vec3 p0, vec3 p1) float magnitude = length(diff); vec3 direction = normalize(diff); float denominator = -direction.z; - float near = czm_entireFrustum.x; - bool behindPlane = -(near + p0.z) < 0.0; + float pointDistance = -(planeDistance + p0.z); + bool behindPlane = nearPlane ? pointDistance < 0.0 : pointDistance > 0.0; - bool culledByNearPlane = false; + bool culledByPlane = false; if (behindPlane && abs(denominator) < czm_epsilon7) { - // point is behind and parallel to the near plane - culledByNearPlane = true; + // point is behind and parallel to the plane + culledByPlane = true; } else if (behindPlane && abs(denominator) > czm_epsilon7) { - // find intersection of ray and near plane - // t = (-plane distance - dot(plane normal, ray origin)) / dot(plane normal, ray direction) - float t = (near + p0.z) / denominator; + // find intersection of ray and the plane + // t = (-dot(plane normal, point on plane) - dot(plane normal, ray origin)) / dot(plane normal, ray direction) + float t = (planeDistance + p0.z) / denominator; if (t < 0.0 || t > magnitude) { - // entire segment is behind the near plane - culledByNearPlane = true; + // entire segment is behind the plane + culledByPlane = true; } else { // compute intersection with plane slightly offset // to prevent precision artifacts - t += 0.001; + t += offset; p0 = p0 + t * direction; } } - if (culledByNearPlane) { - // the segment is behind the near plane. push to near plane and + if (culledByPlane) { + // the segment is behind the plane. push to plane and // slightly offset to prevent precision artifacts - p0.z = min(p0.z, -(near + 0.001)); + //p0.z = min(p0.z, -(planeDistance + offset)); } return czm_projection * vec4(p0, 1.0); @@ -113,24 +84,27 @@ void main() // vec3 eyePosition = position.xyz; - vec3 movedPosition = eyePosition + normal * delta; + vec3 movedPosition = position.xyz + normal * delta; if (all(equal(normal, vec3(0.0)))) { - //gl_Position = clipPointToNearPlane(eyePosition, movedPosition); - //gl_Position = czm_modelViewProjectionRelativeToEye * position; + //gl_Position = clipPointToPlane(eyePosition, movedPosition, false); + gl_Position = czm_depthClampNearFarPlane(clipPointToPlane(eyePosition, movedPosition, false)); + //gl_Position = czm_depthClampNearFarPlane(clipPointToPlane(movedPosition, eyePosition, false)); + + //gl_Position = czm_modelViewProjectionRelativeToEye * (position + vec4(normal * delta, 0.0)); + //gl_Position = czm_depthClampNearFarPlane(czm_modelViewProjectionRelativeToEye * (position + vec4(normal * delta, 0.0))); - eyePosition = (czm_modelViewRelativeToEye * position).xyz; - //eyePosition.z = min(eyePosition.z, -czm_entireFrustum.x); - //eyePosition.z = max(eyePosition.z, -czm_currentFrustum.y); + //eyePosition = (czm_modelViewRelativeToEye * vec4(eyePosition, 1.0)).xyz; + //eyePosition.z = max(eyePosition.z, -(czm_currentFrustum.y + 0.001)); //gl_Position = czm_projection * vec4(eyePosition, 1.0); - gl_Position = czm_depthClampNearFarPlane(czm_projection * vec4(eyePosition, 1.0)); } else { //gl_Position = clipPointToNearPlane(movedPosition, eyePosition); - gl_Position = czm_depthClampNearFarPlane(clipPointToNearPlane(movedPosition, eyePosition)); + gl_Position = czm_depthClampNearFarPlane(clipPointToPlane(movedPosition, eyePosition, true)); + + //gl_Position = czm_modelViewProjectionRelativeToEye * (position + vec4(normal * delta, 0.0)); + //gl_Position = czm_depthClampNearFarPlane(czm_modelViewProjectionRelativeToEye * (position + vec4(normal * delta, 0.0))); } - - //gl_Position = czm_modelViewProjectionRelativeToEye * (position + vec4(normal * delta, 0.0)); -} \ No newline at end of file +} From 60ab05398917bf3150ebd8d41d9bf58e9c2e781c Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Wed, 1 Apr 2015 14:59:33 -0400 Subject: [PATCH 11/12] Remove ground polygon texture coordinates. --- Source/Core/SphericalExtent.js | 289 --------------------------- Source/Renderer/AutomaticUniforms.js | 21 -- Source/Renderer/Context.js | 1 - Source/Renderer/UniformState.js | 75 ------- Source/Scene/GroundPolygon.js | 271 +------------------------ Source/Shaders/ShadowVolumeFS.glsl | 136 ------------- Source/Shaders/ShadowVolumeVS.glsl | 27 +-- 7 files changed, 2 insertions(+), 818 deletions(-) delete mode 100644 Source/Core/SphericalExtent.js diff --git a/Source/Core/SphericalExtent.js b/Source/Core/SphericalExtent.js deleted file mode 100644 index 501768804f0a..000000000000 --- a/Source/Core/SphericalExtent.js +++ /dev/null @@ -1,289 +0,0 @@ -/*global define*/ -define([ - './Cartesian2', - './defaultValue', - './defined', - './DeveloperError', - './Math' - ], function( - Cartesian2, - defaultValue, - defined, - DeveloperError, - CesiumMath) { - "use strict"; - - var SphericalExtent = function() { - this.minimumLatitude = undefined; - this.minimumLongitude = undefined; - this.latitudeExtent = undefined; - this.longitudeExtent = undefined; - }; - - function longitude(extent, v0, v1) { - var lessThan180Degs = extent.lessThan180Degs; - var eastNormal = extent.eastNormal; - var westNormal = extent.westNormal; - var east = extent.east; - var west = extent.west; - - // - // Longitude - // - // If the point is inside the extent, do nothing - // - var tempVec = Cartesian2.clone(v1); - var dotEast = Cartesian2.dot(eastNormal, tempVec); - var dotWest = Cartesian2.dot(westNormal, tempVec); - if (lessThan180Degs) - { - if (dotEast >= -CesiumMath.EPSILON5 && dotWest >= -CesiumMath.EPSILON5) - { - return false; - } - } - else if (!(dotEast < CesiumMath.EPSILON5 && dotWest < CesiumMath.EPSILON5)) - { - return false; - } - - // - // Determine if the line is going more east or more west and assign - // the approriate east or west vector - // - var eastDir = Cartesian2.fromElements(-v0.y, v0.x); - Cartesian2.normalize(eastDir, eastDir); - var diff = Cartesian2.subtract(v1, v0, new Cartesian2()); - var dot = Cartesian2.dot(eastDir, diff); - if (-CesiumMath.EPSILON5 <= dot && dot <= CesiumMath.EPSILON5) - { - // - // It is not obvious if the line is more east or west, so ignore this line - // - extent.previousVertexProcessed = false; - return true; - } - if (dot > CesiumMath.EPSILON5) - { - Cartesian2.clone(v1, east); - Cartesian2.normalize(east, east); - Cartesian2.fromElements(east.y, -east.x, eastNormal); - } - else // if (dot < -CesiumMath.EPSILON5) - { - Cartesian2.clone(v1, west); - Cartesian2.normalize(west, west); - Cartesian2.fromElements(-west.y, west.x, westNormal); - } - - // - // Determine if the extents are less than or equal to 180 degs - // - var crossZ = (east.x * west.y) - (east.y * west.x); - if (!(-CesiumMath.EPSILON5 < crossZ && crossZ < CesiumMath.EPSILON5)) - { - extent.lessThanEqual180Degs = crossZ < 0.0; - } - else - { - Cartesian2.fromElements(-east.y, east.x, eastDir); - diff = Cartesian2.subtract(west, east, diff); - extent.lessThanEqual180Degs = Cartesian2.dot(eastDir, diff) < 0.0; - } - return true; - } - - function initialize(extent, v0, v1) { - // - // This code expects v0 and v1 to be connected - // - // Latitude - // - var zOverXY = v0.z >= 0.0 ? 1.0 : -1.0; - var xYMagSquared = Cartesian2.magnitudeSquared(v0); - if (xYMagSquared !== 0.0) - { - zOverXY *= (v0.z * v0.z) / xYMagSquared; - } - else - { - zOverXY *= Number.MAX_VALUE; - } - extent.minZOverXY = zOverXY; - extent.maxZOverXY = zOverXY; - zOverXY = v1.z >= 0.0 ? 1.0 : -1.0; - xYMagSquared = Cartesian2.magnitudeSquared(v1); - if (xYMagSquared !== 0.0) - { - zOverXY *= (v1.z * v1.z) / xYMagSquared; - } - else - { - zOverXY *= Number.MAX_VALUE; - } - if (zOverXY < extent.minZOverXY) - { - extent.minZOverXY = zOverXY; - } - else if (zOverXY > extent.maxZOverXY) - { - extent.maxZOverXY = zOverXY; - } - - var east = extent.east; - var west = extent.west; - - var eastNormal = extent.eastNormal; - var westNormal = extent.westNormal; - - // - // Longitude - // - // Determine if the line is going more east or more west and assign - // the default east and west vectors; it does not matter if the dot - // product is zero - // - var eastDir = Cartesian2.fromElements(-v0.y, v0.x); - Cartesian2.normalize(eastDir, eastDir); - var diff = Cartesian2.subtract(v1, v0, new Cartesian2()); - if (Cartesian2.dot(eastDir, diff) > 0.0) - { - Cartesian2.clone(v1, east); - Cartesian2.clone(v0, west); - } - else - { - Cartesian2.clone(v0, east); - Cartesian2.clone(v1, west); - } - Cartesian2.normalize(east, east); - Cartesian2.normalize(west, west); - Cartesian2.fromElements(east.y, -east.x, eastNormal); - Cartesian2.fromElements(-west.y, west.x, westNormal); - - // - // The first points cannot be separate by more that 180 degs - // - extent.lessThanEqual180Degs = true; - extent.previousVertexProcessed = true; - } - - function expand(extent, v0, v1) { - // - // This code expects v0 and v1 to be connected - // - // Latitude - // - var zOverXY = v1.z >= 0.0 ? 1.0 : -1.0; - var xYMagSquared = Cartesian2.magnitudeSquared(v1); - if (xYMagSquared !== 0.0) - { - zOverXY *= (v1.z * v1.z) / xYMagSquared; - } - else - { - zOverXY *= Number.MAX_VALUE; - } - if (zOverXY < extent._minZOverXY) - { - extent._minZOverXY = zOverXY; - } - else if (zOverXY > extent._maxZOverXY) - { - extent._maxZOverXY = zOverXY; - } - - // - // Longitude - // - if (!extent._previousVertexProcessed) - { - extent._previousVertexProcessed = true; - if (!longitude(extent, v1, v0) || !extent._previousVertexProcessed) - { - return; - } - } - longitude(extent, v0, v1); - } - - var state = { - minZOverXY : 0.0, - maxZOverXY : 0.0, - east : new Cartesian2(), - west : new Cartesian2(), - eastNormal : new Cartesian2(), - westNormal : new Cartesian2(), - lessThanEqual180Degs : true, - previousVertexProcessed : true - }; - - SphericalExtent.fromPositions = function(positions, result) { - if (!defined(result)) { - result = new SphericalExtent(); - } - - initialize(state, positions[0], positions[1]); - - var length = positions.length; - for (var i = 1; i < length; ++i) { - expand(state, positions[i], positions[(i + 1) % length]); - } - - // - // Latitude - // - var minLat; - if (state.minZOverXY === Number.MAX_VALUE) - { - minLat = CesiumMath.PI_OVER_TWO; - } - else if (state.minZOverXY === -Number.MAX_VALUE) - { - minLat = -CesiumMath.PI_OVER_TWO; - } - else if (state.minZOverXY > 0.0) - { - minLat = Math.atan(Math.sqrt(state.minZOverXY)); - } - else - { - minLat = -Math.atan(Math.sqrt(-state.minZOverXY)); - } - var maxLat; - if (state.maxZOverXY === Number.MAX_VALUE) - { - maxLat = CesiumMath.PI_OVER_TWO; - } - else if (state.maxZOverXY === -Number.MAX_VALUE) - { - maxLat = -CesiumMath.PI_OVER_TWO; - } - else if (state.maxZOverXY > 0.0) - { - maxLat = Math.atan(Math.sqrt(state.maxZOverXY)); - } - else - { - maxLat = -Math.atan(Math.sqrt(-state.maxZOverXY)); - } - - result.minimumLatitude = minLat; - result.latitudeExtent = maxLat - minLat; - - // - // Longitude - // - var west = state.west; - var east = state.east; - - var westAzimuth = Math.atan2(west.y, west.x); - result.minimumLongitude = westAzimuth; - var eastAzimuth = Math.atan2(east.y, east.x); - result.longitudeExtent = eastAzimuth >= westAzimuth ? eastAzimuth - westAzimuth : eastAzimuth - westAzimuth + CesiumMath.TWO_PI; - - return result; - }; - - return SphericalExtent; -}); diff --git a/Source/Renderer/AutomaticUniforms.js b/Source/Renderer/AutomaticUniforms.js index 4b426d1e4d16..519f8d1582ed 100644 --- a/Source/Renderer/AutomaticUniforms.js +++ b/Source/Renderer/AutomaticUniforms.js @@ -653,27 +653,6 @@ define([ } }), - /** - * An automatic GLSL uniform representing a 4x4 model-view-projection transformation matrix that - * transforms model coordinates, relative to the primitive center, to eye coordinates. - * - * @alias czm_modelViewProjectionRelativeToPrimitiveCenterInverseTranspose - * @glslUniform - * - * @example - * // GLSL declaration - * uniform mat4 czm_modelViewProjectionRelativeToPrimitiveCenterInverseTranspose; - * - * @see czm_modelViewProjectionRelativeToEye - */ - czm_modelViewProjectionRelativeToPrimitiveCenterInverseTranspose : new AutomaticUniform({ - size : 1, - datatype : WebGLRenderingContext.FLOAT_MAT4, - getValue : function(uniformState) { - return uniformState.modelViewProjectionRelativeToPrimitiveCenterInverseTranspose; - } - }), - /** * An automatic GLSL uniform representing a 4x4 transformation matrix that * transforms from eye coordinates to model coordinates. diff --git a/Source/Renderer/Context.js b/Source/Renderer/Context.js index f6433b432a68..4a861c00ffd1 100644 --- a/Source/Renderer/Context.js +++ b/Source/Renderer/Context.js @@ -1831,7 +1831,6 @@ define([ //>>includeEnd('debug'); context._us.model = defaultValue(drawCommand.modelMatrix, Matrix4.IDENTITY); - context._us.boundingVolume = drawCommand.boundingVolume; var sp = defaultValue(shaderProgram, drawCommand.shaderProgram); sp._setUniforms(drawCommand.uniformMap, context._us, context.validateShaderProgram); diff --git a/Source/Renderer/UniformState.js b/Source/Renderer/UniformState.js index 3108b8f8e27a..11ee192249f1 100644 --- a/Source/Renderer/UniformState.js +++ b/Source/Renderer/UniformState.js @@ -46,8 +46,6 @@ define([ this._viewportOrthographicMatrix = Matrix4.clone(Matrix4.IDENTITY); this._viewportTransformation = Matrix4.clone(Matrix4.IDENTITY); - this._boundingVolume = undefined; - this._model = Matrix4.clone(Matrix4.IDENTITY); this._view = Matrix4.clone(Matrix4.IDENTITY); this._inverseView = Matrix4.clone(Matrix4.IDENTITY); @@ -93,9 +91,6 @@ define([ this._modelViewRelativeToEyeDirty = true; this._modelViewRelativeToEye = new Matrix4(); - this._modelViewProjectionRelativeToPrimitiveCenterInverseTransposeDirty = true; - this._modelViewProjectionRelativeToPrimitiveCenterInverseTranspose = new Matrix4(); - this._inverseModelViewDirty = true; this._inverseModelView = new Matrix4(); @@ -211,21 +206,6 @@ define([ } }, - /** - * @memberof UniformState.prototype - * @type {BoundingSphere|undefined} - */ - boundingVolume : { - get : function() { - return this._boundingVolume; - }, - set : function(volume) { - this._boundingVolume = volume; - - this._modelViewProjectionRelativeToPrimitiveCenterInverseTransposeDirty = true; - } - }, - /** * @memberof UniformState.prototype * @type {Matrix4} @@ -250,7 +230,6 @@ define([ this._modelViewProjectionDirty = true; this._inverseModelViewProjectionDirty = true; this._modelViewProjectionRelativeToEyeDirty = true; - this._modelViewProjectionRelativeToPrimitiveCenterInverseTransposeDirty = true; this._modelViewInfiniteProjectionDirty = true; this._normalDirty = true; this._inverseNormalDirty = true; @@ -561,19 +540,6 @@ define([ } }, - /** - * Model-view-projection relative to the primitive center inverse transpose matrix. - * - * @memberof UniformState.prototype - * @type {Matrix4} - */ - modelViewProjectionRelativeToPrimitiveCenterInverseTranspose : { - get : function() { - cleanModelViewProjectionRelativeToPrimitiveCenterInverseTranspose(this); - return this._modelViewProjectionRelativeToPrimitiveCenterInverseTranspose; - } - }, - /** * @memberof UniformState.prototype * @type {Matrix4} @@ -802,7 +768,6 @@ define([ uniformState._viewProjectionDirty = true; uniformState._modelViewProjectionDirty = true; uniformState._modelViewProjectionRelativeToEyeDirty = true; - uniformState._modelViewProjectionRelativeToPrimitiveCenterInverseTransposeDirty = true; uniformState._modelViewInfiniteProjectionDirty = true; uniformState._normalDirty = true; uniformState._inverseNormalDirty = true; @@ -823,7 +788,6 @@ define([ uniformState._viewProjectionDirty = true; uniformState._modelViewProjectionDirty = true; uniformState._modelViewProjectionRelativeToEyeDirty = true; - uniformState._modelViewProjectionRelativeToPrimitiveCenterInverseTransposeDirty = true; } function setInfiniteProjection(uniformState, matrix) { @@ -1049,45 +1013,6 @@ define([ } } - function cleanModelViewProjectionRelativeToPrimitiveCenterInverseTranspose(uniformState) { - if (uniformState._modelViewProjectionRelativeToPrimitiveCenterInverseTransposeDirty) { - uniformState._modelViewProjectionRelativeToPrimitiveCenterInverseTransposeDirty = false; - - var mvpRTP = uniformState._modelViewProjectionRelativeToPrimitiveCenterInverseTranspose; - var boundingVolume = uniformState._boundingVolume; - if (!defined(boundingVolume)) { - Matrix4.clone(uniformState.inverseModelViewProjection, mvpRTP); - Matrix4.transpose(mvpRTP, mvpRTP); - } else { - var mv = uniformState.modelView; - var center = boundingVolume.center; - - center = Matrix4.multiplyByPoint(mv, center, new Cartesian3()); - - mvpRTP[0] = mv[0]; - mvpRTP[1] = mv[1]; - mvpRTP[2] = mv[2]; - mvpRTP[3] = mv[3]; - mvpRTP[4] = mv[4]; - mvpRTP[5] = mv[5]; - mvpRTP[6] = mv[6]; - mvpRTP[7] = mv[7]; - mvpRTP[8] = mv[8]; - mvpRTP[9] = mv[9]; - mvpRTP[10] = mv[10]; - mvpRTP[11] = mv[11]; - mvpRTP[12] = center.x; - mvpRTP[13] = center.y; - mvpRTP[14] = center.z; - mvpRTP[15] = mv[15]; - - Matrix4.multiply(uniformState.projection, mvpRTP, mvpRTP); - Matrix4.inverse(mvpRTP, mvpRTP); - Matrix4.transpose(mvpRTP, mvpRTP); - } - } - } - function cleanModelViewInfiniteProjection(uniformState) { if (uniformState._modelViewInfiniteProjectionDirty) { uniformState._modelViewInfiniteProjectionDirty = false; diff --git a/Source/Scene/GroundPolygon.js b/Source/Scene/GroundPolygon.js index 64c0a59a129c..894c7dbbec38 100644 --- a/Source/Scene/GroundPolygon.js +++ b/Source/Scene/GroundPolygon.js @@ -1,9 +1,7 @@ /*global define*/ define([ '../Core/BoundingSphere', - '../Core/Cartesian2', '../Core/Cartesian3', - '../Core/Cartesian4', '../Core/ComponentDatatype', '../Core/defaultValue', '../Core/defined', @@ -16,7 +14,6 @@ define([ '../Core/PolygonGeometryLibrary', '../Core/PolygonPipeline', '../Core/PrimitiveType', - '../Core/SphericalExtent', '../Core/WindingOrder', '../Renderer/BufferUsage', '../Renderer/DrawCommand', @@ -25,15 +22,12 @@ define([ '../Shaders/ShadowVolumeVS', './BlendingState', './CullFace', - './DepthFunction', './Pass', './StencilFunction', './StencilOperation' ], function( BoundingSphere, - Cartesian2, Cartesian3, - Cartesian4, ComponentDatatype, defaultValue, defined, @@ -46,7 +40,6 @@ define([ PolygonGeometryLibrary, PolygonPipeline, PrimitiveType, - SphericalExtent, WindingOrder, BufferUsage, DrawCommand, @@ -55,7 +48,6 @@ define([ ShadowVolumeVS, BlendingState, CullFace, - DepthFunction, Pass, StencilFunction, StencilOperation) { @@ -74,16 +66,6 @@ define([ this._boundingSphere = undefined; - this._northPlane = new Cartesian4(); - this._southPlane = new Cartesian4(); - this._eastPlane = new Cartesian4(); - this._westPlane = new Cartesian4(); - - this._sinCosDeltas = new Cartesian4(); - this._centerAzimuthAndInverseDeltas = new Cartesian4(); - - this._centerAzimuthFromWest = 0.0; - this._va = undefined; this._sp = undefined; this._rs = undefined; @@ -91,15 +73,6 @@ define([ this.debugVolume = defaultValue(options.debugVolume, false); this._debugVolumeCommand = undefined; - this.debugPauseCamera = false; - this._debugPauseCamera = this.debugPauseCamera; - - this._debugCameraPosition = new Cartesian3(); - this._debugCameraDirection = new Cartesian3(); - - this._cameraPosition = undefined; - this._cameraDirection = undefined; - var that = this; this._uniformMap = { centralBodyMinimumAltitude : function() { @@ -107,33 +80,6 @@ define([ }, LODNegativeToleranceOverDistance : function() { return -0.01; - }, - northPlane : function() { - return that._northPlane; - }, - southPlane : function() { - return that._southPlane; - }, - eastPlane : function() { - return that._eastPlane; - }, - westPlane : function() { - return that._westPlane; - }, - sinCosDeltas : function () { - return that._sinCosDeltas; - }, - centerAzimuthAndInverseDeltas : function () { - return that._centerAzimuthAndInverseDeltas; - }, - centerAzimuthFromWest : function () { - return that._centerAzimuthFromWest; - }, - u_cameraPosition : function() { - return that._cameraPosition; - }, - u_cameraDirection : function() { - return that._cameraDirection; } }; @@ -339,210 +285,13 @@ define([ polygon._va = context.createVertexArray(attributes, indexBuffer); - polygon._bottomCapOffset = 0; - polygon._bottomCapCount = numBottomIndices; - polygon._topCapOffset = numBottomIndices; - polygon._topCapCount = numBottomIndices; - polygon._wallOffset = numCapIndices; - polygon._wallCount = numWallIndices; - polygon._boundingSphere = BoundingSphere.fromPoints(outerRing); - - // TODO: Correct bounding sphere - polygon._boundingSphere.radius = upDelta; - } - - function latitudePlane(north, theta, xy, z, center, plane) { - var normal = scratchNormal; - if (xy !== 0.0) - { - var tempVec0 = Cartesian3.fromElements(Math.cos(theta)* xy, Math.sin(theta) * xy, z); - var tempVec1 = Cartesian3.fromElements(tempVec0.y, -tempVec0.x, 0.0); - if (north) - { - Cartesian3.cross(tempVec0, tempVec1, normal); - } - else - { - Cartesian3.cross(tempVec1, tempVec0, normal); - } - } - else - { - normal.x = center.x; - normal.y = center.y; - normal.z = 0.0; - } - Cartesian3.normalize(normal, normal); - plane.x = normal.x; - plane.y = normal.y; - plane.z = normal.z; - plane.w = Cartesian3.dot(normal, center); - } - - var scratchPlane = new Cartesian4(); - var scratchPlaneRotated = new Cartesian4(); - - function computeTextureCoordinates(polygon) { - var polygonHierarchy = polygon._polygonHierarchy; - var outerRing = polygonHierarchy.positions; - - var center = polygon._boundingSphere.center; - - var sphericalExtent = SphericalExtent.fromPositions(outerRing); - var minimumLatitude = sphericalExtent.minimumLatitude; - var minimumLongitude = sphericalExtent.minimumLongitude; - var latitudeExtent = sphericalExtent.latitudeExtent; - var longitudeExtent = sphericalExtent.longitudeExtent; - - // - // West plane - // - var westPlane = polygon._westPlane; - westPlane.x = -Math.sin(minimumLongitude); - westPlane.y = Math.cos(minimumLongitude); - westPlane.z = 0.0; - westPlane.w = (westPlane.x * center.x) + (westPlane.y * center.y) + (westPlane.z * center.z); - - // - // East plane - // - var eastPlane = polygon._eastPlane; - var tempDouble = minimumLongitude + longitudeExtent; - eastPlane.x = Math.sin(tempDouble); - eastPlane.y = -Math.cos(tempDouble); - eastPlane.z = 0.0; - eastPlane.w = (eastPlane.x * center.x) + (eastPlane.y * center.y) + (eastPlane.z * center.z); - - // - // Sin and cos lon/lat deltas - // - var sinCosDeltas = polygon._sinCosDeltas; - sinCosDeltas.x = Math.sin(longitudeExtent); - sinCosDeltas.y = Math.cos(longitudeExtent); - sinCosDeltas.z = Math.sin(latitudeExtent); - sinCosDeltas.w = Math.cos(latitudeExtent); - - // - // Center azimuth - // - var centerAzimuth = Cartesian2.fromElements(center.x, center.y); - Cartesian2.normalize(centerAzimuth, centerAzimuth); - var centerAzimuthAndInverseDeltas = polygon._centerAzimuthAndInverseDeltas; - centerAzimuthAndInverseDeltas.x = centerAzimuth.x; - centerAzimuthAndInverseDeltas.y = centerAzimuth.y; - - // - // Inverse deltas - // - /* - if (volue is medium or small) - { - // - // When both extents are sufficiently small, the shader approximates arctan and - // tan to be the same; due to differences, the extents must be increased slightly - // - centerAzimuthAndInverseDeltas.z = 1.0 / Math.tan(longitudeExtent); - centerAzimuthAndInverseDeltas.w = 1.0 / Math.tan(latitudeExtent); - var azimuthXY = Cartesian2.fromElements(centerAzimuthAndInverseDeltas.x, centerAzimuthAndInverseDeltas.y); - Cartesian2.normalize(azimuthXY, azimuthXY); - var westXY = Cartesian2.fromElements(westPlane.y, -westPlane.x); - Cartesian2.normalize(westXY, westXY); - polygon._centerAzimuthFromWest = Math.acos(Cartesian2.dot(azimuthXY, westXY)); - } - else - { - */ - centerAzimuthAndInverseDeltas.z = 1.0 / longitudeExtent; - centerAzimuthAndInverseDeltas.w = 1.0 / latitudeExtent; - polygon._centerAzimuthFromWest = 0.0; - //} - - // - // North/South plane - for small volumes the north and south planes are calculated - // once. They are approximations that improve frame rate at the slight cost - // of visual quality. For larger volumes, the pixel shader calculates the - // planes on the fly using data calculated below. This is slower but is - // necessary to preserve visual quality. - // - // North plane - // - var northPlane = polygon._northPlane; - var centerAzimuthAngle = Math.atan2(centerAzimuth.y, centerAzimuth.x); - var z = Math.sin(minimumLatitude + latitudeExtent); - var xy = 1.0 - z * z; - xy = (xy < 0.0) ? 0.0 : Math.sqrt(xy); - - var plane = scratchPlane; - var planeRotated180Degs = scratchPlaneRotated; - latitudePlane(true, centerAzimuthAngle, xy, z, center, plane); - - /* - if (volume is small) - { - northPlane.x = plane.x; - northPlane.y = plane.y; - northPlane.z = plane.z; - northPlane.w = (northPlane.x * center.x) + (northPlane.y * center.y) + (northPlane.z * center.z); - } - else - { - */ - latitudePlane(true, centerAzimuthAngle + Math.PI, xy, z, center, planeRotated180Degs); - xy = 1.0 - plane.z * plane.z; - xy = (xy > 0.0) ? Math.sqrt(xy) : 0.0; - if (minimumLatitude + latitudeExtent < 0.0) - { - xy = -xy; - } - northPlane.x = plane.z; - northPlane.y = xy; - northPlane.z = planeRotated180Degs.w; - northPlane.w = plane.w - planeRotated180Degs.w; - //} - - // - // South plane - // - var southPlane = polygon._southPlane; - z = Math.sin(minimumLatitude); - xy = 1.0 - z * z; - xy = (xy < 0.0) ? 0.0 : Math.sqrt(xy); - latitudePlane(false, centerAzimuthAngle, xy, z, center, plane); - /* - if (volume is small) - { - southPlane.x = plane.x; - southPlane.y = plane.y; - southPlane.z = plane.z; - southPlane.w = (southPlane.x * center.x) + (southPlane.y * center.y) + (southPlane.z * center.z); - } - else - { - */ - latitudePlane(false, centerAzimuthAngle + Math.PI, xy, z, center, planeRotated180Degs); - xy = 1.0 - plane.z * plane.z; - xy = (xy > 0.0) ? Math.sqrt(xy) : 0.0; - if (minimumLatitude > 0.0) - { - xy = -xy; - } - southPlane.x = plane.z; - southPlane.y = xy; - southPlane.z = planeRotated180Degs.w; - southPlane.w = plane.w - planeRotated180Degs.w; - //} + polygon._boundingSphere.radius = upDelta; // TODO: Correct bounding sphere } GroundPolygon.prototype.update = function(context, frameState, commandList) { - // TODO: determine if supported - if (!defined(context.uniformState.globeDepthTexture)) { - return; - } - if (!defined(this._va)) { createShadowVolume(this, context); - computeTextureCoordinates(this); } if (!defined(this._sp)) { @@ -668,24 +417,6 @@ define([ }); } - var camera = frameState.camera; - - if (this.debugPauseCamera !== this._debugPauseCamera) { - if (this.debugPauseCamera) { - Cartesian3.clone(camera.position, this._debugCameraPosition); - Cartesian3.clone(camera.direction, this._debugCameraDirection); - } - this._debugPauseCamera = this.debugPauseCamera; - } - - if (this.debugPauseCamera) { - this._cameraPosition = this._debugCameraPosition; - this._cameraDirection = this._debugCameraDirection; - } else { - this._cameraPosition = camera.position; - this._cameraDirection = camera.direction; - } - var pass = frameState.passes; if (pass.render) { if (this.debugVolume) { diff --git a/Source/Shaders/ShadowVolumeFS.glsl b/Source/Shaders/ShadowVolumeFS.glsl index ee301c148e50..5df05c448392 100644 --- a/Source/Shaders/ShadowVolumeFS.glsl +++ b/Source/Shaders/ShadowVolumeFS.glsl @@ -2,47 +2,6 @@ #extension GL_EXT_frag_depth : enable #endif -// -// The west longitude plane relative to the center of the mesh -// -uniform vec4 westPlane; -// -// The west longitude plane relative to the center of the mesh -// -uniform vec4 eastPlane; -// -// Center azimuth and deltas: -// x = center azimuth x -// y = center azimuth y -// z = 1.0 / longitude delta -// w = 1.0 / latitude delta -// -uniform vec4 centerAzimuthAndInverseDeltas; -// -// Sin and cos deltas: -// x = sin(longitude delta) -// y = cos(longitude delta) -// z = sin(latitude delta) -// w = cos(latitude delta) -// -uniform vec4 sinCosDeltas; -// -// North plane: -// x = normal C magnitude -// y = normal AB magnitude -// z = minimum D -// w = maximum D - minimum D -// -uniform vec4 northPlane; -// -// South plane: -// x = normal C magnitude -// y = normal AB magnitude -// z = minimum D -// w = maximum D - minimum D -// -uniform vec4 southPlane; - varying float v_z; void czm_writeDepthClampedToFarPlane() @@ -54,101 +13,6 @@ void czm_writeDepthClampedToFarPlane() void main(void) { - /* - // - // Get the depth value of the terrain for the current fragment. - // Note that this is almost certainly different then the depth - // value of the shadow volume, which is why the depth texture - // is used in the first place. - // - vec2 depthTextureCoord; - depthTextureCoord.x = (gl_FragCoord.x - czm_viewport.x) / czm_viewport.z; - depthTextureCoord.y = (gl_FragCoord.y - czm_viewport.y) / czm_viewport.w; - vec4 terrainDepth = texture2D(czm_globeDepthTexture, depthTextureCoord); - // - // Convert to normalized device coordinates. That is - // a cube from [-1, -1, -1] to [1, 1, 1]. - // - vec4 ndcCoord; - ndcCoord.xy = (2.0 * depthTextureCoord.xy) - 1.0; - ndcCoord.z = (2.0 * terrainDepth.r) - 1.0; - ndcCoord.w = 1.0; - // - // Use the Inverse ModelView Projection Matrix to convert from NDC to CBF - // - vec4 cbfPosition = ndcCoord * czm_modelViewProjectionRelativeToPrimitiveCenterInverseTranspose; - // - // Reverse perspective divide - // - float w = 1.0 / cbfPosition.w; - cbfPosition.xyz *= w; - cbfPosition.w = 1.0; - // - // West is 0.0 and east is 1.0 - // - float tangent = 0.0; - float bitangent = 1.0; - float atan1 = 0.0; - float u = 0.0; - float d1 = dot(westPlane, cbfPosition); - if (d1 != 0.0) - { - float tempFloat = (dot(eastPlane, cbfPosition) / d1) + sinCosDeltas.y; - if (tempFloat != 0.0) - { - tangent = sinCosDeltas.x / tempFloat; - bitangent = 1.0; - } - else - { - tangent = 1.0; - bitangent = 0.0; - } - atan1 = atan(sinCosDeltas.x / tempFloat); - atan1 += (step(atan1, 0.0) + step(d1, 0.0)) * czm_pi; - u = atan1 * centerAzimuthAndInverseDeltas.z; - } - // - // Calculate the az vector for this pixel - // - vec2 azVec = normalize(vec2(westPlane.xy * tangent) + (vec2(westPlane.y, -westPlane.x) * bitangent)); - if ((atan1 >= czm_piOverTwo) && (atan1 <= czm_threePiOver2)) - { - azVec = -azVec; - } - // - // The following commented out code might perform better than the above if statement. It - // is slower on a GeForce6. - // - // float theSign = sign(atan1 - czm_piOverTwo) * sign(atan1 - czm_threePiOver2); - // azVec *= theSign; - // - // Calculate the north and south planes - // - float mrDead = (dot(centerAzimuthAndInverseDeltas.xy, azVec) + 1.0) * 0.5; - vec4 southPlane = vec4(azVec.xy * southPlane.y, southPlane.x, southPlane.z + (mrDead * southPlane.w)); - vec4 northPlane = vec4(azVec.xy * northPlane.y, northPlane.x, northPlane.z + (mrDead * northPlane.w)); - // - // South is 0.0 and north is 1.0 - // - float v = 0.0; - d1 = dot(southPlane, cbfPosition); - if (d1 != 0.0) - { - atan1 = atan(sinCosDeltas.z, (dot(northPlane, cbfPosition) / d1) + sinCosDeltas.w); - atan1 += (step(atan1, 0.0) + step(d1, 0.0)) * czm_pi; - v = atan1 * centerAzimuthAndInverseDeltas.w; - } - - vec2 textureCoordinates = vec2(u, v); - */ - gl_FragColor = vec4(1.0, 1.0, 0.0, 0.5); - - //float depth = terrainDepth.r; - //gl_FragColor = vec4(vec3(depth), 1.0); - - //gl_FragColor = vec4(textureCoordinates, 0.0, 0.5); - czm_writeDepthClampedToFarPlane(); } \ No newline at end of file diff --git a/Source/Shaders/ShadowVolumeVS.glsl b/Source/Shaders/ShadowVolumeVS.glsl index 53a440ec5750..4e840c05ce55 100644 --- a/Source/Shaders/ShadowVolumeVS.glsl +++ b/Source/Shaders/ShadowVolumeVS.glsl @@ -69,42 +69,17 @@ void main() { vec4 position = czm_translateRelativeToEye(positionHigh, positionLow); - // - // Make sure the vertex is moved down far enough to cover the central body - // - float delta = 1.0;//min(centralBodyMinimumAltitude, LODNegativeToleranceOverDistance * length(position.xyz)); - - // - // Move vertex down. This is not required if it belongs to a top - // cap or top of the wall, in which case it was already moved up just - // once on the CPU so the normal will be (0, 0, 0). - // - // Moving the vertex down is a function of the view parameters so - // it is done here to avoid buring CPU time. - // + float delta = 1.0; // TODO: moving the vertex is a function of the view vec3 eyePosition = position.xyz; vec3 movedPosition = position.xyz + normal * delta; if (all(equal(normal, vec3(0.0)))) { - //gl_Position = clipPointToPlane(eyePosition, movedPosition, false); gl_Position = czm_depthClampNearFarPlane(clipPointToPlane(eyePosition, movedPosition, false)); - //gl_Position = czm_depthClampNearFarPlane(clipPointToPlane(movedPosition, eyePosition, false)); - - //gl_Position = czm_modelViewProjectionRelativeToEye * (position + vec4(normal * delta, 0.0)); - //gl_Position = czm_depthClampNearFarPlane(czm_modelViewProjectionRelativeToEye * (position + vec4(normal * delta, 0.0))); - - //eyePosition = (czm_modelViewRelativeToEye * vec4(eyePosition, 1.0)).xyz; - //eyePosition.z = max(eyePosition.z, -(czm_currentFrustum.y + 0.001)); - //gl_Position = czm_projection * vec4(eyePosition, 1.0); } else { - //gl_Position = clipPointToNearPlane(movedPosition, eyePosition); gl_Position = czm_depthClampNearFarPlane(clipPointToPlane(movedPosition, eyePosition, true)); - - //gl_Position = czm_modelViewProjectionRelativeToEye * (position + vec4(normal * delta, 0.0)); - //gl_Position = czm_depthClampNearFarPlane(czm_modelViewProjectionRelativeToEye * (position + vec4(normal * delta, 0.0))); } } From f1d58312c9bc423e3971485346faebaa868edc4c Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Wed, 1 Apr 2015 15:15:40 -0400 Subject: [PATCH 12/12] Add ground polygon development Sandcastle example. --- .../gallery/development/Ground Polygon.html | 65 +++++++++++++++++++ Source/Scene/GroundPolygon.js | 9 +-- 2 files changed, 66 insertions(+), 8 deletions(-) create mode 100644 Apps/Sandcastle/gallery/development/Ground Polygon.html diff --git a/Apps/Sandcastle/gallery/development/Ground Polygon.html b/Apps/Sandcastle/gallery/development/Ground Polygon.html new file mode 100644 index 000000000000..275240ca6bce --- /dev/null +++ b/Apps/Sandcastle/gallery/development/Ground Polygon.html @@ -0,0 +1,65 @@ + + + + + + + + + Cesium Demo + + + + + + +
+

Loading...

+
+ + + diff --git a/Source/Scene/GroundPolygon.js b/Source/Scene/GroundPolygon.js index 894c7dbbec38..de2f737c16d9 100644 --- a/Source/Scene/GroundPolygon.js +++ b/Source/Scene/GroundPolygon.js @@ -395,14 +395,7 @@ define([ blending : BlendingState.ALPHA_BLEND }); - var pauseVS = new ShaderSource({ - sources : [ShadowVolumeVS], - defined : ['DEBUG_PAUSE'] - }); - var pauseFS = new ShaderSource({ - sources : [ShadowVolumeFS] - }); - var sp = context.createShaderProgram(pauseVS, pauseFS, attributeLocations); + var sp = context.createShaderProgram(ShadowVolumeVS, ShadowVolumeFS, attributeLocations); this._debugVolumeCommand = new DrawCommand({ primitiveType : PrimitiveType.TRIANGLES,