mirror of https://bitbucket.org/CPMADevs/cnq3
added NanoVDB support
- added the foundation for a GPU particle system - reworked volumetric particle injection
This commit is contained in:
parent
385a75c9cd
commit
afc81437c3
|
@ -146,7 +146,7 @@ chg: reworked renderer with 2 new rendering pipelines
|
||||||
- order-independent transparency
|
- order-independent transparency
|
||||||
- depth of field (scatter-as-gather or accumulation)
|
- depth of field (scatter-as-gather or accumulation)
|
||||||
- shadowed point lights and sunlight
|
- shadowed point lights and sunlight
|
||||||
- volumetric lighting
|
- volumetric lighting: fog and NanoVDB animations lit by ambient light, point lights and sunlight
|
||||||
- all corresponding CVars have the "crp_" prefix
|
- all corresponding CVars have the "crp_" prefix
|
||||||
|
|
||||||
chg: removed cl_drawMouseLag, r_backend, r_frameSleep, r_gpuMipGen, r_alphaToCoverage, r_alphaToCoverageMipBoost
|
chg: removed cl_drawMouseLag, r_backend, r_frameSleep, r_gpuMipGen, r_alphaToCoverage, r_alphaToCoverageMipBoost
|
||||||
|
|
|
@ -30,6 +30,7 @@ const vec3_t vec2_one = { 1, 1 };
|
||||||
const vec3_t vec3_origin = { 0, 0, 0 };
|
const vec3_t vec3_origin = { 0, 0, 0 };
|
||||||
const vec3_t vec3_zero = { 0, 0, 0 };
|
const vec3_t vec3_zero = { 0, 0, 0 };
|
||||||
const vec3_t vec3_one = { 1, 1, 1 };
|
const vec3_t vec3_one = { 1, 1, 1 };
|
||||||
|
const vec3_t vec3_axis[3] = { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } };
|
||||||
const vec4_t vec4_zero = { 0, 0, 0, 0 };
|
const vec4_t vec4_zero = { 0, 0, 0, 0 };
|
||||||
const vec4_t vec4_one = { 1, 1, 1, 1 };
|
const vec4_t vec4_one = { 1, 1, 1, 1 };
|
||||||
|
|
||||||
|
|
|
@ -65,8 +65,10 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
|
||||||
#if defined(__cplusplus) && !defined(min)
|
#if defined(__cplusplus) && !defined(min)
|
||||||
template <typename T> __inline T min( T a, T b ) { return (a < b) ? a : b; }
|
template <typename T> T min( T a, T b ) { return (a < b) ? a : b; }
|
||||||
template <typename T> __inline T max( T a, T b ) { return (a > b) ? a : b; }
|
template <typename T> T max( T a, T b ) { return (a > b) ? a : b; }
|
||||||
|
template <typename T> T min3(T a, T b, T c) { return min(a, min(b, c)); }
|
||||||
|
template <typename T> T max3(T a, T b, T c) { return max(a, max(b, c)); }
|
||||||
#elif defined(Q3_VM) // #elif !defined(min) doesn't work here, because the VC headers are shit
|
#elif defined(Q3_VM) // #elif !defined(min) doesn't work here, because the VC headers are shit
|
||||||
#define min( a, b ) ((a) < (b) ? (a) : (b))
|
#define min( a, b ) ((a) < (b) ? (a) : (b))
|
||||||
#define max( a, b ) ((a) > (b) ? (a) : (b))
|
#define max( a, b ) ((a) > (b) ? (a) : (b))
|
||||||
|
@ -279,6 +281,7 @@ extern const vec3_t vec2_one;
|
||||||
extern const vec3_t vec3_origin;
|
extern const vec3_t vec3_origin;
|
||||||
extern const vec3_t vec3_zero;
|
extern const vec3_t vec3_zero;
|
||||||
extern const vec3_t vec3_one;
|
extern const vec3_t vec3_one;
|
||||||
|
extern const vec3_t vec3_axis[3];
|
||||||
extern const vec4_t vec4_zero;
|
extern const vec4_t vec4_zero;
|
||||||
extern const vec4_t vec4_one;
|
extern const vec4_t vec4_one;
|
||||||
|
|
||||||
|
@ -393,6 +396,7 @@ void ByteToDir( int b, vec3_t dir );
|
||||||
#define DotProduct(x,y) ((x)[0]*(y)[0]+(x)[1]*(y)[1]+(x)[2]*(y)[2])
|
#define DotProduct(x,y) ((x)[0]*(y)[0]+(x)[1]*(y)[1]+(x)[2]*(y)[2])
|
||||||
#define VectorSubtract(a,b,c) ((c)[0]=(a)[0]-(b)[0],(c)[1]=(a)[1]-(b)[1],(c)[2]=(a)[2]-(b)[2])
|
#define VectorSubtract(a,b,c) ((c)[0]=(a)[0]-(b)[0],(c)[1]=(a)[1]-(b)[1],(c)[2]=(a)[2]-(b)[2])
|
||||||
#define VectorAdd(a,b,c) ((c)[0]=(a)[0]+(b)[0],(c)[1]=(a)[1]+(b)[1],(c)[2]=(a)[2]+(b)[2])
|
#define VectorAdd(a,b,c) ((c)[0]=(a)[0]+(b)[0],(c)[1]=(a)[1]+(b)[1],(c)[2]=(a)[2]+(b)[2])
|
||||||
|
#define VectorMultiply(a,b,c) ((c)[0]=(a)[0]*(b)[0],(c)[1]=(a)[1]*(b)[1],(c)[2]=(a)[2]*(b)[2])
|
||||||
#define VectorCopy(a,b) ((b)[0]=(a)[0],(b)[1]=(a)[1],(b)[2]=(a)[2])
|
#define VectorCopy(a,b) ((b)[0]=(a)[0],(b)[1]=(a)[1],(b)[2]=(a)[2])
|
||||||
#define VectorScale(v, s, o) ((o)[0]=(v)[0]*(s),(o)[1]=(v)[1]*(s),(o)[2]=(v)[2]*(s))
|
#define VectorScale(v, s, o) ((o)[0]=(v)[0]*(s),(o)[1]=(v)[1]*(s),(o)[2]=(v)[2]*(s))
|
||||||
#define VectorMA(v, s, b, o) ((o)[0]=(v)[0]+(b)[0]*(s),(o)[1]=(v)[1]+(b)[1]*(s),(o)[2]=(v)[2]+(b)[2]*(s))
|
#define VectorMA(v, s, b, o) ((o)[0]=(v)[0]+(b)[0]*(s),(o)[1]=(v)[1]+(b)[1]*(s),(o)[2]=(v)[2]+(b)[2]*(s))
|
||||||
|
|
|
@ -438,6 +438,142 @@ struct Sunlight
|
||||||
HTexture penumbraTexture;
|
HTexture penumbraTexture;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct VDBSequenceDesc
|
||||||
|
{
|
||||||
|
const char* folderPath = NULL;
|
||||||
|
const char* smokeGridName = "density";
|
||||||
|
const char* fireGridName = "flames";
|
||||||
|
vec3_t originOffset = {}; // index space
|
||||||
|
vec3_t position = { 700 }; // world space
|
||||||
|
vec3_t anglesRad = {}; // in radians
|
||||||
|
vec3_t scale = { 1.0f, 1.0f, 1.0f };
|
||||||
|
float smokeExtinctionScale = 1.0f;
|
||||||
|
float smokeAlbedo = 0.9f; // real smoke: 0.9 to 0.97
|
||||||
|
float fireEmissionScale = 0.1f;
|
||||||
|
float fireTemperatureScale = 1000.0f;
|
||||||
|
float frameRate = 60.0f;
|
||||||
|
int startTimeMS = 0;
|
||||||
|
int startTimeUS = 0;
|
||||||
|
bool loop = false;
|
||||||
|
bool useSequenceOffset = true;
|
||||||
|
bool gpuResident = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NanoVDBManager
|
||||||
|
{
|
||||||
|
struct Instance;
|
||||||
|
struct DrawInstance;
|
||||||
|
struct CPUFrame;
|
||||||
|
|
||||||
|
void Init();
|
||||||
|
void DrawGUI();
|
||||||
|
void BeforeFrame();
|
||||||
|
bool AddSequence(const VDBSequenceDesc& desc);
|
||||||
|
void MakeWorldToIndexMatrix(matrix3x3_t matrix, const Instance& instance);
|
||||||
|
void Purge();
|
||||||
|
int FindStreamedFrameIndex(uint32_t sequenceIndex, uint32_t frameIndex);
|
||||||
|
|
||||||
|
struct Sequence
|
||||||
|
{
|
||||||
|
char folderPath[64];
|
||||||
|
vec3_t originOffset;
|
||||||
|
vec3_t scale;
|
||||||
|
HBuffer buffer;
|
||||||
|
uint32_t bufferByteCount;
|
||||||
|
uint32_t frameCount;
|
||||||
|
uint32_t firstFrameIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Instance
|
||||||
|
{
|
||||||
|
char smokeGridName[64];
|
||||||
|
char fireGridName[64];
|
||||||
|
vec3_t originOffset; // index space
|
||||||
|
vec3_t position; // world space
|
||||||
|
vec3_t anglesRad; // in radians
|
||||||
|
vec3_t scale;
|
||||||
|
float smokeExtinctionScale;
|
||||||
|
float smokeAlbedo;
|
||||||
|
float fireEmissionScale;
|
||||||
|
float fireTemperatureScale;
|
||||||
|
float frameRate;
|
||||||
|
int startTimeMS;
|
||||||
|
int startTimeUS;
|
||||||
|
uint32_t sequenceIndex;
|
||||||
|
bool loop;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DrawInstance
|
||||||
|
{
|
||||||
|
HBuffer buffer;
|
||||||
|
uint32_t smokeByteOffset;
|
||||||
|
uint32_t fireByteOffset;
|
||||||
|
uint32_t smokeByteOffset2;
|
||||||
|
uint32_t fireByteOffset2;
|
||||||
|
float t;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GPUFrame
|
||||||
|
{
|
||||||
|
uint32_t smokeByteOffset;
|
||||||
|
uint32_t fireByteOffset;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CPUFrame
|
||||||
|
{
|
||||||
|
char filePath[64];
|
||||||
|
uint32_t smokeByteOffset;
|
||||||
|
uint32_t smokeByteCount;
|
||||||
|
uint32_t fireByteOffset;
|
||||||
|
uint32_t fireByteCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct StreamedFrame
|
||||||
|
{
|
||||||
|
uint32_t sequenceIndex;
|
||||||
|
uint32_t frameIndex;
|
||||||
|
uint32_t smokeByteOffset;
|
||||||
|
uint32_t flamesByteOffset;
|
||||||
|
};
|
||||||
|
|
||||||
|
StaticArray<Sequence, 16> sequences;
|
||||||
|
StaticArray<Instance, 64> instances;
|
||||||
|
StaticArray<DrawInstance, 64> drawInstances; // for the current frame
|
||||||
|
StaticArray<StreamedFrame, 128> streamedFrames; // for the current frame
|
||||||
|
StaticArray<GPUFrame, 4096> gpuFrames;
|
||||||
|
StaticArray<CPUFrame, 4096> cpuFrames;
|
||||||
|
HBuffer streamBuffers[FrameCount + 1];
|
||||||
|
uint32_t streamBufferByteCount;
|
||||||
|
uint32_t streamBufferIndex;
|
||||||
|
bool windowActive = false;
|
||||||
|
bool linearInterpolation = false;
|
||||||
|
bool accurateOverlapTest = false;
|
||||||
|
bool supersampling = false;
|
||||||
|
int ambientRaymarchLOD = 8;
|
||||||
|
bool ambientIncreasedCoverage = true;
|
||||||
|
bool previewMode = false;
|
||||||
|
float emissiveScatterScale = 0.5f;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ParticleSystem
|
||||||
|
{
|
||||||
|
void Init();
|
||||||
|
void Draw();
|
||||||
|
|
||||||
|
//private:
|
||||||
|
HPipeline clearPipeline;
|
||||||
|
HPipeline setupPipeline;
|
||||||
|
HPipeline emitPipeline;
|
||||||
|
HPipeline simulatePipeline;
|
||||||
|
HBuffer particleBuffer;
|
||||||
|
HBuffer liveBuffers[2]; // indices before and after simulation
|
||||||
|
HBuffer deadBuffer; // indices
|
||||||
|
HBuffer emitterBuffer;
|
||||||
|
HBuffer indirectBuffer; // 0: emit dispatch, 1: simulate dispatch
|
||||||
|
uint32_t liveBufferReadIndex;
|
||||||
|
bool needsClearing;
|
||||||
|
};
|
||||||
|
|
||||||
struct VolumetricLight
|
struct VolumetricLight
|
||||||
{
|
{
|
||||||
void Init();
|
void Init();
|
||||||
|
@ -453,6 +589,7 @@ struct VolumetricLight
|
||||||
bool ShouldDrawDebug();
|
bool ShouldDrawDebug();
|
||||||
bool LoadFogFile(const char* filePath);
|
bool LoadFogFile(const char* filePath);
|
||||||
void SaveFogFile(const char* filePath);
|
void SaveFogFile(const char* filePath);
|
||||||
|
void SetLightGridRootConstants(struct LightGridRC& rc);
|
||||||
|
|
||||||
// GUI/user-friendly data layout
|
// GUI/user-friendly data layout
|
||||||
struct Fog
|
struct Fog
|
||||||
|
@ -462,9 +599,9 @@ struct VolumetricLight
|
||||||
vec3_t boxMin;
|
vec3_t boxMin;
|
||||||
vec3_t boxMax;
|
vec3_t boxMax;
|
||||||
float extinction;
|
float extinction;
|
||||||
float albedo; // scatter / extinction
|
float albedo; // thin fog: 0.3 to 0.5, thick fog: 0.6 to 0.9
|
||||||
float emissive;
|
float emissive;
|
||||||
float anisotropy;
|
float anisotropy; // thin fog: 0.9, thick fog: 0.9 with strong backscatter
|
||||||
float noiseStrength;
|
float noiseStrength;
|
||||||
float noiseSpatialPeriod;
|
float noiseSpatialPeriod;
|
||||||
float noiseTimePeriod;
|
float noiseTimePeriod;
|
||||||
|
@ -472,30 +609,30 @@ struct VolumetricLight
|
||||||
bool isHeightFog;
|
bool isHeightFog;
|
||||||
};
|
};
|
||||||
|
|
||||||
#if defined(VL_CPU_PARTICLES)
|
|
||||||
HPipeline particleDispatchPipeline;
|
|
||||||
HPipeline particlePreProcessExtinctionPipeline;
|
|
||||||
HPipeline particlePreProcessFrustumPipeline;
|
|
||||||
HPipeline extinctionParticlePipeline;
|
|
||||||
HPipeline frustumParticlePipeline;
|
|
||||||
HBuffer particleBuffer; // should be double buffered...
|
|
||||||
HBuffer particleHitBuffer; // for each tile, is there at least 1 particle?
|
|
||||||
HBuffer particleTileBuffer; // array of tiles, each tile has a uint3 index
|
|
||||||
HBuffer particleDispatchBuffer; // indirect dispatch buffer
|
|
||||||
HBuffer particleDispatchClearBuffer; // indirect dispatch buffer with values (0, 1, 1)
|
|
||||||
uint32_t particleCount;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Fog fogs[64];
|
Fog fogs[64];
|
||||||
uint32_t fogCount = 0;
|
uint32_t fogCount = 0;
|
||||||
HPipeline extinctionFogPipeline;
|
HPipeline extinctionFogPipeline;
|
||||||
|
HPipeline extinctionVDBPipeline;
|
||||||
HPipeline frustumAmbientPipeline;
|
HPipeline frustumAmbientPipeline;
|
||||||
HPipeline frustumAnisotropyPipeline;
|
HPipeline frustumAnisotropyPipeline;
|
||||||
HPipeline frustumFogPipeline;
|
HPipeline frustumFogPipeline;
|
||||||
|
HPipeline frustumLightPropNXPipeline;
|
||||||
|
HPipeline frustumLightPropNYPipeline;
|
||||||
|
HPipeline frustumLightPropPXPipeline;
|
||||||
|
HPipeline frustumLightPropPYPipeline;
|
||||||
|
HPipeline frustumParticlePipeline;
|
||||||
HPipeline frustumPointLightScatterPipeline;
|
HPipeline frustumPointLightScatterPipeline;
|
||||||
HPipeline frustumRaymarchPipeline;
|
HPipeline frustumRaymarchPipeline;
|
||||||
HPipeline frustumSunlightVisPipeline;
|
HPipeline frustumSunlightVisPipeline;
|
||||||
HPipeline frustumTemporalPipeline;
|
HPipeline frustumTemporalFloatPipeline;
|
||||||
|
HPipeline frustumTemporalFloat4Pipeline;
|
||||||
|
HPipeline frustumVDBPipeline;
|
||||||
|
HPipeline frustumVDBLQPipeline;
|
||||||
|
HPipeline frustumDepthTestPipeline;
|
||||||
|
HPipeline particleClearPipeline;
|
||||||
|
HPipeline particleHitPipeline;
|
||||||
|
HPipeline particleListPipeline;
|
||||||
|
HPipeline particleTilesPipeline;
|
||||||
HPipeline pointLightShadowPipeline;
|
HPipeline pointLightShadowPipeline;
|
||||||
HPipeline sunlightScatterPipeline;
|
HPipeline sunlightScatterPipeline;
|
||||||
HPipeline sunlightShadowPipeline;
|
HPipeline sunlightShadowPipeline;
|
||||||
|
@ -514,12 +651,19 @@ struct VolumetricLight
|
||||||
HTexture sunShadowTextures[4]; // cube, R = transmittance
|
HTexture sunShadowTextures[4]; // cube, R = transmittance
|
||||||
HTexture ambientLightTextureA; // box, can be NULL, RGB = ambient.rgb, A = directional.r
|
HTexture ambientLightTextureA; // box, can be NULL, RGB = ambient.rgb, A = directional.r
|
||||||
HTexture ambientLightTextureB; // box, can be NULL, RG = directional.gb, B = longitude, A = latitude
|
HTexture ambientLightTextureB; // box, can be NULL, RG = directional.gb, B = longitude, A = latitude
|
||||||
|
HTexture frustumVisTexture; // screen tiles, R = Z index of furthest visible froxel tile
|
||||||
|
HBuffer particleTileBuffer; // voxel tiles: StructuredBuffer<Tile>
|
||||||
|
HBuffer particleCounterBuffer; // global counters: StructuredBuffer<Counters>
|
||||||
|
HBuffer particleTileIndexBuffer; // flattened voxel tile indices: StructuredBuffer<uint>
|
||||||
|
HBuffer particleIndexBuffer; // particle indices: StructuredBuffer<uint>
|
||||||
|
HBuffer particleDispatchBuffer; // indirect dispatch buffer
|
||||||
uvec3_t frustumSize; // frustum volume pixel counts
|
uvec3_t frustumSize; // frustum volume pixel counts
|
||||||
uvec3_t frustumTileScale; // by how much do we divide
|
uvec3_t frustumTileScale; // by how much do we divide
|
||||||
uvec3_t frustumTileSize; // frustum volume tile pixel counts
|
uvec3_t frustumTileSize; // frustum volume tile pixel counts
|
||||||
uvec3_t extinctionSize; // extinction volume pixel counts
|
uvec3_t extinctionSize; // extinction volume pixel counts
|
||||||
uvec3_t extinctionTileScale; // by how much do we divide
|
uvec3_t extinctionTileScale; // by how much do we divide
|
||||||
uvec3_t extinctionTileSize; // extinction volume tile pixel counts
|
uvec3_t extinctionTileSize; // extinction volume tile pixel counts
|
||||||
|
uint32_t maxParticleIndexCount; // uint count in particleIndexBuffer
|
||||||
uint32_t shadowPixelCount; // @TODO: transform into uvec3_t as well
|
uint32_t shadowPixelCount; // @TODO: transform into uvec3_t as well
|
||||||
uint32_t jitterCounter;
|
uint32_t jitterCounter;
|
||||||
uint32_t depthMip; // has to match the X/Y scale of frustumSize
|
uint32_t depthMip; // has to match the X/Y scale of frustumSize
|
||||||
|
@ -527,8 +671,10 @@ struct VolumetricLight
|
||||||
float pointShadowVolumeScale; // how many world units per pixel
|
float pointShadowVolumeScale; // how many world units per pixel
|
||||||
vec4_t sunShadowVolumeScale; // how many world units per pixel
|
vec4_t sunShadowVolumeScale; // how many world units per pixel
|
||||||
uvec3_t sunShadowSize; // sunlight shadow volume pixel counts
|
uvec3_t sunShadowSize; // sunlight shadow volume pixel counts
|
||||||
vec3_t ambientColor;
|
vec3_t ambientColorGUI;
|
||||||
|
vec3_t ambientColor; // normalized to 0.5 brightness
|
||||||
float ambientIntensity;
|
float ambientIntensity;
|
||||||
|
float pointLightIntensity = 20.0f;
|
||||||
vec3_t debugCameraPosition;
|
vec3_t debugCameraPosition;
|
||||||
float debugBoxScale = 1.0f;
|
float debugBoxScale = 1.0f;
|
||||||
float debugExtinctionScale = 50.0f;
|
float debugExtinctionScale = 50.0f;
|
||||||
|
@ -540,10 +686,17 @@ struct VolumetricLight
|
||||||
bool lockCameraPosition = false;
|
bool lockCameraPosition = false;
|
||||||
bool firstFrame = true;
|
bool firstFrame = true;
|
||||||
bool windowActive = false;
|
bool windowActive = false;
|
||||||
|
bool drawSunlight = true;
|
||||||
|
bool enableLightGrid = true;
|
||||||
vec3_t mapBoxMin;
|
vec3_t mapBoxMin;
|
||||||
vec3_t mapBoxMax;
|
vec3_t mapBoxMax;
|
||||||
vec3_t lightGridCenter;
|
vec3_t lightGridCenter;
|
||||||
float debugSphereScale = 0.5f;
|
float debugSphereScale = 0.5f;
|
||||||
|
int xySubsampling = 2;
|
||||||
|
int zResolution = 256;
|
||||||
|
int extinctionResolution = 128;
|
||||||
|
int sunShadowResolution = 128;
|
||||||
|
int pointShadowResolution = 64;
|
||||||
};
|
};
|
||||||
|
|
||||||
#pragma pack(push, 1)
|
#pragma pack(push, 1)
|
||||||
|
@ -551,7 +704,8 @@ struct SunlightData
|
||||||
{
|
{
|
||||||
vec3_t direction;
|
vec3_t direction;
|
||||||
vec3_t color;
|
vec3_t color;
|
||||||
float intensity;
|
float intensityVL = 40.0f;
|
||||||
|
float intensityDL = 2.0f;
|
||||||
};
|
};
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
@ -657,6 +811,7 @@ struct CRP : IRenderPipeline
|
||||||
HTexture lightTexture;
|
HTexture lightTexture;
|
||||||
HTexture shadingPositionTexture;
|
HTexture shadingPositionTexture;
|
||||||
HTexture renderTarget;
|
HTexture renderTarget;
|
||||||
|
HTexture blackbodyTexture;
|
||||||
TextureFormat::Id renderTargetFormat;
|
TextureFormat::Id renderTargetFormat;
|
||||||
HTexture renderTargets[2];
|
HTexture renderTargets[2];
|
||||||
uint32_t renderTargetIndex; // the one to write to
|
uint32_t renderTargetIndex; // the one to write to
|
||||||
|
@ -705,6 +860,8 @@ struct CRP : IRenderPipeline
|
||||||
GBufferViz gbufferViz;
|
GBufferViz gbufferViz;
|
||||||
SunlightEditor sunlightEditor;
|
SunlightEditor sunlightEditor;
|
||||||
SunlightData sunlightData;
|
SunlightData sunlightData;
|
||||||
|
ParticleSystem particleSystem;
|
||||||
|
NanoVDBManager vdbManager;
|
||||||
};
|
};
|
||||||
|
|
||||||
HPipeline CreateComputePipeline(const char* name, const ShaderByteCode& shader);
|
HPipeline CreateComputePipeline(const char* name, const ShaderByteCode& shader);
|
||||||
|
|
|
@ -42,7 +42,6 @@ struct SceneViewConst
|
||||||
{
|
{
|
||||||
MaxViews = 1024,
|
MaxViews = 1024,
|
||||||
LightBytes = sizeof(DynamicLight),
|
LightBytes = sizeof(DynamicLight),
|
||||||
MaxLights = SCENE_VIEW_MAX_LIGHTS,
|
|
||||||
StructBytes = sizeof(SceneView),
|
StructBytes = sizeof(SceneView),
|
||||||
BufferBytes = MaxViews * StructBytes
|
BufferBytes = MaxViews * StructBytes
|
||||||
};
|
};
|
||||||
|
@ -595,6 +594,8 @@ void CRP::Init()
|
||||||
volumetricLight.Init();
|
volumetricLight.Init();
|
||||||
gbufferViz.Init();
|
gbufferViz.Init();
|
||||||
sunlightEditor.Init();
|
sunlightEditor.Init();
|
||||||
|
particleSystem.Init();
|
||||||
|
vdbManager.Init();
|
||||||
|
|
||||||
srp.firstInit = false;
|
srp.firstInit = false;
|
||||||
}
|
}
|
||||||
|
@ -603,6 +604,7 @@ void CRP::LoadResources()
|
||||||
{
|
{
|
||||||
const int flags = IMG_NOPICMIP | IMG_NOMIPMAP | IMG_NOIMANIP | IMG_NOAF;
|
const int flags = IMG_NOPICMIP | IMG_NOMIPMAP | IMG_NOIMANIP | IMG_NOAF;
|
||||||
blueNoise2D = LoadTexture("textures/stbn_2d.tga", flags, TW_REPEAT);
|
blueNoise2D = LoadTexture("textures/stbn_2d.tga", flags, TW_REPEAT);
|
||||||
|
blackbodyTexture = LoadTexture("textures/blackbody.tga", flags, TW_CLAMP_TO_EDGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CRP::ShutDown(bool fullShutDown)
|
void CRP::ShutDown(bool fullShutDown)
|
||||||
|
@ -621,6 +623,8 @@ void CRP::BeginFrame()
|
||||||
// have it be first to we can use ImGUI in the other components too
|
// have it be first to we can use ImGUI in the other components too
|
||||||
imgui.BeginFrame();
|
imgui.BeginFrame();
|
||||||
|
|
||||||
|
vdbManager.BeforeFrame();
|
||||||
|
|
||||||
// must be run outside of the RHI::BeginFrame/RHI::EndFrame pair
|
// must be run outside of the RHI::BeginFrame/RHI::EndFrame pair
|
||||||
const bool rtasUpdate =
|
const bool rtasUpdate =
|
||||||
dynamicLights.WantRTASUpdate(tr.rtRefdef) ||
|
dynamicLights.WantRTASUpdate(tr.rtRefdef) ||
|
||||||
|
@ -667,6 +671,7 @@ void CRP::EndFrame()
|
||||||
magnifier.DrawGUI();
|
magnifier.DrawGUI();
|
||||||
sunlightEditor.DrawGUI();
|
sunlightEditor.DrawGUI();
|
||||||
volumetricLight.DrawGUI();
|
volumetricLight.DrawGUI();
|
||||||
|
vdbManager.DrawGUI();
|
||||||
imgui.Draw(renderTarget);
|
imgui.Draw(renderTarget);
|
||||||
toneMap.DrawToneMap();
|
toneMap.DrawToneMap();
|
||||||
magnifier.Draw();
|
magnifier.Draw();
|
||||||
|
@ -834,15 +839,12 @@ void CRP::ExecuteRenderCommands(const byte* data, bool /*readbackRequested*/)
|
||||||
ui.End();
|
ui.End();
|
||||||
break;
|
break;
|
||||||
case RC_BEGIN_3D:
|
case RC_BEGIN_3D:
|
||||||
// @TODO:
|
|
||||||
srp.renderMode = RenderMode::None;
|
srp.renderMode = RenderMode::None;
|
||||||
break;
|
break;
|
||||||
case RC_END_3D:
|
case RC_END_3D:
|
||||||
// @TODO:
|
|
||||||
srp.renderMode = RenderMode::None;
|
srp.renderMode = RenderMode::None;
|
||||||
break;
|
break;
|
||||||
case RC_END_SCENE:
|
case RC_END_SCENE:
|
||||||
// @TODO: post-processing
|
|
||||||
break;
|
break;
|
||||||
case RC_BEGIN_NK:
|
case RC_BEGIN_NK:
|
||||||
nuklear.Begin(renderTarget);
|
nuklear.Begin(renderTarget);
|
||||||
|
@ -884,6 +886,7 @@ void CRP::DrawSceneView3D(const drawSceneViewCommand_t& cmd)
|
||||||
|
|
||||||
prepass.Draw(cmd);
|
prepass.Draw(cmd);
|
||||||
BuildDepthPyramid();
|
BuildDepthPyramid();
|
||||||
|
particleSystem.Draw();
|
||||||
dynamicLights.DrawBegin();
|
dynamicLights.DrawBegin();
|
||||||
if(volumetricLight.ShouldDraw())
|
if(volumetricLight.ShouldDraw())
|
||||||
{
|
{
|
||||||
|
@ -1045,8 +1048,6 @@ void CRP::UploadSceneViewData()
|
||||||
memcpy(scene.prevProjectionMatrix, tr.prevProjMatrix, sizeof(scene.prevProjectionMatrix));
|
memcpy(scene.prevProjectionMatrix, tr.prevProjMatrix, sizeof(scene.prevProjectionMatrix));
|
||||||
|
|
||||||
// we want the first Z slice to be closest to the sun to simplify ray marching
|
// we want the first Z slice to be closest to the sun to simplify ray marching
|
||||||
vec3_t zDown;
|
|
||||||
VectorSet(zDown, 0, 0, -1);
|
|
||||||
SunToZMatrix(scene.sunToZMatrix);
|
SunToZMatrix(scene.sunToZMatrix);
|
||||||
R_InvMatrix3x3(scene.sunToZMatrix, scene.zToSunMatrix);
|
R_InvMatrix3x3(scene.sunToZMatrix, scene.zToSunMatrix);
|
||||||
|
|
||||||
|
@ -1076,7 +1077,9 @@ void CRP::UploadSceneViewData()
|
||||||
|
|
||||||
VectorCopy(sunlightData.direction, scene.sunDirection);
|
VectorCopy(sunlightData.direction, scene.sunDirection);
|
||||||
VectorCopy(sunlightData.color, scene.sunColor);
|
VectorCopy(sunlightData.color, scene.sunColor);
|
||||||
scene.sunIntensity = sunlightData.intensity;
|
scene.sunIntensityVL = sunlightData.intensityVL;
|
||||||
|
scene.sunIntensityDL = sunlightData.intensityDL;
|
||||||
|
scene.pointLightIntensityVL = volumetricLight.pointLightIntensity;
|
||||||
|
|
||||||
VectorCopy(volumetricLight.ambientColor, scene.ambientColor);
|
VectorCopy(volumetricLight.ambientColor, scene.ambientColor);
|
||||||
scene.ambientIntensity = volumetricLight.ambientIntensity;
|
scene.ambientIntensity = volumetricLight.ambientIntensity;
|
||||||
|
|
|
@ -0,0 +1,941 @@
|
||||||
|
/*
|
||||||
|
===========================================================================
|
||||||
|
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 - NanoVDB support
|
||||||
|
|
||||||
|
|
||||||
|
#include "crp_local.h"
|
||||||
|
#include "../client/cl_imgui.h"
|
||||||
|
|
||||||
|
#define NANOVDB_MAGIC_NUMBER 0x304244566F6E614Eul // "NanoVDB0"
|
||||||
|
#define NANOVDB_MAGIC_GRID 0x314244566F6E614Eul // "NanoVDB1"
|
||||||
|
#define NANOVDB_MAGIC_FILE 0x324244566F6E614Eul // "NanoVDB2"
|
||||||
|
|
||||||
|
#define NANOVDB_GRID_BUFFER_ALIGNMENT 32
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
File structure:
|
||||||
|
FileHeader [GridHeader GridName]+ [GridData]+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
|
||||||
|
struct FileHeader
|
||||||
|
{
|
||||||
|
uint64_t magic;
|
||||||
|
uint32_t version;
|
||||||
|
uint16_t gridCount;
|
||||||
|
uint16_t codec;
|
||||||
|
|
||||||
|
bool IsValid()
|
||||||
|
{
|
||||||
|
return magic == NANOVDB_MAGIC_NUMBER || magic == NANOVDB_MAGIC_FILE;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(FileHeader) == 16, "Invalid FileHeader size");
|
||||||
|
|
||||||
|
struct FileGridHeader
|
||||||
|
{
|
||||||
|
uint64_t memoryByteCount;
|
||||||
|
uint64_t fileByteCount;
|
||||||
|
uint64_t gridNameHashKey;
|
||||||
|
uint64_t activeVoxelCount;
|
||||||
|
uint32_t gridType;
|
||||||
|
uint32_t gridClass;
|
||||||
|
double worldBBox[6]; // AABB in world space
|
||||||
|
int32_t indexBBox[6]; // AABB in index space
|
||||||
|
double voxelSize[3]; // in world units
|
||||||
|
uint32_t gridNameLength; // it includes the NULL terminator
|
||||||
|
uint32_t nodeCount[4]; // # nodes per level
|
||||||
|
uint32_t tileCount[3]; // # of active tiles per level
|
||||||
|
uint16_t codec;
|
||||||
|
uint16_t padding;
|
||||||
|
uint32_t versionNumber;
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(FileGridHeader) == 176, "Invalid FileHeader size");
|
||||||
|
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
struct FileGrid
|
||||||
|
{
|
||||||
|
uint32_t byteOffset;
|
||||||
|
uint32_t byteCount;
|
||||||
|
|
||||||
|
bool IsValid() const
|
||||||
|
{
|
||||||
|
return byteOffset > 0 && byteCount > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Id
|
||||||
|
{
|
||||||
|
Smoke,
|
||||||
|
Fire,
|
||||||
|
Count
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static void ScaleMatrix(matrix3x3_t m, const vec3_t scale)
|
||||||
|
{
|
||||||
|
m[0] = scale[0];
|
||||||
|
m[1] = 0.0f;
|
||||||
|
m[2] = 0.0f;
|
||||||
|
m[3] = 0.0f;
|
||||||
|
m[4] = scale[1];
|
||||||
|
m[5] = 0.0f;
|
||||||
|
m[6] = 0.0f;
|
||||||
|
m[7] = 0.0f;
|
||||||
|
m[8] = scale[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RotationMatrixX(matrix3x3_t m, float angleRad)
|
||||||
|
{
|
||||||
|
const float c = cosf(angleRad);
|
||||||
|
const float s = sinf(angleRad);
|
||||||
|
m[0] = 1.0f;
|
||||||
|
m[1] = 0.0f;
|
||||||
|
m[2] = 0.0f;
|
||||||
|
m[3] = 0.0f;
|
||||||
|
m[4] = c;
|
||||||
|
m[5] = -s;
|
||||||
|
m[6] = 0.0f;
|
||||||
|
m[7] = s;
|
||||||
|
m[8] = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RotationMatrixY(matrix3x3_t m, float angleRad)
|
||||||
|
{
|
||||||
|
const float c = cosf(angleRad);
|
||||||
|
const float s = sinf(angleRad);
|
||||||
|
m[0] = c;
|
||||||
|
m[1] = 0.0f;
|
||||||
|
m[2] = s;
|
||||||
|
m[3] = 0.0f;
|
||||||
|
m[4] = 1.0f;
|
||||||
|
m[5] = 0.0f;
|
||||||
|
m[6] = -s;
|
||||||
|
m[7] = 0.0f;
|
||||||
|
m[8] = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RotationMatrixZ(matrix3x3_t m, float angleRad)
|
||||||
|
{
|
||||||
|
const float c = cosf(angleRad);
|
||||||
|
const float s = sinf(angleRad);
|
||||||
|
m[0] = c;
|
||||||
|
m[1] = -s;
|
||||||
|
m[2] = 0.0f;
|
||||||
|
m[3] = s;
|
||||||
|
m[4] = c;
|
||||||
|
m[5] = 0.0f;
|
||||||
|
m[6] = 0.0f;
|
||||||
|
m[7] = 0.0f;
|
||||||
|
m[8] = 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void MultMatrix(matrix3x3_t m, const matrix3x3_t a, const matrix3x3_t b)
|
||||||
|
{
|
||||||
|
m[0] = a[0] * b[0] + a[1] * b[3] + a[2] * b[6];
|
||||||
|
m[1] = a[0] * b[1] + a[1] * b[4] + a[2] * b[7];
|
||||||
|
m[2] = a[0] * b[2] + a[1] * b[5] + a[2] * b[8];
|
||||||
|
m[3] = a[3] * b[0] + a[4] * b[3] + a[5] * b[6];
|
||||||
|
m[4] = a[3] * b[1] + a[4] * b[4] + a[5] * b[7];
|
||||||
|
m[5] = a[3] * b[2] + a[4] * b[5] + a[5] * b[8];
|
||||||
|
m[6] = a[6] * b[0] + a[7] * b[3] + a[8] * b[6];
|
||||||
|
m[7] = a[6] * b[1] + a[7] * b[4] + a[8] * b[7];
|
||||||
|
m[8] = a[6] * b[2] + a[7] * b[5] + a[8] * b[8];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void FindGrids(FileGrid* grids, fileHandle_t fh, int byteCount, const VDBSequenceDesc& desc)
|
||||||
|
{
|
||||||
|
FileHeader fileHeader;
|
||||||
|
FS_Read(&fileHeader, sizeof(fileHeader), fh);
|
||||||
|
if(!fileHeader.IsValid())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// for all grids
|
||||||
|
uint32_t gridByteCounts[16] = {};
|
||||||
|
Q_assert(fileHeader.gridCount <= ARRAY_LEN(gridByteCounts));
|
||||||
|
|
||||||
|
// for grids of interest
|
||||||
|
int fileToCNQ3[FileGrid::Count];
|
||||||
|
for(int g = 0; g < FileGrid::Count; g++)
|
||||||
|
{
|
||||||
|
fileToCNQ3[g] = -1;
|
||||||
|
grids[g].byteOffset = 0;
|
||||||
|
grids[g].byteCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint32_t fileGridCount = (uint32_t)fileHeader.gridCount;
|
||||||
|
for(uint32_t g = 0; g < fileGridCount; g++)
|
||||||
|
{
|
||||||
|
FileGridHeader gridHeader;
|
||||||
|
FS_Read(&gridHeader, sizeof(gridHeader), fh);
|
||||||
|
|
||||||
|
char gridName[64];
|
||||||
|
Q_assert(gridHeader.gridNameLength <= ARRAY_LEN(gridName));
|
||||||
|
FS_Read(gridName, (int)gridHeader.gridNameLength, fh);
|
||||||
|
|
||||||
|
// vdb_lod.exe auto-renames "density" to "density_level_2" for mip level 2
|
||||||
|
if(Q_stristr(gridName, desc.smokeGridName) != NULL)
|
||||||
|
{
|
||||||
|
fileToCNQ3[g] = (int)FileGrid::Smoke;
|
||||||
|
}
|
||||||
|
else if(Q_stristr(gridName, desc.fireGridName) != NULL)
|
||||||
|
{
|
||||||
|
fileToCNQ3[g] = (int)FileGrid::Fire;
|
||||||
|
}
|
||||||
|
gridByteCounts[g] = gridHeader.fileByteCount;
|
||||||
|
|
||||||
|
if(fileToCNQ3[g] >= 0 && fileToCNQ3[g] < FileGrid::Count)
|
||||||
|
{
|
||||||
|
grids[fileToCNQ3[g]].byteOffset = 0;
|
||||||
|
grids[fileToCNQ3[g]].byteCount = gridHeader.fileByteCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(uint32_t g = 0; g < fileGridCount; g++)
|
||||||
|
{
|
||||||
|
uint64_t magic;
|
||||||
|
FS_Read(&magic, sizeof(magic), fh);
|
||||||
|
Q_assert(magic == NANOVDB_MAGIC_NUMBER || magic == NANOVDB_MAGIC_GRID);
|
||||||
|
if(fileToCNQ3[g] >= 0 && fileToCNQ3[g] < FileGrid::Count)
|
||||||
|
{
|
||||||
|
grids[fileToCNQ3[g]].byteOffset = (uint32_t)FS_FTell(fh) - 8;
|
||||||
|
}
|
||||||
|
FS_Seek(fh, gridByteCounts[g] - 8, FS_SEEK_CUR);
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_assert(grids[FileGrid::Smoke].IsValid() || grids[FileGrid::Fire].IsValid());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ReadTransform(vec3_t originOffset, vec3_t scale, fileHandle_t fh, int byteOffset)
|
||||||
|
{
|
||||||
|
FS_Seek(fh, byteOffset + 296, FS_SEEK_SET);
|
||||||
|
FS_Read(&scale[0], 4, fh);
|
||||||
|
FS_Seek(fh, byteOffset + 312, FS_SEEK_SET);
|
||||||
|
FS_Read(&scale[1], 4, fh);
|
||||||
|
FS_Seek(fh, byteOffset + 328, FS_SEEK_SET);
|
||||||
|
FS_Read(&scale[2], 4, fh);
|
||||||
|
FS_Seek(fh, byteOffset + 368, FS_SEEK_SET);
|
||||||
|
FS_Read(originOffset, 12, fh);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ReadTransform(vec3_t originOffset, vec3_t scale, fileHandle_t fh, const FileGrid* grids)
|
||||||
|
{
|
||||||
|
if(grids[FileGrid::Smoke].IsValid())
|
||||||
|
{
|
||||||
|
ReadTransform(originOffset, scale, fh, grids[FileGrid::Smoke].byteOffset);
|
||||||
|
}
|
||||||
|
else if(grids[FileGrid::Fire].IsValid())
|
||||||
|
{
|
||||||
|
ReadTransform(originOffset, scale, fh, grids[FileGrid::Fire].byteOffset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void VectorScaleGUI(vec3_t vector, const char* id)
|
||||||
|
{
|
||||||
|
ImGui::Text(" ");
|
||||||
|
ImGui::SameLine();
|
||||||
|
if(ImGui::Button(va("x2##%s", id)))
|
||||||
|
{
|
||||||
|
VectorScale(vector, 2.0f, vector);
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if(ImGui::Button(va("/2##%s", id)))
|
||||||
|
{
|
||||||
|
VectorScale(vector, 0.5f, vector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void UploadFrame(
|
||||||
|
uint32_t& smokeByteOffset, uint32_t& fireByteOffset, uint32_t& gpuBufferOffset,
|
||||||
|
HBuffer buffer, const NanoVDBManager::CPUFrame& frame)
|
||||||
|
{
|
||||||
|
if(frame.fireByteCount > 0 || frame.smokeByteCount > 0)
|
||||||
|
{
|
||||||
|
fileHandle_t fh;
|
||||||
|
const int fileByteCount = FS_FOpenFileRead(frame.filePath, &fh, qfalse);
|
||||||
|
if(fileByteCount > 0)
|
||||||
|
{
|
||||||
|
if(frame.smokeByteCount > 0)
|
||||||
|
{
|
||||||
|
smokeByteOffset = gpuBufferOffset;
|
||||||
|
FS_Seek(fh, frame.smokeByteOffset, FS_SEEK_SET);
|
||||||
|
const uint32_t gridByteCount = AlignUp<uint32_t>(frame.smokeByteCount, NANOVDB_GRID_BUFFER_ALIGNMENT);
|
||||||
|
uint8_t* const mapped = BeginBufferUpload(buffer, gpuBufferOffset, gridByteCount);
|
||||||
|
FS_Read(mapped, frame.smokeByteCount, fh);
|
||||||
|
EndBufferUpload(buffer);
|
||||||
|
gpuBufferOffset += gridByteCount;
|
||||||
|
}
|
||||||
|
if(frame.fireByteCount > 0)
|
||||||
|
{
|
||||||
|
fireByteOffset = gpuBufferOffset;
|
||||||
|
FS_Seek(fh, frame.fireByteOffset, FS_SEEK_SET);
|
||||||
|
const uint32_t gridByteCount = AlignUp<uint32_t>(frame.fireByteCount, NANOVDB_GRID_BUFFER_ALIGNMENT);
|
||||||
|
uint8_t* const mapped = BeginBufferUpload(buffer, gpuBufferOffset, gridByteCount);
|
||||||
|
FS_Read(mapped, frame.fireByteCount, fh);
|
||||||
|
EndBufferUpload(buffer);
|
||||||
|
gpuBufferOffset += gridByteCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(fileByteCount >= 0)
|
||||||
|
{
|
||||||
|
FS_FCloseFile(fh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int64_t GetTimeStampUS(int ms, int us)
|
||||||
|
{
|
||||||
|
return int64_t(1000) * (int64_t)ms + (int64_t)us;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void NanoVDBManager::Init()
|
||||||
|
{
|
||||||
|
sequences.Clear();
|
||||||
|
instances.Clear();
|
||||||
|
drawInstances.Clear();
|
||||||
|
cpuFrames.Clear();
|
||||||
|
gpuFrames.Clear();
|
||||||
|
streamBufferIndex = 0;
|
||||||
|
|
||||||
|
{
|
||||||
|
streamBufferByteCount = 256 << 20; // @TODO: CVar
|
||||||
|
BufferDesc desc("", streamBufferByteCount, ResourceStates::ComputeShaderAccessBit);
|
||||||
|
desc.shortLifeTime = true;
|
||||||
|
desc.structureByteCount = 4;
|
||||||
|
for(int i = 0; i < ARRAY_LEN(streamBuffers); i++)
|
||||||
|
{
|
||||||
|
desc.name = va("NanoVDB stream #%d", i + 1);
|
||||||
|
streamBuffers[i] = CreateBuffer(desc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NanoVDBManager::BeforeFrame()
|
||||||
|
{
|
||||||
|
drawInstances.Clear();
|
||||||
|
streamedFrames.Clear();
|
||||||
|
if(!tr.hasWorldRender)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
streamBufferIndex = (streamBufferIndex + 1) % ARRAY_LEN(streamBuffers);
|
||||||
|
const HBuffer streamBuffer = streamBuffers[streamBufferIndex];
|
||||||
|
|
||||||
|
uint32_t gpuBufferOffset = NANOVDB_GRID_BUFFER_ALIGNMENT;
|
||||||
|
|
||||||
|
const int64_t renderTimeUS = GetTimeStampUS(tr.worldRenderTimeMS, tr.worldRenderTimeUS);
|
||||||
|
for(int i = (int)instances.count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
const Instance& inst = instances[i];
|
||||||
|
if(inst.loop)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Sequence& seq = sequences[inst.sequenceIndex];
|
||||||
|
const float durationSec = (float)seq.frameCount / inst.frameRate;
|
||||||
|
const int64_t durationUS = (int64_t)ceilf(durationSec * 1000000.0f);
|
||||||
|
const int64_t endTimeUS = GetTimeStampUS(inst.startTimeMS, inst.startTimeUS) + durationUS;
|
||||||
|
if(renderTimeUS >= endTimeUS)
|
||||||
|
{
|
||||||
|
instances.RemoveUnordered((uint32_t)i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(uint32_t i = 0; i < instances.count; i++)
|
||||||
|
{
|
||||||
|
const Instance& inst = instances[i];
|
||||||
|
const Sequence& seq = sequences[inst.sequenceIndex];
|
||||||
|
const int64_t startTimeUS = GetTimeStampUS(inst.startTimeMS, inst.startTimeUS);
|
||||||
|
const int64_t usPerFrame = (int64_t)(1000000.0f / instances[i].frameRate);
|
||||||
|
const uint32_t frameIndex = (uint32_t)((renderTimeUS - startTimeUS) / usPerFrame) % seq.frameCount;
|
||||||
|
const uint32_t remainder = (uint32_t)((renderTimeUS - startTimeUS) % usPerFrame);
|
||||||
|
const uint32_t frameIndex2 = min(frameIndex + 1, seq.frameCount - 1);
|
||||||
|
const float t = (float)remainder / (float)usPerFrame; // lerp(frame, frame2, t)
|
||||||
|
|
||||||
|
DrawInstance drawInst = {};
|
||||||
|
if(IsNullHandle(seq.buffer))
|
||||||
|
{
|
||||||
|
const CPUFrame& frame = cpuFrames[seq.firstFrameIndex + frameIndex];
|
||||||
|
const CPUFrame& frame2 = cpuFrames[seq.firstFrameIndex + frameIndex2];
|
||||||
|
const int sf1 = FindStreamedFrameIndex(inst.sequenceIndex, frameIndex);
|
||||||
|
const int sf2 = FindStreamedFrameIndex(inst.sequenceIndex, frameIndex2);
|
||||||
|
|
||||||
|
uint32_t requestedByteCount = 0;
|
||||||
|
if(sf1 >= 0)
|
||||||
|
{
|
||||||
|
drawInst.smokeByteOffset = streamedFrames[sf1].smokeByteOffset;
|
||||||
|
drawInst.fireByteOffset = streamedFrames[sf1].flamesByteOffset;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
requestedByteCount += frame.smokeByteCount + frame.fireByteCount;
|
||||||
|
}
|
||||||
|
if(sf2 >= 0)
|
||||||
|
{
|
||||||
|
drawInst.smokeByteOffset2 = streamedFrames[sf2].smokeByteOffset;
|
||||||
|
drawInst.fireByteOffset2 = streamedFrames[sf2].flamesByteOffset;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
requestedByteCount += frame2.smokeByteOffset + frame2.fireByteCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
drawInst.buffer = streamBuffer;
|
||||||
|
if(requestedByteCount > 0 &&
|
||||||
|
gpuBufferOffset + requestedByteCount <= streamBufferByteCount)
|
||||||
|
{
|
||||||
|
UploadFrame(drawInst.smokeByteOffset, drawInst.fireByteOffset, gpuBufferOffset, streamBuffer, frame);
|
||||||
|
UploadFrame(drawInst.smokeByteOffset2, drawInst.fireByteOffset2, gpuBufferOffset, streamBuffer, frame2);
|
||||||
|
|
||||||
|
StreamedFrame sf = {};
|
||||||
|
sf.sequenceIndex = inst.sequenceIndex;
|
||||||
|
if(drawInst.smokeByteOffset > 0 || drawInst.fireByteOffset > 0)
|
||||||
|
{
|
||||||
|
sf.frameIndex = frameIndex;
|
||||||
|
sf.smokeByteOffset = drawInst.smokeByteOffset;
|
||||||
|
sf.flamesByteOffset = drawInst.fireByteOffset;
|
||||||
|
streamedFrames.Add(sf);
|
||||||
|
}
|
||||||
|
if(drawInst.smokeByteOffset2 > 0 || drawInst.fireByteOffset2 > 0)
|
||||||
|
{
|
||||||
|
sf.frameIndex = frameIndex2;
|
||||||
|
sf.smokeByteOffset = drawInst.smokeByteOffset2;
|
||||||
|
sf.flamesByteOffset = drawInst.fireByteOffset2;
|
||||||
|
streamedFrames.Add(sf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const NanoVDBManager::GPUFrame& frame = gpuFrames[seq.firstFrameIndex + frameIndex];
|
||||||
|
const NanoVDBManager::GPUFrame& frame2 = gpuFrames[seq.firstFrameIndex + frameIndex2];
|
||||||
|
drawInst.buffer = seq.buffer;
|
||||||
|
drawInst.fireByteOffset = frame.fireByteOffset;
|
||||||
|
drawInst.fireByteOffset2 = frame2.fireByteOffset;
|
||||||
|
drawInst.smokeByteOffset = frame.smokeByteOffset;
|
||||||
|
drawInst.smokeByteOffset2 = frame2.smokeByteOffset;
|
||||||
|
}
|
||||||
|
drawInst.t = t;
|
||||||
|
drawInstances.Add(drawInst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NanoVDBManager::AddSequence(const VDBSequenceDesc& desc)
|
||||||
|
{
|
||||||
|
if(!tr.worldMapLoaded)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(instances.IsFull())
|
||||||
|
{
|
||||||
|
ri.Printf(PRINT_WARNING, "^3WARNING: NanoVDB instance limit reached\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3_t originOffset = {};
|
||||||
|
vec3_t scale;
|
||||||
|
VectorSet(scale, 1, 1, 1);
|
||||||
|
|
||||||
|
int sequenceIndex = -1;
|
||||||
|
for(uint32_t i = 0; i < sequences.count; i++)
|
||||||
|
{
|
||||||
|
if(Q_stricmp(sequences[i].folderPath, desc.folderPath) == 0)
|
||||||
|
{
|
||||||
|
sequenceIndex = (int)i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(sequenceIndex < 0 && !sequences.IsFull())
|
||||||
|
{
|
||||||
|
HBuffer gpuBuffer = RHI_MAKE_NULL_HANDLE();
|
||||||
|
uint32_t gpuByteCount = 0;
|
||||||
|
uint32_t firstFrameIndex = 0;
|
||||||
|
int fileCount = 0;
|
||||||
|
|
||||||
|
if(desc.gpuResident)
|
||||||
|
{
|
||||||
|
int startTimeMS = Sys_Milliseconds();
|
||||||
|
|
||||||
|
gpuByteCount = NANOVDB_GRID_BUFFER_ALIGNMENT;
|
||||||
|
char** fileList = ri.FS_ListFiles(desc.folderPath, ".nvdb", &fileCount);
|
||||||
|
for(int f = 0; f < fileCount; f++)
|
||||||
|
{
|
||||||
|
FileGrid grids[FileGrid::Count] = {};
|
||||||
|
fileHandle_t fh;
|
||||||
|
const int byteCount = FS_FOpenFileRead(va("%s/%s", desc.folderPath, fileList[f]), &fh, qfalse);
|
||||||
|
if(byteCount > 0)
|
||||||
|
{
|
||||||
|
FindGrids(grids, fh, byteCount, desc);
|
||||||
|
}
|
||||||
|
if(byteCount >= 0)
|
||||||
|
{
|
||||||
|
FS_FCloseFile(fh);
|
||||||
|
}
|
||||||
|
for(uint32_t g = 0; g < FileGrid::Count; g++)
|
||||||
|
{
|
||||||
|
if(grids[g].byteCount > 0)
|
||||||
|
{
|
||||||
|
gpuByteCount += AlignUp<uint32_t>(grids[g].byteCount, NANOVDB_GRID_BUFFER_ALIGNMENT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ri.FS_FreeFileList(fileList);
|
||||||
|
if(fileCount <= 0 || gpuByteCount <= NANOVDB_GRID_BUFFER_ALIGNMENT)
|
||||||
|
{
|
||||||
|
ri.Printf(PRINT_WARNING, "^3WARNING: invalid NanoVDB folder '%s'\n", desc.folderPath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(gpuByteCount >= uint32_t(1 << 31))
|
||||||
|
{
|
||||||
|
ri.Printf(PRINT_WARNING, "^3WARNING: NanoVDB sequence '%s' too large for GPU storage\n", desc.folderPath);
|
||||||
|
VDBSequenceDesc newDesc = desc;
|
||||||
|
newDesc.gpuResident = false;
|
||||||
|
return AddSequence(newDesc);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(gpuFrames.count + fileCount > gpuFrames.capacity)
|
||||||
|
{
|
||||||
|
ri.Printf(PRINT_WARNING, "^3WARNING: NanoVDB frame limit reached\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ri.Printf(PRINT_ALL, "NanoVDB: analyzed %s in %d ms\n", desc.folderPath, Sys_Milliseconds() - startTimeMS);
|
||||||
|
startTimeMS = Sys_Milliseconds();
|
||||||
|
|
||||||
|
gpuByteCount = AlignUp<uint32_t>(gpuByteCount, NANOVDB_GRID_BUFFER_ALIGNMENT);
|
||||||
|
BufferDesc bufferDesc("NanoVDB full sequence", gpuByteCount, ResourceStates::ComputeShaderAccessBit);
|
||||||
|
bufferDesc.shortLifeTime = true;
|
||||||
|
bufferDesc.structureByteCount = 4;
|
||||||
|
gpuBuffer = CreateBuffer(bufferDesc);
|
||||||
|
|
||||||
|
uint32_t gpuByteOffset = NANOVDB_GRID_BUFFER_ALIGNMENT;
|
||||||
|
firstFrameIndex = gpuFrames.count;
|
||||||
|
fileList = ri.FS_ListFiles(desc.folderPath, ".nvdb", &fileCount);
|
||||||
|
for(int f = 0; f < fileCount; f++)
|
||||||
|
{
|
||||||
|
GPUFrame frame = {};
|
||||||
|
FileGrid grids[FileGrid::Count] = {};
|
||||||
|
fileHandle_t fh;
|
||||||
|
const int byteCount = FS_FOpenFileRead(va("%s/%s", desc.folderPath, fileList[f]), &fh, qfalse);
|
||||||
|
if(byteCount > 0)
|
||||||
|
{
|
||||||
|
FindGrids(grids, fh, byteCount, desc);
|
||||||
|
if(grids[FileGrid::Smoke].IsValid())
|
||||||
|
{
|
||||||
|
const uint32_t gridByteCount = AlignUp<uint32_t>(grids[FileGrid::Smoke].byteCount, NANOVDB_GRID_BUFFER_ALIGNMENT);
|
||||||
|
uint8_t* const cpuBuffer = BeginBufferUpload(gpuBuffer, gpuByteOffset, gridByteCount);
|
||||||
|
FS_Seek(fh, (int)grids[FileGrid::Smoke].byteOffset, FS_SEEK_SET);
|
||||||
|
FS_Read(cpuBuffer, (int)grids[FileGrid::Smoke].byteCount, fh);
|
||||||
|
EndBufferUpload(gpuBuffer);
|
||||||
|
frame.smokeByteOffset = gpuByteOffset;
|
||||||
|
gpuByteOffset += gridByteCount;
|
||||||
|
}
|
||||||
|
if(grids[FileGrid::Fire].IsValid())
|
||||||
|
{
|
||||||
|
const uint32_t gridByteCount = AlignUp<uint32_t>(grids[FileGrid::Fire].byteCount, NANOVDB_GRID_BUFFER_ALIGNMENT);
|
||||||
|
uint8_t* const cpuBuffer = BeginBufferUpload(gpuBuffer, gpuByteOffset, gridByteCount);
|
||||||
|
FS_Seek(fh, (int)grids[FileGrid::Fire].byteOffset, FS_SEEK_SET);
|
||||||
|
FS_Read(cpuBuffer, (int)grids[FileGrid::Fire].byteCount, fh);
|
||||||
|
EndBufferUpload(gpuBuffer);
|
||||||
|
frame.fireByteOffset = gpuByteOffset;
|
||||||
|
gpuByteOffset += gridByteCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(f == 0)
|
||||||
|
{
|
||||||
|
ReadTransform(originOffset, scale, fh, grids);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(byteCount >= 0)
|
||||||
|
{
|
||||||
|
FS_FCloseFile(fh);
|
||||||
|
}
|
||||||
|
Q_assert(frame.fireByteOffset > 0 || frame.smokeByteOffset > 0);
|
||||||
|
gpuFrames.Add(frame);
|
||||||
|
}
|
||||||
|
ri.FS_FreeFileList(fileList);
|
||||||
|
|
||||||
|
ri.Printf(PRINT_ALL, "NanoVDB: processed %s in %d ms\n", desc.folderPath, Sys_Milliseconds() - startTimeMS);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const int startTimeMS = Sys_Milliseconds();
|
||||||
|
|
||||||
|
firstFrameIndex = cpuFrames.count;
|
||||||
|
char** fileList = ri.FS_ListFiles(desc.folderPath, ".nvdb", &fileCount);
|
||||||
|
for(int f = 0; f < fileCount; f++)
|
||||||
|
{
|
||||||
|
FileGrid grids[FileGrid::Count] = {};
|
||||||
|
const char* const filePath = va("%s/%s", desc.folderPath, fileList[f]);
|
||||||
|
fileHandle_t fh;
|
||||||
|
const int byteCount = FS_FOpenFileRead(filePath, &fh, qfalse);
|
||||||
|
if(byteCount > 0)
|
||||||
|
{
|
||||||
|
FindGrids(grids, fh, byteCount, desc);
|
||||||
|
CPUFrame frame = {};
|
||||||
|
Q_strncpyz(frame.filePath, filePath, sizeof(frame.filePath));
|
||||||
|
frame.fireByteOffset = grids[FileGrid::Fire].byteOffset;
|
||||||
|
frame.fireByteCount = grids[FileGrid::Fire].byteCount;
|
||||||
|
frame.smokeByteOffset = grids[FileGrid::Smoke].byteOffset;
|
||||||
|
frame.smokeByteCount = grids[FileGrid::Smoke].byteCount;
|
||||||
|
cpuFrames.Add(frame);
|
||||||
|
if(f == 0)
|
||||||
|
{
|
||||||
|
ReadTransform(originOffset, scale, fh, grids);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(byteCount >= 0)
|
||||||
|
{
|
||||||
|
FS_FCloseFile(fh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ri.FS_FreeFileList(fileList);
|
||||||
|
|
||||||
|
if(fileCount <= 0)
|
||||||
|
{
|
||||||
|
ri.Printf(PRINT_WARNING, "^3WARNING: invalid NanoVDB folder '%s'\n", desc.folderPath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(cpuFrames.count + fileCount > cpuFrames.capacity)
|
||||||
|
{
|
||||||
|
ri.Printf(PRINT_WARNING, "^3WARNING: NanoVDB frame limit reached\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ri.Printf(PRINT_ALL, "NanoVDB: analyzed %s in %d ms\n", desc.folderPath, Sys_Milliseconds() - startTimeMS);
|
||||||
|
}
|
||||||
|
|
||||||
|
sequenceIndex = (int)sequences.count;
|
||||||
|
Sequence sequence = {};
|
||||||
|
Q_strncpyz(sequence.folderPath, desc.folderPath, sizeof(sequence.folderPath));
|
||||||
|
sequence.frameCount = (uint32_t)fileCount;
|
||||||
|
sequence.firstFrameIndex = firstFrameIndex;
|
||||||
|
sequence.buffer = gpuBuffer;
|
||||||
|
sequence.bufferByteCount = gpuByteCount;
|
||||||
|
VectorCopy(originOffset, sequence.originOffset);
|
||||||
|
VectorCopy(scale, sequence.scale);
|
||||||
|
sequences.Add(sequence);
|
||||||
|
}
|
||||||
|
if(sequenceIndex < 0)
|
||||||
|
{
|
||||||
|
ri.Printf(PRINT_WARNING, "^3WARNING: NanoVDB sequence limit reached\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Instance instance = {};
|
||||||
|
instance.fireEmissionScale = desc.fireEmissionScale;
|
||||||
|
Q_strncpyz(instance.fireGridName, desc.fireGridName, sizeof(instance.fireGridName));
|
||||||
|
instance.fireTemperatureScale = desc.fireTemperatureScale;
|
||||||
|
instance.frameRate = desc.frameRate;
|
||||||
|
instance.smokeExtinctionScale = desc.smokeExtinctionScale;
|
||||||
|
instance.smokeAlbedo = desc.smokeAlbedo;
|
||||||
|
Q_strncpyz(instance.smokeGridName, desc.smokeGridName, sizeof(instance.smokeGridName));
|
||||||
|
instance.startTimeMS = desc.startTimeMS;
|
||||||
|
instance.startTimeUS = desc.startTimeUS;
|
||||||
|
instance.sequenceIndex = (uint32_t)sequenceIndex;
|
||||||
|
VectorMultiply(desc.scale, sequences[sequenceIndex].scale, instance.scale);
|
||||||
|
VectorCopy(desc.position, instance.position);
|
||||||
|
VectorCopy(desc.useSequenceOffset ? sequences[sequenceIndex].originOffset : desc.originOffset, instance.originOffset);
|
||||||
|
VectorCopy(desc.anglesRad, instance.anglesRad);
|
||||||
|
instance.loop = desc.loop;
|
||||||
|
instances.Add(instance);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NanoVDBManager::MakeWorldToIndexMatrix(matrix3x3_t matrix, const Instance& instance)
|
||||||
|
{
|
||||||
|
matrix3x3_t scale, rot, temp, temp2;
|
||||||
|
vec3_t scaleVector;
|
||||||
|
|
||||||
|
for(int i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
scaleVector[i] = 1.0f / instance.scale[i];
|
||||||
|
}
|
||||||
|
ScaleMatrix(scale, scaleVector);
|
||||||
|
|
||||||
|
RotationMatrixX(rot, -instance.anglesRad[0]);
|
||||||
|
MultMatrix(temp, scale, rot);
|
||||||
|
|
||||||
|
RotationMatrixY(rot, -instance.anglesRad[1]);
|
||||||
|
MultMatrix(temp2, temp, rot);
|
||||||
|
|
||||||
|
RotationMatrixZ(rot, -instance.anglesRad[2]);
|
||||||
|
MultMatrix(matrix, temp2, rot);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NanoVDBManager::DrawGUI()
|
||||||
|
{
|
||||||
|
static const char* const sequencePopupTitle = "Add NanoVDB Sequence";
|
||||||
|
|
||||||
|
if(!tr.worldMapLoaded)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GUI_AddMainMenuItem(GUI_MainMenu::Tools, "Edit NanoVDB", "", &windowActive);
|
||||||
|
|
||||||
|
if(!windowActive)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ImGui::Begin("NanoVDB Settings", &windowActive, ImGuiWindowFlags_AlwaysAutoResize))
|
||||||
|
{
|
||||||
|
if(rhiInfo.forceNanoVDBPreviewMode)
|
||||||
|
{
|
||||||
|
static bool forcedPreviewMode = true;
|
||||||
|
ImGui::BeginDisabled(true);
|
||||||
|
ImGui::Checkbox("Preview mode (forced due to driver bug)", &forcedPreviewMode);
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ImGui::Checkbox("Preview mode", &previewMode);
|
||||||
|
if(!previewMode)
|
||||||
|
{
|
||||||
|
ImGui::Checkbox("2x super-sampling", &supersampling);
|
||||||
|
ImGui::Checkbox("Linear interpolation", &linearInterpolation);
|
||||||
|
ImGui::Checkbox("Accurate overlap test", &accurateOverlapTest);
|
||||||
|
ImGui::Checkbox("Ambient lighting: higher angular LoD", &ambientIncreasedCoverage);
|
||||||
|
ImGui::SliderInt("Ambient lighting: sub-sampling", &ambientRaymarchLOD, 1, 8);
|
||||||
|
ImGui::SliderFloat("Emissive scattering scale", &emissiveScatterScale, 0.0f, 1.0f, "%g");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint64_t streamByteCount = (uint64_t)ARRAY_LEN(streamBuffers) * (uint64_t)streamBufferByteCount;
|
||||||
|
uint64_t dedicatedByteCount = 0;
|
||||||
|
for(uint32_t i = 0; i < sequences.count; i++)
|
||||||
|
{
|
||||||
|
dedicatedByteCount += (uint64_t)sequences[i].bufferByteCount;
|
||||||
|
}
|
||||||
|
ImGui::Text("%d sequence%s, %s dedicated, %s stream",
|
||||||
|
(int)sequences.count, sequences.count >= 2 ? "s" : "",
|
||||||
|
Com_FormatBytes(dedicatedByteCount),
|
||||||
|
Com_FormatBytes(streamByteCount));
|
||||||
|
ImGui::Text("%d CPU frame%s, %d GPU frame%s",
|
||||||
|
(int)cpuFrames.count, cpuFrames.count >= 2 ? "s" : "",
|
||||||
|
(int)gpuFrames.count, gpuFrames.count >= 2 ? "s" : "");
|
||||||
|
ImGui::Text("%d streamed file%s", (int)streamedFrames.count, streamedFrames.count >= 2 ? "s" : "");
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
if(ImGui::Button("Purge unused sequences"))
|
||||||
|
{
|
||||||
|
Purge();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ImGui::BeginTabBar("Tabs#VDB", ImGuiTabBarFlags_AutoSelectNewTabs))
|
||||||
|
{
|
||||||
|
for(uint32_t i = 0; i < instances.count; i++)
|
||||||
|
{
|
||||||
|
if(ImGui::BeginTabItem(va("#%d", i + 1)))
|
||||||
|
{
|
||||||
|
Instance& inst = instances[i];
|
||||||
|
Sequence& seq = sequences[inst.sequenceIndex];
|
||||||
|
|
||||||
|
ImGui::Text("%s (%d frame%s, %s)", seq.folderPath, (int)seq.frameCount,
|
||||||
|
seq.frameCount >= 2 ? "s" : "",
|
||||||
|
IsNullHandle(seq.buffer) ? "streamed" : "in VRAM");
|
||||||
|
ImGui::SliderFloat("Framerate", &inst.frameRate, 15.0f, 120.0f, "%g");
|
||||||
|
ImGui::SliderFloat("Smoke extinction scale (thickness)", &inst.smokeExtinctionScale, 0.0f, 10.0f, "%g");
|
||||||
|
ImGui::SliderFloat("Smoke albedo (reflectivity)", &inst.smokeAlbedo, 0.0f, 1.0f, "%g");
|
||||||
|
ImGui::SliderFloat("Flame emission scale (brightness)", &inst.fireEmissionScale, 0.0f, 1.0f, "%g");
|
||||||
|
ImGui::SliderFloat("Flame temperature scale (color)", &inst.fireTemperatureScale, 0.0f, 20000.0f, "%g");
|
||||||
|
|
||||||
|
vec3_t angles;
|
||||||
|
for(int a = 0; a < 3; a++)
|
||||||
|
{
|
||||||
|
angles[a] = RAD2DEG(inst.anglesRad[a]);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::SliderFloat3("Origin offset (index space)", inst.originOffset, -1000.0f, 1000.0f, "%g");
|
||||||
|
VectorScaleGUI(inst.originOffset, "origin");
|
||||||
|
ImGui::SliderFloat3("Scale", inst.scale, 0.0f, 100.0f, "%g");
|
||||||
|
VectorScaleGUI(inst.scale, "scale");
|
||||||
|
ImGui::SliderFloat3("Position (world space)", inst.position, -100 * 1000.0f, 100 * 1000.0f, "%g");
|
||||||
|
ImGui::SliderFloat3("Angles", angles, 0.0f, 360.0f, "%g");
|
||||||
|
for(int a = 0; a < 3; a++)
|
||||||
|
{
|
||||||
|
inst.anglesRad[a] = DEG2RAD(angles[a]);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
if(ImGui::Button("Remove"))
|
||||||
|
{
|
||||||
|
instances.Remove(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndTabItem();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ImGui::BeginTabItem("Add"))
|
||||||
|
{
|
||||||
|
static char sequencePath[64];
|
||||||
|
static char flamesGridName[64] = "flames";
|
||||||
|
static char smokeGridName[64] = "density";
|
||||||
|
static bool gpuResident = true;
|
||||||
|
|
||||||
|
ImGui::InputText("Folder path", sequencePath, sizeof(sequencePath));
|
||||||
|
ImGui::SameLine();
|
||||||
|
if(ImGui::Button("..."))
|
||||||
|
{
|
||||||
|
OpenFolderDialog_Open("nanovdb");
|
||||||
|
}
|
||||||
|
ImGui::InputText("Flames grid", flamesGridName, sizeof(flamesGridName));
|
||||||
|
ImGui::InputText("Smoke grid", smokeGridName, sizeof(smokeGridName));
|
||||||
|
ImGui::Checkbox("GPU resident", &gpuResident);
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
if(ImGui::Button("Add"))
|
||||||
|
{
|
||||||
|
VDBSequenceDesc desc = {};
|
||||||
|
desc.fireGridName = flamesGridName;
|
||||||
|
desc.folderPath = sequencePath;
|
||||||
|
desc.gpuResident = gpuResident;
|
||||||
|
desc.loop = true;
|
||||||
|
desc.smokeGridName = smokeGridName;
|
||||||
|
AddSequence(desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(OpenFolderDialog_Do())
|
||||||
|
{
|
||||||
|
Q_strncpyz(sequencePath, OpenFolderDialog_GetPath(), sizeof(sequencePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndTabItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndTabBar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NanoVDBManager::Purge()
|
||||||
|
{
|
||||||
|
// build sequence reference counts
|
||||||
|
uint32_t sequenceRefCounts[ARRAY_LEN(sequences.items)] = {};
|
||||||
|
for(uint32_t i = 0; i < instances.count; i++)
|
||||||
|
{
|
||||||
|
const uint32_t s = instances[i].sequenceIndex;
|
||||||
|
sequenceRefCounts[s]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// queue GPU buffer deletions
|
||||||
|
for(uint32_t s = 0; s < sequences.count; s++)
|
||||||
|
{
|
||||||
|
if(sequenceRefCounts[s] == 0 &&
|
||||||
|
!IsNullHandle(sequences[s].buffer))
|
||||||
|
{
|
||||||
|
DestroyBufferDelayed(sequences[s].buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// compact sequence array, build index map, remove frames, fix frame offsets
|
||||||
|
uint32_t sequenceRemap[ARRAY_LEN(sequences.items)] = {};
|
||||||
|
uint32_t removed = 0;
|
||||||
|
uint32_t dst = 0;
|
||||||
|
uint32_t src = 0;
|
||||||
|
for(; src < sequences.count; src++)
|
||||||
|
{
|
||||||
|
if(sequenceRefCounts[src] == 0)
|
||||||
|
{
|
||||||
|
const uint32_t first = sequences[src].firstFrameIndex;
|
||||||
|
const uint32_t count = sequences[src].frameCount;
|
||||||
|
const bool streamed = IsNullHandle(sequences[src].buffer);
|
||||||
|
if(streamed)
|
||||||
|
{
|
||||||
|
cpuFrames.RemoveRange(first, count);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gpuFrames.RemoveRange(first, count);
|
||||||
|
}
|
||||||
|
for(uint32_t s = 0; s < sequences.count; s++)
|
||||||
|
{
|
||||||
|
if(sequences[s].firstFrameIndex > first)
|
||||||
|
{
|
||||||
|
sequences[s].firstFrameIndex -= count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
removed++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
sequenceRemap[src] = dst;
|
||||||
|
if(src > dst)
|
||||||
|
{
|
||||||
|
sequences[dst] = sequences[src];
|
||||||
|
}
|
||||||
|
dst++;
|
||||||
|
}
|
||||||
|
sequences.count -= removed;
|
||||||
|
|
||||||
|
// fix sequence indices
|
||||||
|
for(uint32_t i = 0; i < instances.count; i++)
|
||||||
|
{
|
||||||
|
const uint32_t s = instances[i].sequenceIndex;
|
||||||
|
instances[i].sequenceIndex = sequenceRemap[s];
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(_DEBUG)
|
||||||
|
for(uint32_t i = 0; i < instances.count; i++)
|
||||||
|
{
|
||||||
|
Q_assert(instances[i].sequenceIndex < sequences.count);
|
||||||
|
}
|
||||||
|
for(uint32_t s = 0; s < sequences.count; s++)
|
||||||
|
{
|
||||||
|
const Sequence& seq = sequences[s];
|
||||||
|
const uint32_t frameCount = IsNullHandle(seq.buffer) ? cpuFrames.count : gpuFrames.count;
|
||||||
|
Q_assert(seq.firstFrameIndex + seq.frameCount <= frameCount);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int NanoVDBManager::FindStreamedFrameIndex(uint32_t sequenceIndex, uint32_t frameIndex)
|
||||||
|
{
|
||||||
|
int index = -1;
|
||||||
|
for(uint32_t f = 0; f < streamedFrames.count; f++)
|
||||||
|
{
|
||||||
|
if(streamedFrames[f].sequenceIndex == sequenceIndex &&
|
||||||
|
streamedFrames[f].frameIndex == frameIndex)
|
||||||
|
{
|
||||||
|
index = (int)f;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
|
@ -0,0 +1,255 @@
|
||||||
|
/*
|
||||||
|
===========================================================================
|
||||||
|
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 - GPU particle system
|
||||||
|
|
||||||
|
|
||||||
|
#include "crp_local.h"
|
||||||
|
#include "shaders/crp/scene_view.h.hlsli"
|
||||||
|
#include "compshaders/crp/particles_clear.h"
|
||||||
|
#include "compshaders/crp/particles_setup.h"
|
||||||
|
#include "compshaders/crp/particles_emit.h"
|
||||||
|
#include "compshaders/crp/particles_simulate.h"
|
||||||
|
|
||||||
|
|
||||||
|
// @TODO:
|
||||||
|
#if 0
|
||||||
|
static const uint32_t EmitterParticleCount = 5;
|
||||||
|
static const uint32_t EmitterEmitCount = 2;
|
||||||
|
static const float EmitterMaxSeconds = 2.0f / 60.0f;
|
||||||
|
#else
|
||||||
|
static const float EmitterMaxSeconds = 1.0f;
|
||||||
|
static const float EmitterFPS = 60.0f;
|
||||||
|
static const uint32_t EmitterParticleCount = 1024;
|
||||||
|
static const uint32_t EmitterEmitCount = EmitterParticleCount / (uint32_t)ceilf(EmitterMaxSeconds * EmitterFPS);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#pragma pack(push, 4)
|
||||||
|
|
||||||
|
struct GPSClearRC
|
||||||
|
{
|
||||||
|
uint32_t emitterBufferIndex;
|
||||||
|
uint32_t deadBufferIndex;
|
||||||
|
uint32_t emitterIndex;
|
||||||
|
uint32_t firstParticle;
|
||||||
|
uint32_t particleCount;
|
||||||
|
float maxSeconds;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GPSSetupRC
|
||||||
|
{
|
||||||
|
uint32_t emitterBufferIndex;
|
||||||
|
uint32_t indirectBufferIndex;
|
||||||
|
uint32_t emitterIndex;
|
||||||
|
uint32_t emitCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GPSEmitRC
|
||||||
|
{
|
||||||
|
uint32_t particleBufferIndex;
|
||||||
|
uint32_t liveBufferIndex;
|
||||||
|
uint32_t deadBufferIndex;
|
||||||
|
uint32_t emitterBufferIndex;
|
||||||
|
uint32_t emitterIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GPSSimulateRC
|
||||||
|
{
|
||||||
|
uint32_t particleBufferIndex;
|
||||||
|
uint32_t liveSrcBufferIndex;
|
||||||
|
uint32_t liveDstBufferIndex;
|
||||||
|
uint32_t deadBufferIndex;
|
||||||
|
uint32_t emitterBufferIndex;
|
||||||
|
uint32_t emitterIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
|
||||||
|
void ParticleSystem::Init()
|
||||||
|
{
|
||||||
|
Q_assert(EmitterParticleCount <= MAX_PARTICLES);
|
||||||
|
|
||||||
|
clearPipeline = CreateComputePipeline("GPS Clear", ShaderByteCode(g_particles_clear_cs));
|
||||||
|
setupPipeline = CreateComputePipeline("GPS Setup", ShaderByteCode(g_particles_setup_cs));
|
||||||
|
emitPipeline = CreateComputePipeline("GPS Emit", ShaderByteCode(g_particles_emit_cs));
|
||||||
|
simulatePipeline = CreateComputePipeline("GPS Simulate", ShaderByteCode(g_particles_simulate_cs));
|
||||||
|
|
||||||
|
{
|
||||||
|
BufferDesc desc("particles", MAX_PARTICLES * sizeof(Particle), ResourceStates::UnorderedAccessBit);
|
||||||
|
desc.shortLifeTime = true;
|
||||||
|
desc.structureByteCount = sizeof(Particle);
|
||||||
|
particleBuffer = CreateBuffer(desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
BufferDesc desc("", MAX_PARTICLES * 4, ResourceStates::UnorderedAccessBit);
|
||||||
|
desc.shortLifeTime = true;
|
||||||
|
desc.structureByteCount = 4;
|
||||||
|
desc.name = "live particles #1";
|
||||||
|
liveBuffers[0] = CreateBuffer(desc);
|
||||||
|
desc.name = "live particles #2";
|
||||||
|
liveBuffers[1] = CreateBuffer(desc);
|
||||||
|
desc.name = "dead particles";
|
||||||
|
deadBuffer = CreateBuffer(desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
BufferDesc desc("particle emitters", MAX_PARTICLE_EMITTERS * sizeof(ParticleEmitter), ResourceStates::UnorderedAccessBit);
|
||||||
|
desc.shortLifeTime = true;
|
||||||
|
desc.structureByteCount = sizeof(ParticleEmitter);
|
||||||
|
emitterBuffer = CreateBuffer(desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const uint32_t dispatchData[] =
|
||||||
|
{
|
||||||
|
0, 1, 1,
|
||||||
|
0, 1, 1
|
||||||
|
};
|
||||||
|
|
||||||
|
BufferDesc desc("particle dispatch", sizeof(dispatchData), ResourceStates::UnorderedAccessBit);
|
||||||
|
desc.shortLifeTime = true;
|
||||||
|
indirectBuffer = CreateBuffer(desc);
|
||||||
|
|
||||||
|
uint8_t* const mapped = BeginBufferUpload(indirectBuffer);
|
||||||
|
memcpy(mapped, dispatchData, sizeof(dispatchData));
|
||||||
|
EndBufferUpload(indirectBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
needsClearing = true;
|
||||||
|
liveBufferReadIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::Draw()
|
||||||
|
{
|
||||||
|
#if 1 // @TODO: shouldn't be necessary once cgame is adding the emitters
|
||||||
|
if(tr.world == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
static int counter = 0;
|
||||||
|
counter++;
|
||||||
|
if(counter == 4)
|
||||||
|
{
|
||||||
|
counter = 0;
|
||||||
|
needsClearing = true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
SCOPED_RENDER_PASS("Particles", 1.0f, 1.0f, 1.0f);
|
||||||
|
|
||||||
|
if(needsClearing)
|
||||||
|
{
|
||||||
|
SCOPED_DEBUG_LABEL("Clear", 1.0f, 1.0f, 1.0f);
|
||||||
|
|
||||||
|
CmdBeginBarrier();
|
||||||
|
CmdBufferBarrier(emitterBuffer, ResourceStates::UnorderedAccessBit);
|
||||||
|
CmdBufferBarrier(deadBuffer, ResourceStates::UnorderedAccessBit);
|
||||||
|
CmdEndBarrier();
|
||||||
|
|
||||||
|
GPSClearRC rc = {};
|
||||||
|
rc.emitterBufferIndex = GetBufferIndexUAV(emitterBuffer);
|
||||||
|
rc.deadBufferIndex = GetBufferIndexUAV(deadBuffer);
|
||||||
|
rc.emitterIndex = 0;
|
||||||
|
rc.firstParticle = 0;
|
||||||
|
rc.particleCount = EmitterParticleCount;
|
||||||
|
rc.maxSeconds = EmitterMaxSeconds;
|
||||||
|
|
||||||
|
CmdBindPipeline(clearPipeline);
|
||||||
|
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
|
||||||
|
CmdDispatch((EmitterParticleCount + 63) / 64, 1, 1);
|
||||||
|
|
||||||
|
needsClearing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
SCOPED_DEBUG_LABEL("Setup", 1.0f, 1.0f, 1.0f);
|
||||||
|
|
||||||
|
CmdBeginBarrier();
|
||||||
|
CmdBufferBarrier(emitterBuffer, ResourceStates::UnorderedAccessBit);
|
||||||
|
CmdBufferBarrier(indirectBuffer, ResourceStates::UnorderedAccessBit);
|
||||||
|
CmdEndBarrier();
|
||||||
|
|
||||||
|
GPSSetupRC rc = {};
|
||||||
|
rc.emitterBufferIndex = GetBufferIndexUAV(emitterBuffer);
|
||||||
|
rc.indirectBufferIndex = GetBufferIndexUAV(indirectBuffer);
|
||||||
|
rc.emitterIndex = 0; // @TODO:
|
||||||
|
rc.emitCount = EmitterEmitCount; // @TODO:
|
||||||
|
|
||||||
|
CmdBindPipeline(setupPipeline);
|
||||||
|
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
|
||||||
|
CmdDispatch(1, 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
SCOPED_DEBUG_LABEL("Emit", 1.0f, 1.0f, 1.0f);
|
||||||
|
|
||||||
|
CmdBeginBarrier();
|
||||||
|
CmdBufferBarrier(indirectBuffer, ResourceStates::IndirectDispatchBit);
|
||||||
|
CmdBufferBarrier(emitterBuffer, ResourceStates::UnorderedAccessBit);
|
||||||
|
CmdBufferBarrier(deadBuffer, ResourceStates::UnorderedAccessBit);
|
||||||
|
CmdBufferBarrier(liveBuffers[liveBufferReadIndex], ResourceStates::UnorderedAccessBit);
|
||||||
|
CmdBufferBarrier(particleBuffer, ResourceStates::UnorderedAccessBit);
|
||||||
|
CmdEndBarrier();
|
||||||
|
|
||||||
|
GPSEmitRC rc = {};
|
||||||
|
rc.emitterBufferIndex = GetBufferIndexUAV(emitterBuffer);
|
||||||
|
rc.deadBufferIndex = GetBufferIndexUAV(deadBuffer);
|
||||||
|
rc.liveBufferIndex = GetBufferIndexUAV(liveBuffers[liveBufferReadIndex]);
|
||||||
|
rc.particleBufferIndex = GetBufferIndexUAV(particleBuffer);
|
||||||
|
rc.emitterIndex = 0; // @TODO:
|
||||||
|
|
||||||
|
CmdBindPipeline(emitPipeline);
|
||||||
|
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
|
||||||
|
CmdDispatchIndirect(indirectBuffer, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
SCOPED_DEBUG_LABEL("Simulate", 1.0f, 1.0f, 1.0f);
|
||||||
|
|
||||||
|
CmdBeginBarrier();
|
||||||
|
CmdBufferBarrier(indirectBuffer, ResourceStates::IndirectDispatchBit);
|
||||||
|
CmdBufferBarrier(emitterBuffer, ResourceStates::UnorderedAccessBit);
|
||||||
|
CmdBufferBarrier(deadBuffer, ResourceStates::UnorderedAccessBit);
|
||||||
|
CmdBufferBarrier(liveBuffers[liveBufferReadIndex], ResourceStates::UnorderedAccessBit);
|
||||||
|
CmdBufferBarrier(liveBuffers[liveBufferReadIndex ^ 1], ResourceStates::UnorderedAccessBit);
|
||||||
|
CmdBufferBarrier(particleBuffer, ResourceStates::UnorderedAccessBit);
|
||||||
|
CmdEndBarrier();
|
||||||
|
|
||||||
|
GPSSimulateRC rc = {};
|
||||||
|
rc.particleBufferIndex = GetBufferIndexUAV(particleBuffer);
|
||||||
|
rc.liveSrcBufferIndex = GetBufferIndexUAV(liveBuffers[liveBufferReadIndex]);
|
||||||
|
rc.liveDstBufferIndex = GetBufferIndexUAV(liveBuffers[liveBufferReadIndex ^ 1]);
|
||||||
|
rc.deadBufferIndex = GetBufferIndexUAV(deadBuffer);
|
||||||
|
rc.emitterBufferIndex = GetBufferIndexUAV(emitterBuffer);
|
||||||
|
rc.emitterIndex = 0; // @TODO:
|
||||||
|
|
||||||
|
CmdBindPipeline(simulatePipeline);
|
||||||
|
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
|
||||||
|
CmdDispatchIndirect(indirectBuffer, 12);
|
||||||
|
|
||||||
|
liveBufferReadIndex ^= 1;
|
||||||
|
}
|
||||||
|
}
|
|
@ -71,7 +71,6 @@ static void LoadSunFromShader(const shader_t* skyShader)
|
||||||
angles[0] = DEG2RAD(skyShader->sunAzimuth);
|
angles[0] = DEG2RAD(skyShader->sunAzimuth);
|
||||||
angles[1] = DEG2RAD(skyShader->sunInclination);
|
angles[1] = DEG2RAD(skyShader->sunInclination);
|
||||||
VectorCopy(skyShader->sunColor, crp.sunlightData.color);
|
VectorCopy(skyShader->sunColor, crp.sunlightData.color);
|
||||||
crp.sunlightData.intensity = 1.0f;
|
|
||||||
AzimuthInclinationToDirection(crp.sunlightData.direction, angles);
|
AzimuthInclinationToDirection(crp.sunlightData.direction, angles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,7 +174,8 @@ void SunlightEditor::DrawGUI()
|
||||||
ImGui::SliderAngle("Inclination", &angles[1], 0.0f, 180.0f);
|
ImGui::SliderAngle("Inclination", &angles[1], 0.0f, 180.0f);
|
||||||
AzimuthInclinationToDirection(crp.sunlightData.direction, angles);
|
AzimuthInclinationToDirection(crp.sunlightData.direction, angles);
|
||||||
ImGui::ColorEdit3("Color", crp.sunlightData.color);
|
ImGui::ColorEdit3("Color", crp.sunlightData.color);
|
||||||
ImGui::SliderFloat("Light intensity", &crp.sunlightData.intensity, 0.0f, 10.0f);
|
ImGui::SliderFloat("Light intensity in fog/smoke", &crp.sunlightData.intensityVL, 0.0f, 200.0f);
|
||||||
|
ImGui::SliderFloat("Light intensity on surfaces", &crp.sunlightData.intensityDL, 0.0f, 10.0f);
|
||||||
|
|
||||||
ImGui::NewLine();
|
ImGui::NewLine();
|
||||||
if(ImGui::Button("Save Config..."))
|
if(ImGui::Button("Save Config..."))
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -3733,6 +3733,7 @@ namespace RHI
|
||||||
rhiInfo.hasInlineRaytracing = hasInlineRaytracing;
|
rhiInfo.hasInlineRaytracing = hasInlineRaytracing;
|
||||||
rhiInfo.hasBarycentrics = hasBarycentrics;
|
rhiInfo.hasBarycentrics = hasBarycentrics;
|
||||||
rhiInfo.allocatedByteCount = 0;
|
rhiInfo.allocatedByteCount = 0;
|
||||||
|
rhiInfo.forceNanoVDBPreviewMode = rhi.vendorId == VENDORID_AMD || rhi.vendorId == VENDORID_INTEL;
|
||||||
|
|
||||||
rhi.initialized = true;
|
rhi.initialized = true;
|
||||||
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
*.exe
|
|
||||||
*.pdb
|
|
||||||
*.h
|
|
File diff suppressed because it is too large
Load Diff
|
@ -48,6 +48,12 @@ along with Challenge Quake 3. If not, see <https://www.gnu.org/licenses/>.
|
||||||
#define UINT32_MAX 0xFFFFFFFF
|
#define UINT32_MAX 0xFFFFFFFF
|
||||||
#define UINT64_MAX 0xFFFFFFFFFFFFFFFF
|
#define UINT64_MAX 0xFFFFFFFFFFFFFFFF
|
||||||
|
|
||||||
|
#define FLT_INF asfloat(0x7F800000)
|
||||||
|
#define FLT_NAN asfloat(0xFFC00000)
|
||||||
|
#define FLT_MAX asfloat(0x7F7FFFFF)
|
||||||
|
|
||||||
|
#define HALF_MAX 65504.0
|
||||||
|
|
||||||
|
|
||||||
typedef RaytracingAccelerationStructure RTAS;
|
typedef RaytracingAccelerationStructure RTAS;
|
||||||
|
|
||||||
|
@ -69,6 +75,20 @@ float Brightness(float3 color)
|
||||||
return brightness;
|
return brightness;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float3 ColorAtBrightness(float3 color, float targetBrightness)
|
||||||
|
{
|
||||||
|
float brightness = Brightness(color);
|
||||||
|
if(brightness <= 0.000001)
|
||||||
|
{
|
||||||
|
color = float3(0.5, 0.5, 0.5);
|
||||||
|
brightness = Brightness(color);
|
||||||
|
}
|
||||||
|
float brightnessScale = targetBrightness / brightness;
|
||||||
|
float3 result = color * brightnessScale;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
float4 MakeGreyscale(float4 input, float amount)
|
float4 MakeGreyscale(float4 input, float amount)
|
||||||
{
|
{
|
||||||
float grey = dot(input.rgb, float3(0.299, 0.587, 0.114));
|
float grey = dot(input.rgb, float3(0.299, 0.587, 0.114));
|
||||||
|
@ -163,6 +183,13 @@ float EaseInQuad(float x)
|
||||||
return x * x;
|
return x * x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float EaseOutQuad(float x)
|
||||||
|
{
|
||||||
|
float y = 1.0 - x;
|
||||||
|
|
||||||
|
return 1.0 - y * y;
|
||||||
|
}
|
||||||
|
|
||||||
float EaseInExp(float x)
|
float EaseInExp(float x)
|
||||||
{
|
{
|
||||||
return x == 0.0 ? 0.0 : pow(2.0, 10.0 * x - 10.0);
|
return x == 0.0 ? 0.0 : pow(2.0, 10.0 * x - 10.0);
|
||||||
|
@ -404,6 +431,111 @@ float AnimateBlueNoise(float blueNoise, uint frameIndex)
|
||||||
return frac(blueNoise + float(frameIndex % 32) * 0.61803399);
|
return frac(blueNoise + float(frameIndex % 32) * 0.61803399);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// credit: David Hoskins
|
||||||
|
float Hash1To1(float p)
|
||||||
|
{
|
||||||
|
p = frac(p * .1031);
|
||||||
|
p *= p + 33.33;
|
||||||
|
p *= p + p;
|
||||||
|
return frac(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
// credit: David Hoskins
|
||||||
|
float Hash2To1(float2 p)
|
||||||
|
{
|
||||||
|
float3 p3 = frac(float3(p.xyx) * .1031);
|
||||||
|
p3 += dot(p3, p3.yzx + 33.33);
|
||||||
|
return frac((p3.x + p3.y) * p3.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
// credit: David Hoskins
|
||||||
|
float Hash3To1(float3 p3)
|
||||||
|
{
|
||||||
|
p3 = frac(p3 * .1031);
|
||||||
|
p3 += dot(p3, p3.yzx + 33.33);
|
||||||
|
return frac((p3.x + p3.y) * p3.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
// credit: David Hoskins
|
||||||
|
float2 Hash1To2(float p)
|
||||||
|
{
|
||||||
|
float3 p3 = frac(float3(p, p, p) * float3(.1031, .1030, .0973));
|
||||||
|
p3 += dot(p3, p3.yzx + 33.33);
|
||||||
|
return frac((p3.xx + p3.yz) * p3.zy);
|
||||||
|
}
|
||||||
|
|
||||||
|
// credit: David Hoskins
|
||||||
|
float2 Hash2To2(float2 p)
|
||||||
|
{
|
||||||
|
float3 p3 = frac(float3(p.xyx) * float3(.1031, .1030, .0973));
|
||||||
|
p3 += dot(p3, p3.yzx + 33.33);
|
||||||
|
return frac((p3.xx + p3.yz) * p3.zy);
|
||||||
|
}
|
||||||
|
|
||||||
|
// credit: David Hoskins
|
||||||
|
float2 Hash3To2(float3 p3)
|
||||||
|
{
|
||||||
|
p3 = frac(p3 * float3(.1031, .1030, .0973));
|
||||||
|
p3 += dot(p3, p3.yzx + 33.33);
|
||||||
|
return frac((p3.xx + p3.yz) * p3.zy);
|
||||||
|
}
|
||||||
|
|
||||||
|
// credit: David Hoskins
|
||||||
|
float3 Hash1To3(float p)
|
||||||
|
{
|
||||||
|
float3 p3 = frac(float3(p, p, p) * float3(.1031, .1030, .0973));
|
||||||
|
p3 += dot(p3, p3.yzx + 33.33);
|
||||||
|
return frac((p3.xxy + p3.yzz) * p3.zyx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// credit: David Hoskins
|
||||||
|
float3 Hash2To3(float2 p)
|
||||||
|
{
|
||||||
|
float3 p3 = frac(float3(p.xyx) * float3(.1031, .1030, .0973));
|
||||||
|
p3 += dot(p3, p3.yxz + 33.33);
|
||||||
|
return frac((p3.xxy + p3.yzz) * p3.zyx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// credit: David Hoskins
|
||||||
|
float3 Hash3To3(float3 p3)
|
||||||
|
{
|
||||||
|
p3 = frac(p3 * float3(.1031, .1030, .0973));
|
||||||
|
p3 += dot(p3, p3.yxz + 33.33);
|
||||||
|
return frac((p3.xxy + p3.yxx) * p3.zyx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// credit: David Hoskins
|
||||||
|
float4 Hash1To4(float p)
|
||||||
|
{
|
||||||
|
float4 p4 = frac(float4(p, p, p, p) * float4(.1031, .1030, .0973, .1099));
|
||||||
|
p4 += dot(p4, p4.wzxy + 33.33);
|
||||||
|
return frac((p4.xxyz + p4.yzzw) * p4.zywx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// credit: David Hoskins
|
||||||
|
float4 Hash2To4(float2 p)
|
||||||
|
{
|
||||||
|
float4 p4 = frac(float4(p.xyxy) * float4(.1031, .1030, .0973, .1099));
|
||||||
|
p4 += dot(p4, p4.wzxy + 33.33);
|
||||||
|
return frac((p4.xxyz + p4.yzzw) * p4.zywx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// credit: David Hoskins
|
||||||
|
float4 Hash3To4(float3 p)
|
||||||
|
{
|
||||||
|
float4 p4 = frac(float4(p.xyzx) * float4(.1031, .1030, .0973, .1099));
|
||||||
|
p4 += dot(p4, p4.wzxy + 33.33);
|
||||||
|
return frac((p4.xxyz + p4.yzzw) * p4.zywx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// credit: David Hoskins
|
||||||
|
float4 Hash4To4(float4 p4)
|
||||||
|
{
|
||||||
|
p4 = frac(p4 * float4(.1031, .1030, .0973, .1099));
|
||||||
|
p4 += dot(p4, p4.wzxy + 33.33);
|
||||||
|
return frac((p4.xxyz + p4.yzzw) * p4.zywx);
|
||||||
|
}
|
||||||
|
|
||||||
float2 NDCToTC(float2 ndc)
|
float2 NDCToTC(float2 ndc)
|
||||||
{
|
{
|
||||||
float2 tc = ndc * float2(0.5, -0.5) + float2(0.5, 0.5);
|
float2 tc = ndc * float2(0.5, -0.5) + float2(0.5, 0.5);
|
||||||
|
@ -515,12 +647,13 @@ int FlattenIndex(int3 tileIndex, int3 tileResolution)
|
||||||
|
|
||||||
uint3 UnflattenIndex(uint flatIndex, uint3 tileResolution)
|
uint3 UnflattenIndex(uint flatIndex, uint3 tileResolution)
|
||||||
{
|
{
|
||||||
|
uint w = tileResolution.x;
|
||||||
uint h = tileResolution.y;
|
uint h = tileResolution.y;
|
||||||
uint wh = tileResolution.x * h;
|
uint wh = w * h;
|
||||||
uint z = flatIndex / wh;
|
uint z = flatIndex / wh;
|
||||||
flatIndex -= z * wh;
|
flatIndex -= z * wh;
|
||||||
uint y = flatIndex / h;
|
uint y = flatIndex / w;
|
||||||
uint x = flatIndex - y * h;
|
uint x = flatIndex - y * w;
|
||||||
uint3 result = uint3(x, y, z);
|
uint3 result = uint3(x, y, z);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -528,12 +661,13 @@ uint3 UnflattenIndex(uint flatIndex, uint3 tileResolution)
|
||||||
|
|
||||||
int3 UnflattenIndex(int flatIndex, int3 tileResolution)
|
int3 UnflattenIndex(int flatIndex, int3 tileResolution)
|
||||||
{
|
{
|
||||||
|
int w = tileResolution.x;
|
||||||
int h = tileResolution.y;
|
int h = tileResolution.y;
|
||||||
int wh = tileResolution.x * h;
|
int wh = w * h;
|
||||||
int z = flatIndex / wh;
|
int z = flatIndex / wh;
|
||||||
flatIndex -= z * wh;
|
flatIndex -= z * wh;
|
||||||
int y = flatIndex / h;
|
int y = flatIndex / w;
|
||||||
int x = flatIndex - y * h;
|
int x = flatIndex - y * w;
|
||||||
int3 result = int3(x, y, z);
|
int3 result = int3(x, y, z);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -545,6 +679,12 @@ void ClearBoundingBox(out int3 boxMin, out int3 boxMax)
|
||||||
boxMax = int3(INT32_MIN, INT32_MIN, INT32_MIN);
|
boxMax = int3(INT32_MIN, INT32_MIN, INT32_MIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClearBoundingBox(out float3 boxMin, out float3 boxMax)
|
||||||
|
{
|
||||||
|
boxMin = float3(FLT_MAX, FLT_MAX, FLT_MAX);
|
||||||
|
boxMax = float3(-FLT_MAX, -FLT_MAX, -FLT_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void ExpandBoundingBox(inout T boxMin, inout T boxMax, T newPoint)
|
void ExpandBoundingBox(inout T boxMin, inout T boxMax, T newPoint)
|
||||||
{
|
{
|
||||||
|
@ -699,17 +839,103 @@ float3 DirectionFromLongLat(float longitude01, float latitude01)
|
||||||
return direction;
|
return direction;
|
||||||
}
|
}
|
||||||
|
|
||||||
float3 AmbientColor(float4 payloadA, float4 payloadB, float3 normal, float3 fallbackColor)
|
float SphereVolume(float radius)
|
||||||
{
|
{
|
||||||
float3 ambColor = payloadA.rgb;
|
float volume = ((4.0 / 3.0) * PI) * radius * radius * radius;
|
||||||
float3 localColor = float3(payloadA.a, payloadB.rg);
|
|
||||||
float3 localDir = DirectionFromLongLat(payloadB.b, payloadB.a);
|
|
||||||
float localScale = dot(localDir, normal) * 0.5 + 0.5; // wraps around
|
|
||||||
float3 interpColor = ambColor + localColor * localScale;
|
|
||||||
float brightNew = Brightness(interpColor);
|
|
||||||
float brightFall = Brightness(fallbackColor);
|
|
||||||
float t = saturate(brightNew / max(brightFall, 0.001));
|
|
||||||
float3 color = lerp(fallbackColor, interpColor, t);
|
|
||||||
|
|
||||||
return color;
|
return volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
// "2D Polyhedral Bounds of a Clipped, Perspective-Projected 3D Sphere" by Mara and McGuire
|
||||||
|
float2 ProjectedSphereExtentsAxis(float xy, float z, float r)
|
||||||
|
{
|
||||||
|
float t = sqrt(xy * xy + z * z - r * r);
|
||||||
|
float min = (t * xy - r * z) / (t * z + r * xy);
|
||||||
|
float max = (t * xy + r * z) / (t * z - r * xy);
|
||||||
|
float2 result = float2(min, max);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// "2D Polyhedral Bounds of a Clipped, Perspective-Projected 3D Sphere" by Mara and McGuire
|
||||||
|
float4 ProjectedSphereExtentsNDC(float3 spherePositionWS, float sphereRadius, matrix viewMatrix, matrix projMatrix)
|
||||||
|
{
|
||||||
|
float4 spherePosVSw = mul(viewMatrix, float4(spherePositionWS, 1));
|
||||||
|
float3 spherePosVS = spherePosVSw.xyz / spherePosVSw.w;
|
||||||
|
float2 extentsX = ProjectedSphereExtentsAxis(spherePosVS.x, -spherePosVS.z, sphereRadius) * projMatrix[0][0] + projMatrix[2][0];
|
||||||
|
float2 extentsY = ProjectedSphereExtentsAxis(spherePosVS.y, spherePosVS.z, sphereRadius) * projMatrix[1][1] + projMatrix[2][1];
|
||||||
|
float4 result = float4(extentsX.x, extentsY.x, extentsX.y, extentsY.y);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
float3 Project(float3 P, matrix m)
|
||||||
|
{
|
||||||
|
float4 Qw = mul(m, float4(P, 1));
|
||||||
|
float3 Q = Qw.xyz / Qw.w;
|
||||||
|
|
||||||
|
return Q;
|
||||||
|
}
|
||||||
|
|
||||||
|
float VoxelStepSize(float3 dir, float3 voxelSize)
|
||||||
|
{
|
||||||
|
float3 stepSize3 = voxelSize / max(abs(dir), float(0.000001).xxx);
|
||||||
|
float stepSize = min3(stepSize3.x, stepSize3.y, stepSize3.z);
|
||||||
|
|
||||||
|
return stepSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
float2x2 RandomRotationMatrix2x2(float3 position)
|
||||||
|
{
|
||||||
|
float angle = Hash3To1(position) * 2.0 * PI;
|
||||||
|
float sin, cos;
|
||||||
|
sincos(angle, sin, cos);
|
||||||
|
float2x2 result = float2x2(cos, -sin, sin, cos);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
float3x3 RandomRotationMatrix3x3(float3 position)
|
||||||
|
{
|
||||||
|
float3 angles = Hash3To3(position) * 2.0 * PI;
|
||||||
|
float3 sin, cos;
|
||||||
|
sincos(angles.x, sin.x, cos.x);
|
||||||
|
sincos(angles.y, sin.y, cos.y);
|
||||||
|
sincos(angles.z, sin.z, cos.z);
|
||||||
|
float r0 = cos.x * cos.y;
|
||||||
|
float r1 = cos.x * sin.y * sin.z - sin.x * cos.z;
|
||||||
|
float r2 = cos.x * sin.y * cos.z + sin.x * sin.z;
|
||||||
|
float r3 = sin.x * cos.y;
|
||||||
|
float r4 = sin.x * sin.y * sin.z + cos.x * cos.z;
|
||||||
|
float r5 = sin.x * sin.y * cos.z - cos.x * sin.z;
|
||||||
|
float r6 = -sin.y;
|
||||||
|
float r7 = cos.y * sin.z;
|
||||||
|
float r8 = cos.y * cos.z;
|
||||||
|
float3x3 result = float3x3(r0, r1, r2, r3, r4, r5, r6, r7, r8);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
float2x2 IdentityMatrix2x2()
|
||||||
|
{
|
||||||
|
return float2x2(
|
||||||
|
1, 0,
|
||||||
|
0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
float3x3 IdentityMatrix3x3()
|
||||||
|
{
|
||||||
|
return float3x3(
|
||||||
|
1, 0, 0,
|
||||||
|
0, 1, 0,
|
||||||
|
0, 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
matrix IdentityMatrix4x4()
|
||||||
|
{
|
||||||
|
return matrix(
|
||||||
|
1, 0, 0, 0,
|
||||||
|
0, 1, 0, 0,
|
||||||
|
0, 0, 1, 0,
|
||||||
|
0, 0, 0, 1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,141 @@
|
||||||
|
/*
|
||||||
|
===========================================================================
|
||||||
|
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/>.
|
||||||
|
===========================================================================
|
||||||
|
*/
|
||||||
|
// shared structure for the Quake 3 light grid
|
||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
|
#include "typedefs.h.hlsli"
|
||||||
|
#if !defined(__cplusplus)
|
||||||
|
# include "common.hlsli"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
# pragma pack(push, 4)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct LightGridRC
|
||||||
|
{
|
||||||
|
float3 centerPosition;
|
||||||
|
uint textureAIndex;
|
||||||
|
float3 worldScale;
|
||||||
|
uint textureBIndex;
|
||||||
|
uint samplerIndex;
|
||||||
|
uint isAvailable;
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
# pragma pack(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
static_assert(sizeof(LightGridRC) == 40, "sizeof(LightGridRC) is wrong");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(__cplusplus)
|
||||||
|
|
||||||
|
struct LightGridSample
|
||||||
|
{
|
||||||
|
float4 a;
|
||||||
|
float4 b;
|
||||||
|
|
||||||
|
float3 GetLightDirection()
|
||||||
|
{
|
||||||
|
return DirectionFromLongLat(b.z, b.w);
|
||||||
|
}
|
||||||
|
|
||||||
|
float3 GetLocalColor()
|
||||||
|
{
|
||||||
|
return float3(a.w, b.xy);
|
||||||
|
}
|
||||||
|
|
||||||
|
float3 GetGlobalColor()
|
||||||
|
{
|
||||||
|
return a.xyz;
|
||||||
|
}
|
||||||
|
|
||||||
|
float3 GetAmbientColor(float3 normal, float3 fallbackColor, float ambColorScale, float localColorScale)
|
||||||
|
{
|
||||||
|
float3 ambColor = GetGlobalColor();
|
||||||
|
float3 localColor = GetLocalColor();
|
||||||
|
float3 localDir = GetLightDirection();
|
||||||
|
float localScale = dot(localDir, normal) * 0.5 + 0.5; // wraps around
|
||||||
|
float3 interpColor = ambColor * ambColorScale + localColor * localScale * localColorScale;
|
||||||
|
float brightNew = Brightness(interpColor);
|
||||||
|
float brightFall = Brightness(fallbackColor);
|
||||||
|
float t = saturate(brightNew / max(brightFall, 0.001));
|
||||||
|
float3 color = lerp(fallbackColor, interpColor, t);
|
||||||
|
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LightGrid
|
||||||
|
{
|
||||||
|
float3 centerPosition;
|
||||||
|
float3 worldScale;
|
||||||
|
float3 textureSize;
|
||||||
|
Texture3D textureA;
|
||||||
|
Texture3D textureB;
|
||||||
|
SamplerState sampler0;
|
||||||
|
|
||||||
|
LightGridSample SampleAtPosition(float3 positionWS)
|
||||||
|
{
|
||||||
|
float3 ambientTC = AABoxWorldSpaceToTC(positionWS, centerPosition, textureSize, worldScale);
|
||||||
|
LightGridSample sample;
|
||||||
|
sample.a = textureA.SampleLevel(sampler0, ambientTC, 0);
|
||||||
|
sample.b = textureB.SampleLevel(sampler0, ambientTC, 0);
|
||||||
|
|
||||||
|
return sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
LightGridSample SampleAtIndex(int3 index)
|
||||||
|
{
|
||||||
|
LightGridSample sample;
|
||||||
|
sample.a = textureA[index];
|
||||||
|
sample.b = textureB[index];
|
||||||
|
|
||||||
|
return sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
float3 IndexToWorldSpace(int3 voxelIndex)
|
||||||
|
{
|
||||||
|
float3 voxelCenter = AABoxIndexToWorldSpace(voxelIndex, centerPosition, textureSize, worldScale);
|
||||||
|
|
||||||
|
return voxelCenter;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
LightGrid GetLightGrid(LightGridRC rc)
|
||||||
|
{
|
||||||
|
LightGrid grid;
|
||||||
|
grid.textureA = ResourceDescriptorHeap[rc.textureAIndex];
|
||||||
|
grid.textureB = ResourceDescriptorHeap[rc.textureBIndex];
|
||||||
|
grid.sampler0 = SamplerDescriptorHeap[rc.samplerIndex];
|
||||||
|
grid.centerPosition = rc.centerPosition;
|
||||||
|
grid.worldScale = rc.worldScale;
|
||||||
|
grid.textureSize = float3(GetTextureSize(grid.textureA));
|
||||||
|
|
||||||
|
return grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
===========================================================================
|
||||||
|
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/>.
|
||||||
|
===========================================================================
|
||||||
|
*/
|
||||||
|
// GPU particle system: emitter and particle free list initialization
|
||||||
|
|
||||||
|
|
||||||
|
#include "common.hlsli"
|
||||||
|
#include "scene_view.h.hlsli"
|
||||||
|
|
||||||
|
|
||||||
|
cbuffer RootConstants
|
||||||
|
{
|
||||||
|
uint emitterBufferIndex;
|
||||||
|
uint deadBufferIndex;
|
||||||
|
uint emitterIndex;
|
||||||
|
uint firstParticle;
|
||||||
|
uint particleCount;
|
||||||
|
float maxSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
[numthreads(64, 1, 1)]
|
||||||
|
void cs(uint3 id : SV_DispatchThreadID)
|
||||||
|
{
|
||||||
|
if(id.x >= particleCount)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(id.x == 0)
|
||||||
|
{
|
||||||
|
RWStructuredBuffer<ParticleEmitter> emitterBuffer = ResourceDescriptorHeap[emitterBufferIndex];
|
||||||
|
ParticleEmitter e;
|
||||||
|
e.deadCount = particleCount;
|
||||||
|
e.firstIndex = firstParticle;
|
||||||
|
e.liveCount = 0;
|
||||||
|
e.liveCount2 = 0;
|
||||||
|
e.totalCount = particleCount;
|
||||||
|
e.emitCount = 0;
|
||||||
|
e.maxSeconds = maxSeconds;
|
||||||
|
emitterBuffer[emitterIndex] = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
RWStructuredBuffer<uint> deadBuffer = ResourceDescriptorHeap[deadBufferIndex];
|
||||||
|
deadBuffer[firstParticle + id.x] = particleCount - 1 - id.x;
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
===========================================================================
|
||||||
|
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/>.
|
||||||
|
===========================================================================
|
||||||
|
*/
|
||||||
|
// GPU particle system: particle emission step
|
||||||
|
|
||||||
|
|
||||||
|
#include "common.hlsli"
|
||||||
|
#include "scene_view.h.hlsli"
|
||||||
|
|
||||||
|
|
||||||
|
cbuffer RootConstants
|
||||||
|
{
|
||||||
|
uint particleBufferIndex;
|
||||||
|
uint liveBufferIndex;
|
||||||
|
uint deadBufferIndex;
|
||||||
|
uint emitterBufferIndex;
|
||||||
|
uint emitterIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
[numthreads(64, 1, 1)]
|
||||||
|
void cs(uint3 id : SV_DispatchThreadID)
|
||||||
|
{
|
||||||
|
RWStructuredBuffer<ParticleEmitter> emitterBuffer = ResourceDescriptorHeap[emitterBufferIndex];
|
||||||
|
uint emitCount = emitterBuffer[emitterIndex].emitCount;
|
||||||
|
if(id.x >= emitCount)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RWStructuredBuffer<Particle> particleBuffer = ResourceDescriptorHeap[particleBufferIndex];
|
||||||
|
RWStructuredBuffer<uint> deadBuffer = ResourceDescriptorHeap[deadBufferIndex];
|
||||||
|
RWStructuredBuffer<uint> liveBuffer = ResourceDescriptorHeap[liveBufferIndex];
|
||||||
|
SceneView scene = GetSceneView();
|
||||||
|
|
||||||
|
uint firstIndex = emitterBuffer[emitterIndex].firstIndex;
|
||||||
|
uint oldDeadCount;
|
||||||
|
InterlockedAdd(emitterBuffer[emitterIndex].deadCount, -1, oldDeadCount);
|
||||||
|
uint newIndex = deadBuffer[firstIndex + oldDeadCount - 1];
|
||||||
|
uint oldLiveCount;
|
||||||
|
InterlockedAdd(emitterBuffer[emitterIndex].liveCount, 1, oldLiveCount);
|
||||||
|
liveBuffer[firstIndex + oldLiveCount] = newIndex;
|
||||||
|
|
||||||
|
Particle p;
|
||||||
|
#if 1 // @TODO: insert proper logic here
|
||||||
|
p.absorption = 1.0;
|
||||||
|
p.anisotropy = 0.5;
|
||||||
|
p.isEmissive = 0;
|
||||||
|
p.position = float3(694, -42, -300);
|
||||||
|
p.velocity = (Hash1To3(scene.frameSeed + 17.69 * float(id.x)) * 2.0 - 1.0) * 20.0;
|
||||||
|
p.radius = 10.0;
|
||||||
|
p.scattering = float3(1.0, 0.5, 0.0);
|
||||||
|
p.lifeTime = 0.0;
|
||||||
|
p.froxelMin = int3(0, 0, 0);
|
||||||
|
p.froxelMax = int3(-1, -1, -1);
|
||||||
|
#endif
|
||||||
|
particleBuffer[firstIndex + newIndex] = p;
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
===========================================================================
|
||||||
|
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/>.
|
||||||
|
===========================================================================
|
||||||
|
*/
|
||||||
|
// GPU particle system: sets up buffers for 1 emission step and 1 simulation step
|
||||||
|
|
||||||
|
|
||||||
|
#include "common.hlsli"
|
||||||
|
#include "scene_view.h.hlsli"
|
||||||
|
|
||||||
|
|
||||||
|
cbuffer RootConstants
|
||||||
|
{
|
||||||
|
uint emitterBufferIndex;
|
||||||
|
uint indirectBufferIndex;
|
||||||
|
uint emitterIndex;
|
||||||
|
uint emitCountRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
[numthreads(1, 1, 1)]
|
||||||
|
void cs(uint3 id : SV_DispatchThreadID)
|
||||||
|
{
|
||||||
|
RWStructuredBuffer<ParticleEmitter> emitterBuffer = ResourceDescriptorHeap[emitterBufferIndex];
|
||||||
|
RWByteAddressBuffer indirectBuffer = ResourceDescriptorHeap[indirectBufferIndex];
|
||||||
|
|
||||||
|
uint deadCount = emitterBuffer[emitterIndex].deadCount;
|
||||||
|
uint liveCount = emitterBuffer[emitterIndex].liveCount2;
|
||||||
|
uint emitCount = min(deadCount, emitCountRequest);
|
||||||
|
indirectBuffer.Store(0, (emitCount + 63) / 64); // emit.x
|
||||||
|
indirectBuffer.Store(12, (liveCount + emitCount + 63) / 64); // simulate.x
|
||||||
|
emitterBuffer[emitterIndex].liveCount = liveCount;
|
||||||
|
emitterBuffer[emitterIndex].liveCount2 = 0;
|
||||||
|
emitterBuffer[emitterIndex].emitCount = emitCount;
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
/*
|
||||||
|
===========================================================================
|
||||||
|
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/>.
|
||||||
|
===========================================================================
|
||||||
|
*/
|
||||||
|
// GPU particle system: particle simulation step
|
||||||
|
|
||||||
|
|
||||||
|
#include "common.hlsli"
|
||||||
|
#include "scene_view.h.hlsli"
|
||||||
|
|
||||||
|
|
||||||
|
cbuffer RootConstants
|
||||||
|
{
|
||||||
|
uint particleBufferIndex;
|
||||||
|
uint liveSrcBufferIndex;
|
||||||
|
uint liveDstBufferIndex;
|
||||||
|
uint deadBufferIndex;
|
||||||
|
uint emitterBufferIndex;
|
||||||
|
uint emitterIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
[numthreads(64, 1, 1)]
|
||||||
|
void cs(uint3 id : SV_DispatchThreadID)
|
||||||
|
{
|
||||||
|
RWStructuredBuffer<ParticleEmitter> emitterBuffer = ResourceDescriptorHeap[emitterBufferIndex];
|
||||||
|
if(id.x >= emitterBuffer[emitterIndex].liveCount)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RWStructuredBuffer<Particle> particleBuffer = ResourceDescriptorHeap[particleBufferIndex];
|
||||||
|
RWStructuredBuffer<uint> liveSrcBuffer = ResourceDescriptorHeap[liveSrcBufferIndex];
|
||||||
|
|
||||||
|
uint firstIndex = emitterBuffer[emitterIndex].firstIndex;
|
||||||
|
uint particleIndex = liveSrcBuffer[firstIndex + id.x];
|
||||||
|
Particle p = particleBuffer[firstIndex + particleIndex];
|
||||||
|
if(p.lifeTime >= emitterBuffer[emitterIndex].maxSeconds)
|
||||||
|
{
|
||||||
|
RWStructuredBuffer<uint> deadBuffer = ResourceDescriptorHeap[deadBufferIndex];
|
||||||
|
uint oldDeadCount;
|
||||||
|
InterlockedAdd(emitterBuffer[emitterIndex].deadCount, 1, oldDeadCount);
|
||||||
|
deadBuffer[firstIndex + oldDeadCount] = particleIndex;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RWStructuredBuffer<uint> liveDstBuffer = ResourceDescriptorHeap[liveDstBufferIndex];
|
||||||
|
#if 1 // @TODO: insert proper logic here
|
||||||
|
float dt = 1.0 / 60.0;
|
||||||
|
float t = p.lifeTime / emitterBuffer[emitterIndex].maxSeconds;
|
||||||
|
float fadeOut = 1.0 - EaseOutQuad(t);
|
||||||
|
float3 velocityNoise = Hash3To3(p.position); // want Curl
|
||||||
|
float scatterNoise = Hash2To1(float2(particleIndex * 1337.0, p.lifeTime)); // want Simplex
|
||||||
|
float3 color0 = float3(1, 1, 1);
|
||||||
|
float3 color1 = float3(0.25, 0.25, 0.25);
|
||||||
|
float3 color = lerp(color0, color1, t);
|
||||||
|
p.lifeTime += dt;
|
||||||
|
p.position += p.velocity * dt;
|
||||||
|
p.velocity *= 0.85;
|
||||||
|
p.velocity += velocityNoise * (1.0 + 0.077 * 100.0);
|
||||||
|
p.velocity += float3(0, 0, 1) * (1.0 + 0.210 * 100.0);
|
||||||
|
p.absorption = fadeOut * 0.02;
|
||||||
|
p.scattering = fadeOut * (color + float(0.5 + scatterNoise).xxx) * 0.02;
|
||||||
|
p.radius = 5.0 + 2.0 * EaseInCubic(t);
|
||||||
|
#endif
|
||||||
|
particleBuffer[firstIndex + particleIndex] = p;
|
||||||
|
|
||||||
|
uint oldLiveCount;
|
||||||
|
InterlockedAdd(emitterBuffer[emitterIndex].liveCount2, 1, oldLiveCount);
|
||||||
|
liveDstBuffer[firstIndex + oldLiveCount] = particleIndex;
|
||||||
|
}
|
|
@ -33,19 +33,39 @@ along with Challenge Quake 3. If not, see <https://www.gnu.org/licenses/>.
|
||||||
# pragma pack(push, 4)
|
# pragma pack(push, 4)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// @TODO: move out
|
||||||
|
struct ParticleEmitter
|
||||||
|
{
|
||||||
|
uint firstIndex;
|
||||||
|
uint liveCount; // post-emission, pre-simulation
|
||||||
|
uint liveCount2; // post-simulation
|
||||||
|
uint deadCount;
|
||||||
|
uint totalCount;
|
||||||
|
uint emitCount; // how many added this frame
|
||||||
|
float maxSeconds;
|
||||||
|
};
|
||||||
|
|
||||||
// @TODO: move out
|
// @TODO: move out
|
||||||
struct Particle
|
struct Particle
|
||||||
{
|
{
|
||||||
|
// needed for injection, set by user
|
||||||
float3 position;
|
float3 position;
|
||||||
float radius;
|
float radius;
|
||||||
float3 scattering; // or emissive
|
float3 scattering; // or emissive
|
||||||
float absorption;
|
float absorption;
|
||||||
float anisotropy;
|
float anisotropy;
|
||||||
uint isEmissive;
|
uint isEmissive;
|
||||||
|
// needed for injection, private
|
||||||
|
int3 froxelMin;
|
||||||
|
int3 froxelMax;
|
||||||
|
// extra data for simulation only
|
||||||
|
float3 velocity;
|
||||||
|
float lifeTime; // in seconds
|
||||||
};
|
};
|
||||||
|
|
||||||
// @TODO: move out
|
// @TODO: move out
|
||||||
#define MAX_PARTICLES 8192
|
#define MAX_PARTICLES (1 << 20)
|
||||||
|
#define MAX_PARTICLE_EMITTERS (1 << 10)
|
||||||
|
|
||||||
// @TODO: move out
|
// @TODO: move out
|
||||||
struct DynamicLight
|
struct DynamicLight
|
||||||
|
@ -56,9 +76,6 @@ struct DynamicLight
|
||||||
float padding;
|
float padding;
|
||||||
};
|
};
|
||||||
|
|
||||||
// @TODO: move out
|
|
||||||
#define SCENE_VIEW_MAX_LIGHTS 32
|
|
||||||
|
|
||||||
#if !defined(__cplusplus)
|
#if !defined(__cplusplus)
|
||||||
struct ExtinctionCascade
|
struct ExtinctionCascade
|
||||||
{
|
{
|
||||||
|
@ -230,7 +247,7 @@ struct SceneView
|
||||||
float4 clipPlane;
|
float4 clipPlane;
|
||||||
float4 debug;
|
float4 debug;
|
||||||
float3 cameraPosition;
|
float3 cameraPosition;
|
||||||
float sunIntensity;
|
float sunIntensityDL;
|
||||||
float3 sunDirection;
|
float3 sunDirection;
|
||||||
float zNear;
|
float zNear;
|
||||||
float3 sunColor;
|
float3 sunColor;
|
||||||
|
@ -240,9 +257,9 @@ struct SceneView
|
||||||
float3 cameraForward;
|
float3 cameraForward;
|
||||||
float prevZFar;
|
float prevZFar;
|
||||||
float3 cameraLeft;
|
float3 cameraLeft;
|
||||||
float padding0;
|
float sunIntensityVL;
|
||||||
float3 cameraUp;
|
float3 cameraUp;
|
||||||
float padding1;
|
float pointLightIntensityVL;
|
||||||
float3 linearDepthConstants;
|
float3 linearDepthConstants;
|
||||||
float frameSeed;
|
float frameSeed;
|
||||||
float3 ambientColor;
|
float3 ambientColor;
|
||||||
|
@ -282,7 +299,7 @@ struct SceneView
|
||||||
return ray;
|
return ray;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 1 // exponential depth distribution like The Last of Us Part II
|
#if 0 // exponential depth distribution like The Last of Us Part II
|
||||||
|
|
||||||
float TLOU2SliceToViewDepth(float slice, float C)
|
float TLOU2SliceToViewDepth(float slice, float C)
|
||||||
{
|
{
|
||||||
|
@ -295,7 +312,8 @@ struct SceneView
|
||||||
float TLOU2ViewDepthToSlice(float viewDepth, float C)
|
float TLOU2ViewDepthToSlice(float viewDepth, float C)
|
||||||
{
|
{
|
||||||
const float Q = 1.0;
|
const float Q = 1.0;
|
||||||
float slice = C * (log2(exp2(Q) + viewDepth) - Q);
|
float logArg = exp2(Q) + viewDepth;
|
||||||
|
float slice = logArg <= 0.0 ? -666.0 : (C * (log2(logArg) - Q));
|
||||||
|
|
||||||
return slice;
|
return slice;
|
||||||
}
|
}
|
||||||
|
@ -328,6 +346,24 @@ struct SceneView
|
||||||
return viewDepth;
|
return viewDepth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#elif 1 // quadratic depth distribution
|
||||||
|
|
||||||
|
float FroxelViewDepthToZ01Ex(float viewDepth, float sliceCount, float zn, float zf)
|
||||||
|
{
|
||||||
|
float depth01 = (viewDepth - zn) / (zf - zn);
|
||||||
|
depth01 = sqrt(depth01);
|
||||||
|
|
||||||
|
return depth01;
|
||||||
|
}
|
||||||
|
|
||||||
|
float FroxelZ01ToViewDepth(float depth01, float sliceCount)
|
||||||
|
{
|
||||||
|
depth01 *= depth01;
|
||||||
|
float viewDepth = zNear + (zFar - zNear) * depth01;
|
||||||
|
|
||||||
|
return viewDepth;
|
||||||
|
}
|
||||||
|
|
||||||
#else // linear depth distribution
|
#else // linear depth distribution
|
||||||
|
|
||||||
float FroxelViewDepthToZ01Ex(float viewDepth, float sliceCount, float zn, float zf)
|
float FroxelViewDepthToZ01Ex(float viewDepth, float sliceCount, float zn, float zf)
|
||||||
|
@ -400,6 +436,19 @@ struct SceneView
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int2 FroxelSphereZExtents(float3 positionWS, float radius, float3 textureSize)
|
||||||
|
{
|
||||||
|
float4 positionVSw = mul(viewMatrix, float4(positionWS, 1));
|
||||||
|
float viewDepth = -positionVSw.z / positionVSw.w;
|
||||||
|
float viewDepthMin = viewDepth - radius;
|
||||||
|
float viewDepthMax = viewDepth + radius;
|
||||||
|
float zMin01 = FroxelViewDepthToZ01(viewDepthMin, textureSize.z);
|
||||||
|
float zMax01 = FroxelViewDepthToZ01(viewDepthMax, textureSize.z);
|
||||||
|
int2 extents = int2(zMin01 * textureSize.z, ceil(zMax01 * textureSize.z));
|
||||||
|
|
||||||
|
return extents;
|
||||||
|
}
|
||||||
|
|
||||||
// @TODO: validate new logic
|
// @TODO: validate new logic
|
||||||
float3 FroxelReproject01(int3 currIndex, float3 textureSize)
|
float3 FroxelReproject01(int3 currIndex, float3 textureSize)
|
||||||
{
|
{
|
||||||
|
@ -420,6 +469,82 @@ struct SceneView
|
||||||
return prevTC;
|
return prevTC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float FroxelVolume(uint3 index, float3 textureSize)
|
||||||
|
{
|
||||||
|
float3 tcBase = (float3(index) + float3(0.5, 0.5, 0.5)) / textureSize;
|
||||||
|
float3 halfTexel = float3(0.5, 0.5, 0.5) / textureSize;
|
||||||
|
float3 posL = FroxelTCToWorldSpace(tcBase + float3(-halfTexel.x, 0, 0), textureSize);
|
||||||
|
float3 posR = FroxelTCToWorldSpace(tcBase + float3(halfTexel.x, 0, 0), textureSize);
|
||||||
|
float w = distance(posL, posR);
|
||||||
|
float3 posU = FroxelTCToWorldSpace(tcBase + float3(0, halfTexel.y, 0), textureSize);
|
||||||
|
float3 posD = FroxelTCToWorldSpace(tcBase + float3(0, -halfTexel.y, 0), textureSize);
|
||||||
|
float h = distance(posU, posD);
|
||||||
|
float3 posF = FroxelTCToWorldSpace(tcBase + float3(0, 0, halfTexel.z), textureSize);
|
||||||
|
float3 posB = FroxelTCToWorldSpace(tcBase + float3(0, 0, -halfTexel.z), textureSize);
|
||||||
|
float d = distance(posF, posB);
|
||||||
|
float volume = w * h * d;
|
||||||
|
|
||||||
|
return volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
float3 FroxelAverageDimensions(uint3 index, float3 textureSize)
|
||||||
|
{
|
||||||
|
float3 tcBase = (float3(index) + float3(0.5, 0.5, 0.5)) / textureSize;
|
||||||
|
float3 halfTexel = float3(0.5, 0.5, 0.5) / textureSize;
|
||||||
|
float3 posL = FroxelTCToWorldSpace(tcBase + float3(-halfTexel.x, 0, 0), textureSize);
|
||||||
|
float3 posR = FroxelTCToWorldSpace(tcBase + float3(halfTexel.x, 0, 0), textureSize);
|
||||||
|
float w = distance(posL, posR);
|
||||||
|
float3 posU = FroxelTCToWorldSpace(tcBase + float3(0, halfTexel.y, 0), textureSize);
|
||||||
|
float3 posD = FroxelTCToWorldSpace(tcBase + float3(0, -halfTexel.y, 0), textureSize);
|
||||||
|
float h = distance(posU, posD);
|
||||||
|
float3 posF = FroxelTCToWorldSpace(tcBase + float3(0, 0, -halfTexel.z), textureSize);
|
||||||
|
float3 posB = FroxelTCToWorldSpace(tcBase + float3(0, 0, halfTexel.z), textureSize);
|
||||||
|
float d = distance(posF, posB);
|
||||||
|
float3 dimensions = float3(w, h, d);
|
||||||
|
|
||||||
|
return dimensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
float3 FroxelMaxDimensions(uint3 index, float3 textureSize)
|
||||||
|
{
|
||||||
|
float3 tcBase = (float3(index) + float3(0.5, 0.5, 0.5)) / textureSize;
|
||||||
|
float3 halfTexel = float3(0.5, 0.5, 0.5) / textureSize;
|
||||||
|
float3 posL = FroxelTCToWorldSpace(tcBase + float3(-halfTexel.x, 0, halfTexel.z), textureSize);
|
||||||
|
float3 posR = FroxelTCToWorldSpace(tcBase + float3(halfTexel.x, 0, halfTexel.z), textureSize);
|
||||||
|
float w = distance(posL, posR);
|
||||||
|
float3 posU = FroxelTCToWorldSpace(tcBase + float3(0, halfTexel.y, halfTexel.z), textureSize);
|
||||||
|
float3 posD = FroxelTCToWorldSpace(tcBase + float3(0, -halfTexel.y, halfTexel.z), textureSize);
|
||||||
|
float h = distance(posU, posD);
|
||||||
|
float3 posF = FroxelTCToWorldSpace(tcBase + float3(0, 0, -halfTexel.z), textureSize);
|
||||||
|
float3 posB = FroxelTCToWorldSpace(tcBase + float3(0, 0, halfTexel.z), textureSize);
|
||||||
|
float d = distance(posF, posB);
|
||||||
|
float3 dimensions = float3(w, h, d);
|
||||||
|
|
||||||
|
return dimensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
void FroxelAABB(out float boxMin, out float3 boxMax, int3 index, float3 textureSize)
|
||||||
|
{
|
||||||
|
float3 tc = (float3(index) + float3(0.5, 0.5, 0.5)) / textureSize;
|
||||||
|
float3 halfTexel = float3(0.5, 0.5, 0.5) / textureSize;
|
||||||
|
float3 pointWS;
|
||||||
|
ClearBoundingBox(boxMin, boxMax);
|
||||||
|
pointWS = FroxelTCToWorldSpace(tc + float3(-halfTexel.x, 0, 0), textureSize);
|
||||||
|
ExpandBoundingBox(boxMin, boxMax, pointWS);
|
||||||
|
pointWS = FroxelTCToWorldSpace(tc + float3(halfTexel.x, 0, 0), textureSize);
|
||||||
|
ExpandBoundingBox(boxMin, boxMax, pointWS);
|
||||||
|
pointWS = FroxelTCToWorldSpace(tc + float3(0, -halfTexel.y, 0), textureSize);
|
||||||
|
ExpandBoundingBox(boxMin, boxMax, pointWS);
|
||||||
|
pointWS = FroxelTCToWorldSpace(tc + float3(0, halfTexel.y, 0), textureSize);
|
||||||
|
ExpandBoundingBox(boxMin, boxMax, pointWS);
|
||||||
|
pointWS = FroxelTCToWorldSpace(tc + float3(0, 0, -halfTexel.z), textureSize);
|
||||||
|
ExpandBoundingBox(boxMin, boxMax, pointWS);
|
||||||
|
pointWS = FroxelTCToWorldSpace(tc + float3(0, 0, halfTexel.z), textureSize);
|
||||||
|
ExpandBoundingBox(boxMin, boxMax, pointWS);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
float3 ExtinctionIndexToWorldSpace(int3 index, float3 textureSize, float worldScale)
|
float3 ExtinctionIndexToWorldSpace(int3 index, float3 textureSize, float worldScale)
|
||||||
{
|
{
|
||||||
return AABoxIndexToWorldSpace(index, cameraPosition, textureSize, worldScale);
|
return AABoxIndexToWorldSpace(index, cameraPosition, textureSize, worldScale);
|
||||||
|
|
|
@ -106,12 +106,11 @@ float4 ps(VOut input) : SV_Target
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// @TODO: all light intensities are 50x for light scattering?
|
|
||||||
float visOpaque = weightSum > 0.0 ? visSum / weightSum : 0.0;
|
float visOpaque = weightSum > 0.0 ? visSum / weightSum : 0.0;
|
||||||
float visVolume = cascade.TransmittanceAt(positionWS);
|
float visVolume = cascade.TransmittanceAt(positionWS);
|
||||||
float vis = visOpaque * visVolume;
|
float vis = visOpaque * visVolume;
|
||||||
float lambert = max(dot(normalWS, scene.sunDirection), 0.0);
|
float lambert = max(dot(normalWS, scene.sunDirection), 0.0);
|
||||||
float3 color = vis * scene.sunColor * min(scene.sunIntensity / 10.0, 5.0) * lambert;
|
float3 color = vis * scene.sunColor * scene.sunIntensityDL * lambert;
|
||||||
float4 result = float4(color, 1);
|
float4 result = float4(color, 1);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -70,13 +70,27 @@ struct FogVolume
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Tile
|
||||||
|
{
|
||||||
|
uint firstParticle;
|
||||||
|
uint particleCount;
|
||||||
|
uint particleIndex;
|
||||||
|
uint pad0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Counters
|
||||||
|
{
|
||||||
|
uint particleCount;
|
||||||
|
uint tileCount;
|
||||||
|
};
|
||||||
|
|
||||||
#if defined(__cplusplus)
|
#if defined(__cplusplus)
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(__cplusplus)
|
#if !defined(__cplusplus)
|
||||||
|
|
||||||
// defines voxel sampling offsets for super-sampled fog injection
|
// defines voxel sampling offsets for super-sampled fog/particle injection
|
||||||
|
|
||||||
#if defined(VOXEL_SUPERSAMPLING_1X)
|
#if defined(VOXEL_SUPERSAMPLING_1X)
|
||||||
static const int VoxelSampleCount = 1;
|
static const int VoxelSampleCount = 1;
|
||||||
|
@ -203,4 +217,46 @@ static const float3 VoxelSamples[65] =
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// defines sphere sampling offsets for super-sampled particle injection
|
||||||
|
|
||||||
|
#if defined(SPHERE_SUPERSAMPLING_1X)
|
||||||
|
static const int SphereSampleCount = 1;
|
||||||
|
static const float3 SphereSamples[1] =
|
||||||
|
{
|
||||||
|
float3(0, 0, 0)
|
||||||
|
};
|
||||||
|
#elif defined(SPHERE_SUPERSAMPLING_2X)
|
||||||
|
static const int SphereSampleCount = 13;
|
||||||
|
static const float3 SphereSamples[13] =
|
||||||
|
{
|
||||||
|
float3(-0.000070998815798, -0.000005560663499, -0.666666662862852),
|
||||||
|
float3(-0.184273763669020, -0.567104158597683, -0.298128324331843),
|
||||||
|
float3(0.482401099870031, -0.350493177757963, -0.298141167291191),
|
||||||
|
float3(-0.596343845795264, -0.000022877791848, -0.298024263279293),
|
||||||
|
float3(0.482326151451980, 0.350363917328204, -0.298414231403936),
|
||||||
|
float3(-0.184272548445118, 0.567103656272574, -0.298130030986924),
|
||||||
|
float3(0.000000000000000, 0.000000000000000, 0.000000000000000),
|
||||||
|
float3(0.184241177452589, -0.567097520996929, 0.298161088431179),
|
||||||
|
float3(-0.482404802195943, -0.350487719732367, 0.298141593172679),
|
||||||
|
float3(0.596236953290593, -0.000018337625111, 0.298238058669458),
|
||||||
|
float3(-0.482641375963473, 0.350268310738867, 0.298016538374417),
|
||||||
|
float3(0.184262272798761, 0.567101475888716, 0.298140529469443),
|
||||||
|
float3(-0.000135987657896, -0.000010037080285, 0.666666652721627)
|
||||||
|
};
|
||||||
|
#elif defined(SPHERE_SUPERSAMPLING_2X_OLD) // 8x with 0,0,0 added
|
||||||
|
static const int SphereSampleCount = 9;
|
||||||
|
static const float3 SphereSamples[9] =
|
||||||
|
{
|
||||||
|
float3(0, 0, 0),
|
||||||
|
float3(-0.378024926878978, -0.378024806866870, -0.317879684372474),
|
||||||
|
float3(0.378024762722181, -0.378024944670528, -0.317879715711806),
|
||||||
|
float3(-0.378024789075323, 0.378024882734286, -0.317879758027500),
|
||||||
|
float3(0.378024900525835, 0.378024744930628, -0.317879789366832),
|
||||||
|
float3(-0.000000084265328, -0.534607831462839, 0.317879788951622),
|
||||||
|
float3(-0.534607849254392, 0.000000128410024, 0.317879759029907),
|
||||||
|
float3(0.534607875607536, -0.000000066473779, 0.317879714709398),
|
||||||
|
float3(0.000000110618471, 0.534607893399083, 0.317879684787684)
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -23,15 +23,13 @@ along with Challenge Quake 3. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
#include "common.hlsli"
|
#include "common.hlsli"
|
||||||
#include "scene_view.h.hlsli"
|
#include "scene_view.h.hlsli"
|
||||||
|
#include "light_grid.h.hlsli"
|
||||||
|
|
||||||
|
|
||||||
cbuffer RootConstants
|
cbuffer RootConstants
|
||||||
{
|
{
|
||||||
float3 centerPosition;
|
LightGridRC lightGridRC;
|
||||||
float sphereScale;
|
float sphereScale;
|
||||||
float3 worldScale;
|
|
||||||
uint lightGridTextureAIndex;
|
|
||||||
uint lightGridTextureBIndex;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct VOut
|
struct VOut
|
||||||
|
@ -44,16 +42,15 @@ struct VOut
|
||||||
|
|
||||||
VOut vs(uint vertexId : SV_VertexID)
|
VOut vs(uint vertexId : SV_VertexID)
|
||||||
{
|
{
|
||||||
Texture3D lightGridTexture = ResourceDescriptorHeap[lightGridTextureAIndex];
|
|
||||||
SceneView scene = GetSceneView();
|
SceneView scene = GetSceneView();
|
||||||
|
LightGrid lightGrid = GetLightGrid(lightGridRC);
|
||||||
|
|
||||||
uint3 textureSize = GetTextureSize(lightGridTexture);
|
|
||||||
uint flatVoxelIndex = vertexId / 6;
|
uint flatVoxelIndex = vertexId / 6;
|
||||||
uint vertexIndex = vertexId % 6;
|
uint vertexIndex = vertexId % 6;
|
||||||
int3 voxelIndex = int3(UnflattenIndex(flatVoxelIndex, textureSize));
|
int3 voxelIndex = int3(UnflattenIndex(flatVoxelIndex, lightGrid.textureSize));
|
||||||
float3 voxelCenter = AABoxIndexToWorldSpace(voxelIndex, centerPosition, float3(textureSize), worldScale);
|
float3 voxelCenter = lightGrid.IndexToWorldSpace(voxelIndex);
|
||||||
float2 quadPosition = QuadFromVertexID(vertexIndex);
|
float2 quadPosition = QuadFromVertexID(vertexIndex);
|
||||||
float radius = 0.5 * sphereScale * min3(worldScale.x, worldScale.y, worldScale.z);
|
float radius = 0.5 * sphereScale * min3(lightGrid.worldScale.x, lightGrid.worldScale.y, lightGrid.worldScale.z);
|
||||||
float3 up = scene.cameraUp;
|
float3 up = scene.cameraUp;
|
||||||
float3 forward = normalize(voxelCenter - scene.cameraPosition);
|
float3 forward = normalize(voxelCenter - scene.cameraPosition);
|
||||||
float3 right = normalize(cross(forward, up));
|
float3 right = normalize(cross(forward, up));
|
||||||
|
@ -79,9 +76,8 @@ VOut vs(uint vertexId : SV_VertexID)
|
||||||
|
|
||||||
float4 ps(VOut input) : SV_Target
|
float4 ps(VOut input) : SV_Target
|
||||||
{
|
{
|
||||||
Texture3D lightGridTextureA = ResourceDescriptorHeap[lightGridTextureAIndex];
|
|
||||||
Texture3D lightGridTextureB = ResourceDescriptorHeap[lightGridTextureBIndex];
|
|
||||||
SceneView scene = GetSceneView();
|
SceneView scene = GetSceneView();
|
||||||
|
LightGrid lightGrid = GetLightGrid(lightGridRC);
|
||||||
|
|
||||||
float3 rayDir = normalize(input.positionWS - scene.cameraPosition);
|
float3 rayDir = normalize(input.positionWS - scene.cameraPosition);
|
||||||
float t = RaytraceSphere(scene.cameraPosition, rayDir, input.sphere.xyz, input.sphere.w);
|
float t = RaytraceSphere(scene.cameraPosition, rayDir, input.sphere.xyz, input.sphere.w);
|
||||||
|
@ -90,11 +86,10 @@ float4 ps(VOut input) : SV_Target
|
||||||
discard;
|
discard;
|
||||||
}
|
}
|
||||||
|
|
||||||
float4 payloadA = lightGridTextureA[input.voxelIndex];
|
LightGridSample ambient = lightGrid.SampleAtIndex(input.voxelIndex);
|
||||||
float4 payloadB = lightGridTextureB[input.voxelIndex];
|
|
||||||
float3 hitPosition = scene.cameraPosition + rayDir * t;
|
float3 hitPosition = scene.cameraPosition + rayDir * t;
|
||||||
float3 normal = normalize(hitPosition - input.sphere.xyz);
|
float3 normal = normalize(hitPosition - input.sphere.xyz);
|
||||||
float3 color = AmbientColor(payloadA, payloadB, normal, scene.ambientColor);
|
float3 color = ambient.GetAmbientColor(normal, scene.ambientColor, 1, 1);
|
||||||
float4 result = float4(color * 0.5, 1);
|
float4 result = float4(color * 0.5, 1);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -0,0 +1,113 @@
|
||||||
|
/*
|
||||||
|
===========================================================================
|
||||||
|
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/>.
|
||||||
|
===========================================================================
|
||||||
|
*/
|
||||||
|
// volumetric lighting: inject a NanoVDB volume into the extinction volume
|
||||||
|
|
||||||
|
|
||||||
|
#include "common.hlsli"
|
||||||
|
#include "scene_view.h.hlsli"
|
||||||
|
#include "vl_common.h.hlsli"
|
||||||
|
#include "vl_nanovdb.hlsli"
|
||||||
|
|
||||||
|
|
||||||
|
#define LOW_QUALITY_INJECTION_MODE 1
|
||||||
|
|
||||||
|
|
||||||
|
cbuffer RootConstants
|
||||||
|
{
|
||||||
|
float4 packedTransform[4];
|
||||||
|
float2 packedTransform2;
|
||||||
|
uint nanovdbBufferIndex;
|
||||||
|
uint extinctionTextureIndex;
|
||||||
|
uint densityGridByteOffset;
|
||||||
|
uint densityGridByteOffset2;
|
||||||
|
uint linearInterpolation;
|
||||||
|
float worldScale;
|
||||||
|
float densityExtinctionScale;
|
||||||
|
float t;
|
||||||
|
}
|
||||||
|
|
||||||
|
[numthreads(4, 4, 4)]
|
||||||
|
void cs(uint3 id : SV_DispatchThreadID)
|
||||||
|
{
|
||||||
|
RWTexture3D<float> extinctionTexture = ResourceDescriptorHeap[extinctionTextureIndex];
|
||||||
|
uint3 textureSize = GetTextureSize(extinctionTexture);
|
||||||
|
if(any(id >= textureSize))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pnanovdb_buf_t nanovdbBuffer = ResourceDescriptorHeap[nanovdbBufferIndex];
|
||||||
|
SceneView scene = GetSceneView();
|
||||||
|
|
||||||
|
float3 textureSizeF = float3(textureSize);
|
||||||
|
float3 tcBase = (float3(id) + float3(0.5, 0.5, 0.5)) / textureSizeF;
|
||||||
|
float3 voxelPosition = scene.ExtinctionIndexToWorldSpace(id, textureSizeF, worldScale);
|
||||||
|
Transform transform = DecodeTransform(packedTransform, packedTransform2);
|
||||||
|
#if LOW_QUALITY_INJECTION_MODE
|
||||||
|
// big perf boost, tiny visual impact
|
||||||
|
transform.stepSize = 0.5 * worldScale.xxx;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if(linearInterpolation != 0u)
|
||||||
|
{
|
||||||
|
SampleResult extResult1 = CreateSampleResult();
|
||||||
|
if(densityGridByteOffset)
|
||||||
|
{
|
||||||
|
Grid grid1 = GetGrid(nanovdbBuffer, densityGridByteOffset);
|
||||||
|
if(grid1.OverlapsAxisAlignedBox(scene, id, textureSizeF, worldScale, transform))
|
||||||
|
{
|
||||||
|
extResult1 = grid1.GetAxisAlignedBoxAverage(scene, voxelPosition, worldScale.xxx, transform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SampleResult extResult2 = CreateSampleResult();
|
||||||
|
if(densityGridByteOffset2 > 0)
|
||||||
|
{
|
||||||
|
Grid grid2 = GetGrid(nanovdbBuffer, densityGridByteOffset2);
|
||||||
|
if(grid2.OverlapsAxisAlignedBox(scene, id, textureSizeF, worldScale, transform))
|
||||||
|
{
|
||||||
|
extResult2 = grid2.GetAxisAlignedBoxAverage(scene, voxelPosition, worldScale.xxx, transform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(extResult1.sum > 0.0 || extResult2.sum > 0.0)
|
||||||
|
{
|
||||||
|
float extinction1 = (extResult1.sum / float(extResult1.maxSampleCount));
|
||||||
|
float extinction2 = (extResult2.sum / float(extResult2.maxSampleCount));
|
||||||
|
float extinction = lerp(extinction1, extinction2, t) * densityExtinctionScale;
|
||||||
|
extinctionTexture[id] += extinction;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(densityGridByteOffset > 0)
|
||||||
|
{
|
||||||
|
Grid grid = GetGrid(nanovdbBuffer, densityGridByteOffset);
|
||||||
|
if(grid.OverlapsAxisAlignedBox(scene, id, textureSizeF, worldScale, transform))
|
||||||
|
{
|
||||||
|
SampleResult extResult = CreateSampleResult();
|
||||||
|
extResult = grid.GetAxisAlignedBoxAverage(scene, voxelPosition, worldScale.xxx, transform);
|
||||||
|
if(extResult.sum > 0.0)
|
||||||
|
{
|
||||||
|
float extinction = (extResult.sum / float(extResult.maxSampleCount)) * densityExtinctionScale;
|
||||||
|
extinctionTexture[id] += extinction;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,100 +0,0 @@
|
||||||
/*
|
|
||||||
===========================================================================
|
|
||||||
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/>.
|
|
||||||
===========================================================================
|
|
||||||
*/
|
|
||||||
// volumetric lighting: inject particles into the extinction volume
|
|
||||||
|
|
||||||
|
|
||||||
#include "common.hlsli"
|
|
||||||
#include "scene_view.h.hlsli"
|
|
||||||
#define VOXEL_SUPERSAMPLING_2X
|
|
||||||
#include "vl_common.h.hlsli"
|
|
||||||
|
|
||||||
|
|
||||||
cbuffer RootConstants
|
|
||||||
{
|
|
||||||
uint3 tileScale;
|
|
||||||
uint particleBufferIndex;
|
|
||||||
uint particleCount;
|
|
||||||
uint extinctionTextureIndex;
|
|
||||||
uint tileBufferIndex;
|
|
||||||
uint tileCount;
|
|
||||||
float extinctionWorldScale;
|
|
||||||
}
|
|
||||||
|
|
||||||
[numthreads(512, 1, 1)]
|
|
||||||
void cs(uint3 dtid : SV_DispatchThreadID, uint gidx : SV_GroupIndex)
|
|
||||||
{
|
|
||||||
uint tileIndex = dtid.x / 512;
|
|
||||||
if(tileIndex >= tileCount)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
RWStructuredBuffer<uint3> tileBuffer = ResourceDescriptorHeap[tileBufferIndex];
|
|
||||||
RWTexture3D<float> extinctionTexture = ResourceDescriptorHeap[extinctionTextureIndex];
|
|
||||||
uint3 textureSize = GetTextureSize(extinctionTexture);
|
|
||||||
uint3 tileCornerIndex = tileBuffer[tileIndex];
|
|
||||||
uint3 tileThreadIndex = UnflattenIndex(gidx, tileScale);
|
|
||||||
uint3 id = tileCornerIndex * tileScale + tileThreadIndex;
|
|
||||||
if(any(id >= textureSize))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
StructuredBuffer<Particle> particleBuffer = ResourceDescriptorHeap[particleBufferIndex];
|
|
||||||
SceneView scene = GetSceneView();
|
|
||||||
|
|
||||||
float3 textureSizeF = float3(textureSize);
|
|
||||||
float3 tcBase = (float3(id) + float3(0.5, 0.5, 0.5)) / textureSizeF;
|
|
||||||
float accumExtinction = 0.0;
|
|
||||||
for(uint i = 0; i < particleCount; i++)
|
|
||||||
{
|
|
||||||
Particle particle = particleBuffer[i];
|
|
||||||
float extinction = particle.absorption;
|
|
||||||
[flatten]
|
|
||||||
if(particle.isEmissive == 0)
|
|
||||||
{
|
|
||||||
extinction += Brightness(particle.scattering);
|
|
||||||
}
|
|
||||||
|
|
||||||
float particleCoverage = 0.0;
|
|
||||||
for(uint s = 0; s < VoxelSampleCount; s++)
|
|
||||||
{
|
|
||||||
float3 tcSample = tcBase + VoxelSamples[s] / textureSizeF;
|
|
||||||
float3 position = scene.ExtinctionTCToWorldSpace(tcSample, textureSizeF, extinctionWorldScale);
|
|
||||||
float dist = distance(position, particle.position);
|
|
||||||
if(dist >= particle.radius)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
float coverage = sqrt(saturate(1.0 - dist / particle.radius));
|
|
||||||
particleCoverage += coverage;
|
|
||||||
}
|
|
||||||
particleCoverage /= float(VoxelSampleCount);
|
|
||||||
|
|
||||||
accumExtinction += particleCoverage * extinction;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(accumExtinction > 0.0)
|
|
||||||
{
|
|
||||||
extinctionTexture[id] += accumExtinction;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
===========================================================================
|
||||||
|
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/>.
|
||||||
|
===========================================================================
|
||||||
|
*/
|
||||||
|
// volumetric lighting: depth test screen tiles
|
||||||
|
|
||||||
|
|
||||||
|
#include "common.hlsli"
|
||||||
|
#include "scene_view.h.hlsli"
|
||||||
|
|
||||||
|
|
||||||
|
cbuffer RootConstants
|
||||||
|
{
|
||||||
|
uint3 frustumTextureSize;
|
||||||
|
uint frustumVisTextureIndex;
|
||||||
|
uint depthMip;
|
||||||
|
}
|
||||||
|
|
||||||
|
[numthreads(8, 8, 1)]
|
||||||
|
void cs(uint3 id : SV_DispatchThreadID)
|
||||||
|
{
|
||||||
|
SceneView scene = GetSceneView();
|
||||||
|
RWTexture2D<uint> frustumVisTexture = ResourceDescriptorHeap[frustumVisTextureIndex];
|
||||||
|
uint2 visTextureSize = GetTextureSize(frustumVisTexture);
|
||||||
|
uint2 tileIndex = id.xy;
|
||||||
|
if(any(tileIndex >= visTextureSize.xy))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Texture2D<float2> depthMinMaxTexture = ResourceDescriptorHeap[scene.depthMinMaxTextureIndex];
|
||||||
|
float3 frustumTextureSizeF = float3(frustumTextureSize);
|
||||||
|
// x=min is furthest with reverse Z
|
||||||
|
float tileDepthZW = depthMinMaxTexture.mips[depthMip][id.xy].x;
|
||||||
|
float tileDepth = scene.LinearDepth(tileDepthZW);
|
||||||
|
uint furthestVisibleIndex = 0;
|
||||||
|
for(uint d = 1; d < frustumTextureSize.z; d++)
|
||||||
|
{
|
||||||
|
// Z offset is 0 because we want the closest part of the froxel
|
||||||
|
float3 froxelTC = (float3(tileIndex.xy, d) + float3(0.5, 0.5, 0)) / frustumTextureSizeF;
|
||||||
|
float froxelDepth = scene.FroxelZ01ToViewDepth(froxelTC.z, frustumTextureSizeF.z);
|
||||||
|
if(froxelDepth < tileDepth)
|
||||||
|
{
|
||||||
|
furthestVisibleIndex = d;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
frustumVisTexture[tileIndex] = furthestVisibleIndex;
|
||||||
|
}
|
|
@ -0,0 +1,398 @@
|
||||||
|
/*
|
||||||
|
===========================================================================
|
||||||
|
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/>.
|
||||||
|
===========================================================================
|
||||||
|
*/
|
||||||
|
// volumetric lighting: inject a NanoVDB volume into the material textures
|
||||||
|
|
||||||
|
|
||||||
|
//#define PREVIEW_MODE 1
|
||||||
|
// preview mode is a faster and less complex version of the shader
|
||||||
|
// that doesn't crash the AMD/Intel shader compilers
|
||||||
|
|
||||||
|
// this shader considers smoke to be fully isotropic (g = 0)
|
||||||
|
// in "Creating the Atmospheric World of Red Dead Redemption 2" by Fabian Bauer,
|
||||||
|
// g is 0.1 with strong backscatter for smoke
|
||||||
|
// RDR2 uses a phase function with multiple octaves of HG
|
||||||
|
// it fakes backscattering for a specific extinction range
|
||||||
|
// the phase function returns the max. of the sum of octaves and the backscatter
|
||||||
|
|
||||||
|
|
||||||
|
#include "common.hlsli"
|
||||||
|
#include "scene_view.h.hlsli"
|
||||||
|
#include "light_grid.h.hlsli"
|
||||||
|
#include "vl_common.h.hlsli"
|
||||||
|
#include "vl_nanovdb.hlsli"
|
||||||
|
|
||||||
|
|
||||||
|
cbuffer RootConstants
|
||||||
|
{
|
||||||
|
float4 packedTransform[4];
|
||||||
|
float2 packedTransform2;
|
||||||
|
uint nanovdbBufferIndex;
|
||||||
|
uint blackbodyTextureIndex;
|
||||||
|
LightGridRC lightGridRC;
|
||||||
|
uint materialTextureAIndex;
|
||||||
|
uint materialTextureBIndex;
|
||||||
|
uint materialTextureCIndex;
|
||||||
|
uint scatterExtTextureIndex;
|
||||||
|
uint frustumVisTextureIndex;
|
||||||
|
uint densityGridByteOffset;
|
||||||
|
uint flamesGridByteOffset;
|
||||||
|
uint densityGridByteOffset2;
|
||||||
|
uint flamesGridByteOffset2;
|
||||||
|
uint linearInterpolation;
|
||||||
|
uint accurateOverlapTest;
|
||||||
|
uint ambientAngularCoverage;
|
||||||
|
float densityExtinctionScale;
|
||||||
|
float densityAlbedo;
|
||||||
|
float flamesEmissionScale;
|
||||||
|
float flamesTemperatureScale;
|
||||||
|
float stepScale; // for super-sampling froxel average
|
||||||
|
float transStepScale; // for under-sampling ambient transmittance
|
||||||
|
float t;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const float SqrtOneThird = sqrt(1.0 / 3.0);
|
||||||
|
#if PREVIEW_MODE
|
||||||
|
static const float3 Dirs[4] =
|
||||||
|
{
|
||||||
|
float3(-SqrtOneThird, -SqrtOneThird, SqrtOneThird),
|
||||||
|
float3(SqrtOneThird, SqrtOneThird, SqrtOneThird),
|
||||||
|
float3(-SqrtOneThird, SqrtOneThird, SqrtOneThird),
|
||||||
|
float3(SqrtOneThird, -SqrtOneThird, SqrtOneThird)
|
||||||
|
};
|
||||||
|
#else
|
||||||
|
static const float3 Dirs[6 + 8] =
|
||||||
|
{
|
||||||
|
float3(1, 0, 0),
|
||||||
|
float3(-1, 0, 0),
|
||||||
|
float3(0, 1, 0),
|
||||||
|
float3(0, -1, 0),
|
||||||
|
float3(0, 0, 1),
|
||||||
|
float3(0, 0, -1),
|
||||||
|
float3(-SqrtOneThird, -SqrtOneThird, -SqrtOneThird),
|
||||||
|
float3(-SqrtOneThird, -SqrtOneThird, SqrtOneThird),
|
||||||
|
float3(-SqrtOneThird, SqrtOneThird, -SqrtOneThird),
|
||||||
|
float3(-SqrtOneThird, SqrtOneThird, SqrtOneThird),
|
||||||
|
float3(SqrtOneThird, -SqrtOneThird, -SqrtOneThird),
|
||||||
|
float3(SqrtOneThird, -SqrtOneThird, SqrtOneThird),
|
||||||
|
float3(SqrtOneThird, SqrtOneThird, -SqrtOneThird),
|
||||||
|
float3(SqrtOneThird, SqrtOneThird, SqrtOneThird)
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
float3 BlackbodyColor(float temperatureK, Texture2D blackbodyTexture, SamplerState blackbodySampler)
|
||||||
|
{
|
||||||
|
const float minT = 800;
|
||||||
|
const float maxT = 12000;
|
||||||
|
float t = saturate((temperatureK - minT) / (maxT - minT));
|
||||||
|
float3 emission = blackbodyTexture.SampleLevel(blackbodySampler, float2(t, 0.5), 0.0).rgb;
|
||||||
|
|
||||||
|
return emission;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stefan-Boltzmann law
|
||||||
|
float BlackbodyRadiation(float temperatureK)
|
||||||
|
{
|
||||||
|
const float sigma = 5.670373e-8;
|
||||||
|
float T = temperatureK;
|
||||||
|
float T2 = T * T;
|
||||||
|
float T4 = T2 * T2;
|
||||||
|
float radiation = (T4 * sigma) / PI;
|
||||||
|
|
||||||
|
return radiation;
|
||||||
|
}
|
||||||
|
|
||||||
|
float3 BlackbodyEmission(float temperatureK, Texture2D blackbodyTexture, SamplerState blackbodySampler)
|
||||||
|
{
|
||||||
|
const float scale = 1.0e-6;
|
||||||
|
float3 color = BlackbodyColor(temperatureK, blackbodyTexture, blackbodySampler);
|
||||||
|
float radiation = BlackbodyRadiation(temperatureK);
|
||||||
|
float colorBrightness = Brightness(color);
|
||||||
|
float3 result = (color * radiation * scale) / colorBrightness;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SampleRequest
|
||||||
|
{
|
||||||
|
pnanovdb_buf_t buffer;
|
||||||
|
uint gridByteOffset;
|
||||||
|
float3 froxelPosition;
|
||||||
|
int3 froxelIndex;
|
||||||
|
float3 textureSize;
|
||||||
|
float3 froxelSize;
|
||||||
|
Transform transform;
|
||||||
|
SceneView scene;
|
||||||
|
bool ambientLight;
|
||||||
|
int3 froxelId;
|
||||||
|
LightGrid lightGrid;
|
||||||
|
};
|
||||||
|
|
||||||
|
#if PREVIEW_MODE
|
||||||
|
SampleResult SampleFroxel(SampleRequest request)
|
||||||
|
{
|
||||||
|
SampleResult result = CreateSampleResult();
|
||||||
|
if(request.gridByteOffset == 0)
|
||||||
|
{
|
||||||
|
// no grid requested
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Grid grid = GetGrid(request.buffer, request.gridByteOffset);
|
||||||
|
float3 indexF = grid.WorldToIndex(request.froxelPosition, request.transform);
|
||||||
|
bool overlaps = IsInRange(indexF, grid.bboxMin, grid.bboxMax);
|
||||||
|
if(!overlaps)
|
||||||
|
{
|
||||||
|
// no grid/froxel intersection
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = grid.GetFroxelAverage(request.scene, request.froxelPosition, request.froxelSize, request.transform);
|
||||||
|
if(!request.ambientLight)
|
||||||
|
{
|
||||||
|
// no ambient light requested
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float inScatterScale = 2.0;
|
||||||
|
const float stepScale = 8.0;
|
||||||
|
const uint dirCount = 4;
|
||||||
|
float jitterScale = 0.5 * Hash3To1(request.froxelId);
|
||||||
|
float extScale = densityExtinctionScale;
|
||||||
|
float trans = 0.0;
|
||||||
|
[unroll]
|
||||||
|
for(uint i = 0; i < dirCount; i++)
|
||||||
|
{
|
||||||
|
float3 dir = Dirs[i];
|
||||||
|
float3 step = dir * stepScale;
|
||||||
|
trans += grid.RaymarchTransmittance(request.froxelPosition + step * jitterScale, step, request.transform, extScale);
|
||||||
|
}
|
||||||
|
result.inScatteredLight = request.scene.ambientColor * (inScatterScale * trans * request.scene.ambientIntensity);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
SampleResult SampleFroxel(SampleRequest request)
|
||||||
|
{
|
||||||
|
SampleResult result = CreateSampleResult();
|
||||||
|
if(request.gridByteOffset == 0)
|
||||||
|
{
|
||||||
|
// no grid requested
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Grid grid = GetGrid(request.buffer, request.gridByteOffset);
|
||||||
|
bool overlaps;
|
||||||
|
if(accurateOverlapTest != 0u)
|
||||||
|
{
|
||||||
|
overlaps = grid.OverlapsFroxel(request.scene, request.froxelIndex, request.textureSize, request.transform);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
float3 indexF = grid.WorldToIndex(request.froxelPosition, request.transform);
|
||||||
|
overlaps = IsInRange(indexF, grid.bboxMin, grid.bboxMax);
|
||||||
|
}
|
||||||
|
if(!overlaps)
|
||||||
|
{
|
||||||
|
// no grid/froxel intersection
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = grid.GetFroxelAverage(request.scene, request.froxelPosition, request.froxelSize, request.transform);
|
||||||
|
if(!request.ambientLight)
|
||||||
|
{
|
||||||
|
// no ambient light requested
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
float3x3 rotation = RandomRotationMatrix3x3(float3(request.froxelId));
|
||||||
|
float jitterScale = 0.5 * Hash3To1(request.froxelId);
|
||||||
|
float stepScale = transStepScale;
|
||||||
|
uint dirCount = ambientAngularCoverage != 0 ? 14 : 6;
|
||||||
|
float localWeight = ambientAngularCoverage != 0 ? 1.75 : 0.75;
|
||||||
|
if(lightGridRC.isAvailable)
|
||||||
|
{
|
||||||
|
LightGridSample ambient = request.lightGrid.SampleAtPosition(request.froxelPosition);
|
||||||
|
float extScale = densityExtinctionScale;
|
||||||
|
float3 lightDir = ambient.GetLightDirection();
|
||||||
|
float3 dirTransStep = lightDir * stepScale;
|
||||||
|
float dirTrans = grid.RaymarchTransmittance(request.froxelPosition + dirTransStep * jitterScale, dirTransStep, request.transform, extScale);
|
||||||
|
float3 globalColor = float3(0, 0, 0);
|
||||||
|
for(uint i = 0; i < dirCount; i++)
|
||||||
|
{
|
||||||
|
float3 dir = mul(rotation, Dirs[i]);
|
||||||
|
float scale = stepScale;
|
||||||
|
float3 step = dir * stepScale;
|
||||||
|
LightGridSample sample = request.lightGrid.SampleAtPosition(request.froxelPosition + dir * scale * 0.5);
|
||||||
|
float trans = grid.RaymarchTransmittance(request.froxelPosition + step * jitterScale, step, request.transform, extScale);
|
||||||
|
float3 color = ColorAtBrightness(sample.GetGlobalColor(), 0.5);
|
||||||
|
globalColor += color * trans;
|
||||||
|
}
|
||||||
|
float3 cameraDir = normalize(request.froxelPosition - request.scene.cameraPosition);
|
||||||
|
float localScale = dot(-cameraDir, lightDir) * 0.5 + 0.5; // wraps around
|
||||||
|
float3 localColor = ColorAtBrightness(ambient.GetLocalColor(), 0.5) * localScale * dirTrans;
|
||||||
|
float3 color = (globalColor + localColor * localWeight) / (float(dirCount) + localWeight);
|
||||||
|
result.inScatteredLight = color * request.scene.ambientIntensity;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
float extScale = densityExtinctionScale;
|
||||||
|
float trans = 0.0;
|
||||||
|
for(uint i = 0; i < dirCount; i++)
|
||||||
|
{
|
||||||
|
float3 dir = Dirs[i];
|
||||||
|
float3 step = dir * stepScale;
|
||||||
|
trans += grid.RaymarchTransmittance(request.froxelPosition + step * jitterScale, step, request.transform, extScale);
|
||||||
|
}
|
||||||
|
trans /= float(dirCount);
|
||||||
|
result.inScatteredLight = request.scene.ambientColor * (trans * request.scene.ambientIntensity);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
[numthreads(4, 4, 4)]
|
||||||
|
void cs(uint3 id : SV_DispatchThreadID)
|
||||||
|
{
|
||||||
|
RWTexture3D<float4> materialTextureA = ResourceDescriptorHeap[materialTextureAIndex];
|
||||||
|
uint3 textureSize = GetTextureSize(materialTextureA);
|
||||||
|
if(any(id >= textureSize))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RWTexture2D<uint> frustumVisTexture = ResourceDescriptorHeap[frustumVisTextureIndex];
|
||||||
|
uint furthestVisibleFroxelZIndex = frustumVisTexture[id.xy];
|
||||||
|
if(id.z > furthestVisibleFroxelZIndex)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SceneView scene = GetSceneView();
|
||||||
|
LightGrid lightGrid;
|
||||||
|
if(lightGridRC.isAvailable)
|
||||||
|
{
|
||||||
|
lightGrid = GetLightGrid(lightGridRC);
|
||||||
|
}
|
||||||
|
pnanovdb_buf_t nanovdbBuffer = ResourceDescriptorHeap[nanovdbBufferIndex];
|
||||||
|
Texture2D blackbodyTexture = ResourceDescriptorHeap[blackbodyTextureIndex];
|
||||||
|
SamplerState blackbodySampler = SamplerDescriptorHeap[scene.linearClampSamplerIndex];
|
||||||
|
RWTexture3D<float4> materialTextureB = ResourceDescriptorHeap[materialTextureBIndex];
|
||||||
|
RWTexture3D<float> materialTextureC = ResourceDescriptorHeap[materialTextureCIndex];
|
||||||
|
RWTexture3D<float4> scatterExtTexture = ResourceDescriptorHeap[scatterExtTextureIndex];
|
||||||
|
|
||||||
|
float3 textureSizeF = float3(textureSize);
|
||||||
|
float3 froxelPosition = scene.FroxelIndexToWorldSpace(int3(id), textureSizeF);
|
||||||
|
float3 froxelSize = scene.FroxelMaxDimensions(id, textureSizeF);
|
||||||
|
Transform transform = DecodeTransform(packedTransform, packedTransform2);
|
||||||
|
transform.stepSize *= stepScale;
|
||||||
|
|
||||||
|
#if !PREVIEW_MODE
|
||||||
|
if(linearInterpolation != 0u)
|
||||||
|
{
|
||||||
|
SampleRequest r;
|
||||||
|
r.buffer = nanovdbBuffer;
|
||||||
|
r.froxelIndex = id;
|
||||||
|
r.froxelPosition = froxelPosition;
|
||||||
|
r.froxelSize = froxelSize;
|
||||||
|
r.gridByteOffset = densityGridByteOffset;
|
||||||
|
r.scene = scene;
|
||||||
|
r.textureSize = textureSizeF;
|
||||||
|
r.transform = transform;
|
||||||
|
r.lightGrid = lightGrid;
|
||||||
|
r.froxelId = id;
|
||||||
|
r.ambientLight = true;
|
||||||
|
SampleResult extResult1 = SampleFroxel(r);
|
||||||
|
r.gridByteOffset = densityGridByteOffset2;
|
||||||
|
SampleResult extResult2 = SampleFroxel(r);
|
||||||
|
|
||||||
|
r.ambientLight = false;
|
||||||
|
r.gridByteOffset = flamesGridByteOffset;
|
||||||
|
SampleResult emResult1 = SampleFroxel(r);
|
||||||
|
r.gridByteOffset = flamesGridByteOffset2;
|
||||||
|
SampleResult emResult2 = SampleFroxel(r);
|
||||||
|
|
||||||
|
if(extResult1.sum > 0.0 || extResult2.sum > 0.0)
|
||||||
|
{
|
||||||
|
float extinction1 = (extResult1.sum / float(extResult1.maxSampleCount));
|
||||||
|
float extinction2 = (extResult2.sum / float(extResult2.maxSampleCount));
|
||||||
|
float extinction = lerp(extinction1, extinction2, t) * densityExtinctionScale;
|
||||||
|
float scatter = extinction * densityAlbedo;
|
||||||
|
float absorption = extinction - scatter;
|
||||||
|
float coverage1 = float(extResult1.sampleCount) / float(extResult1.maxSampleCount);
|
||||||
|
float coverage2 = float(extResult2.sampleCount) / float(extResult2.maxSampleCount);
|
||||||
|
float coverage = lerp(coverage1, coverage2, t);
|
||||||
|
float3 inScattered = lerp(extResult1.inScatteredLight, extResult2.inScatteredLight, t);
|
||||||
|
materialTextureA[id] += float4(scatter.xxx, absorption);
|
||||||
|
materialTextureC[id] += coverage;
|
||||||
|
scatterExtTexture[id] += float4(scatter * inScattered, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(emResult1.sum > 0.0 || emResult2.sum > 0.0)
|
||||||
|
{
|
||||||
|
float Tnorm1 = emResult1.sum / float(emResult1.maxSampleCount);
|
||||||
|
float Tnorm2 = emResult2.sum / float(emResult2.maxSampleCount);
|
||||||
|
float T = lerp(Tnorm1, Tnorm2, t) * flamesTemperatureScale;
|
||||||
|
float3 emission = BlackbodyEmission(T, blackbodyTexture, blackbodySampler) * flamesEmissionScale;
|
||||||
|
materialTextureB[id] += float4(emission, 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
SampleRequest r;
|
||||||
|
r.buffer = nanovdbBuffer;
|
||||||
|
r.froxelIndex = id;
|
||||||
|
r.froxelPosition = froxelPosition;
|
||||||
|
r.froxelSize = froxelSize;
|
||||||
|
r.gridByteOffset = densityGridByteOffset;
|
||||||
|
r.scene = scene;
|
||||||
|
r.textureSize = textureSizeF;
|
||||||
|
r.transform = transform;
|
||||||
|
r.lightGrid = lightGrid;
|
||||||
|
r.froxelId = id;
|
||||||
|
r.ambientLight = true;
|
||||||
|
SampleResult extResult = SampleFroxel(r);
|
||||||
|
|
||||||
|
r.gridByteOffset = flamesGridByteOffset;
|
||||||
|
r.ambientLight = false;
|
||||||
|
SampleResult emResult = SampleFroxel(r);
|
||||||
|
|
||||||
|
if(extResult.sum > 0.0)
|
||||||
|
{
|
||||||
|
float extinction = (extResult.sum / float(extResult.maxSampleCount)) * densityExtinctionScale;
|
||||||
|
float scatter = extinction * densityAlbedo;
|
||||||
|
float absorption = extinction - scatter;
|
||||||
|
float coverage = float(extResult.sampleCount) / float(extResult.maxSampleCount);
|
||||||
|
materialTextureA[id] += float4(scatter.xxx, absorption);
|
||||||
|
materialTextureC[id] += coverage;
|
||||||
|
scatterExtTexture[id] += float4(scatter * extResult.inScatteredLight, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(emResult.sum > 0.0)
|
||||||
|
{
|
||||||
|
float Tnorm = emResult.sum / float(emResult.maxSampleCount);
|
||||||
|
float T = Tnorm * flamesTemperatureScale;
|
||||||
|
float3 emission = BlackbodyEmission(T, blackbodyTexture, blackbodySampler) * flamesEmissionScale;
|
||||||
|
materialTextureB[id] += float4(emission, 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,60 +18,129 @@ 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/>.
|
along with Challenge Quake 3. If not, see <https://www.gnu.org/licenses/>.
|
||||||
===========================================================================
|
===========================================================================
|
||||||
*/
|
*/
|
||||||
// volumetric lighting: inject particles into the material textures
|
// volumetric lighting: inject particles into the frustum material textures
|
||||||
|
|
||||||
|
|
||||||
|
// 0 -> particle is a point
|
||||||
|
// 1 -> particle is a sphere, no super-sampling
|
||||||
|
// 2 -> particle is a sphere, 2x super-sampling
|
||||||
|
#define QUALITY 1
|
||||||
|
|
||||||
|
|
||||||
#include "common.hlsli"
|
#include "common.hlsli"
|
||||||
#include "scene_view.h.hlsli"
|
#include "scene_view.h.hlsli"
|
||||||
|
#if QUALITY >= 2
|
||||||
#define VOXEL_SUPERSAMPLING_2X
|
#define VOXEL_SUPERSAMPLING_2X
|
||||||
|
#define SPHERE_SUPERSAMPLING_2X
|
||||||
|
#else
|
||||||
|
#define VOXEL_SUPERSAMPLING_1X
|
||||||
|
#define SPHERE_SUPERSAMPLING_1X
|
||||||
|
#endif
|
||||||
#include "vl_common.h.hlsli"
|
#include "vl_common.h.hlsli"
|
||||||
|
|
||||||
|
|
||||||
cbuffer RootConstants
|
cbuffer RootConstants
|
||||||
{
|
{
|
||||||
uint3 tileScale;
|
uint3 tileScale;
|
||||||
|
uint pad0;
|
||||||
|
uint3 tileResolution;
|
||||||
uint particleBufferIndex;
|
uint particleBufferIndex;
|
||||||
uint particleCount;
|
|
||||||
uint materialTextureAIndex;
|
uint materialTextureAIndex;
|
||||||
uint materialTextureBIndex;
|
uint materialTextureBIndex;
|
||||||
uint materialTextureCIndex;
|
uint materialTextureCIndex;
|
||||||
uint tileBufferIndex;
|
uint tileBufferIndex;
|
||||||
|
uint tileIndexBufferIndex;
|
||||||
|
uint particleIndexBufferIndex;
|
||||||
|
uint counterBufferIndex;
|
||||||
uint tileCount;
|
uint tileCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
[numthreads(1024, 1, 1)]
|
#define VOXEL_COUNT 512
|
||||||
void cs(uint3 dtid : SV_DispatchThreadID, uint gidx : SV_GroupIndex)
|
#define THREAD_COUNT 512
|
||||||
{
|
groupshared uint s_scatterR[VOXEL_COUNT];
|
||||||
uint tileIndex = dtid.x / 1024;
|
groupshared uint s_scatterG[VOXEL_COUNT];
|
||||||
if(tileIndex >= tileCount)
|
groupshared uint s_scatterB[VOXEL_COUNT];
|
||||||
{
|
groupshared uint s_absorption[VOXEL_COUNT];
|
||||||
return;
|
groupshared uint s_emissiveR[VOXEL_COUNT];
|
||||||
}
|
groupshared uint s_emissiveG[VOXEL_COUNT];
|
||||||
|
groupshared uint s_emissiveB[VOXEL_COUNT];
|
||||||
|
groupshared uint s_anisotropy[VOXEL_COUNT];
|
||||||
|
groupshared uint s_coverage[VOXEL_COUNT];
|
||||||
|
static const float g_materialScale = 131072.0;
|
||||||
|
static const float g_anisotropyScale = 1024.0;
|
||||||
|
static const float g_coverageScale = 1024.0;
|
||||||
|
|
||||||
RWStructuredBuffer<uint3> tileBuffer = ResourceDescriptorHeap[tileBufferIndex];
|
float FroxelMinSize(SceneView scene, uint3 id, float3 textureSize)
|
||||||
|
{
|
||||||
|
float3 center = scene.FroxelIndexToWorldSpace(id, textureSize);
|
||||||
|
float w = distance(center, scene.FroxelIndexToWorldSpace(id + uint3(1, 0, 0), textureSize));
|
||||||
|
float h = distance(center, scene.FroxelIndexToWorldSpace(id + uint3(0, 1, 0), textureSize));
|
||||||
|
float d = distance(center, scene.FroxelIndexToWorldSpace(id + uint3(0, 0, 1), textureSize));
|
||||||
|
float size = min3(w, h, d);
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
[numthreads(THREAD_COUNT, 1, 1)]
|
||||||
|
void cs(uint3 dtid : SV_DispatchThreadID, uint gtidx : SV_GroupIndex)
|
||||||
|
{
|
||||||
|
uint tileIndexIndex = dtid.x / THREAD_COUNT;
|
||||||
|
#if 0
|
||||||
|
RWStructuredBuffer<Counters> counterBuffer = ResourceDescriptorHeap[counterBufferIndex];
|
||||||
|
Counters counters = counterBuffer[0];
|
||||||
|
//if(tileIndexIndex >= tileCount)
|
||||||
|
if(tileIndexIndex >= counters.tileCount)
|
||||||
|
{
|
||||||
|
return; // should never happen
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
RWStructuredBuffer<uint> tileIndexBuffer = ResourceDescriptorHeap[tileIndexBufferIndex];
|
||||||
RWTexture3D<float4> materialTextureA = ResourceDescriptorHeap[materialTextureAIndex];
|
RWTexture3D<float4> materialTextureA = ResourceDescriptorHeap[materialTextureAIndex];
|
||||||
uint3 textureSize = GetTextureSize(materialTextureA);
|
uint3 textureSize = GetTextureSize(materialTextureA);
|
||||||
uint3 tileCornerIndex = tileBuffer[tileIndex];
|
uint tileIndex = tileIndexBuffer[tileIndexIndex];
|
||||||
uint3 tileThreadIndex = UnflattenIndex(gidx, tileScale);
|
uint3 tileCornerIndex = UnflattenIndex(tileIndex, tileResolution);
|
||||||
|
uint3 tileThreadIndex = UnflattenIndex(gtidx, tileScale);
|
||||||
uint3 id = tileCornerIndex * tileScale + tileThreadIndex;
|
uint3 id = tileCornerIndex * tileScale + tileThreadIndex;
|
||||||
if(any(id >= textureSize))
|
int3 froxelIndexMin = int3(tileCornerIndex * tileScale);
|
||||||
|
int3 froxelIndexMax = int3(tileCornerIndex * tileScale) + int3(tileScale) - int3(1, 1, 1);
|
||||||
|
uint smIndex = FlattenIndex(id - uint3(froxelIndexMin), tileScale);
|
||||||
|
if(smIndex < VOXEL_COUNT)
|
||||||
{
|
{
|
||||||
return;
|
s_scatterR[smIndex] = 0;
|
||||||
|
s_scatterG[smIndex] = 0;
|
||||||
|
s_scatterB[smIndex] = 0;
|
||||||
|
s_absorption[smIndex] = 0;
|
||||||
|
s_emissiveR[smIndex] = 0;
|
||||||
|
s_emissiveG[smIndex] = 0;
|
||||||
|
s_emissiveB[smIndex] = 0;
|
||||||
|
s_anisotropy[smIndex] = 0;
|
||||||
|
s_coverage[smIndex] = 0;
|
||||||
}
|
}
|
||||||
|
GroupMemoryBarrierWithGroupSync();
|
||||||
|
|
||||||
RWTexture3D<float4> materialTextureB = ResourceDescriptorHeap[materialTextureBIndex];
|
RWTexture3D<float4> materialTextureB = ResourceDescriptorHeap[materialTextureBIndex];
|
||||||
RWTexture3D<float> materialTextureC = ResourceDescriptorHeap[materialTextureCIndex];
|
RWTexture3D<float> materialTextureC = ResourceDescriptorHeap[materialTextureCIndex];
|
||||||
StructuredBuffer<Particle> particleBuffer = ResourceDescriptorHeap[particleBufferIndex];
|
RWStructuredBuffer<Particle> particleBuffer = ResourceDescriptorHeap[particleBufferIndex];
|
||||||
|
RWStructuredBuffer<Tile> tileBuffer = ResourceDescriptorHeap[tileBufferIndex];
|
||||||
|
RWStructuredBuffer<uint> particleIndexBuffer = ResourceDescriptorHeap[particleIndexBufferIndex];
|
||||||
SceneView scene = GetSceneView();
|
SceneView scene = GetSceneView();
|
||||||
|
|
||||||
|
Tile tile = tileBuffer[tileIndex];
|
||||||
float3 textureSizeF = float3(textureSize);
|
float3 textureSizeF = float3(textureSize);
|
||||||
float3 tcBase = (float3(id) + float3(0.5, 0.5, 0.5)) / textureSizeF;
|
#if QUALITY > 0
|
||||||
float4 accumScatterAbs = float4(0, 0, 0, 0);
|
float3 left = scene.cameraLeft;
|
||||||
float4 accumEmissiveAniso = float4(0, 0, 0, 0);
|
float3 up = scene.cameraUp;
|
||||||
float accumCoverage = 0.0;
|
float3 fwd = scene.cameraForward;
|
||||||
for(uint i = 0; i < particleCount; i++)
|
float froxelMinSize = FroxelMinSize(scene, (tileCornerIndex * tileScale) + (tileScale / 2) - uint3(1, 1, 1), textureSizeF);
|
||||||
|
#endif
|
||||||
|
uint particleCount = tile.particleCount;
|
||||||
|
uint firstParticle = tile.firstParticle;
|
||||||
|
for(uint i = smIndex; i < particleCount; i += THREAD_COUNT)
|
||||||
{
|
{
|
||||||
Particle particle = particleBuffer[i];
|
uint particleIndex = particleIndexBuffer[firstParticle + i];
|
||||||
|
Particle particle = particleBuffer[particleIndex];
|
||||||
|
|
||||||
float3 scattering;
|
float3 scattering;
|
||||||
float3 emissive;
|
float3 emissive;
|
||||||
[flatten]
|
[flatten]
|
||||||
|
@ -86,29 +155,124 @@ void cs(uint3 dtid : SV_DispatchThreadID, uint gidx : SV_GroupIndex)
|
||||||
emissive = float3(0, 0, 0);
|
emissive = float3(0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
float particleCoverage = 0.0;
|
#if QUALITY > 0
|
||||||
for(uint s = 0; s < VoxelSampleCount; s++)
|
|
||||||
|
bool isBigParticle = particle.radius >= froxelMinSize;
|
||||||
|
bool isMediumParticle = particle.radius >= 0.125 * froxelMinSize;
|
||||||
|
int3 boxMin = particle.froxelMin - froxelIndexMin;
|
||||||
|
int3 boxMax = particle.froxelMax - froxelIndexMin;
|
||||||
|
boxMin = max(boxMin, int3(0, 0, 0));
|
||||||
|
boxMax = min(boxMax, int3(tileScale) - int3(1, 1, 1));
|
||||||
|
|
||||||
|
if(all(boxMax < boxMin))
|
||||||
{
|
{
|
||||||
float3 tcSample = tcBase + VoxelSamples[s] / textureSizeF;
|
continue;
|
||||||
float3 position = scene.FroxelTCToWorldSpace(tcSample, textureSizeF);
|
|
||||||
float dist = distance(position, particle.position);
|
|
||||||
if(dist >= particle.radius)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
float coverage = sqrt(saturate(1.0 - dist / particle.radius));
|
|
||||||
particleCoverage += coverage;
|
|
||||||
}
|
}
|
||||||
particleCoverage /= float(VoxelSampleCount);
|
|
||||||
|
|
||||||
accumScatterAbs += particleCoverage * float4(scattering, particle.absorption);
|
for(int z = boxMin.z; z <= boxMax.z; z++)
|
||||||
accumEmissiveAniso += particleCoverage * float4(emissive, particle.anisotropy);
|
{
|
||||||
accumCoverage += particleCoverage;
|
for(int y = boxMin.y; y <= boxMax.y; y++)
|
||||||
|
{
|
||||||
|
for(int x = boxMin.x; x <= boxMax.x; x++)
|
||||||
|
{
|
||||||
|
uint3 froxelGroupThreadId = uint3(x, y, z);
|
||||||
|
uint froxelFlatIndex = FlattenIndex(froxelGroupThreadId, tileScale);
|
||||||
|
uint3 froxelThreadId = tileCornerIndex * tileScale + froxelGroupThreadId;
|
||||||
|
float particleCoverage = 0.0;
|
||||||
|
|
||||||
|
if(isBigParticle)
|
||||||
|
{
|
||||||
|
float3 tcBase = (float3(froxelThreadId) + float3(0.5, 0.5, 0.5)) / textureSizeF;
|
||||||
|
for(uint s = 0; s < VoxelSampleCount; s++)
|
||||||
|
{
|
||||||
|
float3 tcSample = tcBase + VoxelSamples[s] / textureSizeF;
|
||||||
|
float3 position = scene.FroxelTCToWorldSpace(tcSample, textureSizeF);
|
||||||
|
float dist = distance(position, particle.position);
|
||||||
|
float coverage = sqrt(saturate(1.0 - dist / particle.radius));
|
||||||
|
coverage *= 0.25 + 0.75 * SimplexNoise3D(0.25 * (position - particle.position));
|
||||||
|
particleCoverage += coverage;
|
||||||
|
}
|
||||||
|
particleCoverage /= float(VoxelSampleCount);
|
||||||
|
}
|
||||||
|
else if(isMediumParticle)
|
||||||
|
{
|
||||||
|
float3 basePosition = scene.FroxelIndexToWorldSpace(froxelThreadId, textureSizeF);
|
||||||
|
for(uint s = 0; s < SphereSampleCount; s++)
|
||||||
|
{
|
||||||
|
float3 position = basePosition + particle.radius * SphereSamples[s];
|
||||||
|
int3 sampleVoxelIdx = scene.FroxelWorldSpaceToIndex(position, textureSizeF);
|
||||||
|
bool isInVoxel = all(froxelThreadId == uint3(sampleVoxelIdx));
|
||||||
|
float dist = isInVoxel ? distance(position, particle.position) : 0.0;
|
||||||
|
float coverage = sqrt(saturate(1.0 - dist / particle.radius));
|
||||||
|
particleCoverage += coverage;
|
||||||
|
}
|
||||||
|
particleCoverage /= float(SphereSampleCount);
|
||||||
|
particleCoverage *= min(SphereVolume(particle.radius) / scene.FroxelVolume(froxelThreadId, textureSizeF), 1.0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// assumes the sphere's density is not 1 but 1/distance
|
||||||
|
float density = 2.0 * PI * particle.radius * particle.radius;
|
||||||
|
particleCoverage = min(density / scene.FroxelVolume(froxelThreadId, textureSizeF), 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(particleCoverage == 0.0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint4 scatterAbs = g_materialScale * particleCoverage * float4(scattering, particle.absorption);
|
||||||
|
uint4 emissiveAniso = float4(g_materialScale.xxx, g_anisotropyScale) * particleCoverage * float4(emissive, particle.anisotropy);
|
||||||
|
uint coverage = g_coverageScale * particleCoverage;
|
||||||
|
InterlockedAdd(s_scatterR[froxelFlatIndex], scatterAbs.r);
|
||||||
|
InterlockedAdd(s_scatterG[froxelFlatIndex], scatterAbs.g);
|
||||||
|
InterlockedAdd(s_scatterB[froxelFlatIndex], scatterAbs.b);
|
||||||
|
InterlockedAdd(s_absorption[froxelFlatIndex], scatterAbs.w);
|
||||||
|
InterlockedAdd(s_emissiveR[froxelFlatIndex], emissiveAniso.r);
|
||||||
|
InterlockedAdd(s_emissiveG[froxelFlatIndex], emissiveAniso.g);
|
||||||
|
InterlockedAdd(s_emissiveB[froxelFlatIndex], emissiveAniso.b);
|
||||||
|
InterlockedAdd(s_anisotropy[froxelFlatIndex], emissiveAniso.w);
|
||||||
|
InterlockedAdd(s_coverage[froxelFlatIndex], coverage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
int3 froxelIndex = scene.FroxelWorldSpaceToIndex(particle.position, textureSizeF);
|
||||||
|
if(!IsInRange(froxelIndex, froxelIndexMin, froxelIndexMax))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
uint froxelFlatIndex = FlattenIndex(uint3(froxelIndex) - uint3(froxelIndexMin), tileScale);
|
||||||
|
float particleCoverage = 1.0;
|
||||||
|
|
||||||
|
uint4 scatterAbs = g_materialScale * particleCoverage * float4(scattering, particle.absorption);
|
||||||
|
uint4 emissiveAniso = float4(g_materialScale.xxx, g_anisotropyScale) * particleCoverage * float4(emissive, particle.anisotropy);
|
||||||
|
uint coverage = g_coverageScale * particleCoverage;
|
||||||
|
InterlockedAdd(s_scatterR[froxelFlatIndex], scatterAbs.r);
|
||||||
|
InterlockedAdd(s_scatterG[froxelFlatIndex], scatterAbs.g);
|
||||||
|
InterlockedAdd(s_scatterB[froxelFlatIndex], scatterAbs.b);
|
||||||
|
InterlockedAdd(s_absorption[froxelFlatIndex], scatterAbs.w);
|
||||||
|
InterlockedAdd(s_emissiveR[froxelFlatIndex], emissiveAniso.r);
|
||||||
|
InterlockedAdd(s_emissiveG[froxelFlatIndex], emissiveAniso.g);
|
||||||
|
InterlockedAdd(s_emissiveB[froxelFlatIndex], emissiveAniso.b);
|
||||||
|
InterlockedAdd(s_anisotropy[froxelFlatIndex], emissiveAniso.w);
|
||||||
|
InterlockedAdd(s_coverage[froxelFlatIndex], coverage);
|
||||||
|
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if(accumCoverage > 0.0)
|
GroupMemoryBarrierWithGroupSync();
|
||||||
|
|
||||||
|
if(smIndex < VOXEL_COUNT &&
|
||||||
|
s_coverage[smIndex] > 0 &&
|
||||||
|
all(id < textureSize))
|
||||||
{
|
{
|
||||||
|
float4 accumScatterAbs = float4(s_scatterR[smIndex], s_scatterG[smIndex], s_scatterB[smIndex], s_absorption[smIndex]) / g_materialScale;
|
||||||
|
float4 accumEmissiveAniso = float4(s_emissiveR[smIndex], s_emissiveG[smIndex], s_emissiveB[smIndex], s_anisotropy[smIndex]) / float4(g_materialScale.xxx, g_anisotropyScale);
|
||||||
|
float accumCoverage = s_coverage[smIndex] / g_coverageScale;
|
||||||
|
|
||||||
materialTextureA[id] += accumScatterAbs;
|
materialTextureA[id] += accumScatterAbs;
|
||||||
materialTextureB[id] += accumEmissiveAniso;
|
materialTextureB[id] += accumEmissiveAniso;
|
||||||
materialTextureC[id] += accumCoverage;
|
materialTextureC[id] += accumCoverage;
|
||||||
|
|
|
@ -23,18 +23,14 @@ along with Challenge Quake 3. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
#include "common.hlsli"
|
#include "common.hlsli"
|
||||||
#include "scene_view.h.hlsli"
|
#include "scene_view.h.hlsli"
|
||||||
|
#include "light_grid.h.hlsli"
|
||||||
|
|
||||||
|
|
||||||
cbuffer RootConstants
|
cbuffer RootConstants
|
||||||
{
|
{
|
||||||
float3 centerPosition;
|
LightGridRC lightGridRC;
|
||||||
uint materialTextureAIndex;
|
uint materialTextureAIndex;
|
||||||
float3 worldScale;
|
|
||||||
uint scatterExtTextureIndex;
|
uint scatterExtTextureIndex;
|
||||||
uint ambientLightTextureAIndex;
|
|
||||||
uint ambientLightTextureBIndex;
|
|
||||||
uint ambientSamplerIndex;
|
|
||||||
uint isLightGridAvailable;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[numthreads(4, 4, 4)]
|
[numthreads(4, 4, 4)]
|
||||||
|
@ -47,40 +43,28 @@ void cs(uint3 id : SV_DispatchThreadID)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(isLightGridAvailable != 0)
|
SceneView scene = GetSceneView();
|
||||||
{
|
RWTexture3D<float4> materialTextureA = ResourceDescriptorHeap[materialTextureAIndex];
|
||||||
SceneView scene = GetSceneView();
|
ExtinctionCascade cascade = scene.GetExtinctionCascade(scene.extinctionWorldScale.y);
|
||||||
RWTexture3D<float4> materialTextureA = ResourceDescriptorHeap[materialTextureAIndex];
|
float3 positionWS = scene.FroxelIndexToWorldSpace(id, textureSize);
|
||||||
Texture3D ambientLightTextureA = ResourceDescriptorHeap[ambientLightTextureAIndex];
|
float3 normalWS = normalize(scene.cameraPosition - positionWS);
|
||||||
Texture3D ambientLightTextureB = ResourceDescriptorHeap[ambientLightTextureBIndex];
|
float4 scatterAbs = materialTextureA[id];
|
||||||
SamplerState ambientSampler = SamplerDescriptorHeap[ambientSamplerIndex];
|
float3 scattering = scatterAbs.rgb;
|
||||||
float3 ambientTextureSize = float3(GetTextureSize(ambientLightTextureA));
|
float extinction = Brightness(scattering) + scatterAbs.a;
|
||||||
|
|
||||||
float3 positionWS = scene.FroxelIndexToWorldSpace(id, textureSize);
|
if(lightGridRC.isAvailable != 0)
|
||||||
float3 normalWS = normalize(scene.cameraPosition - positionWS);
|
{
|
||||||
float4 scatterAbs = materialTextureA[id];
|
LightGrid lightGrid = GetLightGrid(lightGridRC);
|
||||||
float3 scattering = scatterAbs.rgb;
|
LightGridSample ambient = lightGrid.SampleAtPosition(positionWS);
|
||||||
float extinction = Brightness(scattering) + scatterAbs.a;
|
float3 ambientColor = ambient.GetAmbientColor(normalWS, scene.ambientColor, 1, 1);
|
||||||
float3 ambientTC = AABoxWorldSpaceToTC(positionWS, centerPosition, ambientTextureSize, worldScale);
|
|
||||||
float4 ambientA = ambientLightTextureA.SampleLevel(ambientSampler, ambientTC, 0);
|
|
||||||
float4 ambientB = ambientLightTextureB.SampleLevel(ambientSampler, ambientTC, 0);
|
|
||||||
float3 ambientColor = AmbientColor(ambientA, ambientB, normalWS, scene.ambientColor);
|
|
||||||
float3 inScattering = scattering * ambientColor * scene.ambientIntensity;
|
float3 inScattering = scattering * ambientColor * scene.ambientIntensity;
|
||||||
|
|
||||||
scatterExtTexture[id] = float4(inScattering, extinction);
|
scatterExtTexture[id] += float4(inScattering, extinction);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SceneView scene = GetSceneView();
|
|
||||||
RWTexture3D<float4> materialTextureA = ResourceDescriptorHeap[materialTextureAIndex];
|
|
||||||
|
|
||||||
float3 positionWS = scene.FroxelIndexToWorldSpace(id, textureSize);
|
|
||||||
float3 normalWS = normalize(scene.cameraPosition - positionWS);
|
|
||||||
float4 scatterAbs = materialTextureA[id];
|
|
||||||
float3 scattering = scatterAbs.rgb;
|
|
||||||
float extinction = Brightness(scattering) + scatterAbs.a;
|
|
||||||
float3 inScattering = scattering * scene.ambientColor * scene.ambientIntensity;
|
float3 inScattering = scattering * scene.ambientColor * scene.ambientIntensity;
|
||||||
|
|
||||||
scatterExtTexture[id] = float4(inScattering, extinction);
|
scatterExtTexture[id] += float4(inScattering, extinction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,7 +70,7 @@ void cs(uint3 id : SV_DispatchThreadID)
|
||||||
float3 shadowTC = AABoxWorldSpaceToTC(froxelPosition, lightPosition, GetTextureSize(transmittanceTexture), shadowWorldScale);
|
float3 shadowTC = AABoxWorldSpaceToTC(froxelPosition, lightPosition, GetTextureSize(transmittanceTexture), shadowWorldScale);
|
||||||
float trans = transmittanceTexture.SampleLevel(transmittanceSampler, shadowTC, 0);
|
float trans = transmittanceTexture.SampleLevel(transmittanceSampler, shadowTC, 0);
|
||||||
float intensity = saturate(1.0 - dist / radius);
|
float intensity = saturate(1.0 - dist / radius);
|
||||||
float3 lightRaw = light.color * intensity * 50.0; // @TODO:
|
float3 lightRaw = light.color * intensity * scene.pointLightIntensityVL;
|
||||||
float2 froxelTC = (float2(id.xy) + float2(0.5, 0.5)) / float2(textureSize.xy);
|
float2 froxelTC = (float2(id.xy) + float2(0.5, 0.5)) / float2(textureSize.xy);
|
||||||
float2 froxelNDC = TCToNDC(froxelTC);
|
float2 froxelNDC = TCToNDC(froxelTC);
|
||||||
float3 cameraRay = scene.CamerayRay(froxelNDC);
|
float3 cameraRay = scene.CamerayRay(froxelNDC);
|
||||||
|
|
|
@ -34,24 +34,6 @@ cbuffer RootConstants
|
||||||
uint sunlightVisTextureIndex;
|
uint sunlightVisTextureIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
float FroxelSize(uint3 index, float3 textureSize, SceneView scene)
|
|
||||||
{
|
|
||||||
float3 tcBase = (float3(index) + float3(0.5, 0.5, 0.5)) / textureSize;
|
|
||||||
float3 halfTexel = float3(0.5, 0.5, 0.5) / textureSize;
|
|
||||||
float3 posL = scene.FroxelTCToWorldSpace(tcBase + float3(-halfTexel.x, 0, 0), textureSize);
|
|
||||||
float3 posR = scene.FroxelTCToWorldSpace(tcBase + float3( halfTexel.x, 0, 0), textureSize);
|
|
||||||
float w = distance(posL, posR);
|
|
||||||
float3 posU = scene.FroxelTCToWorldSpace(tcBase + float3(0, halfTexel.y, 0), textureSize);
|
|
||||||
float3 posD = scene.FroxelTCToWorldSpace(tcBase + float3(0, -halfTexel.y, 0), textureSize);
|
|
||||||
float h = distance(posU, posD);
|
|
||||||
float3 posF = scene.FroxelTCToWorldSpace(tcBase + float3(0, 0, halfTexel.z), textureSize);
|
|
||||||
float3 posB = scene.FroxelTCToWorldSpace(tcBase + float3(0, 0, -halfTexel.z), textureSize);
|
|
||||||
float d = distance(posF, posB);
|
|
||||||
float size = max3(w, h, d);
|
|
||||||
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
[numthreads(4, 4, 4)]
|
[numthreads(4, 4, 4)]
|
||||||
void cs(uint3 id : SV_DispatchThreadID)
|
void cs(uint3 id : SV_DispatchThreadID)
|
||||||
{
|
{
|
||||||
|
@ -66,7 +48,8 @@ void cs(uint3 id : SV_DispatchThreadID)
|
||||||
RWTexture3D<float4> materialTextureB = ResourceDescriptorHeap[materialTextureBIndex];
|
RWTexture3D<float4> materialTextureB = ResourceDescriptorHeap[materialTextureBIndex];
|
||||||
RWTexture3D<float> sunlightVisTexture = ResourceDescriptorHeap[sunlightVisTextureIndex];
|
RWTexture3D<float> sunlightVisTexture = ResourceDescriptorHeap[sunlightVisTextureIndex];
|
||||||
SceneView scene = GetSceneView();
|
SceneView scene = GetSceneView();
|
||||||
float froxelSize = FroxelSize(id, float3(textureSize), scene);
|
float3 froxelSize3 = scene.FroxelAverageDimensions(id, float3(textureSize));
|
||||||
|
float froxelSize = max3(froxelSize3.x, froxelSize3.y, froxelSize3.z);
|
||||||
SunVShadowCascade cascade = scene.GetSunVShadowCascade(froxelSize);
|
SunVShadowCascade cascade = scene.GetSunVShadowCascade(froxelSize);
|
||||||
|
|
||||||
float3 positionWS = scene.FroxelIndexToWorldSpace(id, textureSize);
|
float3 positionWS = scene.FroxelIndexToWorldSpace(id, textureSize);
|
||||||
|
@ -80,7 +63,7 @@ void cs(uint3 id : SV_DispatchThreadID)
|
||||||
float3 scattering = materialTextureA[id].rgb;
|
float3 scattering = materialTextureA[id].rgb;
|
||||||
float anisotropy = materialTextureB[id].a;
|
float anisotropy = materialTextureB[id].a;
|
||||||
float phase = HenyeyGreenstein(cosTheta, anisotropy);
|
float phase = HenyeyGreenstein(cosTheta, anisotropy);
|
||||||
float3 inScattering = vis * scene.sunColor * scene.sunIntensity * scattering * phase;
|
float3 inScattering = vis * scene.sunColor * scene.sunIntensityVL * scattering * phase;
|
||||||
|
|
||||||
scatterExtTexture[id].rgb += inScattering;
|
scatterExtTexture[id].rgb += inScattering;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,157 @@
|
||||||
|
/*
|
||||||
|
===========================================================================
|
||||||
|
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/>.
|
||||||
|
===========================================================================
|
||||||
|
*/
|
||||||
|
// volumetric lighting: raymarch froxels on the X/Y axis to propagate light from emissive froxels
|
||||||
|
|
||||||
|
|
||||||
|
#include "common.hlsli"
|
||||||
|
#include "scene_view.h.hlsli"
|
||||||
|
|
||||||
|
|
||||||
|
cbuffer RootConstants
|
||||||
|
{
|
||||||
|
uint materialTextureAIndex;
|
||||||
|
uint materialTextureBIndex;
|
||||||
|
float emissiveScatter; // how much of the light emitted sideways is reflected towards the camera
|
||||||
|
}
|
||||||
|
|
||||||
|
[numthreads(8, 8, 1)]
|
||||||
|
void cs(uint3 id : SV_DispatchThreadID)
|
||||||
|
{
|
||||||
|
RWTexture3D<float4> materialTextureA = ResourceDescriptorHeap[materialTextureAIndex];
|
||||||
|
uint3 textureSize = GetTextureSize(materialTextureA);
|
||||||
|
#if DIRECTION_NX || DIRECTION_PX
|
||||||
|
if(any(id.xy >= textureSize.yz))
|
||||||
|
#else
|
||||||
|
if(any(id.xy >= textureSize.xz))
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SceneView scene = GetSceneView();
|
||||||
|
RWTexture3D<float4> materialTextureB = ResourceDescriptorHeap[materialTextureBIndex];
|
||||||
|
|
||||||
|
#if DIRECTION_PX
|
||||||
|
uint3 coords = uint3(0, id.x, id.y);
|
||||||
|
uint3 index0 = coords;
|
||||||
|
float3 tc0 = (float3(index0) + float3(0, 0.5, 0.5)) / textureSize;
|
||||||
|
float3 prevPosition = scene.FroxelTCToWorldSpace(tc0, float3(textureSize));
|
||||||
|
float3 accumEmit = float3(0, 0, 0);
|
||||||
|
for(uint x = 1; x < textureSize.x; x++)
|
||||||
|
{
|
||||||
|
uint3 prevIndex = uint3(x - 1, coords.yz);
|
||||||
|
uint3 currIndex = uint3(x, coords.yz);
|
||||||
|
float3 tc = (float3(currIndex) + float3(0, 0.5, 0.5)) / textureSize;
|
||||||
|
float3 currPosition = scene.FroxelTCToWorldSpace(tc, float3(textureSize));
|
||||||
|
float4 scatterAbs = materialTextureA[prevIndex];
|
||||||
|
float3 emissive = materialTextureB[prevIndex].rgb;
|
||||||
|
float scatter = Brightness(scatterAbs.xyz);
|
||||||
|
float extinction = scatter + scatterAbs.w;
|
||||||
|
float dist = distance(currPosition, prevPosition);
|
||||||
|
float froxelTrans = Transmittance(dist, extinction);
|
||||||
|
accumEmit = (accumEmit + emissive) * froxelTrans;
|
||||||
|
float3 extraEmit = accumEmit * emissiveScatter * scatter;
|
||||||
|
if(any(extraEmit > 0.0))
|
||||||
|
{
|
||||||
|
materialTextureB[currIndex].rgb += extraEmit;
|
||||||
|
}
|
||||||
|
prevPosition = currPosition;
|
||||||
|
}
|
||||||
|
#elif DIRECTION_NX
|
||||||
|
uint3 coords = uint3(textureSize.x - 1, id.x, id.y);
|
||||||
|
uint3 index0 = coords;
|
||||||
|
float3 tc0 = (float3(index0) + float3(1, 0.5, 0.5)) / textureSize;
|
||||||
|
float3 prevPosition = scene.FroxelTCToWorldSpace(tc0, float3(textureSize));
|
||||||
|
float3 accumEmit = float3(0, 0, 0);
|
||||||
|
for(int x = textureSize.x - 2; x >= 0; x--)
|
||||||
|
{
|
||||||
|
uint3 prevIndex = uint3(x + 1, coords.yz);
|
||||||
|
uint3 currIndex = uint3(x, coords.yz);
|
||||||
|
float3 tc = (float3(currIndex) + float3(1, 0.5, 0.5)) / textureSize;
|
||||||
|
float3 currPosition = scene.FroxelTCToWorldSpace(tc, float3(textureSize));
|
||||||
|
float4 scatterAbs = materialTextureA[prevIndex];
|
||||||
|
float3 emissive = materialTextureB[prevIndex].rgb;
|
||||||
|
float scatter = Brightness(scatterAbs.xyz);
|
||||||
|
float extinction = scatter + scatterAbs.w;
|
||||||
|
float dist = distance(currPosition, prevPosition);
|
||||||
|
float froxelTrans = Transmittance(dist, extinction);
|
||||||
|
accumEmit = (accumEmit + emissive) * froxelTrans;
|
||||||
|
float3 extraEmit = accumEmit * emissiveScatter * scatter;
|
||||||
|
if(any(extraEmit > 0.0))
|
||||||
|
{
|
||||||
|
materialTextureB[currIndex].rgb += extraEmit;
|
||||||
|
}
|
||||||
|
prevPosition = currPosition;
|
||||||
|
}
|
||||||
|
#elif DIRECTION_PY
|
||||||
|
uint3 index0 = uint3(id.x, 0, id.y);
|
||||||
|
float3 tc0 = (float3(index0) + float3(0.5, 0, 0.5)) / textureSize;
|
||||||
|
float3 prevPosition = scene.FroxelTCToWorldSpace(tc0, float3(textureSize));
|
||||||
|
float3 accumEmit = float3(0, 0, 0);
|
||||||
|
for(uint y = 1; y < textureSize.y; y++)
|
||||||
|
{
|
||||||
|
uint3 prevIndex = uint3(id.x, y - 1, id.y);
|
||||||
|
uint3 currIndex = uint3(id.x, y + 0, id.y);
|
||||||
|
float3 tc = (float3(currIndex) + float3(0.5, 0, 0.5)) / textureSize;
|
||||||
|
float3 currPosition = scene.FroxelTCToWorldSpace(tc, float3(textureSize));
|
||||||
|
float4 scatterAbs = materialTextureA[prevIndex];
|
||||||
|
float3 emissive = materialTextureB[prevIndex].rgb;
|
||||||
|
float scatter = Brightness(scatterAbs.xyz);
|
||||||
|
float extinction = scatter + scatterAbs.w;
|
||||||
|
float dist = distance(currPosition, prevPosition);
|
||||||
|
float froxelTrans = Transmittance(dist, extinction);
|
||||||
|
accumEmit = (accumEmit + emissive) * froxelTrans;
|
||||||
|
float3 extraEmit = accumEmit * emissiveScatter * scatter;
|
||||||
|
if(any(extraEmit > 0.0))
|
||||||
|
{
|
||||||
|
materialTextureB[currIndex].rgb += extraEmit;
|
||||||
|
}
|
||||||
|
prevPosition = currPosition;
|
||||||
|
}
|
||||||
|
#elif DIRECTION_NY
|
||||||
|
uint3 index0 = uint3(id.x, textureSize.y - 1, id.y);
|
||||||
|
float3 tc0 = (float3(index0) + float3(0.5, 1, 0.5)) / textureSize;
|
||||||
|
float3 prevPosition = scene.FroxelTCToWorldSpace(tc0, float3(textureSize));
|
||||||
|
float3 accumEmit = float3(0, 0, 0);
|
||||||
|
for(int y = textureSize.y - 2; y >= 0; y--)
|
||||||
|
{
|
||||||
|
uint3 prevIndex = uint3(id.x, y + 1, id.y);
|
||||||
|
uint3 currIndex = uint3(id.x, y + 0, id.y);
|
||||||
|
float3 tc = (float3(currIndex) + float3(0.5, 1, 0.5)) / textureSize;
|
||||||
|
float3 currPosition = scene.FroxelTCToWorldSpace(tc, float3(textureSize));
|
||||||
|
float4 scatterAbs = materialTextureA[prevIndex];
|
||||||
|
float3 emissive = materialTextureB[prevIndex].rgb;
|
||||||
|
float scatter = Brightness(scatterAbs.xyz);
|
||||||
|
float extinction = scatter + scatterAbs.w;
|
||||||
|
float dist = distance(currPosition, prevPosition);
|
||||||
|
float froxelTrans = Transmittance(dist, extinction);
|
||||||
|
accumEmit = (accumEmit + emissive) * froxelTrans;
|
||||||
|
float3 extraEmit = accumEmit * emissiveScatter * scatter;
|
||||||
|
if(any(extraEmit > 0.0))
|
||||||
|
{
|
||||||
|
materialTextureB[currIndex].rgb += extraEmit;
|
||||||
|
}
|
||||||
|
prevPosition = currPosition;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
float MissingDirectionMacro[-1];
|
||||||
|
#endif
|
||||||
|
}
|
|
@ -42,8 +42,19 @@ void cs(uint3 id : SV_DispatchThreadID)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// integScatter is computed using Frostbite's analytical solution:
|
// Even if the extinction coefficient is constant all along a given line segment of length Z,
|
||||||
// Int(S * T(Z) * dZ) == S * (1 - T(Z)) / extinction
|
// the transmittance is different at every point.
|
||||||
|
// We compute the final scatter/emissive using an analytical solution like Frostbite does.
|
||||||
|
// Integral(T(x) * dx) == Integral(e^(-extinction*x) * dZ) [0 to Z]
|
||||||
|
// == [e^(-extinction*x) / (-extinction)] [0 to Z]
|
||||||
|
// == (-1 / extinction) * [e^(-Z*extinction)] [0 to Z]
|
||||||
|
// == (-1 / extinction) * [T(Z)] [0 to Z]
|
||||||
|
// == (-1 / extinction) * (T(Z) - T(0))
|
||||||
|
// == (-1 / extinction) * (T(Z) - 1)
|
||||||
|
// == (1 / extinction) * (1 - T(Z))
|
||||||
|
// == (1 - T(Z)) / extinction
|
||||||
|
// The scatter/emissive coefficients are considered uniform in each froxel.
|
||||||
|
// They are therefore constants that can be pulled out of the integral, hence their omission.
|
||||||
|
|
||||||
SceneView scene = GetSceneView();
|
SceneView scene = GetSceneView();
|
||||||
RWTexture3D<float4> resolveTexture = ResourceDescriptorHeap[resolveTextureIndex];
|
RWTexture3D<float4> resolveTexture = ResourceDescriptorHeap[resolveTextureIndex];
|
||||||
|
@ -58,14 +69,14 @@ void cs(uint3 id : SV_DispatchThreadID)
|
||||||
uint3 index = uint3(id.xy, d);
|
uint3 index = uint3(id.xy, d);
|
||||||
float3 tc = (float3(index) + float3(0.5, 0.5, 1)) / textureSize; // far edge of current voxel
|
float3 tc = (float3(index) + float3(0.5, 0.5, 1)) / textureSize; // far edge of current voxel
|
||||||
float4 froxelScatterExt = scatterTexture[index];
|
float4 froxelScatterExt = scatterTexture[index];
|
||||||
float3 emissive = materialTextureB[index].rgb;
|
float3 froxelEmissive = materialTextureB[index].rgb;
|
||||||
float3 froxelScatter = froxelScatterExt.rgb + emissive;
|
float3 froxelScatter = froxelScatterExt.rgb;
|
||||||
float froxelExtinction = froxelScatterExt.a;
|
float froxelExtinction = froxelScatterExt.a;
|
||||||
float3 currPosition = scene.FroxelTCToWorldSpace(tc, float3(textureSize));
|
float3 currPosition = scene.FroxelTCToWorldSpace(tc, float3(textureSize));
|
||||||
float depthStep = distance(currPosition, prevPosition);
|
float depthStep = distance(currPosition, prevPosition);
|
||||||
float froxelTrans = Transmittance(depthStep, froxelExtinction);
|
float froxelTrans = Transmittance(depthStep, froxelExtinction);
|
||||||
float3 integScatter = froxelScatter * (1.0 - froxelTrans) / (froxelExtinction == 0.0 ? 1.0 : froxelExtinction);
|
float froxelTransInteg = (1.0 - froxelTrans) / (froxelExtinction == 0.0 ? 1.0 : froxelExtinction);
|
||||||
accumScatter += accumTrans * integScatter;
|
accumScatter += (accumTrans * froxelTransInteg) * (froxelScatter + froxelEmissive);
|
||||||
accumTrans *= froxelTrans;
|
accumTrans *= froxelTrans;
|
||||||
resolveTexture[index] = float4(accumScatter, accumTrans);
|
resolveTexture[index] = float4(accumScatter, accumTrans);
|
||||||
prevPosition = currPosition;
|
prevPosition = currPosition;
|
||||||
|
|
|
@ -30,6 +30,7 @@ cbuffer RootConstants
|
||||||
{
|
{
|
||||||
float3 jitter;
|
float3 jitter;
|
||||||
uint visTextureIndex;
|
uint visTextureIndex;
|
||||||
|
uint frustumVisTextureIndex;
|
||||||
uint depthMip;
|
uint depthMip;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,9 +44,17 @@ void cs(uint3 id : SV_DispatchThreadID)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RWTexture2D<uint> frustumVisTexture = ResourceDescriptorHeap[frustumVisTextureIndex];
|
||||||
|
uint furthestVisibleFroxelZIndex = frustumVisTexture[id.xy];
|
||||||
|
if(id.z > furthestVisibleFroxelZIndex)
|
||||||
|
{
|
||||||
|
// this helps fix issues like dark spots around opaque geometry set against the skybox
|
||||||
|
visTexture[id] = 0.0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
SceneView scene = GetSceneView();
|
SceneView scene = GetSceneView();
|
||||||
RTAS rtas = ResourceDescriptorHeap[scene.tlasBufferIndex];
|
RTAS rtas = ResourceDescriptorHeap[scene.tlasBufferIndex];
|
||||||
Texture2D<float2> depthMinMaxTexture = ResourceDescriptorHeap[scene.depthMinMaxTextureIndex];
|
|
||||||
|
|
||||||
float2 tc = (float2(id.xy) + float2(0.5, 0.5)) / float2(textureSize.xy);
|
float2 tc = (float2(id.xy) + float2(0.5, 0.5)) / float2(textureSize.xy);
|
||||||
float2 ndc = TCToNDC(tc);
|
float2 ndc = TCToNDC(tc);
|
||||||
|
@ -57,14 +66,5 @@ void cs(uint3 id : SV_DispatchThreadID)
|
||||||
jitter.z * cameraRay;
|
jitter.z * cameraRay;
|
||||||
float vis = TraceVisibilityWithoutAT(rtas, froxelPosition, scene.sunDirection, 10000.0);
|
float vis = TraceVisibilityWithoutAT(rtas, froxelPosition, scene.sunDirection, 10000.0);
|
||||||
|
|
||||||
// this helps fix dark spots around opaque geometry set against the skybox
|
|
||||||
float storedDepth = depthMinMaxTexture.mips[depthMip][id.xy].x;
|
|
||||||
float4 positionCS = mul(scene.projectionMatrix, mul(scene.viewMatrix, float4(froxelPosition, 1)));
|
|
||||||
float froxelDepth = positionCS.z / positionCS.w;
|
|
||||||
if(froxelDepth < storedDepth)
|
|
||||||
{
|
|
||||||
vis = 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
visTexture[id] = vis;
|
visTexture[id] = vis;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,15 @@ along with Challenge Quake 3. If not, see <https://www.gnu.org/licenses/>.
|
||||||
#include "scene_view.h.hlsli"
|
#include "scene_view.h.hlsli"
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(TYPE_FLOAT4)
|
||||||
|
typedef float4 Type;
|
||||||
|
#elif defined(TYPE_FLOAT)
|
||||||
|
typedef float Type;
|
||||||
|
#else
|
||||||
|
#pragma message "define TYPE_FLOAT4 or TYPE_FLOAT"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
cbuffer RootConstants
|
cbuffer RootConstants
|
||||||
{
|
{
|
||||||
uint currTextureIndex;
|
uint currTextureIndex;
|
||||||
|
@ -36,7 +45,7 @@ cbuffer RootConstants
|
||||||
[numthreads(4, 4, 4)]
|
[numthreads(4, 4, 4)]
|
||||||
void cs(uint3 id : SV_DispatchThreadID)
|
void cs(uint3 id : SV_DispatchThreadID)
|
||||||
{
|
{
|
||||||
RWTexture3D<float> currTexture = ResourceDescriptorHeap[currTextureIndex];
|
RWTexture3D<Type> currTexture = ResourceDescriptorHeap[currTextureIndex];
|
||||||
uint3 textureSize = GetTextureSize(currTexture);
|
uint3 textureSize = GetTextureSize(currTexture);
|
||||||
if(any(id >= textureSize))
|
if(any(id >= textureSize))
|
||||||
{
|
{
|
||||||
|
@ -44,17 +53,17 @@ void cs(uint3 id : SV_DispatchThreadID)
|
||||||
}
|
}
|
||||||
|
|
||||||
SceneView scene = GetSceneView();
|
SceneView scene = GetSceneView();
|
||||||
Texture3D<float> prevTexture = ResourceDescriptorHeap[prevTextureIndex];
|
Texture3D<Type> prevTexture = ResourceDescriptorHeap[prevTextureIndex];
|
||||||
SamplerState prevTextureSampler = SamplerDescriptorHeap[prevTextureSamplerIndex];
|
SamplerState prevTextureSampler = SamplerDescriptorHeap[prevTextureSamplerIndex];
|
||||||
|
|
||||||
float3 tc = scene.FroxelReproject01(id, float3(textureSize));
|
float3 tc = scene.FroxelReproject01(id, float3(textureSize));
|
||||||
float currValue = currTexture[id];
|
Type currValue = currTexture[id];
|
||||||
float3 halfPixelSize = float3(0.5, 0.5, 0.5) / float3(textureSize);
|
float3 halfPixelSize = float3(0.5, 0.5, 0.5) / float3(textureSize);
|
||||||
if(IsInRange(tc, halfPixelSize, float3(1, 1, 1) - halfPixelSize))
|
if(IsInRange(tc, halfPixelSize, float3(1, 1, 1) - halfPixelSize))
|
||||||
{
|
{
|
||||||
float prevValue = prevTexture.SampleLevel(prevTextureSampler, tc, 0);
|
Type prevValue = prevTexture.SampleLevel(prevTextureSampler, tc, 0);
|
||||||
float finalValue = lerp(currValue, prevValue, alpha);
|
Type finalValue = lerp(currValue, prevValue, alpha);
|
||||||
if(finalValue != currValue)
|
if(any(finalValue != currValue))
|
||||||
{
|
{
|
||||||
currTexture[id] = finalValue;
|
currTexture[id] = finalValue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,279 @@
|
||||||
|
/*
|
||||||
|
===========================================================================
|
||||||
|
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/>.
|
||||||
|
===========================================================================
|
||||||
|
*/
|
||||||
|
// volumetric lighting: sampling NanoVDB volumes over oriented froxels and axis-aligned boxes
|
||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
|
#include "common.hlsli"
|
||||||
|
#include "scene_view.h.hlsli"
|
||||||
|
#define PNANOVDB_HLSL
|
||||||
|
#include "PNanoVDB.h"
|
||||||
|
|
||||||
|
|
||||||
|
struct Transform
|
||||||
|
{
|
||||||
|
float3x3 worldToIndex;
|
||||||
|
float3 originOffset;
|
||||||
|
float3 translation;
|
||||||
|
float3 stepSize;
|
||||||
|
|
||||||
|
void DecodeTransform(float4 transform[4], float2 transform2)
|
||||||
|
{
|
||||||
|
worldToIndex = float3x3(
|
||||||
|
transform[0].x, transform[0].y, transform[0].z,
|
||||||
|
transform[0].w, transform[1].x, transform[1].y,
|
||||||
|
transform[1].z, transform[1].w, transform[2].x);
|
||||||
|
originOffset = float3(transform[2].yzw);
|
||||||
|
translation = float3(transform[3].xyz);
|
||||||
|
stepSize = float3(transform[3].w, transform2.x, transform2.y);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Transform DecodeTransform(float4 packed[4], float2 packed2)
|
||||||
|
{
|
||||||
|
Transform transform;
|
||||||
|
transform.DecodeTransform(packed, packed2);
|
||||||
|
|
||||||
|
return transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SampleResult
|
||||||
|
{
|
||||||
|
float sum;
|
||||||
|
int maxSampleCount;
|
||||||
|
int sampleCount;
|
||||||
|
float transmittance;
|
||||||
|
float3 inScatteredLight;
|
||||||
|
|
||||||
|
void Clear()
|
||||||
|
{
|
||||||
|
sum = 0.0;
|
||||||
|
maxSampleCount = 1;
|
||||||
|
sampleCount = 0;
|
||||||
|
transmittance = 1.0;
|
||||||
|
inScatteredLight = float3(0, 0, 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
SampleResult CreateSampleResult()
|
||||||
|
{
|
||||||
|
SampleResult result;
|
||||||
|
result.Clear();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Grid
|
||||||
|
{
|
||||||
|
pnanovdb_buf_t buffer;
|
||||||
|
pnanovdb_grid_handle_t grid;
|
||||||
|
pnanovdb_tree_handle_t tree;
|
||||||
|
pnanovdb_root_handle_t root;
|
||||||
|
pnanovdb_vec3_t bboxMin;
|
||||||
|
pnanovdb_vec3_t bboxMax;
|
||||||
|
pnanovdb_uint32_t gridType;
|
||||||
|
pnanovdb_readaccessor_t accessor;
|
||||||
|
|
||||||
|
void Init(pnanovdb_buf_t gridBuffer, uint gridByteOffset)
|
||||||
|
{
|
||||||
|
buffer = gridBuffer;
|
||||||
|
grid.address.byte_offset = gridByteOffset;
|
||||||
|
pnanovdb_vec3_t extBboxMin;
|
||||||
|
pnanovdb_vec3_t extBboxMax;
|
||||||
|
pnanovdb_tree_handle_t tree = pnanovdb_grid_get_tree(buffer, grid);
|
||||||
|
pnanovdb_root_handle_t root = pnanovdb_tree_get_root(buffer, tree);
|
||||||
|
bboxMin = pnanovdb_coord_to_vec3(pnanovdb_root_get_bbox_min(buffer, root));
|
||||||
|
bboxMax = pnanovdb_coord_to_vec3(pnanovdb_root_get_bbox_max(buffer, root));
|
||||||
|
gridType = pnanovdb_grid_get_grid_type(buffer, grid);
|
||||||
|
pnanovdb_readaccessor_init(accessor, root);
|
||||||
|
}
|
||||||
|
|
||||||
|
float3 WorldToIndex(float3 worldPosition, Transform transform)
|
||||||
|
{
|
||||||
|
float3 index = mul(transform.worldToIndex, worldPosition - transform.translation) - transform.originOffset;
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
float ReadFloat(pnanovdb_coord_t coords)
|
||||||
|
{
|
||||||
|
pnanovdb_uint32_t level;
|
||||||
|
pnanovdb_address_t address = pnanovdb_readaccessor_get_value_address_and_level(gridType, buffer, accessor, coords, level);
|
||||||
|
float result;
|
||||||
|
if(level == 0u && gridType != PNANOVDB_GRID_TYPE_FLOAT)
|
||||||
|
{
|
||||||
|
if(gridType == PNANOVDB_GRID_TYPE_FPN)
|
||||||
|
{
|
||||||
|
result = pnanovdb_leaf_fpn_read_float(buffer, address, coords);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = pnanovdb_leaf_fp_read_float(buffer, address, coords, gridType - PNANOVDB_GRID_TYPE_FP4 + 2u);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = pnanovdb_read_float(buffer, address);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
SampleResult GetFroxelAverage(SceneView scene, float3 froxelPosition, float3 froxelSize, Transform transform)
|
||||||
|
{
|
||||||
|
int3 steps = int3(ceil(froxelSize / (2.0 * transform.stepSize)));
|
||||||
|
|
||||||
|
SampleResult result;
|
||||||
|
result.maxSampleCount = (2 * steps.x + 1) * (2 * steps.y + 1) * (2 * steps.z + 1);
|
||||||
|
result.sampleCount = 0;
|
||||||
|
result.sum = 0.0;
|
||||||
|
for(int z = -steps.z; z <= steps.z; z++)
|
||||||
|
{
|
||||||
|
float3 offZ = float(z) * transform.stepSize.z * scene.cameraForward;
|
||||||
|
for(int y = -steps.y; y <= steps.y; y++)
|
||||||
|
{
|
||||||
|
float3 offY = float(y) * transform.stepSize.y * scene.cameraUp;
|
||||||
|
for(int x = -steps.x; x <= steps.x; x++)
|
||||||
|
{
|
||||||
|
float3 offX = float(x) * transform.stepSize.x * scene.cameraLeft;
|
||||||
|
float3 worldPosition = froxelPosition + offX + offY + offZ;
|
||||||
|
float3 indexF = WorldToIndex(worldPosition, transform);
|
||||||
|
if(IsInRange(indexF, bboxMin, bboxMax))
|
||||||
|
{
|
||||||
|
int3 index = int3(indexF);
|
||||||
|
float value = ReadFloat(index);
|
||||||
|
result.sum += value;
|
||||||
|
result.sampleCount += value > 0.0 ? 1.0 : 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
SampleResult GetAxisAlignedBoxAverage(SceneView scene, float3 voxelPosition, float3 voxelSize, Transform transform)
|
||||||
|
{
|
||||||
|
int3 steps = int3(voxelSize / (2.0 * transform.stepSize));
|
||||||
|
|
||||||
|
SampleResult result;
|
||||||
|
result.maxSampleCount = (2 * steps.x + 1) * (2 * steps.y + 1) * (2 * steps.z + 1);
|
||||||
|
result.sampleCount = 0;
|
||||||
|
result.sum = 0.0;
|
||||||
|
for(int z = -steps.z; z <= steps.z; z++)
|
||||||
|
{
|
||||||
|
for(int y = -steps.y; y <= steps.y; y++)
|
||||||
|
{
|
||||||
|
for(int x = -steps.x; x <= steps.x; x++)
|
||||||
|
{
|
||||||
|
float3 worldPosition = voxelPosition + float3(x, y, z) * transform.stepSize;
|
||||||
|
float3 indexF = WorldToIndex(worldPosition, transform);
|
||||||
|
int3 index = int3(indexF);
|
||||||
|
float value = ReadFloat(index);
|
||||||
|
result.sum += value;
|
||||||
|
result.sampleCount += value > 0.0 ? 1.0 : 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExpandFroxelBoundingBox(inout float3 boxMin, inout float3 boxMax, SceneView scene, float3 tc, float3 textureSize, Transform transform)
|
||||||
|
{
|
||||||
|
float3 pointWS = scene.FroxelTCToWorldSpace(tc, textureSize);
|
||||||
|
float3 indexF = WorldToIndex(pointWS, transform);
|
||||||
|
ExpandBoundingBox(boxMin, boxMax, indexF);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OverlapsFroxel(SceneView scene, int3 index, float3 textureSize, Transform transform)
|
||||||
|
{
|
||||||
|
float3 tc = (float3(index) + float3(0.5, 0.5, 0.5)) / textureSize;
|
||||||
|
float3 halfTexel = float3(0.5, 0.5, 0.5) / textureSize;
|
||||||
|
float3 froxelMin, froxelMax;
|
||||||
|
ClearBoundingBox(froxelMin, froxelMax);
|
||||||
|
ExpandFroxelBoundingBox(froxelMin, froxelMax, scene, tc + float3(-halfTexel.x, 0, 0), textureSize, transform);
|
||||||
|
ExpandFroxelBoundingBox(froxelMin, froxelMax, scene, tc + float3(halfTexel.x, 0, 0), textureSize, transform);
|
||||||
|
ExpandFroxelBoundingBox(froxelMin, froxelMax, scene, tc + float3(0, -halfTexel.y, 0), textureSize, transform);
|
||||||
|
ExpandFroxelBoundingBox(froxelMin, froxelMax, scene, tc + float3(0, halfTexel.y, 0), textureSize, transform);
|
||||||
|
ExpandFroxelBoundingBox(froxelMin, froxelMax, scene, tc + float3(0, 0, -halfTexel.z), textureSize, transform);
|
||||||
|
ExpandFroxelBoundingBox(froxelMin, froxelMax, scene, tc + float3(0, 0, halfTexel.z), textureSize, transform);
|
||||||
|
bool overlaps = all(froxelMax >= bboxMin) && all(froxelMin <= bboxMax);
|
||||||
|
|
||||||
|
return overlaps;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExpandAABB(inout float3 boxMin, inout float3 boxMax, SceneView scene, float3 tc, float3 textureSize, float worldScale, Transform transform)
|
||||||
|
{
|
||||||
|
float3 pointWS = scene.ExtinctionTCToWorldSpace(tc, textureSize, worldScale);
|
||||||
|
float3 indexF = WorldToIndex(pointWS, transform);
|
||||||
|
ExpandBoundingBox(boxMin, boxMax, indexF);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OverlapsAxisAlignedBox(SceneView scene, int3 index, float3 textureSize, float worldScale, Transform transform)
|
||||||
|
{
|
||||||
|
float3 tc = (float3(index) + float3(0.5, 0.5, 0.5)) / textureSize;
|
||||||
|
float3 halfTexel = float3(0.5, 0.5, 0.5) / textureSize;
|
||||||
|
float3 voxelMin, voxelMax;
|
||||||
|
ClearBoundingBox(voxelMin, voxelMax);
|
||||||
|
ExpandAABB(voxelMin, voxelMax, scene, tc + float3(-halfTexel.x, 0, 0), textureSize, worldScale, transform);
|
||||||
|
ExpandAABB(voxelMin, voxelMax, scene, tc + float3(halfTexel.x, 0, 0), textureSize, worldScale, transform);
|
||||||
|
ExpandAABB(voxelMin, voxelMax, scene, tc + float3(0, -halfTexel.y, 0), textureSize, worldScale, transform);
|
||||||
|
ExpandAABB(voxelMin, voxelMax, scene, tc + float3(0, halfTexel.y, 0), textureSize, worldScale, transform);
|
||||||
|
ExpandAABB(voxelMin, voxelMax, scene, tc + float3(0, 0, -halfTexel.z), textureSize, worldScale, transform);
|
||||||
|
ExpandAABB(voxelMin, voxelMax, scene, tc + float3(0, 0, halfTexel.z), textureSize, worldScale, transform);
|
||||||
|
bool overlaps = all(voxelMax >= bboxMin) && all(voxelMin <= bboxMax);
|
||||||
|
|
||||||
|
return overlaps;
|
||||||
|
}
|
||||||
|
|
||||||
|
float RaymarchTransmittance(float3 position, float3 step, Transform transform, float extinctionScale)
|
||||||
|
{
|
||||||
|
float stepDist = length(step);
|
||||||
|
float accumTrans = 1.0;
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
position += step;
|
||||||
|
float3 indexF = WorldToIndex(position, transform);
|
||||||
|
if(!IsInRange(indexF, bboxMin, bboxMax))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int3 index = int3(indexF);
|
||||||
|
float ext = ReadFloat(index) * extinctionScale;
|
||||||
|
float sampleTrans = Transmittance(stepDist, ext);
|
||||||
|
accumTrans *= saturate(sampleTrans);
|
||||||
|
}
|
||||||
|
|
||||||
|
return accumTrans;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Grid GetGrid(pnanovdb_buf_t buffer, uint gridByteOffset)
|
||||||
|
{
|
||||||
|
Grid grid;
|
||||||
|
grid.Init(buffer, gridByteOffset);
|
||||||
|
|
||||||
|
return grid;
|
||||||
|
}
|
|
@ -18,38 +18,42 @@ 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/>.
|
along with Challenge Quake 3. If not, see <https://www.gnu.org/licenses/>.
|
||||||
===========================================================================
|
===========================================================================
|
||||||
*/
|
*/
|
||||||
// volumetric lighting: update indirect dispatch buffer for particle injection
|
// volumetric lighting particles: clear all froxel tiles and global counters
|
||||||
|
|
||||||
|
|
||||||
#include "common.hlsli"
|
#include "common.hlsli"
|
||||||
|
#include "vl_common.h.hlsli"
|
||||||
|
#include "scene_view.h.hlsli"
|
||||||
|
|
||||||
|
|
||||||
cbuffer RootConstants
|
cbuffer RootConstants
|
||||||
{
|
{
|
||||||
uint3 tileResolution;
|
uint counterBufferIndex;
|
||||||
uint tileBufferIndex;
|
uint tileBufferIndex;
|
||||||
uint dispatchBufferIndex;
|
uint tileCount;
|
||||||
uint particleTileBufferIndex;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[numthreads(4, 4, 4)]
|
[numthreads(64, 1, 1)]
|
||||||
void cs(uint3 id : SV_DispatchThreadID)
|
void cs(uint3 id : SV_DispatchThreadID)
|
||||||
{
|
{
|
||||||
if(any(id >= tileResolution))
|
if(id.x >= tileCount)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
RWByteAddressBuffer dispatchBuffer = ResourceDescriptorHeap[dispatchBufferIndex];
|
RWStructuredBuffer<Tile> tileBuffer= ResourceDescriptorHeap[tileBufferIndex];
|
||||||
RWByteAddressBuffer tileHitBuffer = ResourceDescriptorHeap[tileBufferIndex];
|
if(id.x == 0)
|
||||||
RWStructuredBuffer<uint3> tileWorkBuffer = ResourceDescriptorHeap[particleTileBufferIndex];
|
|
||||||
|
|
||||||
uint tileIndex = FlattenIndex(id, tileResolution);
|
|
||||||
uint hasParticle = tileHitBuffer.Load(tileIndex * 4);
|
|
||||||
if(hasParticle != 0)
|
|
||||||
{
|
{
|
||||||
uint workIndex;
|
RWStructuredBuffer<Counters> counterBuffer = ResourceDescriptorHeap[counterBufferIndex];
|
||||||
dispatchBuffer.InterlockedAdd(0, 1, workIndex);
|
Counters counters;
|
||||||
tileWorkBuffer[workIndex] = id;
|
counters.particleCount = 0;
|
||||||
|
counters.tileCount = 0;
|
||||||
|
counterBuffer[0] = counters;
|
||||||
}
|
}
|
||||||
|
Tile tile;
|
||||||
|
tile.firstParticle = 0;
|
||||||
|
tile.particleCount = 0;
|
||||||
|
tile.particleIndex = 0;
|
||||||
|
tile.pad0 = 0;
|
||||||
|
tileBuffer[id.x] = tile;
|
||||||
}
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
/*
|
||||||
|
===========================================================================
|
||||||
|
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/>.
|
||||||
|
===========================================================================
|
||||||
|
*/
|
||||||
|
// volumetric lighting particles: count the number of particles in each froxel tile
|
||||||
|
|
||||||
|
|
||||||
|
#include "common.hlsli"
|
||||||
|
#include "vl_common.h.hlsli"
|
||||||
|
#include "scene_view.h.hlsli"
|
||||||
|
|
||||||
|
|
||||||
|
cbuffer RootConstants
|
||||||
|
{
|
||||||
|
uint3 fullResolution;
|
||||||
|
uint tileBufferIndex;
|
||||||
|
uint3 tileResolution;
|
||||||
|
uint pad0;
|
||||||
|
uint3 tileScale;
|
||||||
|
uint pad1;
|
||||||
|
uint particleBufferIndex;
|
||||||
|
uint emitterBufferIndex;
|
||||||
|
uint liveBufferIndex;
|
||||||
|
uint emitterIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
[numthreads(64, 1, 1)]
|
||||||
|
void cs(uint3 id : SV_DispatchThreadID)
|
||||||
|
{
|
||||||
|
RWStructuredBuffer<ParticleEmitter> emitterBuffer = ResourceDescriptorHeap[emitterBufferIndex];
|
||||||
|
ParticleEmitter emitter = emitterBuffer[emitterIndex];
|
||||||
|
if(id.x >= emitter.liveCount2)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RWStructuredBuffer<Particle> particleBuffer = ResourceDescriptorHeap[particleBufferIndex];
|
||||||
|
RWStructuredBuffer<Tile> tileBuffer= ResourceDescriptorHeap[tileBufferIndex];
|
||||||
|
RWStructuredBuffer<uint> liveBuffer = ResourceDescriptorHeap[liveBufferIndex];
|
||||||
|
SceneView scene = GetSceneView();
|
||||||
|
|
||||||
|
uint firstIndex = emitter.firstIndex;
|
||||||
|
uint particleIndex = liveBuffer[firstIndex + id.x];
|
||||||
|
Particle particle = particleBuffer[firstIndex + particleIndex];
|
||||||
|
float3 P = particle.position;
|
||||||
|
float r = particle.radius;
|
||||||
|
float3 fwd = scene.cameraForward;
|
||||||
|
float4 extentsXYNDC = ProjectedSphereExtentsNDC(particle.position, particle.radius, scene.viewMatrix, scene.projectionMatrix);
|
||||||
|
float4 extentsXYFroxel = (extentsXYNDC * 0.5 + float(0.5).xxxx) * float4(fullResolution.xy, fullResolution.xy);
|
||||||
|
int2 extentsZFroxel = scene.FroxelSphereZExtents(particle.position, particle.radius, fullResolution);
|
||||||
|
int3 boxMin = int3(extentsXYFroxel.x, extentsXYFroxel.y, extentsZFroxel.x);
|
||||||
|
int3 boxMax = int3(ceil(extentsXYFroxel.z), ceil(extentsXYFroxel.w), extentsZFroxel.y);
|
||||||
|
particleBuffer[firstIndex + particleIndex].froxelMin = max(boxMin, int3(0, 0, 0));
|
||||||
|
particleBuffer[firstIndex + particleIndex].froxelMax = min(boxMax, int3(fullResolution) - int3(1, 1, 1));
|
||||||
|
boxMin /= int3(tileScale);
|
||||||
|
boxMax /= int3(tileScale);
|
||||||
|
boxMin = max(boxMin, int3(0, 0, 0));
|
||||||
|
boxMax = min(boxMax, int3(tileResolution) - int3(1, 1, 1));
|
||||||
|
|
||||||
|
for(int x = boxMin.x; x <= boxMax.x; x++)
|
||||||
|
{
|
||||||
|
for(int y = boxMin.y; y <= boxMax.y; y++)
|
||||||
|
{
|
||||||
|
for(int z = boxMin.z; z <= boxMax.z; z++)
|
||||||
|
{
|
||||||
|
uint3 tileIndex = uint3(x, y, z);
|
||||||
|
uint flatTileIndex = FlattenIndex(tileIndex, tileResolution);
|
||||||
|
InterlockedAdd(tileBuffer[flatTileIndex].particleCount, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,53 +18,49 @@ 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/>.
|
along with Challenge Quake 3. If not, see <https://www.gnu.org/licenses/>.
|
||||||
===========================================================================
|
===========================================================================
|
||||||
*/
|
*/
|
||||||
// volumetric lighting: pre-process particles for frustum volume injection
|
// volumetric lighting particles: create final live particle index array
|
||||||
|
|
||||||
|
|
||||||
#include "common.hlsli"
|
#include "common.hlsli"
|
||||||
|
#include "vl_common.h.hlsli"
|
||||||
#include "scene_view.h.hlsli"
|
#include "scene_view.h.hlsli"
|
||||||
|
|
||||||
|
|
||||||
cbuffer RootConstants
|
cbuffer RootConstants
|
||||||
{
|
{
|
||||||
uint3 fullResolution;
|
uint3 fullResolution;
|
||||||
uint tileBufferIndex;
|
uint emitterIndex;
|
||||||
uint3 tileResolution;
|
uint3 tileResolution;
|
||||||
uint particleBufferIndex;
|
uint maxParticleIndexCount;
|
||||||
uint3 tileScale;
|
uint3 tileScale;
|
||||||
uint particleCount;
|
uint tileBufferIndex;
|
||||||
|
uint emitterBufferIndex;
|
||||||
|
uint particleBufferIndex;
|
||||||
|
uint liveBufferIndex;
|
||||||
|
uint indexBufferIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
[numthreads(64, 1, 1)]
|
[numthreads(64, 1, 1)]
|
||||||
void cs(uint3 id : SV_DispatchThreadID)
|
void cs(uint3 id : SV_DispatchThreadID)
|
||||||
{
|
{
|
||||||
uint particleIndex = id.x;
|
RWStructuredBuffer<ParticleEmitter> emitterBuffer = ResourceDescriptorHeap[emitterBufferIndex];
|
||||||
if(particleIndex >= particleCount)
|
ParticleEmitter emitter = emitterBuffer[emitterIndex];
|
||||||
|
if(id.x >= emitter.liveCount2)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
StructuredBuffer<Particle> particleBuffer = ResourceDescriptorHeap[particleBufferIndex];
|
RWStructuredBuffer<Particle> particleBuffer = ResourceDescriptorHeap[particleBufferIndex];
|
||||||
RWByteAddressBuffer tileBuffer = ResourceDescriptorHeap[tileBufferIndex];
|
RWStructuredBuffer<Tile> tileBuffer = ResourceDescriptorHeap[tileBufferIndex];
|
||||||
|
RWStructuredBuffer<uint> liveBuffer = ResourceDescriptorHeap[liveBufferIndex];
|
||||||
|
RWStructuredBuffer<uint> indexBuffer = ResourceDescriptorHeap[indexBufferIndex];
|
||||||
SceneView scene = GetSceneView();
|
SceneView scene = GetSceneView();
|
||||||
|
|
||||||
Particle particle = particleBuffer[particleIndex];
|
uint firstIndex = emitter.firstIndex;
|
||||||
float3 P = particle.position;
|
uint particleIndex = liveBuffer[firstIndex + id.x];
|
||||||
float r = particle.radius * 1.0625;
|
Particle particle = particleBuffer[firstIndex + particleIndex];
|
||||||
float3 left = scene.cameraLeft;
|
int3 boxMin = particle.froxelMin / int3(tileScale);
|
||||||
float3 up = scene.cameraUp;
|
int3 boxMax = particle.froxelMax / int3(tileScale);
|
||||||
float3 fwd = scene.cameraForward;
|
|
||||||
int3 boxMin;
|
|
||||||
int3 boxMax;
|
|
||||||
ClearBoundingBox(boxMin, boxMax);
|
|
||||||
ExpandBoundingBox(boxMin, boxMax, scene.FroxelWorldSpaceToIndex(P + r * left, fullResolution));
|
|
||||||
ExpandBoundingBox(boxMin, boxMax, scene.FroxelWorldSpaceToIndex(P - r * left, fullResolution));
|
|
||||||
ExpandBoundingBox(boxMin, boxMax, scene.FroxelWorldSpaceToIndex(P + r * up, fullResolution));
|
|
||||||
ExpandBoundingBox(boxMin, boxMax, scene.FroxelWorldSpaceToIndex(P - r * up, fullResolution));
|
|
||||||
ExpandBoundingBox(boxMin, boxMax, scene.FroxelWorldSpaceToIndex(P + r * fwd, fullResolution));
|
|
||||||
ExpandBoundingBox(boxMin, boxMax, scene.FroxelWorldSpaceToIndex(P - r * fwd, fullResolution));
|
|
||||||
boxMin /= int3(tileScale);
|
|
||||||
boxMax /= int3(tileScale);
|
|
||||||
boxMin = max(boxMin, int3(0, 0, 0));
|
boxMin = max(boxMin, int3(0, 0, 0));
|
||||||
boxMax = min(boxMax, int3(tileResolution) - int3(1, 1, 1));
|
boxMax = min(boxMax, int3(tileResolution) - int3(1, 1, 1));
|
||||||
for(int x = boxMin.x; x <= boxMax.x; x++)
|
for(int x = boxMin.x; x <= boxMax.x; x++)
|
||||||
|
@ -74,8 +70,15 @@ void cs(uint3 id : SV_DispatchThreadID)
|
||||||
for(int z = boxMin.z; z <= boxMax.z; z++)
|
for(int z = boxMin.z; z <= boxMax.z; z++)
|
||||||
{
|
{
|
||||||
uint3 tileIndex = uint3(x, y, z);
|
uint3 tileIndex = uint3(x, y, z);
|
||||||
uint index = FlattenIndex(tileIndex, tileResolution);
|
uint flatTileIndex = FlattenIndex(tileIndex, tileResolution);
|
||||||
tileBuffer.Store(index * 4, 1);
|
uint particleWriteOffset;
|
||||||
|
InterlockedAdd(tileBuffer[flatTileIndex].particleIndex, 1, particleWriteOffset);
|
||||||
|
uint particleWriteIndex = tileBuffer[flatTileIndex].firstParticle + particleWriteOffset;
|
||||||
|
if(particleWriteOffset < tileBuffer[flatTileIndex].particleCount &&
|
||||||
|
particleWriteIndex < maxParticleIndexCount)
|
||||||
|
{
|
||||||
|
indexBuffer[particleWriteIndex] = firstIndex + particleIndex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,73 +0,0 @@
|
||||||
/*
|
|
||||||
===========================================================================
|
|
||||||
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/>.
|
|
||||||
===========================================================================
|
|
||||||
*/
|
|
||||||
// volumetric lighting: pre-process particles for extinction volume injection
|
|
||||||
|
|
||||||
|
|
||||||
#include "common.hlsli"
|
|
||||||
#include "scene_view.h.hlsli"
|
|
||||||
|
|
||||||
|
|
||||||
cbuffer RootConstants
|
|
||||||
{
|
|
||||||
uint3 fullResolution;
|
|
||||||
uint tileBufferIndex;
|
|
||||||
uint3 tileResolution;
|
|
||||||
uint particleBufferIndex;
|
|
||||||
uint3 tileScale;
|
|
||||||
uint particleCount;
|
|
||||||
float extinctionWorldScale;
|
|
||||||
}
|
|
||||||
|
|
||||||
[numthreads(64, 1, 1)]
|
|
||||||
void cs(uint3 id : SV_DispatchThreadID)
|
|
||||||
{
|
|
||||||
uint particleIndex = id.x;
|
|
||||||
if(particleIndex >= particleCount)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
StructuredBuffer<Particle> particleBuffer = ResourceDescriptorHeap[particleBufferIndex];
|
|
||||||
RWByteAddressBuffer tileBuffer = ResourceDescriptorHeap[tileBufferIndex];
|
|
||||||
SceneView scene = GetSceneView();
|
|
||||||
|
|
||||||
Particle particle = particleBuffer[particleIndex];
|
|
||||||
float3 P = particle.position;
|
|
||||||
float r = particle.radius;
|
|
||||||
int3 boxMin = scene.ExtinctionWorldSpaceToIndex(P - float3(r, r, r), fullResolution, extinctionWorldScale);
|
|
||||||
int3 boxMax = scene.ExtinctionWorldSpaceToIndex(P + float3(r, r, r), fullResolution, extinctionWorldScale);
|
|
||||||
boxMin /= int3(tileScale);
|
|
||||||
boxMax /= int3(tileScale);
|
|
||||||
boxMin = max(boxMin, int3(0, 0, 0));
|
|
||||||
boxMax = min(boxMax, int3(tileResolution) - int3(1, 1, 1));
|
|
||||||
for(int x = boxMin.x; x <= boxMax.x; x++)
|
|
||||||
{
|
|
||||||
for(int y = boxMin.y; y <= boxMax.y; y++)
|
|
||||||
{
|
|
||||||
for(int z = boxMin.z; z <= boxMax.z; z++)
|
|
||||||
{
|
|
||||||
uint3 tileIndex = uint3(x, y, z);
|
|
||||||
uint index = FlattenIndex(tileIndex, tileResolution);
|
|
||||||
tileBuffer.Store(index * 4, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
===========================================================================
|
||||||
|
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/>.
|
||||||
|
===========================================================================
|
||||||
|
*/
|
||||||
|
// volumetric lighting particles: create compacted froxel tile array and compute per-tile particle offset
|
||||||
|
|
||||||
|
|
||||||
|
#include "common.hlsli"
|
||||||
|
#include "vl_common.h.hlsli"
|
||||||
|
#include "scene_view.h.hlsli"
|
||||||
|
|
||||||
|
|
||||||
|
cbuffer RootConstants
|
||||||
|
{
|
||||||
|
uint counterBufferIndex;
|
||||||
|
uint tileBufferIndex;
|
||||||
|
uint tileIndexBufferIndex;
|
||||||
|
uint tileCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
[numthreads(64, 1, 1)]
|
||||||
|
void cs(uint3 id : SV_DispatchThreadID)
|
||||||
|
{
|
||||||
|
uint tileIndex = id.x;
|
||||||
|
if(tileIndex >= tileCount)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RWStructuredBuffer<Tile> tileBuffer = ResourceDescriptorHeap[tileBufferIndex];
|
||||||
|
if(tileBuffer[tileIndex].particleCount == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RWStructuredBuffer<Counters> counterBuffer = ResourceDescriptorHeap[counterBufferIndex];
|
||||||
|
RWStructuredBuffer<uint> tileIndexBuffer = ResourceDescriptorHeap[tileIndexBufferIndex];
|
||||||
|
|
||||||
|
InterlockedAdd(counterBuffer[0].particleCount, tileBuffer[tileIndex].particleCount, tileBuffer[tileIndex].firstParticle);
|
||||||
|
uint tileIndexIndex;
|
||||||
|
InterlockedAdd(counterBuffer[0].tileCount, 1, tileIndexIndex);
|
||||||
|
tileIndexBuffer[tileIndexIndex] = tileIndex;
|
||||||
|
}
|
|
@ -337,6 +337,7 @@ void RE_BeginFrame( stereoFrame_t stereoFrame )
|
||||||
tr.renderMode = RM_NONE;
|
tr.renderMode = RM_NONE;
|
||||||
tr.sceneCounterRT = 0;
|
tr.sceneCounterRT = 0;
|
||||||
tr.numRTSurfs = 0;
|
tr.numRTSurfs = 0;
|
||||||
|
tr.hasWorldRender = qfalse;
|
||||||
|
|
||||||
// delayed screenshot
|
// delayed screenshot
|
||||||
if ( r_delayedScreenshotPending ) {
|
if ( r_delayedScreenshotPending ) {
|
||||||
|
|
|
@ -1008,6 +1008,11 @@ typedef struct {
|
||||||
|
|
||||||
renderMode_t renderMode;
|
renderMode_t renderMode;
|
||||||
|
|
||||||
|
// world rendering info for the current frame
|
||||||
|
qbool hasWorldRender;
|
||||||
|
int worldRenderTimeMS;
|
||||||
|
int worldRenderTimeUS; // [0;999]
|
||||||
|
|
||||||
// the following are only to be used after calling R_UpdateShader()
|
// the following are only to be used after calling R_UpdateShader()
|
||||||
// the save state boolean is needed because otherwise, a delayed shader load
|
// the save state boolean is needed because otherwise, a delayed shader load
|
||||||
// could change the error and warning messages of the edited shader
|
// could change the error and warning messages of the edited shader
|
||||||
|
@ -1669,6 +1674,7 @@ void R_TransposeMatrix( const matrix4x4_t in, matrix4x4_t out );
|
||||||
void R_CameraPositionFromMatrix( const matrix4x4_t modelView, vec3_t cameraPos );
|
void R_CameraPositionFromMatrix( const matrix4x4_t modelView, vec3_t cameraPos );
|
||||||
void R_CameraAxisVectorsFromMatrix( const matrix4x4_t modelView, vec3_t axisX, vec3_t axisY, vec3_t axisZ );
|
void R_CameraAxisVectorsFromMatrix( const matrix4x4_t modelView, vec3_t axisX, vec3_t axisY, vec3_t axisZ );
|
||||||
void R_MakeIdentityMatrix( matrix4x4_t m );
|
void R_MakeIdentityMatrix( matrix4x4_t m );
|
||||||
|
void R_MakeIdentityMatrix3x3( matrix3x3_t m );
|
||||||
void R_MakeOrthoProjectionMatrix( matrix4x4_t m, float w, float h );
|
void R_MakeOrthoProjectionMatrix( matrix4x4_t m, float w, float h );
|
||||||
|
|
||||||
// LinearDepth(depthZW, A, B, C) -> A / (B + depthZW * C)
|
// LinearDepth(depthZW, A, B, C) -> A / (B + depthZW * C)
|
||||||
|
@ -1774,6 +1780,7 @@ struct RHIInfo
|
||||||
qbool isCacheCoherentUMA;
|
qbool isCacheCoherentUMA;
|
||||||
qbool hasInlineRaytracing;
|
qbool hasInlineRaytracing;
|
||||||
qbool hasBarycentrics;
|
qbool hasBarycentrics;
|
||||||
|
qbool forceNanoVDBPreviewMode; // work-around for driver crashes (shader compiler)
|
||||||
};
|
};
|
||||||
|
|
||||||
extern RHIInfo rhiInfo;
|
extern RHIInfo rhiInfo;
|
||||||
|
|
|
@ -456,6 +456,20 @@ void R_MakeIdentityMatrix( matrix4x4_t m )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void R_MakeIdentityMatrix3x3( matrix3x3_t m )
|
||||||
|
{
|
||||||
|
m[0] = 1.0f;
|
||||||
|
m[1] = 0.0f;
|
||||||
|
m[2] = 0.0f;
|
||||||
|
m[3] = 0.0f;
|
||||||
|
m[4] = 1.0f;
|
||||||
|
m[5] = 0.0f;
|
||||||
|
m[6] = 0.0f;
|
||||||
|
m[7] = 0.0f;
|
||||||
|
m[8] = 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void R_MakeOrthoProjectionMatrix( matrix4x4_t m, float w, float h )
|
void R_MakeOrthoProjectionMatrix( matrix4x4_t m, float w, float h )
|
||||||
{
|
{
|
||||||
// 2/(r-l) 0 0 0
|
// 2/(r-l) 0 0 0
|
||||||
|
|
|
@ -292,6 +292,11 @@ void RE_RenderScene( const refdef_t* fd, int us )
|
||||||
if ((tr.refdef.rdflags & RDF_NOWORLDMODEL) == 0) {
|
if ((tr.refdef.rdflags & RDF_NOWORLDMODEL) == 0) {
|
||||||
tr.sceneCounterRT++;
|
tr.sceneCounterRT++;
|
||||||
}
|
}
|
||||||
|
if (!tr.hasWorldRender && (tr.refdef.rdflags & RDF_NOWORLDMODEL) == 0) {
|
||||||
|
tr.hasWorldRender = qtrue;
|
||||||
|
tr.worldRenderTimeMS = tr.refdef.time;
|
||||||
|
tr.worldRenderTimeUS = tr.refdef.microSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
// setup view parms for the initial view
|
// setup view parms for the initial view
|
||||||
//
|
//
|
||||||
|
|
|
@ -268,20 +268,31 @@ void CompilePixelShader(const char* headerPath, const char* shaderPath, const ch
|
||||||
CompileShader(args, psExtraCount, psExtras);
|
CompileShader(args, psExtraCount, psExtras);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompileCompute(const char* headerPath, const char* shaderPath, const char* varName)
|
void CompileCompute(const char* headerPath, const char* shaderPath, const char* varName, int csOptionCount = 0, ...)
|
||||||
{
|
{
|
||||||
const char* extras[] =
|
int csExtraCount = 4;
|
||||||
|
const char* csExtras[64] =
|
||||||
{
|
{
|
||||||
"-D", "COMPUTE_SHADER=1",
|
"-D", "COMPUTE_SHADER=1",
|
||||||
"-Vn", HeaderVariable(va("g_%s_cs", varName))
|
"-Vn", HeaderVariable(va("g_%s_cs", varName))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
assert(csExtraCount + csOptionCount <= _countof(csExtras));
|
||||||
|
|
||||||
|
va_list argPtr;
|
||||||
|
va_start(argPtr, csOptionCount);
|
||||||
|
for(int i = 0; i < csOptionCount; i++)
|
||||||
|
{
|
||||||
|
csExtras[csExtraCount++] = va_arg(argPtr, const char*);
|
||||||
|
}
|
||||||
|
va_end(argPtr);
|
||||||
|
|
||||||
ShaderArgs args;
|
ShaderArgs args;
|
||||||
args.entryPoint = "cs";
|
args.entryPoint = "cs";
|
||||||
args.headerPath = headerPath;
|
args.headerPath = headerPath;
|
||||||
args.shaderPath = shaderPath;
|
args.shaderPath = shaderPath;
|
||||||
args.targetProfile = targetCS;
|
args.targetProfile = targetCS;
|
||||||
CompileShader(args, _countof(extras), extras);
|
CompileShader(args, csExtraCount, csExtras);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompileUberVS(const char* headerPath, const char* shaderPath, int stageCount)
|
void CompileUberVS(const char* headerPath, const char* shaderPath, int stageCount)
|
||||||
|
@ -455,22 +466,23 @@ void ProcessCRP()
|
||||||
CompilePixelShader("sun_blur.h", "sun_blur.hlsl", "sun_blur");
|
CompilePixelShader("sun_blur.h", "sun_blur.hlsl", "sun_blur");
|
||||||
const char* vlComputeShaders[] =
|
const char* vlComputeShaders[] =
|
||||||
{
|
{
|
||||||
#if 0
|
|
||||||
"vl_particles_dispatch",
|
|
||||||
"vl_particles_preprocess_extinction",
|
|
||||||
"vl_particles_preprocess_frustum",
|
|
||||||
"vl_extinction_injection_particles",
|
|
||||||
"vl_frustum_injection_particles",
|
|
||||||
#endif
|
|
||||||
"vl_extinction_injection_fog",
|
"vl_extinction_injection_fog",
|
||||||
|
"vl_extinction_injection_nanovdb",
|
||||||
|
//"vl_extinction_injection_particles",
|
||||||
"vl_frustum_anisotropy_average",
|
"vl_frustum_anisotropy_average",
|
||||||
|
"vl_frustum_depth_test",
|
||||||
"vl_frustum_injection_fog",
|
"vl_frustum_injection_fog",
|
||||||
|
"vl_frustum_injection_nanovdb",
|
||||||
|
"vl_frustum_injection_particles",
|
||||||
"vl_frustum_inscatter_ambient",
|
"vl_frustum_inscatter_ambient",
|
||||||
"vl_frustum_inscatter_point_light",
|
"vl_frustum_inscatter_point_light",
|
||||||
"vl_frustum_inscatter_sunlight",
|
"vl_frustum_inscatter_sunlight",
|
||||||
"vl_frustum_raymarch",
|
"vl_frustum_raymarch",
|
||||||
"vl_frustum_sunlight_visibility",
|
"vl_frustum_sunlight_visibility",
|
||||||
"vl_frustum_temporal",
|
"vl_particles_clear",
|
||||||
|
"vl_particles_hit",
|
||||||
|
"vl_particles_list",
|
||||||
|
"vl_particles_tiles",
|
||||||
"vl_shadow_point_light",
|
"vl_shadow_point_light",
|
||||||
"vl_shadow_sun"
|
"vl_shadow_sun"
|
||||||
};
|
};
|
||||||
|
@ -479,10 +491,21 @@ void ProcessCRP()
|
||||||
const char* const s = vlComputeShaders[i];
|
const char* const s = vlComputeShaders[i];
|
||||||
CompileCompute(va("%s.h", s), va("%s.hlsl", s), s);
|
CompileCompute(va("%s.h", s), va("%s.hlsl", s), s);
|
||||||
}
|
}
|
||||||
|
CompileCompute("vl_frustum_temporal_float4.h", "vl_frustum_temporal.hlsl", "vl_frustum_temporal_float4", 1, "-D TYPE_FLOAT4=1");
|
||||||
|
CompileCompute("vl_frustum_temporal_float.h", "vl_frustum_temporal.hlsl", "vl_frustum_temporal_float", 1, "-D TYPE_FLOAT=1");
|
||||||
|
CompileCompute("vl_frustum_injection_nanovdb_lq.h", "vl_frustum_injection_nanovdb.hlsl", "vl_frustum_injection_nanovdb_lq", 1, "-D PREVIEW_MODE=1");
|
||||||
|
CompileCompute("vl_frustum_light_propagation_nx.h", "vl_frustum_light_propagation.hlsl", "vl_frustum_light_propagation_nx", 1, "-D DIRECTION_NX=1");
|
||||||
|
CompileCompute("vl_frustum_light_propagation_ny.h", "vl_frustum_light_propagation.hlsl", "vl_frustum_light_propagation_ny", 1, "-D DIRECTION_NY=1");
|
||||||
|
CompileCompute("vl_frustum_light_propagation_px.h", "vl_frustum_light_propagation.hlsl", "vl_frustum_light_propagation_px", 1, "-D DIRECTION_PX=1");
|
||||||
|
CompileCompute("vl_frustum_light_propagation_py.h", "vl_frustum_light_propagation.hlsl", "vl_frustum_light_propagation_py", 1, "-D DIRECTION_PY=1");
|
||||||
CompileGraphics("vl_debug_ambient.h", "vl_debug_ambient.hlsl", "vl_debug_ambient");
|
CompileGraphics("vl_debug_ambient.h", "vl_debug_ambient.hlsl", "vl_debug_ambient");
|
||||||
CompileGraphics("vl_debug_extinction.h", "vl_debug_extinction.hlsl", "vl_debug_extinction");
|
CompileGraphics("vl_debug_extinction.h", "vl_debug_extinction.hlsl", "vl_debug_extinction");
|
||||||
CompileGraphics("vl_debug_shadow_sun.h", "vl_debug_shadow_sun.hlsl", "vl_debug_shadow_sun");
|
CompileGraphics("vl_debug_shadow_sun.h", "vl_debug_shadow_sun.hlsl", "vl_debug_shadow_sun");
|
||||||
CompileCompute("depth_pyramid.h", "depth_pyramid.hlsl", "depth_pyramid");
|
CompileCompute("depth_pyramid.h", "depth_pyramid.hlsl", "depth_pyramid");
|
||||||
|
CompileCompute("particles_clear.h", "particles_clear.hlsl", "particles_clear");
|
||||||
|
CompileCompute("particles_setup.h", "particles_setup.hlsl", "particles_setup");
|
||||||
|
CompileCompute("particles_emit.h", "particles_emit.hlsl", "particles_emit");
|
||||||
|
CompileCompute("particles_simulate.h", "particles_simulate.hlsl", "particles_simulate");
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int /*argc*/, const char** argv)
|
int main(int /*argc*/, const char** argv)
|
||||||
|
|
|
@ -650,6 +650,7 @@ solution "cnq3"
|
||||||
kind "StaticLib"
|
kind "StaticLib"
|
||||||
language "C++"
|
language "C++"
|
||||||
AddSourcesAndHeaders("renderer")
|
AddSourcesAndHeaders("renderer")
|
||||||
|
includedirs { string.format("%s", path_src) }
|
||||||
if os.istarget("bsd") then
|
if os.istarget("bsd") then
|
||||||
includedirs { "/usr/local/include" }
|
includedirs { "/usr/local/include" }
|
||||||
end
|
end
|
||||||
|
@ -668,6 +669,7 @@ solution "cnq3"
|
||||||
flags { "ExcludeFromBuild" }
|
flags { "ExcludeFromBuild" }
|
||||||
filter { }
|
filter { }
|
||||||
end
|
end
|
||||||
|
files { string.format("%s/renderer/shaders/**.h", path_src) }
|
||||||
ApplyLibProjectSettings()
|
ApplyLibProjectSettings()
|
||||||
includedirs { path_src.."/imgui" }
|
includedirs { path_src.."/imgui" }
|
||||||
filter "action:gmake"
|
filter "action:gmake"
|
||||||
|
|
|
@ -59,7 +59,7 @@
|
||||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||||
<WarningLevel>Level4</WarningLevel>
|
<WarningLevel>Level4</WarningLevel>
|
||||||
<PreprocessorDefinitions>DEBUG;_DEBUG;_CRT_SECURE_NO_WARNINGS;WIN32;_WIN32;_HAS_EXCEPTIONS=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>DEBUG;_DEBUG;_CRT_SECURE_NO_WARNINGS;WIN32;_WIN32;_HAS_EXCEPTIONS=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<AdditionalIncludeDirectories>..\..\..\cnq3tools\aftermath;..\..\..\cnq3tools\nvapi;..\..\code\imgui;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>..\..\code;..\..\..\cnq3tools\aftermath;..\..\..\cnq3tools\nvapi;..\..\code\imgui;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||||
<Optimization>Disabled</Optimization>
|
<Optimization>Disabled</Optimization>
|
||||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||||
|
@ -83,7 +83,7 @@
|
||||||
<WarningLevel>Level4</WarningLevel>
|
<WarningLevel>Level4</WarningLevel>
|
||||||
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
|
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
|
||||||
<PreprocessorDefinitions>NDEBUG;_CRT_SECURE_NO_WARNINGS;WIN32;_WIN32;_HAS_EXCEPTIONS=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>NDEBUG;_CRT_SECURE_NO_WARNINGS;WIN32;_WIN32;_HAS_EXCEPTIONS=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<AdditionalIncludeDirectories>..\..\..\cnq3tools\aftermath;..\..\..\cnq3tools\nvapi;..\..\code\imgui;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>..\..\code;..\..\..\cnq3tools\aftermath;..\..\..\cnq3tools\nvapi;..\..\code\imgui;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||||
<Optimization>MinSpace</Optimization>
|
<Optimization>MinSpace</Optimization>
|
||||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
@ -117,6 +117,7 @@
|
||||||
<ClInclude Include="..\..\code\renderer\grp_uber_shaders.h" />
|
<ClInclude Include="..\..\code\renderer\grp_uber_shaders.h" />
|
||||||
<ClInclude Include="..\..\code\renderer\rhi_local.h" />
|
<ClInclude Include="..\..\code\renderer\rhi_local.h" />
|
||||||
<ClInclude Include="..\..\code\renderer\rhi_public.h" />
|
<ClInclude Include="..\..\code\renderer\rhi_public.h" />
|
||||||
|
<ClInclude Include="..\..\code\renderer\shaders\crp\PNanoVDB.h" />
|
||||||
<ClInclude Include="..\..\code\renderer\smaa_area_texture.h" />
|
<ClInclude Include="..\..\code\renderer\smaa_area_texture.h" />
|
||||||
<ClInclude Include="..\..\code\renderer\smaa_search_texture.h" />
|
<ClInclude Include="..\..\code\renderer\smaa_search_texture.h" />
|
||||||
<ClInclude Include="..\..\code\renderer\srp_local.h" />
|
<ClInclude Include="..\..\code\renderer\srp_local.h" />
|
||||||
|
@ -135,7 +136,9 @@
|
||||||
<ClCompile Include="..\..\code\renderer\crp_magnifier.cpp" />
|
<ClCompile Include="..\..\code\renderer\crp_magnifier.cpp" />
|
||||||
<ClCompile Include="..\..\code\renderer\crp_main.cpp" />
|
<ClCompile Include="..\..\code\renderer\crp_main.cpp" />
|
||||||
<ClCompile Include="..\..\code\renderer\crp_motion_blur.cpp" />
|
<ClCompile Include="..\..\code\renderer\crp_motion_blur.cpp" />
|
||||||
|
<ClCompile Include="..\..\code\renderer\crp_nano_vdb.cpp" />
|
||||||
<ClCompile Include="..\..\code\renderer\crp_opaque.cpp" />
|
<ClCompile Include="..\..\code\renderer\crp_opaque.cpp" />
|
||||||
|
<ClCompile Include="..\..\code\renderer\crp_particles.cpp" />
|
||||||
<ClCompile Include="..\..\code\renderer\crp_prepass.cpp" />
|
<ClCompile Include="..\..\code\renderer\crp_prepass.cpp" />
|
||||||
<ClCompile Include="..\..\code\renderer\crp_raytracing.cpp" />
|
<ClCompile Include="..\..\code\renderer\crp_raytracing.cpp" />
|
||||||
<ClCompile Include="..\..\code\renderer\crp_sun_editor.cpp" />
|
<ClCompile Include="..\..\code\renderer\crp_sun_editor.cpp" />
|
||||||
|
@ -273,6 +276,18 @@
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\opaque.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\opaque.hlsl">
|
||||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
|
<FxCompile Include="..\..\code\renderer\shaders\crp\particles_clear.hlsl">
|
||||||
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
|
</FxCompile>
|
||||||
|
<FxCompile Include="..\..\code\renderer\shaders\crp\particles_emit.hlsl">
|
||||||
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
|
</FxCompile>
|
||||||
|
<FxCompile Include="..\..\code\renderer\shaders\crp\particles_setup.hlsl">
|
||||||
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
|
</FxCompile>
|
||||||
|
<FxCompile Include="..\..\code\renderer\shaders\crp\particles_simulate.hlsl">
|
||||||
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
|
</FxCompile>
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\prepass.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\prepass.hlsl">
|
||||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
|
@ -315,15 +330,21 @@
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_extinction_injection_fog.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_extinction_injection_fog.hlsl">
|
||||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_extinction_injection_particles.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_extinction_injection_nanovdb.hlsl">
|
||||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_anisotropy_average.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_anisotropy_average.hlsl">
|
||||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_depth_test.hlsl">
|
||||||
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
|
</FxCompile>
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_injection_fog.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_injection_fog.hlsl">
|
||||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_injection_nanovdb.hlsl">
|
||||||
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
|
</FxCompile>
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_injection_particles.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_injection_particles.hlsl">
|
||||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
|
@ -336,6 +357,9 @@
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_inscatter_sunlight.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_inscatter_sunlight.hlsl">
|
||||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_light_propagation.hlsl">
|
||||||
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
|
</FxCompile>
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_raymarch.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_raymarch.hlsl">
|
||||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
|
@ -345,13 +369,16 @@
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_temporal.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_temporal.hlsl">
|
||||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_particles_dispatch.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_particles_clear.hlsl">
|
||||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_particles_preprocess_extinction.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_particles_hit.hlsl">
|
||||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_particles_preprocess_frustum.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_particles_list.hlsl">
|
||||||
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
|
</FxCompile>
|
||||||
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_particles_tiles.hlsl">
|
||||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_shadow_point_light.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_shadow_point_light.hlsl">
|
||||||
|
@ -421,12 +448,14 @@
|
||||||
<None Include="..\..\code\renderer\shaders\crp\dof.hlsli" />
|
<None Include="..\..\code\renderer\shaders\crp\dof.hlsli" />
|
||||||
<None Include="..\..\code\renderer\shaders\crp\fullscreen.hlsli" />
|
<None Include="..\..\code\renderer\shaders\crp\fullscreen.hlsli" />
|
||||||
<None Include="..\..\code\renderer\shaders\crp\gatherdof.hlsli" />
|
<None Include="..\..\code\renderer\shaders\crp\gatherdof.hlsli" />
|
||||||
|
<None Include="..\..\code\renderer\shaders\crp\light_grid.h.hlsli" />
|
||||||
<None Include="..\..\code\renderer\shaders\crp\oit.h.hlsli" />
|
<None Include="..\..\code\renderer\shaders\crp\oit.h.hlsli" />
|
||||||
<None Include="..\..\code\renderer\shaders\crp\raytracing.h.hlsli" />
|
<None Include="..\..\code\renderer\shaders\crp\raytracing.h.hlsli" />
|
||||||
<None Include="..\..\code\renderer\shaders\crp\scene_view.h.hlsli" />
|
<None Include="..\..\code\renderer\shaders\crp\scene_view.h.hlsli" />
|
||||||
<None Include="..\..\code\renderer\shaders\crp\simplex_noise.hlsli" />
|
<None Include="..\..\code\renderer\shaders\crp\simplex_noise.hlsli" />
|
||||||
<None Include="..\..\code\renderer\shaders\crp\typedefs.h.hlsli" />
|
<None Include="..\..\code\renderer\shaders\crp\typedefs.h.hlsli" />
|
||||||
<None Include="..\..\code\renderer\shaders\crp\vl_common.h.hlsli" />
|
<None Include="..\..\code\renderer\shaders\crp\vl_common.h.hlsli" />
|
||||||
|
<None Include="..\..\code\renderer\shaders\crp\vl_nanovdb.hlsli" />
|
||||||
<None Include="..\..\code\renderer\shaders\grp\fog.hlsli" />
|
<None Include="..\..\code\renderer\shaders\grp\fog.hlsli" />
|
||||||
<None Include="..\..\code\renderer\shaders\grp\shared.hlsli" />
|
<None Include="..\..\code\renderer\shaders\grp\shared.hlsli" />
|
||||||
<None Include="..\..\code\renderer\shaders\grp\smaa.hlsli" />
|
<None Include="..\..\code\renderer\shaders\grp\smaa.hlsli" />
|
||||||
|
|
|
@ -21,6 +21,9 @@
|
||||||
<ClInclude Include="..\..\code\renderer\grp_uber_shaders.h" />
|
<ClInclude Include="..\..\code\renderer\grp_uber_shaders.h" />
|
||||||
<ClInclude Include="..\..\code\renderer\rhi_local.h" />
|
<ClInclude Include="..\..\code\renderer\rhi_local.h" />
|
||||||
<ClInclude Include="..\..\code\renderer\rhi_public.h" />
|
<ClInclude Include="..\..\code\renderer\rhi_public.h" />
|
||||||
|
<ClInclude Include="..\..\code\renderer\shaders\crp\PNanoVDB.h">
|
||||||
|
<Filter>shaders\crp</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\code\renderer\smaa_area_texture.h" />
|
<ClInclude Include="..\..\code\renderer\smaa_area_texture.h" />
|
||||||
<ClInclude Include="..\..\code\renderer\smaa_search_texture.h" />
|
<ClInclude Include="..\..\code\renderer\smaa_search_texture.h" />
|
||||||
<ClInclude Include="..\..\code\renderer\srp_local.h" />
|
<ClInclude Include="..\..\code\renderer\srp_local.h" />
|
||||||
|
@ -39,7 +42,9 @@
|
||||||
<ClCompile Include="..\..\code\renderer\crp_magnifier.cpp" />
|
<ClCompile Include="..\..\code\renderer\crp_magnifier.cpp" />
|
||||||
<ClCompile Include="..\..\code\renderer\crp_main.cpp" />
|
<ClCompile Include="..\..\code\renderer\crp_main.cpp" />
|
||||||
<ClCompile Include="..\..\code\renderer\crp_motion_blur.cpp" />
|
<ClCompile Include="..\..\code\renderer\crp_motion_blur.cpp" />
|
||||||
|
<ClCompile Include="..\..\code\renderer\crp_nano_vdb.cpp" />
|
||||||
<ClCompile Include="..\..\code\renderer\crp_opaque.cpp" />
|
<ClCompile Include="..\..\code\renderer\crp_opaque.cpp" />
|
||||||
|
<ClCompile Include="..\..\code\renderer\crp_particles.cpp" />
|
||||||
<ClCompile Include="..\..\code\renderer\crp_prepass.cpp" />
|
<ClCompile Include="..\..\code\renderer\crp_prepass.cpp" />
|
||||||
<ClCompile Include="..\..\code\renderer\crp_raytracing.cpp" />
|
<ClCompile Include="..\..\code\renderer\crp_raytracing.cpp" />
|
||||||
<ClCompile Include="..\..\code\renderer\crp_sun_editor.cpp" />
|
<ClCompile Include="..\..\code\renderer\crp_sun_editor.cpp" />
|
||||||
|
@ -177,6 +182,18 @@
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\opaque.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\opaque.hlsl">
|
||||||
<Filter>shaders\crp</Filter>
|
<Filter>shaders\crp</Filter>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
|
<FxCompile Include="..\..\code\renderer\shaders\crp\particles_clear.hlsl">
|
||||||
|
<Filter>shaders\crp</Filter>
|
||||||
|
</FxCompile>
|
||||||
|
<FxCompile Include="..\..\code\renderer\shaders\crp\particles_emit.hlsl">
|
||||||
|
<Filter>shaders\crp</Filter>
|
||||||
|
</FxCompile>
|
||||||
|
<FxCompile Include="..\..\code\renderer\shaders\crp\particles_setup.hlsl">
|
||||||
|
<Filter>shaders\crp</Filter>
|
||||||
|
</FxCompile>
|
||||||
|
<FxCompile Include="..\..\code\renderer\shaders\crp\particles_simulate.hlsl">
|
||||||
|
<Filter>shaders\crp</Filter>
|
||||||
|
</FxCompile>
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\prepass.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\prepass.hlsl">
|
||||||
<Filter>shaders\crp</Filter>
|
<Filter>shaders\crp</Filter>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
|
@ -219,15 +236,21 @@
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_extinction_injection_fog.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_extinction_injection_fog.hlsl">
|
||||||
<Filter>shaders\crp</Filter>
|
<Filter>shaders\crp</Filter>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_extinction_injection_particles.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_extinction_injection_nanovdb.hlsl">
|
||||||
<Filter>shaders\crp</Filter>
|
<Filter>shaders\crp</Filter>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_anisotropy_average.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_anisotropy_average.hlsl">
|
||||||
<Filter>shaders\crp</Filter>
|
<Filter>shaders\crp</Filter>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_depth_test.hlsl">
|
||||||
|
<Filter>shaders\crp</Filter>
|
||||||
|
</FxCompile>
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_injection_fog.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_injection_fog.hlsl">
|
||||||
<Filter>shaders\crp</Filter>
|
<Filter>shaders\crp</Filter>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_injection_nanovdb.hlsl">
|
||||||
|
<Filter>shaders\crp</Filter>
|
||||||
|
</FxCompile>
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_injection_particles.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_injection_particles.hlsl">
|
||||||
<Filter>shaders\crp</Filter>
|
<Filter>shaders\crp</Filter>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
|
@ -240,6 +263,9 @@
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_inscatter_sunlight.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_inscatter_sunlight.hlsl">
|
||||||
<Filter>shaders\crp</Filter>
|
<Filter>shaders\crp</Filter>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_light_propagation.hlsl">
|
||||||
|
<Filter>shaders\crp</Filter>
|
||||||
|
</FxCompile>
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_raymarch.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_raymarch.hlsl">
|
||||||
<Filter>shaders\crp</Filter>
|
<Filter>shaders\crp</Filter>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
|
@ -249,13 +275,16 @@
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_temporal.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_temporal.hlsl">
|
||||||
<Filter>shaders\crp</Filter>
|
<Filter>shaders\crp</Filter>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_particles_dispatch.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_particles_clear.hlsl">
|
||||||
<Filter>shaders\crp</Filter>
|
<Filter>shaders\crp</Filter>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_particles_preprocess_extinction.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_particles_hit.hlsl">
|
||||||
<Filter>shaders\crp</Filter>
|
<Filter>shaders\crp</Filter>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_particles_preprocess_frustum.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_particles_list.hlsl">
|
||||||
|
<Filter>shaders\crp</Filter>
|
||||||
|
</FxCompile>
|
||||||
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_particles_tiles.hlsl">
|
||||||
<Filter>shaders\crp</Filter>
|
<Filter>shaders\crp</Filter>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_shadow_point_light.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_shadow_point_light.hlsl">
|
||||||
|
@ -341,6 +370,9 @@
|
||||||
<None Include="..\..\code\renderer\shaders\crp\gatherdof.hlsli">
|
<None Include="..\..\code\renderer\shaders\crp\gatherdof.hlsli">
|
||||||
<Filter>shaders\crp</Filter>
|
<Filter>shaders\crp</Filter>
|
||||||
</None>
|
</None>
|
||||||
|
<None Include="..\..\code\renderer\shaders\crp\light_grid.h.hlsli">
|
||||||
|
<Filter>shaders\crp</Filter>
|
||||||
|
</None>
|
||||||
<None Include="..\..\code\renderer\shaders\crp\oit.h.hlsli">
|
<None Include="..\..\code\renderer\shaders\crp\oit.h.hlsli">
|
||||||
<Filter>shaders\crp</Filter>
|
<Filter>shaders\crp</Filter>
|
||||||
</None>
|
</None>
|
||||||
|
@ -359,6 +391,9 @@
|
||||||
<None Include="..\..\code\renderer\shaders\crp\vl_common.h.hlsli">
|
<None Include="..\..\code\renderer\shaders\crp\vl_common.h.hlsli">
|
||||||
<Filter>shaders\crp</Filter>
|
<Filter>shaders\crp</Filter>
|
||||||
</None>
|
</None>
|
||||||
|
<None Include="..\..\code\renderer\shaders\crp\vl_nanovdb.hlsli">
|
||||||
|
<Filter>shaders\crp</Filter>
|
||||||
|
</None>
|
||||||
<None Include="..\..\code\renderer\shaders\grp\fog.hlsli">
|
<None Include="..\..\code\renderer\shaders\grp\fog.hlsli">
|
||||||
<Filter>shaders\grp</Filter>
|
<Filter>shaders\grp</Filter>
|
||||||
</None>
|
</None>
|
||||||
|
|
|
@ -59,7 +59,7 @@
|
||||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||||
<WarningLevel>Level4</WarningLevel>
|
<WarningLevel>Level4</WarningLevel>
|
||||||
<PreprocessorDefinitions>DEBUG;_DEBUG;_CRT_SECURE_NO_WARNINGS;WIN32;_WIN32;_HAS_EXCEPTIONS=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>DEBUG;_DEBUG;_CRT_SECURE_NO_WARNINGS;WIN32;_WIN32;_HAS_EXCEPTIONS=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<AdditionalIncludeDirectories>..\..\..\cnq3tools\aftermath;..\..\..\cnq3tools\nvapi;..\..\code\imgui;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>..\..\code;..\..\..\cnq3tools\aftermath;..\..\..\cnq3tools\nvapi;..\..\code\imgui;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||||
<Optimization>Disabled</Optimization>
|
<Optimization>Disabled</Optimization>
|
||||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||||
|
@ -84,7 +84,7 @@
|
||||||
<WarningLevel>Level4</WarningLevel>
|
<WarningLevel>Level4</WarningLevel>
|
||||||
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
|
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
|
||||||
<PreprocessorDefinitions>NDEBUG;_CRT_SECURE_NO_WARNINGS;WIN32;_WIN32;_HAS_EXCEPTIONS=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>NDEBUG;_CRT_SECURE_NO_WARNINGS;WIN32;_WIN32;_HAS_EXCEPTIONS=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<AdditionalIncludeDirectories>..\..\..\cnq3tools\aftermath;..\..\..\cnq3tools\nvapi;..\..\code\imgui;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>..\..\code;..\..\..\cnq3tools\aftermath;..\..\..\cnq3tools\nvapi;..\..\code\imgui;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||||
<Optimization>MinSpace</Optimization>
|
<Optimization>MinSpace</Optimization>
|
||||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
@ -119,6 +119,7 @@
|
||||||
<ClInclude Include="..\..\code\renderer\grp_uber_shaders.h" />
|
<ClInclude Include="..\..\code\renderer\grp_uber_shaders.h" />
|
||||||
<ClInclude Include="..\..\code\renderer\rhi_local.h" />
|
<ClInclude Include="..\..\code\renderer\rhi_local.h" />
|
||||||
<ClInclude Include="..\..\code\renderer\rhi_public.h" />
|
<ClInclude Include="..\..\code\renderer\rhi_public.h" />
|
||||||
|
<ClInclude Include="..\..\code\renderer\shaders\crp\PNanoVDB.h" />
|
||||||
<ClInclude Include="..\..\code\renderer\smaa_area_texture.h" />
|
<ClInclude Include="..\..\code\renderer\smaa_area_texture.h" />
|
||||||
<ClInclude Include="..\..\code\renderer\smaa_search_texture.h" />
|
<ClInclude Include="..\..\code\renderer\smaa_search_texture.h" />
|
||||||
<ClInclude Include="..\..\code\renderer\srp_local.h" />
|
<ClInclude Include="..\..\code\renderer\srp_local.h" />
|
||||||
|
@ -137,7 +138,9 @@
|
||||||
<ClCompile Include="..\..\code\renderer\crp_magnifier.cpp" />
|
<ClCompile Include="..\..\code\renderer\crp_magnifier.cpp" />
|
||||||
<ClCompile Include="..\..\code\renderer\crp_main.cpp" />
|
<ClCompile Include="..\..\code\renderer\crp_main.cpp" />
|
||||||
<ClCompile Include="..\..\code\renderer\crp_motion_blur.cpp" />
|
<ClCompile Include="..\..\code\renderer\crp_motion_blur.cpp" />
|
||||||
|
<ClCompile Include="..\..\code\renderer\crp_nano_vdb.cpp" />
|
||||||
<ClCompile Include="..\..\code\renderer\crp_opaque.cpp" />
|
<ClCompile Include="..\..\code\renderer\crp_opaque.cpp" />
|
||||||
|
<ClCompile Include="..\..\code\renderer\crp_particles.cpp" />
|
||||||
<ClCompile Include="..\..\code\renderer\crp_prepass.cpp" />
|
<ClCompile Include="..\..\code\renderer\crp_prepass.cpp" />
|
||||||
<ClCompile Include="..\..\code\renderer\crp_raytracing.cpp" />
|
<ClCompile Include="..\..\code\renderer\crp_raytracing.cpp" />
|
||||||
<ClCompile Include="..\..\code\renderer\crp_sun_editor.cpp" />
|
<ClCompile Include="..\..\code\renderer\crp_sun_editor.cpp" />
|
||||||
|
@ -275,6 +278,18 @@
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\opaque.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\opaque.hlsl">
|
||||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
|
<FxCompile Include="..\..\code\renderer\shaders\crp\particles_clear.hlsl">
|
||||||
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
|
</FxCompile>
|
||||||
|
<FxCompile Include="..\..\code\renderer\shaders\crp\particles_emit.hlsl">
|
||||||
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
|
</FxCompile>
|
||||||
|
<FxCompile Include="..\..\code\renderer\shaders\crp\particles_setup.hlsl">
|
||||||
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
|
</FxCompile>
|
||||||
|
<FxCompile Include="..\..\code\renderer\shaders\crp\particles_simulate.hlsl">
|
||||||
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
|
</FxCompile>
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\prepass.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\prepass.hlsl">
|
||||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
|
@ -317,15 +332,21 @@
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_extinction_injection_fog.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_extinction_injection_fog.hlsl">
|
||||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_extinction_injection_particles.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_extinction_injection_nanovdb.hlsl">
|
||||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_anisotropy_average.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_anisotropy_average.hlsl">
|
||||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_depth_test.hlsl">
|
||||||
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
|
</FxCompile>
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_injection_fog.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_injection_fog.hlsl">
|
||||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_injection_nanovdb.hlsl">
|
||||||
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
|
</FxCompile>
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_injection_particles.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_injection_particles.hlsl">
|
||||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
|
@ -338,6 +359,9 @@
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_inscatter_sunlight.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_inscatter_sunlight.hlsl">
|
||||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_light_propagation.hlsl">
|
||||||
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
|
</FxCompile>
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_raymarch.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_raymarch.hlsl">
|
||||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
|
@ -347,13 +371,16 @@
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_temporal.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_temporal.hlsl">
|
||||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_particles_dispatch.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_particles_clear.hlsl">
|
||||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_particles_preprocess_extinction.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_particles_hit.hlsl">
|
||||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_particles_preprocess_frustum.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_particles_list.hlsl">
|
||||||
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
|
</FxCompile>
|
||||||
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_particles_tiles.hlsl">
|
||||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_shadow_point_light.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_shadow_point_light.hlsl">
|
||||||
|
@ -423,12 +450,14 @@
|
||||||
<None Include="..\..\code\renderer\shaders\crp\dof.hlsli" />
|
<None Include="..\..\code\renderer\shaders\crp\dof.hlsli" />
|
||||||
<None Include="..\..\code\renderer\shaders\crp\fullscreen.hlsli" />
|
<None Include="..\..\code\renderer\shaders\crp\fullscreen.hlsli" />
|
||||||
<None Include="..\..\code\renderer\shaders\crp\gatherdof.hlsli" />
|
<None Include="..\..\code\renderer\shaders\crp\gatherdof.hlsli" />
|
||||||
|
<None Include="..\..\code\renderer\shaders\crp\light_grid.h.hlsli" />
|
||||||
<None Include="..\..\code\renderer\shaders\crp\oit.h.hlsli" />
|
<None Include="..\..\code\renderer\shaders\crp\oit.h.hlsli" />
|
||||||
<None Include="..\..\code\renderer\shaders\crp\raytracing.h.hlsli" />
|
<None Include="..\..\code\renderer\shaders\crp\raytracing.h.hlsli" />
|
||||||
<None Include="..\..\code\renderer\shaders\crp\scene_view.h.hlsli" />
|
<None Include="..\..\code\renderer\shaders\crp\scene_view.h.hlsli" />
|
||||||
<None Include="..\..\code\renderer\shaders\crp\simplex_noise.hlsli" />
|
<None Include="..\..\code\renderer\shaders\crp\simplex_noise.hlsli" />
|
||||||
<None Include="..\..\code\renderer\shaders\crp\typedefs.h.hlsli" />
|
<None Include="..\..\code\renderer\shaders\crp\typedefs.h.hlsli" />
|
||||||
<None Include="..\..\code\renderer\shaders\crp\vl_common.h.hlsli" />
|
<None Include="..\..\code\renderer\shaders\crp\vl_common.h.hlsli" />
|
||||||
|
<None Include="..\..\code\renderer\shaders\crp\vl_nanovdb.hlsli" />
|
||||||
<None Include="..\..\code\renderer\shaders\grp\fog.hlsli" />
|
<None Include="..\..\code\renderer\shaders\grp\fog.hlsli" />
|
||||||
<None Include="..\..\code\renderer\shaders\grp\shared.hlsli" />
|
<None Include="..\..\code\renderer\shaders\grp\shared.hlsli" />
|
||||||
<None Include="..\..\code\renderer\shaders\grp\smaa.hlsli" />
|
<None Include="..\..\code\renderer\shaders\grp\smaa.hlsli" />
|
||||||
|
|
|
@ -21,6 +21,9 @@
|
||||||
<ClInclude Include="..\..\code\renderer\grp_uber_shaders.h" />
|
<ClInclude Include="..\..\code\renderer\grp_uber_shaders.h" />
|
||||||
<ClInclude Include="..\..\code\renderer\rhi_local.h" />
|
<ClInclude Include="..\..\code\renderer\rhi_local.h" />
|
||||||
<ClInclude Include="..\..\code\renderer\rhi_public.h" />
|
<ClInclude Include="..\..\code\renderer\rhi_public.h" />
|
||||||
|
<ClInclude Include="..\..\code\renderer\shaders\crp\PNanoVDB.h">
|
||||||
|
<Filter>shaders\crp</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\code\renderer\smaa_area_texture.h" />
|
<ClInclude Include="..\..\code\renderer\smaa_area_texture.h" />
|
||||||
<ClInclude Include="..\..\code\renderer\smaa_search_texture.h" />
|
<ClInclude Include="..\..\code\renderer\smaa_search_texture.h" />
|
||||||
<ClInclude Include="..\..\code\renderer\srp_local.h" />
|
<ClInclude Include="..\..\code\renderer\srp_local.h" />
|
||||||
|
@ -39,7 +42,9 @@
|
||||||
<ClCompile Include="..\..\code\renderer\crp_magnifier.cpp" />
|
<ClCompile Include="..\..\code\renderer\crp_magnifier.cpp" />
|
||||||
<ClCompile Include="..\..\code\renderer\crp_main.cpp" />
|
<ClCompile Include="..\..\code\renderer\crp_main.cpp" />
|
||||||
<ClCompile Include="..\..\code\renderer\crp_motion_blur.cpp" />
|
<ClCompile Include="..\..\code\renderer\crp_motion_blur.cpp" />
|
||||||
|
<ClCompile Include="..\..\code\renderer\crp_nano_vdb.cpp" />
|
||||||
<ClCompile Include="..\..\code\renderer\crp_opaque.cpp" />
|
<ClCompile Include="..\..\code\renderer\crp_opaque.cpp" />
|
||||||
|
<ClCompile Include="..\..\code\renderer\crp_particles.cpp" />
|
||||||
<ClCompile Include="..\..\code\renderer\crp_prepass.cpp" />
|
<ClCompile Include="..\..\code\renderer\crp_prepass.cpp" />
|
||||||
<ClCompile Include="..\..\code\renderer\crp_raytracing.cpp" />
|
<ClCompile Include="..\..\code\renderer\crp_raytracing.cpp" />
|
||||||
<ClCompile Include="..\..\code\renderer\crp_sun_editor.cpp" />
|
<ClCompile Include="..\..\code\renderer\crp_sun_editor.cpp" />
|
||||||
|
@ -177,6 +182,18 @@
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\opaque.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\opaque.hlsl">
|
||||||
<Filter>shaders\crp</Filter>
|
<Filter>shaders\crp</Filter>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
|
<FxCompile Include="..\..\code\renderer\shaders\crp\particles_clear.hlsl">
|
||||||
|
<Filter>shaders\crp</Filter>
|
||||||
|
</FxCompile>
|
||||||
|
<FxCompile Include="..\..\code\renderer\shaders\crp\particles_emit.hlsl">
|
||||||
|
<Filter>shaders\crp</Filter>
|
||||||
|
</FxCompile>
|
||||||
|
<FxCompile Include="..\..\code\renderer\shaders\crp\particles_setup.hlsl">
|
||||||
|
<Filter>shaders\crp</Filter>
|
||||||
|
</FxCompile>
|
||||||
|
<FxCompile Include="..\..\code\renderer\shaders\crp\particles_simulate.hlsl">
|
||||||
|
<Filter>shaders\crp</Filter>
|
||||||
|
</FxCompile>
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\prepass.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\prepass.hlsl">
|
||||||
<Filter>shaders\crp</Filter>
|
<Filter>shaders\crp</Filter>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
|
@ -219,15 +236,21 @@
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_extinction_injection_fog.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_extinction_injection_fog.hlsl">
|
||||||
<Filter>shaders\crp</Filter>
|
<Filter>shaders\crp</Filter>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_extinction_injection_particles.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_extinction_injection_nanovdb.hlsl">
|
||||||
<Filter>shaders\crp</Filter>
|
<Filter>shaders\crp</Filter>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_anisotropy_average.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_anisotropy_average.hlsl">
|
||||||
<Filter>shaders\crp</Filter>
|
<Filter>shaders\crp</Filter>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_depth_test.hlsl">
|
||||||
|
<Filter>shaders\crp</Filter>
|
||||||
|
</FxCompile>
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_injection_fog.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_injection_fog.hlsl">
|
||||||
<Filter>shaders\crp</Filter>
|
<Filter>shaders\crp</Filter>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_injection_nanovdb.hlsl">
|
||||||
|
<Filter>shaders\crp</Filter>
|
||||||
|
</FxCompile>
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_injection_particles.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_injection_particles.hlsl">
|
||||||
<Filter>shaders\crp</Filter>
|
<Filter>shaders\crp</Filter>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
|
@ -240,6 +263,9 @@
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_inscatter_sunlight.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_inscatter_sunlight.hlsl">
|
||||||
<Filter>shaders\crp</Filter>
|
<Filter>shaders\crp</Filter>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_light_propagation.hlsl">
|
||||||
|
<Filter>shaders\crp</Filter>
|
||||||
|
</FxCompile>
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_raymarch.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_raymarch.hlsl">
|
||||||
<Filter>shaders\crp</Filter>
|
<Filter>shaders\crp</Filter>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
|
@ -249,13 +275,16 @@
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_temporal.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_frustum_temporal.hlsl">
|
||||||
<Filter>shaders\crp</Filter>
|
<Filter>shaders\crp</Filter>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_particles_dispatch.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_particles_clear.hlsl">
|
||||||
<Filter>shaders\crp</Filter>
|
<Filter>shaders\crp</Filter>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_particles_preprocess_extinction.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_particles_hit.hlsl">
|
||||||
<Filter>shaders\crp</Filter>
|
<Filter>shaders\crp</Filter>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_particles_preprocess_frustum.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_particles_list.hlsl">
|
||||||
|
<Filter>shaders\crp</Filter>
|
||||||
|
</FxCompile>
|
||||||
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_particles_tiles.hlsl">
|
||||||
<Filter>shaders\crp</Filter>
|
<Filter>shaders\crp</Filter>
|
||||||
</FxCompile>
|
</FxCompile>
|
||||||
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_shadow_point_light.hlsl">
|
<FxCompile Include="..\..\code\renderer\shaders\crp\vl_shadow_point_light.hlsl">
|
||||||
|
@ -341,6 +370,9 @@
|
||||||
<None Include="..\..\code\renderer\shaders\crp\gatherdof.hlsli">
|
<None Include="..\..\code\renderer\shaders\crp\gatherdof.hlsli">
|
||||||
<Filter>shaders\crp</Filter>
|
<Filter>shaders\crp</Filter>
|
||||||
</None>
|
</None>
|
||||||
|
<None Include="..\..\code\renderer\shaders\crp\light_grid.h.hlsli">
|
||||||
|
<Filter>shaders\crp</Filter>
|
||||||
|
</None>
|
||||||
<None Include="..\..\code\renderer\shaders\crp\oit.h.hlsli">
|
<None Include="..\..\code\renderer\shaders\crp\oit.h.hlsli">
|
||||||
<Filter>shaders\crp</Filter>
|
<Filter>shaders\crp</Filter>
|
||||||
</None>
|
</None>
|
||||||
|
@ -359,6 +391,9 @@
|
||||||
<None Include="..\..\code\renderer\shaders\crp\vl_common.h.hlsli">
|
<None Include="..\..\code\renderer\shaders\crp\vl_common.h.hlsli">
|
||||||
<Filter>shaders\crp</Filter>
|
<Filter>shaders\crp</Filter>
|
||||||
</None>
|
</None>
|
||||||
|
<None Include="..\..\code\renderer\shaders\crp\vl_nanovdb.hlsli">
|
||||||
|
<Filter>shaders\crp</Filter>
|
||||||
|
</None>
|
||||||
<None Include="..\..\code\renderer\shaders\grp\fog.hlsli">
|
<None Include="..\..\code\renderer\shaders\grp\fog.hlsli">
|
||||||
<Filter>shaders\grp</Filter>
|
<Filter>shaders\grp</Filter>
|
||||||
</None>
|
</None>
|
||||||
|
|
Loading…
Reference in New Issue