Skip to content

Instantly share code, notes, and snippets.

@mattdesl
Last active May 17, 2024 09:48
Show Gist options
  • Save mattdesl/2ee82157a86962347dedb6572142df7c to your computer and use it in GitHub Desktop.
Save mattdesl/2ee82157a86962347dedb6572142df7c to your computer and use it in GitHub Desktop.
fast subsurface scattering in ThreeJS PBR material — see "TRANSLUCENCY" in the frag shader
#define USE_TRANSLUCENCY
#ifdef USE_TRANSLUCENCY
uniform sampler2D thicknessMap;
uniform float thicknessPower;
uniform float thicknessScale;
uniform float thicknessDistortion;
uniform float thicknessAmbient;
uniform vec2 thicknessRepeat;
#endif
#define PHYSICAL
uniform vec3 diffuse;
uniform vec3 emissive;
uniform float roughness;
uniform float metalness;
uniform float opacity;
#ifndef STANDARD
uniform float clearCoat;
uniform float clearCoatRoughness;
#endif
uniform float envMapIntensity; // temporary
varying vec3 vViewPosition;
#ifndef FLAT_SHADED
varying vec3 vNormal;
#endif
#include <common>
#include <packing>
#include <color_pars_fragment>
#include <uv_pars_fragment>
#include <uv2_pars_fragment>
#include <map_pars_fragment>
#include <alphamap_pars_fragment>
#include <aomap_pars_fragment>
#include <lightmap_pars_fragment>
#include <emissivemap_pars_fragment>
#include <envmap_pars_fragment>
#include <fog_pars_fragment>
#include <bsdfs>
#include <cube_uv_reflection_fragment>
#include <lights_pars>
#include <lights_physical_pars_fragment>
#include <shadowmap_pars_fragment>
#include <bumpmap_pars_fragment>
#include <normalmap_pars_fragment>
#include <roughnessmap_pars_fragment>
#include <metalnessmap_pars_fragment>
#include <logdepthbuf_pars_fragment>
#include <clipping_planes_pars_fragment>
void main() {
#include <clipping_planes_fragment>
vec4 diffuseColor = vec4( diffuse, opacity );
ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );
vec3 totalEmissiveRadiance = emissive;
#include <logdepthbuf_fragment>
#include <map_fragment>
#include <color_fragment>
#include <alphamap_fragment>
#include <alphatest_fragment>
#include <specularmap_fragment>
#include <roughnessmap_fragment>
#include <metalnessmap_fragment>
#include <normal_flip>
#include <normal_fragment>
#include <emissivemap_fragment>
// accumulation
#include <lights_physical_fragment>
#include <lights_template>
#ifdef USE_TRANSLUCENCY
vec3 thicknessColor = vec3(1.0, 1.0, 1.0);
vec3 thickness = thicknessColor * texture2D(thicknessMap, vUv * thicknessRepeat).r;
vec3 N = geometry.normal;
vec3 V = normalize(geometry.viewDir);
float thicknessCutoff = 0.75;
float thicknessDecay = 1.0;
#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )
for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {
pointLight = pointLights[ i ];
vec3 vLightDir = pointLight.position - geometry.position;
vec3 L = normalize(vLightDir);
float lightDist = length(vLightDir);
float lightAtten = punctualLightIntensityToIrradianceFactor(lightDist, pointLight.distance, pointLight.decay);
vec3 LTLight = normalize(L + (N * thicknessDistortion));
float LTDot = pow(saturate(dot(V, -LTLight)), thicknessPower) * thicknessScale;
vec3 LT = lightAtten * (LTDot + thicknessAmbient) * thickness;
reflectedLight.directDiffuse += material.diffuseColor * pointLight.color * LT;
}
#endif
#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea )
for ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) {
rectAreaLight = rectAreaLights[ i ];
vec3 vLightDir = rectAreaLight.position - geometry.position;
vec3 L = normalize(vLightDir);
float lightDist = length(vLightDir);
float lightAtten = punctualLightIntensityToIrradianceFactor(lightDist, thicknessCutoff, thicknessDecay);
vec3 LTLight = normalize(L + (N * thicknessDistortion));
float LTDot = pow(saturate(dot(V, -LTLight)), thicknessPower) * thicknessScale;
vec3 LT = lightAtten * (LTDot + thicknessAmbient) * thickness;
reflectedLight.directDiffuse += material.diffuseColor * rectAreaLight.color * LT;
}
#endif
#endif
// modulation
#include <aomap_fragment>
vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;
gl_FragColor = vec4( outgoingLight, diffuseColor.a );
#include <premultiplied_alpha_fragment>
#include <tonemapping_fragment>
#include <encodings_fragment>
#include <fog_fragment>
}
const glslify = require('glslify');
const path = require('path');
const assign = require('object-assign');
const defined = require('defined');
// This is the original source, we will copy + paste it for our own GLSL
// const vertexShader = THREE.ShaderChunk.meshphysical_vert;
// const fragmentShader = THREE.ShaderChunk.meshphysical_frag;
// Our custom shaders
const fragmentShader = glslify(path.resolve(__dirname, 'ice.frag'));
const vertexShader = glslify(path.resolve(__dirname, 'ice.vert'));
module.exports = IceMaterial;
function IceMaterial (parameters) {
parameters = assign({}, parameters);
THREE.MeshStandardMaterial.call(this);
this.uniforms = assign({},
THREE.ShaderLib.standard.uniforms,
{
// your custom uniforms or overrides to built-ins
time: { type: 'f', value: 0 },
thicknessMap: { type: 't', value: parameters.thicknessMap || new THREE.Texture() },
thicknessRepeat: { type: 'v3', value: parameters.thicknessRepeat || new THREE.Vector2() },
thicknessPower: { type: 'f', value: 20 },
thicknessScale: { type: 'f', value: 4 },
thicknessDistortion: { type: 'f', value: 0.185 },
thicknessAmbient: { type: 'f', value: 0.0 },
}
);
setFlags(this);
this.setValues(parameters);
}
IceMaterial.prototype = Object.create(THREE.MeshStandardMaterial.prototype);
IceMaterial.prototype.constructor = IceMaterial;
IceMaterial.prototype.isMeshStandardMaterial = true;
IceMaterial.prototype.copy = function (source) {
THREE.MeshStandardMaterial.prototype.copy.call(this, source);
this.uniforms = THREE.UniformsUtils.clone(source.uniforms);
setFlags(this);
return this;
};
function setFlags (material) {
material.vertexShader = vertexShader;
material.fragmentShader = fragmentShader;
material.type = 'IceMaterial';
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment