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

MeshPhysicalMaterial: Support iridescence / thin-film materials #23869

Merged
merged 18 commits into from
May 25, 2022
Merged
Show file tree
Hide file tree
Changes from 2 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
6 changes: 3 additions & 3 deletions editor/js/Sidebar.Material.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,10 @@ function SidebarMaterial( editor ) {
const materialIridescence = new SidebarMaterialNumberProperty( editor, 'iridescence', strings.getKey( 'sidebar/material/iridescence' ), [ 0, 1 ] );
container.add( materialIridescence );

// iridescenceIOR
// iridescenceIor

const materialIridescenceIOR = new SidebarMaterialNumberProperty( editor, 'iridescenceIOR', strings.getKey( 'sidebar/material/iridescenceIOR' ), [ 1, 5 ] );
container.add( materialIridescenceIOR );
const materialIridescenceIor = new SidebarMaterialNumberProperty( editor, 'iridescenceIor', strings.getKey( 'sidebar/material/iridescenceIor' ), [ 1, 5 ] );
container.add( materialIridescenceIor );

// iridescenceThicknessMax

Expand Down
2 changes: 1 addition & 1 deletion editor/js/Strings.js
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ function Strings( config ) {
'sidebar/material/clearcoat': 'Clearcoat',
'sidebar/material/clearcoatroughness': 'Clearcoat Roughness',
'sidebar/material/iridescence': 'Iridescence',
'sidebar/material/iridescenceIOR': 'Thin-Film IOR',
'sidebar/material/iridescenceIor': 'Thin-Film IOR',
'sidebar/material/iridescenceThicknessMax': 'Thin-Film Thickness',
'sidebar/material/transmission': 'Transmission',
'sidebar/material/attenuationDistance': 'Attenuation Distance',
Expand Down
4 changes: 2 additions & 2 deletions examples/js/loaders/GLTFLoader.js
Original file line number Diff line number Diff line change
Expand Up @@ -667,9 +667,9 @@

}

if ( extension.iridescenceIOR !== undefined ) {
if ( extension.iridescenceIor !== undefined ) {

materialParams.iridescenceIOR = extension.iridescenceIOR;
materialParams.iridescenceIor = extension.iridescenceIor;

}

Expand Down
2 changes: 1 addition & 1 deletion examples/jsm/exporters/GLTFExporter.js
Original file line number Diff line number Diff line change
Expand Up @@ -2400,7 +2400,7 @@ class GLTFMaterialsIridescenceExtension {

}

extensionDef.iridescenceIOR = material.iridescenceIOR;
extensionDef.iridescenceIor = material.iridescenceIor;
extensionDef.iridescenceThicknessMinimum = material.iridescenceThicknessRange[ 0 ];
extensionDef.iridescenceThicknessMaximum = material.iridescenceThicknessRange[ 1 ];

Expand Down
4 changes: 2 additions & 2 deletions examples/jsm/loaders/GLTFLoader.js
Original file line number Diff line number Diff line change
Expand Up @@ -781,9 +781,9 @@ class GLTFMaterialsIridescenceExtension {

}

if ( extension.iridescenceIOR !== undefined ) {
if ( extension.iridescenceIor !== undefined ) {

materialParams.iridescenceIOR = extension.iridescenceIOR;
materialParams.iridescenceIor = extension.iridescenceIor;

}

Expand Down
10 changes: 5 additions & 5 deletions examples/jsm/renderers/webgl/nodes/WebGLNodeBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,9 @@ class WebGLNodeBuilder extends NodeBuilder {

}

if ( material.iridescenceIORNode && material.iridescenceIORNode.isNode ) {
if ( material.iridescenceIorNode && material.iridescenceIorNode.isNode ) {

this.addSlot( 'fragment', new SlotNode( material.iridescenceIORNode, 'IRIDESCENCE_IOR', 'float' ) );
this.addSlot( 'fragment', new SlotNode( material.iridescenceIorNode, 'IRIDESCENCE_IOR', 'float' ) );

}

Expand Down Expand Up @@ -430,7 +430,7 @@ ${this.shader[ getShaderStageProperty( shaderStage ) ]}
const clearcoatNode = this.getSlot( 'fragment', 'CLEARCOAT' );
const clearcoatRoughnessNode = this.getSlot( 'fragment', 'CLEARCOAT_ROUGHNESS' );
const iridescenceNode = this.getSlot( 'fragment', 'IRIDESCENCE' );
const iridescenceIORNode = this.getSlot( 'fragment', 'IRIDESCENCE_IOR' );
const iridescenceIorNode = this.getSlot( 'fragment', 'IRIDESCENCE_IOR' );
const iridescenceThicknessNode = this.getSlot( 'fragment', 'IRIDESCENCE_THICKNESS' );

const positionNode = this.getSlot( 'vertex', 'POSITION' );
Expand Down Expand Up @@ -526,12 +526,12 @@ ${this.shader[ getShaderStageProperty( shaderStage ) ]}

}

if ( iridescenceIORNode !== undefined ) {
if ( iridescenceIorNode !== undefined ) {

this.addCodeAfterSnippet(
'fragment',
'iridescence_fragment',
`${iridescenceIORNode.code}\n\tmaterial.iridescenceIOR = ${iridescenceIORNode.result};`
`${iridescenceIorNode.code}\n\tmaterial.iridescenceIor = ${iridescenceIorNode.result};`
);

}
Expand Down
2 changes: 1 addition & 1 deletion src/loaders/MaterialLoader.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ class MaterialLoader extends Loader {
if ( json.clearcoat !== undefined ) material.clearcoat = json.clearcoat;
if ( json.clearcoatRoughness !== undefined ) material.clearcoatRoughness = json.clearcoatRoughness;
if ( json.iridescence !== undefined ) material.iridescence = json.iridescence;
if ( json.iridescenceIOR !== undefined ) material.iridescenceIOR = json.iridescenceIOR;
if ( json.iridescenceIor !== undefined ) material.iridescenceIor = json.iridescenceIor;
if ( json.iridescenceThicknessRange !== undefined ) material.iridescenceThicknessRange = json.iridescenceThicknessRange;
if ( json.transmission !== undefined ) material.transmission = json.transmission;
if ( json.thickness !== undefined ) material.thickness = json.thickness;
Expand Down
2 changes: 1 addition & 1 deletion src/materials/Material.js
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ class Material extends EventDispatcher {
}

if ( this.iridescence !== undefined ) data.iridescence = this.iridescence;
if ( this.iridescenceIOR !== undefined ) data.iridescenceIOR = this.iridescenceIOR;
if ( this.iridescenceIor !== undefined ) data.iridescenceIor = this.iridescenceIor;
if ( this.iridescenceThicknessRange !== undefined ) data.iridescenceThicknessRange = this.iridescenceThicknessRange;

if ( this.iridescenceMap && this.iridescenceMap.isTexture ) {
Expand Down
4 changes: 2 additions & 2 deletions src/materials/MeshPhysicalMaterial.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class MeshPhysicalMaterial extends MeshStandardMaterial {
} );

this.iridescenceMap = null;
this.iridescenceIOR = 1.3;
Copy link
Collaborator

Choose a reason for hiding this comment

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

@PascalSchoen sorry for the churn here, but glTF's camelCase convention for acronyms like XYZ (fooBarXyz) is different from what we follow in three.js (fooBarXYZ). I think for the public API of MeshPhysicalMaterial, this should remain iridescenceIOR. Feel free to use whichever you prefer for internal code in the rest of the PR. 🙏

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@donmccurdy I reverted the change and only kept it for the glTF extension parts. 🙂

Copy link
Collaborator

Choose a reason for hiding this comment

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

@donmccurdy You prefer using .ior and .iridescenceIOR?

Note, we also have the function applyIorToRoughness().

Copy link
Collaborator

@donmccurdy donmccurdy Apr 28, 2022

Choose a reason for hiding this comment

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

I think so, yes — for example we use:

  • attribute.getXYZ not .getXyz
  • attribute.x not attribute.X
  • GLTFLoader not GltfLoader

I'm comfortable with either convention but this seems to be the norm in three.js today, and (I think?) more common/idiomatic in JavaScript generally.

Copy link
Collaborator

Choose a reason for hiding this comment

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

OK, I don't have a strong feeling on this one. Maybe applyIORToRoughness()?

There is also the option of .refractiveIndex instead of .ior.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Slight preference for applyIORToRoughness, yes - but not as worried about internal methods.

Somehow .ior does not bother me, feels similar to vec.xyz in GLSL. 🤷‍♂️

this.iridescenceIor = 1.3;
this.iridescenceThicknessRange = [ 100, 400 ];
this.iridescenceThicknessMap = null;

Expand Down Expand Up @@ -164,7 +164,7 @@ class MeshPhysicalMaterial extends MeshStandardMaterial {

this.iridescence = source.iridescence;
this.iridescenceMap = source.iridescenceMap;
this.iridescenceIOR = source.iridescenceIOR;
this.iridescenceIor = source.iridescenceIor;
this.iridescenceThicknessRange = [ ...source.iridescenceThicknessRange ];
this.iridescenceThicknessMap = source.iridescenceThicknessMap;

Expand Down
24 changes: 12 additions & 12 deletions src/renderers/shaders/ShaderChunk/iridescence_fragment.glsl.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,13 @@ vec3 evalSensitivity( float OPD, vec3 shift ) {
return srgb;
}

vec3 evalIridescence( float outsideIOR, float eta2, float cosTheta1, float thinFilmThickness, vec3 baseF0 ) {
vec3 evalIridescence( float outsideIor, float eta2, float cosTheta1, float thinFilmThickness, vec3 baseF0 ) {
vec3 I;

// Force iridescenceIOR -> outsideIOR when thinFilmThickness -> 0.0
float iridescenceIOR = mix( outsideIOR, eta2, smoothstep( 0.0, 0.03, thinFilmThickness ) );
// Force iridescenceIor -> outsideIor when thinFilmThickness -> 0.0
float iridescenceIor = mix( outsideIor, eta2, smoothstep( 0.0, 0.03, thinFilmThickness ) );
// Evaluate the cosTheta on the base layer (Snell law)
float sinTheta2Sq = pow2( outsideIOR / iridescenceIOR ) * ( 1.0 - pow2( cosTheta1 ) );
float sinTheta2Sq = pow2( outsideIor / iridescenceIor ) * ( 1.0 - pow2( cosTheta1 ) );

// Handle TIR:
float cosTheta2Sq = 1.0 - sinTheta2Sq;
Expand All @@ -60,25 +60,25 @@ vec3 evalIridescence( float outsideIOR, float eta2, float cosTheta1, float thinF
float cosTheta2 = sqrt( cosTheta2Sq );

// First interface
float R0 = IorToFresnel0( iridescenceIOR, outsideIOR );
float R0 = IorToFresnel0( iridescenceIor, outsideIor );
float R12 = F_Schlick( R0, 1.0, cosTheta1 );
float R21 = R12;
float T121 = 1.0 - R12;
float phi12 = 0.0;
if ( iridescenceIOR < outsideIOR ) phi12 = PI;
if ( iridescenceIor < outsideIor ) phi12 = PI;
float phi21 = PI - phi12;

// Second interface
vec3 baseIOR = Fresnel0ToIor( clamp( baseF0, 0.0, 0.9999 ) ); // guard against 1.0
vec3 R1 = IorToFresnel0( baseIOR, iridescenceIOR );
vec3 baseIor = Fresnel0ToIor( clamp( baseF0, 0.0, 0.9999 ) ); // guard against 1.0
vec3 R1 = IorToFresnel0( baseIor, iridescenceIor );
vec3 R23 = F_Schlick( R1, 1.0, cosTheta2 );
vec3 phi23 = vec3( 0.0 );
if ( baseIOR[0] < iridescenceIOR ) phi23[0] = PI;
if ( baseIOR[1] < iridescenceIOR ) phi23[1] = PI;
if ( baseIOR[2] < iridescenceIOR ) phi23[2] = PI;
if ( baseIor[0] < iridescenceIor ) phi23[0] = PI;
if ( baseIor[1] < iridescenceIor ) phi23[1] = PI;
if ( baseIor[2] < iridescenceIor ) phi23[2] = PI;

// Phase shift
float OPD = 2.0 * iridescenceIOR * thinFilmThickness * cosTheta2;
float OPD = 2.0 * iridescenceIor * thinFilmThickness * cosTheta2;
vec3 phi = vec3( phi21 ) + phi23;

// Compound terms
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ if ( material.iridescenceThickness == 0.0 ) {

if ( material.iridescence > 0.0 ) {

material.iridescenceFresnel = evalIridescence( 1.0, material.iridescenceIOR, dotNV, material.iridescenceThickness, material.specularColor );
material.iridescenceFresnel = evalIridescence( 1.0, material.iridescenceIor, dotNV, material.iridescenceThickness, material.specularColor );

// Iridescence F0 approximation
material.iridescenceF0 = Schlick_to_F0( material.iridescenceFresnel, 1.0, dotNV );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ material.roughness = min( material.roughness, 1.0 );
#ifdef USE_IRIDESCENCE

material.iridescence = iridescence;
material.iridescenceIOR = iridescenceIOR;
material.iridescenceIor = iridescenceIor;

#ifdef USE_IRIDESCENCEMAP

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ struct PhysicalMaterial {

#ifdef USE_IRIDESCENCE
float iridescence;
float iridescenceIOR;
float iridescenceIor;
float iridescenceThickness;
vec3 iridescenceFresnel;
vec3 iridescenceF0;
Expand Down Expand Up @@ -220,7 +220,7 @@ void RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 irradia

#ifdef USE_IRIDESCENCE

computeMultiscatteringIridescence( geometry.normal, geometry.viewDir, material.specularColor, material.specularF90, material.iridescence, material.iridescenceF0, material.roughness, singleScattering, multiScattering );
computeMultiscatteringIridescence( geometry.normal, geometry.viewDir, material.specularColor, material.specularF90, material.iridescence, material.iridescenceFresnel, material.roughness, singleScattering, multiScattering );

#else

Expand Down
2 changes: 1 addition & 1 deletion src/renderers/shaders/ShaderLib.js
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ ShaderLib.physical = {
clearcoatNormalMap: { value: null },
iridescence: { value: 0 },
iridescenceMap: { value: null },
iridescenceIOR: { value: 1.3 },
iridescenceIor: { value: 1.3 },
iridescenceThicknessMinimum: { value: 100 },
iridescenceThicknessMaximum: { value: 400 },
iridescenceThicknessMap: { value: null },
Expand Down
2 changes: 1 addition & 1 deletion src/renderers/shaders/ShaderLib/meshphysical.glsl.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ uniform float opacity;

#ifdef USE_IRIDESCENCE
uniform float iridescence;
uniform float iridescenceIOR;
uniform float iridescenceIor;
uniform float iridescenceThicknessMinimum;
uniform float iridescenceThicknessMaximum;
#endif
Expand Down
2 changes: 1 addition & 1 deletion src/renderers/webgl/WebGLMaterials.js
Original file line number Diff line number Diff line change
Expand Up @@ -589,7 +589,7 @@ function WebGLMaterials( renderer, properties ) {
if ( material.iridescence > 0 ) {

uniforms.iridescence.value = material.iridescence;
uniforms.iridescenceIOR.value = material.iridescenceIOR;
uniforms.iridescenceIor.value = material.iridescenceIor;
uniforms.iridescenceThicknessMinimum.value = material.iridescenceThicknessRange[ 0 ];
uniforms.iridescenceThicknessMaximum.value = material.iridescenceThicknessRange[ 1 ];

Expand Down