Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ShaderChunk to ES6 Module #14447

Closed
wants to merge 1 commit into from
Closed

Conversation

arodic
Copy link
Contributor

@arodic arodic commented Jul 11, 2018

Added another step to rollup.config.js to make ShaderChunk.js compatible with ES6 modules.

Fixes #14446

@mrdoob mrdoob changed the title Fixed ShaderChunk issue #14446 ShaderChunk to ES6 Module Jul 12, 2018
@mrdoob
Copy link
Owner

mrdoob commented Jul 12, 2018

Very nice!


var alphatest_fragment = "#ifdef ALPHATEST\r\n\r\n\tif ( diffuseColor.a < ALPHATEST ) discard;\r\n\r\n#endif\r\n";

var aomap_fragment = "#ifdef USE_AOMAP\r\n\r\n\t// reads channel R, compatible with a combined OcclusionRoughnessMetallic (RGB) texture\r\n\tfloat ambientOcclusion = ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0;\r\n\r\n\treflectedLight.indirectDiffuse *= ambientOcclusion;\r\n\r\n\t#if defined( USE_ENVMAP ) && defined( PHYSICAL )\r\n\r\n\t\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\r\n\r\n\t\treflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.specularRoughness );\r\n\r\n\t#endif\r\n\r\n#endif\r\n";
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the glsl() plugin is not cleaning up the code comments anymore?

@mrdoob mrdoob added this to the r95 milestone Jul 12, 2018
output: [
{
format: 'es',
file: 'src/renderers/shaders/ShaderChunk.js',
Copy link
Collaborator

@Mugen87 Mugen87 Jul 12, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So a build-result is now part of the src directory?

Copy link
Contributor

@pailhead pailhead Jul 13, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah i'm confused, this file gets built, and then is used for another build? How would you describe ShaderChunk.js now?

@looeee
Copy link
Collaborator

looeee commented Jul 12, 2018

With this change we will have files in the src/ directory that are out of sync in the dev branch.

@arodic
Copy link
Contributor Author

arodic commented Jul 12, 2018

So a build-result is now part of the src directory?

Sort of... It's a compiled ES6 version of glsl files which otherwise cannot be used as modules. Alternatively, the file can sit outside src/ however, modules that need it should have import paths pointing to built version, not .glsl. I'm not sure I like import { ShaderChunk } from "../../../build/ShaderChunk.js" better.

@arodic
Copy link
Contributor Author

arodic commented Jul 12, 2018

@looeee I'm not sure what you mean by that? As far as I can tell, making any changes to glsl files requires a build step. This PR only splits the build in two steps to unblock ShaderChunk from breaking otherwise working modular architecture.

As long as you continue building after changing glsl (same as before) nothing should be out of sync. However @Mugen87 brings up a valid concern, should modularized (built) version of ShaderChunk sit in src/ or build/ ?

Also, I'm thinking there should be a separate npm task for watching and building glsl files alone.

Are there any other suggestions to fix the issue?

@mrdoob
Copy link
Owner

mrdoob commented Jul 12, 2018

Are there any other suggestions to fix the issue?

Yes. As I mentioned above, the comments in the glsl code are no loger being removed when converting to js string. Any ideas?

@looeee
Copy link
Collaborator

looeee commented Jul 12, 2018

As long as you continue building after changing glsl (same as before) nothing should be out of sync

This is what I mean - if I submit a PR to update or add a glsl file, once that PR is accepted the src/ files will be out of sync in the dev branch, unless I also run build and include the build files in the PR.

Since build currently builds all the files, we would need at least a separate npm task for this.

Conceptually, it's a little odd to have files from a build process in src/, although an argument can be made as to why this should be an exception, since the user will be using the built files in a secondary build process.

But then again, it's not hard for the user to include the rollup glsl() plugin. Unless there are other build systems for which this is an issue, perhaps we could just document how to do that instead and avoid adding this entirely?

@arodic
Copy link
Contributor Author

arodic commented Jul 13, 2018

[Perhaps] this should be an exception since the user will be using the built files in a secondary build process.
But then again, it's not hard for the user to include the rollup glsl() plugin.

My hope is to make threejs work without any build process whatsoever since modules and http2 have landed in most browsers. Of course - bundling, minifying, tree shaking etc. will be necessary for most projects. But increasing number of projects could benefit from using threejs in its source form.

So, glsl files converted to js are still javascript source files and belong to src/ from javascript perspective. Unfortunately, since there is a "build" step to convert glsl to js, ShaderChunk.js in not a true source file.

Perhaps it would be less confusing if glsl files are converted to js individually (not bundled)?

Ideally, I'd like to have shader files consumable without any "build" process. As I already mentioned, there are alternative solutions such as to write shaders source in template literals (which are quite powerful and can remove comments btw).

Another option is to use XHR with http2-push but I believe it requires a custom server...

Anyway... I am happy with this as a temporary solution for my project. I can amend this PR if there is consensus on how to resolve the brought up issues. Otherwise, I'll be closing this PR.

@looeee
Copy link
Collaborator

looeee commented Jul 13, 2018

My hope is to make threejs work without any build process whatsoever since modules and http2 have landed in most browsers.

Hmm, that is a good point. On the other hand, we may possibly never reach the time where importing thousands of small files is as performant as importing ten or so larger ones, since you will still want to do minification and compression. Compression will not work as well on small files as it does on large ones, while minification will at least still require a build step of some kind.

There is also an overhead associated with importing ES6 modules. If you are importing ten or twenty modules, you can probably ignore this. But if you are importing 1000 critical modules, it's going to add some significant time to your page load.

Perhaps it would be less confusing if glsl files are converted to js individually (not bundled)?

This would be less confusing, but for the reasons above is unlikely to ever be useful.

bundling, minifying, tree shaking etc. will be necessary for most projects. But increasing number of projects could benefit from using threejs in its source form.

Could you give an example of such a project? Since browsers being able to import modules is very new and I'm not fully familiar with it yet, perhaps I'm just not seeing the potential applications.

@pailhead
Copy link
Contributor

pailhead commented Jul 13, 2018

I've started exploring how to remove the ShaderChunk stuff completely out of three.js. The idea is pretty simple:

  1. assume that this works:
const myVertexShader = require('my.vert')
const myFragmentShader = require('my.frag')
const myShader = new THREE.ShaderMaterial({
  vertexShader: myVertexShader,
  fragmentShader: myFragmentShader,
  uniforms:{ foo: {value:0}, bar: {value: new THREE.Vector2()}
})
  1. this should work too
const myVertexShader = parseIncludes( someTemplate.vert )
const myFragmentShader = parseIncludes( someTemplate.frag)
const myShader = new THREE.ShaderMaterial({
  vertexShader: myVertexShader,
  fragmentShader: myFragmentShader,
  uniforms:{ 
    diffuse: {value: new THREE.Color},
    diffuseMap: {value: null }
  }
})

I'm under the impression, that for my needs, this introduces a lot of flexibility, and i can nuke a lot of code from THREE.WebGLRenderer, THREE.ShaderChunk can be optionally provided. However, i've encountered concerns about tree shaking a few times, but it's really hard to gather all the bits and pieces and i have to admit that i don't know how it applies here.

Given that this is the most recent THREE.ShaderChunk discussion, could someone possibly elaborate more on the intricacies of tree shaking in this particular case. What is the beginning state, and what is the state after building or after importing stuff in some bundler?

I don't think i'll ever end up in a situation where some .html page would load a bunch of atomic es6 modules from three.js but this is a super common scenario:

import {WebGLRenderer} from 'three'
import {StandardMaterial } from 'three'

Should this tree shake all the other non StandardMaterial related code?

Perhaps it would be less confusing if glsl files are converted to js individually (not bundled)?

This would be less confusing, but for the reasons above is unlikely to ever be useful.

I think it would be very useful with some build system though.

@looeee
Copy link
Collaborator

looeee commented Jul 13, 2018

@pailhead this PR is about importing GLSL files as ES6 modules in the browser (or in module bundlers). No matter what system we use for materials - the current system, your system, node materials etc. we will still have GLSL code that needs to be consumed by tools that are designed for JavaScript modules - this means that discussion of alternate approaches to materials, while interesting, is not relevant here.

To summarise:

  • we have files that end in .glsl and we want to keep these for easy syntax highlighting.
  • we have build tools and importers that expect only valid .js files and we need a way to use the .glsl files in these.

Possible solutions:

  • this PR: a pre-build step that imports all the .glsl files via shaderChunkSrc.js and bundles them into shaderChunk.js, which can then be consumed by browsers and bundlers.
  • a pre-build step that bundles each file seperately so that e.g. shadowmap_pars_vertex.glsl becomes shadowmap_pars_vertex.js
  • rewrite the .glsl files as .js using template literals and expect people to set up complex syntax highighting themselves (if that is even possible)
  • do nothing (or write better documentation): it will not be possible to import the three.js src directly in the browser, and people bundling the source themselves have to add plugins such as glslify to their build step (as they currently do)

@pailhead
Copy link
Contributor

pailhead commented Jul 13, 2018

we have build tools and importers that expect only valid .js files and we need a way to use the .glsl files in these.

I don't understand this, could you please elaborate?

If we refers to people who write javascript then your statement is wrong, and there are ways to transform various .ext files to something that javascript can use. It may actually be more common today than importing things manually into an html file (that's how I understood this discussion).

Both your a) and b) mean this: (?)

npm run build:glsl
npm run build

Which makes a whole lot of sense to me.

JavaScript modules - this means that discussion of alternate approaches to materials, while interesting, is not relevant here.

I think you misread something.

bundling, minifying, tree shaking etc. will be necessary for most projects. But increasing number of projects could benefit from using threejs in its source form.

Could you give an example of such a project? Since browsers being able to import modules is very new and I'm not fully familiar with it yet, perhaps I'm just not seeing the potential applications.

These two comments, from yourself and @arodic seem to tackle the material systems or at least the intricacies of building them (along with the rest of three). Not just how browsers import some js files in 2018. Feel free to clarify what you want discussed in this thread, but i'm curious about the same thing you are.

@looeee
Copy link
Collaborator

looeee commented Jul 13, 2018

we have build tools and importers that expect only valid .js files and we need a way to use the .glsl files in these.

I don't understand this, could you please elaborate?

Sure, I should have said "bundlers" rather than build tools. Rollup.js is an example - you cannot pass a .glsl file into rollup without a plugin. By importers, I mainly mean browsers, since you can now import ES6 modules directly in the browser.

Both your a) and b) mean this: (?)

Yes.

@pailhead
Copy link
Contributor

pailhead commented Jul 13, 2018

Is a rollup plugin analogous to a webpack loader?

I still don't understand the final scenario.

Say we have some chunk

/src/chunks/some_chunk.glsl

What are the potential uses for it? Someone who uses three.min.js never sees a .glsl file, they see THREE.ShaderChunk dictionary with strings. So for that user, it's as if the .glsl file never existed.

I couldn't find if the es6 syntax is any different but if it's the same as before:

<script src='/js/three/src/chunks/some_chunk.glsl.module.js'/>

What does this achieve, since, it's highly likely that this page will call new THREE.WebGLRenderer() which imports the entire compiled ShaderChunk.

Sounds like this is not even a consideration for this library (?):

import someChunk from 'node_modules/three/src/chunks/some_chunk.glsl'

With this pr though, it seems like something like this would be possible:

import { someChunk } from 'node_modules/three/src/chunks'

But i'm not sure if it is actually, nor if it is - how does it map to what the browser does. Either way, each one of these snippets results in a string, no more no less.

All of this seems very relevant to how the materials are being used?

@arodic
Copy link
Contributor Author

arodic commented Jul 13, 2018

I don't think i'll ever end up in a situation where some .html page would load a bunch of atomic es6 modules from three.js

Here is where my motivation for this PR comes from... It's mostly about development process, not deployment.

I currently find myself in a situation where I experiment with UI framework designed around three.js while simultaneously I thinker with three.js source and examples/ From my process perspective, these components share the same source tree. This enables me to quickly explore ideas that require changes in three.js source, examples, and UI framework without any constraints or limitations.

At the same time, In my development environment, I have no issues loading thousands of files via module imports in a split second. Full threejs src reload with multiple example reload is so fast it adds pretty much zero overhead to my process thus I have no need for any build tools whatsoever #nonpminstall

Anyway,

Another thing to consider (future tech) is HTML modules. It should give us an option to include HTML files from js modules. If glsl code lives inside HTML modules with <script type=glsl> it could be the most graceful and #usetheplatform solution for the above.

Thanks @looeee for the summary. Should I close this PR @mrdoob?

@tschoartschi
Copy link

@arodic
My hope is to make threejs work without any build process whatsoever since modules and http2 have landed in most browsers.

@looeee
Hmm, that is a good point. On the other hand, we may possibly never reach the time where importing thousands of small files is as performant as importing ten or so larger ones, since you will still want to do minification and compression. Compression will not work as well on small files as it does on large ones, while minification will at least still require a build step of some kind.

I think loading the shaders async could be an interesting approach. It would enalbe you to use the glsl files as they are (maybe do some minification, whitespace and comment removal, variable renaming for production). Furthermore it would trim down the JS size of Three.js which is important for many reasons, it's not only download time also JavaScript parsing can be expensive on some devices (very interesting comparision between parsing JS and an JPEG, full article here). If you have more chunks it enables you to do better caching. If you would split the code into one JS file and one glsl file you could cache them seperately. If there are only changes in the JS file you only need to load the JS file and keep the glsl file cached and the other way around.

The Ember.js team are investigating something interesting for the templates (HTML files) in their framework. They split the HTML "code" from the JS code and serve the HTML "code" as binary data. This makes parsing very fast and helped them improve startup time a lot. More info here, and a very interesting video here

I think something similar could be possible with glsl files as well.

But if you load glsl files async this makes everything which relies on these files async as well and this can get complicated very quickly. I'm not sure if this is worth the whole effort but definitly an interesting idea.

@pailhead
Copy link
Contributor

pailhead commented Jul 16, 2018

@tschoartschi

I think every single user involved in this conversation has a different use case. @arodic doesn’t seem to use npm at all.

What you’re describing is totally possible, right here right now. You just have to write your own work around and three.js will still load the glsl through the built chunks thus doing it twice.

Removing the chunks from the core may be the common denominator that all of us people, and all of the proposals share.

SO sees the async loading and CORS questions daily. Imagine if Material had this requirement as well, no one would be able to use three.js.

@roomle-build
Copy link

I stumbled across this issue after going through the following issue: #6241

SO sees the async loading and CORS questions daily. Imagine if Material had this requirement as well, no one would be able to use three.js.

I don't think we should bloat three.js only because some hobby developers have problem setting up a server correctly. We could still provide three.legacy.js with everything built into and then a modern version which utilizes the web platform best. I think file size should be a concern. Tools like Unity started to acknowledge this problem as well. Unity presented a runtime which only weighs in 72KB (right now it's only for 2D but Unity will bring this to 3D for sure). If you are interested the presentation is on youtube: https://youtu.be/cmRSkHl-Gv0?t=1h34m12s

It's off topic but I think if we do not focus on file size, we will lose ground against upcoming and other engines. In my opinion it's good to focus on ease of use but there should also be an option for professional users. And it makes sense to bake this into the framework and not let every user implement her or his own workaround.

@arodic
Copy link
Contributor Author

arodic commented Jul 17, 2018

Ok, since there is no clear path forward for this PR, I'll close it and update the issue with relevant information from this discussion.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants