flutter_scene
flutter_scene copied to clipboard
Custom material shaders
Implement a workflow for users to write shaders that customize managed lighting models. To start off, support standard (PBR) and unlit (blank slate) lighting models.
Why managed?
We could potentially go the route of making custom materials extremely simple by having the user straight up write the final Flutter GPU vertex/fragment shaders in their entirety. But such an approach would present an enormous usability tax on users:
- In order to keep the animation system working, users would be forced to add a bunch of confusing boilerplate functions or macro calls in their shaders (or just not support customizing the vertex stage, which IMO would be an unacceptable limitation).
- The vast majority of the time, users will want to customize an existing lighting model rather than entirely roll their own (but we'd still allow users to basically do this by providing an
unlitlighting model). We can optimize for the common case by allowing the user to customize existing lighting models instead.
Design proposal
- Users can import a
MaterialLibraryusing a Native Assets buildhookbuildMaterialLibrary. - The descriptor handed to
buildMaterialLibraryis a list of paths to.materialfiles. -
buildMaterialLibraryrenders out a shader set for each material, with the vertexvertexstage containing at least two variations (forskinnedandunskinned). -
.materialfiles are text files which contain:- A
lighting_modelmetadata field to specify the lighting model. For starters, supportstandard(default, uses Flutter Scene's PBR lighting) andunlit(a blank slate). This determines the writable globals in the fragment stage. All globals are optional and have reasonable defaults. (The globals below loosely resemble's Godot's convention for specifying in/out parameters) - Writable fragment globals are as follows:
-
unlitexposesout vec4 COLORglobal. -
standardexposesout vec3 ALBEDO,out float ALPHA,out float METALLIC,out float ROUGHNESS,vec3 EMISSION,float AMBIENT_OCCLUSION, andvec3 NORMAL_MAP(whereNORMAL_MAPis the tangent space normal, as opposed to the vertex normal -- similar to other varying attributes, vertex normals are accessible viain vec3 v_normal).
-
- For vertex shaders, the basic globals include:
inout vec3 MODEL_POSITION,inout vec3 NORMAL(note: no tangents are necessary, we currently work out the tangent space while rendering), andinout vec2 TEXTURE_COORDS. All of these globals are already set to the input geometry data beforevoid vertex()(the user's code) is called. We can consider adding flags later for e.g. setting the world position directly instead. - Metadata lists specifying custom uniform inputs (including textures), and which stages each uniform intends to target) as well as shared varying parameters. Uniform block fields can have defaults.
- A shader section, containing GLSL code.
- The shader section can optionally contain
void vertex()and/orvoid fragment(). - The vertex/fragment stages have documented sets of globals available (as specified above).
- The shader section can optionally contain
- A
- At runtime, material libraries can be loaded with
/* static */ MaterialLibrary.fromAsset('mylibrary.matlib'). - Materials of the correct base type can be instantiated with
matlib.createMaterial('custommat'). - Internally, the
Materialis handed the vert/frag gpu.Shaders along with a custom uniform map. -
Materialprovides functionality for setting custom uniform parameters (which are type checked in debug builds). E.g.material.setUniform(String name, dynamic value). Uniform layout and allocation management are internal concerns.