Skip to content

Commit 63d4a8c

Browse files
[REMIX-4116] make animated water with translucent materials work without a texture transform.
This is done by animating the primary texcoord and secondary normal sample using the Remix animation time. Also makes animated water in translucent materials compatible with animated spritesheets.
1 parent 1b834dd commit 63d4a8c

10 files changed

+103
-40
lines changed

RtxOptions.md

+4
Original file line numberDiff line numberDiff line change
@@ -739,6 +739,10 @@ Tables below enumerate all the options and their defaults set by RTX Remix. Note
739739
|rtx.tonemappingMode|int|1|The tonemapping type to use, 0 for Global, 1 for Local \(Default\)\.<br>Global tonemapping tonemaps the image with respect to global parameters, usually based on statistics about the rendered image as a whole\.<br>Local tonemapping on the other hand uses more spatially\-local parameters determined by regions of the rendered image rather than the whole image\.<br>Local tonemapping can result in better preservation of highlights and shadows in scenes with high amounts of dynamic range whereas global tonemapping may have to comprimise between over or underexposure\.|
740740
|rtx.trackParticleObjects|bool|True|Track last frame's corresponding particle object\.|
741741
|rtx.translucentDecalAlbedoFactor|float|10|A global scale factor applied to the albedo of decals that are applied to a translucent base material, to make the decals more visible\.<br>This is generally needed as albedo values for decals may be fairly low when dealing with opaque surfaces, but the translucent diffuse layer requires a fairly high albedo value to result in an expected look\.<br>The need for this option could be avoided by simply authoring decals applied to translucent materials with a higher albedo to begin with, but sometimes applications may share decals between different material types\.|
742+
|rtx.translucentMaterial.animatedWaterEnable|bool|True|If enabled, draw calls in the AnimatedWater category and a translucent material will have their primary texcoordinates animated, and will also take a second sample from the normal map\.<br>Note that objects must be properly categorized as animated water to be rendered with this mode\.|
743+
|rtx.translucentMaterial.animatedWaterPrimaryNormalMotion|float2|0.05, 0.05|Velocity in UV space for the primary texture coordinate\.<br>This relies on Remix animation time, which will not respect any time warping performed by the game \(so pause, slow mo, etc will not change animation speed\)\.|
744+
|rtx.translucentMaterial.animatedWaterSecondaryNormalLodBias|float|1|LoD bias to use when taking the second normal map sample\.<br>Values greater than 0 will cause the secondary normal map to be blurred, resulting in lower frequency waves\.|
745+
|rtx.translucentMaterial.animatedWaterSecondaryNormalMotion|float2|-0.03, -0.06|Velocity in UV space for the secondary texture coordinate\.<br>This relies on Remix animation time, which will not respect any time warping performed by the game \(so pause, slow mo, etc will not change animation speed\)\.|
742746
|rtx.translucentMaterial.enableDiffuseLayerOverride|bool|False|A flag to force the diffuse layer on the translucent material to be enabled\. Should only be used for debugging or development\.|
743747
|rtx.translucentMaterial.normalIntensity|float|1|An arbitrary strength scale factor to apply when decoding normals in the translucent material\. Should only be used for debugging or development\.|
744748
|rtx.translucentMaterial.transmittanceColorBias|float|0|A bias factor to add to all transmittance color values in the opaque material\. Should only be used for debugging or development\.|

src/dxvk/imgui/dxvk_imgui.cpp

+10-1
Original file line numberDiff line numberDiff line change
@@ -680,9 +680,10 @@ namespace dxvk {
680680
ImGui::SliderFloat("Roughness Bias", &opaqueMaterialOptions.roughnessBiasObject(), -1.0f, 1.f, "%.3f", sliderFlags);
681681
ImGui::SliderFloat("Normal Strength##1", &opaqueMaterialOptions.normalIntensityObject(), -10.0f, 10.f, "%.3f", sliderFlags);
682682

683-
ImGui::Checkbox("Enable dual-layer animated water normal", &opaqueMaterialOptions.layeredWaterNormalEnableObject());
683+
ImGui::Checkbox("Enable dual-layer animated water normal for Opaque", &opaqueMaterialOptions.layeredWaterNormalEnableObject());
684684

685685
if (opaqueMaterialOptions.layeredWaterNormalEnable()) {
686+
ImGui::TextWrapped("Animated water with Opaque material is dependent on the original draw call animating using a texture transform.");
686687
ImGui::SliderFloat2("Layered Motion Direction", &opaqueMaterialOptions.layeredWaterNormalMotionObject(), -1.0f, 1.0f, "%.3f", sliderFlags);
687688
ImGui::SliderFloat("Layered Motion Scale", &opaqueMaterialOptions.layeredWaterNormalMotionScaleObject(), -10.0f, 10.0f, "%.3f", sliderFlags);
688689
ImGui::SliderFloat("LOD bias", &opaqueMaterialOptions.layeredWaterNormalLodBiasObject(), 0.0f, 16.0f, "%.3f", sliderFlags);
@@ -699,6 +700,14 @@ namespace dxvk {
699700
ImGui::SliderFloat("Transmit. Color Bias", &translucentMaterialOptions.transmittanceColorBiasObject(), -1.0f, 1.f, "%.3f", sliderFlags);
700701
ImGui::SliderFloat("Normal Strength##2", &translucentMaterialOptions.normalIntensityObject(), -10.0f, 10.f, "%.3f", sliderFlags);
701702

703+
ImGui::Checkbox("Enable dual-layer animated water normal for Translucent", &translucentMaterialOptions.animatedWaterEnableObject());
704+
if (translucentMaterialOptions.animatedWaterEnable()) {
705+
ImGui::TextWrapped("Animated water with Translucent materials will animate using Remix animation time.");
706+
707+
ImGui::SliderFloat2("Primary Texcoord Velocity", &translucentMaterialOptions.animatedWaterPrimaryNormalMotionObject(), -0.5f, 0.5f, "%.3f", sliderFlags);
708+
ImGui::SliderFloat2("Secondary Normal Texcoord Velocity", &translucentMaterialOptions.animatedWaterSecondaryNormalMotionObject(), -0.5f, 0.5f, "%.3f", sliderFlags);
709+
ImGui::SliderFloat("Secondary Normal LOD bias", &translucentMaterialOptions.animatedWaterSecondaryNormalLodBiasObject(), 0.0f, 16.0f, "%.3f", sliderFlags);
710+
}
702711
ImGui::Unindent();
703712
}
704713

src/dxvk/rtx_render/rtx_context.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -1193,7 +1193,7 @@ namespace dxvk {
11931193
// at the 24 bit boundary (as we use a 8 bit scalar on top of this time which we want to fit into 32 bits without issues,
11941194
// plus we also convert this value to a floating point value at some point as well which has 23 bits of precision).
11951195
// Bitwise and used rather than modulus as well for slightly better performance.
1196-
constants.timeSinceStartMS = static_cast<uint32_t>(getSceneManager().getGameTimeSinceStartMS()) & ((1U << 24U) - 1U);
1196+
constants.timeSinceStartSeconds = (static_cast<uint32_t>(getSceneManager().getGameTimeSinceStartMS()) & ((1U << 24U) - 1U)) / 1000.f;
11971197

11981198
m_common->metaRtxdiRayQuery().setRaytraceArgs(rtOutput);
11991199
getSceneManager().getLightManager().setRaytraceArgs(

src/dxvk/rtx_render/rtx_materials.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -219,8 +219,9 @@ struct RtSurface {
219219

220220
std::uint32_t textureSpritesheetData = 0;
221221

222-
textureSpritesheetData |= (static_cast<uint32_t>(spriteSheetRows) << 0);
223-
textureSpritesheetData |= (static_cast<uint32_t>(spriteSheetCols) << 8);
222+
// Clamp rows and cols to at least 1, to avoid divide by 0 errors.
223+
textureSpritesheetData |= (static_cast<uint32_t>(std::max<uint8_t>(1, spriteSheetRows)) << 0);
224+
textureSpritesheetData |= (static_cast<uint32_t>(std::max<uint8_t>(1, spriteSheetCols)) << 8);
224225
textureSpritesheetData |= (static_cast<uint32_t>(spriteSheetFPS) << 16);
225226
// pack decalSortOrder into data13.x's last 8 bits.
226227
textureSpritesheetData |= (static_cast<uint32_t>(decalSortOrder) << 24);

src/dxvk/shaders/rtx/algorithm/geometry_resolver.slangh

+1-1
Original file line numberDiff line numberDiff line change
@@ -732,7 +732,7 @@ void geometryResolverVertexOutputDebugView(
732732
case DEBUG_VIEW_VALUE_NOISE:
733733
// Some test values to produce validation view
734734
float3 pos = surfaceInteraction.position * 0.1f;
735-
float time = cb.timeSinceStartMS * 0.001f;
735+
float time = cb.timeSinceStartSeconds;
736736
if(cb.debugKnob.x <= 1.f)
737737
{
738738
const float noise = valueNoise4D(float4(pos, time));

src/dxvk/shaders/rtx/concept/surface/surface_interaction.slangh

+27-9
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ void calcSpriteSheetAdjustment(
152152
inout vec2 textureCoordinates,
153153
inout vec2 textureGradientX,
154154
inout vec2 textureGradientY,
155-
uint32_t timeSinceStartMS,
155+
float timeSinceStartSeconds,
156156
uint8_t spriteSheetRows,
157157
uint8_t spriteSheetCols,
158158
uint8_t spriteSheetFPS)
@@ -162,19 +162,37 @@ void calcSpriteSheetAdjustment(
162162
{
163163
return;
164164
}
165-
166-
const uint numSprites = spriteSheetCols * spriteSheetRows;
167-
// Note: timeSinceStartMS is clamped to 2^24 - 1 on the CPU side so this multiplication shouldn't overflow.
168-
const uint frame = ((timeSinceStartMS * spriteSheetFPS) / 1000) % numSprites;
169-
170-
const vec2 uvSize = vec2(1.0f / spriteSheetCols, 1.0f / spriteSheetRows);
171-
const vec2 uvBias = vec2(frame % spriteSheetCols, frame / spriteSheetCols) * uvSize;
165+
vec2 uvSize, uvBias;
166+
calcSpritesheetValues(
167+
timeSinceStartSeconds,
168+
spriteSheetRows,
169+
spriteSheetCols,
170+
spriteSheetFPS,
171+
uvSize,
172+
uvBias);
172173

173174
textureCoordinates = uvBias + frac(textureCoordinates) * uvSize;
174175
textureGradientX *= uvSize;
175176
textureGradientY *= uvSize;
176177
}
177178

179+
void calcSpritesheetValues(
180+
float timeSinceStartSeconds,
181+
uint8_t spriteSheetRows,
182+
uint8_t spriteSheetCols,
183+
uint8_t spriteSheetFPS,
184+
inout vec2 uvSize,
185+
inout vec2 uvBias
186+
)
187+
{
188+
const uint numSprites = spriteSheetCols * spriteSheetRows;
189+
// Note: timeSinceStartSeconds is clamped to (2^24 - 1) / 1000 on the CPU side so this multiplication shouldn't overflow.
190+
const uint frame = (uint(timeSinceStartSeconds * spriteSheetFPS)) % numSprites;
191+
192+
uvSize = vec2(1.0f / spriteSheetCols, 1.0f / spriteSheetRows);
193+
uvBias = vec2(frame % spriteSheetCols, frame / spriteSheetCols) * uvSize;
194+
}
195+
178196
SurfaceInteraction surfaceInteractionCreate<let GenerateTangents : bool>(
179197
Surface surface, RayInteraction rayInteraction, Ray ray,
180198
bool usePreviousPositions = false, uint footprintMode = kFootprintFromRayDirection)
@@ -581,7 +599,7 @@ SurfaceInteraction surfaceInteractionCreate<let GenerateTangents : bool>(
581599
surfaceInteraction.textureCoordinates,
582600
surfaceInteraction.textureGradientX,
583601
surfaceInteraction.textureGradientY,
584-
cb.timeSinceStartMS,
602+
cb.timeSinceStartSeconds,
585603
surface.spriteSheetRows,
586604
surface.spriteSheetCols,
587605
surface.spriteSheetFPS);

src/dxvk/shaders/rtx/concept/surface_material/ray_portal_surface_material_interaction.slangh

+4-6
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ bool rayPortalSurfaceMaterialInteractionSpriteSheetReadHelper(
3030
uint16_t samplerIndex,
3131
bool textureGammaCorrect,
3232
SurfaceInteraction surfaceInteraction,
33-
uint32_t timeSinceStartMS,
33+
float timeSinceStartSeconds,
3434
uint8_t spriteSheetRows,
3535
uint8_t spriteSheetCols,
3636
uint8_t spriteSheetFPS,
@@ -45,10 +45,8 @@ bool rayPortalSurfaceMaterialInteractionSpriteSheetReadHelper(
4545

4646
// Note: No early out for spriteSheetFPS == 0 case because Ray Portals will commonly use animated textures.
4747

48-
const float timeSinceStartSecs = timeSinceStartMS * 0.001f;
49-
5048
// Some constant to slow down, and a unique direction for opposing Portals
51-
const float t = float(rotationSpeed) * timeSinceStartSecs;
49+
const float t = float(rotationSpeed) * timeSinceStartSeconds;
5250
const float2x2 rot = float2x2(cos(t), -sin(t), sin(t), cos(t));
5351

5452
vec2 originalCoord = surfaceInteraction.textureCoordinates;
@@ -60,7 +58,7 @@ bool rayPortalSurfaceMaterialInteractionSpriteSheetReadHelper(
6058
originalCoord = saturate(originalCoord);
6159

6260
const uint numSprites = (spriteSheetCols * spriteSheetRows);
63-
const float frameWithFraction = (timeSinceStartSecs * spriteSheetFPS) % numSprites;
61+
const float frameWithFraction = (timeSinceStartSeconds * spriteSheetFPS) % numSprites;
6462
uint frame = floor(frameWithFraction);
6563
const float frameFraction = frac(frameWithFraction);
6664
const vec2 uvSize = vec2(1.0f / spriteSheetCols, 1.0f / spriteSheetRows);
@@ -113,7 +111,7 @@ RayPortalSurfaceMaterialInteraction rayPortalSurfaceMaterialInteractionCreate(
113111
rayPortalSurfaceMaterial.samplerIndex,
114112
true,
115113
surfaceInteraction,
116-
cb.timeSinceStartMS,
114+
cb.timeSinceStartSeconds,
117115
surface.spriteSheetRows,
118116
surface.spriteSheetCols,
119117
surface.spriteSheetFPS,

src/dxvk/shaders/rtx/concept/surface_material/translucent_surface_material_interaction.slangh

+29-19
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ float16_t translucentSurfaceMaterialInteractionGetThinWallThickness(
4040
TranslucentSurfaceMaterialInteraction translucentSurfaceMaterialInteractionCreate(
4141
TranslucentSurfaceMaterial translucentSurfaceMaterial,
4242
Surface surface,
43-
SurfaceInteraction surfaceInteraction,
43+
inout SurfaceInteraction surfaceInteraction,
4444
MinimalRayInteraction minimalRayInteraction)
4545
{
4646
TranslucentSurfaceMaterialInteraction translucentSurfaceMaterialInteraction;
@@ -52,31 +52,41 @@ TranslucentSurfaceMaterialInteraction translucentSurfaceMaterialInteractionCreat
5252
f16vec4 transmittanceOrDiffuseSample;
5353
f16vec4 emissiveColorSample;
5454

55+
bool secondNormalLoaded = false;
56+
if (cb.translucentMaterialArgs.animatedWaterEnable && surface.isAnimatedWater)
57+
{
58+
vec2 uvSize, uvBias;
59+
calcSpritesheetValues(
60+
cb.timeSinceStartSeconds,
61+
surface.spriteSheetRows,
62+
surface.spriteSheetCols,
63+
surface.spriteSheetFPS,
64+
uvSize,
65+
uvBias);
66+
67+
// The UV as it was before applying a spritesheet (in 0-1 space).
68+
const vec2 uvPreSpritesheet = frac(surfaceInteraction.textureCoordinates * vec2(surface.spriteSheetCols, surface.spriteSheetRows));
69+
70+
// The texcoords with primary motion applied, put back into spritesheet space.
71+
const vec2 primaryUV = uvBias + uvSize *(frac(uvPreSpritesheet + cb.timeSinceStartSeconds * cb.translucentMaterialArgs.animatedWaterPrimaryNormalMotion));
72+
73+
// Take a second sample before taking the primary, so that we don't have to keep the secondary UV around.
74+
// Use a lod bias now and a different texcoord velocity to make it a second 'layer' of normals.
75+
const vec2 secondaryUV = uvBias + uvSize *(frac(uvPreSpritesheet + cb.timeSinceStartSeconds * cb.translucentMaterialArgs.animatedWaterSecondaryNormalMotion));
76+
const float lodBias = cb.translucentMaterialArgs.animatedWaterSecondaryNormalLodBias;
77+
78+
surfaceInteraction.textureCoordinates = secondaryUV;;
79+
secondNormalLoaded = surfaceMaterialInteractionTextureReadHelper(translucentSurfaceMaterial.normalTextureIndex, translucentSurfaceMaterial.samplerIndex, surfaceInteraction, secondNormalSample, lodBias);
80+
surfaceInteraction.textureCoordinates = primaryUV;
81+
}
82+
5583
const bool normalLoaded = surfaceMaterialInteractionTextureReadHelper(translucentSurfaceMaterial.normalTextureIndex, translucentSurfaceMaterial.samplerIndex, surfaceInteraction, normalSample);
5684
// Note: Premultiplied alpha should really be used here for the transmittance or diffuse color as without it "transparent" regions of the texture in the diffuse color case for
5785
// example may cause incorrect coloration on the edges of more opaque regions due to texture filtering. This is hard to ensure on the art pipeline side of things however.
5886
const bool transmittanceOrDiffuseLoaded = surfaceMaterialInteractionTextureReadHelper(translucentSurfaceMaterial.transmittanceOrDiffuseTextureIndex, translucentSurfaceMaterial.samplerIndex, surfaceInteraction, transmittanceOrDiffuseSample);
5987
const bool emissiveColorLoaded = surfaceMaterialInteractionTextureReadHelper(translucentSurfaceMaterial.emissiveColorTextureIndex, translucentSurfaceMaterial.samplerIndex, surfaceInteraction, emissiveColorSample);
6088

61-
bool secondNormalLoaded = false;
62-
if (cb.opaqueMaterialArgs.layeredWaterNormalEnable && surface.isAnimatedWater)
63-
{
64-
// take a second sample from a higher LOD using a modified texture xform
65-
const vec2 motionScale = vec2(cb.opaqueMaterialArgs.layeredWaterNormalMotionX, cb.opaqueMaterialArgs.layeredWaterNormalMotionY) * cb.opaqueMaterialArgs.layeredWaterNormalMotionScale;
66-
const float lodBias = cb.opaqueMaterialArgs.layeredWaterNormalLodBias;
67-
68-
mat4x2 xform = surface.textureTransform;
69-
xform[0][2] *= motionScale.x;
70-
xform[1][2] *= motionScale.y;
71-
72-
vec2 savedCoordinates = surfaceInteraction.textureCoordinates;
73-
surfaceInteraction.textureCoordinates = mul(xform, float4(surfaceInteraction.textureCoordinates, 0.f, 1.f)).xy;
74-
secondNormalLoaded = surfaceMaterialInteractionTextureReadHelper(translucentSurfaceMaterial.normalTextureIndex, translucentSurfaceMaterial.samplerIndex, surfaceInteraction, secondNormalSample, lodBias);
75-
surfaceInteraction.textureCoordinates = savedCoordinates;
76-
}
77-
7889
// Create a tangent to world space matrix for future calculations
79-
8090
const f16mat3 tangentToWorld = transpose(f16mat3(surfaceInteraction.interpolatedTangent, surfaceInteraction.interpolatedBitangent, surfaceInteraction.interpolatedNormal));
8191

8292
// Load Normal

src/dxvk/shaders/rtx/pass/material_args.h

+23
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,12 @@ struct TranslucentMaterialArgs {
5151
float transmittanceColorBias = 0.f;
5252
float normalIntensity = 1.f;
5353
uint enableDiffuseLayerOverride = 0;
54+
vec2 animatedWaterPrimaryNormalMotion = vec2(0.f, 0.f);
55+
vec2 animatedWaterSecondaryNormalMotion = vec2(0.f, 0.f);
56+
float animatedWaterSecondaryNormalLodBias = 0.f;
57+
uint animatedWaterEnable = 0;
58+
uint pad0 = 0;
59+
uint pad1 = 0;
5460
};
5561

5662
#ifdef __cplusplus
@@ -130,6 +136,19 @@ struct TranslucentMaterialOptions {
130136
RTX_OPTION("rtx.translucentMaterial", float, transmittanceColorBias, 0.0f, "A bias factor to add to all transmittance color values in the opaque material. Should only be used for debugging or development.");
131137
RTX_OPTION("rtx.translucentMaterial", float, normalIntensity, 1.0f, "An arbitrary strength scale factor to apply when decoding normals in the translucent material. Should only be used for debugging or development.");
132138

139+
// Animated Water
140+
RTX_OPTION("rtx.translucentMaterial", Vector2, animatedWaterPrimaryNormalMotion, Vector2(0.05f, 0.05f),
141+
"Velocity in UV space for the primary texture coordinate.\n"
142+
"This relies on Remix animation time, which will not respect any time warping performed by the game (so pause, slow mo, etc will not change animation speed).");
143+
RTX_OPTION("rtx.translucentMaterial", Vector2, animatedWaterSecondaryNormalMotion, Vector2(-0.03f, -0.06f),
144+
"Velocity in UV space for the secondary texture coordinate.\n"
145+
"This relies on Remix animation time, which will not respect any time warping performed by the game (so pause, slow mo, etc will not change animation speed).");
146+
RTX_OPTION("rtx.translucentMaterial", float, animatedWaterSecondaryNormalLodBias, 1.0f,
147+
"LoD bias to use when taking the second normal map sample.\n"
148+
"Values greater than 0 will cause the secondary normal map to be blurred, resulting in lower frequency waves.");
149+
RTX_OPTION("rtx.translucentMaterial", bool, animatedWaterEnable, true,
150+
"If enabled, draw calls in the AnimatedWater category and a translucent material will have their primary texcoordinates animated, and will also take a second sample from the normal map.\n"
151+
"Note that objects must be properly categorized as animated water to be rendered with this mode.");
133152
// Overrides
134153

135154
RTX_OPTION("rtx.translucentMaterial", bool, enableDiffuseLayerOverride, false, "A flag to force the diffuse layer on the translucent material to be enabled. Should only be used for debugging or development.");
@@ -139,6 +158,10 @@ struct TranslucentMaterialOptions {
139158
args.transmittanceColorScale = transmittanceColorScale();
140159
args.transmittanceColorBias = transmittanceColorBias();
141160
args.normalIntensity = normalIntensity();
161+
args.animatedWaterPrimaryNormalMotion = animatedWaterPrimaryNormalMotion();
162+
args.animatedWaterSecondaryNormalMotion = animatedWaterSecondaryNormalMotion();
163+
args.animatedWaterSecondaryNormalLodBias = animatedWaterSecondaryNormalLodBias();
164+
args.animatedWaterEnable = animatedWaterEnable();
142165
args.enableDiffuseLayerOverride = enableDiffuseLayerOverride();
143166
}
144167
};

src/dxvk/shaders/rtx/pass/raytrace_args.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ struct RaytraceArgs {
227227
uint16_t translucentTransmissionLobeSamplingProbabilityZeroThreshold;
228228
uint16_t minTranslucentTransmissionLobeSamplingProbability;
229229
float roughnessDemodulationOffset;
230-
uint timeSinceStartMS;
230+
float timeSinceStartSeconds;
231231

232232
uint enableCalculateVirtualShadingNormals;
233233
uint enableDirectLighting;

0 commit comments

Comments
 (0)