91 lines
3.2 KiB
JavaScript
91 lines
3.2 KiB
JavaScript
import {
|
||
Color,
|
||
ShaderChunk,
|
||
ShaderLib,
|
||
UniformsUtils
|
||
} from 'three';
|
||
|
||
/**
|
||
* ------------------------------------------------------------------------------------------
|
||
* Subsurface Scattering shader
|
||
* Based on GDC 2011 – Approximating Translucency for a Fast, Cheap and Convincing Subsurface Scattering Look
|
||
* https://colinbarrebrisebois.com/2011/03/07/gdc-2011-approximating-translucency-for-a-fast-cheap-and-convincing-subsurface-scattering-look/
|
||
*------------------------------------------------------------------------------------------
|
||
*/
|
||
|
||
function replaceAll( string, find, replace ) {
|
||
|
||
return string.split( find ).join( replace );
|
||
|
||
}
|
||
|
||
const meshphong_frag_head = ShaderChunk[ 'meshphong_frag' ].slice( 0, ShaderChunk[ 'meshphong_frag' ].indexOf( 'void main() {' ) );
|
||
const meshphong_frag_body = ShaderChunk[ 'meshphong_frag' ].slice( ShaderChunk[ 'meshphong_frag' ].indexOf( 'void main() {' ) );
|
||
|
||
const SubsurfaceScatteringShader = {
|
||
|
||
name: 'SubsurfaceScatteringShader',
|
||
|
||
uniforms: UniformsUtils.merge( [
|
||
ShaderLib[ 'phong' ].uniforms,
|
||
{
|
||
'thicknessMap': { value: null },
|
||
'thicknessColor': { value: new Color( 0xffffff ) },
|
||
'thicknessDistortion': { value: 0.1 },
|
||
'thicknessAmbient': { value: 0.0 },
|
||
'thicknessAttenuation': { value: 0.1 },
|
||
'thicknessPower': { value: 2.0 },
|
||
'thicknessScale': { value: 10.0 }
|
||
}
|
||
|
||
] ),
|
||
|
||
vertexShader: [
|
||
'#define USE_UV',
|
||
ShaderChunk[ 'meshphong_vert' ],
|
||
].join( '\n' ),
|
||
|
||
fragmentShader: [
|
||
'#define USE_UV',
|
||
'#define SUBSURFACE',
|
||
|
||
meshphong_frag_head,
|
||
|
||
'uniform sampler2D thicknessMap;',
|
||
'uniform float thicknessPower;',
|
||
'uniform float thicknessScale;',
|
||
'uniform float thicknessDistortion;',
|
||
'uniform float thicknessAmbient;',
|
||
'uniform float thicknessAttenuation;',
|
||
'uniform vec3 thicknessColor;',
|
||
|
||
'void RE_Direct_Scattering(const in IncidentLight directLight, const in vec2 uv, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, inout ReflectedLight reflectedLight) {',
|
||
' vec3 thickness = thicknessColor * texture2D(thicknessMap, uv).r;',
|
||
' vec3 scatteringHalf = normalize(directLight.direction + (geometryNormal * thicknessDistortion));',
|
||
' float scatteringDot = pow(saturate(dot(geometryViewDir, -scatteringHalf)), thicknessPower) * thicknessScale;',
|
||
' vec3 scatteringIllu = (scatteringDot + thicknessAmbient) * thickness;',
|
||
' reflectedLight.directDiffuse += scatteringIllu * thicknessAttenuation * directLight.color;',
|
||
'}',
|
||
|
||
meshphong_frag_body.replace( '#include <lights_fragment_begin>',
|
||
|
||
replaceAll(
|
||
ShaderChunk[ 'lights_fragment_begin' ],
|
||
'RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );',
|
||
[
|
||
'RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );',
|
||
|
||
'#if defined( SUBSURFACE ) && defined( USE_UV )',
|
||
' RE_Direct_Scattering(directLight, vUv, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, reflectedLight);',
|
||
'#endif',
|
||
].join( '\n' )
|
||
),
|
||
|
||
),
|
||
|
||
].join( '\n' ),
|
||
|
||
};
|
||
|
||
export { SubsurfaceScatteringShader };
|