mrdoob / three.js

JavaScript 3D Library.
https://threejs.org/
MIT License
100.8k stars 35.22k forks source link

New Pattern for Coding Shaders #3768

Closed WestLangley closed 10 years ago

WestLangley commented 10 years ago

@jeromeetienne has posted a gist with an interesting proposal. In it, he proposes a new pattern for coding shaders. I have provided some three.js-specific examples here.

Below, shader1 uses the current pattern; shader2 uses the new pattern. Notice that shader2 avoids the use of quotes and commas. It is also more readable in debug output.

THREE.Shader = {

'shader1' : {

    vertexShader : [

        "#ifdef USE_LIGHTMAP",

            "varying vec2 vUv2;",

        "#endif",

        "varying vec3 vNormal;",

        "void main() {",

            "#ifdef USE_LIGHTMAP",

                "vUv2 = uv2;",

            "#endif",

            "vNormal = normalize( normalMatrix * normal );",

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

        "}"

    ].join("\n")

},

'shader2' : {

    vertexShader : ( function() { /*

        #ifdef USE_LIGHTMAP

            varying vec2 vUv2;

        #endif

        varying vec3 vNormal;

        void main() {

            #ifdef USE_LIGHTMAP

                vUv2 = uv2;

            #endif

            vNormal = normalize( normalMatrix * normal );

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

        }

    */ } ).toString().split( '\n' ).slice( 1, -1 ).join( '\n' )

}

};

console.log( THREE.Shader[ 'shader1' ].vertexShader );

#ifdef USE_LIGHTMAP
varying vec2 vUv2;
#endif
varying vec3 vNormal;
void main() {
#ifdef USE_LIGHTMAP
vUv2 = uv2;
#endif
vNormal = normalize( normalMatrix * normal );
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}

console.log( THREE.Shader[ 'shader2' ].vertexShader );

    #ifdef USE_LIGHTMAP

        varying vec2 vUv2;

    #endif

    varying vec3 vNormal;

    void main() {

        #ifdef USE_LIGHTMAP

            vUv2 = uv2;

        #endif

        vNormal = normalize( normalMatrix * normal );

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

    }
skeelogy commented 10 years ago

I noticed that there has been discussion for a while, about how to write shaders in Three.js in an easier way, without commas and quotations.

I'm currently writing GLSL shaders in external files (.vert, .frag) and then using AJAX to load the contents in, non-asynchronously. Seems to work for what I need to do.

Is there any particular reason why such an approach is not considered?

mrdoob commented 10 years ago

Is there any particular reason why such an approach is not considered?

Saving on network requests?

zz85 commented 10 years ago

one reason why you may not want to put your code in comments is that js minifiers may just strip out the code.

zz85 commented 10 years ago

on moving shader code into vert and frag files is worth considering - we could have an automated build tool which codgens our current shader compatible code (in a way like this)

@skeelogy, i realized we have met once in life at DN few years back :)

skeelogy commented 10 years ago

@mrdoob Yes that makes sense. Just curious.

@zz85 Wow, didn't know that it's you, from your username. Great to see that you are doing so well here!

WestLangley commented 10 years ago

one reason why you may not want to put your code in comments is that js minifiers may just strip out the code.

OK, well that shoots down this idea...

DavidSouther commented 10 years ago

@zz85 I think has the best approach, IMHO

jeromeetienne commented 10 years ago

@zz85 good point!

@preserve is a jsdoc tag which preserve the comment. it is used to preserve comment which contains license text. it may fix the issue but it makes it less eleguant.

jeromeetienne commented 10 years ago

apparently i cant make uglify to keep this comment, with or without @preserve.

so i agree with @WestLangley it likely kills the idea.

zz85 commented 10 years ago

@skeelogy i'm a nobody here, but nice to see you adopting three.js too :)

@mrdoob @WestLangley i'll try to pen my thoughts and come up with a poc later if we're still interested to pursue a better way to write shaders here.

gero3 commented 10 years ago

Actually I wouldn't change much at how we are writing shaders for now. But I think of writing a new layer on top of the existing layer which makes use of shadernodes, like in blender.

updatetoonnodesetup

zz85 commented 10 years ago

this brings up the discussion we had eariler in #2162

@gero3 what you're want might be similiar to @unconed's https://github.com/unconed/ShaderGraph.js

The pain currently we have writing shader code the way we do it is this:

  1. you have to write it in a certain way (whether its escaping slashes at the end of the line, or in our case, double quoting lines and indentation)
  2. it breaks glsl syntax highlighting you get when you try to read/edit them with your editors
  3. the way we currently format shader code lose indentation when its compiled into the shader. not a problem to most people, but eyes hurt when you're trying to edit and debug shader code in the console.
gero3 commented 10 years ago

Altough shadergraph is similar to what I mean, It wouldn't be possible to create the base three.js shaders. There are a bit of problems with the way it defines nodes.

If 1 to 3 are the only requirements, then a build system with the shader chunks defined in files with extension glsl. And if we need to use shaderchunks in othere file we use a macro of format '#define THREEJSSHADERCHUNK{NAME}'. And if we use the correct indentation in the chunks, Then we have all 3 points. The build system would then just convert them back to the old system. The only problem i see with this is that the minified file will be larger due to indentation in the strings.

mrdoob commented 10 years ago
  1. the way we currently format shader code lose indentation when its compiled into the shader. not a problem to most people, but eyes hurt when you're trying to edit and debug shader code in the console.

That's fairly easy to solve.

The only problem i see with this is that the minified file will be larger due to indentation in the strings.

I think that's ok...

WestLangley commented 10 years ago

OK, here is @mrdoob's suggestion. Indentation is via tabs. It remains to see how well it works with ShaderChunks....

THREE.Shader = {

'shader1' : {

    vertexShader : [

        "#ifdef USE_LIGHTMAP",

        "   varying vec2 vUv2;",

        "#endif",

        "varying vec3 vNormal;",

        "void main() {",

        "   #ifdef USE_LIGHTMAP",

        "       vUv2 = uv2;",

        "   #endif",

        "   vNormal = normalize( normalMatrix * normal );",

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

        "}"

    ].join("\n")

}

};

console.log( THREE.Shader[ 'shader1' ].vertexShader );

#ifdef USE_LIGHTMAP
    varying vec2 vUv2;
#endif
varying vec3 vNormal;
void main() {
    #ifdef USE_LIGHTMAP
        vUv2 = uv2;
    #endif
    vNormal = normalize( normalMatrix * normal );
    gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
jeromeetienne commented 10 years ago

@mrdoob is there something to modify existing materials in new refactored renderer ? Currently modifying existing materials isnt easy. either we use existing shaders, or we fall back on THREE.ShaderMaterial which is very raw to say the least.

suppose devs need to do some funky displacement in the vertex shader... devs need to clone the original material shader, say phong one, and modify the shader. All properties must be done manually in shader.uniforms.

not the ideal situation.

could we think about this while it is possible with the renderer refactorisation ?

mrdoob commented 10 years ago

Yup. That's something I keep in mind while reworking the renderer. It'll take me some time though.

zz85 commented 10 years ago

my proposal is along the lines of what @gero3 says.

we place all glsl files with some marco template format in a directory structure.

build tool comes picks up all these files and auto generates code compatible to what we have right now (that is into our src directory).

i don't think additional bytes/indentation in final shader code is a big issue - we can have a DEVELOPMENT flag which preserves indentation, otherwise strip whitespace. then we wouldn't worry about how shader code should be formatted, and i think it makes it more friendly for others to try editing/contributing shader code.

besides: double quoting every line isn't the most space efficient compare to 'line0line0line0line'.split(0) // where 0 is unique character ;) my point being - allowing a tool to decide how it decides to generate the code would be better than us enforcing how the code should be written.

thoughts?

zz85 commented 10 years ago

Ah just realized what I said about the string splitting thing is silly. Just need "Line1\nline2"

WestLangley commented 10 years ago

I'm thinking that pursuing a new shader coding pattern is not worth it. @zz85's deficiencies 1-3 are not that bad, IMHO.

cecilemuller commented 10 years ago

From this discussion, it sounds like that a visual editor for creating ShaderMaterial-based shaders is what would be the most useful rather than changing the coding pattern, e.g. something like the Blender one mentioned above or Minko ShaderLab screenshot

It would be a great complement to the already good material editor that is in the three.js editor.

nhalloran commented 10 years ago

I was thinking of starting a Wiki page on "shader best practices," to help clarify how best to do shader work through some examples... But if things are about to be reworked, maybe it's better to wait. The current system seems to direct all custom shader work to ShaderMaterial, but I usually want to make a slight change to one of the base (Phong, Lambert, Particle..) materials. It would be great if you could add a custom uniform / attribute to Phong, and then override one of the shader chunks. Based on comments I've read on stackoverflow, you can't really do that. If not, that would be a key requirement for the new system - selfishly speaking :) .

mrdoob commented 10 years ago

Ah just realized what I said about the string splitting thing is silly. Just need "Line1\nline2"

Closure Compiler already does that for us. If you look at the minified file all the ["",""].join('\n') stuff gets converted to single strings.

mrdoob commented 10 years ago

It would be a great complement to the already good material editor that is in the three.js editor.

All in good time :)

zz85 commented 10 years ago

for those who still pursuing for a easier/different way to write shaders, ShaderDSL could be another interesting approach http://typedarray.org/shaderdsl-js-javascript-based-dsls-for-gpu-shaders/