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

Support glTF Specular Glossiness Extension #7006

Merged
merged 16 commits into from
Sep 17, 2018
Merged

Support glTF Specular Glossiness Extension #7006

merged 16 commits into from
Sep 17, 2018

Conversation

OmarShehata
Copy link
Contributor

@OmarShehata OmarShehata commented Sep 5, 2018

This PR adds support for the KHR_materials_pbrSpecularGlossiness glTF extension. Resolves #6921.

I still have to write tests, but I'm opening it now to get some early feedback.

It's currently implemented in processPbrMetallicRoughness.js because it seems implementation-wise the only difference between Metallic Roughness and Specular Glossiness is how the BRDF inputs are computed. It'll check for this extension and modify the relevant part of the shader, keeping the rest of it intact.

Since processPbrMetallicRoughness.js would now be doing both metallic-roughness and specular-glossiness, should it be renamed to something more general like processPbrMaterials.js ? The other option would be to abstract away most of processPbrMetallicRoughness.js into a common class that could be shared. @emackey what do you think?

I added a test model with spec-gloss in Specs/Data/Models/PBR/BoomBoxSpecularGlossiness/. Here's a snippet to test it locally in Sandcastle, and to switch between the metallic-roughness and the specular-glossiness versions of the model.

var viewer = new Cesium.Viewer('cesiumContainer', {
    selectionIndicator : false,
});

viewer.scene.globe.enableLighting = true;
viewer.scene.globe.depthTestAgainstTerrain = true;

var position = new Cesium.Cartesian3(-1371108.1511167218, -5508684.080096612, 2901825);
var heading = Cesium.Math.toRadians(180);
var pitch = Cesium.Math.toRadians(2);
var roll = Cesium.Math.toRadians(-6);
var hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll);
var orientation = Cesium.Transforms.headingPitchRollQuaternion(position, hpr);

var bareGLTF = viewer.entities.add({
    position : position,
    orientation : orientation,
    model : {
        uri : '../../../Specs/Data/Models/PBR/BoomBox/BoomBox.gltf',
        heightReference : Cesium.HeightReference.RELATIVE_TO_GROUND ,
        minimumPixelSize : 512,
        maximumScale : 100,
        scale : 8.0,
        runAnimations : false
    }
});

viewer.trackedEntity = bareGLTF;

var position2 = new Cesium.Cartesian3(-1371100.6511167218, -5508684.080096612, 2901825);

var specGlossGLTF = viewer.entities.add({
    position : position2,
    orientation : orientation,
    model : {
        uri :'../../../Specs/Data/Models/PBR/BoomBoxSpecularGlossiness/BoomBox.gltf',
        heightReference : Cesium.HeightReference.RELATIVE_TO_GROUND ,
        minimumPixelSize : 512,
        maximumScale : 100,
        scale : 8.0,
        runAnimations : false
    }
});


var options = [{
    text : 'glTF',
    onselect : function() {
        viewer.trackedEntity = bareGLTF;
    }
}, {
    text : 'glTF Spec Gloss',
    onselect : function() {
        viewer.trackedEntity = specGlossGLTF;
    }
}];

Sandcastle.addToolbarMenu(options);

For this model, they look identical which I think is intended.

normal

Adding a specularFactor: [1.0, 0.5, 0.5] gives the specular a reddish tone:

reddish

For reference, this is how Threejs implements it.

The only other thing is that the spec says:

In addition to the material properties, if a primitive specifies a vertex color using the attribute semantic property COLOR_0, then this value acts as an additional linear multiplier to diffuse.

@lilleyse do you know if this is something that's already handled?

@cesium-concierge
Copy link

Thanks for the pull request @OmarShehata!

  • ✔️ Signed CLA found.

Reviewers, don't forget to make sure that:

  • Cesium Viewer works.
  • Works in 2D/CV.
  • Works (or fails gracefully) in IE11.

I am a bot who helps you make Cesium awesome! Contributions to my configuration are welcome.

🌍 🌎 🌏

@lilleyse
Copy link
Contributor

lilleyse commented Sep 5, 2018

Very cool @OmarShehata!

In addition to the material properties, if a primitive specifies a vertex color using the attribute semantic property COLOR_0, then this value acts as an additional linear multiplier to diffuse.

@lilleyse do you know if this is something that's already handled?

Yeah we handle that already, for metallic roughness at least - https://github.com/AnalyticalGraphicsInc/cesium/blob/master/Source/ThirdParty/GltfPipeline/processPbrMetallicRoughness.js#L529

fragmentShader += ' float alpha = pow((1.0 - glossiness), 2.0);\n';
fragmentShader += ' vec3 diffuseColor = diffuse.rgb * (1.0 - max(max(specular.r, specular.g), specular.b));\n';
fragmentShader += ' vec3 specularColor = specular;\n';
fragmentShader += ' float roughness = 1.0 - glossiness;\n';
Copy link
Contributor

Choose a reason for hiding this comment

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

Do this above the alpha calculation on line 638 above, then you can avoid the pow and just have float alpha = roughness * roughness.

fragmentShader += ' vec3 specular = vec3(1.0);\n';
}
if (defined(generatedMaterialValues.u_glossinessFactor)) {
fragmentShader += ' float glossiness *= clamp(u_glossinessFactor, 0.0, 1.0);\n';
Copy link
Contributor

Choose a reason for hiding this comment

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

You have float glossiness *= here, that doesn't compile.

Copy link
Contributor

Choose a reason for hiding this comment

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

I tried fixing this locally by removing the *. This works and shows my SpecGlossVsMetalRough new test model correctly!

} else {
fragmentShader += ' float metalness = 1.0;\n';
if (defined(generatedMaterialValues.u_diffuseFactor)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

eslint is complaining here, make sure you're set up to run the code style tools.

@emackey
Copy link
Contributor

emackey commented Sep 6, 2018

Need to merge in gltf-update-pipeline and resolve conflicts.

Left some comments above, otherwise looks great!

specglosstest

@OmarShehata
Copy link
Contributor Author

Thanks for the review @emackey, and for the water bottle test case!

It should be all good now. The test cases I added check for being able to render a model when all parameters are provided, when none are provided and when no textures are provided, so it would have caught the shader compile error earlier.

I also renamed the class to processPbrMaterials because I think that better reflects what it does now.

@OmarShehata
Copy link
Contributor Author

@emackey did you want to give this another look for the new commits? (Biggest change is adding tests and renaming the class).

@emackey
Copy link
Contributor

emackey commented Sep 10, 2018 via email

Copy link
Contributor

@emackey emackey left a comment

Choose a reason for hiding this comment

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

Looks good to me. Anyone else want to review?

@emackey
Copy link
Contributor

emackey commented Sep 11, 2018

Travis is having a ton of problems, I have trouble understanding when that's a fluke vs not.

@OmarShehata
Copy link
Contributor Author

Thanks Ed! And yeah, I believe those are because the base branch gltf-update-pipeline doesn't have updated tests yet.

@pjcozzi
Copy link
Contributor

pjcozzi commented Sep 12, 2018

Does anyone else need to look at this before it is merged? And what is needed for AnalyticalGraphicsInc:gltf-update-pipeline to be merged into master?

Copy link
Contributor

@lilleyse lilleyse left a comment

Choose a reason for hiding this comment

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

Just one minor comment. Looks good to me.

var uniformName;
var pbrMetallicRoughness = material.pbrMetallicRoughness;
if (defined(pbrMetallicRoughness)) {
for (var parameterName in pbrMetallicRoughness) {
var parameterName;
Copy link
Contributor

Choose a reason for hiding this comment

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

Minor style note:

var uniformName;
var parameterName;
var pbrMetallicRoughness = material.pbrMetallicRoughness;

looks cleaner than

var uniformName;
var pbrMetallicRoughness = material.pbrMetallicRoughness;
var parameterName;

@lilleyse
Copy link
Contributor

And what is needed for AnalyticalGraphicsInc:gltf-update-pipeline to be merged into master?

There was one failing model (CesiumAir.gltf), but we identified the fix in gltf-pipeline and it just needs to be updated back in Cesium. I believe that was the last thing holding up the PR. @ggetz can confirm.

@@ -88,7 +88,7 @@ define([
return gltf;
}

function isPbrMaterial(material) {
function isMetallicRoughnessMaterial(material) {
Copy link
Contributor

@lilleyse lilleyse Sep 13, 2018

Choose a reason for hiding this comment

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

This should actually not be renamed. The function is meant to catch if PBR shaders need to be generated. It should be catching whether the material uses metallic roughness, spec gloss, unlit, or the top-level 2.0 material properties. The purpose is to turn away materials that are using KHR_techniques_webgl or something else processPbrMaterials doesn't recognize.

One way to test is with a material like:

 {
  "extensions": {
    "KHR_materials_pbrSpecularGlossiness": {}
  }
}

Which I don't think will get shaders generated for it right now.

Copy link
Contributor

Choose a reason for hiding this comment

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

This check probably also belongs before the hasPbrMetallicRoughness check:

        // No need to create new techniques if they already exist,
        // the shader should handle these values
        if (hasExtension(gltf, 'KHR_techniques_webgl')) {
            return gltf;
        }

I feel like a lot of the material existence checking was a little messy before this PR and could use some cleanup.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, good catch! I think it's helpful to have those two functions separately, so I might just add:

function isPbrMaterial(material){
  return isMetallicRoughnessMaterial(material) || isSpecularGlossinessMaterial(material);
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Sounds good - though make sure sure the extreme edge case works too. This should produce a default PBR shader.

"materials": [
  {
  }
]

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Wow, you're right. I didn't realize glTF materials were all PBR by default. That extreme case currently doesn't work in master. It should be good here now.

So isMetallicRoughnessMaterial() isn't needed at all then. If any material is found, then it is metallic roughness, unless spec-gloss is also found.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah it might not be needed at all.

@OmarShehata
Copy link
Contributor Author

Thanks for the cleanup suggestions @lilleyse ! It should be good to merge now.

@OmarShehata
Copy link
Contributor Author

@lilleyse have you gotten a chance to look at this yet/see if it's ready to merge?

Copy link
Contributor

@lilleyse lilleyse left a comment

Choose a reason for hiding this comment

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

Just one small comment and this is ready to merge.


if (!hasPbrMaterial) {
return gltf;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Simpler to just return early if there are no materials.

@OmarShehata
Copy link
Contributor Author

@lilleyse I simplified that check, but to make it compatible with both glTF 2.0 and 1.0 I had to make sure to check if it's an array vs an object.

@lilleyse
Copy link
Contributor

@OmarShehata the 2.0 vs 1.0 distinction isn't necessary. The model will always be 2.0 coming into this function.

@OmarShehata
Copy link
Contributor Author

Ah, that makes it even simpler. Should be good now.

@lilleyse
Copy link
Contributor

Thanks @OmarShehata!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants