629 lines
27 KiB
C++
629 lines
27 KiB
C++
/*
|
|
* Copyright (C) 2017 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "ShaderGenerator.h"
|
|
|
|
#include <filament/MaterialEnums.h>
|
|
|
|
#include <private/filament/EngineEnums.h>
|
|
#include <private/filament/SibGenerator.h>
|
|
#include <private/filament/Variant.h>
|
|
|
|
#include <utils/CString.h>
|
|
|
|
#include "filamat/MaterialBuilder.h"
|
|
#include "CodeGenerator.h"
|
|
#include "../UibGenerator.h"
|
|
|
|
#include <iterator>
|
|
|
|
namespace filamat {
|
|
|
|
using namespace filament;
|
|
using namespace filament::backend;
|
|
using namespace utils;
|
|
|
|
static const char* getShadingDefine(Shading shading) noexcept {
|
|
switch (shading) {
|
|
case Shading::LIT: return "SHADING_MODEL_LIT";
|
|
case Shading::UNLIT: return "SHADING_MODEL_UNLIT";
|
|
case Shading::SUBSURFACE: return "SHADING_MODEL_SUBSURFACE";
|
|
case Shading::CLOTH: return "SHADING_MODEL_CLOTH";
|
|
case Shading::SPECULAR_GLOSSINESS: return "SHADING_MODEL_SPECULAR_GLOSSINESS";
|
|
}
|
|
}
|
|
|
|
static void generateMaterialDefines(io::sstream& os, MaterialBuilder::PropertyList const properties,
|
|
const MaterialBuilder::PreprocessorDefineList& defines) noexcept {
|
|
for (size_t i = 0; i < MaterialBuilder::MATERIAL_PROPERTIES_COUNT; i++) {
|
|
CodeGenerator::generateMaterialProperty(os, static_cast<MaterialBuilder::Property>(i), properties[i]);
|
|
}
|
|
// synthetic defines
|
|
bool hasTBN =
|
|
properties[static_cast<int>(MaterialBuilder::Property::ANISOTROPY)] ||
|
|
properties[static_cast<int>(MaterialBuilder::Property::NORMAL)] ||
|
|
properties[static_cast<int>(MaterialBuilder::Property::BENT_NORMAL)] ||
|
|
properties[static_cast<int>(MaterialBuilder::Property::CLEAR_COAT_NORMAL)];
|
|
CodeGenerator::generateDefine(os, "MATERIAL_NEEDS_TBN", hasTBN);
|
|
|
|
// Additional, user-provided defines.
|
|
for (const auto& define : defines) {
|
|
CodeGenerator::generateDefine(os, define.name.c_str(), define.value.c_str());
|
|
}
|
|
}
|
|
|
|
static void generateVertexDomain(io::sstream& vs, VertexDomain domain) noexcept {
|
|
switch (domain) {
|
|
case VertexDomain::OBJECT:
|
|
CodeGenerator::generateDefine(vs, "VERTEX_DOMAIN_OBJECT", true);
|
|
break;
|
|
case VertexDomain::WORLD:
|
|
CodeGenerator::generateDefine(vs, "VERTEX_DOMAIN_WORLD", true);
|
|
break;
|
|
case VertexDomain::VIEW:
|
|
CodeGenerator::generateDefine(vs, "VERTEX_DOMAIN_VIEW", true);
|
|
break;
|
|
case VertexDomain::DEVICE:
|
|
CodeGenerator::generateDefine(vs, "VERTEX_DOMAIN_DEVICE", true);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void generatePostProcessMaterialVariantDefines(io::sstream& shader,
|
|
PostProcessVariant variant) noexcept {
|
|
switch (variant) {
|
|
case PostProcessVariant::OPAQUE:
|
|
CodeGenerator::generateDefine(shader, "POST_PROCESS_OPAQUE", 1u);
|
|
break;
|
|
case PostProcessVariant::TRANSLUCENT:
|
|
CodeGenerator::generateDefine(shader, "POST_PROCESS_OPAQUE", 0u);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static size_t countLines(const char* s) noexcept {
|
|
size_t lines = 0;
|
|
size_t i = 0;
|
|
while (s[i] != 0) {
|
|
if (s[i++] == '\n') lines++;
|
|
}
|
|
return lines;
|
|
}
|
|
|
|
static size_t countLines(const CString& s) noexcept {
|
|
size_t lines = 0;
|
|
for (char i : s) {
|
|
if (i == '\n') lines++;
|
|
}
|
|
return lines;
|
|
}
|
|
|
|
static void appendShader(io::sstream& ss,
|
|
const CString& shader, size_t lineOffset) noexcept {
|
|
if (!shader.empty()) {
|
|
size_t lines = countLines(ss.c_str());
|
|
ss << "#line " << lineOffset + 1 << '\n';
|
|
ss << shader.c_str();
|
|
if (shader[shader.size() - 1] != '\n') {
|
|
ss << "\n";
|
|
lines++;
|
|
}
|
|
// + 2 to account for the #line directives we just added
|
|
ss << "#line " << lines + countLines(shader) + 2 << "\n";
|
|
}
|
|
}
|
|
|
|
ShaderGenerator::ShaderGenerator(
|
|
MaterialBuilder::PropertyList const& properties,
|
|
MaterialBuilder::VariableList const& variables,
|
|
MaterialBuilder::OutputList const& outputs,
|
|
MaterialBuilder::PreprocessorDefineList const& defines,
|
|
CString const& materialCode, size_t lineOffset,
|
|
CString const& materialVertexCode, size_t vertexLineOffset,
|
|
MaterialBuilder::MaterialDomain materialDomain) noexcept {
|
|
|
|
std::copy(std::begin(properties), std::end(properties), std::begin(mProperties));
|
|
std::copy(std::begin(variables), std::end(variables), std::begin(mVariables));
|
|
std::copy(std::begin(outputs), std::end(outputs), std::back_inserter(mOutputs));
|
|
|
|
mMaterialFragmentCode = materialCode;
|
|
mMaterialVertexCode = materialVertexCode;
|
|
mIsMaterialVertexShaderEmpty = materialVertexCode.empty();
|
|
mMaterialLineOffset = lineOffset;
|
|
mMaterialVertexLineOffset = vertexLineOffset;
|
|
mMaterialDomain = materialDomain;
|
|
mDefines = defines;
|
|
|
|
if (mMaterialFragmentCode.empty()) {
|
|
if (mMaterialDomain == MaterialBuilder::MaterialDomain::SURFACE) {
|
|
mMaterialFragmentCode =
|
|
CString("void material(inout MaterialInputs m) {\n prepareMaterial(m);\n}\n");
|
|
} else if (mMaterialDomain == MaterialBuilder::MaterialDomain::POST_PROCESS) {
|
|
mMaterialFragmentCode =
|
|
CString("void postProcess(inout PostProcessInputs p) {\n}\n");
|
|
}
|
|
}
|
|
if (mMaterialVertexCode.empty()) {
|
|
if (mMaterialDomain == MaterialBuilder::MaterialDomain::SURFACE) {
|
|
mMaterialVertexCode =
|
|
CString("void materialVertex(inout MaterialVertexInputs m) {\n}\n");
|
|
} else if (mMaterialDomain == MaterialBuilder::MaterialDomain::POST_PROCESS) {
|
|
mMaterialVertexCode =
|
|
CString("void postProcessVertex(inout PostProcessVertexInputs m) {\n}\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string ShaderGenerator::createVertexProgram(ShaderModel shaderModel,
|
|
MaterialBuilder::TargetApi targetApi, MaterialBuilder::TargetLanguage targetLanguage,
|
|
MaterialInfo const& material, const filament::Variant variant, Interpolation interpolation,
|
|
VertexDomain vertexDomain) const noexcept {
|
|
if (mMaterialDomain == MaterialBuilder::MaterialDomain::POST_PROCESS) {
|
|
return createPostProcessVertexProgram(shaderModel, targetApi,
|
|
targetLanguage, material, variant.key, material.samplerBindings);
|
|
}
|
|
|
|
io::sstream vs;
|
|
|
|
const CodeGenerator cg(shaderModel, targetApi, targetLanguage);
|
|
const bool lit = material.isLit;
|
|
|
|
cg.generateProlog(vs, ShaderType::VERTEX, material.hasExternalSamplers);
|
|
|
|
cg.generateQualityDefine(vs, material.quality);
|
|
|
|
CodeGenerator::generateDefine(vs, "MAX_SHADOW_CASTING_SPOTS", uint32_t(CONFIG_MAX_SHADOW_CASTING_SPOTS));
|
|
|
|
CodeGenerator::generateDefine(vs, "FLIP_UV_ATTRIBUTE", material.flipUV);
|
|
|
|
CodeGenerator::generateDefine(vs, "LEGACY_MORPHING", material.useLegacyMorphing);
|
|
|
|
const bool litVariants = lit || material.hasShadowMultiplier;
|
|
|
|
assert_invariant(filament::Variant::isValid(variant));
|
|
|
|
// note: even if the user vertex shader is empty, we can't use the "optimized" version if
|
|
// we're in masked mode because fragment shader needs the color varyings
|
|
const bool useOptimizedDepthVertexShader =
|
|
// must be a depth variant
|
|
filament::Variant::isValidDepthVariant(variant) &&
|
|
// must have an empty vertex shader
|
|
mIsMaterialVertexShaderEmpty &&
|
|
// but must not be MASKED mode
|
|
material.blendingMode != BlendingMode::MASKED &&
|
|
// and must not have transparent shadows
|
|
!(material.hasTransparentShadow &&
|
|
(material.blendingMode == BlendingMode::TRANSPARENT ||
|
|
material.blendingMode == BlendingMode::FADE));
|
|
|
|
CodeGenerator::generateDefine(vs, "USE_OPTIMIZED_DEPTH_VERTEX_SHADER",
|
|
useOptimizedDepthVertexShader);
|
|
|
|
// material defines
|
|
CodeGenerator::generateDefine(vs, "MATERIAL_HAS_SHADOW_MULTIPLIER",
|
|
material.hasShadowMultiplier);
|
|
|
|
CodeGenerator::generateDefine(vs, "MATERIAL_HAS_INSTANCES", material.instanced);
|
|
|
|
CodeGenerator::generateDefine(vs, "MATERIAL_HAS_VERTEX_DOMAIN_DEVICE_JITTERED",
|
|
material.vertexDomainDeviceJittered);
|
|
|
|
CodeGenerator::generateDefine(vs, "MATERIAL_HAS_TRANSPARENT_SHADOW",
|
|
material.hasTransparentShadow);
|
|
|
|
CodeGenerator::generateDefine(vs, "VARIANT_HAS_DIRECTIONAL_LIGHTING",
|
|
litVariants && variant.hasDirectionalLighting());
|
|
CodeGenerator::generateDefine(vs, "VARIANT_HAS_DYNAMIC_LIGHTING",
|
|
litVariants && variant.hasDynamicLighting());
|
|
CodeGenerator::generateDefine(vs, "VARIANT_HAS_SHADOWING",
|
|
litVariants && filament::Variant::isShadowReceiverVariant(variant));
|
|
CodeGenerator::generateDefine(vs, "VARIANT_HAS_SKINNING_OR_MORPHING",
|
|
variant.hasSkinningOrMorphing());
|
|
CodeGenerator::generateDefine(vs, "VARIANT_HAS_VSM",
|
|
filament::Variant::isVSMVariant(variant));
|
|
|
|
CodeGenerator::generateDefine(vs, getShadingDefine(material.shading), true);
|
|
generateMaterialDefines(vs, mProperties, mDefines);
|
|
|
|
AttributeBitset attributes = material.requiredAttributes;
|
|
if (variant.hasSkinningOrMorphing()) {
|
|
attributes.set(VertexAttribute::BONE_INDICES);
|
|
attributes.set(VertexAttribute::BONE_WEIGHTS);
|
|
if (material.useLegacyMorphing) {
|
|
attributes.set(VertexAttribute::MORPH_POSITION_0);
|
|
attributes.set(VertexAttribute::MORPH_POSITION_1);
|
|
attributes.set(VertexAttribute::MORPH_POSITION_2);
|
|
attributes.set(VertexAttribute::MORPH_POSITION_3);
|
|
attributes.set(VertexAttribute::MORPH_TANGENTS_0);
|
|
attributes.set(VertexAttribute::MORPH_TANGENTS_1);
|
|
attributes.set(VertexAttribute::MORPH_TANGENTS_2);
|
|
attributes.set(VertexAttribute::MORPH_TANGENTS_3);
|
|
}
|
|
}
|
|
CodeGenerator::generateShaderInputs(vs, ShaderType::VERTEX, attributes, interpolation);
|
|
|
|
// custom material variables
|
|
size_t variableIndex = 0;
|
|
for (const auto& variable : mVariables) {
|
|
CodeGenerator::generateVariable(vs, ShaderType::VERTEX, variable, variableIndex++);
|
|
}
|
|
|
|
// materials defines
|
|
generateVertexDomain(vs, vertexDomain);
|
|
|
|
// uniforms
|
|
cg.generateUniforms(vs, ShaderType::VERTEX,
|
|
BindingPoints::PER_VIEW, UibGenerator::getPerViewUib());
|
|
cg.generateUniforms(vs, ShaderType::VERTEX,
|
|
BindingPoints::PER_RENDERABLE, UibGenerator::getPerRenderableUib());
|
|
if (variant.hasSkinningOrMorphing()) {
|
|
cg.generateUniforms(vs, ShaderType::VERTEX,
|
|
BindingPoints::PER_RENDERABLE_BONES,
|
|
UibGenerator::getPerRenderableBonesUib());
|
|
cg.generateUniforms(vs, ShaderType::VERTEX,
|
|
BindingPoints::PER_RENDERABLE_MORPHING,
|
|
UibGenerator::getPerRenderableMorphingUib());
|
|
cg.generateSamplers(vs,
|
|
material.samplerBindings.getBlockOffset(BindingPoints::PER_RENDERABLE_MORPHING),
|
|
SibGenerator::getPerRenderPrimitiveMorphingSib(variant));
|
|
}
|
|
cg.generateUniforms(vs, ShaderType::VERTEX,
|
|
BindingPoints::PER_MATERIAL_INSTANCE, material.uib);
|
|
CodeGenerator::generateSeparator(vs);
|
|
// TODO: should we generate per-view SIB in the vertex shader?
|
|
cg.generateSamplers(vs,
|
|
material.samplerBindings.getBlockOffset(BindingPoints::PER_MATERIAL_INSTANCE),
|
|
material.sib);
|
|
|
|
// shader code
|
|
CodeGenerator::generateCommon(vs, ShaderType::VERTEX);
|
|
CodeGenerator::generateGetters(vs, ShaderType::VERTEX);
|
|
CodeGenerator::generateCommonMaterial(vs, ShaderType::VERTEX);
|
|
|
|
// main entry point
|
|
appendShader(vs, mMaterialVertexCode, mMaterialVertexLineOffset);
|
|
CodeGenerator::generateShaderMain(vs, ShaderType::VERTEX);
|
|
|
|
CodeGenerator::generateEpilog(vs);
|
|
|
|
return vs.c_str();
|
|
}
|
|
|
|
std::string ShaderGenerator::createFragmentProgram(ShaderModel shaderModel,
|
|
MaterialBuilder::TargetApi targetApi, MaterialBuilder::TargetLanguage targetLanguage,
|
|
MaterialInfo const& material, const filament::Variant variant,
|
|
Interpolation interpolation) const noexcept {
|
|
if (mMaterialDomain == MaterialBuilder::MaterialDomain::POST_PROCESS) {
|
|
return createPostProcessFragmentProgram(shaderModel, targetApi, targetLanguage, material,
|
|
variant.key, material.samplerBindings);
|
|
}
|
|
|
|
const CodeGenerator cg(shaderModel, targetApi, targetLanguage);
|
|
const bool lit = material.isLit;
|
|
|
|
io::sstream fs;
|
|
cg.generateProlog(fs, ShaderType::FRAGMENT, material.hasExternalSamplers);
|
|
|
|
cg.generateQualityDefine(fs, material.quality);
|
|
|
|
CodeGenerator::generateDefine(fs, "GEOMETRIC_SPECULAR_AA", material.specularAntiAliasing && lit);
|
|
|
|
CodeGenerator::generateDefine(fs, "CLEAR_COAT_IOR_CHANGE", material.clearCoatIorChange);
|
|
|
|
CodeGenerator::generateDefine(fs, "MAX_SHADOW_CASTING_SPOTS", uint32_t(CONFIG_MAX_SHADOW_CASTING_SPOTS));
|
|
|
|
auto defaultSpecularAO = shaderModel == ShaderModel::MOBILE ?
|
|
SpecularAmbientOcclusion::NONE : SpecularAmbientOcclusion::SIMPLE;
|
|
auto specularAO = material.specularAOSet ? material.specularAO : defaultSpecularAO;
|
|
CodeGenerator::generateDefine(fs, "SPECULAR_AMBIENT_OCCLUSION", uint32_t(specularAO));
|
|
|
|
// We only support both screen-space refractions and reflections at the same time.
|
|
// And the MATERIAL_HAS_REFRACTION/MATERIAL_HAS_REFLECTIONS defines signify if refraction/reflections are supported by
|
|
// the material.
|
|
|
|
CodeGenerator::generateDefine(fs, "MATERIAL_HAS_REFRACTION", material.refractionMode != RefractionMode::NONE);
|
|
if (material.refractionMode != RefractionMode::NONE) {
|
|
CodeGenerator::generateDefine(fs, "REFRACTION_MODE_CUBEMAP", uint32_t(RefractionMode::CUBEMAP));
|
|
CodeGenerator::generateDefine(fs, "REFRACTION_MODE_SCREEN_SPACE", uint32_t(RefractionMode::SCREEN_SPACE));
|
|
switch (material.refractionMode) {
|
|
case RefractionMode::NONE:
|
|
// can't be here
|
|
break;
|
|
case RefractionMode::CUBEMAP:
|
|
CodeGenerator::generateDefine(fs, "REFRACTION_MODE", "REFRACTION_MODE_CUBEMAP");
|
|
break;
|
|
case RefractionMode::SCREEN_SPACE:
|
|
CodeGenerator::generateDefine(fs, "REFRACTION_MODE", "REFRACTION_MODE_SCREEN_SPACE");
|
|
break;
|
|
}
|
|
CodeGenerator::generateDefine(fs, "REFRACTION_TYPE_SOLID", uint32_t(RefractionType::SOLID));
|
|
CodeGenerator::generateDefine(fs, "REFRACTION_TYPE_THIN", uint32_t(RefractionType::THIN));
|
|
switch (material.refractionType) {
|
|
case RefractionType::SOLID:
|
|
CodeGenerator::generateDefine(fs, "REFRACTION_TYPE", "REFRACTION_TYPE_SOLID");
|
|
break;
|
|
case RefractionType::THIN:
|
|
CodeGenerator::generateDefine(fs, "REFRACTION_TYPE", "REFRACTION_TYPE_THIN");
|
|
break;
|
|
}
|
|
}
|
|
|
|
CodeGenerator::generateDefine(fs, "MATERIAL_HAS_REFLECTIONS",
|
|
material.reflectionMode == ReflectionMode::SCREEN_SPACE);
|
|
|
|
bool multiBounceAO = material.multiBounceAOSet ?
|
|
material.multiBounceAO : shaderModel == ShaderModel::DESKTOP;
|
|
CodeGenerator::generateDefine(fs, "MULTI_BOUNCE_AMBIENT_OCCLUSION", multiBounceAO ? 1u : 0u);
|
|
|
|
assert_invariant(filament::Variant::isValid(variant));
|
|
|
|
// lighting variants
|
|
bool litVariants = lit || material.hasShadowMultiplier;
|
|
|
|
// material defines
|
|
CodeGenerator::generateDefine(fs, "MATERIAL_HAS_SHADOW_MULTIPLIER",
|
|
material.hasShadowMultiplier);
|
|
|
|
CodeGenerator::generateDefine(fs, "MATERIAL_HAS_INSTANCES", material.instanced);
|
|
|
|
CodeGenerator::generateDefine(fs, "MATERIAL_HAS_VERTEX_DOMAIN_DEVICE_JITTERED",
|
|
material.vertexDomainDeviceJittered);
|
|
|
|
CodeGenerator::generateDefine(fs, "MATERIAL_HAS_TRANSPARENT_SHADOW",
|
|
material.hasTransparentShadow);
|
|
|
|
CodeGenerator::generateDefine(fs, "VARIANT_HAS_DIRECTIONAL_LIGHTING",
|
|
litVariants && variant.hasDirectionalLighting());
|
|
CodeGenerator::generateDefine(fs, "VARIANT_HAS_DYNAMIC_LIGHTING",
|
|
litVariants && variant.hasDynamicLighting());
|
|
CodeGenerator::generateDefine(fs, "VARIANT_HAS_SHADOWING",
|
|
litVariants && filament::Variant::isShadowReceiverVariant(variant));
|
|
CodeGenerator::generateDefine(fs, "VARIANT_HAS_FOG",
|
|
filament::Variant::isFogVariant(variant));
|
|
CodeGenerator::generateDefine(fs, "VARIANT_HAS_PICKING",
|
|
filament::Variant::isPickingVariant(variant));
|
|
CodeGenerator::generateDefine(fs, "VARIANT_HAS_VSM",
|
|
filament::Variant::isVSMVariant(variant));
|
|
CodeGenerator::generateDefine(fs, "VARIANT_HAS_SSR",
|
|
filament::Variant::isSSRVariant(variant));
|
|
|
|
CodeGenerator::generateDefine(fs, "MATERIAL_HAS_DOUBLE_SIDED_CAPABILITY",
|
|
material.hasDoubleSidedCapability);
|
|
switch (material.blendingMode) {
|
|
case BlendingMode::OPAQUE:
|
|
CodeGenerator::generateDefine(fs, "BLEND_MODE_OPAQUE", true);
|
|
break;
|
|
case BlendingMode::TRANSPARENT:
|
|
CodeGenerator::generateDefine(fs, "BLEND_MODE_TRANSPARENT", true);
|
|
break;
|
|
case BlendingMode::ADD:
|
|
CodeGenerator::generateDefine(fs, "BLEND_MODE_ADD", true);
|
|
break;
|
|
case BlendingMode::MASKED:
|
|
CodeGenerator::generateDefine(fs, "BLEND_MODE_MASKED", true);
|
|
break;
|
|
case BlendingMode::FADE:
|
|
// Fade is a special case of transparent
|
|
CodeGenerator::generateDefine(fs, "BLEND_MODE_TRANSPARENT", true);
|
|
CodeGenerator::generateDefine(fs, "BLEND_MODE_FADE", true);
|
|
break;
|
|
case BlendingMode::MULTIPLY:
|
|
CodeGenerator::generateDefine(fs, "BLEND_MODE_MULTIPLY", true);
|
|
break;
|
|
case BlendingMode::SCREEN:
|
|
CodeGenerator::generateDefine(fs, "BLEND_MODE_SCREEN", true);
|
|
break;
|
|
}
|
|
switch (material.postLightingBlendingMode) {
|
|
case BlendingMode::OPAQUE:
|
|
CodeGenerator::generateDefine(fs, "POST_LIGHTING_BLEND_MODE_OPAQUE", true);
|
|
break;
|
|
case BlendingMode::TRANSPARENT:
|
|
CodeGenerator::generateDefine(fs, "POST_LIGHTING_BLEND_MODE_TRANSPARENT", true);
|
|
break;
|
|
case BlendingMode::ADD:
|
|
CodeGenerator::generateDefine(fs, "POST_LIGHTING_BLEND_MODE_ADD", true);
|
|
break;
|
|
case BlendingMode::MULTIPLY:
|
|
CodeGenerator::generateDefine(fs, "POST_LIGHTING_BLEND_MODE_MULTIPLY", true);
|
|
break;
|
|
case BlendingMode::SCREEN:
|
|
CodeGenerator::generateDefine(fs, "POST_LIGHTING_BLEND_MODE_SCREEN", true);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
CodeGenerator::generateDefine(fs, getShadingDefine(material.shading), true);
|
|
generateMaterialDefines(fs, mProperties, mDefines);
|
|
|
|
CodeGenerator::generateDefine(fs, "MATERIAL_HAS_CUSTOM_SURFACE_SHADING", material.hasCustomSurfaceShading);
|
|
|
|
CodeGenerator::generateShaderInputs(fs, ShaderType::FRAGMENT, material.requiredAttributes, interpolation);
|
|
|
|
// custom material variables
|
|
size_t variableIndex = 0;
|
|
for (const auto& variable : mVariables) {
|
|
CodeGenerator::generateVariable(fs, ShaderType::FRAGMENT, variable, variableIndex++);
|
|
}
|
|
|
|
// uniforms and samplers
|
|
cg.generateUniforms(fs, ShaderType::FRAGMENT,
|
|
BindingPoints::PER_VIEW, UibGenerator::getPerViewUib());
|
|
cg.generateUniforms(fs, ShaderType::FRAGMENT,
|
|
BindingPoints::PER_RENDERABLE, UibGenerator::getPerRenderableUib());
|
|
cg.generateUniforms(fs, ShaderType::FRAGMENT,
|
|
BindingPoints::LIGHTS, UibGenerator::getLightsUib());
|
|
if (litVariants && filament::Variant::isShadowReceiverVariant(variant)) {
|
|
cg.generateUniforms(fs, ShaderType::FRAGMENT,
|
|
BindingPoints::SHADOW, UibGenerator::getShadowUib());
|
|
}
|
|
cg.generateUniforms(fs, ShaderType::FRAGMENT,
|
|
BindingPoints::FROXEL_RECORDS, UibGenerator::getFroxelRecordUib());
|
|
cg.generateUniforms(fs, ShaderType::FRAGMENT,
|
|
BindingPoints::PER_MATERIAL_INSTANCE, material.uib);
|
|
CodeGenerator::generateSeparator(fs);
|
|
cg.generateSamplers(fs,
|
|
material.samplerBindings.getBlockOffset(BindingPoints::PER_VIEW),
|
|
SibGenerator::getPerViewSib(variant));
|
|
cg.generateSamplers(fs,
|
|
material.samplerBindings.getBlockOffset(BindingPoints::PER_MATERIAL_INSTANCE),
|
|
material.sib);
|
|
|
|
fs << "float filament_lodBias;\n";
|
|
|
|
// shading code
|
|
CodeGenerator::generateCommon(fs, ShaderType::FRAGMENT);
|
|
CodeGenerator::generateGetters(fs, ShaderType::FRAGMENT);
|
|
CodeGenerator::generateCommonMaterial(fs, ShaderType::FRAGMENT);
|
|
CodeGenerator::generateParameters(fs, ShaderType::FRAGMENT);
|
|
CodeGenerator::generateFog(fs, ShaderType::FRAGMENT);
|
|
|
|
// shading model
|
|
if (filament::Variant::isValidDepthVariant(variant)) {
|
|
// In MASKED mode or with transparent shadows, we need the alpha channel computed by
|
|
// the material (user code), so we append it here.
|
|
if (material.blendingMode == BlendingMode::MASKED || material.hasTransparentShadow) {
|
|
appendShader(fs, mMaterialFragmentCode, mMaterialLineOffset);
|
|
}
|
|
// these variants are special and are treated as DEPTH variants. Filament will never
|
|
// request that variant for the color pass.
|
|
CodeGenerator::generateDepthShaderMain(fs, ShaderType::FRAGMENT);
|
|
} else {
|
|
appendShader(fs, mMaterialFragmentCode, mMaterialLineOffset);
|
|
if (filament::Variant::isSSRVariant(variant)) {
|
|
CodeGenerator::generateShaderReflections(fs, ShaderType::FRAGMENT, variant);
|
|
} else if (material.isLit) {
|
|
CodeGenerator::generateShaderLit(fs, ShaderType::FRAGMENT, variant,
|
|
material.shading,material.hasCustomSurfaceShading);
|
|
} else {
|
|
CodeGenerator::generateShaderUnlit(fs, ShaderType::FRAGMENT, variant,
|
|
material.hasShadowMultiplier);
|
|
}
|
|
// entry point
|
|
CodeGenerator::generateShaderMain(fs, ShaderType::FRAGMENT);
|
|
}
|
|
|
|
CodeGenerator::generateEpilog(fs);
|
|
|
|
return fs.c_str();
|
|
}
|
|
|
|
void ShaderGenerator::fixupExternalSamplers(ShaderModel sm,
|
|
std::string& shader, MaterialInfo const& material) noexcept {
|
|
// External samplers are only supported on GL ES at the moment, we must
|
|
// skip the fixup on desktop targets
|
|
if (material.hasExternalSamplers && sm == ShaderModel::MOBILE) {
|
|
CodeGenerator::fixupExternalSamplers(shader, material.sib);
|
|
}
|
|
}
|
|
|
|
std::string ShaderGenerator::createPostProcessVertexProgram(
|
|
ShaderModel sm, MaterialBuilder::TargetApi targetApi,
|
|
MaterialBuilder::TargetLanguage targetLanguage, MaterialInfo const& material,
|
|
const filament::Variant::type_t variantKey, SamplerBindingMap const& samplerBindingMap) const noexcept {
|
|
const CodeGenerator cg(sm, targetApi, targetLanguage);
|
|
io::sstream vs;
|
|
cg.generateProlog(vs, ShaderType::VERTEX, false);
|
|
|
|
cg.generateQualityDefine(vs, material.quality);
|
|
|
|
CodeGenerator::generateDefine(vs, "LOCATION_POSITION", uint32_t(VertexAttribute::POSITION));
|
|
|
|
// custom material variables
|
|
size_t variableIndex = 0;
|
|
for (const auto& variable : mVariables) {
|
|
CodeGenerator::generateVariable(vs, ShaderType::VERTEX, variable, variableIndex++);
|
|
}
|
|
|
|
CodeGenerator::generatePostProcessInputs(vs, ShaderType::VERTEX);
|
|
generatePostProcessMaterialVariantDefines(vs, PostProcessVariant(variantKey));
|
|
|
|
cg.generateUniforms(vs, ShaderType::VERTEX,
|
|
BindingPoints::PER_VIEW, UibGenerator::getPerViewUib());
|
|
cg.generateUniforms(vs, ShaderType::VERTEX,
|
|
BindingPoints::PER_MATERIAL_INSTANCE, material.uib);
|
|
|
|
cg.generateSamplers(vs,
|
|
material.samplerBindings.getBlockOffset(BindingPoints::PER_MATERIAL_INSTANCE),
|
|
material.sib);
|
|
|
|
CodeGenerator::generatePostProcessCommon(vs, ShaderType::VERTEX);
|
|
CodeGenerator::generatePostProcessGetters(vs, ShaderType::VERTEX);
|
|
|
|
appendShader(vs, mMaterialVertexCode, mMaterialVertexLineOffset);
|
|
|
|
CodeGenerator::generatePostProcessMain(vs, ShaderType::VERTEX);
|
|
|
|
CodeGenerator::generateEpilog(vs);
|
|
return vs.c_str();
|
|
}
|
|
|
|
std::string ShaderGenerator::createPostProcessFragmentProgram(
|
|
ShaderModel sm, MaterialBuilder::TargetApi targetApi,
|
|
MaterialBuilder::TargetLanguage targetLanguage, MaterialInfo const& material,
|
|
uint8_t variant, const SamplerBindingMap& samplerBindingMap) const noexcept {
|
|
const CodeGenerator cg(sm, targetApi, targetLanguage);
|
|
io::sstream fs;
|
|
cg.generateProlog(fs, ShaderType::FRAGMENT, false);
|
|
|
|
cg.generateQualityDefine(fs, material.quality);
|
|
|
|
generatePostProcessMaterialVariantDefines(fs, PostProcessVariant(variant));
|
|
|
|
// custom material variables
|
|
size_t variableIndex = 0;
|
|
for (const auto& variable : mVariables) {
|
|
CodeGenerator::generateVariable(fs, ShaderType::FRAGMENT, variable, variableIndex++);
|
|
}
|
|
|
|
cg.generateUniforms(fs, ShaderType::FRAGMENT,
|
|
BindingPoints::PER_VIEW, UibGenerator::getPerViewUib());
|
|
cg.generateUniforms(fs, ShaderType::FRAGMENT,
|
|
BindingPoints::PER_MATERIAL_INSTANCE, material.uib);
|
|
|
|
cg.generateSamplers(fs,
|
|
material.samplerBindings.getBlockOffset(BindingPoints::PER_MATERIAL_INSTANCE),
|
|
material.sib);
|
|
|
|
// subpass
|
|
CodeGenerator::generateSubpass(fs, material.subpass);
|
|
|
|
CodeGenerator::generatePostProcessCommon(fs, ShaderType::FRAGMENT);
|
|
CodeGenerator::generatePostProcessGetters(fs, ShaderType::FRAGMENT);
|
|
|
|
// Generate post-process outputs.
|
|
for (const auto& output : mOutputs) {
|
|
if (output.target == MaterialBuilder::OutputTarget::COLOR) {
|
|
cg.generateOutput(fs, ShaderType::FRAGMENT, output.name, output.location,
|
|
output.qualifier, output.type);
|
|
}
|
|
if (output.target == MaterialBuilder::OutputTarget::DEPTH) {
|
|
CodeGenerator::generateDefine(fs, "FRAG_OUTPUT_DEPTH", 1u);
|
|
}
|
|
}
|
|
|
|
CodeGenerator::generatePostProcessInputs(fs, ShaderType::FRAGMENT);
|
|
|
|
appendShader(fs, mMaterialFragmentCode, mMaterialLineOffset);
|
|
|
|
CodeGenerator::generatePostProcessMain(fs, ShaderType::FRAGMENT);
|
|
CodeGenerator::generateEpilog(fs);
|
|
return fs.c_str();
|
|
}
|
|
|
|
|
|
} // namespace filament
|