WuhuIslandTesting/Library/PackageCache/com.unity.render-pipelines.universal@8148.0.7-4/Shaders/Volumetrics/VolumetricBaking.compute
2025-01-07 02:06:59 +01:00

668 lines
19 KiB
Text

///This bakes lights into a 3d texture
//#pragma kernel VolumetricAreaBake
#pragma use_dxc vulkan
#pragma kernel PointLight
#pragma kernel SpotLight
#pragma kernel RectangleLight
#pragma kernel DiscLight
#pragma kernel DirectionalLight
#pragma kernel ClearBuffer
//#pragma editor_sync_compilation
#define M_PI 3.1415926535897932384626433832795
RWTexture3D<float4> AccumulatedLights;
//Obsolete
//float3 DirectionalLightDirection;
//float4 DirectionalLightColor;
//Convert to a structured buffer?
//Setting single light variable because we are looping through lights on the CPU.
//TODO: batch groups of lights together?
float3 LightPosition;
float3 LightDirection;
float4 LightColor;
int LightCount; //not used ATM
float SpotPram;
float InnerSpotPram;
float2 AreaSize;
float AreaLightSamples;
float4x4 AreaMatrix;
///Volume Variables
float3 Size;
float3 Position;
///
///TODO: Depreciate this
float4 OpaqueSphere[128];
int SphereCount;
float AmbientMedia;
float3 MediaSphere;
float4 MediaSphereAbsorption[128];
int MediaSphereCount;
Texture3D<float4> ParticipatingMediaTexture;
struct MeshObject
{
float4x4 localToWorldMatrix;
int indices_offset;
int indices_count;
};
StructuredBuffer<MeshObject> _MeshObjects;
StructuredBuffer<float3> _Vertices;
StructuredBuffer<int> _Indices;
//Bakes Lights into a volumetic texture to use later
//Raytracer using some code from http://three-eyed-games.com/2018/05/03/gpu-ray-tracing-in-unity-part-1/
struct Ray {
float3 origin;
float3 direction;
};
void SaveLightTexture(int3 id, float4 colorData) {
//DebugBuffer[0].debugCount = 1;
AccumulatedLights[id] += colorData;
}
static const float EPSILON = 1e-8;
bool IntersectTriangle_MT97(Ray ray, float3 vert0, float3 vert1, float3 vert2,
inout float t, inout float u, inout float v)
{
// find vectors for two edges sharing vert0
float3 edge1 = vert1 - vert0;
float3 edge2 = vert2 - vert0;
// begin calculating determinant - also used to calculate U parameter
float3 pvec = cross(ray.direction, edge2);
// if determinant is near zero, ray lies in plane of triangle
float det = dot(edge1, pvec);
// use backface culling
//if (det < EPSILON)
// return false;
float inv_det = 1.0f / det;
// calculate distance from vert0 to ray origin
float3 tvec = ray.origin - vert0;
// calculate U parameter and test bounds
u = dot(tvec, pvec) * inv_det;
if (u < 0.0 || u > 1.0f)
return false;
// prepare to test V parameter
float3 qvec = cross(tvec, edge1);
// calculate V parameter and test bounds
v = dot(ray.direction, qvec) * inv_det;
if (v < 0.0 || u + v > 1.0f)
return false;
// calculate t, ray intersects triangle
t = dot(edge2, qvec) * inv_det;
return true;
}
Ray CreateRay(float3 origin, float3 direction) {
Ray ray;
ray.origin = origin;
ray.direction = direction;
return ray;
}
Ray DirectionToPoint(float3 LightPosition, float3 UVW) {
float3 direction = UVW - LightPosition;
return CreateRay(LightPosition,direction);
}
struct RayHit
{
float3 position;
float distance;
float3 normal;
};
RayHit CreateRayHit()
{
RayHit hit;
hit.position = float3(0.0f, 0.0f, 0.0f);
hit.distance = 1.#INF;
hit.normal = float3(0.0f, 0.0f, 0.0f);
return hit;
}
///////////////
//
//Intersection types
//
///////////////
void IntersectGroundPlane(Ray ray, inout RayHit bestHit)
{
// Calculate distance along the ray where the ground plane is intersected
float t = -ray.origin.y / ray.direction.y;
if (t > 0 && t < bestHit.distance)
{
bestHit.distance = t;
bestHit.position = ray.origin + t * ray.direction;
bestHit.normal = float3(0.0f, 1.0f, 0.0f);
}
}
void IntersectSphere(Ray ray, inout RayHit bestHit, float4 sphere)
{
// Calculate distance along the ray where the sphere is intersected
float3 d = ray.origin - sphere.xyz;
float p1 = -dot(ray.direction, d);
float p2sqr = p1 * p1 - dot(d, d) + sphere.w * sphere.w;
if (p2sqr < 0)
return;
float p2 = sqrt(p2sqr);
float t = p1 - p2 > 0 ? p1 - p2 : p1 + p2;
if (t > 0 && t < bestHit.distance)
{
bestHit.distance = t;
bestHit.position = ray.origin + t * ray.direction;
bestHit.normal = normalize(bestHit.position - sphere.xyz);
}
}
//A debuging triangle
void IntersectTriangle(Ray ray, inout RayHit bestHit ){
// Trace single triangle
float3 v0 = float3(10, 2, 0);
float3 v1 = float3(20, 2, 0);
float3 v2 = float3(15, 8, 15);
float t, u, v;
if (IntersectTriangle_MT97(ray, v0, v1, v2, t, u, v))
{
if (t > 0 && t < bestHit.distance)
{
bestHit.distance = t;
bestHit.position = ray.origin + t * ray.direction;
bestHit.normal = normalize(cross(v1 - v0, v2 - v0));
// bestHit.albedo = 0.00f;
// bestHit.specular = 0.65f * float3(1, 0.4f, 0.2f);
// bestHit.smoothness = 0.9f;
// bestHit.emission = 0.0f;
}
}
}
void IntersectMeshObject(Ray ray, inout RayHit bestHit, MeshObject meshObject)
{
uint offset = meshObject.indices_offset;
uint count = offset + meshObject.indices_count;
for (uint i = offset; i < count; i += 3)
{
float3 v2 = (mul(meshObject.localToWorldMatrix, float4(_Vertices[_Indices[i]], 1))).xyz;
float3 v1 = (mul(meshObject.localToWorldMatrix, float4(_Vertices[_Indices[i + 1]], 1))).xyz;
float3 v0 = (mul(meshObject.localToWorldMatrix, float4(_Vertices[_Indices[i + 2]], 1))).xyz;
float t, u, v;
if (IntersectTriangle_MT97(ray, v0, v1, v2, t, u, v))
{
if (t > 0 && t < bestHit.distance)
{
bestHit.distance = t;
bestHit.position = ray.origin + t * ray.direction;
bestHit.normal = normalize(cross(v1 - v0, v2 - v0));
// bestHit.albedo = 0.0f;
// bestHit.specular = 0.65f;
// bestHit.smoothness = 0.99f;
// bestHit.emission = 0.0f;
}
}
}
}
//Set up interactions here
RayHit Trace(Ray ray)
{
RayHit bestHit = CreateRayHit();
uint count, stride;
//IntersectGroundPlane(ray, bestHit);
//Sphere tracing
for (int i = 0; i < SphereCount; i++) {
IntersectSphere( ray, bestHit, OpaqueSphere[i] );
}
// IntersectTriangle(ray, bestHit);
//// Trace mesh objects
_MeshObjects.GetDimensions(count, stride);
for (uint r = 0; r < count; r++)
{
IntersectMeshObject(ray, bestHit, _MeshObjects[r]);
}
return bestHit;
}
float3 Shade(inout Ray ray, RayHit hit)
{
if (hit.distance < 1.#INF)
{
// Return the normal
//return hit.normal * 0.5f + 0.5f;
return hit.distance;
//Inverse square law
//float LightRadius = (hit.distance( (id + 0.5 ) / (w / Size), LightPosition[i]) ); //Distance from center of voxel
//return 1 / (4 * M_PI * LightRadius*LightRadius);
}
else
{
// Sample the skybox and write it
float theta = acos(ray.direction.y) / -M_PI;
float phi = atan2(ray.direction.x, -ray.direction.z) / -M_PI * 0.5f;
return float3(1, 0, 1);
}
}
float4 SimpleGround(float3 pos) {
if (pos.y > 1) {
return float4(0, 1, 1, 0);
}
else {
return float4(.25, .125, 0, 1);
}
}
uniform float _Seed;
float rand(float2 Pixel, inout float seed)
{
float result = frac(sin(seed / 100.0f * dot(Pixel, float2(12.9898f, 78.233f) ) ) * 43758.5453f);
seed += 1.0f;
return result;
}
float rand(float3 Pixel, inout float seed)
{
float result = frac(sin(seed / 100.0f * dot(Pixel, float3(12.9898f, 49.1165f, 29.1165f))) * 43758.5453f);
seed += 1.0f;
return result;
}
///////////////////
///
///Media passes
///
///////////////////
SamplerState _LinearClamp;
[numthreads(4, 4, 4)]
void ClearBuffer(uint3 id : SV_DispatchThreadID) {
AccumulatedLights[id] = float4(0,0,0,0);
}
[numthreads(4, 4, 4)]
void ParticipatingMedia(uint3 id : SV_DispatchThreadID) {
// ParticipatingMediaTexture.SampleLevel(_LinearClamp, POS ,0,0)
// SaveLightTexture(id, inverseSquareColor * ShadowColor);
}
[numthreads(4, 4, 4)]
void ParticipatingSphere(uint3 id : SV_DispatchThreadID) {
// ParticipatingMediaTexture.SampleLevel(_LinearClamp, POS ,0,0)
// SaveLightTexture(id, inverseSquareColor * ShadowColor);
}
///////////////////
///
///Light Passes
///
///////////////////
[numthreads(4, 4, 4)]
void PointLight(uint3 id : SV_DispatchThreadID) {
float3 whd;
AccumulatedLights.GetDimensions(whd.x, whd.y, whd.z);
float3 VoxelWorldPosition = Position + ((id + 0.5) / (whd / Size));
//Distance from light
float LightRadius = (distance(VoxelWorldPosition, LightPosition)); //Distance from center of voxel
float4 ShadowColor = float4(1, 1, 1, 1);
Ray PointShadowRay = CreateRay(VoxelWorldPosition, -normalize(VoxelWorldPosition - LightPosition));
RayHit PointShadowHit = Trace(PointShadowRay);
if (PointShadowHit.distance < LightRadius)
{
//todo: Account for tinted materials to make colored shadows
ShadowColor *= 0;
}
//Currently just doing the inverse square law for falloff. Figure out physical scattering and absorption
float4 inverseSquareColor = LightColor / (4 * M_PI * LightRadius * LightRadius);
SaveLightTexture(id, inverseSquareColor * ShadowColor);
//AccumulatedLights[id] += inverseSquareColor * ShadowColor;
}
[numthreads(4, 4, 4)]
void SpotLight(uint3 id : SV_DispatchThreadID) {
float3 whd;
AccumulatedLights.GetDimensions(whd.x, whd.y, whd.z);
float3 VoxelWorldPosition = Position + ((id + 0.5) / (whd / Size));
float3 VoxelDirectionToLight = -normalize(VoxelWorldPosition - LightPosition);
//LightDirection = float3(0, -1, 0);
//Currently taking a point light and adding attenuation
float attenuation = clamp(dot(LightDirection, -VoxelDirectionToLight), 0, 1);
///
float flOuterConeCos = SpotPram;
float flTemp = dot(LightDirection, -VoxelDirectionToLight) - flOuterConeCos;
float vSpotAtten = saturate(flTemp * InnerSpotPram);
///
//Distance from light
float LightRadius = (distance(VoxelWorldPosition, LightPosition)); //Distance from center of voxel
float4 ShadowColor = float4(1, 1, 1, 1);
//Only casting rays in lit areas
if (attenuation > 0) {
Ray PointShadowRay = CreateRay(VoxelWorldPosition, VoxelDirectionToLight);
RayHit PointShadowHit = Trace(PointShadowRay);
if (PointShadowHit.distance < LightRadius)
{
//todo: Account for tinted materials to make colored shadows
ShadowColor *= 0;
}
}
//Currently just doing the inverse square law for falloff.
//TODO: Figure out physical scattering and absorption for more accuraete color and falloff
float4 inverseSquareColor = LightColor / (4 * M_PI * LightRadius * LightRadius);
//AccumulatedLights[id] += inverseSquareColor * ShadowColor * vSpotAtten;
SaveLightTexture(id, inverseSquareColor * ShadowColor * vSpotAtten);
}
//Add a skylight portal to better control light leaking
[numthreads(4, 4, 4)]
void DirectionalLight(uint3 id : SV_DispatchThreadID) {
float3 whd;
AccumulatedLights.GetDimensions(whd.x, whd.y, whd.z);
float3 VoxelWorldPosition = Position + ((id + 0.5) / (whd / Size));
//Directional Light // Make an array
// Shadow test ray
float4 DirectionalShadow = float4(1, 1, 1, 1);
Ray shadowRay = CreateRay(VoxelWorldPosition, -LightDirection.xyz);
RayHit shadowHit = Trace(shadowRay);
if (shadowHit.distance != 1.#INF && shadowHit.distance != -1.#INF)
{
DirectionalShadow *= 0;
}
// AccumulatedLights[id.xyz] += DirectionalColor;
SaveLightTexture(id, LightColor * 0.01 * DirectionalShadow); //TODO: remove temp multiplier
}
//
[numthreads(4, 4, 4)]
void RectangleLight(uint3 id : SV_DispatchThreadID) {
float3 whd;
AccumulatedLights.GetDimensions(whd.x, whd.y, whd.z);
float3 VoxelWorldPosition = Position + ((id + 0.5) / (whd / Size));
float4 MonteCarlointegration = float4(0, 0, 0, 0);
float seed = _Seed;
for (int i = 0; i < AreaLightSamples; i++) {
float4 ShadowColor = float4(1, 1, 1, 1);
float3 VoxelDirectionToLight = -normalize(VoxelWorldPosition - LightPosition);
float Facing = saturate(dot(LightDirection, -VoxelDirectionToLight)); //Intal direction check
//Only cast rays from one side of the light
if (Facing > 0){
float3 LightPosSample = LightPosition + mul((float3x3)AreaMatrix, float3( (rand(id.xyz, seed)-0.5) * AreaSize.x , (rand(id.xyz, seed) - 0.5) * AreaSize.y, 0) );
VoxelDirectionToLight = -normalize(VoxelWorldPosition - LightPosSample);
float LightRadius = (distance(VoxelWorldPosition, LightPosSample)); //Distance from center of voxel
float attenuation = saturate(dot(LightDirection, -VoxelDirectionToLight));
Ray PointShadowRay = CreateRay(VoxelWorldPosition, -normalize(VoxelWorldPosition - LightPosSample) );
RayHit PointShadowHit = Trace(PointShadowRay);
if (PointShadowHit.distance < LightRadius)
{
//todo: Account for tinted materials to make colored shadows
ShadowColor *= 0;
}
//TODO: figure out what's unity's area lighting model is and match it
float4 inverseSquareColor = (LightColor ) / (4 * M_PI * LightRadius * LightRadius);
MonteCarlointegration += (inverseSquareColor * ShadowColor * attenuation) / AreaLightSamples;
}
//Currently just doing the inverse square law for falloff. Figure out physical scattering and absorption
}
// AccumulatedLights[id] += MonteCarlointegration;
SaveLightTexture(id, MonteCarlointegration);
}
[numthreads(4, 4, 4)]
void DiscLight(uint3 id : SV_DispatchThreadID) {
float3 whd;
AccumulatedLights.GetDimensions(whd.x, whd.y, whd.z);
float3 VoxelWorldPosition = Position + ((id + 0.5) / (whd / Size));
float4 MonteCarlointegration = float4(0, 0, 0, 0);
float seed = _Seed;
for (int i = 0; i < AreaLightSamples; i++) {
float4 ShadowColor = float4(1, 1, 1, 1);
float3 VoxelDirectionToLight = -normalize(VoxelWorldPosition - LightPosition);
float Facing = saturate(dot(LightDirection, -VoxelDirectionToLight)); //Intal direction check
//Only cast rays from one side of the light
if (Facing > 0) {
//https://stackoverflow.com/questions/5837572/generate-a-random-point-within-a-circle-uniformly
float t = 2 * M_PI * rand(id.xyz, seed);
float u = rand(id.xyz, seed) + rand(id.xyz, seed);
float r;
if (u > 1)
{
r = (2 - u);
}
else { r = u; }
// [r * cos(t), r * sin(t)]
float3 LightPosSample = LightPosition + mul((float3x3)AreaMatrix,
float3( ( r * cos(t)) * AreaSize.x,
( r * sin(t)) * AreaSize.x,
0));
//TEMP
//float3 LightPosSample = LightPosition + mul(AreaMatrix, float3((rand(id.xyz) - 0.5) * AreaSize.x, (rand(id.xyz) - 0.5) * AreaSize.x, 0));
VoxelDirectionToLight = -normalize(VoxelWorldPosition - LightPosSample);
float LightRadius = (distance(VoxelWorldPosition, LightPosSample)); //Distance from center of voxel
float attenuation = saturate(dot(LightDirection, -VoxelDirectionToLight));
Ray PointShadowRay = CreateRay(VoxelWorldPosition, -normalize(VoxelWorldPosition - LightPosSample));
RayHit PointShadowHit = Trace(PointShadowRay);
if (PointShadowHit.distance < LightRadius)
{
//todo: Account for tinted materials to make colored shadows
ShadowColor *= 0;
}
//TODO: figure out what's unity's area lighting model is and match it
float4 inverseSquareColor = (LightColor) / (4 * M_PI * LightRadius * LightRadius);
MonteCarlointegration += (inverseSquareColor * ShadowColor * attenuation) / AreaLightSamples;
}
//Currently just doing the inverse square law for falloff. Figure out physical scattering and absorption
}
// AccumulatedLights[id] += MonteCarlointegration;
SaveLightTexture(id, MonteCarlointegration);
}
/////UNUSED
//
//[numthreads(4, 4, 4)]
//void VolumetricAreaBake(uint3 id : SV_DispatchThreadID)
//{
// float3 whd;
// AccumulatedLights.GetDimensions(whd.x, whd.y, whd.z);
//
//
// Debug debug;
// debug.debugCount = 0;
//
// //w -= 1;
//
// float4 ColoredLights = float4(0,0,0,0);
// float ParticipatingMedia = 0;
//
// float3 VoxelWorldPosition = Position + ((id + 0.5) / (whd / Size));
//
// //Directional Light // Make an array
// // Shadow test ray
// bool shadow = false;
// Ray shadowRay = CreateRay(VoxelWorldPosition, LightDirection.xyz);
// RayHit shadowHit = Trace(shadowRay);
// float4 DirectionalColor = DirectionalLightColor;
//
// if (shadowHit.distance != 1.#INF)
// {
// DirectionalColor *= 0;
// }
// ///
//
//
// ColoredLights += DirectionalColor;
//
// ////Point lights without shadows
// //for (int i = 0; i < LightCount; i++) {
//
// // //Distance from light
// // float LightRadius = (distance(VoxelWorldPosition, LightPosition[i]) ); //Distance from center of voxel
//
// // float4 ShadowColor = float4(1,1,1,1);
//
// // Ray PointShadowRay = CreateRay(VoxelWorldPosition, -normalize(VoxelWorldPosition - LightPosition[i]) );
//
// // RayHit PointShadowHit = Trace(PointShadowRay);
// // if (PointShadowHit.distance < LightRadius)
// // {
// // //todo: Account for tinted materials to make colored shadows
// // ShadowColor *=0;
// // }
// // //Currently just doing the inverse square law for falloff. Figure out physical scattering and absorption
// // float4 inverseSquareColor = LightColor[i] / (4 * M_PI * LightRadius*LightRadius);
// //
// // ColoredLights += inverseSquareColor * ShadowColor;
//
// //}
//
//
// //Bake in fog densitiy
// for (int s = 0; s < MediaSphereCount; s++) {
//
// float mediaStep = distance ( MediaSphere[s], VoxelWorldPosition) ;
//
//
// if (mediaStep > 0.5)
// {
// mediaStep = 0;
// }
// else {
// mediaStep = MediaSphereAbsorption[s].x;
// }
//
// ParticipatingMedia += mediaStep;
//
// }
//
// ParticipatingMedia += AmbientMedia;
//
//
// //float4 LightColored = (1 - saturate(distance(id / (w / Size), LightPosition))) * LightColor;
//
//
// //RayHit hit = Trace(ray);
// //float3 result = Shade(ray, hit);
// //Result[id.xyz] = float4(result, 1);
//
// AccumulatedLights[id.xyz] = float4(ColoredLights.xyz, ParticipatingMedia) ; //Store Light Colors and
// //Result[id.xyz] = SimpleGround(LightPosition[0]-ray.direction);
//
//// Result[id.xyz] = float4(id / (w / Size),1); //DEBUG
//// DebugBuffer[id.x].debugCount = debug.debugCount;
//}