如何在外部文件中移动着色器脚本?

问题描述:

我想知道如何将包含在我的html中的着色器移动到外部文件中。通过这种方式,我可以将它们包含在我的大量任务中。我看了一下如何编写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> 
+0

您只需将着色器代码作为字符串使用,因此可以使用允许您从外部文件引入字符串的任何方式。 – 2pha

+0

你能举个例子吗? – ClassikD

+0

我编辑了由邵 – 2pha

建议加入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() {...}' 
     ... 
+0

该方法看起来不错,但它会不会增加HTTP请求的数量?包含在Javascript文件中的着色器对我来说是件好事,因为我能够缩小/合并它们,并为我的所有网站加载一个Javascript。 .vs文件中的代码与脚本代码中的代码相同,或者我需要修改它? – ClassikD

+0

不,这是与webpack或其他捆绑工具一起使用的。 '.vs'只是一个txt文件。捆绑工具处理完成后,'require(..)'将被替换为文件的内容。没有额外的请求,这只是在你的主包中内联。 – pailhead

+0

**请注意**着色器不过是字符串,这里的问题是获取字符串,同步,异步,格式化或不。 Three本身提供了一些用于处理这些特殊字符串的特性,例如'#import ...'语句,它们不属于GLSL的一部分。因此,选择最合适的格式就有点儿了。简单的旧javascript不能做一个多行字符串,这就是为什么你需要添加'\ n'或者加入数组中的行。下一步是es6字符串,您可以简单地写入多行。 – pailhead

您可以将着色器代码移动到单独的JS文件中,并在three.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 
}); 

ofcourse你不需要调用对象THREE.ColorifyShader,你可以把它叫做什么你要。

+0

给出的答案不知道为什么这得到了一个反对票,似乎对我来说是一个合理的答案,这是我有时过去做过的。 – 2pha

+0

我已经知道我可以将着色器代码移动到单独的JS文件中,并在three.js之后包含该文件,那不是我的问题。 – ClassikD

+0

我想知道将一种语法转换为另一种语法的方法,代码似乎是以两种不同的方式编写的。 – ClassikD

提供的另一个答案是简单地取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中支持(尽管polyfills确实存在)。 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处理程序,因此您不会在加载代码之前尝试访问代码。

+0

它不应该是一个合法的解决方案。它的2017年下半年,应该都是可以在代码中“需要”并内联的扩展,或者执行xhr请求。第二种方法应该折旧。 – pailhead

+0

@pailhead您可能会发现我的补充有趣,如果有点不适合跨浏览器。 – TheJim01

+0

非常有趣的是,我的负面观点是它需要一个服务器配置,而且它的管理有点沉重:/ – ClassikD