Last active
March 25, 2025 16:06
Revisions
-
MichaelMoroz revised this gist
Nov 2, 2024 . 1 changed file with 17 additions and 12 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -18,6 +18,7 @@ Shader "Misha/Schwartzschild" _StepSize("Step Size", Range(0, 0.02)) = 0.016 _MaxSteps("Max Steps", int) = 128 _DepthThickness("Depth Occlusion Thickness", Range(0, 0.1)) = 0.057 [Toggle] _UseDepth("Use Depth Occlusion", int) = 1 _MaxRadius("Max Ray Trace Distance", Range(0.5, 10)) = 0.5 } @@ -75,6 +76,7 @@ Shader "Misha/Schwartzschild" float _BHStrength; float _MaxRadius; float _EventHorizonRadius; int _UseDepth; float3 PositionToScreen(float3 pos) { @@ -209,16 +211,19 @@ Shader "Misha/Schwartzschild" steps = float(j); break; } if(_UseDepth) //if depth occlusion is enabled { float3 screenPos = PositionToScreen(ro); float depth_scene = worldToObjectScale*LinearEyeDepth(SampleDepth(screenPos.xy)); float depth_ro = worldToObjectScale*LinearEyeDepth(screenPos.z); float diff = depth_scene - depth_ro; if((diff < 0.0) && (diff > -_DepthThickness)) //if has hit an object in the scene { steps = float(j); stopped = true; break; } } } @@ -228,7 +233,7 @@ Shader "Misha/Schwartzschild" bool behind = screenPos.z < depth; float4 bgcolor; if(_UseDepth && behind && !stopped) //if background sample occluded - fallback to no distortion { screenPos.xy = i.screenPos; } @@ -241,4 +246,4 @@ Shader "Misha/Schwartzschild" ENDCG } } } -
MichaelMoroz created this gist
Nov 2, 2024 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,244 @@ Shader "Misha/Schwartzschild" { Properties { [Header(Black Hole)] _EventHorizonRadius("Event Horizon Radius", Range(0.0, 0.5)) = 0.075 [Header(Accretion)] [HDR] _AccretionColor("Color", Color) = (1,1,1,1) _AccretionDensity("Accretion Density", Range(0, 1000)) = 200.0 _AccretionThickness("Accretion Thickness", Range(0, 0.2)) = 0.05 _AccretionRadius("Accretion Radius", Range(0, 1)) = 0.5 _AccretionInnerRadius("Accretion Inner Radius", Range(0, 1)) = 0.1 _Opacity("Opacity", Range(0, 100)) = 0.5 [Header(Quality)] _StepSize("Step Size", Range(0, 0.02)) = 0.016 _MaxSteps("Max Steps", int) = 128 _DepthThickness("Depth Occlusion Thickness", Range(0, 0.1)) = 0.057 _MaxRadius("Max Ray Trace Distance", Range(0.5, 10)) = 0.5 } SubShader { Tags { "Queue" = "Transparent" } GrabPass { "_BackgroundTexture" } Pass { ZWrite Off ZTest Always Cull Front CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" float3 GetCameraPosition() { return _WorldSpaceCameraPos; } struct v2f { float3 worldPos : TEXCOORD0; float2 screenPos : TEXCOORD1; float4 pos : SV_POSITION; #if defined(UNITY_VERTEX_OUTPUT_STEREO) UNITY_VERTEX_OUTPUT_STEREO #endif }; struct appdata { float4 vertex : POSITION; #if defined(UNITY_VERTEX_INPUT_INSTANCE_ID) UNITY_VERTEX_INPUT_INSTANCE_ID #endif }; UNITY_DECLARE_SCREENSPACE_TEXTURE(_BackgroundTexture); UNITY_DECLARE_DEPTH_TEXTURE(_CameraDepthTexture); float4 _AccretionColor; float _Opacity; float _AccretionDensity; float _AccretionThickness; float _AccretionRadius; float _AccretionInnerRadius; float _StepSize; float _DepthThickness; int _MaxSteps; float _BHStrength; float _MaxRadius; float _EventHorizonRadius; float3 PositionToScreen(float3 pos) { float4 clipPos = UnityObjectToClipPos(float4(pos, 1)); return float3(ComputeGrabScreenPos(clipPos).xy / clipPos.w, clipPos.z / clipPos.w); } float4 SampleBackground(float2 uv) { return UNITY_SAMPLE_SCREENSPACE_TEXTURE(_BackgroundTexture, uv); } float4 SampleDepth(float2 uv) { uv.y = _ProjectionParams.x * .5 + .5 - uv.y * _ProjectionParams.x; return SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, uv); } v2f vert(appdata v) { v2f o; UNITY_SETUP_INSTANCE_ID( v ); UNITY_INITIALIZE_OUTPUT( v2f, o ); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO( o ); o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; o.pos = UnityObjectToClipPos(v.vertex); o.screenPos = ComputeGrabScreenPos(o.pos) / o.pos.w; return o; } float2 SphereIntersection(float3 ro, float3 rd, float3 sphPos, float sphRad) { float3 oc = ro - sphPos; float b = dot(oc, rd); float c = dot(oc, oc) - sphRad * sphRad; float h = b * b - c; if (h < 0.0) return float2(-1.0, -1.0); h = sqrt(h); return float2(-b - h, -b + h); } float BHStrength(float radius) { return pow(radius / 0.75, 3.0); } //the black hole "force" float3 force(float dist, float3 p) { return -1.5 * BHStrength(_EventHorizonRadius) * p * pow(dist, -5.0) * smoothstep(1.0, 0.9, dist); //smoothstep to make space flat outside the region } float sqr(float x) { return x * x; } float accretionDisk(float dist, float z) { //just uniform exponential disk float h = _AccretionThickness; float R = _AccretionRadius; float r = _AccretionInnerRadius; float heigh_weight = exp(- sqr(z / h)); float rad_weight = exp(- dist / R) * smoothstep(0.05, 1.0, 1.0 - dist / R) * smoothstep(r, r*1.2, dist); return _AccretionDensity * heigh_weight * rad_weight; } float InterleavedGradientNoise(float2 pixelPos) { return frac(52.9829189f * frac(0.06711056f*float(pixelPos.x) + 0.00583715f*float(pixelPos.y))); } half4 frag(v2f i, bool isFront : SV_IsFrontFace) : SV_Target { UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i); //this blog post is used as reference //https://rantonels.github.io/starless/ float3 cam = GetCameraPosition(); float3 rd = i.worldPos - cam; rd = normalize(mul(unity_WorldToObject, float4(rd, 0.0)).xyz); cam = mul(unity_WorldToObject, float4(cam, 1.0)).xyz; float2 t = SphereIntersection(cam, rd, float3(0, 0, 0), 0.5); //boundary of flat space float td = max(0.0, t.x); cam += td * rd; if(t.x >= 0.0) //if outside the ray marching region - check if region is behind the object { float3 screenPos = PositionToScreen(cam); float this_depth = SampleDepth(screenPos.xy); if(screenPos.z < this_depth) { discard; } } float noise = InterleavedGradientNoise(int2(i.pos.xy)); float3 rv = rd; float3 ro = cam; ro += noise * _StepSize * rv; float accretion_rho = 0.0; float accretion_color = 0.0; bool hit_horizon = false; bool stopped = false; float steps = 0.0; float worldToObjectScale = length(mul(unity_WorldToObject, float4(1, 1, 1, 0)).xyz); [loop] for (int j = 0; j < _MaxSteps; j++) { float dt = _StepSize;// / length(rv); //will make integration step size constant float dist = length(ro); if(dist < 0.5) //is inside curved space region { float rho = accretionDisk(dist, ro.y); accretion_rho += _Opacity*rho * dt; float opacity = exp(-accretion_rho); accretion_color += rho * dt * opacity; //integrate velocity rv += force(dist, ro) * dt; } //integrate position ro += rv * dt; if (dist < _EventHorizonRadius) { //if has hit the event horizon steps = float(j); hit_horizon = true; break; } if(dist > _MaxRadius || accretion_rho > 32.0) //if outside the region or occluded { steps = float(j); break; } float3 screenPos = PositionToScreen(ro); float depth_scene = worldToObjectScale*LinearEyeDepth(SampleDepth(screenPos.xy)); float depth_ro = worldToObjectScale*LinearEyeDepth(screenPos.z); float diff = depth_scene - depth_ro; if((diff < 0.0) && (diff > -_DepthThickness)) //if has hit an object in the scene { steps = float(j); stopped = true; break; } } float3 screenPos = PositionToScreen(ro); float depth = SampleDepth(screenPos.xy); bool behind = screenPos.z < depth; float4 bgcolor; if(behind && !stopped) //if background sample occluded - fallback to no distortion { screenPos.xy = i.screenPos; } float4 background = SampleBackground(screenPos.xy) * (1.0 - float(hit_horizon)); float4 accretionColor = _AccretionColor * accretion_color; float opacity = 1.0 - exp(-accretion_rho); return float4(lerp(background.rgb, accretionColor.rgb, opacity), 1.0); } ENDCG } } }