First, this commit introduces some very simple bugfixes regarding ES2
compatibility related to postprocessing.
Second, this commit adds support for creating textures specified as R8, SRGB8,
and SRGB8_A8 in ES2. R8 is trivial: just use GL_LUMINANCE instead. The sRGB
formats, however, are maybe a bit more controversial. As implemented, they
instead just use the equivalent non-sRGB formats. This is of course technically
incorrect. There are a few approaches to how to add sRGB compatibility for ES2
that I can think of.
1. Do a bunch of complex shader nonsense in matc. Maybe even traversing the AST
and ensuring any texture lookup of a texture flagged as sRGB uses some
compatibility function. This would require static analysis to track if samplers
are reassigned to another variable, for example. This of course also breaks down
if you don't know at compile time if the shader will receive an RGB or an sRGB
sampler, or if the shader should be able to support both RGB or sRGB samplers.
Really only worth mentioning here for the sake of completion.
2. You could also generate simple compatibility functions to look up each
sampler, which would only apply to FL0 materials.
First, we would have to extend the material format to be able to explicitly
"color" a sampler as sRGB or not, like:
```
parameters : [
{
type : sampler2d,
name : albedo,
precision : medium,
colorSpace : srgb,
},
{
type : sampler2d,
name : normal,
precision : medium,
colorSpace : linear,
}
],
```
Then, the following GLSL code would be generated.
```glsl
\#if __VERSION__ == 100
vec4 texture_albedo(vec2 position) {
return sRGBtoLinear(texture2D(materialParams_albedo, position));
}
vec4 texture_normal(vec2 position) {
return texture2D(materialParams_albedo, position);
}
\#else
vec4 texture_albedo(vec2 position) {
return texture(materialParams_albedo, position);
}
vec4 texture_normal(vec2 normal) {
return texture(materialParams_normal, position);
}
\#endif
```
Finally, at runtime, if a sampler is "colored" one way or the other, we would
verify that only the appropriate kinds of samplers are bound.
I'm actually very partial to this solution. Since sRGB compatibility is only a
concern on ES2, we can generate this code only for FL0 shaders, which already
require GLSL shader authors to care about ESSL 1.0 compatibility by calling the
appropriate `textureXX` functions. Additionally, it provides a layer of
high-level validation that texture lookups are correct, even if a real ES2
context is not available on the device being tested.
3. Leave it entirely up to the client. (What this commit does.) This leaves
client code ripe for making mistakes, but luckily, we can go back and do
solution 2 whenever. If specifying a color space for a sampler remains optional,
then if this feature is retrofitted in the future, client code will continue to
compile.
Filamat
Filamat allows for generating materials programatically on the device as opposed to with the matc
tool on the host machine. The cost is a binary size increase of your app due to the relatively
larger size of the filamat library.
For a smaller-sized library, see filamat_lite. It has no dependencies on
glslang, but can only compile materials for OpenGL and does no shader code optimization.
The filamat package is included in the releases available on GitHub.
Libraries
Filamat is distributed as a set of static libraries you must link against:
filamat, Filamat main libraryfilabridge, Support library for Filament / Filamatshaders, Shader text for material generationutils, Support library for Filament / Filamatsmol-v, SPIR-V compression library
To use Filamat from Java you must use the following two libraries instead:
filamat-java.jar, Contains Filamat's Java classesfilamat-jni, Filamat's JNI bindings
Linking against Filamat
This walkthrough will get you successfully compiling and linking native code against Filamat with minimum dependencies.
To start, download Filament's latest binary release
and extract into a directory of your choosing. Binary releases are suffixed with the platform name,
for example, filament-20181009-linux.tgz.
Create a file, main.cpp, in the same directory with the following contents:
#include <filamat/MaterialBuilder.h>
#include <iostream>
using namespace filamat;
int main(int argc, char** argv)
{
// Must be called before any materials can be built.
MaterialBuilder::init();
MaterialBuilder builder;
builder
.name("My material")
.material("void material (inout MaterialInputs material) {"
" prepareMaterial(material);"
" material.baseColor.rgb = float3(1.0, 0.0, 0.0);"
"}")
.shading(MaterialBuilder::Shading::LIT)
.targetApi(MaterialBuilder::TargetApi::ALL)
.platform(MaterialBuilder::Platform::ALL);
Package package = builder.build();
if (package.isValid()) {
std::cout << "Success!" << std::endl;
}
// Call when finished building all materials to release internal MaterialBuilder resources.
MaterialBuilder::shutdown();
return 0;
}
The directory should look like:
|-- README.md
|-- bin
|-- docs
|-- include
|-- lib
|-- main.cpp
We'll use a platform-specific Makefile to compile and link main.cpp with Filamat's libraries.
Copy your platform's Makefile below into a Makefile inside the same directory.
Linux
FILAMENT_LIBS=-lfilamat -lfilabridge -lshaders -lutils -lsmol-v
CC=clang++
main: main.o
$(CC) -Llib/x86_64/ -stdlib=libc++ main.o $(FILAMENT_LIBS) -lpthread -ldl -o main
main.o: main.cpp
$(CC) -Iinclude/ -std=c++17 -stdlib=libc++ -pthread -c main.cpp
clean:
rm -f main main.o
.PHONY: clean
macOS
FILAMENT_LIBS=-lfilamat -lfilabridge -lshaders -lutils -lsmol-v
CC=clang++
main: main.o
$(CC) -Llib/x86_64/ main.o $(FILAMENT_LIBS) -o main
main.o: main.cpp
$(CC) -Iinclude/ -std=c++17 -c main.cpp
clean:
rm -f main main.o
.PHONY: clean
Windows
Note that the static libraries distributed for Windows include several
variants: mt, md, mtd, mdd. These correspond to the run-time library
flags
/MT, /MD, /MTd, and /MDd, respectively. Here we use the mt variant.
When building Filamat from source, the USE_STATIC_CRT CMake option can be
used to change the run-time library version.
FILAMENT_LIBS=lib/x86_64/mt/filamat.lib lib/x86_64/mt/filabridge.lib lib/x86_64/mt/shaders.lib \
lib/x86_64/mt/utils.lib lib/x86_64/mt/smol-v.lib
CC=clang-cl.exe
main.exe: main.obj
$(CC) main.obj $(FILAMENT_LIBS) gdi32.lib user32.lib opengl32.lib
main.obj: main.cpp
$(CC) /MT /Iinclude/ /std:c++17 /c main.cpp
clean:
del main.exe main.obj
.PHONY: clean
Compiling
You should be able to invoke make and run the executable successfully:
$ make
$ ./main
Success!
On Windows, you'll need to open up a Visual Studio Native Tools Command Prompt
and invoke nmake instead of make.
Using the Material with Filament
For simplicity, this demo doesn't do anything useful with the built material package. To use the material with Filament, pass the material package's data into a Filament Material builder:
Package package = builder.build();
filament::Material* myMaterial = Material::Builder()
.package(package.getData(), package.getSize())
.build(*engine);
Note that this will require linking against Filament's libraries in addition to Filamat's.
Filamat Lite
The filamat_lite library is interchangeable with filamat, with a few caveats:
- Material compilation is only supported for the OpenGL backend.
- No shader-level optimization is performed.
- GLSL correctness is not checked.
In addition, filamat_lite only performs a simple text match to determine which properties on the
MaterialInputs structure are set. The material input variable must also always be refered to by
the name material.
void anotherFunction(inout MaterialInputs m) {
// Incorrect! The MaterialInputs is being referred to by the name "m".
m.metallic = 0.0;
}
void aFunction(inout MaterialInputs material) {
// Works, but only because the variable name "material" is used.
material.reflectance = 0.5;
}
// The MaterialInputs variable must be named material.
void material(inout MaterialInputs material) {
prepareMaterial(material);
// Good.
material.roughness = materialParams.roughness;
material.baseColor.rgb = vec3(1.0, 0.0, 1.0);
aFunction(material);
anotherFunction(material);
}