Files
UnrealEngine/Engine/Source/ThirdParty/MaterialX/MaterialX-1.39.3/source/MaterialXGenMsl/Nodes/SurfaceNodeMsl.cpp
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

273 lines
12 KiB
C++

//
// Copyright Contributors to the MaterialX Project
// SPDX-License-Identifier: Apache-2.0
//
#include <MaterialXGenMsl/Nodes/SurfaceNodeMsl.h>
#include <MaterialXGenMsl/MslShaderGenerator.h>
#include <MaterialXGenShader/Shader.h>
#include <MaterialXGenShader/GenContext.h>
MATERIALX_NAMESPACE_BEGIN
SurfaceNodeMsl::SurfaceNodeMsl()
{}
ShaderNodeImplPtr SurfaceNodeMsl::create()
{
return std::make_shared<SurfaceNodeMsl>();
}
void SurfaceNodeMsl::createVariables(const ShaderNode&, GenContext& context, Shader& shader) const
{
// TODO:
// The surface shader needs position, normal, view position and light sources. We should solve this by adding some
// dependency mechanism so this implementation can be set to depend on the HwPositionNode, HwNormalNode
// HwViewDirectionNode and LightNodeMsl nodes instead? This is where the MaterialX attribute "internalgeomprops"
// is needed.
//
ShaderStage& vs = shader.getStage(Stage::VERTEX);
ShaderStage& ps = shader.getStage(Stage::PIXEL);
addStageInput(HW::VERTEX_INPUTS, Type::VECTOR3, HW::T_IN_POSITION, vs);
addStageInput(HW::VERTEX_INPUTS, Type::VECTOR3, HW::T_IN_NORMAL, vs);
addStageUniform(HW::PRIVATE_UNIFORMS, Type::MATRIX44, HW::T_WORLD_INVERSE_TRANSPOSE_MATRIX, vs);
addStageConnector(HW::VERTEX_DATA, Type::VECTOR3, HW::T_POSITION_WORLD, vs, ps);
addStageConnector(HW::VERTEX_DATA, Type::VECTOR3, HW::T_NORMAL_WORLD, vs, ps);
addStageUniform(HW::PRIVATE_UNIFORMS, Type::VECTOR3, HW::T_VIEW_POSITION, ps);
const MslShaderGenerator& shadergen = static_cast<const MslShaderGenerator&>(context.getShaderGenerator());
shadergen.addStageLightingUniforms(context, ps);
}
void SurfaceNodeMsl::emitFunctionCall(const ShaderNode& node, GenContext& context, ShaderStage& stage) const
{
const MslShaderGenerator& shadergen = static_cast<const MslShaderGenerator&>(context.getShaderGenerator());
DEFINE_SHADER_STAGE(stage, Stage::VERTEX)
{
VariableBlock& vertexData = stage.getOutputBlock(HW::VERTEX_DATA);
const string prefix = shadergen.getVertexDataPrefix(vertexData);
ShaderPort* position = vertexData[HW::T_POSITION_WORLD];
if (!position->isEmitted())
{
position->setEmitted();
shadergen.emitLine(prefix + position->getVariable() + " = hPositionWorld.xyz", stage);
}
ShaderPort* normal = vertexData[HW::T_NORMAL_WORLD];
if (!normal->isEmitted())
{
normal->setEmitted();
shadergen.emitLine(prefix + normal->getVariable() + " = normalize((" + HW::T_WORLD_INVERSE_TRANSPOSE_MATRIX + " * float4(" + HW::T_IN_NORMAL + ", 0)).xyz)", stage);
}
if (context.getOptions().hwAmbientOcclusion)
{
ShaderPort* texcoord = vertexData[HW::T_TEXCOORD + "_0"];
if (!texcoord->isEmitted())
{
texcoord->setEmitted();
shadergen.emitLine(prefix + texcoord->getVariable() + " = " + HW::T_IN_TEXCOORD + "_0", stage);
}
}
}
DEFINE_SHADER_STAGE(stage, Stage::PIXEL)
{
VariableBlock& vertexData = stage.getInputBlock(HW::VERTEX_DATA);
const string prefix = shadergen.getVertexDataPrefix(vertexData);
// Declare the output variable
const ShaderOutput* output = node.getOutput();
shadergen.emitLineBegin(stage);
shadergen.emitOutput(output, true, true, context, stage);
shadergen.emitLineEnd(stage);
shadergen.emitScopeBegin(stage);
shadergen.emitLine("float3 N = normalize(" + prefix + HW::T_NORMAL_WORLD + ")", stage);
shadergen.emitLine("float3 V = normalize(" + HW::T_VIEW_POSITION + " - " + prefix + HW::T_POSITION_WORLD + ")", stage);
shadergen.emitLine("float3 P = " + prefix + HW::T_POSITION_WORLD, stage);
shadergen.emitLine("float3 L = float3(0,0,0);", stage);
shadergen.emitLine("float occlusion = 1.0", stage);
shadergen.emitLineBreak(stage);
const string outColor = output->getVariable() + ".color";
const string outTransparency = output->getVariable() + ".transparency";
const ShaderInput* bsdfInput = node.getInput("bsdf");
if (const ShaderNode* bsdf = bsdfInput->getConnectedSibling())
{
shadergen.emitLineBegin(stage);
shadergen.emitString("float surfaceOpacity = ", stage);
shadergen.emitInput(node.getInput("opacity"), context, stage);
shadergen.emitLineEnd(stage);
shadergen.emitLineBreak(stage);
//
// Handle direct lighting
//
shadergen.emitComment("Shadow occlusion", stage);
if (context.getOptions().hwShadowMap)
{
shadergen.emitLine("float3 shadowCoord = (" + HW::T_SHADOW_MATRIX + " * float4(" + prefix + HW::T_POSITION_WORLD + ", 1.0)).xyz", stage);
shadergen.emitLine("shadowCoord.xy = shadowCoord.xy * 0.5 + 0.5", stage);
shadergen.emitLine("float2 shadowMoments = texture(" + HW::T_SHADOW_MAP + ", shadowCoord.xy).xy", stage);
shadergen.emitLine("occlusion = mx_variance_shadow_occlusion(shadowMoments, shadowCoord.z)", stage);
}
shadergen.emitLineBreak(stage);
emitLightLoop(node, context, stage, outColor);
//
// Handle indirect lighting.
//
shadergen.emitComment("Ambient occlusion", stage);
if (context.getOptions().hwAmbientOcclusion)
{
ShaderPort* texcoord = vertexData[HW::T_TEXCOORD + "_0"];
shadergen.emitLine("float2 ambOccUv = mx_transform_uv(" + prefix + texcoord->getVariable() + ", float2(1.0), float2(0.0))", stage);
shadergen.emitLine("occlusion = mix(1.0, texture(" + HW::T_AMB_OCC_MAP + ", ambOccUv).x, " + HW::T_AMB_OCC_GAIN + ")", stage);
}
else
{
shadergen.emitLine("occlusion = 1.0", stage);
}
shadergen.emitLineBreak(stage);
shadergen.emitComment("Add environment contribution", stage);
shadergen.emitScopeBegin(stage);
if (bsdf->hasClassification(ShaderNode::Classification::BSDF_R)) {
shadergen.emitLine("ClosureData closureData = {CLOSURE_TYPE_INDIRECT, L, V, N, P, occlusion}", stage);
shadergen.emitFunctionCall(*bsdf, context, stage);
}
else
{
shadergen.emitLineBegin(stage);
shadergen.emitOutput(bsdf->getOutput(), true, true, context, stage);
shadergen.emitLineEnd(stage);
}
shadergen.emitLineBreak(stage);
shadergen.emitLine(outColor + " += occlusion * " + bsdf->getOutput()->getVariable() + ".response", stage);
shadergen.emitScopeEnd(stage);
shadergen.emitLineBreak(stage);
}
//
// Handle surface emission.
//
const ShaderInput* edfInput = node.getInput("edf");
if (const ShaderNode* edf = edfInput->getConnectedSibling())
{
shadergen.emitComment("Add surface emission", stage);
shadergen.emitScopeBegin(stage);
if (edf->hasClassification(ShaderNode::Classification::EDF)) {
shadergen.emitLine("ClosureData closureData = {CLOSURE_TYPE_EMISSION, L, V, N, P, occlusion}", stage);
shadergen.emitFunctionCall(*edf, context, stage);
}
else
{
shadergen.emitLineBegin(stage);
shadergen.emitOutput(edf->getOutput(), true, true, context, stage);
shadergen.emitLineEnd(stage);
}
shadergen.emitLine(outColor + " += " + edf->getOutput()->getVariable(), stage);
shadergen.emitScopeEnd(stage);
shadergen.emitLineBreak(stage);
}
//
// Handle surface transmission and opacity.
//
if (const ShaderNode* bsdf = bsdfInput->getConnectedSibling())
{
shadergen.emitComment("Calculate the BSDF transmission for viewing direction", stage);
if (bsdf->hasClassification(ShaderNode::Classification::BSDF_T)) {
shadergen.emitLine("ClosureData closureData = {CLOSURE_TYPE_TRANSMISSION, L, V, N, P, occlusion}", stage);
shadergen.emitFunctionCall(*bsdf, context, stage);
}
else
{
shadergen.emitLineBegin(stage);
shadergen.emitOutput(bsdf->getOutput(), true, true, context, stage);
shadergen.emitLineEnd(stage);
}
if (context.getOptions().hwTransmissionRenderMethod == TRANSMISSION_REFRACTION)
{
shadergen.emitLine(outColor + " += " + bsdf->getOutput()->getVariable() + ".response", stage);
}
else
{
shadergen.emitLine(outTransparency + " += " + bsdf->getOutput()->getVariable() + ".response", stage);
}
shadergen.emitLineBreak(stage);
shadergen.emitComment("Compute and apply surface opacity", stage);
shadergen.emitScopeBegin(stage);
shadergen.emitLine(outColor + " *= surfaceOpacity", stage);
shadergen.emitLine(outTransparency + " = mix(float3(1.0), " + outTransparency + ", surfaceOpacity)", stage);
shadergen.emitScopeEnd(stage);
}
shadergen.emitScopeEnd(stage);
shadergen.emitLineBreak(stage);
}
}
void SurfaceNodeMsl::emitLightLoop(const ShaderNode& node, GenContext& context, ShaderStage& stage, const string& outColor) const
{
//
// Generate Light loop if requested
//
if (context.getOptions().hwMaxActiveLightSources > 0)
{
const MslShaderGenerator& shadergen = static_cast<const MslShaderGenerator&>(context.getShaderGenerator());
const VariableBlock& vertexData = stage.getInputBlock(HW::VERTEX_DATA);
const string prefix = shadergen.getVertexDataPrefix(vertexData);
const ShaderInput* bsdfInput = node.getInput("bsdf");
const ShaderNode* bsdf = bsdfInput->getConnectedSibling();
shadergen.emitComment("Light loop", stage);
shadergen.emitLine("int numLights = numActiveLightSources()", stage);
shadergen.emitLine("lightshader lightShader", stage);
shadergen.emitLine("for (int activeLightIndex = 0; activeLightIndex < numLights; ++activeLightIndex)", stage, false);
shadergen.emitScopeBegin(stage);
shadergen.emitLine("sampleLightSource(" + HW::T_LIGHT_DATA_INSTANCE + "[activeLightIndex], " + prefix + HW::T_POSITION_WORLD + ", lightShader)", stage);
shadergen.emitLine("L = lightShader.direction", stage);
shadergen.emitLineBreak(stage);
shadergen.emitComment("Calculate the BSDF response for this light source", stage);
if (bsdf->hasClassification(ShaderNode::Classification::BSDF_R)) {
shadergen.emitLine("ClosureData closureData = {CLOSURE_TYPE_REFLECTION, L, V, N, P, occlusion}", stage);
shadergen.emitFunctionCall(*bsdf, context, stage);
}
else
{
shadergen.emitLineBegin(stage);
shadergen.emitOutput(bsdf->getOutput(), true, true, context, stage);
shadergen.emitLineEnd(stage);
}
shadergen.emitLineBreak(stage);
shadergen.emitComment("Accumulate the light's contribution", stage);
shadergen.emitLine(outColor + " += lightShader.intensity * " + bsdf->getOutput()->getVariable() + ".response", stage);
shadergen.emitScopeEnd(stage);
shadergen.emitLineBreak(stage);
}
}
MATERIALX_NAMESPACE_END