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

A proposed implementation for passing imagery layers data to Fragment Shaders #10187

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Source/Scene/GlobeSurfaceShaderSet.js
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,9 @@ GlobeSurfaceShaderSet.prototype.getShaderProgram = function (options) {
computeDayColor +=
"\
color,\n\
u_layerIndex[" +
i +
"],\n\
u_dayTextures[" +
i +
"],\n\
Expand Down
7 changes: 6 additions & 1 deletion Source/Scene/GlobeSurfaceTileProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -1589,6 +1589,9 @@ const scratchClippingPlanesMatrix = new Matrix4();
const scratchInverseTransposeClippingPlanesMatrix = new Matrix4();
function createTileUniformMap(frameState, globeSurfaceTileProvider) {
const uniformMap = {
u_layerIndex: function () {
return this.properties.layerIndex;
},
u_initialColor: function () {
return this.properties.initialColor;
},
Expand Down Expand Up @@ -1786,6 +1789,7 @@ function createTileUniformMap(frameState, globeSurfaceTileProvider) {

terrainExaggerationAndRelativeHeight: new Cartesian2(1.0, 0.0),

layerIndex: [],
dayTextures: [],
dayTextureTranslationAndScale: [],
dayTextureTexCoordsRectangle: [],
Expand Down Expand Up @@ -2505,7 +2509,8 @@ function addDrawCommandsForTile(tileProvider, tile, frameState) {
tileImagery
);
}

uniformMapProperties.layerIndex[numberOfDayTextures] =
imageryLayer._layerIndex;
uniformMapProperties.dayTextures[numberOfDayTextures] = texture;
uniformMapProperties.dayTextureTranslationAndScale[numberOfDayTextures] =
tileImagery.textureTranslationAndScale;
Expand Down
1 change: 1 addition & 0 deletions Source/Shaders/Builtin/Structs/materialInput.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,5 @@ struct czm_materialInput
float height;
float slope;
float aspect;
vec4 layerColor[czm_layerColorNumber];
Copy link
Contributor

Choose a reason for hiding this comment

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

Just confirming - to check that a layer exists would the material have code like materialInput.layerColor[index] != vec4(0.0)?

layerColor should be added to the documentation above.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This can be a way to test it, indeed. If ever the number of layers passed to the material could be set by the user, it would be more obvious which layer colors are made available to the material.

Copy link
Contributor

Choose a reason for hiding this comment

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

Should there be a layerColorNumber.glsl file? I had to create one locally to test this. Also a sandcastle demo would be helpful. I modified the Globe Materials sandcastle.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry, I forgot to check in layerColorNumber.glsl
I would like to find the best way to implement this and make sure it just makes sense at all before writing any demo. This is why your input is very valuable here.

};
27 changes: 21 additions & 6 deletions Source/Shaders/GlobeFS.glsl
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
uniform vec4 u_initialColor;

#if TEXTURE_UNITS > 0
uniform int u_layerIndex[TEXTURE_UNITS];
uniform sampler2D u_dayTextures[TEXTURE_UNITS];
uniform vec4 u_dayTextureTranslationAndScale[TEXTURE_UNITS];
uniform bool u_dayTextureUseWebMercatorT[TEXTURE_UNITS];
Expand Down Expand Up @@ -160,8 +161,13 @@ bool inTranslucencyRectangle()
}
#endif

#ifdef APPLY_MATERIAL
czm_materialInput materialInput;
#endif

vec4 sampleAndBlend(
vec4 previousColor,
int layerIndex,
sampler2D textureToSample,
vec2 tileTextureCoordinates,
vec4 textureCoordinateRectangle,
Expand Down Expand Up @@ -191,17 +197,27 @@ vec4 sampleAndBlend(
alphaMultiplier = step(vec2(0.0), textureCoordinateRectangle.pq - tileTextureCoordinates);
textureAlpha = textureAlpha * alphaMultiplier.x * alphaMultiplier.y;

#if defined(APPLY_DAY_NIGHT_ALPHA) && defined(ENABLE_DAYNIGHT_SHADING)
textureAlpha *= mix(textureDayAlpha, textureNightAlpha, nightBlend);
#endif

vec2 translation = textureCoordinateTranslationAndScale.xy;
vec2 scale = textureCoordinateTranslationAndScale.zw;
vec2 textureCoordinates = tileTextureCoordinates * scale + translation;
vec4 value = texture2D(textureToSample, textureCoordinates);
vec3 color = value.rgb;
float alpha = value.a;

#ifdef APPLY_MATERIAL
if (textureAlpha > 0.0) {
// itterate over the layerColor array to check for matching layerIndex and send color value to materialInput
//TODO: Find a different way to set pass layerColor values. Here, only the first two layers are handled as this loop leads to performance problems (shaders compilation time)
for (int i = 0; i < czm_layerColorNumber; i++) {
if (i == layerIndex) {materialInput.layerColor[i] = value; break;}
}
}
#endif
Comment on lines +207 to +215
Copy link
Contributor

Choose a reason for hiding this comment

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

It's unfortunate that czm_layerColorNumber would be capped to the first two layers due to slow compilation.

I can't think of a good way to avoid this in WebGL 1 unless layerIndex were somehow made to be a constant expression, and that seems tricky since GlobeSurfaceShaderSet.prototype.getShaderProgram doesn't have access to the imagery layers.

Since WebGL2 allows dynamically indexed arrays this would simply be materialInput.layerColor[layerIndex] = value. Another reason to switch to WebGL 2 soon.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the feedback @lilleyse. This pull request is just me thinking out loud about this feature and how best to implement it. In my use case, only one layer is made available to the shader. To work around the limitation of a hard coded number of layers, could this number be set by the user in the form of a "define"?

Copy link
Contributor

@lilleyse lilleyse Mar 15, 2022

Choose a reason for hiding this comment

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

To work around the limitation of a hard coded number of layers, could this number be set by the user in the form of a "define"?

Possibly. I considered that as well. The only problem is that materialInput.glsl is used by other systems that don't have access to the define — though they could mock the define and set it to 1.

I'm not sure about the exact requirements of your application, but would it be acceptable for the material to have a single color after all the imagery layers have been resolved rather than an array of layer colors? That would simplify things quite a bit.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My application needs the color of one specific layer, so using the resulting mix of all layers will not be acceptable. In any case, this pull request aims at finding a general solution that can be useful to Cesium users at large. I will investigate a "define" based approach to the problem and post results in the PR.


#if defined(APPLY_DAY_NIGHT_ALPHA) && defined(ENABLE_DAYNIGHT_SHADING)
textureAlpha *= mix(textureDayAlpha, textureNightAlpha, nightBlend);
#endif

#ifdef APPLY_COLOR_TO_ALPHA
vec3 colorDiff = abs(color.rgb - colorToAlpha.rgb);
colorDiff.r = max(max(colorDiff.r, colorDiff.g), colorDiff.b);
Expand Down Expand Up @@ -382,11 +398,10 @@ void main()
#endif

#ifdef APPLY_MATERIAL
czm_materialInput materialInput;
materialInput.st = v_textureCoordinates.st;
materialInput.normalEC = normalize(v_normalEC);
materialInput.positionToEyeEC = -v_positionEC;
materialInput.tangentToEyeMatrix = czm_eastNorthUpToEyeCoordinates(v_positionMC, normalize(v_normalEC));
materialInput.tangentToEyeMatrix = czm_eastNorthUpToEyeCoordinates(v_positionMC, normalize(v_normalEC));
materialInput.slope = v_slope;
materialInput.height = v_height;
materialInput.aspect = v_aspect;
Expand Down