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

时间:2017-08-14 03:14:01

标签: opengl-es three.js shader

我想知道如何将实际包含在我的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>

3 个答案:

答案 0 :(得分:1)

提供的另一个答案是简单地使用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将功能列为&#34;正在考虑中,&#34;并且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处理程序,这样您才能在加载代码之前尝试访问该代码。

答案 1 :(得分:0)

您可以将着色器代码移动到单独的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
});

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

答案 2 :(得分:0)

建议加入一系列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() {...}'
      ...