【问题标题】:How to move shader scripts in external files?如何在外部文件中移动着色器脚本?
【发布时间】:2018-01-21 20:17:18
【问题描述】:

我想知道如何将实际包含在我的 html 中的着色器移动到外部文件。通过这种方式,我将能够将它们包含在我的 gulp 任务中。我看了一下 JavaScript 着色器文件是如何编写的,但我不是很了解。

例如使用下面的 Glow 着色器代码,我如何将其移动到外部文件?

<script type="x-shader/x-vertex" id="vertexShaderGlow">
    uniform vec3 viewVector;
    uniform float c;
    uniform float p;
    varying float intensity;
    void main()
    {
        vec3 vNormal = normalize( normalMatrix * normal );
        vec3 vNormel = normalize( normalMatrix * viewVector );
        intensity = pow( c - dot(vNormal, vNormel), p );
        gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
    }
</script>
<script type="x-shader/x-vertex" id="fragmentShaderGlow">
    uniform vec3 glowColor;
    varying float intensity;
    void main()
    {
        vec3 glow = glowColor * intensity;
        gl_FragColor = vec4( glow, 1.0 );
    }
</script>

【问题讨论】:

  • 您只需要将着色器代码作为字符串,因此可以使用任何允许您从外部文件中引入字符串的方式。
  • 你能给我举个例子吗?
  • 我已经编辑了邵给出的答案

标签: opengl-es three.js shader


【解决方案1】:

建议加入 glsl 行数组的答案是三个可以遇到的模式,但在这个用例中应该避免。

它可能在某种模块中很有用,它是着色器的“编译”快照,不打算修改。

否则,这种方法的主要缺点是缺乏语法高亮和冗长。

如今,大多数 js 代码都以一种或另一种方式转换。着色器代码应该像这样内联。

const myShader = new THREE.ShaderMaterial({
  vertexShader: require('./myVert.vs'),
  fragmentShader: require('./myFrag.vs'),
})

编辑

myVert.vs:

//this is all nicely highlighted in sublime text for example
void main (){ 
    gl_Position = vec4( position.xy, 0., 1.); 
}

myVert.fs:

void main (){ 
    gl_FragColor = vec4(1.,0.,0.,1.); 
}

myClass.js:

class myMaterial extends THREE.ShaderMaterial{
  constructor(){
    super({
      vertexShader: require('./myVert.vs'), 
      //^ becomes vertexShader: 'void main() {...}'
      ...

【讨论】:

  • 那个方法看起来不错,但它会增加HTTP请求的数量不是吗? Javascript 文件中包含的着色器对我来说是件好事,因为我能够缩小/合并它们并为我的所有网站只加载一个 Javascript。 .vs 文件中的代码是否与脚本标签中的代码相同,或者我需要对其进行调整?
  • 不,这将与 webpack 或其他一些捆绑工具一起使用。 .vs 只是一个 txt 文件。一旦捆绑工具处理了这个,require(..) 将被替换为文件的内容。没有其他请求,这只是内联在您的主包中。
  • 请注意着色器只不过是字符串,这里的问题是获取字符串,同步,异步,格式化与否。 Three 本身提供了一些处理这些特殊字符串的功能,例如不属于 GLSL 的 #import ... 语句。因此,您可以选择最合适的格式。普通的旧 javascript 不能做多行字符串,这就是为什么你需要添加 \n 或加入数组中的行。下一步是 es6 字符串,您可以在其中简单地写多行。
  • 你仍然不会有突出显示,但至少它不那么冗长。下一步是将其保存在自己的文件中,然后以某种方式获取它。使用捆绑器,可以“内联”它(将require 语句转换为字符串格式)。如果你想远程加载它,它只是一个文本文件,它可以被获取。如果您担心请求的数量,您可以将多个着色器放在一个文件中,那么问题是如何提取它们。同样它只是字符串,你可以放一些东西并对其进行正则表达式,或者将它放在一个 json 文件中,以着色器名称作为键等。
  • 感谢您的回答。这很困难,但我现在明白了一点。
【解决方案2】:

提供的另一个答案是简单地获取 GLSL 代码并将每一行转换为字符串。每个字符串都是数组中的一个值。 join 调用将所有字符串与\n 字符连接起来,以使调试时代码更易于阅读。我以前多次这样做过,这是您尝试做的合法解决方案。

但是,如果您希望拥有带有原始 GLSL 代码的外部文件,您也可以这样做。考虑这两个文件:

  • glow_vertex.glsl
  • glow_fragment.glsl

这些文件包含您通常在脚本标签中拥有的着色器代码。您可以使用XMLHTTPRequest 来获取文件,并将返回的文本用作您的着色器代码。

var vertexShader = null;
var fragmentShader = null;

function shadersDone(){
    var material = new THREE.ShaderMaterial({
        uniforms: { /* define your uniforms */},
        vertexShader: vertexShader,
        fragmentShader: fragmentShader
    });
}

function vertexDone(code){
    vertexShader = code;
    if(fragmentShader !== null){
        shadersDone();
    }
}

function fragmentDone(code){
    fragmentShader = code;
    if(vertexShader !== null){
        shadersDone();
    }
}

var xhr1 = new XMLHttpRequest();
var xhr2 = new XMLHttpRequest();

xhr1.open("GET", "/server/glow_vertex.glsl", true);
xhr2.open("GET", "/server/glow_fragment.glsl", true);

xhr1.responseType = "text";
xhr2.responseType = "text";

xhr1.onload = function(){
    if(xhr1.readyState === xhr1.DONE && xhr1.status === 200){
        vertexDone(xhr1.resultText);
    }
};
xhr2.onload = function(){
    if(xhr2.readyState === xhr2.DONE && xhr2.status === 200){
        fragmentDone(xhr2.resultText);
    }
};

xhr1.send(null);
xhr2.send(null);

请注意,这都是异步的。此外,您的服务器需要配置为以纯文本形式发送 GLSL 文件。

只要我们谈论的是现代网络......

您还可以选择import 您的着色器代码。 非常大,但目前仅在 Chrome 和 Opera 中支持(尽管确实存在 polyfill)。 Microsoft Edge 将该功能列为“正在考虑中”,并且 Firefox 并不打算在其当前状态下实现它。因此,请注意以下内容:http://caniuse.com/#search=import

在您的 HTML 中,以及在使用它的 JavaScript 之前...

<link id="vertexImport" rel="import" href="/server/glow_vertex.glsl" />
<link id="fragmentImport" rel="import" href="/server/glow_fragment.glsl" />

然后在你的 JavaScript 中:

var material = new THREE.ShaderMaterial({
    vertexShader: document.getElementById("vertexImport").import.body.childNodes[0].data,
    fragmentShader: document.getElementById("fragmentImport").import.body.childNodes[0].data,
});

同样,这是异步的。您可能需要为每个链接添加一个onload 处理程序,因此在加载之前不要尝试访问代码。

【讨论】:

  • 它不应该是一个合法的解决方案。 2017 年下半年,(glsl | vert | frag | fs | vs ) 应该都是扩展,您可以在代码中使用require 和内联,或者执行 xhr 请求。第二种方法应该折旧。
  • @pailhead 你可能会觉得我的补充很有趣,如果对跨浏览器有点没用的话。
  • 很有趣,对我来说不利的是它需要一个服务器配置,并且管理起来有点繁重:/
  • 不,现代方式就是 webpack 的一个例子:D
【解决方案3】:

您可以将着色器代码移动到单独的 JS 文件中,并将该文件包含在三个.js 之后。
这是https://github.com/timoxley/threejs/blob/master/examples/js/shaders/ColorifyShader.js的一个例子

/**
 * @author alteredq / http://alteredqualia.com/
 *
 * Colorify shader
 */

THREE.ColorifyShader = {

    uniforms: {

        "tDiffuse": { type: "t", value: null },
        "color":    { type: "c", value: new THREE.Color( 0xffffff ) }

    },

    vertexShader: [

        "varying vec2 vUv;",

        "void main() {",

            "vUv = uv;",
            "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",

        "}"

    ].join("\n"),

    fragmentShader: [

        "uniform vec3 color;",
        "uniform sampler2D tDiffuse;",

        "varying vec2 vUv;",

        "void main() {",

            "vec4 texel = texture2D( tDiffuse, vUv );",

            "vec3 luma = vec3( 0.299, 0.587, 0.114 );",
            "float v = dot( texel.xyz, luma );",

            "gl_FragColor = vec4( v * color, texel.w );",

        "}"

    ].join("\n")

};

使用上述内容,您可以像这样创建我们的材料:

material = new THREE.ShaderMaterial({
  uniforms : THREE.ColorifyShader.uniforms,
  vertexShader : THREE.ColorifyShader.vertexShader,
  fragmentShader : THREE.ColorifyShader.fragmentShader
});

当然你不需要调用对象THREE.ColorifyShader,你可以随意调用它。

【讨论】:

  • 不知道为什么这会被否决,这对我来说似乎是一个合理的答案,而且我过去有时就是这样做的。
  • 我已经知道我可以将着色器代码移动到一个单独的 JS 文件中,并在三个.js 之后包含该文件,这不是我的问题。
  • 我想知道将一种语法转换为另一种语法的方法,代码似乎是用两种不同的方式编写的。
  • 这有很多问题,它很冗长,容易出错,它没有给你语法高亮。现在的 Javascript 被转换了,这应该只是 require('myShader.vs').
猜你喜欢
  • 1970-01-01
  • 2012-01-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多