Files
beyond/Assets/ThirdParty/SDF Baker/Example/Shaders/RaymarchExample.shader
2024-11-20 15:21:28 +01:00

186 lines
6.6 KiB
GLSL

Shader "Custom/RaymarchExample"
{
Properties
{
}
SubShader
{
// No culling or depth
Cull Off ZWrite Off ZTest Always
Pass
{
CGPROGRAM
// TODO: In 2019 - change to shader_feature_local
#pragma shader_feature _ SDFr_VISUALIZE_STEPS SDFr_VISUALIZE_HEATMAP SDFr_VISUALIZE_DIST
#pragma vertex vert_proc_quad
#pragma fragment frag
#include "UnityCG.cginc"
#include "Assets/SDFr/Shaders/SDFrProcedural.hlsl"
#include "Assets/SDFr/Shaders/SDFrVolumeTex.hlsl"
#include "Assets/SDFr/Shaders/SDFrUtilities.hlsl"
#define MAX_STEPS 512
#define EPSILON 0.003
#define NORMAL_DELTA 0.03
uniform float4x4 _PixelCoordToViewDirWS;
Texture3D _VolumeATex;
Texture3D _VolumeBTex;
StructuredBuffer<SDFrVolumeData> _VolumeBuffer;
float4 _Sphere;
float4 _Box;
float sdSphere( float3 p, float s )
{
return length(p)-s;
}
float sdBox( float3 p, float3 b )
{
float3 d = abs(p) - b;
return min(max(d.x,max(d.y,d.z)),0.0) + length(max(d,0.0));
}
// ISSUE: When stepping through a volume and not intersecting, it seams to keep a low step size.
// Probably cos closest intersect is previous AABB?
// Should maybe have an encompassing bounding box for scene - so if we hit that we early out!!!!
float DistanceFunction( float3 rayPos, float3 rayOrigin, float3 rayEnd )
{
float sphere = sdSphere(rayPos-_Sphere.xyz,_Sphere.w);
float box = sdBox(rayPos-_Box.xyz,float3(1,1,1)*_Box.w);
float d = min(box,sphere);
float ad = DistanceFunctionTex3D( rayPos, rayOrigin, rayEnd, _VolumeBuffer[0], _VolumeATex );
d = min(ad,d);
float bd = DistanceFunctionTex3D( rayPos, rayOrigin, rayEnd, _VolumeBuffer[1], _VolumeBTex );
d = min(bd,d);
return d;
}
// Does no boundss checking - so assuming we hit something - but still checking against all primitives
float DistanceFunctionFast(float3 rayPos)
{
float sphere = sdSphere(rayPos - _Sphere.xyz, _Sphere.w);
float box = sdBox(rayPos - _Box.xyz, float3(1, 1, 1)*_Box.w);
float d = min(box, sphere);
float ad = DistanceFunctionTex3DFast(rayPos, _VolumeBuffer[0], _VolumeATex);
d = min(ad, d);
float bd = DistanceFunctionTex3DFast(rayPos, _VolumeBuffer[1], _VolumeBTex);
d = min(bd, d);
return d;
}
// Calculate the furthest safe ray start distance based on bounds of each element.
float FurthestRayStartDistance(float3 rayOrigin, float3 rayEnd)
{
float d0 = DistanceToAABB(rayOrigin, rayEnd, _VolumeBuffer[0]);
float d1 = DistanceToAABB(rayOrigin, rayEnd, _VolumeBuffer[1]);
float d2 = DistanceToAABB(rayOrigin, rayEnd, _VolumeBuffer[2]);
float d3 = DistanceToAABB(rayOrigin, rayEnd, _VolumeBuffer[3]);
float d = min(d0, d1);
d = min(d2, d);
d = min(d3, d);
return d;
}
half4 frag (Varyings_Proc input) : SV_Target
{
//ray origin
float3 ro = _WorldSpaceCameraPos;
//ray from camera to pixel coordinates in world space
float3 rd = -normalize(mul(float3(input.positionCS.xy, 1.0), (float3x3)_PixelCoordToViewDirWS));
float3 re = ro + rd * _ProjectionParams.z;
// Set starting distance to furthest safe distance to closest AABB ( originally was 0 ).
// Otherwise number of steps is much higher as step size is based on first distance found within AABB to SDF.
float dist = FurthestRayStartDistance(ro, re);
int steps = 0;
// Need to exit if max dist obtained elese empty pixels will use MAX_STEPS for nothing!
while ( steps < MAX_STEPS && dist < _ProjectionParams.z )
{
float3 rayPos = ro + rd * dist;
float d = DistanceFunction(rayPos,ro,re);
if ( d < EPSILON )
{
#ifdef SDFr_VISUALIZE_DIST
return half4(0, 0, dist / 10.0, 1);
#endif
#ifdef SDFr_VISUALIZE_STEPS
return half4(steps / (float)MAX_STEPS, 0, 0, 1);
#endif
#ifdef SDFr_VISUALIZE_HEATMAP
// HeatMap - Green = minimal, Red = maximum number of steps
float stepf = steps / (float)MAX_STEPS;
float hue = lerp(0.33, 0.0, stepf);
float3 rgb = HsvToRgb(float3(hue, 1, 1));
return half4(rgb, 1);
#endif
//fast normal
float3 nx = rayPos + float3(NORMAL_DELTA,0,0);
float3 ny = rayPos + float3(0,NORMAL_DELTA,0);
float3 nz = rayPos + float3(0,0,NORMAL_DELTA);
// Can we not assume that any grad function is going to hit bounds and thus use a simpler method call?
/*
float dx = DistanceFunction(nx,ro,re)-d;
float dy = DistanceFunction(ny,ro,re)-d;
float dz = DistanceFunction(nz,ro,re)-d;
*/
/*
float dx = DistanceFunctionFast(nx) - d;
float dy = DistanceFunctionFast(ny) - d;
float dz = DistanceFunctionFast(nz) - d;
*/
float halfDelta = NORMAL_DELTA * 0.5;
float dx = DistanceFunctionFast(rayPos + float3(halfDelta, 0, 0)) - DistanceFunctionFast(rayPos - float3(halfDelta, 0, 0));
float dy = DistanceFunctionFast(rayPos + float3(0, halfDelta, 0)) - DistanceFunctionFast(rayPos - float3(0, halfDelta, 0));
float dz = DistanceFunctionFast(rayPos + float3(0, 0, halfDelta)) - DistanceFunctionFast(rayPos - float3(0, 0, halfDelta));
float3 normalWS = normalize(float3(dx,dy,dz));
//TODO lighting
return half4(normalWS,1);
}
dist += d;
steps++;
}
#ifdef SDFr_VISUALIZE_DIST
return half4(0, 0, dist / 10.0, 1);
#endif
#ifdef SDFr_VISUALIZE_STEPS
return half4(steps / (float)MAX_STEPS, 0, 0, 1);
#endif
#ifdef SDFr_VISUALIZE_HEATMAP
// HeatMap - Green = minimal, Red = maximum number of steps
float stepf = steps / (float)MAX_STEPS;
float hue = lerp(0.33, 0.0, stepf);
float3 rgb = HsvToRgb(float3(hue, 1, 1));
return half4(rgb, 1);
#endif
return half4(0.2,0.2,0.2,1);
}
ENDCG
}
}
}