From c14c385fd4e88d5a78ad4cbb6b9d4b26c2ab2874 Mon Sep 17 00:00:00 2001 From: Dave Pagurek Date: Sun, 6 Apr 2025 15:50:07 -0400 Subject: [PATCH 1/5] Rename createVec4 to vec4 --- src/webgl/ShaderGenerator.js | 132 +++++++++++++++++++---------------- 1 file changed, 70 insertions(+), 62 deletions(-) diff --git a/src/webgl/ShaderGenerator.js b/src/webgl/ShaderGenerator.js index b1b75cd251..722689cff0 100644 --- a/src/webgl/ShaderGenerator.js +++ b/src/webgl/ShaderGenerator.js @@ -76,7 +76,7 @@ function shadergenerator(p5, fn) { type: 'Literal', value: node.operator, } - + const standardReplacement = (node) => { node.type = 'CallExpression' node.callee = { @@ -93,11 +93,11 @@ function shadergenerator(p5, fn) { ['r', 'g', 'b', 'a'], ['s', 't', 'p', 'q'] ]; - + let isSwizzle = swizzleSets.some(set => [...property].every(char => set.includes(char)) ) && node.argument.type === 'MemberExpression'; - + if (isSwizzle) { node.type = 'MemberExpression'; node.object = { @@ -114,7 +114,7 @@ function shadergenerator(p5, fn) { }; } else { standardReplacement(node); - } + } } else { standardReplacement(node); } @@ -139,7 +139,7 @@ function shadergenerator(p5, fn) { } }, Identifier(node, _state, _ancestors) { - if (_state.varyings[node.name] + if (_state.varyings[node.name] && !_ancestors.some(a => a.type === 'AssignmentExpression' && a.left === node)) { node.type = 'ExpressionStatement'; node.expression = { @@ -297,7 +297,7 @@ function shadergenerator(p5, fn) { this.useTemp = true; } - assertUsedInConditional(branch) { + assertUsedInConditional(branch) { this.usedInConditional = true; this.usedIn.push(branch); this.forceTemporaryVariable(); @@ -316,8 +316,8 @@ function shadergenerator(p5, fn) { statement.dependsOnSatisfied.push(this); } if (statement.usedIn.includes(this) && !statement.usedInSatisfied.includes(this)) { - statement.usedInSatisfied.push(this); - } + statement.usedInSatisfied.push(this); + } if (isDepsSatisfied() && isUsedSatisfied()) { statement.saveState(context, isDepsSatisfied(), isUsedSatisfied()); } @@ -334,7 +334,7 @@ function shadergenerator(p5, fn) { diff = diff > 0 ? diff : undefined; this.dependsOn.forEach(dependency => { if (dependency.isVector) { - const dependencies = dependency.originalComponents.map((component, i) => + const dependencies = dependency.originalComponents.map((component, i) => component === dependency.currentComponents[i] ); context.updateComponents(dependency.node, diff, dependencies); @@ -345,14 +345,14 @@ function shadergenerator(p5, fn) { } else { result = this.toGLSL(context); } - this.checkConditionalDependencies(context) + this.checkConditionalDependencies(context) return result; } shouldUseTemporaryVariable() { if (this.componentsChanged || hasTemporaryVariable(this) || this.useTemp) { return true; } if (this.isInternal || isVariableNode(this) || isConditionalNode(this) || this.type === 'sampler2D') { return false; } - + // return false; // Swizzles must use temporary variables as otherwise they will not be registered let score = 0; @@ -480,7 +480,7 @@ function shadergenerator(p5, fn) { this.originalValues = conformVectorParameters(values, parseInt(type.slice(3))); this.componentNames = ['x', 'y', 'z', 'w'].slice(0, this.originalValues.length); } - + addVectorComponents() { const values = this.originalValues; this.componentsChanged = false; @@ -556,8 +556,8 @@ function shadergenerator(p5, fn) { const findBestOverload = function (best, current) { current = determineFunctionSignature(current); if (!current.valid) { return best; } - if (!best || current.similarity > best.similarity) { - best = current; + if (!best || current.similarity > best.similarity) { + best = current; } return best; } @@ -568,7 +568,7 @@ function shadergenerator(p5, fn) { if (!functionSignature || !functionSignature.valid) { const argsStrJoin = (args) => `(${args.map((arg) => arg).join(', ')})`; - const expectedArgsString = Array.isArray(properties) ? + const expectedArgsString = Array.isArray(properties) ? properties.map(prop => argsStrJoin(prop.args)).join(' or ') : argsStrJoin(properties.args); const providedArgsString = argsStrJoin(userArgs.map((a)=>getType(a))); @@ -645,7 +645,7 @@ function shadergenerator(p5, fn) { } } - // + // class VaryingNode extends VariableNode { constructor(name, type, isInternal = false) { super(name, type, isInternal); @@ -771,36 +771,36 @@ function shadergenerator(p5, fn) { // Conditions and logical modifiers BaseNode.prototype.equalTo = function(other) { - return binaryExpressionNodeConstructor(this, this.enforceType(other), '=='); + return binaryExpressionNodeConstructor(this, this.enforceType(other), '=='); } BaseNode.prototype.greaterThan = function(other) { - return binaryExpressionNodeConstructor(this, this.enforceType(other), '>'); + return binaryExpressionNodeConstructor(this, this.enforceType(other), '>'); } BaseNode.prototype.greaterThanEqualTo = function(other) { - return binaryExpressionNodeConstructor(this, this.enforceType(other), '>='); + return binaryExpressionNodeConstructor(this, this.enforceType(other), '>='); } BaseNode.prototype.lessThan = function(other) { - return binaryExpressionNodeConstructor(this, this.enforceType(other), '<'); + return binaryExpressionNodeConstructor(this, this.enforceType(other), '<'); } - + BaseNode.prototype.lessThanEqualTo = function(other) { return binaryExpressionNodeConstructor(this, this.enforceType(other), '<='); } - + BaseNode.prototype.not = function() { - return new UnaryExpressionNode(this.condition, '!', true); + return new UnaryExpressionNode(this.condition, '!', true); } - + BaseNode.prototype.or = function(other) { - return new binaryExpressionNodeConstructor(this, this.enforceType(other), '||', true); + return new binaryExpressionNodeConstructor(this, this.enforceType(other), '||', true); } - + BaseNode.prototype.and = function(other) { - return new binaryExpressionNodeConstructor(this, this.enforceType(other), '&&', true); + return new binaryExpressionNodeConstructor(this, this.enforceType(other), '&&', true); } - + function branch(callback) { const branch = new BranchNode(); callback(); @@ -847,8 +847,8 @@ function shadergenerator(p5, fn) { }; saveState(context, usedInSatisfied, dependsOnSatisfied) { - this.states.push({ - line: context.declarations.length, + this.states.push({ + line: context.declarations.length, usedInSatisfied, dependsOnSatisfied }); @@ -951,13 +951,13 @@ function shadergenerator(p5, fn) { } else { result = value.toGLSLBase(context); } - + if (isVariableNode(node) || hasTemporaryVariable(node)) { statement = `${node.toGLSLBase(context)} = ${result};`; - } + } else if (isFloatNode(node) && node.name) { statement = `${node.parent.toGLSLBase(context)}.${node.name} = ${result};`; - } + } else { node.temporaryVariable = `temp_${context.getNextID()}`; statement = `${node.type} ${node.toGLSLBase(context)} = ${result};` @@ -994,11 +994,11 @@ function shadergenerator(p5, fn) { if (Array.isArray(values)) { for(let val of values) { if (isVectorType(val)) { - length += parseInt(val.type.slice(3)); + length += parseInt(val.type.slice(3)); } else length += 1; } - } + } else if (isVectorType(values)) { length += parseInt(val.type.slice(3)); } @@ -1208,7 +1208,7 @@ function shadergenerator(p5, fn) { }); codeLines.push(...finalVaryingAssignments); } - + codeLines.push(' return finalReturnValue;', '}'); this.output[hookName] = codeLines.join('\n'); this.resetGLSLContext(); @@ -1220,7 +1220,7 @@ function shadergenerator(p5, fn) { GLOBAL_SHADER[hookTypes.name](userOverride); }; }); - + this.cleanup = () => { for (const key in windowOverrides) { @@ -1230,8 +1230,8 @@ function shadergenerator(p5, fn) { } registerVarying(node, value) { - if (!Array.isArray(this.context.varyings[node.name])) { - this.context.varyings[node.name] = []; + if (!Array.isArray(this.context.varyings[node.name])) { + this.context.varyings[node.name] = []; } this.context.varyings[node.name].push({ node, value }); this.output.vertexDeclarations.add(`OUT ${node.type} ${node.name};`); @@ -1303,7 +1303,7 @@ function shadergenerator(p5, fn) { }; } } - + function makeDependencyArray(dependencies) { return dependencies.map(dep => makeDependencyObject(dep)); } @@ -1336,7 +1336,7 @@ function shadergenerator(p5, fn) { ['r', 'g', 'b', 'a'], ['s', 't', 'p', 'q'] ].map(s => s.slice(0, size)); - return { + return { get(target, property, receiver) { if (property in target) { return Reflect.get(...arguments); @@ -1344,7 +1344,7 @@ function shadergenerator(p5, fn) { for (const set of swizzleSets) { if ([...property].every(char => set.includes(char))) { if (property.length === 1) { - return target[swizzleSets[0][set.indexOf(property[0])]] + return target[swizzleSets[0][set.indexOf(property[0])]] } const components = [...property].map(char => { const index = set.indexOf(char); @@ -1376,7 +1376,7 @@ function shadergenerator(p5, fn) { } } } - + // User functions fn.If = function (condition, branch) { return new ConditionalNode(condition, branch); @@ -1390,7 +1390,7 @@ function shadergenerator(p5, fn) { const props = { args: ['sampler2D', 'vec2'], returnType: 'vec4', isp5Function: true }; return fnNodeConstructor('getTexture', userArgs, props); } - + // Generating uniformFloat, uniformVec, createFloat, etc functions // Maps a GLSL type to the name suffix for method names const GLSLTypesToIdentifiers = { @@ -1407,7 +1407,7 @@ function shadergenerator(p5, fn) { const size = _size ? _size : parseInt(node.type.slice(3)); node = new Proxy(node, swizzleTrap(size)); node.addVectorComponents(); - } + } return node; } @@ -1480,8 +1480,8 @@ function shadergenerator(p5, fn) { return GLOBAL_SHADER[uniformMethodName](...args); }; - - // We don't need a createTexture method. + + // We don't need a texture creation method. if (glslType === 'sampler2D') { continue; } const varyingMethodName = `varying${typeIdentifier}`; @@ -1493,16 +1493,24 @@ function shadergenerator(p5, fn) { return GLOBAL_SHADER[varyingMethodName](name); }; - // Generate the create*() Methods for creating variables in shaders - const createMethodName = `create${typeIdentifier}`; - fn[createMethodName] = function (...value) { - if (glslType.startsWith('vec')) { - value = conformVectorParameters(value, parseInt(glslType.slice(3))); + // Generate the creation methods for creating variables in shaders + const originalFn = fn[glslType]; + fn[glslType] = function (...value) { + if (GLOBAL_SHADER?.isGenerating) { + if (glslType.startsWith('vec')) { + value = conformVectorParameters(value, parseInt(glslType.slice(3))); + } else { + value = value[0]; + } + return nodeConstructors[glslType](value); + } else if (originalFn) { + return originalFn.apply(this, value); } else { - value = value[0]; + p5._friendlyError( + `It looks like you've called ${glslType} outside of a shader's modify() function.` + ); } - return nodeConstructors[glslType](value); - } + }; } // GLSL Built in functions @@ -1546,16 +1554,16 @@ function shadergenerator(p5, fn) { 'log': { args: ['genType'], returnType: 'genType', isp5Function: true}, 'log2': { args: ['genType'], returnType: 'genType', isp5Function: false}, 'max': [ - { args: ['genType', 'genType'], returnType: 'genType', isp5Function: true}, - { args: ['genType', 'float'], returnType: 'genType', isp5Function: true}, + { args: ['genType', 'genType'], returnType: 'genType', isp5Function: true}, + { args: ['genType', 'float'], returnType: 'genType', isp5Function: true}, ], 'min': [ - { args: ['genType', 'genType'], returnType: 'genType', isp5Function: true}, - { args: ['genType', 'float'], returnType: 'genType', isp5Function: true}, + { args: ['genType', 'genType'], returnType: 'genType', isp5Function: true}, + { args: ['genType', 'float'], returnType: 'genType', isp5Function: true}, ], 'mix': [ - { args: ['genType', 'genType', 'genType'], returnType: 'genType', isp5Function: false}, - { args: ['genType', 'genType', 'float'], returnType: 'genType', isp5Function: false}, + { args: ['genType', 'genType', 'genType'], returnType: 'genType', isp5Function: false}, + { args: ['genType', 'genType', 'float'], returnType: 'genType', isp5Function: false}, ], // 'mod': {}, // 'modf': {}, @@ -1582,7 +1590,7 @@ function shadergenerator(p5, fn) { // 'notEqual': {}, 'reflect': { args: ['genType', 'genType'], returnType: 'genType', isp5Function: false}, 'refract': { args: ['genType', 'genType', 'float'], returnType: 'genType', isp5Function: false}, - + ////////// Texture sampling ////////// 'texture': {args: ['sampler2D', 'vec2'], returnType: 'vec4', isp5Function: true}, } From 0995029e7314fa5b8774e1d7b3a979b8a88d1e17 Mon Sep 17 00:00:00 2001 From: Dave Pagurek Date: Sun, 6 Apr 2025 15:50:17 -0400 Subject: [PATCH 2/5] Convert some hooks examples to p5.strands --- src/webgl/material.js | 59 ++++++++++++++++--------------------------- 1 file changed, 22 insertions(+), 37 deletions(-) diff --git a/src/webgl/material.js b/src/webgl/material.js index b93681415a..5b5f3871c1 100644 --- a/src/webgl/material.js +++ b/src/webgl/material.js @@ -1441,17 +1441,15 @@ function material(p5, fn){ * environment = await loadImage('assets/outdoor_spheremap.jpg'); * * createCanvas(200, 200, WEBGL); - * myShader = baseMaterialShader().modify({ - * 'Inputs getPixelInputs': `(Inputs inputs) { - * float factor = - * sin( - * inputs.texCoord.x * ${TWO_PI} + - * inputs.texCoord.y * ${TWO_PI} - * ) * 0.4 + 0.5; - * inputs.shininess = mix(1., 100., factor); + * myShader = baseMaterialShader().modify(() => { + * getPixelInputs((inputs) => { + * let factor = sin( + * TWO_PI * (inputs.texCoord.x + inputs.texCoord.y) + * ); + * inputs.shininess = mix(1, 100, factor); * inputs.metalness = factor; * return inputs; - * }` + * }) * }); * } * @@ -1476,25 +1474,17 @@ function material(p5, fn){ * * function setup() { * createCanvas(200, 200, WEBGL); - * myShader = baseMaterialShader().modify({ - * 'Inputs getPixelInputs': `(Inputs inputs) { - * vec3 newNormal = inputs.normal; - * // Simple bump mapping: adjust the normal based on position - * newNormal.x += 0.2 * sin( - * sin( - * inputs.texCoord.y * ${TWO_PI} * 10.0 + - * inputs.texCoord.x * ${TWO_PI} * 25.0 - * ) - * ); - * newNormal.y += 0.2 * sin( - * sin( - * inputs.texCoord.x * ${TWO_PI} * 10.0 + - * inputs.texCoord.y * ${TWO_PI} * 25.0 - * ) + * myShader = baseMaterialShader().modify(() => { + * getPixelInputs((inputs) => { + * inputs.normal.x += 0.2 * sin( + * sin(inputs.texCoord.yx * TWO_PI * [10, 25]) * ); - * inputs.normal = normalize(newNormal); + * inputs.normal.y += 0.2 * sin( + * sin(inputs.texCoord * TWO_PI * [10, 25]) + * ); + * inputs.normal = normalize(inputs.normal); * return inputs; - * }` + * }); * }); * } * @@ -1568,18 +1558,13 @@ function material(p5, fn){ * async function setup() { * img = await loadImage('assets/bricks.jpg'); * createCanvas(100, 100, WEBGL); - * myShader = baseFilterShader().modify({ - * uniforms: { - * 'float time': () => millis() - * }, - * 'vec4 getColor': `( - * FilterInputs inputs, - * in sampler2D canvasContent - * ) { + * myShader = baseFilterShader().modify(() => { + * let time = uniformFloat(() => millis()); + * getColor((color, canvasContent) => { * inputs.texCoord.y += - * 0.01 * sin(time * 0.001 + inputs.position.x * 5.0); - * return getTexture(canvasContent, inputs.texCoord); - * }` + * 0.01 * sin(time * 0.001 + inputs.position.x * 5); + * return texture(canvasContent, inputs.texCoord); + * }); * }); * } * From bdc83100f8b0b374afa9c1f57f2a9db444ab2e81 Mon Sep 17 00:00:00 2001 From: Dave Pagurek Date: Sun, 6 Apr 2025 16:21:54 -0400 Subject: [PATCH 3/5] Fix panorama() --- src/webgl/p5.RendererGL.js | 2 +- src/webgl/shaders/sphereMapping.frag | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/webgl/p5.RendererGL.js b/src/webgl/p5.RendererGL.js index 3189442033..ce3bcf411e 100644 --- a/src/webgl/p5.RendererGL.js +++ b/src/webgl/p5.RendererGL.js @@ -1838,7 +1838,7 @@ class RendererGL extends Renderer { this.sphereMapping.setUniform("uFovY", this.states.curCamera.cameraFOV); this.sphereMapping.setUniform("uAspect", this.states.curCamera.aspectRatio); this.sphereMapping.setUniform("uNewNormalMatrix", this.scratchMat3.mat3); - this.sphereMapping.setUniform("uSampler", img); + this.sphereMapping.setUniform("uEnvMap", img); return this.sphereMapping; } diff --git a/src/webgl/shaders/sphereMapping.frag b/src/webgl/shaders/sphereMapping.frag index 3f448b7aad..9f2daefc39 100644 --- a/src/webgl/shaders/sphereMapping.frag +++ b/src/webgl/shaders/sphereMapping.frag @@ -2,7 +2,7 @@ precision highp float; -uniform sampler2D uSampler; +uniform sampler2D uEnvMap; uniform mat3 uNewNormalMatrix; uniform float uFovY; uniform float uAspect; @@ -11,7 +11,6 @@ varying vec2 vTexCoord; void main() { float uFovX = uFovY * uAspect; - vec4 newTexColor = texture2D(uSampler, vTexCoord); float angleY = mix(uFovY/2.0, -uFovY/2.0, vTexCoord.y); float angleX = mix(uFovX/2.0, -uFovX/2.0, vTexCoord.x); vec3 rotatedNormal = vec3( angleX, angleY, 1.0 ); @@ -22,6 +21,6 @@ void main() { vec2 suv; suv.y = 0.5 + 0.5 * (-rotatedNormal.y); suv.x = atan(rotatedNormal.z, rotatedNormal.x) / (2.0 * PI) + 0.5; - newTexColor = texture2D(uSampler, suv.xy); + vec4 newTexColor = texture2D(uEnvMap, suv.xy); gl_FragColor = newTexColor; } From 5db01eaf1bc083c66eb248f3c75362c40c7664a2 Mon Sep 17 00:00:00 2001 From: Dave Pagurek Date: Sun, 6 Apr 2025 16:29:32 -0400 Subject: [PATCH 4/5] Update more material shader examples --- src/webgl/material.js | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/src/webgl/material.js b/src/webgl/material.js index 5b5f3871c1..a1a8514ab6 100644 --- a/src/webgl/material.js +++ b/src/webgl/material.js @@ -1374,14 +1374,12 @@ function material(p5, fn){ * function setup() { * createCanvas(200, 200, WEBGL); * myShader = baseMaterialShader().modify({ - * uniforms: { - * 'float time': () => millis() - * }, - * 'Vertex getWorldInputs': `(Vertex inputs) { + * let time = uniformFloat(() => millis()); + * getWorldInputs((inputs) => { * inputs.position.y += - * 20.0 * sin(time * 0.001 + inputs.position.x * 0.05); + * 20 * sin(time * 0.001 + inputs.position.x * 0.05); * return inputs; - * }` + * }); * }); * } * @@ -1403,19 +1401,19 @@ function material(p5, fn){ * * function setup() { * createCanvas(200, 200, WEBGL); - * myShader = baseMaterialShader().modify({ - * declarations: 'vec3 myNormal;', - * 'Inputs getPixelInputs': `(Inputs inputs) { + * myShader = baseMaterialShader().modify(() => { + * let myNormal = varyingVec3(); + * getPixelInputs((inputs) => { * myNormal = inputs.normal; * return inputs; - * }`, - * 'vec4 getFinalColor': `(vec4 color) { + * }); + * getFinalColor((color) => { * return mix( - * vec4(1.0, 1.0, 1.0, 1.0), + * vec4(1, 1, 1, 1), * color, * abs(dot(myNormal, vec3(0.0, 0.0, 1.0))) * ); - * }` + * }); * }); * } * @@ -1477,10 +1475,10 @@ function material(p5, fn){ * myShader = baseMaterialShader().modify(() => { * getPixelInputs((inputs) => { * inputs.normal.x += 0.2 * sin( - * sin(inputs.texCoord.yx * TWO_PI * [10, 25]) + * sin(TWO_PI * dot(inputs.texCoord.yx, vec2(10, 25))) * ); * inputs.normal.y += 0.2 * sin( - * sin(inputs.texCoord * TWO_PI * [10, 25]) + * sin(TWO_PI * dot(inputs.texCoord, vec2(10, 25))) * ); * inputs.normal = normalize(inputs.normal); * return inputs; From 209a63d360d23efc77645bdc066803faaf672920 Mon Sep 17 00:00:00 2001 From: Dave Pagurek Date: Sun, 6 Apr 2025 16:42:17 -0400 Subject: [PATCH 5/5] Fix syntax error, put back example that we can't do yet --- src/webgl/material.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/webgl/material.js b/src/webgl/material.js index a1a8514ab6..cc22d3d405 100644 --- a/src/webgl/material.js +++ b/src/webgl/material.js @@ -1373,8 +1373,8 @@ function material(p5, fn){ * * function setup() { * createCanvas(200, 200, WEBGL); - * myShader = baseMaterialShader().modify({ - * let time = uniformFloat(() => millis()); + * myShader = baseMaterialShader().modify(() => { + * let time = uniformFloat(() => millis()); * getWorldInputs((inputs) => { * inputs.position.y += * 20 * sin(time * 0.001 + inputs.position.x * 0.05); @@ -1401,19 +1401,19 @@ function material(p5, fn){ * * function setup() { * createCanvas(200, 200, WEBGL); - * myShader = baseMaterialShader().modify(() => { - * let myNormal = varyingVec3(); - * getPixelInputs((inputs) => { + * myShader = baseMaterialShader().modify({ + * declarations: 'vec3 myNormal;', + * 'Inputs getPixelInputs': `(Inputs inputs) { * myNormal = inputs.normal; * return inputs; - * }); - * getFinalColor((color) => { + * }`, + * 'vec4 getFinalColor': `(vec4 color) { * return mix( - * vec4(1, 1, 1, 1), + * vec4(1.0, 1.0, 1.0, 1.0), * color, * abs(dot(myNormal, vec3(0.0, 0.0, 1.0))) * ); - * }); + * }` * }); * } *