Skip to content

Instantly share code, notes, and snippets.

@dghez
Last active May 13, 2022 22:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dghez/93a93f0b0946145ea4f405959c55ea96 to your computer and use it in GitHub Desktop.
Save dghez/93a93f0b0946145ea4f405959c55ea96 to your computer and use it in GitHub Desktop.
Extend material threejs
// inspired by https://github.com/jamieowen/three-material-modifier
// Kudos jam3
// typescript, need to convert to js soon
import { UniformsUtils, IUniform } from 'three';
export type Shader = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
uniforms: { [uniform: string]: IUniform<any> };
vertexShader: string;
fragmentShader: string;
};
type ShaderProps = {
[prop: string]: string;
};
export type ShaderConfig = {
uniforms: {
[prop: string]: Object;
};
vertexShader: { uniforms: string; functions: string; [prop: string]: string };
fragmentShader: { uniforms: string; functions: string; [prop: string]: string };
};
const hooks = {
vertex: {
preTransform: 'before:#include <begin_vertex>\n',
postTransform: 'after:#include <project_vertex>\n',
preNormal: 'before:#include <beginnormal_vertex>\n'
},
fragment: {
outgoingLightColor:
'after:vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n',
fragColor: 'after:#include <dithering_fragment>\n'
}
};
function replace(shader: string, hooks: ShaderProps, config: ShaderProps) {
Object.keys(hooks).forEach((hook: string) => {
if (config[hook] != null) {
const parts = hooks[hook].split(':');
const line = parts[1];
switch (parts[0]) {
case 'after': {
shader = shader.replace(
line,
`${line}
${config[hook]}`
);
break;
}
default: {
// before
shader = shader.replace(
line,
`${config[hook]}
${line}`
);
break;
}
}
}
});
return shader;
}
/**
* Modify threejs built in materials
*
* @export
* @param {Shader} shader
* @param {ShaderConfig} config
* @return {*}
*/
export default function materialModifier(shader: Shader, config: ShaderConfig) {
shader.uniforms = UniformsUtils.merge([shader.uniforms, config.uniforms]);
shader.vertexShader = `
${config.vertexShader.uniforms}
${config.vertexShader.functions}
${shader.vertexShader}
`;
shader.fragmentShader = `
${config.fragmentShader.uniforms}
${config.fragmentShader.functions}
${shader.fragmentShader}
`;
shader.vertexShader = replace(shader.vertexShader, hooks.vertex, config.vertexShader);
shader.fragmentShader = replace(shader.fragmentShader, hooks.fragment, config.fragmentShader);
return shader;
}
const material = new MeshLambertMaterial({ transparent: true, opacity: 1 });
material.onBeforeCompile = (shader: Shader) => {
this.shader = materialModifier(shader, shaderConfig);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment