diff --git a/src/materials/Material.js b/src/materials/Material.js index 20e556e..6972752 100644 --- a/src/materials/Material.js +++ b/src/materials/Material.js @@ -46,9 +46,9 @@ export default { methods: { propsValues() { const props = {}; - for (const [key, value] of Object.entries(this.$props)) { + Object.entries(this.$props).forEach(([key, value]) => { if (key !== 'id') props[key] = value; - } + }); return props; }, }, diff --git a/src/materials/ShaderMaterial.js b/src/materials/ShaderMaterial.js new file mode 100644 index 0000000..fe5fedf --- /dev/null +++ b/src/materials/ShaderMaterial.js @@ -0,0 +1,25 @@ +import { ShaderMaterial } from 'three'; + +export default { + inject: ['three'], + props: { + id: String, + uniforms: Object, + vertexShader: String, + fragmentShader: String, + }, + mounted() { + if (!this.material) { + // this.material = new ShaderMaterial(this.$props); + } + this.three.materials[this.id] = this.material; + }, + unmounted() { + this.material.dispose(); + }, + methods: { + }, + render() { + return []; + }, +}; diff --git a/src/materials/SubSurfaceMaterial.js b/src/materials/SubSurfaceMaterial.js new file mode 100644 index 0000000..347d3be --- /dev/null +++ b/src/materials/SubSurfaceMaterial.js @@ -0,0 +1,54 @@ +import { Color, ShaderMaterial as TShaderMaterial, UniformsUtils } from 'three'; +import ShaderMaterial from './ShaderMaterial.js'; +import SubsurfaceScatteringShader from './SubsurfaceScatteringShader.js'; + +export default { + extends: ShaderMaterial, + props: { + diffuse: { + type: String, + default: '#ffffff', + }, + thicknessColor: { + type: String, + default: '#ffffff', + }, + thicknessDistortion: { + type: Number, + default: 0.4, + }, + thicknessAmbient: { + type: Number, + default: 0.01, + }, + thicknessAttenuation: { + type: Number, + default: 0.7, + }, + thicknessPower: { + type: Number, + default: 2, + }, + thicknessScale: { + type: Number, + default: 4, + }, + }, + created() { + const params = SubsurfaceScatteringShader; + const uniforms = UniformsUtils.clone(params.uniforms); + Object.entries(this.$props).forEach(([key, value]) => { + if (key === 'diffuse' || key === 'thicknessColor') { + value = new Color(value); + } + if (key !== 'id') uniforms[key].value = value; + }); + + this.material = new TShaderMaterial({ + ...params, + uniforms, + lights: true, + transparent: true, + }); + }, +}; diff --git a/src/materials/SubsurfaceScatteringShader.js b/src/materials/SubsurfaceScatteringShader.js new file mode 100644 index 0000000..49283f0 --- /dev/null +++ b/src/materials/SubsurfaceScatteringShader.js @@ -0,0 +1,69 @@ +import { + Color, + ShaderChunk, + ShaderLib, + UniformsUtils, +} from 'three'; + +function replaceAll(string, find, replace) { + return string.split(find).join(replace); +} + +const meshphongFragHead = ShaderChunk.meshphong_frag.slice(0, ShaderChunk.meshphong_frag.indexOf('void main() {')); +const meshphongFragBody = ShaderChunk.meshphong_frag.slice(ShaderChunk.meshphong_frag.indexOf('void main() {')); + +const SubsurfaceScatteringShader = { + + uniforms: UniformsUtils.merge([ + ShaderLib.phong.uniforms, + { + thicknessColor: { value: new Color(0x668597) }, + 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} + `, + + fragmentShader: ` + #define USE_UV + #define SUBSURFACE + + ${meshphongFragHead} + + 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 GeometricContext geometry, inout ReflectedLight reflectedLight) { + vec3 thickness = thicknessColor; + vec3 scatteringHalf = normalize(directLight.direction + (geometry.normal * thicknessDistortion)); + float scatteringDot = pow(saturate(dot(geometry.viewDir, -scatteringHalf)), thicknessPower) * thicknessScale; + vec3 scatteringIllu = (scatteringDot + thicknessAmbient) * thickness; + reflectedLight.directDiffuse += scatteringIllu * thicknessAttenuation * directLight.color; + } + ` + meshphongFragBody.replace( + '#include ', + replaceAll( + ShaderChunk.lights_fragment_begin, + 'RE_Direct( directLight, geometry, material, reflectedLight );', + ` + RE_Direct( directLight, geometry, material, reflectedLight ); + #if defined( SUBSURFACE ) && defined( USE_UV ) + RE_Direct_Scattering(directLight, vUv, geometry, reflectedLight); + #endif + ` + ) + ), +}; + +export default SubsurfaceScatteringShader; diff --git a/src/materials/index.js b/src/materials/index.js index 9bcd542..4c4a3be 100644 --- a/src/materials/index.js +++ b/src/materials/index.js @@ -3,3 +3,4 @@ export { default as LambertMaterial } from './LambertMaterial.js'; export { default as PhongMaterial } from './PhongMaterial.js'; export { default as PhysicalMaterial } from './PhysicalMaterial.js'; export { default as StandardMaterial } from './StandardMaterial.js'; +export { default as SubSurfaceMaterial } from './SubSurfaceMaterial.js';