WebGL - czy istnieje alternatywa dla osadzania shaderów w HTML?

Popularnym sposobem używania shaderów GLSL w WebGL wydaje się osadzanie ich w głównym pliku html. Shadery wierzchołków i fragmentów są osadzone w znacznikach takich jak:

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

Jest to ta sama Konwencja, którą widzę w próbkach WebGL na stronie Mozilla Developer Network.

To działa dobrze dla prostych aplikacji, ale gdy masz złożoną aplikację z wieloma shaderami, plik html staje się zaśmiecony. (Ciągle edytuję zły shader!) Również jeśli chcesz ponownie użyć shaderów, ten schemat to niewygodne.

Więc myślałem o umieszczeniu tych shaderów w osobnych plikach XML i załadowaniu ich za pomocą XMLHttpRequest (). Potem zobaczyłem, że ktoś inny miał ten sam pomysł:

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

Podoba mi się ta sugestia .pliki c, ponieważ daje to podświetlanie składni i inne wygody edytora dla GLSL.

Ale problem z powyższym podejściem jest taki, że (o ile XMLHttpRequest () nie może załadować lokalnego .plik c-czyli po stronie klienta-podczas tworzenia i testowania aplikacji WebGL. Ale jest uciążliwe przesyłanie go na serwer podczas tego procesu.

Więc jeśli chcę trzymać shadery z dala od pliku html, czy jest jedyna opcja, aby osadzić je jako ciągi znaków w kodzie? Ale to utrudniłoby pisanie i debugowanie...

Byłbym wdzięczny za wszelkie sugestie dotyczące zarządzania wieloma shaderami GLSL w aplikacjach WebGL.

Pozdrawiam

Edit (Maj 05 2011)

Ponieważ używam Mac do rozwoju, postanowiłem włączyć serwer Apache i umieścić mój kod webgl pod http://localhost / ~ username / . To omija problem wyłączenia file: protocol podczas programowania. Teraz kod wczytywania plików javascript działa lokalnie, ponieważ używany jest http:, a nie file:. Pomyślałem, że postawię to tutaj na wypadek, gdyby ktoś uznał to za przydatne.

 29
Author: M-V, 2011-05-04

11 answers

Yup, lokalny serwer jest naprawdę jedynym sposobem, aby przejść, jeśli chcesz korzystać z XHR. Napisałem kilka lekcji WebGL i często rozważałem odejście od osadzania shaderów w HTML, ale byłem przerażony ilością wyjaśnień dotyczących bezpieczeństwa sieci, które muszę napisać...

Na szczęście bardzo łatwo jest uruchomić serwer. Po prostu Otwórz powłokę
cd path-to-files
python -m SimpleHTTPServer

Następnie wskaż przeglądarkę na

http://localhost:8000

To działa w prostych przypadkach, takich jak tekstury i GLSL. Do wideo i audio streaming see

Co to jest szybsza alternatywa dla HTTP Pythona.serwer (lub SimpleHTTPServer)?

Z drugiej strony każda przeglądarka, która obsługuje WebGL obsługuje ES6 mutli-liniowe literały szablonów więc jeśli nie dbasz o stare przeglądarki, możesz po prostu umieścić shadery w JavaScript za pomocą takich backtików

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

  void main() {
    gl_Position = u_matrix * position;
  }
`;
 15
Author: Giles Thomas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:07

Używałem require.js ' S text plugin .

Oto fragment:

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);
        ....
    }
);

Struktura katalogów jest następująca:

~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

Osobiście miałem trochę krzywej uczenia się z require, ale to naprawdę pomogło mi zachować czystszy kod.

 14
Author: Valer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-21 21:10:46

Mój kumpel stworzył ładny obiekt utils z kilkoma przydatnymi funkcjami do tego typu scenariuszy. Shadery można przechowywać w plikach tekstowych w folderze o nazwie "shadery":

Nazwa pliku: vertex.shader

attribute vec3 blah;

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

void main(void) {
    magic goes here
}

Nazwa pliku: fragment.shader

#ifdef GL_ES
    precision highp float;
#endif

varying vec4 vYadaYada;
uniform sampler2D uSampler;

void main(void) {
    fragic magic goes here      
}

I po prostu wywołujesz to, aby utworzyć nowy program z tymi plikami shader:

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

A oto słodki obiekt util do obsługi 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
Dzięki kolego za słodki kodeezy.. ciesz się jego wkładem w społeczność webgl.. znacznie ułatwia zarządzanie programami / shaderami.
 6
Author: John David Five,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-14 02:40:16

Podążając za podpowiedzią @ droidballoon skończyłem używając stack.gl który "jest otwartym ekosystemem oprogramowania dla WebGL, zbudowanym na bazie browserify i npm".

Its glslify zapewnia transformację browserify, która może być używana w połączeniu z GL-shader w celu załadowania shaderów. Javascript wyglądałby mniej więcej tak:

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
Author: andyfeind,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-11 11:48:12

Używam tego: https://www.npmjs.com/package/webpack-glsl-loader Pasuje do priorytetu, aby podświetlanie składni nie miało odpowiednich plików glsl zamiast fragmentów tekstu. Później opiszę, jak to działa.

[edytuj Sierpień-17, 2015] to podejście działa dobrze dla mnie. Zakłada, że webpack jest w Twoim przepływie budowania, ale to nie jest taka zła rzecz.

[edytuj 11-czerwiec-2016] https://github.com/kulicuu/Spacewar_WebGL_React ma działający przykład importowania glsl pliki poprzez tworzenie Webpacka. Sama gra powinna być rozwijana w nadchodzącym tygodniu.

 4
Author: Wylie Kulik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-11 09:25:42

Dobrym sposobem na to jest rozszerzenie browserify-shader do Browserify.

 2
Author: droidballoon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-24 21:13:15

Jeśli możesz używać skryptów po stronie serwera, możesz napisać mały skrypt, który odczytuje pliki shadera i zwraca plik JavaScript ze skryptami w obiekcie globalnym. W ten sposób możesz dołączyć go używając zwykłego i edytować skrypty jako .pliki C.

Coś takiego jak skrypt 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
Author: Ilmari Heikkinen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-19 10:16:31

Może nie jest to najlepszy sposób, ale używam php. Ja wrzucam shadery do osobnego pliku, a ty po prostu używasz:

<?php include('shaders.html'); ?>
Dla mnie działa świetnie.
 1
Author: Luca Pescatore,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-05 18:57:39

Używam również Require.js do organizowania moich plików, ale zamiast używać wtyczki tekstowej ,jak sugeruje @ Vlr, mam skrypt, który bierze shadery i konwertuje go na Wymaganie.moduł js, który mogę wykorzystać gdzie indziej. Więc plik shader, simple.frag w ten sposób:

uniform vec3 uColor;

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

Zostanie przekonwertowany do pliku shader.js:

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

        "void main() {",
        "  gl_FragColor = vec4(uColor, 1.0);",
        "}",
      ].join("\n"),
    },
  }
} );
Wygląda to na bałagan, ale nie chodzi o to, że jest czytelny dla człowieka. Następnie, jeśli chcę gdzieś użyć tego shadera, po prostu wciągam moduł shader i uzyskuję do niego dostęp używając shader.fragment.simple, w ten sposób:
var simple = new THREE.ShaderMaterial( {
  vertexShader: shader.vertex.simple,
  fragmentShader: shader.fragment.simple
} );

Napisałem post na blogu z bardziej szczegółowymi informacjami i linkami do kodu demo tutaj: http://www.pheelicks.com/2013/12/webgl-working-with-glsl-source-files/

 0
Author: pheelicks,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-10 16:47:19

Shadery można umieszczać w różnych plikach, podobnie jak kod javascript w różnych plikach. Ta biblioteka https://github.com/codecruzer/webgl-shader-loader-js osiąga to ze znajomą składnią:

Przykładowe użycie (zaczerpnięte dosłownie z powyższej strony):

[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
Author: gaitat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-25 18:59:05

Nie jest to dokładne rozwiązanie, ale jest dobre dla mnie. Używam Pug (old Jade) do kompilacji HTML i używam includes inside shaders script tags

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

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

Wynik jest taki sam, HTML z kodem w linii, ale można pracować z shaderem osobno.

 0
Author: Andros Guiradó,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-26 09:52:50