diff --git a/src/webgl/p5.Shader.js b/src/webgl/p5.Shader.js index c0bfe1e53d..48ac91a8c3 100644 --- a/src/webgl/p5.Shader.js +++ b/src/webgl/p5.Shader.js @@ -9,13 +9,118 @@ import p5 from '../core/main'; /** - * Shader class for WEBGL Mode + * A class to describe a shader program. + * + * Each `p5.Shader` object contains a shader program that runs on the graphics + * processing unit (GPU). Shaders can process many pixels or vertices at the + * same time, making them fast for many graphics tasks. They’re written in a + * language called + * GLSL + * and run along with the rest of the code in a sketch. + * + * A shader program consists of two files, a vertex shader and a fragment + * shader. The vertex shader affects where 3D geometry is drawn on the screen + * and the fragment shader affects color. Once the `p5.Shader` object is + * created, it can be used with the shader() + * function, as in `shader(myShader)`. + * + * Note: createShader(), + * createFilterShader(), and + * loadShader() are the recommended ways to + * create an instance of this class. + * * @class p5.Shader * @constructor - * @param {p5.RendererGL} renderer an instance of p5.RendererGL that - * will provide the GL context for this new p5.Shader - * @param {String} vertSrc source code for the vertex shader (as a string) - * @param {String} fragSrc source code for the fragment shader (as a string) + * @param {p5.RendererGL} renderer WebGL context for this shader. + * @param {String} vertSrc source code for the vertex shader program. + * @param {String} fragSrc source code for the fragment shader program. + * + * @example + *
+ * // Note: A "uniform" is a global variable within a shader program.
+ *
+ * // Create a string with the vertex shader program.
+ * // The vertex shader is called for each vertex.
+ * let vertSrc = `
+ * precision highp float;
+ * uniform mat4 uModelViewMatrix;
+ * uniform mat4 uProjectionMatrix;
+ *
+ * attribute vec3 aPosition;
+ * attribute vec2 aTexCoord;
+ * varying vec2 vTexCoord;
+ *
+ * void main() {
+ * vTexCoord = aTexCoord;
+ * vec4 positionVec4 = vec4(aPosition, 1.0);
+ * gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;
+ * }
+ * `;
+ *
+ * // Create a string with the fragment shader program.
+ * // The fragment shader is called for each pixel.
+ * let fragSrc = `
+ * precision highp float;
+ *
+ * void main() {
+ * // Set each pixel's RGBA value to yellow.
+ * gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);
+ * }
+ * `;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Shader object.
+ * let myShader = createShader(vertSrc, fragSrc);
+ *
+ * // Apply the p5.Shader object.
+ * shader(myShader);
+ *
+ * // Style the drawing surface.
+ * noStroke();
+ *
+ * // Add a plane as a drawing surface.
+ * plane(100, 100);
+ *
+ * describe('A yellow square.');
+ * }
+ *
+ *
+ * // Note: A "uniform" is a global variable within a shader program.
+ *
+ * let mandelbrot;
+ *
+ * // Load the shader and create a p5.Shader object.
+ * function preload() {
+ * mandelbrot = loadShader('assets/shader.vert', 'assets/shader.frag');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Use the p5.Shader object.
+ * shader(mandelbrot);
+ *
+ * // Set the shader uniform p to an array.
+ * mandelbrot.setUniform('p', [-0.74364388703, 0.13182590421]);
+ *
+ * describe('A fractal image zooms in and out of focus.');
+ * }
+ *
+ * function draw() {
+ * // Set the shader uniform r to a value that oscillates between 0 and 2.
+ * mandelbrot.setUniform('r', sin(frameCount * 0.01) + 1);
+ *
+ * // Add a quad as a display surface for the shader.
+ * quad(-1, -1, 1, -1, 1, 1, -1, 1);
+ * }
+ *
+ *
+ * // Note: A "uniform" is a global variable within a shader program.
+ *
+ * // Create a string with the vertex shader program.
+ * // The vertex shader is called for each vertex.
+ * let vertSrc = `
+ * precision highp float;
+ * uniform mat4 uModelViewMatrix;
+ * uniform mat4 uProjectionMatrix;
+ *
+ * attribute vec3 aPosition;
+ * attribute vec2 aTexCoord;
+ * varying vec2 vTexCoord;
+ *
+ * void main() {
+ * vTexCoord = aTexCoord;
+ * vec4 positionVec4 = vec4(aPosition, 1.0);
+ * gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;
+ * }
+ * `;
+ *
+ * // Create a string with the fragment shader program.
+ * // The fragment shader is called for each pixel.
+ * let fragSrc = `
+ * precision mediump float;
+ * varying vec2 vTexCoord;
+ *
+ * void main() {
+ * vec2 uv = vTexCoord;
+ * vec3 color = vec3(uv.x, uv.y, min(uv.x + uv.y, 1.0));
+ * gl_FragColor = vec4(color, 1.0);\
+ * }
+ * `;
+ *
+ * let pg;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * background(200);
+ *
+ * // Create a p5.Shader object.
+ * let original = createShader(vertSrc, fragSrc);
+ *
+ * // Compile the p5.Shader object.
+ * shader(original);
+ *
+ * // Create a p5.Graphics object.
+ * pg = createGraphics(50, 50, WEBGL);
+ *
+ * // Copy the original shader to the p5.Graphics object.
+ * let copied = original.copyToContext(pg);
+ *
+ * // Apply the copied shader to the p5.Graphics object.
+ * pg.shader(copied);
+ *
+ * // Style the display surface.
+ * pg.noStroke();
+ *
+ * // Add a display surface for the shader.
+ * pg.plane(50, 50);
+ *
+ * describe('A square with purple-blue gradient on its surface drawn against a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Draw the p5.Graphics object to the main canvas.
+ * image(pg, -25, -25);
+ * }
+ *
+ *
- * let graphic = createGraphics(200, 200, WEBGL);
- * let graphicShader = graphic.createShader(vert, frag);
- * graphic.shader(graphicShader); // Use graphicShader on the graphic
+ * // Note: A "uniform" is a global variable within a shader program.
*
- * let mainShader = graphicShader.copyToContext(window);
- * shader(mainShader); // Use `mainShader` on the main canvas
+ * // Create a string with the vertex shader program.
+ * // The vertex shader is called for each vertex.
+ * let vertSrc = `
+ * precision highp float;
+ * uniform mat4 uModelViewMatrix;
+ * uniform mat4 uProjectionMatrix;
+ *
+ * attribute vec3 aPosition;
+ * attribute vec2 aTexCoord;
+ * varying vec2 vTexCoord;
+ *
+ * void main() {
+ * vTexCoord = aTexCoord;
+ * vec4 positionVec4 = vec4(aPosition, 1.0);
+ * gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;
+ * }
+ * `;
+ *
+ * // Create a string with the fragment shader program.
+ * // The fragment shader is called for each pixel.
+ * let fragSrc = `
+ * precision mediump float;
+ *
+ * varying vec2 vTexCoord;
+ *
+ * void main() {
+ * vec2 uv = vTexCoord;
+ * vec3 color = vec3(uv.x, uv.y, min(uv.x + uv.y, 1.0));
+ * gl_FragColor = vec4(color, 1.0);
+ * }
+ * `;
+ *
+ * let copied;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Graphics object.
+ * let pg = createGraphics(25, 25, WEBGL);
+ *
+ * // Create a p5.Shader object.
+ * let original = pg.createShader(vertSrc, fragSrc);
+ *
+ * // Compile the p5.Shader object.
+ * pg.shader(original);
+ *
+ * // Copy the original shader to the main canvas.
+ * copied = original.copyToContext(window);
+ *
+ * // Apply the copied shader to the main canvas.
+ * shader(copied);
+ *
+ * describe('A rotating cube with a purple-blue gradient on its surface drawn against a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Rotate around the x-, y-, and z-axes.
+ * rotateX(frameCount * 0.01);
+ * rotateY(frameCount * 0.01);
+ * rotateZ(frameCount * 0.01);
+ *
+ * // Draw the box.
+ * box(50);
+ * }
*
*
- * // Click within the image to toggle the value of uniforms
- * // Note: for an alternative approach to the same example,
- * // involving toggling between shaders please refer to:
- * // https://p5js.org/reference/#/p5/shader
- *
- * let grad;
- * let showRedGreen = false;
- *
- * function preload() {
- * // note that we are using two instances
- * // of the same vertex and fragment shaders
- * grad = loadShader('assets/shader.vert', 'assets/shader-gradient.frag');
+ * // Note: A "uniform" is a global variable within a shader program.
+ *
+ * // Create a string with the vertex shader program.
+ * // The vertex shader is called for each vertex.
+ * let vertSrc = `
+ * precision highp float;
+ * uniform mat4 uModelViewMatrix;
+ * uniform mat4 uProjectionMatrix;
+ *
+ * attribute vec3 aPosition;
+ * attribute vec2 aTexCoord;
+ * varying vec2 vTexCoord;
+ *
+ * void main() {
+ * vTexCoord = aTexCoord;
+ * vec4 positionVec4 = vec4(aPosition, 1.0);
+ * gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;
* }
+ * `;
+ *
+ * // Create a string with the fragment shader program.
+ * // The fragment shader is called for each pixel.
+ * let fragSrc = `
+ * precision mediump float;
+ *
+ * uniform float r;
+ *
+ * void main() {
+ * gl_FragColor = vec4(r, 1.0, 1.0, 1.0);
+ * }
+ * `;
*
* function setup() {
* createCanvas(100, 100, WEBGL);
- * shader(grad);
+ *
+ * // Create a p5.Shader object.
+ * let myShader = createShader(vertSrc, fragSrc);
+ *
+ * // Apply the p5.Shader object.
+ * shader(myShader);
+ *
+ * // Set the r uniform to 0.5.
+ * myShader.setUniform('r', 0.5);
+ *
+ * // Style the drawing surface.
* noStroke();
*
- * describe(
- * 'canvas toggles between a circular gradient of orange and blue vertically. and a circular gradient of red and green moving horizontally when mouse is clicked/pressed.'
- * );
+ * // Add a plane as a drawing surface for the shader.
+ * plane(100, 100);
+ *
+ * describe('A cyan square.');
+ * }
+ *
+ *
+ * // Note: A "uniform" is a global variable within a shader program.
+ *
+ * // Create a string with the vertex shader program.
+ * // The vertex shader is called for each vertex.
+ * let vertSrc = `
+ * precision highp float;
+ * uniform mat4 uModelViewMatrix;
+ * uniform mat4 uProjectionMatrix;
+ *
+ * attribute vec3 aPosition;
+ * attribute vec2 aTexCoord;
+ * varying vec2 vTexCoord;
+ *
+ * void main() {
+ * vTexCoord = aTexCoord;
+ * vec4 positionVec4 = vec4(aPosition, 1.0);
+ * gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;
+ * }
+ * `;
+ *
+ * // Create a string with the fragment shader program.
+ * // The fragment shader is called for each pixel.
+ * let fragSrc = `
+ * precision mediump float;
+ *
+ * uniform float r;
+ *
+ * void main() {
+ * gl_FragColor = vec4(r, 1.0, 1.0, 1.0);
+ * }
+ * `;
+ *
+ * let myShader;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Shader object.
+ * myShader = createShader(vertSrc, fragSrc);
+ *
+ * // Compile and apply the p5.Shader object.
+ * shader(myShader);
+ *
+ * describe('A square oscillates color between cyan and white.');
* }
*
* function draw() {
- * // update the offset values for each scenario,
- * // moving the "grad" shader in either vertical or
- * // horizontal direction each with differing colors
- *
- * if (showRedGreen === true) {
- * grad.setUniform('colorCenter', [1, 0, 0]);
- * grad.setUniform('colorBackground', [0, 1, 0]);
- * grad.setUniform('offset', [sin(millis() / 2000), 1]);
- * } else {
- * grad.setUniform('colorCenter', [1, 0.5, 0]);
- * grad.setUniform('colorBackground', [0.226, 0, 0.615]);
- * grad.setUniform('offset', [0, sin(millis() / 2000) + 1]);
+ * background(200);
+ *
+ * // Style the drawing surface.
+ * noStroke();
+ *
+ * // Update the r uniform.
+ * let nextR = 0.5 * (sin(frameCount * 0.01) + 1);
+ * myShader.setUniform('r', nextR);
+ *
+ * // Add a plane as a drawing surface.
+ * plane(100, 100);
+ * }
+ *
+ *
+ * // Note: A "uniform" is a global variable within a shader program.
+ *
+ * // Create a string with the vertex shader program.
+ * // The vertex shader is called for each vertex.
+ * let vertSrc = `
+ * precision highp float;
+ * uniform mat4 uModelViewMatrix;
+ * uniform mat4 uProjectionMatrix;
+ *
+ * attribute vec3 aPosition;
+ * attribute vec2 aTexCoord;
+ * varying vec2 vTexCoord;
+ *
+ * void main() {
+ * vTexCoord = aTexCoord;
+ * vec4 positionVec4 = vec4(aPosition, 1.0);
+ * gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;
+ * }
+ * `;
+ *
+ * // Create a string with the fragment shader program.
+ * // The fragment shader is called for each pixel.
+ * let fragSrc = `
+ * precision highp float;
+ * uniform vec2 p;
+ * uniform float r;
+ * const int numIterations = 500;
+ * varying vec2 vTexCoord;
+ *
+ * void main() {
+ * vec2 c = p + gl_FragCoord.xy * r;
+ * vec2 z = c;
+ * float n = 0.0;
+ *
+ * for (int i = numIterations; i > 0; i--) {
+ * if (z.x * z.x + z.y * z.y > 4.0) {
+ * n = float(i) / float(numIterations);
+ * break;
+ * }
+ *
+ * z = vec2(z.x * z.x - z.y * z.y, 2.0 * z.x * z.y) + c;
* }
- * quad(-1, -1, 1, -1, 1, 1, -1, 1);
+ *
+ * gl_FragColor = vec4(
+ * 0.5 - cos(n * 17.0) / 2.0,
+ * 0.5 - cos(n * 13.0) / 2.0,
+ * 0.5 - cos(n * 23.0) / 2.0,
+ * 1.0
+ * );
* }
+ * `;
+ *
+ * let mandelbrot;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Shader object.
+ * mandelbrot = createShader(vertSrc, fragSrc);
+ *
+ * // Compile and apply the p5.Shader object.
+ * shader(mandelbrot);
+ *
+ * // Set the shader uniform p to an array.
+ * // p is the center point of the Mandelbrot image.
+ * mandelbrot.setUniform('p', [-0.74364388703, 0.13182590421]);
*
- * function mouseClicked() {
- * showRedGreen = !showRedGreen;
+ * describe('A fractal image zooms in and out of focus.');
+ * }
+ *
+ * function draw() {
+ * // Set the shader uniform r to a value that oscillates
+ * // between 0 and 0.005.
+ * // r is the size of the image in Mandelbrot-space.
+ * let radius = 0.005 * (sin(frameCount * 0.01) + 1);
+ * mandelbrot.setUniform('r', radius);
+ *
+ * // Style the drawing surface.
+ * noStroke();
+ *
+ * // Add a plane as a drawing surface.
+ * plane(100, 100);
* }
*
*