Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Model lighting options #7025

Merged
merged 12 commits into from
Oct 19, 2018
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ Change Log
##### Additions :tada:
* Added WMS-T (time) support in WebMapServiceImageryProvider [#2581](https://github.com/AnalyticalGraphicsInc/cesium/issues/2581)
* Added `cutoutRectangle` to `ImageryLayer`, which allows cutting out rectangular areas in imagery layers to reveal underlying imagery. [#7056](https://github.com/AnalyticalGraphicsInc/cesium/pull/7056)
* Added `imageBasedLightingFactor` property to `Cesium3DTileset`, `Model`, and `ModelGraphics` to scale the diffuse and specular image-based lighting contributions to the final color. [#7025](https://github.com/AnalyticalGraphicsInc/cesium/pull/7025)
* Added `lightColor` property to `Cesium3DTileset`, `Model`, and `ModelGraphics` to change the intensity of the light used when shading model. [#7025](https://github.com/AnalyticalGraphicsInc/cesium/pull/7025)

##### Fixes :wrench:
* Fixed an issue where `pickPosition` would return incorrect results when called after `sampleHeight` or `clampToHeight`. [#7113](https://github.com/AnalyticalGraphicsInc/cesium/pull/7113)
Expand Down
26 changes: 25 additions & 1 deletion Source/DataSources/ModelGraphics.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ define([
* @param {Property} [options.colorBlendMode=ColorBlendMode.HIGHLIGHT] An enum Property specifying how the color blends with the model.
* @param {Property} [options.colorBlendAmount=0.5] A numeric Property specifying the color strength when the <code>colorBlendMode</code> is <code>MIX</code>. A value of 0.0 results in the model's rendered color while a value of 1.0 results in a solid color, with any value in-between resulting in a mix of the two.
* @param {Property} [options.clippingPlanes] A property specifying the {@link ClippingPlaneCollection} used to selectively disable rendering the model.
* @param {Property} [options.imageBasedLightingFactor=new Cartesian2(1.0, 1.0)] A property specifying the contribution from diffuse and specular image-based lighting.
* @param {Property} [options.lightColor] A property specifying the light color to use when shading the model. The default sun light color will be used when <code>undefined</code>.
*
* @see {@link https://cesiumjs.org/tutorials/3D-Models-Tutorial/|3D Models Tutorial}
* @demo {@link https://cesiumjs.org/Cesium/Apps/Sandcastle/index.html?src=3D%20Models.html|Cesium Sandcastle 3D Models Demo}
Expand Down Expand Up @@ -96,6 +98,10 @@ define([
this._colorBlendAmountSubscription = undefined;
this._clippingPlanes = undefined;
this._clippingPlanesSubscription = undefined;
this._imageBasedLightingFactor = undefined;
this._imageBasedLightingFactorSubscription = undefined;
this._lightColor = undefined;
this._lightColorSubscription = undefined;
this._definitionChanged = new Event();

this.merge(defaultValue(options, defaultValue.EMPTY_OBJECT));
Expand Down Expand Up @@ -262,7 +268,21 @@ define([
* @memberof ModelGraphics.prototype
* @type {Property}
*/
clippingPlanes : createPropertyDescriptor('clippingPlanes')
clippingPlanes : createPropertyDescriptor('clippingPlanes'),

/**
* A property specifying the {@link Cartesian2} used to scale the diffuse and specular image-based lighting contribution to the final color.
* @memberof ModelGraphics.prototype
* @type {Property}
*/
imageBasedLightingFactor : createPropertyDescriptor('imageBasedLightingFactor'),

/**
* A property specifying the {@link Cartesian3} color of the light source when shading the model.
* @memberOf ModelGraphics.prototype
* @type {Property}
*/
lightColor : createPropertyDescriptor('lightColor')
});

/**
Expand Down Expand Up @@ -293,6 +313,8 @@ define([
result.colorBlendMode = this.colorBlendMode;
result.colorBlendAmount = this.colorBlendAmount;
result.clippingPlanes = this.clippingPlanes;
result.imageBasedLightingFactor = this.imageBasedLightingFactor;
result.lightColor = this.lightColor;

return result;
};
Expand Down Expand Up @@ -327,6 +349,8 @@ define([
this.colorBlendMode = defaultValue(this.colorBlendMode, source.colorBlendMode);
this.colorBlendAmount = defaultValue(this.colorBlendAmount, source.colorBlendAmount);
this.clippingPlanes = defaultValue(this.clippingPlanes, source.clippingPlanes);
this.imageBasedLightingFactor = defaultValue(this.imageBasedLightingFactor, source.imageBasedLightingFactor);
this.lightColor = defaultValue(this.lightColor, source.lightColor);

var sourceNodeTransformations = source.nodeTransformations;
if (defined(sourceNodeTransformations)) {
Expand Down
5 changes: 5 additions & 0 deletions Source/DataSources/ModelVisualizer.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
define([
'../Core/AssociativeArray',
'../Core/BoundingSphere',
'../Core/Cartesian2',
'../Core/Color',
'../Core/defined',
'../Core/destroyObject',
Expand All @@ -17,6 +18,7 @@ define([
], function(
AssociativeArray,
BoundingSphere,
Cartesian2,
Color,
defined,
destroyObject,
Expand All @@ -43,6 +45,7 @@ define([
var defaultColor = Color.WHITE;
var defaultColorBlendMode = ColorBlendMode.HIGHLIGHT;
var defaultColorBlendAmount = 0.5;
var defaultImageBasedLightingFactor = new Cartesian2(1.0, 1.0);

var modelMatrixScratch = new Matrix4();
var nodeMatrixScratch = new Matrix4();
Expand Down Expand Up @@ -157,6 +160,8 @@ define([
model.colorBlendAmount = Property.getValueOrDefault(modelGraphics._colorBlendAmount, time, defaultColorBlendAmount);
model.clippingPlanes = Property.getValueOrUndefined(modelGraphics._clippingPlanes, time);
model.clampAnimations = Property.getValueOrDefault(modelGraphics._clampAnimations, time, defaultClampAnimations);
model.imageBasedLightingFactor = Property.getValueOrDefault(modelGraphics._imageBasedLightingFactor, time, defaultImageBasedLightingFactor);
model.lightColor = Property.getValueOrUndefined(modelGraphics._lightColor, time);

if (model.ready) {
var runAnimations = Property.getValueOrDefault(modelGraphics._runAnimations, time, true);
Expand Down
6 changes: 5 additions & 1 deletion Source/Scene/Batched3DModel3DTileContent.js
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,9 @@ define([
uniformMapLoaded : batchTable.getUniformMapCallback(),
pickIdLoaded : getPickIdCallback(content),
addBatchIdToGeneratedShaders : (batchLength > 0), // If the batch table has values in it, generated shaders will need a batchId attribute
pickObject : pickObject
pickObject : pickObject,
imageBasedLightingFactor : tileset.imageBasedLightingFactor,
lightColor : tileset.lightColor
});
} else {
// This transcodes glTF to an internal representation for geometry so we can take advantage of the re-batching of vector data.
Expand Down Expand Up @@ -460,6 +462,8 @@ define([
this._model.modelMatrix = this._contentModelMatrix;

this._model.shadows = this._tileset.shadows;
this._model.imageBasedLightingFactor = this._tileset.imageBasedLightingFactor;
this._model.lightColor = this._tileset.lightColor;
this._model.debugWireframe = this._tileset.debugWireframe;

// Update clipping planes
Expand Down
40 changes: 40 additions & 0 deletions Source/Scene/Cesium3DTileset.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ define([
* @param {ClassificationType} [options.classificationType] Determines whether terrain, 3D Tiles or both will be classified by this tileset. See {@link Cesium3DTileset#classificationType} for details about restrictions and limitations.
* @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid determining the size and shape of the globe.
* @param {Object} [options.pointCloudShading] Options for constructing a {@link PointCloudShading} object to control point attenuation based on geometric error and lighting.
* @param {Cartesian2} [options.imageBasedLightingFactor=new Cartesian2(1.0, 1.0)] Scales the diffuse and specular image-based lighting from the earth, sky, atmosphere and star skybox.
* @param {Cartesian3} [options.lightColor] The color and intensity of the sunlight used to shade models.
* @param {Boolean} [options.debugFreezeFrame=false] For debugging only. Determines if only the tiles from last frame should be used for rendering.
* @param {Boolean} [options.debugColorizeTiles=false] For debugging only. When true, assigns a random color to each tile.
* @param {Boolean} [options.debugWireframe=false] For debugging only. When true, render's each tile's content as a wireframe.
Expand Down Expand Up @@ -569,6 +571,21 @@ define([
this._clippingPlanes = undefined;
this.clippingPlanes = options.clippingPlanes;

this._imageBasedLightingFactor = new Cartesian2(1.0, 1.0);
Cartesian2.clone(options.imageBasedLightingFactor, this._imageBasedLightingFactor);

/**
* The color and intensity of the sunlight used to shade a model.
* <p>
* For example, disabling additional light sources by setting <code>model.imageBasedLightingFactor = new Cartesian2(0.0, 0.0)</code> will make the
* model much darker. Here, increasing the intensity of the light source will make the model brighter.
* </p>
*
* @type {Cartesian3}
* @default undefined
*/
this.lightColor = options.lightColor;

/**
* This property is for debugging only; it is not optimized for production use.
* <p>
Expand Down Expand Up @@ -1236,6 +1253,29 @@ define([

return this._extras;
}
},

/**
* Cesium adds lighting from the earth, sky, atmosphere, and star skybox. This cartesian is used to scale the final
* diffuse and specular lighting contribution from those sources to the final color. A value of 0.0 will disable those light sources.
*
* @type {Cartesian2}
* @default Cartesian2(1.0, 1.0)
*/
imageBasedLightingFactor : {
get : function() {
return this._imageBasedLightingFactor;
},
set : function(value) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.object('imageBasedLightingFactor', value);
Check.typeOf.number.greaterThanOrEquals('imageBasedLightingFactor.x', value.x, 0.0);
Check.typeOf.number.lessThanOrEquals('imageBasedLightingFactor.x', value.x, 1.0);
Check.typeOf.number.greaterThanOrEquals('imageBasedLightingFactor.y', value.y, 0.0);
Check.typeOf.number.lessThanOrEquals('imageBasedLightingFactor.y', value.y, 1.0);
//>>includeEnd('debug');
Cartesian2.clone(value, this._imageBasedLightingFactor);
}
}
});

Expand Down
8 changes: 6 additions & 2 deletions Source/Scene/Instanced3DModel3DTileContent.js
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,8 @@ define([
gltfView = new Uint8Array(uint8Array.subarray(byteOffset, byteOffset + gltfByteLength));
}

var tileset = content._tileset;

// Create model instance collection
var collectionOptions = {
instances : new Array(instancesLength),
Expand All @@ -282,10 +284,12 @@ define([
gltf : undefined,
basePath : undefined,
incrementallyLoadTextures : false,
upAxis : content._tileset._gltfUpAxis,
upAxis : tileset._gltfUpAxis,
forwardAxis : Axis.X,
opaquePass : Pass.CESIUM_3D_TILE, // Draw opaque portions during the 3D Tiles pass
pickIdLoaded : getPickIdCallback(content)
pickIdLoaded : getPickIdCallback(content),
imageBasedLightingFactor : tileset.imageBasedLightingFactor,
lightColor : tileset.lightColor
};

if (gltfFormat === 0) {
Expand Down
102 changes: 99 additions & 3 deletions Source/Scene/Model.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
define([
'../Core/BoundingSphere',
'../Core/Cartesian2',
'../Core/Cartesian3',
'../Core/Cartesian4',
'../Core/Cartographic',
'../Core/Check',
'../Core/clone',
'../Core/Color',
'../Core/combine',
Expand Down Expand Up @@ -74,9 +76,11 @@ define([
'./ShadowMode'
], function(
BoundingSphere,
Cartesian2,
Cartesian3,
Cartesian4,
Cartographic,
Check,
clone,
Color,
combine,
Expand Down Expand Up @@ -282,6 +286,8 @@ define([
* @param {Number} [options.silhouetteSize=0.0] The size of the silhouette in pixels.
* @param {ClippingPlaneCollection} [options.clippingPlanes] The {@link ClippingPlaneCollection} used to selectively disable rendering the model.
* @param {Boolean} [options.dequantizeInShader=true] Determines if a {@link https://github.com/google/draco|Draco} encoded model is dequantized on the GPU. This decreases total memory usage for encoded models.
* @param {Cartesian2} [options.imageBasedLightingFactor=Cartesian2(1.0, 1.0)] Scales diffuse and specular image-based lighting from the earth, sky, atmosphere and star skybox.
* @param {Cartesian3} [options.lightColor] The color and intensity of the sunlight used to shade the model.
*
* @see Model.fromGltf
*
Expand Down Expand Up @@ -656,6 +662,11 @@ define([
this._rtcCenter2D = undefined; // in projected world coordinates

this._keepPipelineExtras = options.keepPipelineExtras; // keep the buffers in memory for use in other applications

this._imageBasedLightingFactor = new Cartesian2(1.0, 1.0);
Cartesian2.clone(options.imageBasedLightingFactor, this._imageBasedLightingFactor);
this._lightColor = Cartesian3.clone(options.lightColor);
this._regenerateShaders = false;
}

defineProperties(Model.prototype, {
Expand Down Expand Up @@ -1073,6 +1084,59 @@ define([
get : function() {
return this._pickIds;
}
},

/**
* Cesium adds lighting from the earth, sky, atmosphere, and star skybox. This cartesian is used to scale the final
* diffuse and specular lighting contribution from those sources to the final color. A value of 0.0 will disable those light sources.
*
* @memberof Model.prototype
*
* @type {Cartesian2}
* @default Cartesian2(1.0, 1.0)
*/
imageBasedLightingFactor : {
get : function() {
return this._imageBasedLightingFactor;
},
set : function(value) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.object('imageBasedLightingFactor', value);
Check.typeOf.number.greaterThanOrEquals('imageBasedLightingFactor.x', value.x, 0.0);
Check.typeOf.number.lessThanOrEquals('imageBasedLightingFactor.x', value.x, 1.0);
Check.typeOf.number.greaterThanOrEquals('imageBasedLightingFactor.y', value.y, 0.0);
Check.typeOf.number.lessThanOrEquals('imageBasedLightingFactor.y', value.y, 1.0);
//>>includeEnd('debug');
this._regenerateShaders = this._regenerateShaders || (this._imageBasedLightingFactor.x > 0.0 && value.x === 0.0) || (this._imageBasedLightingFactor.x === 0.0 && value.x > 0.0);
this._regenerateShaders = this._regenerateShaders || (this._imageBasedLightingFactor.y > 0.0 && value.y === 0.0) || (this._imageBasedLightingFactor.y === 0.0 && value.y > 0.0);
Cartesian2.clone(value, this._imageBasedLightingFactor);
}
},

/**
* The color and intensity of the sunlight used to shade the model.
* <p>
* For example, disabling additional light sources by setting <code>model.imageBasedLightingFactor = new Cesium.Cartesian2(0.0, 0.0)</code> will make the
* model much darker. Here, increasing the intensity of the light source will make the model brighter.
* </p>
*
* @memberof Model.prototype
*
* @type {Cartesian3}
* @default undefined
*/
lightColor : {
get : function() {
return this._lightColor;
},
set : function(value) {
var lightColor = this._lightColor;
if (value === lightColor || Cartesian3.equals(value, lightColor)) {
return;
}
this._regenerateShaders = this._regenerateShaders || (defined(lightColor) && !defined(value)) || (defined(value) && !defined(lightColor));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it also regenerate the shader if the previous color was not (0,0,0) but the new color is (0,0,0) (and the reverse)?

Some of the other documentation makes it seem as if setting lightColor to undefined would result in using the default sun color. Should that be the behavior here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Meant to delete this comment. See #7025 (comment) instead.

this._lightColor = Cartesian3.clone(value, lightColor);
}
}
});

Expand Down Expand Up @@ -1936,6 +2000,14 @@ define([
drawFS = 'uniform vec4 czm_pickColor;\n' + drawFS;
}

if (model._imageBasedLightingFactor.x > 0.0 || model._imageBasedLightingFactor.y > 0.0) {
drawFS = '#define USE_IBL_LIGHTING \n\n' + drawFS;
}

if (defined(model._lightColor)) {
drawFS = '#define USE_CUSTOM_LIGHT_COLOR \n\n' + drawFS;
}

createAttributesAndProgram(programId, techniqueId, drawFS, drawVS, model, context);
}

Expand Down Expand Up @@ -1978,6 +2050,14 @@ define([
drawFS = 'uniform vec4 czm_pickColor;\n' + drawFS;
}

if (model._imageBasedLightingFactor.x > 0.0 || model._imageBasedLightingFactor.y > 0.0) {
drawFS = '#define USE_IBL_LIGHTING \n\n' + drawFS;
}

if (defined(model._lightColor)) {
drawFS = '#define USE_CUSTOM_LIGHT_COLOR \n\n' + drawFS;
}

createAttributesAndProgram(programId, techniqueId, drawFS, drawVS, model, context);
}

Expand Down Expand Up @@ -2846,6 +2926,18 @@ define([
};
}

function createIBLFactorFunction(model) {
return function() {
return model._imageBasedLightingFactor;
};
}

function createLightColorFunction(model) {
return function() {
return model._lightColor;
};
}

function triangleCountFromPrimitiveIndices(primitive, indicesCount) {
switch (primitive.mode) {
case PrimitiveType.TRIANGLES:
Expand Down Expand Up @@ -2938,7 +3030,9 @@ define([
gltf_colorBlend : createColorBlendFunction(model),
gltf_clippingPlanes: createClippingPlanesFunction(model),
gltf_clippingPlanesEdgeStyle: createClippingPlanesEdgeStyleFunction(model),
gltf_clippingPlanesMatrix: createClippingPlanesMatrixFunction(model)
gltf_clippingPlanesMatrix: createClippingPlanesMatrixFunction(model),
gltf_iblFactor : createIBLFactorFunction(model),
gltf_lightColor : createLightColorFunction(model)
});

// Allow callback to modify the uniformMap
Expand Down Expand Up @@ -4256,7 +4350,7 @@ define([
currentClippingPlanesState = clippingPlanes.clippingPlanesState;
}

var shouldRegenerateShaders = this._clippingPlanesState !== currentClippingPlanesState;
var shouldRegenerateShaders = this._clippingPlanesState !== currentClippingPlanesState || this._regenerateShaders;
this._clippingPlanesState = currentClippingPlanesState;

// Regenerate shaders if color shading changed from last update
Expand All @@ -4272,6 +4366,8 @@ define([
updateColor(this, frameState, false);
updateSilhouette(this, frameState, false);
}

this._regenerateShaders = false;
}

if (justLoaded) {
Expand Down Expand Up @@ -4365,7 +4461,7 @@ define([
destroyIfNotCached(rendererResources, cachedRendererResources);

var programId;
if (isClippingEnabled(model) || isColorShadingEnabled(model)) {
if (isClippingEnabled(model) || isColorShadingEnabled(model) || model._regenerateShaders) {
rendererResources.programs = {};
rendererResources.silhouettePrograms = {};

Expand Down
Loading