Skip to content

Feature/mat system - #33

Open
MrChampz wants to merge 90 commits into
mainfrom
feature/mat-system
Open

MrChampz wants to merge 90 commits into
mainfrom
feature/mat-system

Conversation

@MrChampz

Copy link
Copy Markdown
Owner

No description provided.

MrChampz added 30 commits July 25, 2026 16:34
Add material, material instance, and node-graph primitives with HLSL
generation for scalar and vector channels. Cover graph generation with unit
tests, tighten clang-tidy, and ignore generated shaders plus imported mesh/HDR
assets from source control.
Add a graph-injectable pixel shader with image-based lighting, directional
lighting, and ACES tone mapping. Generated graph code fills the surface
channels while shared frame, material, texture, and environment bindings stay
centralized.
Compile generated material-graph HLSL into SPIR-V at runtime and load it as a
bindable shader. Add the material compiler, wire a sample graph into Dissolve,
copy the template during shader staging, and fix the template's vector and
float4 output expressions.
Rename WaitForAllFrames to ResetFrameUsageState to reflect that it only clears
per-frame usage flags after the device is idle. Expose WaitDeviceIdle through
GraphicsContext so generic graphics clients can synchronize safely.
Add texture coordinates, texture sampling, time, panner, math, and utility
nodes. Coerce mixed pin widths while generating HLSL, and expose Time in the
material frame buffer so animated graphs can evaluate correctly.
Make materials own their graphs and typed parameter definitions. Reject invalid graph references and incompatible instance overrides before shader compilation or resource binding.
Compile authored material schemas into stable value and texture slots. Keep generated HLSL and toolchain diagnostics with the compiled material artifact.
Compile Dissolve's graph through Material so the demo retains the compiled material artifact and reports compiler diagnostics.
Compile the particle sprite material permutation and publish immutable proxy data into a per-frame GPU material table.

Batch sprite draws by compiled material shader while preserving the static sprite fallback and avoiding descriptor writes during draw submission.
Resolve immutable material texture slots through the bindless set before command recording.

New texture registrations use the white fallback until the next frame publishes their descriptors, avoiding descriptor mutations during particle draws.
Apply the Dissolve graph material through an immutable render proxy before compiling the particle system.

Unify the sprite push-constant contract at 72 bytes across the vertex shader and both pixel shader paths.
Compile the Surface permutation before any enabled particle permutation.\n\nReturn no shader for unsupported particle usages instead of falling back to Surface.
Generate Ribbon vertex and fragment shader permutations for enabled materials.\n\nKeep the material push-constant ABI aligned across both stages.
Publish Ribbon material proxies to the GPU material table and bind their generated shader pipelines.\n\nKeep Ribbon vertex resources isolated from fragment material bindings.
Keep Ribbon material resources in the descriptor sets expected by the bindless pipeline layout.\n\nAdd a Dissolve Ribbon material example for runtime coverage.
Compile Mesh shader permutations and publish their material proxies to the GPU material table.\n\nBind Mesh material pipelines with the same texture and push-constant contract as Sprite and Ribbon.
Move particle material frame data, texture registration and GPU storage into Engine/Material.

Keep Aether as a consumer of the centralized frame snapshot and cover bindless descriptor visibility.
Move particle material pipeline selection, resource binding and pipeline caching into Engine/Material.

Keep Aether responsible for particle buffers and draw submission while it consumes prepared material passes.
Move prepared material pass binding and draw dispatch into Engine/Material.

Keep Aether responsible for particle geometry while it supplies draw callbacks for Sprite, Ribbon and Mesh.
Compile and own default particle material proxies in Engine/Material.

Keep Aether responsible for particle geometry and submit every Sprite, Ribbon and Mesh batch through a prepared material pass.
Move default particle material proxy ownership from MaterialSystem to ParticleMaterialLibrary.

Bind an explicit proxy while loading effects so Aether renders only compiled emitter materials.
Introduce MaterialRenderScene as the frame-boundary contract for particle material inputs.

MaterialSystem now builds frame snapshots from the published scene while Aether retains geometry submission for the next phase.
Move particle material batching, pass preparation and draw recording into MaterialSystem.

Aether now publishes immutable geometry data and no longer manipulates material shaders or pipelines.
Move legacy sprite texture selection into the default Sprite material graph.

Expose BaseColor and Opacity as material channels and remove Aether texture bindings from the render contract.
Resolve effect material colors and emission into immutable graph constants during import.

Move particle material compilation and caching into Engine/Material and migrate VFX assets to explicit material definitions.
Move MaterialSystem ownership to Application and inject it into Aether.

Keep the frame material capacity in central configuration instead of particle pool limits.
Register default source materials and cache compiled results by material revision.

Add the radial gradient exponential node for the default sprite opacity.
Move instance creation to System and let Manager add each instance once for persistent simulation and rendering. Compile on first registration, preserve immutable frame snapshots, and update lifecycle and concurrency coverage.
Use the instance map as the registry's single source of truth and remove the redundant registration-order list. Keep frame ordering unspecified so later culling and rendering stages can establish task-specific order.
@coderabbitai

coderabbitai Bot commented Aug 27, 2026

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 165 files, which is 65 over the limit of 100.

To get a review, reduce the PR to 100 files or fewer by splitting it into smaller PRs or changing its base branch.

Upgrade to a paid plan to raise the limit.

This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Team

Run ID: 00d51c59-ad06-4b11-b357-97c3620cd56b

📥 Commits

Reviewing files that changed from the base of the PR and between 35c6f38 and 55d968d.

⛔ Files ignored due to path filters (10)
  • Shaders/Aether/Mesh.vs.hlsl is excluded by !**/*.hlsl
  • Shaders/Aether/Ribbon.vs.hlsl is excluded by !**/*.hlsl
  • Shaders/Aether/Sprite.ps.hlsl is excluded by !**/*.hlsl
  • Shaders/Aether/Sprite.vs.hlsl is excluded by !**/*.hlsl
  • Shaders/Material/Material.ps.hlsl is excluded by !**/*.hlsl
  • Shaders/Material/ParticleMesh.ps.hlsl is excluded by !**/*.hlsl
  • Shaders/Material/ParticleMesh.vs.hlsl is excluded by !**/*.hlsl
  • Shaders/Material/ParticleRibbon.ps.hlsl is excluded by !**/*.hlsl
  • Shaders/Material/ParticleRibbon.vs.hlsl is excluded by !**/*.hlsl
  • Shaders/Material/ParticleSprite.ps.hlsl is excluded by !**/*.hlsl
📒 Files selected for processing (165)
  • AGENTS.md
  • Assets/.gitignore
  • Assets/VFX/FireAndFireworks.json
  • Assets/VFX/RainStorm.json
  • Assets/VFX/RibbonGarden.json
  • Assets/VFX/RibbonVortex.json
  • Dissolve/Source/Dissolve.cpp
  • Dissolve/Source/Dissolve.h
  • Documentation/Plans/MaterialSystem.md
  • Documentation/Plans/MaterialSystemCentralRendering.md
  • Editor/Source/Editor.cpp
  • Editor/Source/Editor.h
  • Editor/Source/UI/Panels/ViewportPanel.h
  • Elixir/.clang-tidy
  • Elixir/Source/Engine.h
  • Elixir/Source/Engine/Aether/Core/ColorCurveStore.cpp
  • Elixir/Source/Engine/Aether/Core/ColorCurveStore.h
  • Elixir/Source/Engine/Aether/Core/CurveStore.cpp
  • Elixir/Source/Engine/Aether/Core/CurveStore.h
  • Elixir/Source/Engine/Aether/Core/ParameterStore.cpp
  • Elixir/Source/Engine/Aether/Core/ParameterStore.h
  • Elixir/Source/Engine/Aether/Core/Particle.h
  • Elixir/Source/Engine/Aether/Core/ParticleStateLayout.cpp
  • Elixir/Source/Engine/Aether/Core/ParticleStateLayout.h
  • Elixir/Source/Engine/Aether/Core/ResourceAllocation.h
  • Elixir/Source/Engine/Aether/Effect.h
  • Elixir/Source/Engine/Aether/Effect/Effect.cpp
  • Elixir/Source/Engine/Aether/Effect/Effect.h
  • Elixir/Source/Engine/Aether/Effect/MaterialDescription.h
  • Elixir/Source/Engine/Aether/Effect/MaterialFactory.cpp
  • Elixir/Source/Engine/Aether/Effect/MaterialFactory.h
  • Elixir/Source/Engine/Aether/Effect/MaterialResolver.cpp
  • Elixir/Source/Engine/Aether/Effect/MaterialResolver.h
  • Elixir/Source/Engine/Aether/Emitter.cpp
  • Elixir/Source/Engine/Aether/Emitter.h
  • Elixir/Source/Engine/Aether/FrameSubmission.h
  • Elixir/Source/Engine/Aether/Manager.cpp
  • Elixir/Source/Engine/Aether/Manager.h
  • Elixir/Source/Engine/Aether/Modules/Modules.cpp
  • Elixir/Source/Engine/Aether/Modules/Modules.h
  • Elixir/Source/Engine/Aether/Particle.h
  • Elixir/Source/Engine/Aether/ParticleResourcePool.h
  • Elixir/Source/Engine/Aether/ParticleStateLayout.h
  • Elixir/Source/Engine/Aether/Renderer.cpp
  • Elixir/Source/Engine/Aether/Renderer.h
  • Elixir/Source/Engine/Aether/Rendering/FrameSubmission.h
  • Elixir/Source/Engine/Aether/Rendering/Renderer.cpp
  • Elixir/Source/Engine/Aether/Rendering/Renderer.h
  • Elixir/Source/Engine/Aether/Rendering/SystemInstanceRenderProxy.cpp
  • Elixir/Source/Engine/Aether/Rendering/SystemInstanceRenderProxy.h
  • Elixir/Source/Engine/Aether/Rendering/SystemInstanceRetirementQueue.h
  • Elixir/Source/Engine/Aether/Runtime/InstanceRegistry.cpp
  • Elixir/Source/Engine/Aether/Runtime/InstanceRegistry.h
  • Elixir/Source/Engine/Aether/Simulation/RenderFrame.h
  • Elixir/Source/Engine/Aether/Simulation/ResourcePool.cpp
  • Elixir/Source/Engine/Aether/Simulation/ResourcePool.h
  • Elixir/Source/Engine/Aether/Simulation/Simulator.cpp
  • Elixir/Source/Engine/Aether/Simulation/Simulator.h
  • Elixir/Source/Engine/Aether/System.cpp
  • Elixir/Source/Engine/Aether/System.h
  • Elixir/Source/Engine/Aether/SystemInstance.cpp
  • Elixir/Source/Engine/Aether/SystemInstance.h
  • Elixir/Source/Engine/Core/Application.cpp
  • Elixir/Source/Engine/Core/Application.h
  • Elixir/Source/Engine/Core/Core.h
  • Elixir/Source/Engine/Core/UUID.h
  • Elixir/Source/Engine/Graphics/FrameSlotPendingState.h
  • Elixir/Source/Engine/Graphics/FrameSlotState.h
  • Elixir/Source/Engine/Graphics/GraphicsContext.h
  • Elixir/Source/Engine/Graphics/Shader/Shader.cpp
  • Elixir/Source/Engine/Graphics/Shader/Shader.h
  • Elixir/Source/Engine/Icon/IconManager.h
  • Elixir/Source/Engine/Materials/Compilation/CompilationCache.cpp
  • Elixir/Source/Engine/Materials/Compilation/CompilationCache.h
  • Elixir/Source/Engine/Materials/Compilation/Compiler.cpp
  • Elixir/Source/Engine/Materials/Compilation/Compiler.h
  • Elixir/Source/Engine/Materials/DefaultMaterials.cpp
  • Elixir/Source/Engine/Materials/DefaultMaterials.h
  • Elixir/Source/Engine/Materials/Material.cpp
  • Elixir/Source/Engine/Materials/Material.h
  • Elixir/Source/Engine/Materials/MaterialGraph.cpp
  • Elixir/Source/Engine/Materials/MaterialGraph.h
  • Elixir/Source/Engine/Materials/MaterialInstance.cpp
  • Elixir/Source/Engine/Materials/MaterialInstance.h
  • Elixir/Source/Engine/Materials/MaterialNode.cpp
  • Elixir/Source/Engine/Materials/MaterialNode.h
  • Elixir/Source/Engine/Materials/MaterialParameter.h
  • Elixir/Source/Engine/Materials/MaterialProxyCache.cpp
  • Elixir/Source/Engine/Materials/MaterialProxyCache.h
  • Elixir/Source/Engine/Materials/MaterialProxyResolver.cpp
  • Elixir/Source/Engine/Materials/MaterialProxyResolver.h
  • Elixir/Source/Engine/Materials/MaterialRegistry.cpp
  • Elixir/Source/Engine/Materials/MaterialRegistry.h
  • Elixir/Source/Engine/Materials/MaterialSystem.cpp
  • Elixir/Source/Engine/Materials/MaterialSystem.h
  • Elixir/Source/Engine/Materials/Nodes/Add.h
  • Elixir/Source/Engine/Materials/Nodes/BinaryOperationNode.h
  • Elixir/Source/Engine/Materials/Nodes/Checkerboard.h
  • Elixir/Source/Engine/Materials/Nodes/ComponentMask.h
  • Elixir/Source/Engine/Materials/Nodes/Constant.h
  • Elixir/Source/Engine/Materials/Nodes/Divide.h
  • Elixir/Source/Engine/Materials/Nodes/Dot.h
  • Elixir/Source/Engine/Materials/Nodes/Fresnel.h
  • Elixir/Source/Engine/Materials/Nodes/Lerp.h
  • Elixir/Source/Engine/Materials/Nodes/Multiply.h
  • Elixir/Source/Engine/Materials/Nodes/OneMinus.h
  • Elixir/Source/Engine/Materials/Nodes/Panner.h
  • Elixir/Source/Engine/Materials/Nodes/Parameter.h
  • Elixir/Source/Engine/Materials/Nodes/Power.h
  • Elixir/Source/Engine/Materials/Nodes/RadialGradientExponential.h
  • Elixir/Source/Engine/Materials/Nodes/Saturate.h
  • Elixir/Source/Engine/Materials/Nodes/Sine.h
  • Elixir/Source/Engine/Materials/Nodes/Subtract.h
  • Elixir/Source/Engine/Materials/Nodes/TexCoord.h
  • Elixir/Source/Engine/Materials/Nodes/TextureSample.h
  • Elixir/Source/Engine/Materials/Nodes/Time.h
  • Elixir/Source/Engine/Materials/Nodes/UnaryOperationNode.h
  • Elixir/Source/Engine/Materials/Rendering/FrameTable.cpp
  • Elixir/Source/Engine/Materials/Rendering/FrameTable.h
  • Elixir/Source/Engine/Materials/Rendering/MaterialRenderProxy.cpp
  • Elixir/Source/Engine/Materials/Rendering/MaterialRenderProxy.h
  • Elixir/Source/Engine/Materials/Rendering/MaterialRenderScene.cpp
  • Elixir/Source/Engine/Materials/Rendering/MaterialRenderScene.h
  • Elixir/Source/Engine/Materials/Rendering/MaterialResolver.h
  • Elixir/Source/Engine/Materials/Rendering/Renderer.cpp
  • Elixir/Source/Engine/Materials/Rendering/Renderer.h
  • Elixir/Source/Engine/Materials/Rendering/TextureRegistry.cpp
  • Elixir/Source/Engine/Materials/Rendering/TextureRegistry.h
  • Elixir/Source/Graphics/Vulkan/VulkanDescriptorPool.cpp
  • Elixir/Source/Graphics/Vulkan/VulkanDescriptorPool.h
  • Elixir/Source/Graphics/Vulkan/VulkanGraphicsContext.cpp
  • Elixir/Source/Graphics/Vulkan/VulkanGraphicsContext.h
  • Elixir/Source/Graphics/Vulkan/VulkanShader.cpp
  • Elixir/Source/Graphics/Vulkan/VulkanShader.h
  • Elixir/Tests/Engine/Aether/Effect/MaterialResolverTest.cpp
  • Elixir/Tests/Engine/Aether/FrameSubmissionTest.cpp
  • Elixir/Tests/Engine/Aether/Rendering/FrameSubmissionPublisherTest.cpp
  • Elixir/Tests/Engine/Aether/Rendering/FrameSubmissionTest.cpp
  • Elixir/Tests/Engine/Aether/Rendering/RendererTest.cpp
  • Elixir/Tests/Engine/Aether/Rendering/SystemInstanceRetirementQueueTest.cpp
  • Elixir/Tests/Engine/Aether/Runtime/InstanceRegistryTest.cpp
  • Elixir/Tests/Engine/Aether/Simulation/RenderFrameTest.cpp
  • Elixir/Tests/Engine/Aether/Simulation/ResourcePoolTest.cpp
  • Elixir/Tests/Engine/Aether/Simulation/SimulatorTest.cpp
  • Elixir/Tests/Engine/Aether/SystemInstanceTest.cpp
  • Elixir/Tests/Engine/Aether/SystemTest.cpp
  • Elixir/Tests/Engine/Aether/TestInstanceRegistry.h
  • Elixir/Tests/Engine/Aether/TestMaterialResolver.h
  • Elixir/Tests/Engine/Graphics/FrameSlotStateTest.cpp
  • Elixir/Tests/Engine/Materials/Compilation/CompilationCacheTest.cpp
  • Elixir/Tests/Engine/Materials/Compilation/CompilerTest.cpp
  • Elixir/Tests/Engine/Materials/MaterialGraphTest.cpp
  • Elixir/Tests/Engine/Materials/MaterialProxyCacheTest.cpp
  • Elixir/Tests/Engine/Materials/MaterialRegistryTest.cpp
  • Elixir/Tests/Engine/Materials/MaterialTest.cpp
  • Elixir/Tests/Engine/Materials/Rendering/FrameTableTest.cpp
  • Elixir/Tests/Engine/Materials/Rendering/MaterialRenderProxyTest.cpp
  • Elixir/Tests/Engine/Materials/Rendering/MaterialRenderSceneTest.cpp
  • Elixir/Tests/Engine/Materials/Rendering/RendererTest.cpp
  • Elixir/Tests/Engine/Materials/Rendering/TextureRegistryTest.cpp
  • Elixir/Tests/Graphics/Vulkan/VulkanBufferTest.cpp
  • Elixir/Tests/Graphics/Vulkan/VulkanImageTest.cpp
  • Elixir/Tests/Graphics/Vulkan/VulkanTestContext.h
  • Shaders/.gitignore
  • Shaders/Shaders.cmake

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 17589ccfac

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Elixir/Source/Engine/Core/Application.cpp

// __GRAPH_BODY__

return float4(surface.BaseColor + surface.Emissive, surface.Opacity);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve simulated particle color in material shaders

When an emitter uses SetColor or ColorOverLife (including the checked FireAndFireworks.json effect), this return ignores the input.Color supplied by the particle vertex shader, so simulated RGB and alpha fades have no effect. The new ribbon and mesh templates likewise return only material surface values, meaning every material-backed particle mode loses per-particle tint and opacity; the final color and alpha need to incorporate the particle input.

Useful? React with 👍 / 👎.

Comment on lines +629 to +630
const auto allocation = m_ResourcePool.Allocate(system);
if (!allocation)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Allow recompilation without double-allocating pool capacity

When a registered instance changes revision while the pool cannot hold both its old and replacement ranges—for example, a valid system using more than half the particle capacity—this allocation fails before the old range is queued for retirement. ResolveSubmittedInstances then drops the instance, while its old record continues owning the space, so every later frame retries under the same pressure and the instance disappears permanently even though the replacement would fit by itself.

Useful? React with 👍 / 👎.


if (input >= 0)
{
in.push_back(EmitNode((uint32_t)input, emitted, types, body, bindings));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject cycles before recursively emitting graph nodes

When Connect creates a cycle such as A → B → A, EmitNode recurses here before either node is inserted into emitted, resulting in unbounded recursion and a stack-overflow crash during material compilation. Because Material::ValidateGraph currently checks only parameter references, cyclic graphs accepted through the public graph API need cycle detection before code generation.

Useful? React with 👍 / 👎.

break;
}
case EMaterialNodeType::OneMinus:
expr = "(1.0 - " + in[0] + ")";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Handle unconnected unary graph nodes safely

When a OneMinus node is added with the default empty Inputs vector and selected as a channel before being connected, this indexes in[0] and invokes undefined behavior during GenerateHLSL; the adjacent Saturate case has the same defect. Other node implementations already use the bounds-safe A(0) helper, so these unary nodes should do likewise or graph validation should reject the missing input.

Useful? React with 👍 / 👎.

type = AT(0);
break;
case EMaterialNodeType::Fresnel:
expr = "pow(saturate(1.0 - dot(N, V)), 5.0)";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Define Fresnel inputs for particle material templates

When a graph containing a Fresnel node enables any particle usage, code generation emits this expression into the particle pixel template, but those templates define neither N nor V; only Material.ps.hlsl declares them. As a result, the surface stage compiles and the subsequent particle permutation fails in DXC, making the otherwise public node unusable for sprite, ribbon, or mesh materials.

Useful? React with 👍 / 👎.

Comment on lines +19 to +20
if (emitter->GetMaterial())
continue;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Refresh resolver-owned materials during recompilation

When an emitter was initially assigned an effect-authored or default material by this resolver, that assignment makes GetMaterial() non-null forever, so a later SetMaterialDescription or SetRenderMode followed by Manager::Recompile skips material resolution. The old generated material and usage remain attached—changing Sprite to Ribbon, for example, leaves an unsupported Sprite material and produces a compiled emitter with no render proxy—so resolver-owned assignments need to be distinguished from explicit overrides and refreshed when authored material state changes.

Useful? React with 👍 / 👎.

Comment on lines +41 to +44
m_Bindings.emplace(texture, SMaterialTextureBinding{
.Handle = handle,
.ReadySubmission = m_SubmissionSerial + 1,
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Release texture bindings that are no longer referenced

When material instances repeatedly switch to newly created textures, every first resolution inserts a strong Ref<Texture> here, and the registry never erases the binding or calls TextureSet::RemoveTexture. Consequently, obsolete textures and their bindless descriptor slots remain alive for the full MaterialSystem lifetime; a dynamic texture workload eventually reaches the Vulkan bindless pool's finite texture capacity even after no material references the old textures.

Useful? React with 👍 / 👎.

Comment on lines +127 to +128
float3 diffuse = SampleIrradiance(N) * surface.BaseColor * (1.0f - surface.Metallic);
float3 R = reflect(-V, N);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Apply the graph normal output to lighting

When a graph drives EMaterialChannel::Normal, the generated body updates surface.Normal, but all lighting below the marker continues to use N, which was computed from input.Normal before the graph ran. Thus normal-map or procedural-normal nodes compile successfully but cannot affect diffuse, reflection, Fresnel, or direct lighting; the graph normal must be transformed and assigned to the normal used by those calculations. The particle mesh template has the same issue by lighting exclusively from input.Normal.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant