emitMaterialFragment function
The .frag source for program, which should already be specialised.
describeMaterial answers the other half — what the engine may bind to it.
Implementation
String emitMaterialFragment(MaterialProgram program) {
final out = StringBuffer()
..writeln('#version 460 core')
..writeln()
..writeln('// Generated from a material written in the material language')
..writeln('// — gfx-84n. The source is the thing to edit; this file is')
..writeln('// what the shader build compiles and what the three GPU')
..writeln('// backends translate. The software backend evaluates the same')
..writeln('// tree instead of reading this.')
// It gathers no lights, so it keeps no light list: see
// `LightingModel.usesLightList`.
..writeln('#define F3D_NO_LIGHT_LIST')
..writeln('#include <lib/surface.glsl>')
..writeln();
// Both prototypes have to be satisfied whether or not anything calls them,
// which is what `unlit.frag` says about its own pair. A material in this
// language never accumulates lights — it returns the light the surface
// emits — so these are the same honest stubs.
out
..writeln('// Never called: a material in this language returns the light')
..writeln('// its surface emits rather than gathering any. The prototypes')
..writeln('// in surface.glsl still have to be satisfied.')
..writeln('vec3 ShadeLight(Surface s, LightSample light) {')
..writeln(' return s.albedo;')
..writeln('}')
..writeln()
..writeln('float LightVisibility(Surface s, LightSample light, int i) {')
..writeln(' return 1.0;')
..writeln('}')
..writeln()
..writeln('void main() {')
..writeln(' Surface s = ReadSurface();');
for (final statement in program.body) {
switch (statement) {
case MaterialLet(:final name, :final value):
out.writeln(' ${value.type.name} $name = ${_glsl(value)};');
case MaterialReturn(:final value):
out
..writeln(' vec4 result = ${_glsl(value)};')
..writeln(' WriteSurface(result.rgb, result.a);');
}
}
out.writeln('}');
return out.toString();
}