241 lines
9.6 KiB
GLSL
241 lines
9.6 KiB
GLSL
Shader "XRA/SDFr"
|
|
{
|
|
Properties
|
|
{
|
|
}
|
|
|
|
HLSLINCLUDE
|
|
|
|
#pragma target 4.5
|
|
#pragma only_renderers d3d11 ps4 xboxone vulkan metal switch
|
|
#pragma enable_d3d11_debug_symbols
|
|
#pragma multi_compile_instancing
|
|
|
|
//remove if using com.unity.render-pipelines.core
|
|
#include "UnityCG.cginc"
|
|
|
|
#include "SDFrProcedural.hlsl"
|
|
#include "SDFrVolumeTex.hlsl"
|
|
#include "SDFrUtilities.hlsl"
|
|
|
|
uniform float4x4 _PixelCoordToViewDirWS;
|
|
|
|
Texture2D _BlueNoiseRGBA;
|
|
SamplerState sampler_point_repeat_BlueNoiseRGBA;
|
|
float4 _BlueNoiseRGBA_TexelSize;
|
|
|
|
float4 screenBlueNoise( float2 uv )
|
|
{
|
|
//keep 0-1
|
|
float2 shift = frac(_Time.xy);
|
|
//then scale by with and height of the target texture
|
|
shift *= _ScreenParams.xy;
|
|
//then round it down (removes fractional part)
|
|
shift = floor(shift);
|
|
//how many times the texture fits into screen if 1:1
|
|
float2 screenByTex = (_ScreenParams.xy/_BlueNoiseRGBA_TexelSize.zw);
|
|
float2 screenCoords = (uv + shift) * (screenByTex);
|
|
return _BlueNoiseRGBA.SampleLevel( sampler_point_repeat_BlueNoiseRGBA, screenCoords, 0 );
|
|
}
|
|
|
|
ENDHLSL
|
|
|
|
SubShader
|
|
{
|
|
Tags{ "RenderType" = "Opaque" }
|
|
|
|
//render signed distance field with world normals (remapped to 0-1)
|
|
Pass //0
|
|
{
|
|
Name "SDFrVisualize"
|
|
Tags { "LightMode" = "SDFrVisualize" }
|
|
|
|
ZTest LEqual
|
|
ZWrite on
|
|
Blend off
|
|
Cull front
|
|
|
|
HLSLPROGRAM
|
|
// 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
|
|
|
|
#define MAX_STEPS 1024
|
|
#define EPSILON 0.003
|
|
#define NORMAL_DELTA 0.03
|
|
|
|
Texture3D _SDFVolumeTex;
|
|
float4x4 _SDFVolumeLocalToWorld;
|
|
float4x4 _SDFVolumeWorldToLocal;
|
|
float3 _SDFVolumeExtents; //xyz extents of volume
|
|
float _SDFVolumeFlip;
|
|
float _SDFPreviewEpsilon = EPSILON;
|
|
float _SDFPreviewNormalDelta = NORMAL_DELTA;
|
|
|
|
inline float DistanceFunction(float3 rayPosLS)
|
|
{
|
|
float3 vp = rayPosLS + 0.5;
|
|
|
|
// Testing filtering and mip levels
|
|
float sample = _SDFVolumeTex.SampleLevel(sdfr_sampler_linear_clamp, vp, 0).r;
|
|
// float sample = _SDFVolumeTex.SampleLevel(sdfr_sampler_trilinear_clamp, vp, 0).r;
|
|
// float sample = _SDFVolumeTex.Sample(sdfr_sampler_trilinear_clamp, vp).r;
|
|
// if (_SDFVolumeFlip < 0) sample = -sample;
|
|
return (_SDFVolumeFlip < 0) ? -sample : sample;
|
|
}
|
|
|
|
// Should normalDeltas be cached?
|
|
inline float3 GenerateNormalsFast(float normalDelta, float dist, float3 rayPosLS)
|
|
{
|
|
float3 nx = rayPosLS + float3(normalDelta, 0, 0);
|
|
float3 ny = rayPosLS + float3(0, normalDelta, 0);
|
|
float3 nz = rayPosLS + float3(0, 0, normalDelta);
|
|
float dx = DistanceFunction(nx) - dist;
|
|
float dy = DistanceFunction(ny) - dist;
|
|
float dz = DistanceFunction(nz) - dist;
|
|
return normalize(float3(dx, dy, dz));
|
|
}
|
|
|
|
inline float3 GenerateNormals(float normalDelta, float dist, float3 rayPosLS)
|
|
{
|
|
float dx = DistanceFunction(rayPosLS + float3(normalDelta, 0, 0)) - DistanceFunction(rayPosLS - float3(normalDelta, 0, 0));
|
|
float dy = DistanceFunction(rayPosLS + float3(0, normalDelta, 0)) - DistanceFunction(rayPosLS - float3(0, normalDelta, 0));
|
|
float dz = DistanceFunction(rayPosLS + float3(0, 0, normalDelta)) - DistanceFunction(rayPosLS - float3(0, 0, normalDelta));
|
|
return normalize(float3(dx, dy, dz));
|
|
}
|
|
|
|
struct OutputPS
|
|
{
|
|
half4 color : COLOR0;
|
|
float depth : DEPTH;
|
|
};
|
|
|
|
OutputPS Raymarch( float3 roWS, float3 rdWS, float3 reWS )
|
|
{
|
|
float normalDelta = max(0.0001,_SDFPreviewNormalDelta);
|
|
float eps = max(0.0001,_SDFPreviewEpsilon);
|
|
|
|
OutputPS o = (OutputPS)0;
|
|
|
|
//ray origin world to local space
|
|
float3 roLS = mul(_SDFVolumeWorldToLocal,float4(roWS,1)).xyz;
|
|
//ray end world to local
|
|
float3 reLS = mul(_SDFVolumeWorldToLocal,float4(reWS,1)).xyz;
|
|
//ray direction world to local
|
|
float3 rdLS = normalize(reLS-roLS); //mul((float3x3)_SDFVolumeWorldToLocal,rdWS.xyz);
|
|
|
|
//min and max of bounds
|
|
float3 minAABB = -_SDFVolumeExtents.xyz;
|
|
float3 maxAABB = _SDFVolumeExtents.xyz;
|
|
|
|
//intersection value is 0 (ray origin) to 1 (ray end)
|
|
//x is enter intersection, y is exit intersection
|
|
//NOTE! this intersection is only for axis-aligned bounding boxes
|
|
//so any rays in world space must be taken into local space if the volume bounds are rotated
|
|
//if the AABB never rotates then the intersection can be in world space & save matrix multiply
|
|
float2 intersection = LineAABBIntersect( roLS, reLS, minAABB, maxAABB );
|
|
float3 enterLS = lerp(roLS,reLS,saturate(intersection.x));
|
|
float3 exitLS = lerp(roLS,reLS,saturate(intersection.y));
|
|
|
|
float distanceToEnter = distance(roLS,enterLS);
|
|
float distanceToExit = distance(roLS,exitLS);
|
|
|
|
//the distance traveled within volume
|
|
float distanceInVolume = distanceToExit - distanceToEnter;
|
|
|
|
//if the ray is intersecting the bounds
|
|
UNITY_BRANCH
|
|
if ( intersection.x < intersection.y && intersection.x < 1 )
|
|
{
|
|
//accumulate distance samples
|
|
float dist = 0;
|
|
//accumulate the steps
|
|
int steps = 0;
|
|
|
|
UNITY_LOOP
|
|
while( dist < distanceInVolume && steps < MAX_STEPS )
|
|
{
|
|
//current position of ray
|
|
//since it is in local space always use the intersection enter position
|
|
//the intersection is on surface of bounds or at camera in bounds
|
|
float3 rayPosLS = enterLS + rdLS * dist;
|
|
|
|
//length(_SDFVolumeLocalToWorld[0])
|
|
|
|
//the ray position at current step
|
|
//normalize into volume texture space
|
|
rayPosLS /= _SDFVolumeExtents.xyz*2;
|
|
float d = DistanceFunction(rayPosLS);
|
|
|
|
UNITY_BRANCH
|
|
if ( d < eps )
|
|
{
|
|
// float3 normalLS = GenerateNormalsFast(normalDelta, d, rayPosLS);
|
|
float3 normalLS = GenerateNormals(normalDelta * 0.5, d, rayPosLS);
|
|
|
|
//object to world space normals
|
|
float3 normalWS = mul((float3x3)_SDFVolumeLocalToWorld,normalLS);
|
|
|
|
//local to world ray hit position
|
|
float3 rayHitWS = mul(_SDFVolumeLocalToWorld,float4(rayPosLS,1)).xyz;
|
|
|
|
// BUG: Depth value appears incorrect in preview mode - it intersects with geometry when it shouldn't.
|
|
|
|
//NOTE only needed for depth if mixing with depth buffer
|
|
// float4 ndc = UnityObjectToClipPos(float4(rayHitWS, 1));
|
|
float4 ndc = mul(UNITY_MATRIX_MVP,float4(rayHitWS,1));
|
|
// float4 ndc = mul(UNITY_MATRIX_VP, float4(rayHitWS, 1));
|
|
float realDepth = ndc.z/ndc.w;
|
|
o.depth = realDepth;
|
|
|
|
// float4 clippos = mul(UNITY_MATRIX_IT_MV, float4(rayHitWS, 1.0));
|
|
// o.depth = clippos.z;
|
|
|
|
#ifdef SDFr_VISUALIZE_DIST
|
|
o.color = half4(0, 0, dist / 10.0, 1);
|
|
#elif SDFr_VISUALIZE_STEPS
|
|
o.color = half4(steps / (float)MAX_STEPS, 0, 0, 1);
|
|
#elif 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));
|
|
o.color = half4(rgb, 1);
|
|
#else
|
|
//visualize world normals
|
|
o.color = half4(normalWS*0.5 + 0.5, 1);
|
|
#endif
|
|
|
|
return o;
|
|
}
|
|
dist += d;
|
|
steps++;
|
|
}
|
|
}
|
|
//else did not intersect volume, discard
|
|
discard;
|
|
return o;
|
|
}
|
|
|
|
OutputPS Frag( Varyings_Proc input )
|
|
{
|
|
//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));
|
|
|
|
//if using blue noise or similar to jitter rays
|
|
//float blueNoise = screenBlueNoise( input.texcoord ).r;
|
|
//ro += rd * blueNoise * 0.1;
|
|
|
|
float3 re = ro + rd * _ProjectionParams.z;
|
|
|
|
return Raymarch( ro, rd, re );
|
|
}
|
|
|
|
ENDHLSL
|
|
}
|
|
}
|
|
} |