Подтвердить что ты не робот

WebGL - есть ли альтернатива встраиванию шейдеров в HTML?

Популярным способом использования шейдеров GLSL в WebGL, по-видимому, является их внедрение в основной файл html. Шейдеры вершин и фрагментов встроены в теги, например:

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

Это то же самое соглашение, которое я вижу в примерах WebGL на странице Mozilla Developer Network.

Это отлично подходит для простых приложений, но когда у вас сложное приложение с несколькими шейдерами, html файл становится загроможденным. (Я продолжаю редактировать неправильный шейдер!) Также, если вы хотите повторно использовать ваши шейдеры, эта схема неудобна.

Итак, я думал о том, чтобы поместить эти шейдеры в отдельные файлы XML и загрузить их с помощью XMLHttpRequest(). Затем я увидел, что у кого-то была такая же идея:

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

Мне нравится предложение использовать файлы .c, поскольку это дает вам подсветку синтаксиса и другие удобства редактора для GLSL.

Но проблема с вышеприведенным подходом заключается в том, что (насколько я понимаю) XMLHttpRequest() не может загружать локальный файл .c, то есть на стороне клиента, при разработке и тестировании приложения WebGL. Но в этот процесс сложно загружать его на сервер.

Итак, если я хочу сохранить шейдеры из html файла, это единственный вариант для их вставки в виде строк в коде? Но это затруднит запись, а также отладка...

Я был бы признателен за любые предложения по управлению несколькими шейдерами GLSL в приложениях WebGL.

Привет

Изменить (май 05 2011)

Поскольку я использую Mac для разработки, я решил включить сервер Apache и поместил свой код webgl под http://localhost/~username/. Это оборачивает проблему файла: протокол отключается во время разработки. Теперь код загрузки файла javascript работает локально, поскольку используется http:, а не файл:. Просто подумал, что я поставил бы это здесь, если кто-нибудь найдет это полезным.

4b9b3361

Ответ 1

Да, локальный сервер действительно единственный путь, если вы хотите использовать XHR. Я написал несколько уроков по WebGL и часто думал о том, чтобы отойти от встраивания шейдеров в HTML, но меня напугало количество объяснений о безопасности в сети, которые мне нужно написать...

К счастью, это очень легко запустить сервер. Просто откройте оболочку, а затем

cd path-to-files
python -m SimpleHTTPServer

Затем укажите в браузере

http://localhost:8000

Это работает для простых случаев, таких как текстуры и GLSL. Для просмотра видео и аудио смотрите

Что является более быстрой альтернативой http.server (или SimpleHTTPServer) в Python?

С другой стороны, каждый браузер, который поддерживает WebGL, поддерживает многострочные литералы шаблона ES6, поэтому, если вам не нужны старые браузеры, вы можете просто поместить свои шейдеры в JavaScript с помощью обратных ссылок, подобных этой

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

  void main() {
    gl_Position = u_matrix * position;
  }
';

Ответ 2

Я использовал require.js текстовый плагин.

Вот фрагмент:

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

Структура каталогов выглядит следующим образом:

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

Лично у меня была небольшая кривая обучения с требованием, но это действительно помогло мне сохранить более чистый код.

Ответ 3

Мой приятель создал приятный объект utils с некоторыми удобными функциями для этого типа сценария. Вы сохранили бы свои шейдеры в текстовых файлах в папке с названием "шейдеры":

имя_файла: vertex.shader

attribute vec3 blah;

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

void main(void) {
    magic goes here
}

filename: fragment.shader

#ifdef GL_ES
    precision highp float;
#endif

varying vec4 vYadaYada;
uniform sampler2D uSampler;

void main(void) {
    fragic magic goes here      
}

И вы просто вызываете это, чтобы создать новую программу с этими шейдерными файлами:

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

И вот объект сладкого использования для обработки бизнеса:

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

Благодарим приятеля за сладкое кодовое... наслаждайтесь его вкладом в сообщество webgl.. упрощает упрощение управления программами/шейдерами.

Ответ 4

Следуя подсказке @droidballoon, я закончил использование stack.gl, который "является открытой экосистемой программного обеспечения для WebGL, построенной поверх браузера и npm".

Его glslify предоставляет преобразование браузера, которое может использоваться совместно с gl-shader в для загрузки шейдеров. Javascript будет выглядеть примерно так:

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

Ответ 5

Я использую это: https://www.npmjs.com/package/webpack-glsl-loader Это соответствует приоритету, чтобы подсветка синтаксиса не имела надлежащих файлов glsl вместо текстовых фрагментов. Я сообщу позже, как это работает.

[edit 17 августа 2015 г.] Этот подход работает для меня нормально. Предполагается, что веб-пакет находится в процессе сборки, но это не так уж и плохо.

[править 11 июня 2016 года] https://github.com/kulicuu/Spacewar_WebGL_React содержит рабочий пример импорта файлов glsl через сборку Webpack. Сама игра должна быть разработана в течение ближайшей недели.

Ответ 6

Хороший способ сделать это через расширение browserify-shader для браузера.

Ответ 7

Если вы можете использовать скрипты на стороне сервера, вы можете написать небольшой script, который читает в файлах шейдера и возвращает файл JavaScript со сценариями в глобальном объекте. Таким образом, вы можете включить его с помощью простого старого < script src= "shader? Prefix = foo" > и отредактировать скрипты как .c файлы.

Что-то вроде этого Ruby CGI script

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
}

Ответ 8

Вы можете поместить свои шейдеры в разные файлы так же, как вы помещаете свой javascript-код в разные файлы. Эта библиотека https://github.com/codecruzer/webgl-shader-loader-js выполняет это со знакомым синтаксисом:

Пример использования (взято стенографически со страницы выше):

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

Ответ 9

Я также использовал Require.js для организации своих файлов, но вместо использования текстового плагина, например, @Vlr, у меня есть script, который берет шейдеры и преобразует его в модуль Require.js, который Затем я могу использовать его в другом месте. Итак, файл шейдера, simple.frag, как это:

uniform vec3 uColor;

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

Будет преобразован в файл shader.js:

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

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

Какой выглядит грязно, но идея заключается не в том, что он читается человеком. Затем, если я хочу использовать этот шейдер где-нибудь, я просто втягиваю модуль shader и получаю доступ к нему с помощью shader.fragment.simple, например:

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

Я написал сообщение в блоге с более подробной информацией и ссылками на демо-код здесь: http://www.pheelicks.com/2013/12/webgl-working-with-glsl-source-files/

Ответ 10

Возможно, не лучший способ, но я использую php. Я помещаю шейдеры в отдельный файл, а затем вы просто используете:

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

отлично работает для меня.

Ответ 11

Не точное решение, но для меня это хорошо. Я использую Pug (старый Jade) для компиляции HTML, и я использую встроенные шейдеры script теги

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

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

Результат тот же, HTML с встроенным кодом, но вы можете работать с шейдером отдельно.

Ответ 12

Используйте макросы C #include и gcc -E (клавиша -E запускает препроцессор без компилятора)

Добавьте это в свой файл js:

const shader = '
#include "shader.fg"
'

и использовать оболочку после:

mov main.js main.c
gcc -E --no-warnings main.c | sed '/^#.*/d' > main.js

sed здесь просто удаляет дополнительные комментарии, сгенерированные препроцессором

Оно работает! ;)