Skip to content

Commit

Permalink
Merge pull request #16971 from DanielSturk/sheen
Browse files Browse the repository at this point in the history
MeshPhysicalMaterial: Added sheen
  • Loading branch information
mrdoob committed Aug 20, 2019
2 parents 52c4153 + 7b14d5c commit 1855c15
Show file tree
Hide file tree
Showing 15 changed files with 282 additions and 8 deletions.
1 change: 1 addition & 0 deletions examples/files.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ var files = {
"webgl_materials_video",
"webgl_materials_video_webcam",
"webgl_materials_wireframe",
"webgl_materials_sheen",
"webgl_math_orientation_transform",
"webgl_mirror",
"webgl_modifier_simplifier",
Expand Down
3 changes: 2 additions & 1 deletion examples/jsm/nodes/materials/StandardNodeMaterial.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ NodeUtils.addShortcuts( StandardNodeMaterial.prototype, 'fragment', [
'ao',
'environment',
'mask',
'position'
'position',
'sheenColor'
] );

export { StandardNodeMaterial };
18 changes: 16 additions & 2 deletions examples/jsm/nodes/materials/nodes/StandardNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -169,16 +169,18 @@ StandardNode.prototype.build = function ( builder ) {
// isolate environment from others inputs ( see TextureNode, CubeTextureNode )
// environment.analyze will detect if there is a need of calculate irradiance

this.environment.analyze( builder, { cache: 'radiance', context: contextEnvironment, slot: 'radiance' } );
this.environment.analyze( builder, { cache: 'radiance', context: contextEnvironment, slot: 'radiance' } );

if ( builder.requires.irradiance ) {

this.environment.analyze( builder, { cache: 'irradiance', context: contextEnvironment, slot: 'irradiance' } );
this.environment.analyze( builder, { cache: 'irradiance', context: contextEnvironment, slot: 'irradiance' } );

}

}

if ( this.sheenColor ) this.sheenColor.analyze( builder );

// build code

var mask = this.mask ? this.mask.flow( builder, 'b' ) : undefined;
Expand Down Expand Up @@ -222,6 +224,8 @@ StandardNode.prototype.build = function ( builder ) {

var clearCoatEnv = useClearCoat && environment ? this.environment.flow( builder, 'c', { cache: 'clearCoat', context: contextClearCoatEnvironment, slot: 'environment' } ) : undefined;

var sheenColor = this.sheenColor ? this.sheenColor.flow( builder, 'c' ) : undefined;

builder.requires.transparent = alpha !== undefined;

builder.addParsCode( [
Expand Down Expand Up @@ -342,6 +346,12 @@ StandardNode.prototype.build = function ( builder ) {

}

if ( sheenColor ) {

output.push( 'material.sheenColor = ' + sheenColor.result + ';' );

}

if ( reflectivity ) {

output.push(
Expand Down Expand Up @@ -515,6 +525,8 @@ StandardNode.prototype.copy = function ( source ) {

if ( source.environment ) this.environment = source.environment;

if ( source.sheenColor ) this.sheenColor = source.sheenColor;

return this;

};
Expand Down Expand Up @@ -559,6 +571,8 @@ StandardNode.prototype.toJSON = function ( meta ) {

if ( this.environment ) data.environment = this.environment.toJSON( meta ).uuid;

if ( this.sheenColor ) data.sheenColor = this.sheenColor.toJSON( meta ).uuid;

}

return data;
Expand Down
Binary file added examples/models/fbx/cloth.fbx
Binary file not shown.
190 changes: 190 additions & 0 deletions examples/webgl_materials_sheen.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
<html lang="en">
<head>
<title>Sheen demo (material property)</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link type="text/css" rel="stylesheet" href="main.css">
<style>
body {
color: #333;
}
</style>
</head>
<body>
<div id="info">Sheen demo by <a href="https://github.com/DanielSturk">DanielSturk</a></div>
<div id="container"></div>

<script src="js/libs/ammo.js"></script>

<script type="module">

import * as THREE from '../build/three.module.js';

import * as Nodes from './jsm/nodes/Nodes.js';

import Stats from './jsm/libs/stats.module.js';
import { GUI } from './jsm/libs/dat.gui.module.js';

import { OrbitControls } from './jsm/controls/OrbitControls.js';

import { FBXLoader } from './jsm/loaders/FBXLoader.js';

// Graphics variables
var camera, controls, scene, renderer, stats;
var directionalLight;
var mesh, sphere, material, nodeMaterial;

var params = {
nodeMaterial: true,
color: new THREE.Color( 255, 0, 127 ),
sheenBRDF: true,
sheenColor: new THREE.Color( 10, 10, 10 ), // corresponds to .04 reflectance
roughness: .9,
exposure: 2,
};

// model
new FBXLoader().load( 'models/fbx/cloth.fbx', function ( loadedModel ) {

mesh = loadedModel.children[0];

init();

} );

function init( ) {

camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 0.2, 2000 );

scene = new THREE.Scene();
scene.background = new THREE.Color( 0xbfd1e5 );

mesh.scale.multiplyScalar( .5 );
scene.add( mesh );

//

material = new THREE.MeshPhysicalMaterial();
material.side = THREE.DoubleSide;
material.metalness = 0;

//

nodeMaterial = new Nodes.StandardNodeMaterial();
nodeMaterial.side = THREE.DoubleSide;
nodeMaterial.metalness = new Nodes.FloatNode( 0 );
nodeMaterial.roughness = new Nodes.FloatNode();
nodeMaterial.color = new Nodes.ColorNode( params.color.clone() );

//

sphere = new THREE.Mesh(
new THREE.SphereBufferGeometry( 1, 100, 100 ),
material
);
scene.add(sphere);

camera.position.set( - 12, 7, 4 );

var container = document.getElementById( 'container' );
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.shadowMap.enabled = true;
container.appendChild( renderer.domElement );

controls = new OrbitControls( camera, renderer.domElement );
controls.target.set( 0, 2, 0 );
controls.update();

directionalLight = new THREE.DirectionalLight( 0xffffff, .5 );
directionalLight.position.set( 0, 10, 0 );
directionalLight.castShadow = true;
directionalLight.add(
new THREE.Mesh(
new THREE.SphereBufferGeometry( .5 ),
new THREE.MeshBasicMaterial( { color: 0xffffff } )
)
);

scene.add( directionalLight );

stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0px';
container.appendChild( stats.dom );

window.addEventListener( 'resize', onWindowResize, false );

var gui = new GUI();

gui.add( params, 'nodeMaterial' );
gui.addColor( params, 'color' );
gui.add( params, 'sheenBRDF' );
gui.addColor( params, 'sheenColor' );
gui.add( params, 'roughness', 0, 1 );
gui.add( params, 'exposure', 0, 3 );
gui.open();

animate();

}

function onWindowResize() {

camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();

renderer.setSize( window.innerWidth, window.innerHeight );

}

function animate() {

requestAnimationFrame( animate );

render();
stats.update();

}

function render() {

mesh.material = sphere.material = params.nodeMaterial
? nodeMaterial
: material;

//

material.sheenColor = params.sheenBRDF
? new THREE.Color().copy( params.sheenColor ).multiplyScalar( 1 / 255 )
: null;

material.color.copy( params.color ).multiplyScalar( 1 / 255 );
material.roughness = params.roughness;

material.needsUpdate = true;

//

nodeMaterial.sheenColor = params.sheenBRDF
? new Nodes.ColorNode( material.sheenColor )
: undefined;

nodeMaterial.color.value.copy( material.color );

nodeMaterial.roughness.value = params.roughness;

nodeMaterial.needsCompile = true;

//

renderer.toneMappingExposure = params.exposure;
renderer.render( scene, camera );

}

</script>

</body>
</html>
7 changes: 5 additions & 2 deletions src/materials/MeshPhysicalMaterial.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,18 @@ import {
MeshStandardMaterialParameters,
MeshStandardMaterial,
} from './MeshStandardMaterial';
import { Color } from './../math/Color';

export interface MeshPhysicalMaterialParameters
extends MeshStandardMaterialParameters {

reflectivity?: number;
clearCoat?: number;
clearCoatRoughness?: number;

sheenColor?: Color;

clearCoatNormalScale?: Vector2;
clearCoatNormalMap?: Texture;

}

export class MeshPhysicalMaterial extends MeshStandardMaterial {
Expand All @@ -26,6 +27,8 @@ export class MeshPhysicalMaterial extends MeshStandardMaterial {
clearCoat: number;
clearCoatRoughness: number;

sheenColor: Color | null;

clearCoatNormalScale: Vector2;
clearCoatNormalMap: Texture | null;

Expand Down
8 changes: 8 additions & 0 deletions src/materials/MeshPhysicalMaterial.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Vector2 } from '../math/Vector2.js';
import { MeshStandardMaterial } from './MeshStandardMaterial.js';
import { Color } from '../math/Color.js';

/**
* @author WestLangley / http://github.com/WestLangley
Expand All @@ -9,6 +10,8 @@ import { MeshStandardMaterial } from './MeshStandardMaterial.js';
* clearCoat: <float>
* clearCoatRoughness: <float>
*
* sheen: <Color>
*
* clearCoatNormalScale: <Vector2>,
* clearCoatNormalMap: new THREE.Texture( <Image> ),
* }
Expand All @@ -27,6 +30,8 @@ function MeshPhysicalMaterial( parameters ) {
this.clearCoat = 0.0;
this.clearCoatRoughness = 0.0;

this.sheenColor = null; // null will disable sheen bsdf

this.clearCoatNormalScale = new Vector2( 1, 1 );
this.clearCoatNormalMap = null;

Expand All @@ -50,6 +55,9 @@ MeshPhysicalMaterial.prototype.copy = function ( source ) {
this.clearCoat = source.clearCoat;
this.clearCoatRoughness = source.clearCoatRoughness;

if ( source.sheenColor ) this.sheenColor = ( this.sheenColor || new Color() ).copy( source.sheenColor );
else this.sheenColor = null;

this.clearCoatNormalMap = source.clearCoatNormalMap;
this.clearCoatNormalScale.copy( source.clearCoatNormalScale );

Expand Down
1 change: 1 addition & 0 deletions src/renderers/WebGLRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2271,6 +2271,7 @@ function WebGLRenderer( parameters ) {

uniforms.clearCoat.value = material.clearCoat;
uniforms.clearCoatRoughness.value = material.clearCoatRoughness;
if ( material.sheenColor ) uniforms.sheenColor.value.copy( material.sheenColor );

if ( material.clearCoatNormalMap ) {

Expand Down
31 changes: 31 additions & 0 deletions src/renderers/shaders/ShaderChunk/bsdfs.glsl.js
Original file line number Diff line number Diff line change
Expand Up @@ -336,4 +336,35 @@ float GGXRoughnessToBlinnExponent( const in float ggxRoughness ) {
float BlinnExponentToGGXRoughness( const in float blinnExponent ) {
return sqrt( 2.0 / ( blinnExponent + 2.0 ) );
}
#if defined( USE_SHEEN )
// https://github.com/google/filament/blob/master/shaders/src/brdf.fs#L94
float D_Charlie(float roughness, float NoH) {
// Estevez and Kulla 2017, "Production Friendly Microfacet Sheen BRDF"
float invAlpha = 1.0 / roughness;
float cos2h = NoH * NoH;
float sin2h = max(1.0 - cos2h, 0.0078125); // 2^(-14/2), so sin2h^2 > 0 in fp16
return (2.0 + invAlpha) * pow(sin2h, invAlpha * 0.5) / (2.0 * PI);
}
// https://github.com/google/filament/blob/master/shaders/src/brdf.fs#L136
float V_Neubelt(float NoV, float NoL) {
// Neubelt and Pettineo 2013, "Crafting a Next-gen Material Pipeline for The Order: 1886"
return saturate(1.0 / (4.0 * (NoL + NoV - NoL * NoV)));
}
vec3 BRDF_Specular_Sheen( const in float roughness, const in vec3 L, const in GeometricContext geometry, vec3 specularColor ) {
vec3 N = geometry.normal;
vec3 V = geometry.viewDir;
vec3 H = normalize( V + L );
float dotNH = saturate( dot( N, H ) );
return specularColor * D_Charlie( roughness, dotNH ) * V_Neubelt( dot(N, V), dot(N, L) );
}
#endif
`;
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,7 @@ material.specularRoughness = clamp( roughnessFactor, 0.04, 1.0 );
material.clearCoat = saturate( clearCoat ); // Burley clearcoat model
material.clearCoatRoughness = clamp( clearCoatRoughness, 0.04, 1.0 );
#endif
#ifdef USE_SHEEN
material.sheenColor = sheenColor;
#endif
`;
Loading

0 comments on commit 1855c15

Please sign in to comment.