Ça fout une claque, mais la dernière fois que j’ai touché du HLSL c’était pour mon travail de fin d’étude en 2005 dans un projet que nous avions baptisé Animator 4D. Bon, ce n’est pas le sujet qui nous amène ici, nous allons plutôt « simplifier » l’usage basique du WEBGL dans un contexte de rendu 2D.
Je vous recommande le très bon site
https://webglfundamentals.org/ ! Basé sur ses articles, et d’autres, je vais vous présenter une simplification de compréhension.
Pour info : je travaille en TypeScript (TS).
Charger une texture
C’est relativement simple si vous avez déjà votre img et son event load prêt. En dedans on va ajouter ceci :
const maTex = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, maTex); // Let's assume all images are not a power of 2 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); // Loaded: so copy in the texture gl.bindTexture(gl.TEXTURE_2D, maTex); // TODO why twice ? gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, monImgObjHTML);
On considérera que monImgObjHTML est votre HTMLImageElement chargé et la variable maTex un attribut de classe ou autre accessible.
Les shaders
En fait il vous en faut 2, un qui gère les vertex (vertex shader) et celui qui s’occupe de la couleur (fragment shader). Du coup, vous aurez besoin de vous faire 2 petites fonctions pour vous aider lors de l’init : une de chargement de shader et une de création de programme contenant vos shader. En résumé (détail sur le site de webglfundamentals) nous avons :
createShader(type: number, source: string): WebGLShader { const gl = this.context; // Create the shader const shader: WebGLShader = gl.createShader(type); // Set the source code gl.shaderSource(shader, source); // Compile the shader gl.compileShader(shader); // Check compilation status const success = gl.getShaderParameter(shader, gl.COMPILE_STATUS); if (!success) { // Something went wrong during compilation; get the error throw new Error('could not compile shader: ' + gl.getShaderInfoLog(shader)); } return shader; } createProgram(vertexShader: WebGLShader, fragmentShader: WebGLShader): WebGLProgram { const gl = this.context; // Create a program const program: WebGLProgram = gl.createProgram(); // Attach the shaders gl.attachShader(program, vertexShader); gl.attachShader(program, fragmentShader); // Link the program gl.linkProgram(program); // Check if it linked const success = gl.getProgramParameter(program, gl.LINK_STATUS); if (!success) { // something went wrong with the link throw new Error('program filed to link: ' + gl.getProgramInfoLog (program)); } return program; }
CreateShader va charger un texte plein (dans ma solution), ou ce que vous voulez, c’est bien foutu; et vous rendre un WebGLShader. Du coup, vous pouvez faire un WebGLProgram qui contiendra vos 2 shaders. Hop le tour est joué.
Évidemment cela demande une fonction d’init’ dans laquelle vous allez préciser les variables de vos shaders et appelez vos 2 fonctions.
maFonctionInit() { const vertexShader: WebGLShader = this.createShader(gl.VERTEX_SHADER, vertexShaderTxtCode); const fragmentShader: WebGLShader = this.createShader(gl.FRAGMENT_SHADER, fragmentShaderTxtCode); // Create program const program = createProgram(vertexShader, fragmentShader); // Buffers const colorLocation = gl.getUniformLocation(program, 'u_color'); ... }
Bon du coup on est initialisé, et on suppose que le workflow est bon, donc on peut dessiner 🙂 / faire son rendu !
Le rendu
render() { gl.bindTexture(gl.TEXTURE_2D, maTex); gl.enable(gl.BLEND); gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); // Tell WebGL to use our shader program pair gl.useProgram(program); gl.uniform3fv(colorLocation, new Float32Array([0.0, 0.0, 1.0])); ... }
On lie notre texture, on utilise notre programme et on envoie une valeur.
Bonus je vous ai indiqué pour le support de la transparence (genre votre texture en PNG avec transparence). Pensez au premultipliedAlpha: false pour votre canvas !
Pour la fin de la fonction, webglfundamentals vous donnera toutes les infos nécessaires, y compris des librairies de matrices m3/m4 adaptées et bien foutue.
Vous voilà orienté dans une des directions possible pour atteindre votre objectif de rendu 2D en 3D :p.