Différences
Ci-dessous, les différences entre deux révisions de la page.
Prochaine révision | Révision précédente | ||
ressource:code:processing:shaders [2022/09/06 12:23] gweltaz créée |
ressource:code:processing:shaders [2023/04/25 18:13] zemog [Variables uniform communes à tous les shaders dans Processing] |
||
---|---|---|---|
Ligne 1: | Ligne 1: | ||
+ | {{tag>processing shaders}} | ||
====== Initiation aux Shaders avec Processing ====== | ====== Initiation aux Shaders avec Processing ====== | ||
+ | ===== Le hello World des shaders ===== | ||
+ | |||
+ | Les fichiers ''vert.glsl'' et ''frag.glsl'' sont à placer dans le dossier ''data'' du sketch. | ||
+ | |||
+ | <accordion><panel title="shader_01"> | ||
+ | <code glsl vert.glsl> | ||
+ | uniform mat4 transformMatrix; | ||
+ | |||
+ | attribute vec4 position; | ||
+ | attribute vec4 color; | ||
+ | |||
+ | varying vec4 vertColor; | ||
+ | |||
+ | void main() { | ||
+ | gl_Position = transformMatrix * position; | ||
+ | | ||
+ | vertColor = color; | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | <code glsl frag.glsl> | ||
+ | #ifdef GL_ES | ||
+ | precision mediump float; | ||
+ | precision mediump int; | ||
+ | #endif | ||
+ | |||
+ | varying vec4 vertColor; | ||
+ | |||
+ | void main() { | ||
+ | gl_FragColor = vertColor; | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | <code java shader_01.pde> | ||
+ | PShader myShader; | ||
+ | int margin = 32; | ||
+ | |||
+ | void setup() { | ||
+ | size(800, 600, P2D); | ||
+ | |||
+ | myShader = loadShader("frag.glsl", "vert.glsl"); | ||
+ | noStroke(); | ||
+ | } | ||
+ | |||
+ | void draw() { | ||
+ | background(0); | ||
+ | shader(myShader); | ||
+ | fill(230, 120, 0); | ||
+ | rect(margin, margin, width - 2*margin, height - 2*margin); | ||
+ | resetShader(); // Désactive le shader, permet de redessiner normalement | ||
+ | } | ||
+ | </code> | ||
+ | </panel></accordion> | ||
+ | |||
+ | ===== Communication entre l'application et les shaders ===== | ||
+ | {{ :ressource:code:processing:shader_comm.png?direct |}} | ||
+ | |||
+ | L'application (programme Processing) peut envoyer des données vers les shaders par des variable déclarées avec le mot-clé ''uniform''. | ||
+ | |||
+ | ==== Fonctions GLSL ==== | ||
+ | * step(seuil, val) | ||
+ | Renvoi 0. si val < seuil, renvoi 1. si val > seuil | ||
+ | * smoothstep(seuil1, seuil2, val) | ||
+ | * clamp() | ||
+ | * pow() | ||
+ | * fract() | ||
+ | Returns the fractional part of a number | ||
+ | * mod(a, b) | ||
+ | a modulo b | ||
+ | * length() | ||
+ | * atan(y,x) | ||
+ | * mix(v1, v2, pct) | ||
+ | Interpolation linéaire entre v1 et v2 en fonction de 'pct' | ||
+ | * sign(float val) | ||
+ | Renvoi -1 si val est négatif, 1 si val est positif | ||
+ | |||
+ | ==== Fonctions Processing pour transmettre des données ==== | ||
+ | set(String name, int x) | ||
+ | set(String name, int x, int y) -> vec2 | ||
+ | set(String name, int x, int y, int z) -> vec3 | ||
+ | set(String name, int x, int y, int z, int w) -> vec4 | ||
+ | set(String name, float x, ...) | ||
+ | set(String name, PVector vec) -> vec3 | ||
+ | | ||
+ | set(String name, int[] vec, int ncoords) // Jusqu'à 4 coordonnées par élément | ||
+ | set(String name, float[] vec, int ncoords) | ||
+ | | ||
+ | set(String name, PMatrix2D mat) -> mat2 | ||
+ | set(String name, PMatrix3D mat) -> mat4 | ||
+ | | ||
+ | set(String name, PImage tex) -> sampler2D | ||
+ | |||
+ | > Attention ! Lorsqu'on transmet des nombres entiers, comme par exemple : ''set("u_resolution", 512, 512)'', soyez sûr d'avoir déclaré les variables ''uniform'' pour des types entiers, comme : ''ivec2'', ''ivec3''... \\ | ||
+ | > Une autre solution est de les convertir en nombres flottants avant de les transmettre : ''set("u_resolution", float(512), float(512))'' | ||
+ | |||
+ | ==== Variables uniform communes à tous les shaders dans Processing ==== | ||
+ | Certaines variables ''uniform'' sont définies dans Processing et sont disponibles dans tous les shaders.\\ | ||
+ | Elles sont déclarées dans le fichier ''PShader.java'' de Processing. | ||
+ | <code glsl> | ||
+ | uniform mat4 transformMatrix; // la matrice de model view projection pour transformer les coordonnées du vertex du model space au clip space | ||
+ | uniform mat4 projectionMatrix; // la matrice de projection permet de passer du camera space au clip space | ||
+ | uniform mat4 modelviewMatrix; // la matrice modelview permet de passer du model space au world space puis au camera space | ||
+ | uniform vec2 resolution; // contient la résolution de notre fenêtre | ||
+ | uniform vec4 viewport; // contient la position de notre fenêtre ainsi que sa résolution | ||
+ | </code> | ||
+ | |||
+ | ===== Textures ===== | ||
+ | Pour sampler un texel en GLSL (extraire la couleur d'une texture à un point donné), on utilise la fonction: | ||
+ | ''texture2D(sampler2D image, vec2 uv)'' \\ | ||
+ | Les coordonnes UV doivent être comprises entre 0.0 et 1.0 \\ | ||
+ | Elles pour origine le coin bas-gauche (0, 0) contrairement aux coordonnées d'écran, qui ont pour origine le coin haut-gauche. | ||
+ | |||
+ | ===== Utilisation d'un buffer hors-écran ===== | ||
+ | Pratique pour créer des effets avec retour d'information (feedback), comme par exemple un effet de réaction-diffusion. | ||
+ | |||
+ | <code java> | ||
+ | PGraphics buffer = createGraphics(x, y, P2D); | ||
+ | |||
+ | buffer.beginDraw(); | ||
+ | buffer.shader(myShader); | ||
+ | buffer.rect(0, 0, buffer.width, buffer.height); | ||
+ | buffer.endDraw(); | ||
+ | |||
+ | image(buffer, 0, 0); | ||
+ | </code> | ||
+ | |||
+ | |||
+ | ===== Fonctions utiles ===== | ||
+ | |||
+ | === Couleur === | ||
+ | |||
+ | == Luminance == | ||
+ | <code glsl> | ||
+ | float luma(vec4 color) { | ||
+ | return dot(color.rgb, vec3(0.299, 0.587, 0.114)); | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | == Brightness == | ||
+ | <code glsl> | ||
+ | float brightness(vec4 color) { | ||
+ | return dot( color.rgb , vec3( 0.2126 , 0.7152 , 0.0722 )); | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | == HSB -> RGB == | ||
+ | <code glsl> | ||
+ | vec3 hsb2rgb( in vec3 c ){ | ||
+ | vec3 rgb = clamp(abs(mod(c.x*6.0+vec3(0.0,4.0,2.0), | ||
+ | 6.0)-3.0)-1.0, | ||
+ | 0.0, | ||
+ | 1.0 ); | ||
+ | rgb = rgb*rgb*(3.0-2.0*rgb); | ||
+ | return c.z * mix(vec3(1.0), rgb, c.y); | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | == RGB -> HSB == | ||
+ | <code glsl> | ||
+ | vec3 rgb2hsb( in vec3 c ){ | ||
+ | vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); | ||
+ | vec4 p = mix(vec4(c.bg, K.wz), | ||
+ | vec4(c.gb, K.xy), | ||
+ | step(c.b, c.g)); | ||
+ | vec4 q = mix(vec4(p.xyw, c.r), | ||
+ | vec4(c.r, p.yzx), | ||
+ | step(p.x, c.r)); | ||
+ | float d = q.x - min(q.w, q.y); | ||
+ | float e = 1.0e-10; | ||
+ | return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), | ||
+ | d / (q.x + e), | ||
+ | q.x); | ||
+ | } | ||
+ | </code> | ||
+ | === Random === | ||
+ | <code glsl> | ||
+ | float random2d(vec2 coord) | ||
+ | { | ||
+ | return fract(sin(dot(coord.xy, vec2(12.9898, 78.233))) * 43758.5453123); | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | === Noise === | ||
+ | == Gradient noise == | ||
+ | <code glsl> | ||
+ | // 2D Noise based on Morgan McGuire @morgan3d | ||
+ | // https://www.shadertoy.com/view/4dS3Wd | ||
+ | float noise (in vec2 coord) { | ||
+ | vec2 i = floor(coord); | ||
+ | vec2 f = fract(coord); | ||
+ | |||
+ | // Four corners in 2D of a tile | ||
+ | float a = random(i); | ||
+ | float b = random(i + vec2(1.0, 0.0)); | ||
+ | float c = random(i + vec2(0.0, 1.0)); | ||
+ | float d = random(i + vec2(1.0, 1.0)); | ||
+ | |||
+ | // Smooth Interpolation | ||
+ | |||
+ | // Cubic Hermite Curve. Same as SmoothStep() | ||
+ | vec2 u = f*f*(3.0-2.0*f); | ||
+ | // u = smoothstep(0.,1.,f); | ||
+ | |||
+ | // Mix 4 coorners percentages | ||
+ | return mix(a, b, u.x) + | ||
+ | (c - a)* u.y * (1.0 - u.x) + | ||
+ | (d - b) * u.x * u.y; | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | == Fractional Brownian Motion == | ||
+ | <code glsl> | ||
+ | float hash(vec2 coord) | ||
+ | { | ||
+ | return fract(sin(dot(coord.xy, vec2(12.9898, 78.233))) * 43758.5453123); | ||
+ | } | ||
+ | |||
+ | float noise(vec2 U) | ||
+ | { | ||
+ | vec2 id = floor(U); | ||
+ | U = fract(U); | ||
+ | U *= U * ( 3. - 2. * U ); | ||
+ | |||
+ | vec2 A = vec2( hash(id), hash(id + vec2(0,1)) ), | ||
+ | B = vec2( hash(id + vec2(1,0)), hash(id + vec2(1,1)) ), | ||
+ | C = mix( A, B, U.x); | ||
+ | |||
+ | return mix( C.x, C.y, U.y ); | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | fBM stands for Fractional Brownian Motion | ||
+ | https://iquilezles.org/articles/fbm/ | ||
+ | Set octave to 8 for a detailed noise | ||
+ | A value of 1.0 for H is good | ||
+ | */ | ||
+ | float fbm(vec2 x, float H, int octave) | ||
+ | { | ||
+ | float G = exp2(-H); | ||
+ | float f = 1.0; | ||
+ | float a = 1.0; | ||
+ | float t = 0.0; | ||
+ | for( int i=0; i<octave; i++ ) | ||
+ | { | ||
+ | t += a*noise(f*x); | ||
+ | f *= 2.0; | ||
+ | a *= G; | ||
+ | } | ||
+ | return t; | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | == Simplex noise == | ||
+ | <code glsl> | ||
+ | // | ||
+ | // Description : GLSL 2D simplex noise function | ||
+ | // Author : Ian McEwan, Ashima Arts | ||
+ | // Maintainer : ijm | ||
+ | // Lastmod : 20110822 (ijm) | ||
+ | // License : | ||
+ | // Copyright (C) 2011 Ashima Arts. All rights reserved. | ||
+ | // Distributed under the MIT License. See LICENSE file. | ||
+ | // https://github.com/ashima/webgl-noise | ||
+ | // | ||
+ | |||
+ | // Some useful functions | ||
+ | vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; } | ||
+ | vec2 mod289(vec2 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; } | ||
+ | vec3 permute(vec3 x) { return mod289(((x*34.0)+1.0)*x); } | ||
+ | |||
+ | float snoise(vec2 v) { | ||
+ | |||
+ | // Precompute values for skewed triangular grid | ||
+ | const vec4 C = vec4(0.211324865405187, | ||
+ | // (3.0-sqrt(3.0))/6.0 | ||
+ | 0.366025403784439, | ||
+ | // 0.5*(sqrt(3.0)-1.0) | ||
+ | -0.577350269189626, | ||
+ | // -1.0 + 2.0 * C.x | ||
+ | 0.024390243902439); | ||
+ | // 1.0 / 41.0 | ||
+ | |||
+ | // First corner (x0) | ||
+ | vec2 i = floor(v + dot(v, C.yy)); | ||
+ | vec2 x0 = v - i + dot(i, C.xx); | ||
+ | |||
+ | // Other two corners (x1, x2) | ||
+ | vec2 i1 = vec2(0.0); | ||
+ | i1 = (x0.x > x0.y)? vec2(1.0, 0.0):vec2(0.0, 1.0); | ||
+ | vec2 x1 = x0.xy + C.xx - i1; | ||
+ | vec2 x2 = x0.xy + C.zz; | ||
+ | |||
+ | // Do some permutations to avoid | ||
+ | // truncation effects in permutation | ||
+ | i = mod289(i); | ||
+ | vec3 p = permute( | ||
+ | permute( i.y + vec3(0.0, i1.y, 1.0)) | ||
+ | + i.x + vec3(0.0, i1.x, 1.0 )); | ||
+ | |||
+ | vec3 m = max(0.5 - vec3( | ||
+ | dot(x0,x0), | ||
+ | dot(x1,x1), | ||
+ | dot(x2,x2) | ||
+ | ), 0.0); | ||
+ | |||
+ | m = m*m ; | ||
+ | m = m*m ; | ||
+ | |||
+ | // Gradients: | ||
+ | // 41 pts uniformly over a line, mapped onto a diamond | ||
+ | // The ring size 17*17 = 289 is close to a multiple | ||
+ | // of 41 (41*7 = 287) | ||
+ | |||
+ | vec3 x = 2.0 * fract(p * C.www) - 1.0; | ||
+ | vec3 h = abs(x) - 0.5; | ||
+ | vec3 ox = floor(x + 0.5); | ||
+ | vec3 a0 = x - ox; | ||
+ | |||
+ | // Normalise gradients implicitly by scaling m | ||
+ | // Approximation of: m *= inversesqrt(a0*a0 + h*h); | ||
+ | m *= 1.79284291400159 - 0.85373472095314 * (a0*a0+h*h); | ||
+ | |||
+ | // Compute final noise value at P | ||
+ | vec3 g = vec3(0.0); | ||
+ | g.x = a0.x * x0.x + h.x * x0.y; | ||
+ | g.yz = a0.yz * vec2(x1.x,x2.x) + h.yz * vec2(x1.y,x2.y); | ||
+ | return 130.0 * dot(m, g); | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | === Rotations === | ||
+ | == 2D == | ||
+ | <code glsl> | ||
+ | mat2 rotation2d(float a) { | ||
+ | float c=cos(a); | ||
+ | float s=sin(a); | ||
+ | return mat2(c,-s,s,c); | ||
+ | } | ||
+ | |||
+ | vec2 rotate(vec2 v, float angle) { | ||
+ | return rotation2d(angle) * v; | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | == 3D == | ||
+ | <code glsl> | ||
+ | mat4 rotation3d(vec3 axis, float angle) { | ||
+ | axis = normalize(axis); | ||
+ | float s = sin(angle); | ||
+ | float c = cos(angle); | ||
+ | float oc = 1.0 - c; | ||
+ | |||
+ | return mat4( | ||
+ | oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, 0.0, | ||
+ | oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0.0, | ||
+ | oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0.0, | ||
+ | 0.0, 0.0, 0.0, 1.0 | ||
+ | ); | ||
+ | } | ||
+ | |||
+ | vec3 rotate(vec3 v, vec3 axis, float angle) { | ||
+ | return (rotation3d(axis, angle) * vec4(v, 1.0)).xyz; | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | === Flou Gaussien === | ||
+ | Code optimisé, d'après https://www.rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling/ \\ | ||
+ | A exécuter en deux passes : horizontale et verticale \\ | ||
+ | |||
+ | <code glsl> | ||
+ | vec4 blur(sampler2D image, vec2 uv, vec2 resolution, vec2 direction) { | ||
+ | const float offset[3] = float[](0.0, 1.3846153846, 3.2307692308); | ||
+ | const float weight[3] = float[](0.2270270270, 0.3162162162, 0.0702702703); | ||
+ | |||
+ | vec4 colorOut = texture2D(image, uv / resolution) * weight[0]; | ||
+ | for (int i=1; i<3; i++) { | ||
+ | vec3 color = texture2D(image, (uv + direction * offset[i]) / resolution); | ||
+ | color += texture2D(image, (uv - direction * offset[i]) / resolution); | ||
+ | colorOut += color * weight[i]; | ||
+ | } | ||
+ | return colorOut; | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | ===== Librairies Processing ===== | ||
+ | Quelques librairies externes pour l'utilisation de shaders dans Processing : | ||
+ | * https://github.com/diwi/PixelFlow | ||
+ | |||
+ | ===== Ressources ===== | ||
+ | Liste de liens incontournables pour approfondir et aller plus loin... | ||
+ | * https://thebookofshaders.com/ | ||
+ | * https://www.shadertoy.com | ||
+ | * https://iquilezles.org/articles/functions/ |