mirror of
https://bitbucket.org/CPMADevs/cnq3
synced 2025-02-23 04:11:22 +00:00
1891 lines
62 KiB
C++
1891 lines
62 KiB
C++
/*
|
|
===========================================================================
|
|
Copyright (C) 2024 Gian 'myT' Schellenbaum
|
|
|
|
This file is part of Challenge Quake 3 (CNQ3).
|
|
|
|
Challenge Quake 3 is free software; you can redistribute it
|
|
and/or modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the License,
|
|
or (at your option) any later version.
|
|
|
|
Challenge Quake 3 is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Challenge Quake 3. If not, see <https://www.gnu.org/licenses/>.
|
|
===========================================================================
|
|
*/
|
|
// Cinematic Rendering Pipeline - fog, NanoVDB volumes and particles lit by the sun and local lights
|
|
|
|
|
|
#include "crp_local.h"
|
|
#include "../client/cl_imgui.h"
|
|
#include "../im3d/im3d.h"
|
|
#include "shaders/crp/scene_view.h.hlsli"
|
|
#include "shaders/crp/light_grid.h.hlsli"
|
|
#include "shaders/crp/vl_common.h.hlsli"
|
|
#include "compshaders/crp/fullscreen.h"
|
|
#include "compshaders/crp/vl_extinction_injection_fog.h"
|
|
#include "compshaders/crp/vl_extinction_injection_nanovdb.h"
|
|
//#include "compshaders/crp/vl_extinction_injection_particles.h"
|
|
#include "compshaders/crp/vl_frustum_anisotropy_average.h"
|
|
#include "compshaders/crp/vl_frustum_depth_test.h"
|
|
#include "compshaders/crp/vl_frustum_injection_fog.h"
|
|
#include "compshaders/crp/vl_frustum_injection_nanovdb.h"
|
|
#include "compshaders/crp/vl_frustum_injection_nanovdb_lq.h"
|
|
#include "compshaders/crp/vl_frustum_injection_particles.h"
|
|
#include "compshaders/crp/vl_frustum_inscatter_ambient.h"
|
|
#include "compshaders/crp/vl_frustum_inscatter_point_light.h"
|
|
#include "compshaders/crp/vl_frustum_inscatter_sunlight.h"
|
|
#include "compshaders/crp/vl_frustum_light_propagation_nx.h"
|
|
#include "compshaders/crp/vl_frustum_light_propagation_ny.h"
|
|
#include "compshaders/crp/vl_frustum_light_propagation_px.h"
|
|
#include "compshaders/crp/vl_frustum_light_propagation_py.h"
|
|
#include "compshaders/crp/vl_frustum_raymarch.h"
|
|
#include "compshaders/crp/vl_frustum_sunlight_visibility.h"
|
|
#include "compshaders/crp/vl_frustum_temporal_float.h"
|
|
#include "compshaders/crp/vl_frustum_temporal_float4.h"
|
|
#include "compshaders/crp/vl_particles_clear.h"
|
|
#include "compshaders/crp/vl_particles_hit.h"
|
|
#include "compshaders/crp/vl_particles_list.h"
|
|
#include "compshaders/crp/vl_particles_tiles.h"
|
|
#include "compshaders/crp/vl_shadow_point_light.h"
|
|
#include "compshaders/crp/vl_shadow_sun.h"
|
|
#include "compshaders/crp/vl_debug_ambient.h"
|
|
#include "compshaders/crp/vl_debug_extinction.h"
|
|
#include "compshaders/crp/vl_debug_shadow_sun.h"
|
|
|
|
|
|
#pragma pack(push, 4)
|
|
|
|
struct VLGlobalFogRC
|
|
{
|
|
FogVolume fog;
|
|
float time;
|
|
uint32_t materialTextureAIndex;
|
|
uint32_t materialTextureBIndex;
|
|
uint32_t materialTextureCIndex;
|
|
};
|
|
|
|
struct VLAnisotropyRC
|
|
{
|
|
uint32_t materialTextureBIndex;
|
|
uint32_t materialTextureCIndex;
|
|
};
|
|
|
|
struct VLSunlightVisRC
|
|
{
|
|
vec3_t jitter;
|
|
uint32_t visTextureIndex;
|
|
uint32_t frustumVisTextureIndex;
|
|
uint32_t depthMip;
|
|
};
|
|
|
|
struct VLSunlightRC
|
|
{
|
|
uint32_t materialTextureAIndex;
|
|
uint32_t materialTextureBIndex;
|
|
uint32_t scatterExtTextureIndex;
|
|
uint32_t sunlightVisTextureIndex;
|
|
};
|
|
|
|
struct VLAmbientRC
|
|
{
|
|
LightGridRC lightGrid;
|
|
uint32_t materialTextureAIndex;
|
|
uint32_t scatterExtTextureIndex;
|
|
};
|
|
|
|
struct VLRaymarchRC
|
|
{
|
|
uint32_t scatterTextureIndex;
|
|
uint32_t resolveTextureIndex;
|
|
uint32_t materialTextureBIndex;
|
|
};
|
|
|
|
struct VLExtinctionFogRC
|
|
{
|
|
FogVolume fog;
|
|
float time;
|
|
uint32_t extinctionTextureIndex;
|
|
float worldScale;
|
|
};
|
|
|
|
struct VLPointLightShadowRC
|
|
{
|
|
vec3_t lightPosition;
|
|
float extinctionWorldScale;
|
|
float shadowWorldScale;
|
|
uint32_t shadowTextureIndex;
|
|
};
|
|
|
|
struct VLSunlightShadowRC
|
|
{
|
|
uint32_t shadowTextureIndex;
|
|
uint32_t sourceTextureIndex;
|
|
float shadowWorldScale;
|
|
float sourceWorldScale;
|
|
};
|
|
|
|
struct VLPointLightScatterRC
|
|
{
|
|
DynamicLight light;
|
|
uint32_t materialTextureAIndex;
|
|
uint32_t materialTextureBIndex;
|
|
uint32_t scatterExtTextureIndex;
|
|
uint32_t transmittanceTextureIndex;
|
|
uint32_t transmittanceSamplerIndex;
|
|
float shadowWorldScale;
|
|
};
|
|
|
|
struct VLExtinctionVizRC
|
|
{
|
|
vec3_t color;
|
|
float worldScale;
|
|
vec3_t cameraPosition;
|
|
float boxScale; // 1 for full size
|
|
float extinctionScale;
|
|
uint32_t extinctionTextureIndex;
|
|
};
|
|
|
|
struct VLSunShadowVizRC
|
|
{
|
|
vec3_t color;
|
|
float worldScale;
|
|
vec3_t cameraPosition;
|
|
float boxScale; // 1 for full size
|
|
uint32_t shadowTextureIndex;
|
|
};
|
|
|
|
struct VLAmbientVizRC
|
|
{
|
|
LightGridRC lightGrid;
|
|
float sphereScale;
|
|
};
|
|
|
|
struct VLTemporalRC
|
|
{
|
|
uint32_t currTextureIndex;
|
|
uint32_t prevTextureIndex;
|
|
uint32_t prevTextureSamplerIndex;
|
|
float alpha;
|
|
};
|
|
|
|
struct VLParticleClearRC
|
|
{
|
|
uint32_t counterBufferIndex;
|
|
uint32_t tileBufferIndex;
|
|
uint32_t tileCount;
|
|
};
|
|
|
|
struct VLParticleHitRC
|
|
{
|
|
uvec3_t fullResolution;
|
|
uint32_t tileBufferIndex;
|
|
uvec3_t tileResolution;
|
|
uint32_t pad0;
|
|
uvec3_t tileScale;
|
|
uint32_t pad1;
|
|
uint32_t particleBufferIndex;
|
|
uint32_t emitterBufferIndex;
|
|
uint32_t liveBufferIndex;
|
|
uint32_t emitterIndex;
|
|
};
|
|
|
|
struct VLParticleTilesRC
|
|
{
|
|
uint32_t counterBufferIndex;
|
|
uint32_t tileBufferIndex;
|
|
uint32_t tileIndexBufferIndex;
|
|
uint32_t tileCount;
|
|
};
|
|
|
|
struct VLParticleListRC
|
|
{
|
|
uvec3_t fullResolution;
|
|
uint32_t emitterIndex;
|
|
uvec3_t tileResolution;
|
|
uint32_t maxParticleIndexCount;
|
|
uvec3_t tileScale;
|
|
uint32_t tileBufferIndex;
|
|
uint32_t emitterBufferIndex;
|
|
uint32_t particleBufferIndex;
|
|
uint32_t liveBufferIndex;
|
|
uint32_t indexBufferIndex;
|
|
};
|
|
|
|
struct VLParticleInjectionRC
|
|
{
|
|
uvec3_t tileScale;
|
|
uint32_t pad0;
|
|
uvec3_t tileResolution;
|
|
uint32_t particleBufferIndex;
|
|
uint32_t materialTextureAIndex;
|
|
uint32_t materialTextureBIndex;
|
|
uint32_t materialTextureCIndex;
|
|
uint32_t tileBufferIndex;
|
|
uint32_t tileIndexBufferIndex;
|
|
uint32_t particleIndexBufferIndex;
|
|
uint32_t counterBufferIndex;
|
|
uint32_t tileCount;
|
|
};
|
|
|
|
struct NanoVDBTransform
|
|
{
|
|
matrix3x3_t worldToIndex;
|
|
vec3_t originOffset;
|
|
vec3_t translation;
|
|
vec3_t stepSize;
|
|
};
|
|
|
|
struct VLNanoVDBFrustInjectionRC
|
|
{
|
|
NanoVDBTransform transform;
|
|
uint32_t nanovdbBufferIndex;
|
|
uint32_t blackbodyTextureIndex;
|
|
LightGridRC lightGrid;
|
|
uint32_t materialTextureAIndex;
|
|
uint32_t materialTextureBIndex;
|
|
uint32_t materialTextureCIndex;
|
|
uint32_t scatterExtTextureIndex;
|
|
uint32_t frustumVisTextureIndex;
|
|
uint32_t densityGridByteOffset;
|
|
uint32_t flamesGridByteOffset;
|
|
uint32_t densityGridByteOffset2;
|
|
uint32_t flamesGridByteOffset2;
|
|
uint32_t linearInterpolation;
|
|
uint32_t accurateOverlapTest;
|
|
uint32_t ambientAngularCoverage;
|
|
float densityExtinctionScale;
|
|
float densityAlbedo;
|
|
float flamesEmissionScale;
|
|
float flamesTemperatureScale;
|
|
float stepScale;
|
|
float transStepScale;
|
|
float t;
|
|
};
|
|
|
|
struct VLNanoVDBExtInjectionRC
|
|
{
|
|
NanoVDBTransform transform;
|
|
uint32_t nanovdbBufferIndex;
|
|
uint32_t extinctionTextureIndex;
|
|
uint32_t densityGridByteOffset;
|
|
uint32_t densityGridByteOffset2;
|
|
uint32_t linearInterpolation;
|
|
float worldScale;
|
|
float densityExtinctionScale;
|
|
float t;
|
|
};
|
|
|
|
struct VLNanoVDBLightPropRC
|
|
{
|
|
uint32_t materialTextureAIndex;
|
|
uint32_t materialTextureBIndex;
|
|
float emissiveScatter;
|
|
};
|
|
|
|
struct VLFrustumDepthTestRC
|
|
{
|
|
uvec3_t frustumTextureSize;
|
|
uint32_t frustumVisTextureIndex;
|
|
uint32_t depthMip;
|
|
};
|
|
|
|
#pragma pack(pop)
|
|
|
|
const float MaxFogCoordinate = 69420.0f;
|
|
|
|
|
|
static uint32_t ReverseBits32(uint32_t n)
|
|
{
|
|
n = (n << 16) | (n >> 16);
|
|
n = ((n & 0x00ff00ff) << 8) | ((n & 0xff00ff00) >> 8);
|
|
n = ((n & 0x0f0f0f0f) << 4) | ((n & 0xf0f0f0f0) >> 4);
|
|
n = ((n & 0x33333333) << 2) | ((n & 0xcccccccc) >> 2);
|
|
n = ((n & 0x55555555) << 1) | ((n & 0xaaaaaaaa) >> 1);
|
|
|
|
return n;
|
|
}
|
|
|
|
static float RadicalInverseBase2(uint32_t seqIndex)
|
|
{
|
|
return ReverseBits32(seqIndex) * 0x1p-32;
|
|
}
|
|
|
|
static float RadicalInverse(uint64_t seqIndex, uint32_t base)
|
|
{
|
|
const float oneMinusEpsilon = 0x1.fffffep-1;
|
|
const float invBase = 1.0f / (float)base;
|
|
uint32_t reversedDigits = 0;
|
|
float invBaseN = 1.0f;
|
|
while(seqIndex)
|
|
{
|
|
uint32_t next = seqIndex / base;
|
|
uint32_t digit = seqIndex - next * base;
|
|
reversedDigits = reversedDigits * base + digit;
|
|
invBaseN *= invBase;
|
|
seqIndex = next;
|
|
}
|
|
|
|
return fminf(reversedDigits * invBaseN, oneMinusEpsilon);
|
|
}
|
|
|
|
static float VanDerCorputSequence(uint32_t seqIndex)
|
|
{
|
|
return RadicalInverseBase2(seqIndex);
|
|
}
|
|
|
|
static void Halton23Sequence(vec2_t values01, uint32_t seqIndex)
|
|
{
|
|
values01[0] = RadicalInverseBase2(seqIndex);
|
|
values01[1] = RadicalInverse(seqIndex, 3);
|
|
}
|
|
|
|
static float Brightness(const vec3_t color)
|
|
{
|
|
return
|
|
color[0] * 0.299f +
|
|
color[1] * 0.587f +
|
|
color[2] * 0.114f;
|
|
}
|
|
|
|
static void ConvertFog(FogVolume& dst, const VolumetricLight::Fog& src, const VolumetricLight& vl)
|
|
{
|
|
const float scatter = src.albedo * src.extinction / Brightness(src.scatterColor);
|
|
VectorScale(src.scatterColor, scatter, dst.scatter);
|
|
dst.absorption = src.extinction - Brightness(dst.scatter);
|
|
VectorScale(src.emissiveColor, src.emissive, dst.emissive);
|
|
dst.anisotropy = src.anisotropy;
|
|
if(src.isGlobalFog)
|
|
{
|
|
VectorCopy(vl.mapBoxMin, dst.boxMin);
|
|
VectorCopy(vl.mapBoxMax, dst.boxMax);
|
|
}
|
|
else
|
|
{
|
|
for(int a = 0; a < 3; a++)
|
|
{
|
|
dst.boxMin[a] = src.boxCenter[a] - 0.5f * src.boxSize[a];
|
|
dst.boxMax[a] = src.boxCenter[a] + 0.5f * src.boxSize[a];
|
|
}
|
|
}
|
|
dst.noiseMin = 1.0f;
|
|
dst.noiseMax = src.noiseStrength;
|
|
dst.noiseScale = 1.0f / src.noiseSpatialPeriod;
|
|
dst.noiseTimeScale = 1.0f / src.noiseTimePeriod;
|
|
dst.isHeightFog = src.isHeightFog;
|
|
}
|
|
|
|
static void TransformPosition(vec3_t out, const matrix3x3_t m, const vec3_t v)
|
|
{
|
|
out[0] = v[0] * m[0] + v[1] * m[3] + v[2] * m[6];
|
|
out[1] = v[0] * m[1] + v[1] * m[4] + v[2] * m[7];
|
|
out[2] = v[0] * m[2] + v[1] * m[5] + v[2] * m[8];
|
|
}
|
|
|
|
static float VoxelStepSize(const vec3_t dir, const vec3_t voxelSize)
|
|
{
|
|
vec3_t stepSize3;
|
|
for(int i = 0; i < 3; i++)
|
|
{
|
|
stepSize3[i] = voxelSize[i] / max(fabsf(dir[i]), 0.000001f);
|
|
}
|
|
const float stepSize = min(stepSize3[0], min(stepSize3[1], stepSize3[2]));
|
|
|
|
return stepSize;
|
|
}
|
|
|
|
static const float OpaqueTransmittanceThreshold = 1.0f / 256.0f;
|
|
static const float LnOpaqueTransmittanceThreshold = logf(OpaqueTransmittanceThreshold);
|
|
|
|
static float OpaqueDistanceToExtinction(float opaqueDistance)
|
|
{
|
|
return -LnOpaqueTransmittanceThreshold / opaqueDistance;
|
|
}
|
|
|
|
static float ExtinctionToOpaqueDistance(float extinction)
|
|
{
|
|
return -LnOpaqueTransmittanceThreshold / extinction;
|
|
}
|
|
|
|
|
|
void VolumetricLight::Init()
|
|
{
|
|
if(srp.firstInit)
|
|
{
|
|
VectorSet(ambientColorGUI, 1.0f, 1.0f, 1.0f);
|
|
VectorSet(ambientColor, 0.5f, 0.5f, 0.5f);
|
|
ambientIntensity = 0.1f;
|
|
}
|
|
|
|
int xyDivisor = 1;
|
|
for(int i = 0; i < xySubsampling; i++)
|
|
{
|
|
xyDivisor *= 2;
|
|
}
|
|
|
|
VectorSet(mapBoxMin, -MaxFogCoordinate, -MaxFogCoordinate, -MaxFogCoordinate); // patched on world load
|
|
VectorSet(mapBoxMax, MaxFogCoordinate, MaxFogCoordinate, MaxFogCoordinate);
|
|
VectorSet(frustumSize, glConfig.vidWidth / xyDivisor, glConfig.vidHeight / xyDivisor, zResolution);
|
|
depthMip = (uint32_t)xySubsampling;
|
|
VectorSet(frustumTileScale, 8, 8, 8); // x*y*z == 512, must match the shader
|
|
VectorSet(frustumTileSize,
|
|
(frustumSize[0] + frustumTileScale[0] - 1) / frustumTileScale[0],
|
|
(frustumSize[1] + frustumTileScale[1] - 1) / frustumTileScale[1],
|
|
(frustumSize[2] + frustumTileScale[2] - 1) / frustumTileScale[2]);
|
|
VectorSet(extinctionSize, extinctionResolution, extinctionResolution, extinctionResolution);
|
|
VectorSet(extinctionTileScale, 8, 8, 8); // 8*8*8 == 512, must match the shader
|
|
VectorSet(extinctionTileSize,
|
|
(extinctionSize[0] + extinctionTileScale[0] - 1) / extinctionTileScale[0],
|
|
(extinctionSize[1] + extinctionTileScale[1] - 1) / extinctionTileScale[1],
|
|
(extinctionSize[2] + extinctionTileScale[2] - 1) / extinctionTileScale[2]);
|
|
Vector4Set(extinctionVolumeScale, 8, 16, 32, 64); // patched on world load
|
|
VectorSet(sunShadowSize, sunShadowResolution, sunShadowResolution, sunShadowResolution);
|
|
Vector4Set(sunShadowVolumeScale, 8, 16, 32, 64); // patched on world load
|
|
shadowPixelCount = pointShadowResolution;
|
|
pointShadowVolumeScale = 8.0f;
|
|
jitterCounter = 0;
|
|
|
|
extinctionFogPipeline = CreateComputePipeline("VL Extinction Fog", ShaderByteCode(g_vl_extinction_injection_fog_cs));
|
|
extinctionVDBPipeline = CreateComputePipeline("VL Extinction VDB", ShaderByteCode(g_vl_extinction_injection_nanovdb_cs));
|
|
frustumAmbientPipeline = CreateComputePipeline("VL Frustum Ambient Light Scatter", ShaderByteCode(g_vl_frustum_inscatter_ambient_cs));
|
|
frustumAnisotropyPipeline = CreateComputePipeline("VL Frustum Finalize Material", ShaderByteCode(g_vl_frustum_anisotropy_average_cs));
|
|
frustumFogPipeline = CreateComputePipeline("VL Frustum Fog", ShaderByteCode(g_vl_frustum_injection_fog_cs));
|
|
frustumLightPropNXPipeline = CreateComputePipeline("VL Frustum Light Prop -X", ShaderByteCode(g_vl_frustum_light_propagation_nx_cs));
|
|
frustumLightPropNYPipeline = CreateComputePipeline("VL Frustum Light Prop -Y", ShaderByteCode(g_vl_frustum_light_propagation_ny_cs));
|
|
frustumLightPropPXPipeline = CreateComputePipeline("VL Frustum Light Prop +X", ShaderByteCode(g_vl_frustum_light_propagation_px_cs));
|
|
frustumLightPropPYPipeline = CreateComputePipeline("VL Frustum Light Prop +Y", ShaderByteCode(g_vl_frustum_light_propagation_py_cs));
|
|
frustumParticlePipeline = CreateComputePipeline("VL Frustum Particles", ShaderByteCode(g_vl_frustum_injection_particles_cs));
|
|
frustumRaymarchPipeline = CreateComputePipeline("VL Frustum Raymarch", ShaderByteCode(g_vl_frustum_raymarch_cs));
|
|
frustumTemporalFloatPipeline = CreateComputePipeline("VL Frustum Temporal Reprojection float", ShaderByteCode(g_vl_frustum_temporal_float_cs));
|
|
frustumTemporalFloat4Pipeline = CreateComputePipeline("VL Frustum Temporal Reprojection float4", ShaderByteCode(g_vl_frustum_temporal_float4_cs));
|
|
frustumVDBLQPipeline = CreateComputePipeline("VL Frustum VDB LQ", ShaderByteCode(g_vl_frustum_injection_nanovdb_lq_cs));
|
|
if(rhiInfo.forceNanoVDBPreviewMode)
|
|
{
|
|
frustumVDBPipeline = frustumVDBLQPipeline;
|
|
}
|
|
else
|
|
{
|
|
frustumVDBPipeline = CreateComputePipeline("VL Frustum VDB", ShaderByteCode(g_vl_frustum_injection_nanovdb_cs));
|
|
}
|
|
frustumDepthTestPipeline = CreateComputePipeline("VL Frustum Z Test", ShaderByteCode(g_vl_frustum_depth_test_cs));
|
|
particleClearPipeline = CreateComputePipeline("VL Particle Clear", ShaderByteCode(g_vl_particles_clear_cs));
|
|
particleHitPipeline = CreateComputePipeline("VL Particle Hit", ShaderByteCode(g_vl_particles_hit_cs));
|
|
particleListPipeline = CreateComputePipeline("VL Particle List Build", ShaderByteCode(g_vl_particles_list_cs));
|
|
particleTilesPipeline = CreateComputePipeline("VL Particle Tile List Build", ShaderByteCode(g_vl_particles_tiles_cs));
|
|
pointLightShadowPipeline = CreateComputePipeline("VL Shadow Raymarch Point Light", ShaderByteCode(g_vl_shadow_point_light_cs));
|
|
sunlightScatterPipeline = CreateComputePipeline("VL Frustum Sunlight Scatter", ShaderByteCode(g_vl_frustum_inscatter_sunlight_cs));
|
|
sunlightShadowPipeline = CreateComputePipeline("VL Shadow Raymarch Sun", ShaderByteCode(g_vl_shadow_sun_cs));
|
|
if(rhiInfo.hasInlineRaytracing)
|
|
{
|
|
frustumSunlightVisPipeline = CreateComputePipeline("VL Frustum Sunlight Visibility", ShaderByteCode(g_vl_frustum_sunlight_visibility_cs));
|
|
frustumPointLightScatterPipeline = CreateComputePipeline("VL Frustum Point Light Scatter", ShaderByteCode(g_vl_frustum_inscatter_point_light_cs));
|
|
}
|
|
else
|
|
{
|
|
frustumSunlightVisPipeline = RHI_MAKE_NULL_HANDLE();
|
|
frustumPointLightScatterPipeline = RHI_MAKE_NULL_HANDLE();
|
|
}
|
|
|
|
{
|
|
GraphicsPipelineDesc desc("VL Extinction Viz");
|
|
desc.shortLifeTime = true;
|
|
desc.rootSignature = RHI_MAKE_NULL_HANDLE();
|
|
desc.vertexShader.Set(g_vl_debug_extinction_vs);
|
|
desc.pixelShader.Set(g_vl_debug_extinction_ps);
|
|
desc.depthStencil.depthStencilFormat = TextureFormat::Depth32_Float;
|
|
desc.depthStencil.depthComparison = ComparisonFunction::GreaterEqual;
|
|
desc.depthStencil.enableDepthTest = true;
|
|
desc.depthStencil.enableDepthWrites = true;
|
|
desc.rasterizer.cullMode = CT_BACK_SIDED;
|
|
desc.AddRenderTarget(0, crp.renderTargetFormat);
|
|
extinctionVizPipeline = CreateGraphicsPipeline(desc);
|
|
}
|
|
|
|
{
|
|
GraphicsPipelineDesc desc("VL Sun Shadow Viz");
|
|
desc.shortLifeTime = true;
|
|
desc.rootSignature = RHI_MAKE_NULL_HANDLE();
|
|
desc.vertexShader.Set(g_vl_debug_shadow_sun_vs);
|
|
desc.pixelShader.Set(g_vl_debug_shadow_sun_ps);
|
|
desc.depthStencil.depthStencilFormat = TextureFormat::Depth32_Float;
|
|
desc.depthStencil.depthComparison = ComparisonFunction::GreaterEqual;
|
|
desc.depthStencil.enableDepthTest = true;
|
|
desc.depthStencil.enableDepthWrites = true;
|
|
desc.rasterizer.cullMode = CT_BACK_SIDED;
|
|
desc.AddRenderTarget(0, crp.renderTargetFormat);
|
|
sunShadowVizPipeline = CreateGraphicsPipeline(desc);
|
|
}
|
|
|
|
{
|
|
GraphicsPipelineDesc desc("VL Ambient Viz");
|
|
desc.shortLifeTime = true;
|
|
desc.rootSignature = RHI_MAKE_NULL_HANDLE();
|
|
desc.vertexShader.Set(g_vl_debug_ambient_vs);
|
|
desc.pixelShader.Set(g_vl_debug_ambient_ps);
|
|
desc.depthStencil.depthStencilFormat = TextureFormat::Depth32_Float;
|
|
desc.depthStencil.depthComparison = ComparisonFunction::GreaterEqual;
|
|
desc.depthStencil.enableDepthTest = true;
|
|
desc.depthStencil.enableDepthWrites = true;
|
|
desc.rasterizer.cullMode = CT_FRONT_SIDED;
|
|
desc.AddRenderTarget(0, crp.renderTargetFormat);
|
|
ambientVizPipeline = CreateGraphicsPipeline(desc);
|
|
}
|
|
|
|
{
|
|
TextureDesc desc("VL", frustumSize[0], frustumSize[1]);
|
|
desc.shortLifeTime = true;
|
|
desc.committedResource = true;
|
|
desc.initialState = ResourceStates::UnorderedAccessBit;
|
|
desc.allowedState = ResourceStates::UnorderedAccessBit | ResourceStates::ComputeShaderAccessBit | ResourceStates::PixelShaderAccessBit;
|
|
desc.depth = frustumSize[2];
|
|
|
|
desc.name = "VL scatter/absorption";
|
|
desc.format = TextureFormat::R16G16B16A16_Float;
|
|
materialTextureA = CreateTexture(desc);
|
|
|
|
desc.name = "VL emissive/anisotropy";
|
|
desc.format = TextureFormat::R16G16B16A16_Float;
|
|
materialTextureB = CreateTexture(desc);
|
|
|
|
desc.name = "VL anisotropy counter";
|
|
desc.format = TextureFormat::R16_Float;
|
|
materialTextureC = CreateTexture(desc);
|
|
|
|
desc.name = "VL sunlight vis";
|
|
desc.format = TextureFormat::R8_UNorm;
|
|
sunlightVisTexture = CreateTexture(desc);
|
|
|
|
desc.name = "VL sunlight vis temporal";
|
|
desc.format = TextureFormat::R8_UNorm;
|
|
prevSunlightVisTexture = CreateTexture(desc);
|
|
|
|
desc.name = "VL in-scatter/ext";
|
|
desc.format = TextureFormat::R16G16B16A16_Float;
|
|
scatterExtTexture = CreateTexture(desc);
|
|
|
|
desc.name = "VL in-scatter/trans";
|
|
desc.format = TextureFormat::R16G16B16A16_Float;
|
|
scatterTransTexture = CreateTexture(desc);
|
|
|
|
desc.width = extinctionSize[0];
|
|
desc.height = extinctionSize[1];
|
|
desc.depth = extinctionSize[2];
|
|
desc.format = TextureFormat::R16_Float;
|
|
for(int i = 0; i < 4; i++)
|
|
{
|
|
desc.name = va("VL extinction #%d", i + 1);
|
|
extinctionTextures[i] = CreateTexture(desc);
|
|
}
|
|
|
|
desc.width = sunShadowSize[0];
|
|
desc.height = sunShadowSize[1];
|
|
desc.depth = sunShadowSize[2];
|
|
desc.format = TextureFormat::R16_Float;
|
|
for(int i = 0; i < 4; i++)
|
|
{
|
|
desc.name = va("VL sun shadow #%d", i + 1);
|
|
sunShadowTextures[i] = CreateTexture(desc);
|
|
}
|
|
|
|
desc.width = shadowPixelCount;
|
|
desc.height = shadowPixelCount;
|
|
desc.depth = shadowPixelCount;
|
|
desc.name = "VL point light shadow";
|
|
desc.format = TextureFormat::R16_Float;
|
|
pointShadowTexture = CreateTexture(desc);
|
|
|
|
desc.width = (glConfig.vidWidth + xyDivisor - 1) / xyDivisor;
|
|
desc.height = (glConfig.vidHeight + xyDivisor - 1) / xyDivisor;
|
|
desc.depth = 1;
|
|
desc.name = "VL frustum visibility";
|
|
desc.format = TextureFormat::R16_UInt;
|
|
frustumVisTexture = CreateTexture(desc);
|
|
|
|
ambientLightTextureA = RHI_MAKE_NULL_HANDLE(); // created on world load when available
|
|
ambientLightTextureB = RHI_MAKE_NULL_HANDLE(); // created on world load when available
|
|
}
|
|
|
|
const uint32_t tileCountF = frustumTileSize[0] * frustumTileSize[1] * frustumTileSize[2];
|
|
const uint32_t tileCountE = extinctionTileSize[0] * extinctionTileSize[1] * extinctionTileSize[2];
|
|
const uint32_t maxTileCount = max(tileCountF, tileCountE);
|
|
|
|
{
|
|
const uint32_t tileSize = sizeof(Tile);
|
|
const uint32_t byteCount = maxTileCount * tileSize;
|
|
BufferDesc desc("VL particle voxel tile", byteCount, ResourceStates::UnorderedAccessBit);
|
|
desc.shortLifeTime = true;
|
|
desc.structureByteCount = tileSize;
|
|
particleTileBuffer = CreateBuffer(desc);
|
|
}
|
|
|
|
{
|
|
BufferDesc desc("VL particle global counters", sizeof(Counters), ResourceStates::UnorderedAccessBit);
|
|
desc.shortLifeTime = true;
|
|
desc.structureByteCount = sizeof(Counters);
|
|
particleCounterBuffer = CreateBuffer(desc);
|
|
}
|
|
|
|
{
|
|
BufferDesc desc("VL particle tile index", maxTileCount * 4, ResourceStates::UnorderedAccessBit);
|
|
desc.shortLifeTime = true;
|
|
desc.structureByteCount = 4;
|
|
particleTileIndexBuffer = CreateBuffer(desc);
|
|
}
|
|
|
|
{
|
|
const uint32_t Magic = 64; // @TODO: adjust or use particle groups
|
|
BufferDesc desc("VL particle index", Magic * MAX_PARTICLES * 4, ResourceStates::UnorderedAccessBit);
|
|
desc.shortLifeTime = true;
|
|
desc.structureByteCount = 4;
|
|
particleIndexBuffer = CreateBuffer(desc);
|
|
maxParticleIndexCount = Magic * MAX_PARTICLES;
|
|
}
|
|
|
|
{
|
|
BufferDesc desc("VL particle dispatch", 12, ResourceStates::UnorderedAccessBit);
|
|
desc.shortLifeTime = true;
|
|
particleDispatchBuffer = CreateBuffer(desc);
|
|
|
|
uint32_t* const mapped = (uint32_t*)BeginBufferUpload(particleDispatchBuffer);
|
|
mapped[0] = 0;
|
|
mapped[1] = 1;
|
|
mapped[2] = 1;
|
|
EndBufferUpload(particleDispatchBuffer);
|
|
}
|
|
}
|
|
|
|
void VolumetricLight::ProcessWorld(world_t& world)
|
|
{
|
|
Q_assert(world.nodes != NULL);
|
|
Q_assert(world.numnodes > 0);
|
|
if(world.nodes == NULL || world.numnodes <= 0)
|
|
{
|
|
VectorSet(mapBoxMin, -MaxFogCoordinate, -MaxFogCoordinate, -MaxFogCoordinate);
|
|
VectorSet(mapBoxMax, MaxFogCoordinate, MaxFogCoordinate, MaxFogCoordinate);
|
|
return;
|
|
}
|
|
|
|
const mnode_t& node = world.nodes[0];
|
|
vec3_t mapDimensions;
|
|
VectorSubtract(node.maxs, node.mins, mapDimensions);
|
|
|
|
VectorCopy(node.mins, mapBoxMin);
|
|
VectorCopy(node.maxs, mapBoxMax);
|
|
|
|
if(world.lightGridData != NULL)
|
|
{
|
|
for(int i = 0; i < 3; i++)
|
|
{
|
|
lightGridCenter[i] =
|
|
world.lightGridOrigin[i] +
|
|
(world.lightGridBounds[i] * 0.5f - 0.5f) * world.lightGridSize[i];
|
|
}
|
|
|
|
TextureDesc desc("VL", world.lightGridBounds[0], world.lightGridBounds[1]);
|
|
desc.shortLifeTime = true;
|
|
desc.committedResource = true;
|
|
desc.initialState = ResourceStates::ComputeShaderAccessBit;
|
|
desc.allowedState = ResourceStates::ComputeShaderAccessBit | ResourceStates::PixelShaderAccessBit;
|
|
desc.depth = world.lightGridBounds[2];
|
|
desc.format = TextureFormat::R8G8B8A8_UNorm;
|
|
desc.name = "VL ambient light A";
|
|
ambientLightTextureA = CreateTexture(desc);
|
|
desc.name = "VL ambient light B";
|
|
ambientLightTextureB = CreateTexture(desc);
|
|
|
|
MappedTexture texture;
|
|
BeginTextureUpload(texture, ambientLightTextureA);
|
|
const uint32_t rowCount = texture.rowCount * texture.sliceCount;
|
|
const uint32_t columnCount = texture.columnCount;
|
|
const uint32_t srcRowByteCount = world.lightGridBounds[0] * 8;
|
|
for(uint32_t r = 0; r < rowCount; r++)
|
|
{
|
|
uint32_t* dst = (uint32_t*)(texture.mappedData + r * texture.dstRowByteCount);
|
|
const uint32_t* src = (const uint32_t*)(world.lightGridData + r * srcRowByteCount);
|
|
for(uint32_t c = 0; c < columnCount; c++)
|
|
{
|
|
*dst = src[0];
|
|
dst += 1;
|
|
src += 2;
|
|
}
|
|
}
|
|
EndTextureUpload();
|
|
|
|
BeginTextureUpload(texture, ambientLightTextureB);
|
|
for(uint32_t r = 0; r < rowCount; r++)
|
|
{
|
|
uint32_t* dst = (uint32_t*)(texture.mappedData + r * texture.dstRowByteCount);
|
|
const uint32_t* src = (const uint32_t*)(world.lightGridData + r * srcRowByteCount);
|
|
for(uint32_t c = 0; c < columnCount; c++)
|
|
{
|
|
*dst = src[1];
|
|
dst += 1;
|
|
src += 2;
|
|
}
|
|
}
|
|
EndTextureUpload();
|
|
}
|
|
|
|
{
|
|
const float largest = max(max(mapDimensions[0], mapDimensions[1]), mapDimensions[2]);
|
|
const float scale3 = largest / (float)extinctionSize[0];
|
|
const float scale2 = extinctionVolumeScale[2];
|
|
float downScale = 1.0f;
|
|
while(scale3 <= 1.5f * scale2 * downScale)
|
|
{
|
|
downScale *= 0.5f;
|
|
}
|
|
extinctionVolumeScale[0] *= downScale;
|
|
extinctionVolumeScale[1] *= downScale;
|
|
extinctionVolumeScale[2] *= downScale;
|
|
extinctionVolumeScale[3] = scale3;
|
|
}
|
|
|
|
{
|
|
const float length = VectorLength(mapDimensions);
|
|
const float scale3 = length / (float)sunShadowSize[0];
|
|
const float scale2 = sunShadowVolumeScale[2];
|
|
float downScale = 1.0f;
|
|
while(scale3 <= 1.5f * scale2 * downScale)
|
|
{
|
|
downScale *= 0.5f;
|
|
}
|
|
sunShadowVolumeScale[0] *= downScale;
|
|
sunShadowVolumeScale[1] *= downScale;
|
|
sunShadowVolumeScale[2] *= downScale;
|
|
sunShadowVolumeScale[3] = scale3;
|
|
}
|
|
|
|
if(!LoadFogFile(va("fogs/%s.fogs", world.baseName)))
|
|
{
|
|
fogCount = 0;
|
|
|
|
// @NOTE: fog 0 is invalid
|
|
for(int f = 1; f < world.numfogs && fogCount < ARRAY_LEN(fogs); f++)
|
|
{
|
|
const float transmittanceThreshold = 1.0 / 256.0f;
|
|
const float lnThreshold = logf(transmittanceThreshold);
|
|
const fog_t& q3fog = world.fogs[f];
|
|
const float distance = q3fog.parms.depthForOpaque;
|
|
const float extinction = -lnThreshold / distance;
|
|
|
|
Fog& fog = fogs[fogCount++];
|
|
fog = {};
|
|
for(int a = 0; a < 3; a++)
|
|
{
|
|
fog.boxCenter[a] = 0.5f * (q3fog.bounds[0][a] + q3fog.bounds[1][a]);
|
|
fog.boxSize[a] = q3fog.bounds[1][a] - q3fog.bounds[0][a];
|
|
}
|
|
fog.extinction = extinction;
|
|
fog.albedo = 0.75f;
|
|
VectorCopy(q3fog.parms.color, fog.scatterColor);
|
|
fog.emissive = 0.0f;
|
|
VectorSet(fog.emissiveColor, 1, 1, 1);
|
|
fog.anisotropy = 0.0f;
|
|
fog.noiseStrength = 2.0f;
|
|
fog.noiseSpatialPeriod = 128.0f;
|
|
fog.noiseTimePeriod = 8.0f;
|
|
}
|
|
}
|
|
}
|
|
|
|
void VolumetricLight::DrawBegin()
|
|
{
|
|
{
|
|
SCOPED_RENDER_PASS("VL Clear", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdTextureBarrier(materialTextureA, ResourceStates::UnorderedAccessBit);
|
|
CmdTextureBarrier(materialTextureB, ResourceStates::UnorderedAccessBit);
|
|
CmdTextureBarrier(materialTextureC, ResourceStates::UnorderedAccessBit);
|
|
CmdTextureBarrier(scatterExtTexture, ResourceStates::UnorderedAccessBit);
|
|
for(int i = 0; i < 4; i++)
|
|
{
|
|
CmdTextureBarrier(extinctionTextures[i], ResourceStates::UnorderedAccessBit);
|
|
}
|
|
CmdEndBarrier();
|
|
|
|
const uint32_t values[4] = {};
|
|
CmdClearTextureUAV(materialTextureA, 0, values);
|
|
CmdClearTextureUAV(materialTextureB, 0, values);
|
|
CmdClearTextureUAV(materialTextureC, 0, values);
|
|
CmdClearTextureUAV(scatterExtTexture, 0, values);
|
|
for(int i = 0; i < 4; i++)
|
|
{
|
|
CmdClearTextureUAV(extinctionTextures[i], 0, values);
|
|
}
|
|
}
|
|
|
|
{
|
|
SCOPED_RENDER_PASS("VL Frustum Depth", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdTextureBarrier(crp.depthMinMaxTexture, ResourceStates::ComputeShaderAccessBit);
|
|
CmdTextureBarrier(frustumVisTexture, ResourceStates::UnorderedAccessBit);
|
|
CmdEndBarrier();
|
|
|
|
VLFrustumDepthTestRC rc = {};
|
|
rc.frustumVisTextureIndex = GetTextureIndexUAV(frustumVisTexture, 0);
|
|
rc.depthMip = depthMip;
|
|
VectorCopy(frustumSize, rc.frustumTextureSize);
|
|
|
|
CmdBindPipeline(frustumDepthTestPipeline);
|
|
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
|
|
CmdDispatch((frustumSize[0] + 7) / 8, (frustumSize[1] + 7) / 8, 1);
|
|
}
|
|
|
|
{
|
|
SCOPED_RENDER_PASS("VL Fog", 1.0f, 1.0f, 1.0f);
|
|
|
|
for(int f = 0; f < fogCount; f++)
|
|
{
|
|
SCOPED_DEBUG_LABEL("Frustum", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdTextureBarrier(materialTextureA, ResourceStates::UnorderedAccessBit);
|
|
CmdTextureBarrier(materialTextureB, ResourceStates::UnorderedAccessBit);
|
|
CmdTextureBarrier(materialTextureC, ResourceStates::UnorderedAccessBit);
|
|
CmdEndBarrier();
|
|
|
|
VLGlobalFogRC rc = {};
|
|
ConvertFog(rc.fog, fogs[f], *this);
|
|
rc.time = backEnd.refdef.floatTime;
|
|
rc.materialTextureAIndex = GetTextureIndexUAV(materialTextureA, 0);
|
|
rc.materialTextureBIndex = GetTextureIndexUAV(materialTextureB, 0);
|
|
rc.materialTextureCIndex = GetTextureIndexUAV(materialTextureC, 0);
|
|
|
|
CmdBindPipeline(frustumFogPipeline);
|
|
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
|
|
CmdDispatch((frustumSize[0] + 3) / 4, (frustumSize[1] + 3) / 4, (frustumSize[2] + 3) / 4);
|
|
}
|
|
|
|
for(int f = 0; f < fogCount; f++)
|
|
{
|
|
VLExtinctionFogRC rc = {};
|
|
ConvertFog(rc.fog, fogs[f], *this);
|
|
rc.time = backEnd.refdef.floatTime;
|
|
|
|
for(int c = 0; c < 4; c++)
|
|
{
|
|
SCOPED_DEBUG_LABEL("Extinction", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdTextureBarrier(extinctionTextures[c], ResourceStates::UnorderedAccessBit);
|
|
CmdEndBarrier();
|
|
|
|
rc.extinctionTextureIndex = GetTextureIndexUAV(extinctionTextures[c], 0);
|
|
rc.worldScale = extinctionVolumeScale[c];
|
|
|
|
CmdBindPipeline(extinctionFogPipeline);
|
|
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
|
|
CmdDispatch((extinctionSize[0] + 3) / 4, (extinctionSize[1] + 3) / 4, (extinctionSize[2] + 3) / 4);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(0) // @TODO:
|
|
{
|
|
SCOPED_RENDER_PASS("VL Frustum Particles", 1.0f, 1.0f, 1.0f);
|
|
|
|
const ParticleSystem& ps = crp.particleSystem;
|
|
const uint32_t tileCount = frustumTileSize[0] * frustumTileSize[1] * frustumTileSize[2];
|
|
|
|
{
|
|
SCOPED_DEBUG_LABEL("Clear", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdBufferBarrier(particleCounterBuffer, ResourceStates::UnorderedAccessBit);
|
|
CmdBufferBarrier(particleTileBuffer, ResourceStates::UnorderedAccessBit);
|
|
CmdEndBarrier();
|
|
|
|
VLParticleClearRC rc = {};
|
|
rc.counterBufferIndex = GetBufferIndexUAV(particleCounterBuffer);
|
|
rc.tileBufferIndex = GetBufferIndexUAV(particleTileBuffer);
|
|
rc.tileCount = tileCount;
|
|
|
|
CmdBindPipeline(particleClearPipeline);
|
|
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
|
|
CmdDispatch((tileCount + 63) / 64, 1, 1);
|
|
}
|
|
|
|
{
|
|
SCOPED_DEBUG_LABEL("Hit", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdBufferBarrier(ps.indirectBuffer, ResourceStates::IndirectDispatchBit);
|
|
CmdBufferBarrier(ps.particleBuffer, ResourceStates::UnorderedAccessBit);
|
|
CmdBufferBarrier(ps.emitterBuffer, ResourceStates::UnorderedAccessBit);
|
|
CmdBufferBarrier(ps.liveBuffers[ps.liveBufferReadIndex], ResourceStates::UnorderedAccessBit);
|
|
CmdBufferBarrier(particleTileBuffer, ResourceStates::UnorderedAccessBit);
|
|
CmdEndBarrier();
|
|
|
|
VLParticleHitRC rc = {};
|
|
rc.particleBufferIndex = GetBufferIndexUAV(ps.particleBuffer);
|
|
rc.emitterBufferIndex = GetBufferIndexUAV(ps.emitterBuffer);
|
|
rc.liveBufferIndex = GetBufferIndexUAV(ps.liveBuffers[ps.liveBufferReadIndex]);
|
|
rc.emitterIndex = 0; // @TODO:
|
|
rc.tileBufferIndex = GetBufferIndexUAV(particleTileBuffer);
|
|
VectorCopy(frustumSize, rc.fullResolution);
|
|
VectorCopy(frustumTileSize, rc.tileResolution);
|
|
VectorCopy(frustumTileScale, rc.tileScale);
|
|
|
|
CmdBindPipeline(particleHitPipeline);
|
|
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
|
|
CmdDispatchIndirect(ps.indirectBuffer, 12);
|
|
}
|
|
|
|
{
|
|
SCOPED_DEBUG_LABEL("Tile List Build", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdBufferBarrier(particleCounterBuffer, ResourceStates::UnorderedAccessBit);
|
|
CmdBufferBarrier(particleTileBuffer, ResourceStates::UnorderedAccessBit);
|
|
CmdBufferBarrier(particleTileIndexBuffer, ResourceStates::UnorderedAccessBit);
|
|
CmdEndBarrier();
|
|
|
|
VLParticleTilesRC rc = {};
|
|
rc.counterBufferIndex = GetBufferIndexUAV(particleCounterBuffer);
|
|
rc.tileBufferIndex = GetBufferIndexUAV(particleTileBuffer);
|
|
rc.tileIndexBufferIndex = GetBufferIndexUAV(particleTileIndexBuffer);
|
|
rc.tileCount = tileCount;
|
|
|
|
CmdBindPipeline(particleTilesPipeline);
|
|
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
|
|
CmdDispatch((tileCount + 63) / 64, 1, 1);
|
|
}
|
|
|
|
{
|
|
SCOPED_DEBUG_LABEL("Particle List Build", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdBufferBarrier(ps.indirectBuffer, ResourceStates::IndirectDispatchBit);
|
|
CmdBufferBarrier(ps.particleBuffer, ResourceStates::UnorderedAccessBit);
|
|
CmdBufferBarrier(ps.emitterBuffer, ResourceStates::UnorderedAccessBit);
|
|
CmdBufferBarrier(ps.liveBuffers[ps.liveBufferReadIndex], ResourceStates::UnorderedAccessBit);
|
|
CmdBufferBarrier(particleTileBuffer, ResourceStates::UnorderedAccessBit);
|
|
CmdBufferBarrier(particleIndexBuffer, ResourceStates::UnorderedAccessBit);
|
|
CmdEndBarrier();
|
|
|
|
VLParticleListRC rc = {};
|
|
rc.emitterBufferIndex = GetBufferIndexUAV(ps.emitterBuffer);
|
|
rc.indexBufferIndex = GetBufferIndexUAV(particleIndexBuffer);
|
|
rc.liveBufferIndex = GetBufferIndexUAV(ps.liveBuffers[ps.liveBufferReadIndex]);
|
|
rc.particleBufferIndex = GetBufferIndexUAV(ps.particleBuffer);
|
|
rc.tileBufferIndex = GetBufferIndexUAV(particleTileBuffer);
|
|
rc.emitterIndex = 0; // @TODO:
|
|
rc.maxParticleIndexCount = maxParticleIndexCount;
|
|
VectorCopy(frustumSize, rc.fullResolution);
|
|
VectorCopy(frustumTileSize, rc.tileResolution);
|
|
VectorCopy(frustumTileScale, rc.tileScale);
|
|
|
|
CmdBindPipeline(particleListPipeline);
|
|
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
|
|
CmdDispatchIndirect(ps.indirectBuffer, 12);
|
|
}
|
|
|
|
{
|
|
SCOPED_DEBUG_LABEL("Set Dispatch Tile Count", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdBufferBarrier(particleCounterBuffer, ResourceStates::CopySourceBit);
|
|
CmdBufferBarrier(particleDispatchBuffer, ResourceStates::CopyDestinationBit);
|
|
CmdEndBarrier();
|
|
|
|
CmdCopyBuffer(particleDispatchBuffer, 0, particleCounterBuffer, 4, 4);
|
|
}
|
|
|
|
{
|
|
SCOPED_DEBUG_LABEL("Injection", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdBufferBarrier(ps.particleBuffer, ResourceStates::UnorderedAccessBit);
|
|
CmdBufferBarrier(particleTileBuffer, ResourceStates::UnorderedAccessBit);
|
|
CmdBufferBarrier(particleTileIndexBuffer, ResourceStates::UnorderedAccessBit);
|
|
CmdBufferBarrier(particleIndexBuffer, ResourceStates::UnorderedAccessBit);
|
|
CmdBufferBarrier(particleCounterBuffer, ResourceStates::UnorderedAccessBit);
|
|
CmdTextureBarrier(materialTextureA, ResourceStates::UnorderedAccessBit);
|
|
CmdTextureBarrier(materialTextureB, ResourceStates::UnorderedAccessBit);
|
|
CmdTextureBarrier(materialTextureC, ResourceStates::UnorderedAccessBit);
|
|
CmdBufferBarrier(particleDispatchBuffer, ResourceStates::IndirectDispatchBit);
|
|
CmdEndBarrier();
|
|
|
|
VLParticleInjectionRC rc = {};
|
|
rc.particleBufferIndex = GetBufferIndexUAV(ps.particleBuffer);
|
|
rc.materialTextureAIndex = GetTextureIndexUAV(materialTextureA, 0);
|
|
rc.materialTextureBIndex = GetTextureIndexUAV(materialTextureB, 0);
|
|
rc.materialTextureCIndex = GetTextureIndexUAV(materialTextureC, 0);
|
|
rc.tileBufferIndex = GetBufferIndexUAV(particleTileBuffer);
|
|
rc.tileIndexBufferIndex = GetBufferIndexUAV(particleTileIndexBuffer);
|
|
rc.particleIndexBufferIndex = GetBufferIndexUAV(particleIndexBuffer);
|
|
rc.counterBufferIndex = GetBufferIndexUAV(particleCounterBuffer);
|
|
rc.tileCount = frustumTileSize[0] * frustumTileSize[1] * frustumTileSize[2];
|
|
VectorCopy(frustumTileScale, rc.tileScale);
|
|
VectorCopy(frustumTileSize, rc.tileResolution);
|
|
|
|
CmdBindPipeline(frustumParticlePipeline);
|
|
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
|
|
CmdDispatchIndirect(particleDispatchBuffer, 0);
|
|
}
|
|
}
|
|
|
|
// NanoVDB: integrated GPUs are too slow and will trigger a TDR
|
|
if(!rhiInfo.isUMA)
|
|
{
|
|
SCOPED_RENDER_PASS("VL NanoVDB", 1.0f, 1.0f, 1.0f);
|
|
|
|
NanoVDBManager& vdb = crp.vdbManager;
|
|
|
|
for(uint32_t i = 0; i < vdb.drawInstances.count; i++)
|
|
{
|
|
const NanoVDBManager::Instance& instance = vdb.instances[i];
|
|
const NanoVDBManager::DrawInstance& drawInstance = vdb.drawInstances[i];
|
|
|
|
NanoVDBTransform transform = {};
|
|
vdb.MakeWorldToIndexMatrix(transform.worldToIndex, instance);
|
|
VectorCopy(instance.originOffset, transform.originOffset);
|
|
VectorCopy(instance.position, transform.translation);
|
|
|
|
if(drawInstance.smokeByteOffset > 0 || drawInstance.fireByteOffset > 0)
|
|
{
|
|
SCOPED_DEBUG_LABEL("Frustum", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdBufferBarrier(drawInstance.buffer, ResourceStates::ComputeShaderAccessBit);
|
|
if(tr.world->lightGridData != NULL)
|
|
{
|
|
CmdTextureBarrier(ambientLightTextureA, ResourceStates::ComputeShaderAccessBit);
|
|
CmdTextureBarrier(ambientLightTextureB, ResourceStates::ComputeShaderAccessBit);
|
|
}
|
|
CmdTextureBarrier(materialTextureA, ResourceStates::UnorderedAccessBit);
|
|
CmdTextureBarrier(materialTextureB, ResourceStates::UnorderedAccessBit);
|
|
CmdTextureBarrier(materialTextureC, ResourceStates::UnorderedAccessBit);
|
|
CmdEndBarrier();
|
|
|
|
// order change: fw, left, up -> left, up, fw
|
|
const orientationr_t& orient = backEnd.viewParms.orient;
|
|
vec3_t forward, left, up;
|
|
TransformPosition(forward, transform.worldToIndex, orient.axis[0]);
|
|
TransformPosition(left, transform.worldToIndex, orient.axis[1]);
|
|
TransformPosition(up, transform.worldToIndex, orient.axis[2]);
|
|
VectorNormalize(forward);
|
|
VectorNormalize(left);
|
|
VectorNormalize(up);
|
|
transform.stepSize[0] = VoxelStepSize(left, instance.scale);
|
|
transform.stepSize[1] = VoxelStepSize(up, instance.scale);
|
|
transform.stepSize[2] = VoxelStepSize(forward, instance.scale);
|
|
|
|
const float transStepScale =
|
|
max3(instance.scale[0], instance.scale[1], instance.scale[2]) * (float)vdb.ambientRaymarchLOD;
|
|
|
|
VLNanoVDBFrustInjectionRC rc = {};
|
|
SetLightGridRootConstants(rc.lightGrid);
|
|
rc.nanovdbBufferIndex = GetBufferIndexSRV(drawInstance.buffer);
|
|
rc.blackbodyTextureIndex = GetTextureIndexSRV(crp.blackbodyTexture);
|
|
rc.materialTextureAIndex = GetTextureIndexUAV(materialTextureA, 0);
|
|
rc.materialTextureBIndex = GetTextureIndexUAV(materialTextureB, 0);
|
|
rc.materialTextureCIndex = GetTextureIndexUAV(materialTextureC, 0);
|
|
rc.scatterExtTextureIndex = GetTextureIndexUAV(scatterExtTexture, 0);
|
|
rc.frustumVisTextureIndex = GetTextureIndexUAV(frustumVisTexture, 0);
|
|
rc.densityGridByteOffset = drawInstance.smokeByteOffset;
|
|
rc.flamesGridByteOffset = drawInstance.fireByteOffset;
|
|
rc.densityGridByteOffset2 = drawInstance.smokeByteOffset2;
|
|
rc.flamesGridByteOffset2 = drawInstance.fireByteOffset2;
|
|
rc.linearInterpolation = vdb.linearInterpolation ? 1 : 0;
|
|
rc.accurateOverlapTest = vdb.accurateOverlapTest ? 1 : 0;
|
|
rc.ambientAngularCoverage = vdb.ambientIncreasedCoverage ? 1 : 0;
|
|
rc.densityExtinctionScale = instance.smokeExtinctionScale;
|
|
rc.densityAlbedo = instance.smokeAlbedo;
|
|
rc.flamesEmissionScale = instance.fireEmissionScale;
|
|
rc.flamesTemperatureScale = instance.fireTemperatureScale;
|
|
rc.transform = transform;
|
|
rc.stepScale = vdb.supersampling ? 0.5f : 1.0f;
|
|
rc.transStepScale = transStepScale;
|
|
rc.t = drawInstance.t;
|
|
|
|
const bool previewMode = rhiInfo.forceNanoVDBPreviewMode != qfalse || vdb.previewMode;
|
|
const HPipeline pipeline = previewMode ? frustumVDBLQPipeline : frustumVDBPipeline;
|
|
CmdBindPipeline(pipeline);
|
|
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
|
|
CmdDispatch((frustumSize[0] + 3) / 4, (frustumSize[1] + 3) / 4, (frustumSize[2] + 3) / 4);
|
|
}
|
|
|
|
if(drawInstance.smokeByteOffset > 0 || drawInstance.smokeByteOffset2 > 0)
|
|
{
|
|
vec3_t axisX, axisY, axisZ;
|
|
VectorSet(axisX, 1, 0, 0);
|
|
VectorSet(axisY, 0, 1, 0);
|
|
VectorSet(axisZ, 0, 0, 1);
|
|
transform.stepSize[0] = VoxelStepSize(axisX, instance.scale);
|
|
transform.stepSize[1] = VoxelStepSize(axisY, instance.scale);
|
|
transform.stepSize[2] = VoxelStepSize(axisZ, instance.scale);
|
|
|
|
for(int c = 0; c < 4; c++)
|
|
{
|
|
SCOPED_DEBUG_LABEL("Extinction", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdBufferBarrier(drawInstance.buffer, ResourceStates::ComputeShaderAccessBit);
|
|
CmdTextureBarrier(extinctionTextures[c], ResourceStates::UnorderedAccessBit);
|
|
CmdEndBarrier();
|
|
|
|
VLNanoVDBExtInjectionRC rc = {};
|
|
rc.nanovdbBufferIndex = GetBufferIndexSRV(drawInstance.buffer);
|
|
rc.extinctionTextureIndex = GetTextureIndexUAV(extinctionTextures[c], 0);
|
|
rc.worldScale = extinctionVolumeScale[c];
|
|
rc.densityGridByteOffset = drawInstance.smokeByteOffset;
|
|
rc.densityGridByteOffset2 = drawInstance.smokeByteOffset2;
|
|
rc.linearInterpolation = vdb.linearInterpolation ? 1 : 0;
|
|
rc.densityExtinctionScale = instance.smokeExtinctionScale;
|
|
rc.transform = transform;
|
|
rc.t = drawInstance.t;
|
|
|
|
CmdBindPipeline(extinctionVDBPipeline);
|
|
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
|
|
CmdDispatch((extinctionSize[0] + 3) / 4, (extinctionSize[1] + 3) / 4, (extinctionSize[2] + 3) / 4);
|
|
}
|
|
}
|
|
}
|
|
|
|
uint32_t fireOffsetMask = 0;
|
|
for(uint32_t i = 0; i < vdb.drawInstances.count; i++)
|
|
{
|
|
fireOffsetMask |= vdb.drawInstances[i].fireByteOffset;
|
|
}
|
|
const bool hasFire = fireOffsetMask > 0;
|
|
if(hasFire && !vdb.previewMode && vdb.emissiveScatterScale > 0.0f)
|
|
{
|
|
VLNanoVDBLightPropRC rc = {};
|
|
rc.materialTextureAIndex = GetTextureIndexUAV(materialTextureA, 0);
|
|
rc.materialTextureBIndex = GetTextureIndexUAV(materialTextureB, 0);
|
|
rc.emissiveScatter = vdb.emissiveScatterScale;
|
|
|
|
{
|
|
SCOPED_DEBUG_LABEL("Light Propagation +X", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdTextureBarrier(materialTextureA, ResourceStates::UnorderedAccessBit);
|
|
CmdTextureBarrier(materialTextureB, ResourceStates::UnorderedAccessBit);
|
|
CmdEndBarrier();
|
|
|
|
CmdBindPipeline(frustumLightPropPXPipeline);
|
|
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
|
|
CmdDispatch((frustumSize[1] + 7) / 8, (frustumSize[2] + 7) / 8, 1);
|
|
}
|
|
|
|
{
|
|
SCOPED_DEBUG_LABEL("Light Propagation -X", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdTextureBarrier(materialTextureA, ResourceStates::UnorderedAccessBit);
|
|
CmdTextureBarrier(materialTextureB, ResourceStates::UnorderedAccessBit);
|
|
CmdEndBarrier();
|
|
|
|
CmdBindPipeline(frustumLightPropNXPipeline);
|
|
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
|
|
CmdDispatch((frustumSize[1] + 7) / 8, (frustumSize[2] + 7) / 8, 1);
|
|
}
|
|
|
|
{
|
|
SCOPED_DEBUG_LABEL("Light Propagation +Y", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdTextureBarrier(materialTextureA, ResourceStates::UnorderedAccessBit);
|
|
CmdTextureBarrier(materialTextureB, ResourceStates::UnorderedAccessBit);
|
|
CmdEndBarrier();
|
|
|
|
CmdBindPipeline(frustumLightPropPYPipeline);
|
|
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
|
|
CmdDispatch((frustumSize[0] + 7) / 8, (frustumSize[2] + 7) / 8, 1);
|
|
}
|
|
|
|
{
|
|
SCOPED_DEBUG_LABEL("Light Propagation -Y", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdTextureBarrier(materialTextureA, ResourceStates::UnorderedAccessBit);
|
|
CmdTextureBarrier(materialTextureB, ResourceStates::UnorderedAccessBit);
|
|
CmdEndBarrier();
|
|
|
|
CmdBindPipeline(frustumLightPropNYPipeline);
|
|
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
|
|
CmdDispatch((frustumSize[0] + 7) / 8, (frustumSize[2] + 7) / 8, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
SCOPED_RENDER_PASS("VL Anisotropy Avg", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdTextureBarrier(materialTextureB, ResourceStates::UnorderedAccessBit);
|
|
CmdTextureBarrier(materialTextureC, ResourceStates::UnorderedAccessBit);
|
|
CmdEndBarrier();
|
|
|
|
VLAnisotropyRC rc = {};
|
|
rc.materialTextureBIndex = GetTextureIndexUAV(materialTextureB, 0);
|
|
rc.materialTextureCIndex = GetTextureIndexUAV(materialTextureC, 0);
|
|
|
|
CmdBindPipeline(frustumAnisotropyPipeline);
|
|
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
|
|
CmdDispatch((frustumSize[0] + 3) / 4, (frustumSize[1] + 3) / 4, (frustumSize[2] + 3) / 4);
|
|
}
|
|
|
|
{
|
|
SCOPED_RENDER_PASS("VL Ambient", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdTextureBarrier(materialTextureA, ResourceStates::UnorderedAccessBit);
|
|
CmdTextureBarrier(scatterExtTexture, ResourceStates::UnorderedAccessBit);
|
|
if(tr.world->lightGridData != NULL)
|
|
{
|
|
CmdTextureBarrier(ambientLightTextureA, ResourceStates::ComputeShaderAccessBit);
|
|
CmdTextureBarrier(ambientLightTextureB, ResourceStates::ComputeShaderAccessBit);
|
|
}
|
|
CmdEndBarrier();
|
|
|
|
VLAmbientRC rc = {};
|
|
rc.materialTextureAIndex = GetTextureIndexUAV(materialTextureA, 0);
|
|
rc.scatterExtTextureIndex = GetTextureIndexUAV(scatterExtTexture, 0);
|
|
SetLightGridRootConstants(rc.lightGrid);
|
|
|
|
CmdBindPipeline(frustumAmbientPipeline);
|
|
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
|
|
CmdDispatch((frustumSize[0] + 3) / 4, (frustumSize[1] + 3) / 4, (frustumSize[2] + 3) / 4);
|
|
}
|
|
|
|
firstFrame = false;
|
|
}
|
|
|
|
void VolumetricLight::DrawPointLight(const dlight_t& light)
|
|
{
|
|
{
|
|
SCOPED_DEBUG_LABEL("VL DL Shadow", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
for(int c = 0; c < 4; c++)
|
|
{
|
|
CmdTextureBarrier(extinctionTextures[c], ResourceStates::ComputeShaderAccessBit);
|
|
}
|
|
CmdTextureBarrier(pointShadowTexture, ResourceStates::UnorderedAccessBit);
|
|
CmdEndBarrier();
|
|
|
|
VLPointLightShadowRC rc = {};
|
|
rc.shadowTextureIndex = GetTextureIndexUAV(pointShadowTexture, 0);
|
|
rc.extinctionWorldScale = extinctionVolumeScale[0]; // pick the highest resolution possible
|
|
rc.shadowWorldScale = pointShadowVolumeScale;
|
|
VectorCopy(light.origin, rc.lightPosition);
|
|
|
|
const uint32_t groupCount = (shadowPixelCount + 3) / 4;
|
|
CmdBindPipeline(pointLightShadowPipeline);
|
|
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
|
|
CmdDispatch(groupCount, groupCount, groupCount);
|
|
}
|
|
|
|
{
|
|
SCOPED_DEBUG_LABEL("VL DL Scatter", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdTextureBarrier(materialTextureA, ResourceStates::UnorderedAccessBit);
|
|
CmdTextureBarrier(materialTextureB, ResourceStates::UnorderedAccessBit);
|
|
CmdTextureBarrier(scatterExtTexture, ResourceStates::UnorderedAccessBit);
|
|
CmdTextureBarrier(pointShadowTexture, ResourceStates::ComputeShaderAccessBit);
|
|
CmdEndBarrier();
|
|
|
|
VLPointLightScatterRC rc = {};
|
|
rc.materialTextureAIndex = GetTextureIndexUAV(materialTextureA, 0);
|
|
rc.materialTextureBIndex = GetTextureIndexUAV(materialTextureB, 0);
|
|
rc.scatterExtTextureIndex = GetTextureIndexUAV(scatterExtTexture, 0);
|
|
rc.transmittanceTextureIndex = GetTextureIndexSRV(pointShadowTexture);
|
|
rc.transmittanceSamplerIndex = GetSamplerIndex(TW_CLAMP_TO_EDGE, TextureFilter::Linear);
|
|
rc.shadowWorldScale = pointShadowVolumeScale;
|
|
VectorCopy(light.origin, rc.light.position);
|
|
rc.light.radius = light.radius;
|
|
VectorCopy(light.color, rc.light.color);
|
|
|
|
CmdBindPipeline(frustumPointLightScatterPipeline);
|
|
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
|
|
CmdDispatch((frustumSize[0] + 3) / 4, (frustumSize[1] + 3) / 4, (frustumSize[2] + 3) / 4);
|
|
}
|
|
}
|
|
|
|
void VolumetricLight::DrawSunlight()
|
|
{
|
|
if(!drawSunlight)
|
|
{
|
|
return;
|
|
}
|
|
|
|
SCOPED_RENDER_PASS("VL Sunlight", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
for(int c = 0; c < 4; c++)
|
|
{
|
|
CmdTextureBarrier(extinctionTextures[c], ResourceStates::ComputeShaderAccessBit);
|
|
}
|
|
for(int c = 0; c < 3; c++)
|
|
{
|
|
CmdTextureBarrier(sunShadowTextures[c], ResourceStates::UnorderedAccessBit);
|
|
}
|
|
CmdTextureBarrier(sunShadowTextures[3], ResourceStates::ComputeShaderAccessBit);
|
|
CmdEndBarrier();
|
|
|
|
// each cascade needs to sample the higher level for setting the initial transmittance value
|
|
for(int c = 3; c >= 0; c--)
|
|
{
|
|
SCOPED_DEBUG_LABEL("Shadow", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdTextureBarrier(sunShadowTextures[c], ResourceStates::UnorderedAccessBit);
|
|
if(c < 3)
|
|
{
|
|
CmdTextureBarrier(sunShadowTextures[c + 1], ResourceStates::ComputeShaderAccessBit);
|
|
}
|
|
CmdEndBarrier();
|
|
|
|
VLSunlightShadowRC rc = {};
|
|
rc.shadowTextureIndex = GetTextureIndexUAV(sunShadowTextures[c], 0);
|
|
rc.shadowWorldScale = sunShadowVolumeScale[c];
|
|
if(c < 3)
|
|
{
|
|
rc.sourceTextureIndex = GetTextureIndexSRV(sunShadowTextures[c + 1]);
|
|
rc.sourceWorldScale = sunShadowVolumeScale[c + 1];
|
|
}
|
|
|
|
CmdBindPipeline(sunlightShadowPipeline);
|
|
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
|
|
CmdDispatch((sunShadowSize[0] + 7) / 8, (sunShadowSize[1] + 7) / 8, 1);
|
|
}
|
|
|
|
{
|
|
SCOPED_DEBUG_LABEL("Vis", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdTextureBarrier(crp.depthMinMaxTexture, ResourceStates::ComputeShaderAccessBit);
|
|
CmdTextureBarrier(sunlightVisTexture, ResourceStates::UnorderedAccessBit);
|
|
CmdEndBarrier();
|
|
|
|
const float maxJitterDepthDistance = 16.0f;
|
|
const float maxJitterRadiusXY = 4.0f;
|
|
|
|
vec2_t point01;
|
|
Halton23Sequence(point01, jitterCounter);
|
|
|
|
// 6 points on the circle, rotated by 15 degrees
|
|
// ordered such that it oscillates around (0, 0)
|
|
const float jitterXY[] =
|
|
{
|
|
0.965926f, 0.258819f,
|
|
-0.965926f, -0.258819f,
|
|
0.707107f, -0.707107f,
|
|
-0.707107f, 0.707107f,
|
|
0.258819f, 0.965926f,
|
|
-0.258819f, -0.965926f
|
|
};
|
|
const int jitterXYSampleIndex = jitterCounter % 6;
|
|
|
|
VLSunlightVisRC rc = {};
|
|
rc.visTextureIndex = GetTextureIndexUAV(sunlightVisTexture, 0);
|
|
rc.frustumVisTextureIndex = GetTextureIndexUAV(frustumVisTexture, 0);
|
|
rc.depthMip = depthMip;
|
|
rc.jitter[0] = (0.5f * jitterXY[2 * jitterXYSampleIndex + 0]) * maxJitterRadiusXY;
|
|
rc.jitter[1] = (0.5f * jitterXY[2 * jitterXYSampleIndex + 1]) * maxJitterRadiusXY;
|
|
rc.jitter[2] = VanDerCorputSequence(jitterCounter) * maxJitterDepthDistance;
|
|
|
|
CmdBindPipeline(frustumSunlightVisPipeline);
|
|
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
|
|
CmdDispatch((frustumSize[0] + 3) / 4, (frustumSize[1] + 3) / 4, (frustumSize[2] + 3) / 4);
|
|
}
|
|
|
|
if(!firstFrame)
|
|
{
|
|
SCOPED_DEBUG_LABEL("Vis Temporal", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdTextureBarrier(prevSunlightVisTexture, ResourceStates::ComputeShaderAccessBit);
|
|
CmdTextureBarrier(sunlightVisTexture, ResourceStates::UnorderedAccessBit);
|
|
CmdEndBarrier();
|
|
|
|
VLTemporalRC rc = {};
|
|
rc.currTextureIndex = GetTextureIndexUAV(sunlightVisTexture, 0);
|
|
rc.prevTextureIndex = GetTextureIndexSRV(prevSunlightVisTexture);
|
|
rc.prevTextureSamplerIndex = GetSamplerIndex(TW_CLAMP_TO_EDGE, TextureFilter::Linear);
|
|
rc.alpha = 0.9f;
|
|
|
|
CmdBindPipeline(frustumTemporalFloatPipeline);
|
|
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
|
|
CmdDispatch((frustumSize[0] + 3) / 4, (frustumSize[1] + 3) / 4, (frustumSize[2] + 3) / 4);
|
|
}
|
|
|
|
{
|
|
SCOPED_DEBUG_LABEL("Vis Copy", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdTextureBarrier(sunlightVisTexture, ResourceStates::CopySourceBit);
|
|
CmdTextureBarrier(prevSunlightVisTexture, ResourceStates::CopyDestinationBit);
|
|
CmdEndBarrier();
|
|
|
|
CmdCopyTexture(prevSunlightVisTexture, sunlightVisTexture);
|
|
}
|
|
|
|
{
|
|
SCOPED_DEBUG_LABEL("Sunlight", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
for(int c = 0; c < 4; c++)
|
|
{
|
|
CmdTextureBarrier(sunShadowTextures[c], ResourceStates::ComputeShaderAccessBit);
|
|
}
|
|
CmdTextureBarrier(materialTextureA, ResourceStates::UnorderedAccessBit);
|
|
CmdTextureBarrier(materialTextureB, ResourceStates::UnorderedAccessBit);
|
|
CmdTextureBarrier(sunlightVisTexture, ResourceStates::UnorderedAccessBit);
|
|
CmdTextureBarrier(scatterExtTexture, ResourceStates::UnorderedAccessBit);
|
|
CmdEndBarrier();
|
|
|
|
VLSunlightRC rc = {};
|
|
rc.materialTextureAIndex = GetTextureIndexUAV(materialTextureA, 0);
|
|
rc.materialTextureBIndex = GetTextureIndexUAV(materialTextureB, 0);
|
|
rc.sunlightVisTextureIndex = GetTextureIndexUAV(sunlightVisTexture, 0);
|
|
rc.scatterExtTextureIndex = GetTextureIndexUAV(scatterExtTexture, 0);
|
|
|
|
CmdBindPipeline(sunlightScatterPipeline);
|
|
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
|
|
CmdDispatch((frustumSize[0] + 3) / 4, (frustumSize[1] + 3) / 4, (frustumSize[2] + 3) / 4);
|
|
}
|
|
}
|
|
|
|
void VolumetricLight::DrawEnd()
|
|
{
|
|
SCOPED_RENDER_PASS("VL Raymarch", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdTextureBarrier(scatterExtTexture, ResourceStates::UnorderedAccessBit);
|
|
CmdTextureBarrier(scatterTransTexture, ResourceStates::UnorderedAccessBit);
|
|
CmdEndBarrier();
|
|
|
|
VLRaymarchRC rc = {};
|
|
rc.scatterTextureIndex = GetTextureIndexUAV(scatterExtTexture, 0);
|
|
rc.resolveTextureIndex = GetTextureIndexUAV(scatterTransTexture, 0);
|
|
rc.materialTextureBIndex = GetTextureIndexUAV(materialTextureB, 0);
|
|
|
|
CmdBindPipeline(frustumRaymarchPipeline);
|
|
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
|
|
CmdDispatch((frustumSize[0] + 7) / 8, (frustumSize[1] + 7) / 8, 1);
|
|
|
|
jitterCounter++;
|
|
}
|
|
|
|
void VolumetricLight::DrawIm3d()
|
|
{
|
|
if((uint32_t)activeFogIndex < fogCount &&
|
|
crp.im3d.ShouldDrawGizmos())
|
|
{
|
|
Fog& fog = fogs[activeFogIndex];
|
|
if(!fog.isGlobalFog)
|
|
{
|
|
const char* const id = va("Fog #%d", (int)activeFogIndex + 1);
|
|
matrix3x3_t rotation;
|
|
R_MakeIdentityMatrix3x3(rotation);
|
|
Im3d::Gizmo(id, fog.boxCenter, rotation, fog.boxSize);
|
|
}
|
|
}
|
|
}
|
|
|
|
void VolumetricLight::DrawDebug()
|
|
{
|
|
if(!ShouldDrawDebug())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(drawExtinctionDebug)
|
|
{
|
|
SCOPED_RENDER_PASS("VL Extinction Viz", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
for(int c = 0; c < 4; c++)
|
|
{
|
|
CmdTextureBarrier(extinctionTextures[c], ResourceStates::UnorderedAccessBit);
|
|
}
|
|
CmdTextureBarrier(crp.renderTarget, ResourceStates::RenderTargetBit);
|
|
CmdTextureBarrier(crp.depthTexture, ResourceStates::DepthWriteBit);
|
|
CmdEndBarrier();
|
|
|
|
const int c = debugExtinctionCascadeIndex;
|
|
VLExtinctionVizRC rc = {};
|
|
rc.extinctionTextureIndex = GetTextureIndexUAV(extinctionTextures[c], 0);
|
|
rc.worldScale = extinctionVolumeScale[c];
|
|
rc.boxScale = debugBoxScale;
|
|
rc.extinctionScale = debugExtinctionScale;
|
|
VectorCopy(debugCameraPosition, rc.cameraPosition);
|
|
VectorCopy(colorRed, rc.color);
|
|
|
|
const uint32_t voxelCount = extinctionSize[0] * extinctionSize[1] * extinctionSize[2];
|
|
const uint32_t vertexCount = voxelCount * 36;
|
|
CmdBindRenderTargets(1, &crp.renderTarget, &crp.depthTexture);
|
|
CmdBindPipeline(extinctionVizPipeline);
|
|
CmdSetGraphicsRootConstants(0, sizeof(rc), &rc);
|
|
CmdDraw(vertexCount, 0);
|
|
}
|
|
|
|
if(drawSunShadowDebug)
|
|
{
|
|
SCOPED_RENDER_PASS("VL Sun Shadow Viz", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
for(int c = 0; c < 4; c++)
|
|
{
|
|
CmdTextureBarrier(sunShadowTextures[c], ResourceStates::UnorderedAccessBit);
|
|
}
|
|
CmdTextureBarrier(crp.renderTarget, ResourceStates::RenderTargetBit);
|
|
CmdTextureBarrier(crp.depthTexture, ResourceStates::DepthWriteBit);
|
|
CmdEndBarrier();
|
|
|
|
const int c = debugSunShadowCascadeIndex;
|
|
VLSunShadowVizRC rc = {};
|
|
rc.shadowTextureIndex = GetTextureIndexUAV(sunShadowTextures[c], 0);
|
|
rc.worldScale = sunShadowVolumeScale[c];
|
|
rc.boxScale = debugBoxScale;
|
|
VectorCopy(debugCameraPosition, rc.cameraPosition);
|
|
VectorCopy(colorRed, rc.color);
|
|
|
|
const uint32_t voxelCount = sunShadowSize[0] * sunShadowSize[1] * sunShadowSize[2];
|
|
const uint32_t vertexCount = voxelCount * 36;
|
|
CmdBindRenderTargets(1, &crp.renderTarget, &crp.depthTexture);
|
|
CmdBindPipeline(sunShadowVizPipeline);
|
|
CmdSetGraphicsRootConstants(0, sizeof(rc), &rc);
|
|
CmdDraw(vertexCount, 0);
|
|
}
|
|
|
|
if(drawAmbientDebug && tr.world->lightGridData != NULL)
|
|
{
|
|
SCOPED_RENDER_PASS("VL Ambient Viz", 1.0f, 1.0f, 1.0f);
|
|
|
|
CmdBeginBarrier();
|
|
CmdTextureBarrier(ambientLightTextureA, ResourceStates::PixelShaderAccessBit);
|
|
CmdTextureBarrier(ambientLightTextureB, ResourceStates::PixelShaderAccessBit);
|
|
CmdTextureBarrier(crp.renderTarget, ResourceStates::RenderTargetBit);
|
|
CmdTextureBarrier(crp.depthTexture, ResourceStates::DepthWriteBit);
|
|
CmdEndBarrier();
|
|
|
|
VLAmbientVizRC rc = {};
|
|
SetLightGridRootConstants(rc.lightGrid);
|
|
rc.sphereScale = debugSphereScale;
|
|
|
|
const uint32_t voxelCount = tr.world->lightGridBounds[0] * tr.world->lightGridBounds[1] * tr.world->lightGridBounds[2];
|
|
const uint32_t vertexCount = voxelCount * 6;
|
|
CmdBindRenderTargets(1, &crp.renderTarget, &crp.depthTexture);
|
|
CmdBindPipeline(ambientVizPipeline);
|
|
CmdSetGraphicsRootConstants(0, sizeof(rc), &rc);
|
|
CmdDraw(vertexCount, 0);
|
|
}
|
|
}
|
|
|
|
void VolumetricLight::DrawGUI()
|
|
{
|
|
activeFogIndex = -1;
|
|
if(!tr.worldMapLoaded)
|
|
{
|
|
return;
|
|
}
|
|
|
|
GUI_AddMainMenuItem(GUI_MainMenu::Tools, "Edit Volumetrics", "", &windowActive);
|
|
|
|
if(!lockCameraPosition)
|
|
{
|
|
VectorCopy(backEnd.viewParms.world.viewOrigin, debugCameraPosition);
|
|
}
|
|
|
|
if(!windowActive)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(ImGui::Begin("Volumetric Data", &windowActive, ImGuiWindowFlags_AlwaysAutoResize))
|
|
{
|
|
const ImGuiColorEditFlags colorEditFlags =
|
|
ImGuiColorEditFlags_Float |
|
|
ImGuiColorEditFlags_NoAlpha |
|
|
ImGuiColorEditFlags_DisplayRGB |
|
|
ImGuiColorEditFlags_InputRGB;
|
|
|
|
if(fogCount < ARRAY_LEN(fogs) && ImGui::Button("Add Fog"))
|
|
{
|
|
const float fogSize = 40.0f;
|
|
Fog& fog = fogs[fogCount++];
|
|
fog = {};
|
|
fog.extinction = OpaqueDistanceToExtinction(5000.0f);
|
|
fog.albedo = 0.7f;
|
|
VectorSet(fog.scatterColor, 1, 1, 1);
|
|
fog.emissive = 0.0f;
|
|
VectorSet(fog.emissiveColor, 1, 1, 1);
|
|
fog.anisotropy = 0.0f;
|
|
fog.noiseStrength = 1.0f;
|
|
fog.noiseSpatialPeriod = 50.0f;
|
|
fog.noiseTimePeriod = 8.0f;
|
|
fog.isHeightFog = false;
|
|
fog.isGlobalFog = true;
|
|
for(int a = 0; a < 3; a++)
|
|
{
|
|
VectorCopy(backEnd.refdef.vieworg, fog.boxCenter);
|
|
VectorSet(fog.boxSize, fogSize, fogSize, fogSize);
|
|
}
|
|
}
|
|
|
|
ImGui::SameLine();
|
|
if(fogCount > 0 && ImGui::Button("Save Config..."))
|
|
{
|
|
SaveFileDialog_Open("fogs", ".fogs");
|
|
}
|
|
ImGui::SameLine();
|
|
if(ImGui::Button("Open Config..."))
|
|
{
|
|
OpenFileDialog_Open("fogs", ".fogs");
|
|
}
|
|
|
|
const char* axisNames[3] = { "X", "Y", "Z" };
|
|
|
|
if(ImGui::BeginTabBar("Tabs#Fogs", ImGuiTabBarFlags_AutoSelectNewTabs))
|
|
{
|
|
for(int i = 0; i < fogCount; i++)
|
|
{
|
|
if(ImGui::BeginTabItem(va("#%d", i + 1)))
|
|
{
|
|
activeFogIndex = i;
|
|
|
|
Fog& fog = fogs[i];
|
|
ImGui::Checkbox("Global fog", &fog.isGlobalFog);
|
|
if(!fog.isGlobalFog)
|
|
{
|
|
for(int a = 0; a < 3; a++)
|
|
{
|
|
ImGui::SliderFloat(va("Box center %s", axisNames[a]), &fog.boxCenter[a], mapBoxMin[a], mapBoxMax[a], "%g");
|
|
}
|
|
for(int a = 0; a < 3; a++)
|
|
{
|
|
ImGui::SliderFloat(va("Box size %s", axisNames[a]), &fog.boxSize[a], 0.0f, mapBoxMax[a] - mapBoxMin[a], "%g");
|
|
}
|
|
}
|
|
float opaqueDistance = ExtinctionToOpaqueDistance(fog.extinction);
|
|
ImGui::SliderFloat("Distance to opaque", &opaqueDistance, 1.0f, 10000.0f, "%g");
|
|
fog.extinction = OpaqueDistanceToExtinction(opaqueDistance);
|
|
ImGui::ColorEdit3("Reflective (scatter) color", fog.scatterColor, colorEditFlags);
|
|
ImGui::SliderFloat("Reflectivity (albedo)", &fog.albedo, 0.0f, 1.0f, "%g");
|
|
ImGui::SliderFloat("Directionality (anisotropy)", &fog.anisotropy, 0.0f, 1.0f, "%g");
|
|
ImGui::SliderFloat("Emissive strength", &fog.emissive, 0.0f, 0.001f, "%g");
|
|
ImGui::ColorEdit3("Emissive color", fog.emissiveColor, colorEditFlags);
|
|
ImGui::SliderFloat("Noise strength", &fog.noiseStrength, 1.0f, 10.0f, "%g");
|
|
ImGui::SliderFloat("Noise space period", &fog.noiseSpatialPeriod, 0.0f, 200.0f, "%g");
|
|
ImGui::SliderFloat("Noise time period", &fog.noiseTimePeriod, 0.0f, 20.0f, "%g");
|
|
ImGui::Checkbox("Height fog", &fog.isHeightFog);
|
|
ImGui::EndTabItem();
|
|
|
|
ImGui::Separator();
|
|
if(ImGui::Button("Remove"))
|
|
{
|
|
if(i < fogCount - 1)
|
|
{
|
|
memmove(&fogs[i], &fogs[i + 1], (fogCount - 1 - i) * sizeof(fogs[0]));
|
|
}
|
|
fogCount--;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(ImGui::BeginTabItem("Main"))
|
|
{
|
|
bool enableVL = crp_volLight->integer != 0;
|
|
if(ImGui::Checkbox("Enable volumetric lighting", &enableVL))
|
|
{
|
|
Cvar_Set(crp_volLight->name, enableVL ? "1" : "0");
|
|
}
|
|
ImGui::Checkbox("Enable sunlight", &drawSunlight);
|
|
|
|
ImGui::Separator();
|
|
ImGui::Checkbox("Enable light grid", &enableLightGrid);
|
|
ImGui::ColorEdit3("Ambient light color", ambientColorGUI, colorEditFlags);
|
|
ImGui::SliderFloat("Ambient light intensity", &ambientIntensity, 0.0f, 1.0f);
|
|
|
|
ImGui::Separator();
|
|
ImGui::SliderFloat("Sunlight intensity", &crp.sunlightData.intensityVL, 0.0f, 200.0f);
|
|
ImGui::SliderFloat("Point light intensity", &pointLightIntensity, 0.0f, 200.0f);
|
|
|
|
const float brightness = Brightness(ambientColorGUI);
|
|
if(brightness > 0.000001f)
|
|
{
|
|
const float scale = 0.5f / brightness;
|
|
for(int i = 0; i < 3; i++)
|
|
{
|
|
ambientColor[i] = ambientColorGUI[i] * scale;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
VectorSet(ambientColor, 0.5f, 0.5f, 0.5f);
|
|
}
|
|
|
|
ImGui::EndTabItem();
|
|
}
|
|
|
|
if(ImGui::BeginTabItem("Resolution"))
|
|
{
|
|
const float titleWidth = 14.0f * ImGui::GetFontSize();
|
|
RadioButton(&xySubsampling, titleWidth, "X/Y sub-sampling", 3, "2x", 1, "4x", 2, "8x", 3);
|
|
RadioButton(&zResolution, titleWidth, "Z resolution", 3, "128", 128, "256", 256, "512", 512);
|
|
RadioButton(&extinctionResolution, titleWidth, "Extinction resolution", 3, "64", 64, "128", 128, "256", 256);
|
|
RadioButton(&sunShadowResolution, titleWidth, "Sun shadow resolution", 3, "64", 64, "128", 128, "256", 256);
|
|
RadioButton(&pointShadowResolution, titleWidth, "Point shadow resolution", 3, "32", 32, "64", 64, "128", 128);
|
|
|
|
int divisor = 1;
|
|
for(int i = 0; i < xySubsampling; i++)
|
|
{
|
|
divisor *= 2;
|
|
}
|
|
|
|
if((glConfig.vidWidth / divisor) != frustumSize[0] ||
|
|
(glConfig.vidHeight / divisor) != frustumSize[1] ||
|
|
zResolution != frustumSize[2] ||
|
|
extinctionResolution != extinctionSize[0] ||
|
|
sunShadowResolution != sunShadowSize[0] ||
|
|
pointShadowResolution != shadowPixelCount)
|
|
{
|
|
if(ImGui::Button("Video restart"))
|
|
{
|
|
Cbuf_AddText("vid_restart\n");
|
|
}
|
|
}
|
|
|
|
ImGui::EndTabItem();
|
|
}
|
|
|
|
if(ImGui::BeginTabItem("Debug"))
|
|
{
|
|
if(tr.world->lightGridData != NULL)
|
|
{
|
|
ImGui::Checkbox("Draw ambient light grid", &drawAmbientDebug);
|
|
if(drawAmbientDebug)
|
|
{
|
|
ImGui::SliderFloat("Voxel sphere scale", &debugSphereScale, 0.25f, 1.0f);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ImGui::TextColored(ImVec4(1.0f, 0.5f, 0.0f, 1.0f), "No valid light grid was found for this map");
|
|
}
|
|
|
|
ImGui::Separator();
|
|
ImGui::Checkbox("Draw extinction volume", &drawExtinctionDebug);
|
|
if(drawExtinctionDebug)
|
|
{
|
|
ImGui::SliderInt("Extinction cascade", &debugExtinctionCascadeIndex, 0, 3);
|
|
ImGui::SliderFloat("Extinction value scale", &debugExtinctionScale, 1.0f, 1000.0f);
|
|
}
|
|
|
|
ImGui::Separator();
|
|
ImGui::Checkbox("Draw sun shadow volume", &drawSunShadowDebug);
|
|
if(drawSunShadowDebug)
|
|
{
|
|
ImGui::SliderInt("Sun shadow cascade", &debugSunShadowCascadeIndex, 0, 3);
|
|
}
|
|
|
|
ImGui::Separator();
|
|
ImGui::Checkbox("Lock camera position", &lockCameraPosition);
|
|
ImGui::SliderFloat("Voxel box scale", &debugBoxScale, 0.25f, 1.0f);
|
|
|
|
ImGui::EndTabItem();
|
|
}
|
|
|
|
ImGui::EndTabBar();
|
|
}
|
|
|
|
if(SaveFileDialog_Do())
|
|
{
|
|
SaveFogFile(SaveFileDialog_GetPath());
|
|
}
|
|
|
|
if(OpenFileDialog_Do())
|
|
{
|
|
LoadFogFile(OpenFileDialog_GetPath());
|
|
}
|
|
}
|
|
ImGui::End();
|
|
}
|
|
|
|
bool VolumetricLight::WantRTASUpdate(const trRefdef_t& scene)
|
|
{
|
|
return crp_volLight->integer != 0;
|
|
}
|
|
|
|
bool VolumetricLight::ShouldDraw()
|
|
{
|
|
if(crp_volLight->integer == 0 ||
|
|
(backEnd.refdef.rdflags & RDF_NOWORLDMODEL) != 0 ||
|
|
!IsViewportFullscreen(backEnd.viewParms) ||
|
|
lockCameraPosition)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool VolumetricLight::ShouldDrawDebug()
|
|
{
|
|
if(crp_volLight->integer == 0 ||
|
|
(backEnd.refdef.rdflags & RDF_NOWORLDMODEL) != 0 ||
|
|
!IsViewportFullscreen(backEnd.viewParms))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return drawExtinctionDebug || drawSunShadowDebug || drawAmbientDebug;
|
|
}
|
|
|
|
bool VolumetricLight::LoadFogFile(const char* filePath)
|
|
{
|
|
bool success = false;
|
|
void* data = NULL;
|
|
const int byteCount = ri.FS_ReadFile(filePath, &data);
|
|
if(data != NULL &&
|
|
byteCount > 0 &&
|
|
byteCount <= sizeof(fogs) &&
|
|
(byteCount % sizeof(fogs[0])) == 0)
|
|
{
|
|
memcpy(&fogs[0], data, byteCount);
|
|
fogCount = byteCount / sizeof(fogs[0]);
|
|
success = true;
|
|
}
|
|
if(data != NULL)
|
|
{
|
|
ri.FS_FreeFile(data);
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
void VolumetricLight::SaveFogFile(const char* filePath)
|
|
{
|
|
if(fogCount > 0)
|
|
{
|
|
FS_EnableCNQ3FolderWrites(qtrue);
|
|
ri.FS_WriteFile(filePath, &fogs[0], fogCount * sizeof(fogs[0]));
|
|
FS_EnableCNQ3FolderWrites(qfalse);
|
|
}
|
|
}
|
|
|
|
void VolumetricLight::SetLightGridRootConstants(LightGridRC& rc)
|
|
{
|
|
rc.isAvailable = tr.world->lightGridData != NULL && enableLightGrid;
|
|
if(rc.isAvailable)
|
|
{
|
|
rc.textureAIndex = GetTextureIndexSRV(ambientLightTextureA);
|
|
rc.textureBIndex = GetTextureIndexSRV(ambientLightTextureB);
|
|
VectorCopy(lightGridCenter, rc.centerPosition);
|
|
VectorCopy(tr.world->lightGridSize, rc.worldScale);
|
|
rc.samplerIndex = GetSamplerIndex(TW_CLAMP_TO_EDGE, TextureFilter::Linear);
|
|
}
|
|
}
|