web-dev-qa-db-fra.com

WebGL - existe-t-il une alternative à l'incorporation de shaders en HTML?

Le moyen le plus répandu d’utiliser les shaders GLSL dans WebGL semble être de les incorporer dans le fichier html principal. Les shaders de vertex et de fragments sont incorporés dans des balises telles que:

<script id="shader-fs" type="x-shader/x-fragment">

C’est la même convention que je vois dans les exemples WebGL de la page Mozilla Developer Network.

Cela fonctionne bien pour les applications simples, mais lorsque vous avez une application complexe avec un certain nombre de nuanceurs, le fichier html est encombré. (Je continue à éditer le mauvais shader!) De plus, si vous souhaitez réutiliser vos shaders, ce schéma est peu pratique.

Je pensais donc à placer ces shaders dans des fichiers XML séparés et à les charger à l'aide de XMLHttpRequest (). Puis j'ai vu que quelqu'un d'autre avait la même idée:

http://webreflection.blogspot.com/2010/09/fragment-and-vertex-shaders-my-way-to.html

J'aime la suggestion d'utiliser des fichiers .c, car cela vous donne la coloration syntaxique et d'autres commodités pour l'éditeur pour GLSL.

Mais le problème avec l’approche ci-dessus est que (pour autant que je sache), XMLHttpRequest () ne puisse pas charger un fichier .c local - c’est-à-dire côté client - pendant que vous développez et testez l’application WebGL. Mais il est fastidieux de continuer à le télécharger sur le serveur pendant ce processus.

Donc, si je veux garder les shaders du fichier html, est-ce que la seule option pour les incorporer en tant que chaînes dans le code? Mais cela rendrait difficile l'écriture ainsi que le débogage ...

J'apprécierais vos suggestions sur la gestion de plusieurs shaders GLSL dans les applications WebGL.

Cordialement

Edit (05 mai 2011)

Étant donné que j'utilise un Mac pour le développement, j'ai décidé d'activer le serveur Apache et de placer mon code Webgl sous http: // localhost/~ username/ . Ceci évite le problème fichier: le protocole est désactivé pendant le développement. Maintenant, le code de chargement de fichier javascript fonctionne localement puisque http: est utilisé, plutôt que fichier :. Je pensais juste que je mettrais ça ici au cas où quelqu'un le trouverait utile.

30
M-V

Eh oui, un serveur local est vraiment le seul moyen d'aller si vous voulez utiliser XHR. J'ai écrit de nombreuses leçons sur WebGL et j'ai souvent envisagé de ne pas incorporer les shaders dans le code HTML, mais j'ai été effrayé par toutes les explications relatives à la sécurité Web que je devrais écrire ...

Heureusement, il est très facile de faire fonctionner un serveur. Il suffit d'ouvrir un shell alors

cd path-to-files
python -m SimpleHTTPServer

Puis pointez votre navigateur sur

http://localhost:8000

Cela fonctionne pour des cas simples comme les textures et GLSL. Pour le streaming vidéo et audio, voir

Quelle est une alternative plus rapide au http.server de Python (ou SimpleHTTPServer)?

D'un autre côté tous les navigateurs prenant en charge WebGL supporte Littéraux de modèle ES6 à plusieurs lignes donc si vous ne vous souciez pas des anciens navigateurs, vous pouvez simplement mettre vos shaders en JavaScript en utilisant des backticks comme celui-ci.

var vertexShaderSource = `
  attribute vec4 position;
  uniform mat4 u_matrix;

  void main() {
    gl_Position = u_matrix * position;
  }
`;
16
Giles Thomas

J'utilise require.js 's text plugin .

Voici un extrait:

define(
    /* Dependencies (I also loaded the gl-matrix library) */
    ["glmatrix", "text!shaders/fragment.shader", "text!shaders/vertex.shader"],

    /* Callback when all has been loaded */
    function(glmatrix, fragmentShaderCode, vertexShaderCode) {
        ....
        var vertexShader = gl.createShader(gl.VERTEX_SHADER);
        gl.shaderSource(vertexShader, vertexShaderCode);
        gl.compileShader(vertexShader);
        ....
    }
);

La structure du répertoire est la suivante:

~require-gl-shaders/
 |~js/
 | |+lib/
 | |~shaders/
 | | |-fragment.shader
 | | `-vertex.shader
 | |-glmatrix.js - gl-matrix library
 | |-shader.js
 | |-text.js     - require.js's text plugin
 |-index.html
 |-main.js
 `-require.js    - the require.js library

Personnellement, j’ai eu un peu d’apprentissage, mais cela m’a vraiment aidé à garder un code plus propre.

14
Valer

Mon copain a créé un objet Nice utils avec des fonctions pratiques pour ce type de scénario. Vous stockeriez vos shaders dans des fichiers texte dans un dossier appelé "shaders":

nom de fichier: vertex.shader

attribute vec3 blah;

uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
uniform mat3 uNMatrix;

void main(void) {
    magic goes here
}

nom du fichier: fragment.shader

#ifdef GL_ES
    precision highp float;
#endif

varying vec4 vYadaYada;
uniform sampler2D uSampler;

void main(void) {
    fragic magic goes here      
}

Et vous appelez simplement cela pour créer un nouveau programme avec ces fichiers de shader:

var shaderProgram = utils.addShaderProg(gl, 'vertex.shader', 'fragment.shader');    

Et voici l'objet sweet util pour gérer biz:

utils = {};

utils.allShaders = {};
utils.SHADER_TYPE_FRAGMENT = "x-shader/x-fragment";
utils.SHADER_TYPE_VERTEX = "x-shader/x-vertex";

utils.addShaderProg = function (gl, vertex, fragment) {

    utils.loadShader(vertex, utils.SHADER_TYPE_VERTEX);
    utils.loadShader(fragment, utils.SHADER_TYPE_FRAGMENT);

    var vertexShader = utils.getShader(gl, vertex);
    var fragmentShader = utils.getShader(gl, fragment);

    var prog = gl.createProgram();
    gl.attachShader(prog, vertexShader);
    gl.attachShader(prog, fragmentShader);
    gl.linkProgram(prog);

    if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) {alert("Could not initialise main shaders");}

    return prog;
};

utils.loadShader = function(file, type) {
    var cache, shader;

    $.ajax({
        async: false, // need to wait... todo: deferred?
        url: "shaders/" + file, //todo: use global config for shaders folder?
        success: function(result) {
           cache = {script: result, type: type};
        }
    });

    // store in global cache
    uilts.allShaders[file] = cache;
};

utils.getShader = function (gl, id) {

    //get the shader object from our main.shaders repository
    var shaderObj = utils.allShaders[id];
    var shaderScript = shaderObj.script;
    var shaderType = shaderObj.type;

    //create the right shader
    var shader;
    if (shaderType == "x-shader/x-fragment") {
        shader = gl.createShader(gl.FRAGMENT_SHADER);
    } else if (shaderType == "x-shader/x-vertex") {
        shader = gl.createShader(gl.VERTEX_SHADER);
    } else {
        return null;
    }

    //wire up the shader and compile
    gl.shaderSource(shader, shaderScript);
    gl.compileShader(shader);

    //if things didn't go so well alert
    if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
        alert(gl.getShaderInfoLog(shader));
        return null;
    }

    //return the shader reference
    return shader;

};//end:getShader

Merci mon pote pour la douce codeezy .. apprécie sa contribution à la communauté webgl ... facilite la simplification de la gestion des programmes/shaders. 

6
John David Five

Suivant l’allusion donnée par @ droidballoon, j’ai fini par utiliser stack.gl qui "est un écosystème logiciel ouvert pour WebGL, construit au-dessus de browserify et npm". 

Son glslify fournit une transformation de type navigateur qui peut être utilisée conjointement avec gl-shader afin de charger des shaders. Le javascript ressemblerait à quelque chose comme ça: 

var glslify       = require('glslify');
var loadShader    = require('gl-shader');
var createContext = require('gl-context');

var canvas = document.createElement('canvas');
var gl = createContext(canvas);

var shader = loadShader(
    gl,
    glslify('./shader.vert'),
    glslify('./shader.frag')
);
6
andyfeind

J'utilise ceci: https://www.npmjs.com/package/webpack-glsl-loader Il est prioritaire de garder la coloration syntaxique d'avoir les fichiers glsl appropriés au lieu de fragments de texte. Je ferai un rapport plus tard sur son fonctionnement.

[modifier le 17 août 2015] Cette approche fonctionne bien pour moi. Cela suppose que webpack fait partie de votre flux de construction, mais ce n'est pas une si mauvaise chose.

[edit 11-June-2016] https://github.com/kulicuu/Spacewar_WebGL_React présente un exemple de travail permettant d'importer des fichiers glsl via une version Webpack. Le jeu lui-même devrait être développé au cours de la semaine à venir.

4
Wylie Kulik

Une bonne façon de procéder consiste à utiliser l’extension browserify-shader de Browserify. 

2
droidballoon

Peut-être pas la meilleure façon mais j'utilise php. Je mets les shaders dans un fichier séparé et vous utilisez simplement:

<?php include('shaders.html'); ?>

fonctionne très bien pour moi.

1
Luca Pescatore

Si vous pouvez utiliser un script côté serveur, vous pouvez écrire un petit script qui lit dans les fichiers de shader et renvoie un fichier JavaScript avec les scripts d'un objet global. De cette façon, vous pouvez l'inclure à l'aide de <script src = "shader? Prefix = foo"> et éditer les scripts en tant que fichiers .c.

Quelque chose comme ce script Ruby CGI

require 'cgi'
require 'json'

cgi = CGI.new
prefix = File.expand_path(cgi["prefix"])
cwd = Dir.getwd + "/"
exit!(1) unless prefix.start_with?(cwd)

shader = prefix + ".c"
source = File.read(shader)
cgi.out("text/javascript") {
  <<-EOF
    if (typeof Shaders == 'undefined') Shaders = {};
    Shaders[#{cgi["prefix"]}] = #{source.to_json};
  EOF
}
1
Ilmari Heikkinen

J'ai également utilisé Require.js pour organiser mes fichiers, mais plutôt que d'utiliser le plugin texte, comme le suggère @Vlr, j'ai un script qui reprend les shaders et le convertit en un module Require.js que je peux ensuite utiliser ailleurs. . Donc, un fichier de shader, simple.frag, ressemble à ceci:

uniform vec3 uColor;

void main() {
  gl_FragColor = vec4(uColor, 1.0);
}

Sera converti en un fichier shader.js:

define( [], function() {
  return {
    fragment: {
      simple: [
        "uniform vec3 uColor;",

        "void main() {",
        "  gl_FragColor = vec4(uColor, 1.0);",
        "}",
      ].join("\n"),
    },
  }
} );

Ce qui semble compliqué, mais l’idée n’est pas qu’il soit lisible par l’homme. Ensuite, si je veux utiliser ce shader quelque part, j'insère simplement le module shader et y accède à l'aide de shader.fragment.simple, comme suit:

var simple = new THREE.ShaderMaterial( {
  vertexShader: shader.vertex.simple,
  fragmentShader: shader.fragment.simple
} );

J'ai écrit un article de blog avec plus de détails et des liens vers le code de démonstration ici: http://www.pheelicks.com/2013/12/webgl-working-with-glsl-source-files/

0
pheelicks

N’est pas la solution exacte, mais est bon pour moi. J’utilise Pug (vieux Jade) pour compiler le HTML, et j’utilise aussi des balises de script dans les shaders

script#vertexShader(type="x-shader/x-vertex")
    include shader.vert

script#fragmentShader(type="x-shader/x-fragment")
    include shader.frag

Le résultat est identique, un code HTML avec le code en ligne, mais vous pouvez utiliser le shader séparément.

0
Andros Guiradó

Vous pouvez placer vos shaders dans différents fichiers, tout comme votre code javascript dans différents fichiers. Cette bibliothèque https://github.com/codecruzer/webgl-shader-loader-js accomplit cela avec une syntaxe familière:

Exemple d'utilisation (extrait textuellement de la page ci-dessus):

[index.html]:

    <script data-src="shaders/particles/vertex.js" data-name="particles"
            type="x-shader/x-vertex"></script>
    <script data-src="shaders/particles/fragment.js" data-name="particles"
            type="x-shader/x-fragment"></script>

[example.js]:

    SHADER_LOADER.load (
        function (data)
        {
            var particlesVertexShader = data.particles.vertex;
            var particlesFragmentShader = data.particles.fragment;
        }
    );
0
gaitat