Skip to content

Commit 6c582b9

Browse files
committed
build shader hooks from existing API
1 parent 41068cb commit 6c582b9

File tree

2 files changed

+157
-100
lines changed

2 files changed

+157
-100
lines changed

preview/global/sketch.js

Lines changed: 26 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -8,44 +8,42 @@ function calculateOffset() {
88
function setup(){
99
createCanvas(windowWidth, windowHeight, WEBGL);
1010

11-
// Raw example
12-
// myShader = baseMaterialShader().modify(() => {
13-
// const offset = calculateOffset();
11+
// // Raw example
12+
myShader = baseMaterialShader().modify(() => {
1413

15-
// getWorldPosition((pos) => {
16-
// let a = createVector3(1, 2, 3);
17-
// let b = createVector3(3, 4, 5);
14+
const offset = uniformFloat(1);
1815

19-
// a = (a * b + offset) / 10;
20-
21-
// pos += a;
22-
23-
// return pos;
24-
// });
25-
// });
16+
getFinalColor((col) => {
17+
let a = createVector4(1, 2, 3, 4);
18+
let b = createVector4(3, 4, 5, 6);
19+
a = (a * b + offset) / 10;
20+
col += a;
21+
return col;
22+
});
23+
});
2624

2725

2826
// Create and use the custom shader.
29-
myShader = baseMaterialShader().modify(
30-
() => {
31-
const offset = uniformFloat('offset', () => calculateOffset)
27+
// myShader = baseMaterialShader().modify(
28+
// () => {
29+
// const offset = uniformFloat('offset', () => calculateOffset)
3230

33-
getWorldPosition((pos) => {
34-
let a = createVector3(1, 2, 3);
35-
let b = createVector3(3, 4, 5);
36-
a = a.add(b);
31+
// getWorldPosition((pos) => {
32+
// let a = createVector3(1, 2, 3);
33+
// let b = createVector3(3, 4, 5);
34+
// a = a.add(b);
3735

38-
let c = a.add(b);
39-
c += c.add(offset);
40-
c.x = b.x.add(1);
36+
// let c = a.add(b);
37+
// c += c.add(offset);
38+
// c.x = b.x.add(1);
4139

4240

43-
pos = pos.add(c);
41+
// pos = pos.add(c);
4442

45-
return pos;
46-
})
47-
}
48-
);
43+
// return pos;
44+
// })
45+
// }
46+
// );
4947
}
5048

5149
function draw(){

src/webgl/ShaderGen.js

Lines changed: 131 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,7 @@ function shadergen(p5, fn) {
3636
}
3737
}
3838

39-
// Transpiler
40-
39+
// AST Transpiler Callbacks and their helpers
4140
function replaceBinaryOperator(codeSource) {
4241
switch (codeSource) {
4342
case '+': return 'add';
@@ -50,8 +49,7 @@ function shadergen(p5, fn) {
5049

5150
const ASTCallbacks = {
5251
// TODO: automatically making uniforms
53-
Literal(node) {
54-
},
52+
5553

5654
// The callbacks for AssignmentExpression and BinaryExpression handle
5755
// operator overloading including +=, *= assignment expressions
@@ -92,9 +90,8 @@ function shadergen(p5, fn) {
9290

9391

9492
// Javascript Node API.
95-
// These classes are for expressing GLSL functions in Javascript with
93+
// These classes are for expressing GLSL functions in Javascript without
9694
// needing to transpile the user's code.
97-
9895
class BaseNode {
9996
constructor(isInternal) {
10097
if (new.target === BaseNode) {
@@ -125,7 +122,7 @@ function shadergen(p5, fn) {
125122
toGLSLBase(context){
126123
if (this.useTempVar()) {
127124
return this.getTemp(context);
128-
}
125+
}
129126
else {
130127
return this.toGLSL(context);
131128
}
@@ -171,25 +168,6 @@ function shadergen(p5, fn) {
171168
mult(other) { return new BinaryOperatorNode(this, this.enforceType(other), '*'); }
172169
div(other) { return new BinaryOperatorNode(this, this.enforceType(other), '/'); }
173170
mod(other) { return new ModulusNode(this, this.enforceType(other)); }
174-
sin() { return new FunctionCallNode('sin', this, 'float'); }
175-
cos() { return new FunctionCallNode('cos', this, 'float'); }
176-
radians() { return new FunctionCallNode('radians', this, 'float'); }
177-
abs() { return new FunctionCallNode('abs',this, this.type) };
178-
ceil() { return new FunctionCallNode(); }
179-
180-
// TODO:
181-
// Add a whole lot of these functions. Probably should take them out of the primitive node and just attach them to global instead.
182-
// https://docs.gl/el3/
183-
184-
max() { return new FunctionCallNode(); }
185-
min() { return new FunctionCallNode(); }
186-
ceil() { return new FunctionCallNode(); }
187-
round() { return new FunctionCallNode(); }
188-
roundEven() { return new FunctionCallNode(); }
189-
sqrt() { return new FunctionCallNode(); }
190-
log() { return new FunctionCallNode(); }
191-
exp() { return new FunctionCallNode(); }
192-
193171

194172
// Check that the types of the operands are compatible.
195173
// TODO: improve this with less branching if elses
@@ -302,13 +280,10 @@ function shadergen(p5, fn) {
302280
class VectorNode extends BaseNode {
303281
constructor(values, type, isInternal = false) {
304282
super(isInternal);
305-
306283
this.components = ['x', 'y', 'z', 'w'].slice(0, values.length);
307284
this.components.forEach((component, i) => {
308285
this[component] = new FloatNode(values[i], true);
309-
// this[component] = new ComponentNode(this, component, true);
310286
});
311-
312287
this.type = type;
313288
}
314289

@@ -468,16 +443,13 @@ function shadergen(p5, fn) {
468443
}
469444

470445
// TODO: finish If Node
471-
class ConditionalNode {
446+
class ConditionalNode extends BaseNode {
472447
constructor(value) {
448+
super(value);
473449
this.value = value;
474450
this.condition = null;
475451
this.thenBranch = null;
476452
this.elseBranch = null;
477-
}
478-
//helper
479-
checkType(value) {
480-
481453
}
482454
// conditions
483455
equalTo(value){}
@@ -492,12 +464,21 @@ function shadergen(p5, fn) {
492464
// returns
493465
thenReturn(value) {}
494466
elseReturn(value) {}
495-
// Then?
496-
then() {
497-
GLOBAL_SHADER.context.declarations.push()
498-
}
467+
//
468+
thenDiscard() {
469+
new ConditionalDiscard(this.condition);
470+
};
499471
};
500472

473+
class ConditionalDiscard extends Base{
474+
constructor(condition){
475+
this.condition = condition;
476+
}
477+
toGLSL(context) {
478+
context.discardConditions.push(`if(${this.condition}{discard;})`);
479+
}
480+
}
481+
501482
fn.if = function (value) {
502483
return new ConditionalNode(value);
503484
}
@@ -531,47 +512,68 @@ function shadergen(p5, fn) {
531512
// This class is responsible for converting the nodes into an object containing GLSL code, to be used by p5.Shader.modify
532513

533514
class ShaderGenerator {
534-
constructor(modifyFunction, shaderToModify) {
535-
this.modifyFunction = modifyFunction;
536-
this.shaderToModify = shaderToModify;
537-
shaderToModify.inspectHooks();
515+
constructor(modifyFunction, originalShader) {
538516
GLOBAL_SHADER = this;
539-
this.uniforms = {};
540-
this.functions = {};
517+
this.modifyFunction = modifyFunction;
518+
this.generateHookBuilders(originalShader);
519+
this.output = {
520+
uniforms: {},
521+
}
541522
this.resetGLSLContext();
542523
}
524+
543525
callModifyFunction() {
544526
this.modifyFunction();
545-
return {
546-
uniforms: this.uniforms,
547-
functions: this.functions,
548-
vertex: this.functions.getWorldPosition,
549-
fragment: this.functions.getFinalColor,
527+
return this.output;
528+
}
529+
530+
generateHookBuilders(originalShader) {
531+
const availableHooks = {
532+
...originalShader.hooks.vertex,
533+
...originalShader.hooks.fragment,
550534
}
535+
536+
// Defines a function for each of the hooks for the shader we are modifying.
537+
Object.keys(availableHooks).forEach((hookName) => {
538+
const hookTypes = originalShader.hookTypes(hookName)
539+
this[hookTypes.name] = function(userOverride) {
540+
let hookArgs = []
541+
let argsArray = [];
542+
543+
hookTypes.parameters.forEach((parameter, i) => {
544+
hookArgs.push(
545+
new VariableNode(parameter.name, parameter.type.typeName, true)
546+
);
547+
if (i === 0) {
548+
argsArray.push(`${parameter.type.typeName} ${parameter.name}`);
549+
} else {
550+
argsArray.push(`, ${parameter.type.typeName} ${parameter.name}`);
551+
}
552+
})
553+
554+
const toGLSLResult = userOverride(...hookArgs).toGLSLBase(this.context);
555+
let codeLines = [`(${argsArray.join(', ')}) {`, this.context.declarations.slice()].flat();
556+
codeLines.push(`\n${hookTypes.returnType.typeName} finalReturnValue = ${toGLSLResult};
557+
\nreturn finalReturnValue;
558+
\n}`);
559+
this.output[hookName] = codeLines.join('\n');
560+
this.resetGLSLContext();
561+
}
562+
563+
// Expose the Functions to global scope for users to use
564+
window[hookTypes.name] = function(userOverride) {
565+
GLOBAL_SHADER[hookTypes.name](userOverride);
566+
};
567+
})
551568
}
569+
552570
resetGLSLContext() {
553571
this.context = {
554572
id: 0,
555573
getNextID: function() { return this.id++ },
556574
declarations: [],
557575
}
558576
}
559-
// TODO:
560-
buildFunction(argumentName, argumentType, callback) {
561-
let functionArgument = new VariableNode(argumentName, argumentType, true);
562-
const finalLine = callback(functionArgument).toGLSLBase(this.context);
563-
let codeLines = this.context.declarations.slice();
564-
codeLines.push(`\n${argumentName} = ${finalLine}; \nreturn ${argumentName};`)
565-
this.resetGLSLContext();
566-
return codeLines.join("\n");
567-
}
568-
569-
getWorldPosition(func) {
570-
this.functions.getWorldPosition = this.buildFunction("pos", "vec3", func);
571-
}
572-
getFinalColor(func) {
573-
this.functions.getFinalColor = this.buildFunction("col", "vec3", func);
574-
}
575577
}
576578

577579
// User functions
@@ -629,20 +631,77 @@ function shadergen(p5, fn) {
629631
for (const type in uniformFns) {
630632
const uniformFnVariant = `uniform${uniformFns[type]}`;
631633
ShaderGenerator.prototype[uniformFnVariant] = function(name, defaultValue) {
632-
this.uniforms[`${type} ${name}`] = defaultValue;
634+
this.output.uniforms[`${type} ${name}`] = defaultValue;
633635
return new VariableNode(name, type);
634636
};
635637
fn[uniformFnVariant] = function (name, value) {
636638
return GLOBAL_SHADER[uniformFnVariant](name, value);
637639
};
638640
}
639641

640-
function getWorldPosition(func){
641-
GLOBAL_SHADER.getWorldPosition(func)
642-
}
643-
644-
function getFinalColor(func){
645-
GLOBAL_SHADER.getFinalColor(func)
642+
// GLSL Built in functions
643+
// TODO:
644+
// Add a whole lot of these functions.
645+
// https://docs.gl/el3/abs
646+
const builtInFunctions = {
647+
// Trigonometry
648+
'acos': {},
649+
'acosh': {},
650+
'asin': {},
651+
'asinh': {},
652+
'atan': {},
653+
'atanh': {},
654+
'cos': {},
655+
'cosh': {},
656+
'degrees': {},
657+
'radians': {},
658+
'sin': {},
659+
'sinh': {},
660+
'tan': {},
661+
'tanh': {},
662+
// Mathematics
663+
'abs': {},
664+
'ceil': {},
665+
'clamp': {},
666+
'dFdx': {},
667+
'dFdy': {},
668+
'exp': {},
669+
'exp2': {},
670+
'floor': {},
671+
'fma': {},
672+
'fract': {},
673+
'fwidth': {},
674+
'inversesqrt': {},
675+
'isinf': {},
676+
'isnan': {},
677+
'log': {},
678+
'log2': {},
679+
'max': {},
680+
'min': {},
681+
'mix': {},
682+
'mod': {},
683+
'modf': {},
684+
'pow': {},
685+
'round': {},
686+
'roundEven': {},
687+
'sign': {},
688+
'smoothstep': {},
689+
'sqrt': {},
690+
'step': {},
691+
'trunc': {},
692+
// Vector
693+
'cross': {},
694+
'distance': {},
695+
'dot': {},
696+
'equal': {},
697+
'faceforward': {},
698+
'length': {},
699+
'normalize': {},
700+
'notEqual': {},
701+
'reflect': {},
702+
'refract': {},
703+
// Texture sampling
704+
'texture': {},
646705
}
647706

648707
const oldTexture = p5.prototype.texture;

0 commit comments

Comments
 (0)