Preserve provider temporal projection convention

This commit is contained in:
Meta Vens 2026-07-16 21:41:18 +01:00
commit 3135d24bb8
4 changed files with 44 additions and 7 deletions

View file

@ -82,6 +82,12 @@ the field textures it samples. A `world.scene3d` may also request
quality, and sharpening values to URP TAA and restores the previous camera
state when the world releases the camera.
Native volume programs also advertise the projection convention consumed by
their history matrix. `non-render-target-projection.previous-view.v1` preserves
the projection convention used by legacy `Graphics.Blit` temporal shaders even
when EveUnity executes the program through a render-target-backed URP pass;
providers must not rely on a runtime guessing that Y-axis convention.
`field.particles3d` may advertise provider material textures, viewport-relative
texture scales, and a logical render-frame index port. These are presentation
inputs: EveUnity binds the selected provider assets and current display frame

View file

@ -140,6 +140,7 @@ namespace GameCult.Eve.UnityScene.Fields
ApplyQuality(field, _historyTextureIndex < 0);
var gpuProjection = GL.GetGPUProjectionMatrix(camera.projectionMatrix, true);
var nonRenderTargetProjection = GL.GetGPUProjectionMatrix(camera.projectionMatrix, false);
var viewProjection = gpuProjection * camera.worldToCameraMatrix;
SetMatrixPort("cameraInverseViewProjection", viewProjection.inverse);
SetMatrixPort("cameraToWorld", CameraTransformToWorld(camera));
@ -170,6 +171,7 @@ namespace GameCult.Eve.UnityScene.Fields
_programMetadata,
_previousViewProjection,
gpuProjection,
nonRenderTargetProjection,
_previousView));
SetFloatPort("resetHistory", _historyTextureIndex < 0 ? 1f : 0f);
}
@ -496,13 +498,17 @@ namespace GameCult.Eve.UnityScene.Fields
IReadOnlyDictionary<string, string>? metadata,
Matrix4x4 previousViewProjection,
Matrix4x4 currentGpuProjection,
Matrix4x4 currentNonRenderTargetProjection,
Matrix4x4 previousView)
{
return metadata != null &&
metadata.TryGetValue("unity.volume.matrixSemantic.previousViewProjection", out var semantic) &&
string.Equals(semantic, "current-projection.previous-view.v1", StringComparison.Ordinal)
? currentGpuProjection * previousView
: previousViewProjection;
if (metadata == null ||
!metadata.TryGetValue("unity.volume.matrixSemantic.previousViewProjection", out var semantic))
return previousViewProjection;
if (string.Equals(semantic, "current-projection.previous-view.v1", StringComparison.Ordinal))
return currentGpuProjection * previousView;
if (string.Equals(semantic, "non-render-target-projection.previous-view.v1", StringComparison.Ordinal))
return currentNonRenderTargetProjection * previousView;
return previousViewProjection;
}
private bool HasTemporalProgram() => ProgramPass("temporal") >= 0;
@ -583,7 +589,8 @@ namespace GameCult.Eve.UnityScene.Fields
return Fail("Native volume temporal pass is missing a required history port.", out error);
if (metadata.TryGetValue("unity.volume.matrixSemantic.previousViewProjection", out var semantic) &&
!string.Equals(semantic, "previous-view-projection.v1", StringComparison.Ordinal) &&
!string.Equals(semantic, "current-projection.previous-view.v1", StringComparison.Ordinal))
!string.Equals(semantic, "current-projection.previous-view.v1", StringComparison.Ordinal) &&
!string.Equals(semantic, "non-render-target-projection.previous-view.v1", StringComparison.Ordinal))
return Fail("Native volume temporal pass has an unsupported previous-view-projection semantic.", out error);
if (metadata.TryGetValue("unity.volume.quality.bootstrap", out var bootstrapQuality) &&
(!metadata.TryGetValue(

View file

@ -341,6 +341,7 @@ namespace GameCult.Eve.UnityScene.Tests
metadata,
previousViewProjection,
currentProjection,
Matrix4x4.identity,
previousView);
Assert.That(resolved, Is.EqualTo(currentProjection * previousView));
@ -356,11 +357,34 @@ namespace GameCult.Eve.UnityScene.Tests
RequiredVolumeProgramMetadata(),
previousViewProjection,
Matrix4x4.Scale(Vector3.one * 2f),
Matrix4x4.identity,
Matrix4x4.Rotate(Quaternion.Euler(10f, 20f, 30f)));
Assert.That(resolved, Is.EqualTo(previousViewProjection));
}
[Test]
public void VolumeRendererUsesNonRenderTargetProjectionWhenAdvertised()
{
var metadata = RequiredVolumeProgramMetadata();
metadata["unity.volume.matrixSemantic.previousViewProjection"] =
"non-render-target-projection.previous-view.v1";
var previousViewProjection = Matrix4x4.Translate(new Vector3(1f, 2f, 3f));
var renderTargetProjection = Matrix4x4.Scale(new Vector3(2f, -3f, 4f));
var nonRenderTargetProjection = Matrix4x4.Scale(new Vector3(2f, 3f, 4f));
var previousView = Matrix4x4.Rotate(Quaternion.Euler(10f, 20f, 30f));
var resolved = EveUnityFieldsVolumeRenderer.ResolvePreviousViewProjection(
metadata,
previousViewProjection,
renderTargetProjection,
nonRenderTargetProjection,
previousView);
Assert.That(resolved, Is.EqualTo(nonRenderTargetProjection * previousView));
Assert.That(resolved, Is.Not.EqualTo(renderTargetProjection * previousView));
}
[Test]
public void VolumeProgramRejectsUnknownPreviousViewProjectionSemantic()
{

View file

@ -1,7 +1,7 @@
{
"name": "org.gamecult.eve.unity-scene",
"displayName": "Eve Unity Scene Lowering",
"version": "0.3.62",
"version": "0.3.63",
"unity": "6000.0",
"description": "Provider-agnostic Unity scene shell for gamecult.eve.surface.v1 interactive world surfaces.",
"dependencies": {