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

WIP: Three.JS Shader Language Proposal #17321

Closed
wants to merge 4 commits into from
Closed

Conversation

sunag
Copy link
Collaborator

@sunag sunag commented Aug 22, 2019

Some motivations

  • Custom syntax for creation of more specifics and advanced shaders
  • Auto GLSL chunk core conversion for Nodes and NodeMaterial System
    ( I think that the last hard change to merger NodeMaterial to the core )
  • Simplifying the current shader creation model
  • More performance
  • Easy integration with WebGL2

Optimization

EDITED -> CHROME WINDOWS ONLY

The flow system of the parser optimize GLSL call tree, I make a test optimizing using NodeMaterial (current performance 1/1), adding the parse, its excluding functions and properties not used in code. The result was 2x at 3x faster (2.5~/1) than the current material.

Link to benchmark

https://rawgit.com/sunag/three.js/dev-shader-parser/examples/webgl_performance_nodes.html?count=10
count=10 program per material

Namespace Proposal

The idea is to replace the chunks for namespaces, making it more organized and clean to work.
For example:

Example source-code only ( Open console )

https://rawgit.com/sunag/three.js/dev-shader-parser/examples/webgl_nodes.html

Example

namespace brdf {

	vec2 integrateSpecularBRDF( const in float dotNV, const in float roughness ) { }

}

namespace common {

	highp float rand( const in vec2 uv ) {
		const highp float a = 12.9898, b = 78.233, c = 43758.5453;
		highp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );
		return fract(sin(sn) * c);
	}

}

void main() {

	// rand is generated as `_common_rand` to GLSL conversion
	// rand function is only declared if is called in flow of the main()
	// this feature is the real reponsible by optimize the code compile 2x at 3x faster
	// integrateSpecularBRDF although declared, is not emited to GLSL code

	common.rand( vec2( 2.0 ) );

}

Material property preprocessor

Now we no longer have to worry about hiding functions using defines since we have namespace. The idea is to use the same preprocessor to detect materials properties in shader code, using #ifprop for example.

#ifprop normalMap

	uniform sampler2D normalMap;
	uniform vec2 normalScale;

#endifprop

CODE IS WIP

@takahirox
Copy link
Collaborator

Link to benchmark

https://rawgit.com/sunag/three.js/dev-shader-parser/examples/webgl_performance_nodes.html

Demo doesn't work on Firefox. Console error is

SyntaxError: bad method definition Parser.js:95:8

@sunag
Copy link
Collaborator Author

sunag commented Aug 23, 2019

@takahirox Thanks updated! I update the description. Unfortunately, I did not get good results on firefox, only Chrome Windows, you tested on Mac?

@looeee
Copy link
Collaborator

looeee commented Aug 23, 2019

On Android chrome:

StandardNodeMaterial: 1.07s
MeshStandardNodeMaterial: 1.067s

MeshStandardMaterial: 0.769s

@donmccurdy
Copy link
Collaborator

It's safe to assume that a GLSL parser will be a bit slower than the current (pre-NodeMaterial) approach. Whether it would ultimately be faster or slower to construct shaders than NodeMaterial today, I'm not sure. For the sake of discussion let's set strict parsing performance aside for a moment, or just assume the problem could be solved by enabling users to precompile their node materials somehow.

The remaining motivations you mention are interesting ones — some of them I understand, and some I do not:

  • Custom syntax for creation of more specifics and advanced shaders

Is the goal here easier shader authoring for developers, similar to #17105? I agree that the closer we are to GLSL, the easier that shader creation will be for developers comfortable with GLSL. Creating a dialect for threejs with namespaces and macros, I'm undecided about – it simplifies some things and makes other things more complicated. #17105 seemed like a good improvement already.

There is another subset of developers and artists who are not comfortable writing GLSL, for whom I would still love to provide a workflow with a UI node graph editor. Whether that means an official UI for creating threejs shaders, or support for loading node graphs created in other tools, I'm not sure. Related: MaterialX Standard Nodes,THREE.ShadeNodeLoader.

  • Auto GLSL chunk core conversion for Nodes and NodeMaterial System ( I think that the last hard change to merger NodeMaterial to the core )

I don't understand this item. Do you mean it allows better sharing of GLSL chunks between existing materials and NodeMaterials? Or how does it help with merging NodeMaterial to core?

  • Simplifying the current shader creation model

Simplifying how NodeMaterial works, or simplifying how developers would use NodeMaterial? This PR adds 1000 lines to nodes/ without removing anything; if you mean simplifying NodeMaterial itself, it might be helpful to understand how much that would help.

  • Easy integration with WebGL2

Yes; I don't know enough here to say how much easier, but intuitively this makes sense.


Summary — I'm undecided about this proposal. I understand some of the potential benefits, but it also sounds more complex to maintain and debug to me. If you disagree and are pretty sure it's cleaner, I'm glad to discuss that more. Otherwise, I'm hoping there is a good path to moving more users over to NodeMaterial without going quite this far, quite yet.

@takahirox
Copy link
Collaborator

@sunag

I tested on Windows + Firefox. Yeah, the new system is slower than the current one.

Node Material System
StandardNodeMaterial | 12.060 seconds
MeshStandardNodeMaterial | 12.255 seconds

Current Material System
MeshStandardMaterial | 5.128 seconds

@sunag
Copy link
Collaborator Author

sunag commented Aug 24, 2019

For the sake of discussion let's set strict parsing performance aside for a moment, or just assume the problem could be solved by enabling users to precompile their node materials somehow....

Yeah. The parser performance in non-Windows+Chrome can be improved caching the includes like the traditional compilers for example, but it is not worth the work if the proposal not convince. @takahirox @looeee thanks your feedback about this.

Auto GLSL chunk core conversion for Nodes and NodeMaterial System
Simplifying the current shader creation model

The idea of simply was not done effectively, that would be closer to brainstorm and a preview of the code, than the proposed result.

An example in case of include nodes in the core, for example, is auto-convert natives to node-input if this is set in properties, like material.roughness in meshphysical_frag.glsl.js

// its check is some node is implemented in native uniform
// if is node is converted and moved automatically to main() code
node uniform float roughness;

For this not need to change the current chunks only implementing the compiler.
This can automate the calls of node.analyze and node.flow just like reusing the same shader code to NodeMaterial, but maybe the sacrifice is not worth it.

glsl parser in other context

The good news of this is that I did not design thinking about implementing in the core but in NodeMaterial, not like this one, on this PR, but using GLSLNode and for this, this code is already optimized. Part of this code is for this release: #17265

For example:
All ConstNode, StructNode, FunctionNode, will be extended of GLSLNode making the parser safer and more focused. This to fix hierarchy problems like your issue in #16440 (comment) about Defining a custom FunctionNode is tricky.

Code like this that are complex to do using many Nodes, can be done writing complex nodes only using GLSL code, so it is not necessary to create complex nodes like this approach: https://github.com/mrdoob/three.js/blob/dev/examples/jsm/nodes/misc/TextureCubeUVNode.js, it can be reused directing of the core code, for example.

If you disagree and are pretty sure it's cleaner, I'm glad to discuss that more. Otherwise, I'm hoping there is a good path to moving more users over to NodeMaterial without going quite this far, quite yet.

It is just an idea that I proposed, I have not great intentions to continue this outside of the context of GLSLNode.

@donmccurdy Thank very much your feedback.

@sunag
Copy link
Collaborator Author

sunag commented Sep 12, 2019

I commit the GLSLParser here 2419ba5 and an example of simplification is possible to see in these two examples:

ColorSpaceNode
reuse specific functions with code dependencies of native shader
Now https://github.com/sunag/three.js/blob/dev-r8/examples/jsm/nodes/utils/ColorSpaceNode.js
After https://github.com/mrdoob/three.js/blob/dev/examples/jsm/nodes/utils/ColorSpaceNode.js

TextureCubeUVNode
interpreter complex code and transform all GLSL functions, consts, defines and struct to nodes including dependencies
Now https://github.com/sunag/three.js/blob/dev-r8/examples/jsm/nodes/misc/TextureCubeUVNode.js
After https://github.com/mrdoob/three.js/blob/dev/examples/jsm/nodes/misc/TextureCubeUVNode.js
Related: #16440 (comment)

And other improvements that I will document later...

I will work on it now to sync native threejs shader functions needs for NodeMaterial with nodes like happen in ColorSpaceNode for example, the same should count for TextureCubeUVNode, NormalMapNode and others...

@sunag sunag closed this Sep 12, 2019
@sunag sunag mentioned this pull request Sep 29, 2021
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.

4 participants