2011-05-04 12 views
27

La forma más popular de usar los shaders GLSL en WebGL parece ser incrustarlos en el archivo HTML principal. el vértice y fragmentos de shaders están incrustados en las etiquetas como:WebGL - ¿existe una alternativa a la incorporación de shaders en HTML?

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

Ésta es la misma convención que veo en las muestras de WebGL en la página de Mozilla Developer Network.

Esto funciona bien para aplicaciones simples, pero cuando se tiene una aplicación compleja con un número de shaders, el archivo html se desordena. (¡Sigo editando el sombreador incorrecto!) Además, si desea reutilizar sus sombreadores, este esquema es inconveniente.

así que estaba pensando en poner estos shaders en unos archivos XML y cargarlos usando XMLHttpRequest(). Entonces vi que alguien más tuvo la misma idea:

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

me gusta la sugerencia de utilizar .c archivos, ya que le da el resaltado de sintaxis y otras comodidades editor para GLSL.

Pero el problema con el enfoque anterior es que (por lo que yo entiendo) XMLHttpRequest() no puede cargar un archivo .c locales - es decir, en el lado del cliente - mientras está desarrollando y probando la aplicación WebGL. Pero es engorroso seguir subiéndolo al servidor durante este proceso.

Así que si quiero mantener los shaders del archivo html, es la única opción para incorporarlos como cadenas en el código? Pero eso sería hacer que sea difícil escribir, así como de depuración ...

lo agradecería cualquier sugerencia sobre la gestión de múltiples shaders GLSL en aplicaciones de WebGL.

Saludos

Edición (Mayo 05 de 2011)

Desde que uso un Mac para el desarrollo, me decidieron a habilitar el servidor Apache, y poner mi código webgl bajo http://localhost/~username/. Esto elude el problema del archivo: el protocolo se deshabilita durante el desarrollo. Ahora el código de carga del archivo javascript funciona localmente desde http: se usa, en lugar de file :. Solo pensé en poner esto aquí en caso de que alguien lo encuentre útil.

+0

su edición es muy útil. Estaba hojeando las respuestas tratando de descubrir cuál era el problema. – izzy

Respuesta

14

Sí, un servidor local es realmente el único camino a seguir si quiere usar XHR. He escrito un montón de lecciones de WebGL y muchas veces he pensado en dejar de incrustar los sombreadores en el HTML, pero me he asustado por la cantidad de explicaciones sobre seguridad web que necesitaría para escribir ...

Afortunadamente es muy fácil ejecutar un servidor. Sólo tiene que abrir una concha entonces

cd path-to-files 
python -m SimpleHTTPServer 

Luego apunta su navegador a

http://localhost:8000 

que funciona para casos simples como texturas y GLSL.Para el vídeo y audio ver

What is a faster alternative to Python's http.server (or SimpleHTTPServer)?

Por otro lado every browser that supports WebGL apoya ES6 mutli-line template literals por lo que si no se preocupan por los navegadores antiguos que se acaba de poner en JavaScript usando shaders acentos abiertos como éste

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

    void main() { 
    gl_Position = u_matrix * position; 
    } 
`; 
+0

Pero si te apegas a http :, no hay ningún problema de seguridad web, ¿verdad? Entiendo que forzar el archivo: trabajar puede ser inseguro. –

+0

De hecho, pero decirle a la gente que configure su propio servidor web solo para ejecutar su primera demo de WebGL sería un obstáculo adicional que calificaría a la gente. ¡Comenzar y aprender sobre el modelo de tubería programable WebGL ya es bastante difícil! –

+3

Ejecutando un servidor local tan simple como 'cd path/to/files' seguido por' python -m SimpleHTTPServer' luego simplemente vaya a 'http: // localhost: 8000' – gman

1

Si puede usar el scripting del lado del servidor, podría escribir un pequeño script que lea en los archivos shader y devuelva un archivo JavaScript con los scripts en un objeto global. De esta forma, puede incluirlo utilizando el script simple < src = "shader? Prefix = foo"> y editar los scripts como archivos .c.

Algo parecido a este script CGI de Ruby

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 
} 
6

Mi amigo crea un agradable utils objeto con algunas funciones útiles para este tipo de escenario. Se podría almacenar sus shaders en archivos de texto sin formato en una carpeta llamada "shaders":

nombre: vertex.shader

attribute vec3 blah; 

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

void main(void) { 
    magic goes here 
} 

Nombre del archivo: fragment.shader

#ifdef GL_ES 
    precision highp float; 
#endif 

varying vec4 vYadaYada; 
uniform sampler2D uSampler; 

void main(void) { 
    fragic magic goes here  
} 

Y sólo tiene que llamar a este para crear un nuevo programa con estos archivos de sombreado:

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

Y aquí está el sweet util ob Ject manejar 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 

Gracias compañero para la codeezy dulce .. disfrutar de su contribución a la comunidad webgl .. hace que sea más fácil manera de simplificar la gestión de programas/shader.

+0

OOPs ... se olvidó de mencionar el uso que incluimos algunos jQuery para usar su pequeña función $ .ajax :-p –

+1

En lugar de confiar en '$ .ajax()' para hacer su "suposición inteligente" del 'dataType correcto ', sería mejor especificar' dataType: "text" 'en el objeto de configuración. – TachyonVortex

14

He estado usando require.js 's text plugin.

He aquí un fragmento:

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 estructura de directorios es la siguiente:

~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 

En lo personal, tuve un poco de curva de aprendizaje con requerir, pero lo que realmente me ayudó a mantener un código más limpio .

0

También he estado usando Require.js para organizar mis archivos, pero en lugar de usar el complemento de texto, como @Vlr sugiere, tengo un script que toma los sombreadores y los convierte en un módulo Require.js que yo puede usar en otro lugar. Por lo que un archivo de sombreado, simple.frag así:

uniform vec3 uColor; 

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

se convertirá en un archivo shader.js:

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

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

que se ve desordenado, pero la idea no es que es legible por humanos.Entonces, si quiero usar esto en algún lugar de sombreado, acabo de tirar en el módulo shader y acceder a ella usando shader.fragment.simple, así:

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

He escrito un post con más detalles y enlaces a DEMO código aquí : http://www.pheelicks.com/2013/12/webgl-working-with-glsl-source-files/

0

Puede colocar sus sombreadores en diferentes archivos al igual que pone su código de JavaScript en diferentes archivos. Esta biblioteca https://github.com/codecruzer/webgl-shader-loader-js logra que con una sintaxis familiar:

Ejemplo de uso (tomado textualmente de la página anterior):

[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; 
     } 
    ); 
2

Una buena manera de hacerlo es a través de la extensión browserify-shader a Browserify.

+1

Ah, he estado usando 'brfs' para sombreadores en línea, tendré que ver si ese plugin es más conveniente. – starwed

0

Puede que no sea la mejor manera pero estoy usando php. Puse los sombreadores en un archivo separado y luego solo uso:

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

funciona muy bien para mí.

6

Siguiendo la sugerencia de @droidballoon terminé usando stack.gl que "es un ecosistema de software abierto para WebGL, construido sobre browserify y npm".

Su glslify proporciona una transformación de browserify que se puede usar junto con gl-shader para cargar sombreadores. El Javascript sería algo como esto:

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') 
); 
3

estoy usando esto: https://www.npmjs.com/package/webpack-glsl-loader Se ajusta la prioridad de mantener el resaltado de sintaxis de tener archivos GLSL adecuados en lugar de fragmentos de texto. Informaré más tarde sobre cómo funciona.

[edit Aug-17, 2015] Este enfoque está funcionando bien para mí. Asume que el paquete web está en tu flujo de compilación, pero eso no es tan malo.

[edit 11-June-2016] https://github.com/kulicuu/Spacewar_WebGL_React tiene un ejemplo de trabajo para importar archivos de Glsl a través de una compilación de Webpack. El juego en sí debería desarrollarse durante la próxima semana.

0

No es la solución exacta, pero es bueno para mí. utilizo amasado (antiguo Jade) para HTML de compilación, y yo uso incluye dentro de shaders etiquetas script

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

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

El resultado es el mismo, un HTML con el código en línea, pero se puede trabajar el shader por separado.

Cuestiones relacionadas