diff --git a/src/accessibility/outputs.js b/src/accessibility/outputs.js index 8d1f399969..97b38712f4 100644 --- a/src/accessibility/outputs.js +++ b/src/accessibility/outputs.js @@ -543,7 +543,7 @@ function outputs(p5, fn){ fn._getPos = function (x, y) { const untransformedPosition = new DOMPointReadOnly(x, y); const currentTransform = this._renderer.isP3D ? - new DOMMatrix(this._renderer.states.uMVMatrix.mat4) : + new DOMMatrix(this._renderer.calculateCombinedMatrix()) : this.drawingContext.getTransform(); const { x: transformedX, y: transformedY } = untransformedPosition .matrixTransform(currentTransform); diff --git a/src/webgl/GeometryBuilder.js b/src/webgl/GeometryBuilder.js index 9d5783dbf9..5cb4862168 100644 --- a/src/webgl/GeometryBuilder.js +++ b/src/webgl/GeometryBuilder.js @@ -37,7 +37,7 @@ class GeometryBuilder { if (!this.hasTransform) return normals; return normals.map( - v => this.renderer.states.uNMatrix.multiplyVec(v) // this is a vec3 + v => this.renderer.scratchMat3.multiplyVec(v) // this is a vec3 ); } @@ -51,7 +51,7 @@ class GeometryBuilder { .every((v, i) => v === this.identityMatrix.mat4[i]); if (this.hasTransform) { - this.renderer.states.uNMatrix.inverseTranspose4x4(this.renderer.states.uModelMatrix); + this.renderer.scratchMat3.inverseTranspose4x4(this.renderer.states.uModelMatrix); } let startIdx = this.geometry.vertices.length; diff --git a/src/webgl/material.js b/src/webgl/material.js index 0a466a23c3..630ccaa1e5 100644 --- a/src/webgl/material.js +++ b/src/webgl/material.js @@ -1471,19 +1471,86 @@ function material(p5, fn){ * You can call `baseNormalShader().modify()` * and change any of the following hooks: * - * Hook | Description - * -----|------------ - * `void beforeVertex` | Called at the start of the vertex shader. - * `vec3 getLocalPosition` | Update the position of vertices before transforms are applied. It takes in `vec3 position` and must return a modified version. - * `vec3 getWorldPosition` | Update the position of vertices after transforms are applied. It takes in `vec3 position` and pust return a modified version. - * `vec3 getLocalNormal` | Update the normal before transforms are applied. It takes in `vec3 normal` and must return a modified version. - * `vec3 getWorldNormal` | Update the normal after transforms are applied. It takes in `vec3 normal` and must return a modified version. - * `vec2 getUV` | Update the texture coordinates. It takes in `vec2 uv` and must return a modified version. - * `vec4 getVertexColor` | Update the color of each vertex. It takes in a `vec4 color` and must return a modified version. - * `void afterVertex` | Called at the end of the vertex shader. - * `void beforeFragment` | Called at the start of the fragment shader. - * `vec4 getFinalColor` | Update the final color after mixing. It takes in a `vec4 color` and must return a modified version. - * `void afterFragment` | Called at the end of the fragment shader. + * + * + * + * + * + * + * + * + * + * + *
HookDescription
+ * + * `void beforeVertex` + * + * + * + * Called at the start of the vertex shader. + * + *
+ * + * `Vertex getObjectInputs` + * + * + * + * Update the vertex data of the model being drawn before any positioning has been applied. It takes in a `Vertex` struct, which includes: + * - `vec3 position`, the position of the vertex + * - `vec3 normal`, the direction facing out of the surface + * - `vec2 uv`, the texture coordinates associeted with the vertex + * - `vec4 color`, the per-vertex color + * The struct can be modified and returned. + * + *
+ * + * `Vertex getWorldInputs` + * + * + * + * Update the vertex data of the model being drawn after transformations such as `translate()` and `scale()` have been applied, but before the camera has been applied. It takes in a `Vertex` struct like, in the `getObjectInputs` hook above, that can be modified and returned. + * + *
+ * + * `Vertex getCameraInputs` + * + * + * + * Update the vertex data of the model being drawn as they appear relative to the camera. It takes in a `Vertex` struct like, in the `getObjectInputs` hook above, that can be modified and returned. + * + *
+ * + * `void afterVertex` + * + * + * + * Called at the end of the vertex shader. + * + *
+ * + * `void beforeFragment` + * + * + * + * Called at the start of the fragment shader. + * + *
+ * + * `vec4 getFinalColor` + * + * + * + * Update the final color after mixing. It takes in a `vec4 color` and must return a modified version. + * + *
+ * + * `void afterFragment` + * + * + * + * Called at the end of the fragment shader. + * + *
* * Most of the time, you will need to write your hooks in GLSL ES version 300. If you * are using WebGL 1 instead of 2, write your hooks in GLSL ES 100 instead. @@ -1506,9 +1573,10 @@ function material(p5, fn){ * uniforms: { * 'float time': () => millis() * }, - * 'vec3 getWorldPosition': `(vec3 pos) { - * pos.y += 20. * sin(time * 0.001 + pos.x * 0.05); - * return pos; + * 'Vertex getWorldInputs': `(Vertex inputs) { + * inputs.position.y += + * 20. * sin(time * 0.001 + inputs.position.x * 0.05); + * return inputs; * }` * }); * } @@ -1530,7 +1598,10 @@ function material(p5, fn){ * function setup() { * createCanvas(200, 200, WEBGL); * myShader = baseNormalShader().modify({ - * 'vec3 getWorldNormal': '(vec3 normal) { return abs(normal); }', + * 'Vertex getCameraInputs': `(Vertex inputs) { + * inputs.normal = abs(inputs.normal); + * return inputs; + * }`, * 'vec4 getFinalColor': `(vec4 color) { * // Map the r, g, and b values of the old normal to new colors * // instead of just red, green, and blue: @@ -1566,19 +1637,86 @@ function material(p5, fn){ * You can call `baseColorShader().modify()` * and change any of the following hooks: * - * Hook | Description - * -------|------------- - * `void beforeVertex` | Called at the start of the vertex shader. - * `vec3 getLocalPosition` | Update the position of vertices before transforms are applied. It takes in `vec3 position` and must return a modified version. - * `vec3 getWorldPosition` | Update the position of vertices after transforms are applied. It takes in `vec3 position` and pust return a modified version. - * `vec3 getLocalNormal` | Update the normal before transforms are applied. It takes in `vec3 normal` and must return a modified version. - * `vec3 getWorldNormal` | Update the normal after transforms are applied. It takes in `vec3 normal` and must return a modified version. - * `vec2 getUV` | Update the texture coordinates. It takes in `vec2 uv` and must return a modified version. - * `vec4 getVertexColor` | Update the color of each vertex. It takes in a `vec4 color` and must return a modified version. - * `void afterVertex` | Called at the end of the vertex shader. - * `void beforeFragment` | Called at the start of the fragment shader. - * `vec4 getFinalColor` | Update the final color after mixing. It takes in a `vec4 color` and must return a modified version. - * `void afterFragment` | Called at the end of the fragment shader. + * + * + * + * + * + * + * + * + * + * + *
HookDescription
+ * + * `void beforeVertex` + * + * + * + * Called at the start of the vertex shader. + * + *
+ * + * `Vertex getObjectInputs` + * + * + * + * Update the vertex data of the model being drawn before any positioning has been applied. It takes in a `Vertex` struct, which includes: + * - `vec3 position`, the position of the vertex + * - `vec3 normal`, the direction facing out of the surface + * - `vec2 uv`, the texture coordinates associeted with the vertex + * - `vec4 color`, the per-vertex color + * The struct can be modified and returned. + * + *
+ * + * `Vertex getWorldInputs` + * + * + * + * Update the vertex data of the model being drawn after transformations such as `translate()` and `scale()` have been applied, but before the camera has been applied. It takes in a `Vertex` struct like, in the `getObjectInputs` hook above, that can be modified and returned. + * + *
+ * + * `Vertex getCameraInputs` + * + * + * + * Update the vertex data of the model being drawn as they appear relative to the camera. It takes in a `Vertex` struct like, in the `getObjectInputs` hook above, that can be modified and returned. + * + *
+ * + * `void afterVertex` + * + * + * + * Called at the end of the vertex shader. + * + *
+ * + * `void beforeFragment` + * + * + * + * Called at the start of the fragment shader. + * + *
+ * + * `vec4 getFinalColor` + * + * + * + * Update the final color after mixing. It takes in a `vec4 color` and must return a modified version. + * + *
+ * + * `void afterFragment` + * + * + * + * Called at the end of the fragment shader. + * + *
* * Most of the time, you will need to write your hooks in GLSL ES version 300. If you * are using WebGL 1 instead of 2, write your hooks in GLSL ES 100 instead. @@ -1601,9 +1739,10 @@ function material(p5, fn){ * uniforms: { * 'float time': () => millis() * }, - * 'vec3 getWorldPosition': `(vec3 pos) { - * pos.y += 20. * sin(time * 0.001 + pos.x * 0.05); - * return pos; + * 'Vertex getWorldInputs': `(Inputs inputs) { + * inputs.position.y += + * 20. * sin(time * 0.001 + inputs.position.x * 0.05); + * return inputs; * }` * }); * } @@ -1642,56 +1781,35 @@ function material(p5, fn){ * * * - * `vec3 getLocalPosition` - * - * - * - * Update the position of vertices before transforms are applied. It takes in `vec3 position` and must return a modified version. - * - * - * - * - * `vec3 getWorldPosition` - * - * - * - * Update the position of vertices after transforms are applied. It takes in `vec3 position` and pust return a modified version. - * - * - * - * - * `float getStrokeWeight` - * - * - * - * Update the stroke weight. It takes in `float weight` and pust return a modified version. - * - * - * - * - * `vec2 getLineCenter` + * `StrokeVertex getObjectInputs` * * * - * Update the center of the line. It takes in `vec2 center` and must return a modified version. + * Update the vertex data of the stroke being drawn before any positioning has been applied. It takes in a `StrokeVertex` struct, which includes: + * - `vec3 position`, the position of the vertex + * - `vec3 tangentIn`, the tangent coming in to the vertex + * - `vec3 tangentOut`, the tangent coming out of the vertex. In straight segments, this will be the same as `tangentIn`. In joins, it will be different. In caps, one of the tangents will be 0. + * - `vec4 color`, the per-vertex color + * - `float weight`, the stroke weight + * The struct can be modified and returned. * * * * - * `vec2 getLinePosition` + * `StrokeVertex getWorldInputs` * * * - * Update the position of each vertex on the edge of the line. It takes in `vec2 position` and must return a modified version. + * Update the vertex data of the model being drawn after transformations such as `translate()` and `scale()` have been applied, but before the camera has been applied. It takes in a `StrokeVertex` struct like, in the `getObjectInputs` hook above, that can be modified and returned. * * * * - * `vec4 getVertexColor` + * `StrokeVertex getCameraInputs` * * * - * Update the color of each vertex. It takes in a `vec4 color` and must return a modified version. + * Update the vertex data of the model being drawn as they appear relative to the camera. It takes in a `StrokeVertex` struct like, in the `getObjectInputs` hook above, that can be modified and returned. * * * @@ -1810,20 +1928,15 @@ function material(p5, fn){ * uniforms: { * 'float time': () => millis() * }, - * declarations: 'vec3 myPosition;', - * 'vec3 getWorldPosition': `(vec3 pos) { - * myPosition = pos; - * return pos; - * }`, - * 'float getStrokeWeight': `(float w) { + * 'StrokeVertex getWorldInputs': `(StrokeVertex inputs) { * // Add a somewhat random offset to the weight * // that varies based on position and time * float scale = 0.8 + 0.2*sin(10.0 * sin( * floor(time/250.) + - * myPosition.x*0.01 + - * myPosition.y*0.01 + * inputs.position.x*0.01 + + * inputs.position.y*0.01 * )); - * return w * scale; + * inputs.weight *= scale; * }` * }); * } diff --git a/src/webgl/p5.RendererGL.js b/src/webgl/p5.RendererGL.js index f0a244ec39..b6b90984af 100644 --- a/src/webgl/p5.RendererGL.js +++ b/src/webgl/p5.RendererGL.js @@ -177,10 +177,7 @@ class RendererGL extends Renderer { // Push/pop state this.states.uModelMatrix = new Matrix(4); this.states.uViewMatrix = new Matrix(4); - this.states.uMVMatrix = new Matrix(4); this.states.uPMatrix = new Matrix(4); - this.states.uNMatrix = new Matrix(3); - this.states.curMatrix = new Matrix(3); this.states.curCamera = new Camera(this); @@ -416,6 +413,8 @@ class RendererGL extends Renderer { this._curShader = undefined; this.drawShapeCount = 1; + + this.scratchMat3 = new Matrix(3); } ////////////////////////////////////////////// @@ -1739,11 +1738,11 @@ class RendererGL extends Renderer { if (!this.sphereMapping) { this.sphereMapping = this._pInst.createFilterShader(sphereMapping); } - this.states.uNMatrix.inverseTranspose4x4(this.states.uViewMatrix); - this.states.uNMatrix.invert(this.states.uNMatrix); // uNMMatrix is 3x3 + this.scratchMat3.inverseTranspose4x4(this.states.uViewMatrix); + this.scratchMat3.invert(this.scratchMat3); // uNMMatrix is 3x3 this.sphereMapping.setUniform("uFovY", this.states.curCamera.cameraFOV); this.sphereMapping.setUniform("uAspect", this.states.curCamera.aspectRatio); - this.sphereMapping.setUniform("uNewNormalMatrix", this.states.uNMatrix.mat3); + this.sphereMapping.setUniform("uNewNormalMatrix", this.scratchMat3.mat3); this.sphereMapping.setUniform("uSampler", img); return this.sphereMapping; } @@ -1860,12 +1859,9 @@ class RendererGL extends Renderer { { vertex: { "void beforeVertex": "() {}", - "vec3 getLocalPosition": "(vec3 position) { return position; }", - "vec3 getWorldPosition": "(vec3 position) { return position; }", - "vec3 getLocalNormal": "(vec3 normal) { return normal; }", - "vec3 getWorldNormal": "(vec3 normal) { return normal; }", - "vec2 getUV": "(vec2 uv) { return uv; }", - "vec4 getVertexColor": "(vec4 color) { return color; }", + "Vertex getObjectInputs": "(Vertex inputs) { return inputs; }", + "Vertex getWorldInputs": "(Vertex inputs) { return inputs; }", + "Vertex getCameraInputs": "(Vertex inputs) { return inputs; }", "void afterVertex": "() {}", }, fragment: { @@ -1895,12 +1891,9 @@ class RendererGL extends Renderer { { vertex: { "void beforeVertex": "() {}", - "vec3 getLocalPosition": "(vec3 position) { return position; }", - "vec3 getWorldPosition": "(vec3 position) { return position; }", - "vec3 getLocalNormal": "(vec3 normal) { return normal; }", - "vec3 getWorldNormal": "(vec3 normal) { return normal; }", - "vec2 getUV": "(vec2 uv) { return uv; }", - "vec4 getVertexColor": "(vec4 color) { return color; }", + "Vertex getObjectInputs": "(Vertex inputs) { return inputs; }", + "Vertex getWorldInputs": "(Vertex inputs) { return inputs; }", + "Vertex getCameraInputs": "(Vertex inputs) { return inputs; }", "void afterVertex": "() {}", }, fragment: { @@ -1986,12 +1979,9 @@ class RendererGL extends Renderer { { vertex: { "void beforeVertex": "() {}", - "vec3 getLocalPosition": "(vec3 position) { return position; }", - "vec3 getWorldPosition": "(vec3 position) { return position; }", - "float getStrokeWeight": "(float weight) { return weight; }", - "vec2 getLineCenter": "(vec2 center) { return center; }", - "vec2 getLinePosition": "(vec2 position) { return position; }", - "vec4 getVertexColor": "(vec4 color) { return color; }", + "StrokeVertex getObjectInputs": "(StrokeVertex inputs) { return inputs; }", + "StrokeVertex getWorldInputs": "(StrokeVertex inputs) { return inputs; }", + "StrokeVertex getCameraInputs": "(StrokeVertex inputs) { return inputs; }", "void afterVertex": "() {}", }, fragment: { @@ -2188,11 +2178,7 @@ class RendererGL extends Renderer { const modelMatrix = this.states.uModelMatrix; const viewMatrix = this.states.uViewMatrix; const projectionMatrix = this.states.uPMatrix; - const modelViewMatrix = modelMatrix.copy().mult(viewMatrix); - this.states.uMVMatrix = this.calculateCombinedMatrix(); - - const modelViewProjectionMatrix = modelViewMatrix.copy(); - modelViewProjectionMatrix.mult(projectionMatrix); + const modelViewMatrix = this.calculateCombinedMatrix(); shader.setUniform( "uPerspective", @@ -2202,17 +2188,29 @@ class RendererGL extends Renderer { shader.setUniform("uProjectionMatrix", projectionMatrix.mat4); shader.setUniform("uModelMatrix", modelMatrix.mat4); shader.setUniform("uModelViewMatrix", modelViewMatrix.mat4); - shader.setUniform( - "uModelViewProjectionMatrix", - modelViewProjectionMatrix.mat4, - ); + if (shader.uniforms.uModelViewProjectionMatrix) { + const modelViewProjectionMatrix = modelViewMatrix.copy(); + modelViewProjectionMatrix.mult(projectionMatrix); + shader.setUniform( + "uModelViewProjectionMatrix", + modelViewProjectionMatrix.mat4, + ); + } if (shader.uniforms.uNormalMatrix) { - this.states.uNMatrix.inverseTranspose4x4(this.states.uMVMatrix); - shader.setUniform("uNormalMatrix", this.states.uNMatrix.mat3); + this.scratchMat3.inverseTranspose4x4(modelViewMatrix); + shader.setUniform("uNormalMatrix", this.scratchMat3.mat3); + } + if (shader.uniforms.uModelNormalMatrix) { + this.scratchMat3.inverseTranspose4x4(this.states.uModelMatrix); + shader.setUniform("uModelNormalMatrix", this.scratchMat3.mat3); + } + if (shader.uniforms.uCameraNormalMatrix) { + this.scratchMat3.inverseTranspose4x4(this.states.uViewMatrix); + shader.setUniform("uCameraNormalMatrix", this.scratchMat3.mat3); } if (shader.uniforms.uCameraRotation) { - this.states.curMatrix.inverseTranspose4x4(this.states.uViewMatrix); - shader.setUniform("uCameraRotation", this.states.curMatrix.mat3); + this.scratchMat3.inverseTranspose4x4(this.states.uViewMatrix); + shader.setUniform("uCameraRotation", this.scratchMat3.mat3); } shader.setUniform("uViewport", this._viewport); } diff --git a/src/webgl/p5.Shader.js b/src/webgl/p5.Shader.js index b3d6dfaacd..8d885c7c53 100644 --- a/src/webgl/p5.Shader.js +++ b/src/webgl/p5.Shader.js @@ -54,9 +54,10 @@ class Shader { shaderSrc(src, shaderType) { const main = 'void main'; - const [preMain, postMain] = src.split(main); + let [preMain, postMain] = src.split(main); let hooks = ''; + let defines = ''; for (const key in this.hooks.uniforms) { hooks += `uniform ${key};\n`; } @@ -76,14 +77,22 @@ class Shader { // Add a #define so that if the shader wants to use preprocessor directives to // optimize away the extra function calls in main, it can do so if (this.hooks.modified[shaderType][hookDef]) { - hooks += '#define AUGMENTED_HOOK_' + hookName + '\n'; + defines += '#define AUGMENTED_HOOK_' + hookName + '\n'; } hooks += hookType + ' HOOK_' + hookName + this.hooks[shaderType][hookDef] + '\n'; } - return preMain + hooks + main + postMain; + // Allow shaders to specify the location of hook #define statements. Normally these + // go after function definitions, but one might want to have them defined earlier + // in order to only conditionally make uniforms. + if (preMain.indexOf('#define HOOK_DEFINES') !== -1) { + preMain = preMain.replace('#define HOOK_DEFINES', '\n' + defines + '\n'); + defines = ''; + } + + return preMain + '\n' + defines + hooks + main + postMain; } /** @@ -365,7 +374,7 @@ class Shader { if (typeof IS_MINIFIED !== 'undefined') { console.error(glError); } else { - throw(glError); + throw glError; p5._friendlyError( `Yikes! An error occurred compiling the vertex shader:${glError}` ); @@ -383,6 +392,7 @@ class Shader { if (typeof IS_MINIFIED !== 'undefined') { console.error(glError); } else { + throw glError; p5._friendlyError( `Darn! An error occurred compiling the fragment shader:${glError}` ); diff --git a/src/webgl/shaders/line.vert b/src/webgl/shaders/line.vert index 9609fc38d4..de422ad6b6 100644 --- a/src/webgl/shaders/line.vert +++ b/src/webgl/shaders/line.vert @@ -18,10 +18,18 @@ #define PROCESSING_LINE_SHADER +#define HOOK_DEFINES + precision highp int; precision highp float; +#ifdef AUGMENTED_HOOK_getWorldInputs +uniform mat4 uModelMatrix; +uniform mat4 uViewMatrix; +#else uniform mat4 uModelViewMatrix; +#endif + uniform mat4 uProjectionMatrix; uniform float uStrokeWeight; @@ -33,7 +41,7 @@ uniform vec4 uViewport; uniform int uPerspective; uniform int uStrokeJoin; -IN vec4 aPosition; +IN vec3 aPosition; IN vec3 aTangentIn; IN vec3 aTangentOut; IN float aSide; @@ -66,6 +74,14 @@ vec2 lineIntersection(vec2 aPoint, vec2 aDir, vec2 bPoint, vec2 bDir) { return aPoint + aDir * intersectionDistance; } +struct StrokeVertex { + vec3 position; + vec3 tangentIn; + vec3 tangentOut; + vec4 color; + float weight; +}; + void main() { HOOK_beforeVertex(); @@ -81,12 +97,43 @@ void main() { ) ? 1. : 0.; } - vec4 localPosition = vec4(HOOK_getLocalPosition(aPosition.xyz), 1.); - vec4 posp = vec4(HOOK_getWorldPosition((uModelViewMatrix * localPosition).xyz), 1.); - vec4 posqIn = posp + uModelViewMatrix * vec4(aTangentIn, 0); - vec4 posqOut = posp + uModelViewMatrix * vec4(aTangentOut, 0); - float strokeWeight = HOOK_getStrokeWeight(uStrokeWeight); - vStrokeWeight = strokeWeight; + StrokeVertex inputs; + inputs.position = aPosition.xyz; + inputs.color = uUseLineColor ? aVertexColor : uMaterialColor; + inputs.weight = uStrokeWeight; + inputs.tangentIn = aTangentIn; + inputs.tangentOut = aTangentOut; + +#ifdef AUGMENTED_HOOK_getObjectInputs + inputs = HOOK_getObjectInputs(inputs); +#endif + +#ifdef AUGMENTED_HOOK_getWorldInputs + inputs.position = (uModelMatrix * vec4(inputs.position, 1.)).xyz; + inputs.tangentIn = (uModelMatrix * vec4(aTangentIn, 0.)).xyz; + inputs.tangentOut = (uModelMatrix * vec4(aTangentOut, 0.)).xyz; + inputs = HOOK_getWorldInputs(inputs); +#endif + +#ifdef AUGMENTED_HOOK_getWorldInputs + // Already multiplied by the model matrix, just apply view + inputs.position = (uViewMatrix * vec4(inputs.position, 1.)).xyz; + inputs.tangentIn = (uViewMatrix * vec4(aTangentIn, 0.)).xyz; + inputs.tangentOut = (uViewMatrix * vec4(aTangentOut, 0.)).xyz; +#else + // Apply both at once + inputs.position = (uModelViewMatrix * vec4(inputs.position, 1.)).xyz; + inputs.tangentIn = (uModelViewMatrix * vec4(aTangentIn, 0.)).xyz; + inputs.tangentOut = (uModelViewMatrix * vec4(aTangentOut, 0.)).xyz; +#endif +#ifdef AUGMENTED_HOOK_getCameraInputs + inputs = hook_getCameraInputs(inputs); +#endif + + vec4 posp = vec4(inputs.position, 1.); + vec4 posqIn = vec4(inputs.position + inputs.tangentIn, 1.); + vec4 posqOut = vec4(inputs.position + inputs.tangentOut, 1.); + vStrokeWeight = inputs.weight; float facingCamera = pow( // The word space tangent's z value is 0 if it's facing the camera @@ -130,7 +177,6 @@ void main() { vec4 p = uProjectionMatrix * posp; vec4 qIn = uProjectionMatrix * posqIn; vec4 qOut = uProjectionMatrix * posqOut; - vCenter = HOOK_getLineCenter(p.xy); // formula to convert from clip space (range -1..1) to screen space (range 0..[width or height]) // screen_p = (p.xy/p.w + <1,1>) * 0.5 * uViewport.zw @@ -195,9 +241,9 @@ void main() { // find where the lines intersect to find the elbow of the join vec2 c = (posp.xy/posp.w + vec2(1.,1.)) * 0.5 * uViewport.zw; vec2 intersection = lineIntersection( - c + (side * normalIn * strokeWeight / 2.), + c + (side * normalIn * inputs.weight / 2.), tangentIn, - c + (side * normalOut * strokeWeight / 2.), + c + (side * normalOut * inputs.weight / 2.), tangentOut ); offset = (intersection - c); @@ -207,21 +253,21 @@ void main() { // the magnitude to avoid lines going across the whole screen when this // happens. float mag = length(offset); - float maxMag = 3. * strokeWeight; + float maxMag = 3. * inputs.weight; if (mag > maxMag) { offset *= maxMag / mag; } } else if (sideEnum == 1.) { - offset = side * normalIn * strokeWeight / 2.; + offset = side * normalIn * inputs.weight / 2.; } else if (sideEnum == 3.) { - offset = side * normalOut * strokeWeight / 2.; + offset = side * normalOut * inputs.weight / 2.; } } if (uStrokeJoin == STROKE_JOIN_BEVEL) { vec2 avgNormal = vec2(-vTangent.y, vTangent.x); - vMaxDist = abs(dot(avgNormal, normalIn * strokeWeight / 2.)); + vMaxDist = abs(dot(avgNormal, normalIn * inputs.weight / 2.)); } else { - vMaxDist = strokeWeight / 2.; + vMaxDist = inputs.weight / 2.; } } else { vec2 tangent = aTangentIn == vec3(0.) ? tangentOut : tangentIn; @@ -233,14 +279,16 @@ void main() { // extends out from the line float tangentOffset = abs(aSide) - 1.; offset = (normal * normalOffset + tangent * tangentOffset) * - strokeWeight * 0.5; - vMaxDist = strokeWeight / 2.; + inputs.weight * 0.5; + vMaxDist = inputs.weight / 2.; } - vPosition = HOOK_getLinePosition(vCenter + offset); + + vCenter = p.xy; + vPosition = vCenter + offset; + vColor = inputs.color; gl_Position.xy = p.xy + offset.xy * curPerspScale; gl_Position.zw = p.zw; - vColor = HOOK_getVertexColor(uUseLineColor ? aVertexColor : uMaterialColor); HOOK_afterVertex(); } diff --git a/src/webgl/shaders/normal.vert b/src/webgl/shaders/normal.vert index 63922714b4..9b466b54c6 100644 --- a/src/webgl/shaders/normal.vert +++ b/src/webgl/shaders/normal.vert @@ -3,9 +3,18 @@ IN vec3 aNormal; IN vec2 aTexCoord; IN vec4 aVertexColor; +#define HOOK_DEFINES + +#ifdef AUGMENTED_HOOK_getWorldInputs +uniform mat4 uModelMatrix; +uniform mat4 uViewMatrix; +uniform mat3 uModelNormalMatrix; +uniform mat3 uCameraNormalMatrix; +#else uniform mat4 uModelViewMatrix; -uniform mat4 uProjectionMatrix; uniform mat3 uNormalMatrix; +#endif +uniform mat4 uProjectionMatrix; uniform vec4 uMaterialColor; uniform bool uUseVertexColor; @@ -14,16 +23,50 @@ OUT vec3 vVertexNormal; OUT highp vec2 vVertTexCoord; OUT vec4 vColor; +struct Vertex { + vec3 position; + vec3 normal; + vec2 uv; + vec4 color; +}; + void main(void) { HOOK_beforeVertex(); - vec4 positionVec4 = vec4(HOOK_getWorldPosition( - (uModelViewMatrix * vec4(HOOK_getLocalPosition(aPosition), 1.0)).xyz - ), 1.); - gl_Position = uProjectionMatrix * positionVec4; + Vertex inputs; + inputs.position = aPosition; + inputs.normal = aNormal; + inputs.uv = aTexCoord; + inputs.color = (uUseVertexColor && aVertexColor.x >= 0.0) ? aVertexColor : uMaterialColor; +#ifdef AUGMENTED_HOOK_getObjectInputs + inputs = HOOK_getObjectInputs(inputs); +#endif + +#ifdef AUGMENTED_HOOK_getWorldInputs + inputs.position = (uModelMatrix * vec4(inputs.position, 1.)).xyz; + inputs.normal = uModelNormalMatrix * inputs.normal; + inputs = HOOK_getWorldInputs(inputs); +#endif + +#ifdef AUGMENTED_HOOK_getWorldInputs + // Already multiplied by the model matrix, just apply view + inputs.position = (uViewMatrix * vec4(inputs.position, 1.)).xyz; + inputs.normal = uCameraNormalMatrix * inputs.normal; +#else + // Apply both at once + inputs.position = (uModelViewMatrix * vec4(inputs.position, 1.)).xyz; + inputs.normal = uNormalMatrix * inputs.normal; +#endif +#ifdef AUGMENTED_HOOK_getCameraInputs + inputs = HOOK_getCameraInputs(inputs); +#endif + + // Pass varyings to fragment shader + vVertTexCoord = inputs.uv; + vVertexNormal = normalize(inputs.normal); + vColor = inputs.color; + + gl_Position = uProjectionMatrix * vec4(inputs.position, 1.); - vVertexNormal = HOOK_getWorldNormal(normalize(uNormalMatrix * HOOK_getLocalNormal(aNormal))); - vVertTexCoord = HOOK_getUV(aTexCoord); - vColor = HOOK_getVertexColor((uUseVertexColor && aVertexColor.x >= 0.0) ? aVertexColor : uMaterialColor); HOOK_afterVertex(); } diff --git a/src/webgl/shaders/phong.vert b/src/webgl/shaders/phong.vert index 589bc8a84b..f59c09744a 100644 --- a/src/webgl/shaders/phong.vert +++ b/src/webgl/shaders/phong.vert @@ -1,5 +1,7 @@ precision highp int; +#define HOOK_DEFINES + IN vec3 aPosition; IN vec3 aNormal; IN vec2 aTexCoord; @@ -7,11 +9,16 @@ IN vec4 aVertexColor; uniform vec3 uAmbientColor[5]; -uniform mat4 uModelViewMatrix; +#ifdef AUGMENTED_HOOK_getWorldInputs uniform mat4 uModelMatrix; uniform mat4 uViewMatrix; -uniform mat4 uProjectionMatrix; +uniform mat3 uModelNormalMatrix; +uniform mat3 uCameraNormalMatrix; +#else +uniform mat4 uModelViewMatrix; uniform mat3 uNormalMatrix; +#endif +uniform mat4 uProjectionMatrix; uniform int uAmbientLightCount; uniform bool uUseVertexColor; @@ -38,26 +45,27 @@ void main(void) { inputs.normal = aNormal; inputs.uv = aTexCoord; inputs.color = (uUseVertexColor && aVertexColor.x >= 0.0) ? aVertexColor : uMaterialColor; -#ifdef AUGMENTED_HOOK_getLocalInputs +#ifdef AUGMENTED_HOOK_getObjectInputs inputs = HOOK_getObjectInputs(inputs); #endif #ifdef AUGMENTED_HOOK_getWorldInputs inputs.position = (uModelMatrix * vec4(inputs.position, 1.)).xyz; + inputs.normal = uModelNormalMatrix * inputs.normal; inputs = HOOK_getWorldInputs(inputs); #endif -#ifdef AUGMENTED_HOOK_getObjectInputs - // Already multiplied by the object matrix, just apply view +#ifdef AUGMENTED_HOOK_getWorldInputs + // Already multiplied by the model matrix, just apply view inputs.position = (uViewMatrix * vec4(inputs.position, 1.)).xyz; + inputs.normal = uCameraNormalMatrix * inputs.normal; #else // Apply both at once inputs.position = (uModelViewMatrix * vec4(inputs.position, 1.)).xyz; -#endif - // TODO: do the same as above here? inputs.normal = uNormalMatrix * inputs.normal; +#endif #ifdef AUGMENTED_HOOK_getCameraInputs - inputs = HOOK_getWorldInputs(inputs); + inputs = HOOK_getCameraInputs(inputs); #endif // Pass varyings to fragment shader diff --git a/test/unit/visual/cases/webgl.js b/test/unit/visual/cases/webgl.js index de0c234a2c..f32f9c2f31 100644 --- a/test/unit/visual/cases/webgl.js +++ b/test/unit/visual/cases/webgl.js @@ -399,4 +399,97 @@ visualSuite('WebGL', function() { screenshot(); }); }); + + visualSuite('Hooks coordinate spaces', () => { + for (const base of ['baseMaterialShader', 'baseColorShader', 'baseNormalShader']) { + visualSuite(base, () => { + visualTest('Object space', (p5, screenshot) => { + p5.createCanvas(50, 50, p5.WEBGL); + const myShader = p5[base]().modify({ + 'Vertex getObjectInputs': `(Vertex inputs) { + inputs.position.x += 0.25; + inputs.normal.x += 0.5 * sin(inputs.position.y * 2.); + inputs.normal = normalize(inputs.normal); + return inputs; + }` + }); + p5.background(255); + p5.lights(); + p5.fill('red'); + p5.noStroke(); + p5.rotateY(p5.PI/2); + p5.camera(-800, 0, 0, 0, 0, 0); + p5.shader(myShader); + p5.sphere(20); + screenshot(); + }); + + visualTest('World space', (p5, screenshot) => { + p5.createCanvas(50, 50, p5.WEBGL); + const myShader = p5[base]().modify({ + 'Vertex getWorldInputs': `(Vertex inputs) { + inputs.position.x += 10.; + inputs.normal.x += 0.5 * sin(inputs.position.y * 2.); + inputs.normal = normalize(inputs.normal); + return inputs; + }` + }); + p5.background(255); + p5.lights(); + p5.fill('red'); + p5.noStroke(); + p5.rotateY(p5.PI/2); + p5.camera(-800, 0, 0, 0, 0, 0); + p5.shader(myShader); + p5.sphere(20); + screenshot(); + }); + + visualTest('Camera space', (p5, screenshot) => { + p5.createCanvas(50, 50, p5.WEBGL); + const myShader = p5[base]().modify({ + 'Vertex getCameraInputs': `(Vertex inputs) { + inputs.position.x += 10.; + inputs.normal.x += 0.5 * sin(inputs.position.y * 2.); + inputs.normal = normalize(inputs.normal); + return inputs; + }` + }); + p5.background(255); + p5.lights(); + p5.fill('red'); + p5.noStroke(); + p5.rotateY(p5.PI/2); + p5.camera(-800, 0, 0, 0, 0, 0); + p5.shader(myShader); + p5.sphere(20); + screenshot(); + }); + + visualTest('Combined vs split matrices', (p5, screenshot) => { + p5.createCanvas(50, 50, p5.WEBGL); + for (const space of ['Object', 'World', 'Camera']) { + const myShader = p5[base]().modify({ + [`Vertex get${space}Inputs`]: `(Vertex inputs) { + // No-op + return inputs; + }` + }); + p5.background(255); + p5.push(); + p5.lights(); + p5.fill('red'); + p5.noStroke(); + p5.translate(20, -10, 5); + p5.rotate(p5.PI * 0.1); + p5.camera(-800, 0, 0, 0, 0, 0); + p5.shader(myShader); + p5.box(30); + p5.pop(); + screenshot(); + } + }); + }); + } + }); }); diff --git a/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseColorShader/Camera space/000.png b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseColorShader/Camera space/000.png new file mode 100644 index 0000000000..5878075344 Binary files /dev/null and b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseColorShader/Camera space/000.png differ diff --git a/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseColorShader/Camera space/metadata.json b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseColorShader/Camera space/metadata.json new file mode 100644 index 0000000000..2d4bfe30da --- /dev/null +++ b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseColorShader/Camera space/metadata.json @@ -0,0 +1,3 @@ +{ + "numScreenshots": 1 +} \ No newline at end of file diff --git a/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseColorShader/Combined vs split matrices/000.png b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseColorShader/Combined vs split matrices/000.png new file mode 100644 index 0000000000..ee9d8c2748 Binary files /dev/null and b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseColorShader/Combined vs split matrices/000.png differ diff --git a/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseColorShader/Combined vs split matrices/001.png b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseColorShader/Combined vs split matrices/001.png new file mode 100644 index 0000000000..ee9d8c2748 Binary files /dev/null and b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseColorShader/Combined vs split matrices/001.png differ diff --git a/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseColorShader/Combined vs split matrices/002.png b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseColorShader/Combined vs split matrices/002.png new file mode 100644 index 0000000000..ee9d8c2748 Binary files /dev/null and b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseColorShader/Combined vs split matrices/002.png differ diff --git a/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseColorShader/Combined vs split matrices/metadata.json b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseColorShader/Combined vs split matrices/metadata.json new file mode 100644 index 0000000000..0c316a63ab --- /dev/null +++ b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseColorShader/Combined vs split matrices/metadata.json @@ -0,0 +1,3 @@ +{ + "numScreenshots": 3 +} \ No newline at end of file diff --git a/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseColorShader/Object space/000.png b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseColorShader/Object space/000.png new file mode 100644 index 0000000000..9692230099 Binary files /dev/null and b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseColorShader/Object space/000.png differ diff --git a/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseColorShader/Object space/metadata.json b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseColorShader/Object space/metadata.json new file mode 100644 index 0000000000..2d4bfe30da --- /dev/null +++ b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseColorShader/Object space/metadata.json @@ -0,0 +1,3 @@ +{ + "numScreenshots": 1 +} \ No newline at end of file diff --git a/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseColorShader/World space/000.png b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseColorShader/World space/000.png new file mode 100644 index 0000000000..075543a498 Binary files /dev/null and b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseColorShader/World space/000.png differ diff --git a/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseColorShader/World space/metadata.json b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseColorShader/World space/metadata.json new file mode 100644 index 0000000000..2d4bfe30da --- /dev/null +++ b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseColorShader/World space/metadata.json @@ -0,0 +1,3 @@ +{ + "numScreenshots": 1 +} \ No newline at end of file diff --git a/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseMaterialShader/Camera space/000.png b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseMaterialShader/Camera space/000.png new file mode 100644 index 0000000000..41f35381dd Binary files /dev/null and b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseMaterialShader/Camera space/000.png differ diff --git a/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseMaterialShader/Camera space/metadata.json b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseMaterialShader/Camera space/metadata.json new file mode 100644 index 0000000000..2d4bfe30da --- /dev/null +++ b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseMaterialShader/Camera space/metadata.json @@ -0,0 +1,3 @@ +{ + "numScreenshots": 1 +} \ No newline at end of file diff --git a/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseMaterialShader/Combined vs split matrices/000.png b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseMaterialShader/Combined vs split matrices/000.png new file mode 100644 index 0000000000..1b77843e48 Binary files /dev/null and b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseMaterialShader/Combined vs split matrices/000.png differ diff --git a/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseMaterialShader/Combined vs split matrices/001.png b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseMaterialShader/Combined vs split matrices/001.png new file mode 100644 index 0000000000..1b77843e48 Binary files /dev/null and b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseMaterialShader/Combined vs split matrices/001.png differ diff --git a/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseMaterialShader/Combined vs split matrices/002.png b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseMaterialShader/Combined vs split matrices/002.png new file mode 100644 index 0000000000..1b77843e48 Binary files /dev/null and b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseMaterialShader/Combined vs split matrices/002.png differ diff --git a/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseMaterialShader/Combined vs split matrices/metadata.json b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseMaterialShader/Combined vs split matrices/metadata.json new file mode 100644 index 0000000000..0c316a63ab --- /dev/null +++ b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseMaterialShader/Combined vs split matrices/metadata.json @@ -0,0 +1,3 @@ +{ + "numScreenshots": 3 +} \ No newline at end of file diff --git a/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseMaterialShader/Object space/000.png b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseMaterialShader/Object space/000.png new file mode 100644 index 0000000000..f6fab1658c Binary files /dev/null and b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseMaterialShader/Object space/000.png differ diff --git a/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseMaterialShader/Object space/metadata.json b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseMaterialShader/Object space/metadata.json new file mode 100644 index 0000000000..2d4bfe30da --- /dev/null +++ b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseMaterialShader/Object space/metadata.json @@ -0,0 +1,3 @@ +{ + "numScreenshots": 1 +} \ No newline at end of file diff --git a/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseMaterialShader/World space/000.png b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseMaterialShader/World space/000.png new file mode 100644 index 0000000000..f545c7da45 Binary files /dev/null and b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseMaterialShader/World space/000.png differ diff --git a/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseMaterialShader/World space/metadata.json b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseMaterialShader/World space/metadata.json new file mode 100644 index 0000000000..2d4bfe30da --- /dev/null +++ b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseMaterialShader/World space/metadata.json @@ -0,0 +1,3 @@ +{ + "numScreenshots": 1 +} \ No newline at end of file diff --git a/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseNormalShader/Camera space/000.png b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseNormalShader/Camera space/000.png new file mode 100644 index 0000000000..1a5e70fea6 Binary files /dev/null and b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseNormalShader/Camera space/000.png differ diff --git a/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseNormalShader/Camera space/metadata.json b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseNormalShader/Camera space/metadata.json new file mode 100644 index 0000000000..2d4bfe30da --- /dev/null +++ b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseNormalShader/Camera space/metadata.json @@ -0,0 +1,3 @@ +{ + "numScreenshots": 1 +} \ No newline at end of file diff --git a/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseNormalShader/Combined vs split matrices/000.png b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseNormalShader/Combined vs split matrices/000.png new file mode 100644 index 0000000000..62eb6320b8 Binary files /dev/null and b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseNormalShader/Combined vs split matrices/000.png differ diff --git a/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseNormalShader/Combined vs split matrices/001.png b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseNormalShader/Combined vs split matrices/001.png new file mode 100644 index 0000000000..62eb6320b8 Binary files /dev/null and b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseNormalShader/Combined vs split matrices/001.png differ diff --git a/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseNormalShader/Combined vs split matrices/002.png b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseNormalShader/Combined vs split matrices/002.png new file mode 100644 index 0000000000..62eb6320b8 Binary files /dev/null and b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseNormalShader/Combined vs split matrices/002.png differ diff --git a/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseNormalShader/Combined vs split matrices/metadata.json b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseNormalShader/Combined vs split matrices/metadata.json new file mode 100644 index 0000000000..0c316a63ab --- /dev/null +++ b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseNormalShader/Combined vs split matrices/metadata.json @@ -0,0 +1,3 @@ +{ + "numScreenshots": 3 +} \ No newline at end of file diff --git a/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseNormalShader/Object space/000.png b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseNormalShader/Object space/000.png new file mode 100644 index 0000000000..2f428f5f74 Binary files /dev/null and b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseNormalShader/Object space/000.png differ diff --git a/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseNormalShader/Object space/metadata.json b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseNormalShader/Object space/metadata.json new file mode 100644 index 0000000000..2d4bfe30da --- /dev/null +++ b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseNormalShader/Object space/metadata.json @@ -0,0 +1,3 @@ +{ + "numScreenshots": 1 +} \ No newline at end of file diff --git a/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseNormalShader/World space/000.png b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseNormalShader/World space/000.png new file mode 100644 index 0000000000..3e03667ebb Binary files /dev/null and b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseNormalShader/World space/000.png differ diff --git a/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseNormalShader/World space/metadata.json b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseNormalShader/World space/metadata.json new file mode 100644 index 0000000000..2d4bfe30da --- /dev/null +++ b/test/unit/visual/screenshots/WebGL/Hooks coordinate spaces/baseNormalShader/World space/metadata.json @@ -0,0 +1,3 @@ +{ + "numScreenshots": 1 +} \ No newline at end of file