Files
SDL3CPlusPlus/MaterialX/source/MaterialXRenderSlang/SlangMaterial.cpp
2026-01-06 13:25:49 +00:00

361 lines
10 KiB
C++

//
// Copyright Contributors to the MaterialX Project
// SPDX-License-Identifier: Apache-2.0
//
#include <MaterialXGenSlang/SlangShaderGenerator.h>
#include <MaterialXRenderSlang/SlangMaterial.h>
#include <MaterialXRenderSlang/SlangProgram.h>
#include <MaterialXRender/Util.h>
#include <MaterialXFormat/Util.h>
#include <iostream>
MATERIALX_NAMESPACE_BEGIN
#define MX_UNIMPLEMENTED throw ExceptionRenderError("Unimplemented");
SlangMaterialPtr SlangMaterial::create(SlangContextPtr context)
{
return std::make_shared<SlangMaterial>(std::move(context));
}
SlangMaterial::SlangMaterial(SlangContextPtr context) :
_context(std::move(context)) { }
SlangMaterial::~SlangMaterial() = default;
/// Load shader source from file.
bool SlangMaterial::loadSource(const FilePath& vertexShaderFile,
const FilePath& pixelShaderFile,
bool hasTransparency)
{
_hasTransparency = hasTransparency;
std::string vertexShader = readFile(vertexShaderFile);
if (vertexShader.empty())
{
return false;
}
std::string pixelShader = readFile(pixelShaderFile);
if (pixelShader.empty())
{
return false;
}
// TODO:
// Here we set new source code on the _program without rebuilding
// the _hwShader instance. So the _hwShader is not in sync with the
// _program after this operation.
_glProgram = SlangProgram::create(_context);
_glProgram->addStage(Stage::VERTEX, vertexShader);
_glProgram->addStage(Stage::PIXEL, pixelShader);
_glProgram->build();
return true;
}
void SlangMaterial::clearShader()
{
_hwShader = {};
_glProgram = {};
}
/// Generate a shader from our currently stored element and
/// the given generator context.
bool SlangMaterial::generateShader(GenContext& context)
{
if (!_elem)
{
return false;
}
_hasTransparency = isTransparentSurface(_elem, context.getShaderGenerator().getTarget());
GenContext materialContext = context;
materialContext.getOptions().hwTransparency = _hasTransparency;
// Initialize in case creation fails and throws an exception
clearShader();
_hwShader = createShader("Shader", materialContext, _elem);
if (!_hwShader)
{
return false;
}
_glProgram = SlangProgram::create(_context);
_glProgram->setStages(_hwShader);
_glProgram->build();
return true;
}
/// Copies shader and API specific generated program from ShaderMaterial to this one.
void SlangMaterial::copyShader(MaterialPtr material)
{
_hwShader = std::static_pointer_cast<SlangMaterial>(material)->_hwShader;
_glProgram = std::static_pointer_cast<SlangMaterial>(material)->_glProgram;
}
/// Generate a shader from the given hardware shader.
bool SlangMaterial::generateShader(ShaderPtr hwShader)
{
_hwShader = hwShader;
_glProgram = SlangProgram::create(_context);
_glProgram->setStages(hwShader);
_glProgram->build();
return true;
}
/// Bind viewing information for this ShaderMaterial.
void SlangMaterial::bindViewInformation(CameraPtr camera)
{
if (!_glProgram)
return;
_glProgram->bindViewInformation(std::move(camera));
}
/// Bind all images for this ShaderMaterial.
void SlangMaterial::bindImages(ImageHandlerPtr imageHandler,
const FileSearchPath& searchPath,
bool)
{
if (!_glProgram)
return;
_boundImages.clear();
FileSearchPath oldSearchPath = imageHandler->getSearchPath();
imageHandler->setSearchPath(searchPath);
_glProgram->bindTextures(imageHandler);
imageHandler->setSearchPath(oldSearchPath);
}
void SlangMaterial::unbindImages(ImageHandlerPtr imageHandler)
{
/// TODO: As we do not have a concept of unbinding images,
/// an image is bound until another image is bound to the same place in the descriptor.
for (ImagePtr image : _boundImages)
{
imageHandler->unbindImage(image);
}
}
ImagePtr SlangMaterial::bindImage(const FilePath& filePath,
const std::string& uniformName,
ImageHandlerPtr imageHandler,
const ImageSamplingProperties& samplingProperties)
{
if (!_glProgram)
return {};
// Create a filename resolver for geometric properties.
StringResolverPtr resolver = StringResolver::create();
if (!getUdim().empty())
{
resolver->setUdimString(getUdim());
}
imageHandler->setFilenameResolver(resolver);
// Acquire the given image.
ImagePtr image = imageHandler->acquireImage(filePath, samplingProperties.defaultColor);
if (!image)
return nullptr;
_glProgram->bindTexture(imageHandler, uniformName, image, samplingProperties);
return image;
}
/// Bind lights to shader.
void SlangMaterial::bindLighting(LightHandlerPtr lightHandler,
ImageHandlerPtr imageHandler,
const ShadowState& shadowState)
{
if (!_glProgram)
return;
_glProgram->bindLighting(lightHandler, imageHandler);
// Bind shadow map properties
if (shadowState.shadowMap && _glProgram->hasUniform(HW::SHADOW_MAP))
{
ImageSamplingProperties samplingProperties;
samplingProperties.uaddressMode = ImageSamplingProperties::AddressMode::CLAMP;
samplingProperties.vaddressMode = ImageSamplingProperties::AddressMode::CLAMP;
samplingProperties.filterType = ImageSamplingProperties::FilterType::LINEAR;
// Bind the shadow map.
_glProgram->bindTexture(imageHandler, HW::SHADOW_MAP, shadowState.shadowMap, samplingProperties);
_glProgram->bindUniform(HW::SHADOW_MATRIX, Value::createValue(shadowState.shadowMatrix));
}
// Bind ambient occlusion properties.
if (shadowState.ambientOcclusionMap && _glProgram->hasUniform(HW::AMB_OCC_MAP))
{
ImageSamplingProperties samplingProperties;
samplingProperties.uaddressMode = ImageSamplingProperties::AddressMode::PERIODIC;
samplingProperties.vaddressMode = ImageSamplingProperties::AddressMode::PERIODIC;
samplingProperties.filterType = ImageSamplingProperties::FilterType::LINEAR;
// Bind the ambient occlusion map.
_glProgram->bindTexture(imageHandler, HW::AMB_OCC_MAP,
shadowState.ambientOcclusionMap,
samplingProperties);
_glProgram->bindUniform(HW::AMB_OCC_GAIN, Value::createValue(shadowState.ambientOcclusionGain));
}
}
void SlangMaterial::bindMesh(MeshPtr mesh)
{
if (!mesh || !_glProgram)
{
return;
}
if (!_renderState || !_passEncoder)
{
throw ExceptionRenderError("Binding mesh can only be done with set renderState and encoder");
}
_glProgram->bind();
if (_boundMesh && mesh != _boundMesh)
{
_glProgram->unbindGeometry();
}
_glProgram->bindMesh(mesh, *_renderState);
_boundMesh = mesh;
}
bool SlangMaterial::bindPartition(MeshPartitionPtr part) const
{
if (!_glProgram)
{
return false;
}
_glProgram->bind();
_renderState->indexBuffer = _glProgram->bindPartition(part);
return true;
}
void SlangMaterial::drawPartition(MeshPartitionPtr part) const
{
if (!part)
return;
if (!bindPartition(part))
return;
rhi::DrawArguments drawArgs = {};
drawArgs.vertexCount = (uint32_t) part->getIndices().size();
_passEncoder->setRenderState(*_renderState);
_passEncoder->drawIndexed(drawArgs);
}
void SlangMaterial::unbindGeometry()
{
if (_glProgram)
{
_glProgram->unbindGeometry();
}
_boundMesh = nullptr;
}
/// Return the block of public uniforms for this ShaderMaterial.
VariableBlock* SlangMaterial::getPublicUniforms() const
{
if (!_hwShader)
return {};
ShaderStage& stage = _hwShader->getStage(Stage::PIXEL);
VariableBlock& block = stage.getUniformBlock(HW::PUBLIC_UNIFORMS);
return &block;
}
/// Find a public uniform from its MaterialX path.
ShaderPort* SlangMaterial::findUniform(const std::string& path) const
{
ShaderPort* port = nullptr;
VariableBlock* publicUniforms = getPublicUniforms();
if (publicUniforms)
{
// Scan block based on path match predicate
port = publicUniforms->find([path](ShaderPort* port)
{
return (port && stringEndsWith(port->getPath(), path));
});
if (!port)
{
port = publicUniforms->find([path](ShaderPort* port)
{
return (port && stringEndsWith(path, port->getName()));
});
}
// Check if the uniform exists in the shader program
if (port && !_glProgram->getUniformsList().count(port->getVariable()))
{
port = nullptr;
}
}
return port;
}
/// Modify the value of the uniform with the given path.
void SlangMaterial::modifyUniform(const std::string& path,
ConstValuePtr value,
std::string valueString)
{
ShaderPort* uniform = findUniform(path);
if (!uniform)
{
return;
}
_glProgram->bindUniform(uniform->getVariable(), value);
if (valueString.empty())
{
valueString = value->getValueString();
}
uniform->setValue(uniform->getType().createValueFromStrings(valueString));
if (_doc)
{
ElementPtr element = _doc->getDescendant(uniform->getPath());
if (element)
{
ValueElementPtr valueElement = element->asA<ValueElement>();
if (valueElement)
{
valueElement->setValueString(valueString);
}
}
}
}
void SlangMaterial::prepareUsedResources(CameraPtr cam,
GeometryHandlerPtr geometryHandler,
ImageHandlerPtr imageHandler,
LightHandlerPtr lightHandler)
{
if (!_glProgram)
return;
_glProgram->bindViewInformation(cam);
_glProgram->bindTimeAndFrame();
_glProgram->bindLighting(lightHandler, imageHandler);
_glProgram->bindTextures(imageHandler);
}
MATERIALX_NAMESPACE_END