mirror of
https://github.com/johndoe6345789/SDL3CPlusPlus.git
synced 2026-04-27 15:14:58 +00:00
361 lines
10 KiB
C++
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 █
|
|
}
|
|
|
|
/// 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
|