Posts
-
OBS Custom transition with a Shader
This is something I wrote for my own model debut, as I knew I won’t be able to afford a custom stinger transition at the time of the debut. Plus I wanted the transition to be a bit out of ordinary - stinger transitions are great, but they do not allow you to modify pixel positions from scene A/B - only fade between A/B. Shaders don’t have that limitation. This initially started with me planning out everythin in Unity’s shader graph to have a better view on each stage of the transition and once I had a general concept, I started translating shader graph to shader language used OBS Shader component. The original transition was suppose to be using sampling from scene A and B, logo and a something similar to a normal texture to produce a much more interesting and complex transition. However mid-project I have run into an issue that seems to be related to a limitation of using only 3 textures that can be sampled. With 4, the results would just break with OBS Shader plugin not providing any error message.
As a workaround, I was planning on looking up how to create hexagons using pixel shaders, however due to the debut being set in stone, I have eventually decided to just simplify it to a simple wave pattern (which I am not exactly happy with).
If you want, you can always follow me on Twitch or YouTube.While I won’t provide you with a logo, you are free to use this code with your own logo if you find it interesting:
// Glass-shield transition // written by SuiMachine / Sui_VT. // You are free to modify it as you please. // Credit is not required but appriciated. // // There may be a few sections in here that seem like they are overcomplicated but may be intentional // For example I initially wrote a shader so that it built UVs for a logo and sampleed image_a or image_b // but then run into an issue where sampling background with normal coorinates would result // in old data being overriden, so I had to rewrite it in a way where image_a or image_b are sampled only once! uniform texture2d image_a; uniform texture2d image_b; uniform float transition_time = 0.5; uniform float Aspect_ratio = 1.77777777778; uniform bool convert_linear = true; uniform texture2d LogoTexture = "SuiLogo.png"; uniform float2 LogoColorDistortionXY = { 0.2, 0.05 }; //Made it look cooler, though uniform float LogoColorDistortion_Strength = 0.51; uniform float3 Logo_Shine_Line = {1, 1, 1}; uniform float Logo_scale = 0; uniform float2 Logo_positionXY = {0, 0}; uniform float Start_Logo_scale = 10; uniform float2 Start_Logo_positionXY = {0, 0}; uniform float Distortion_wave_strength = 1; uniform float phase2Start = 1; uniform float phase3Start = 1; float2 GetCenteredUV(float2 uvIn) { return uvIn - float2(0.5, 0.5); } float2 GetCenteredUVScaled(float2 uvIn, float X_scale) { float2 result = uvIn - float2(0.5, 0.5); return result * float2(X_scale, 1.0); } float2 RestoreNormalUVCoordinate(float2 uvIn) { return uvIn + float2(0.5, 0.5); } float2 MirrorUV(float2 uv) { return 1.0 - abs(frac(uv * 0.5) * 2.0 - 1.0); } //Based on https://realtimevfx.com/t/collection-of-useful-curve-shaping-functions/3704 float SmoothSlowdownCurve(float x) { return 1.0 - pow(abs(x - 1.0), 3.5); } uniform float4 background_color = {0.0, 0.0, 0.0, 1.0}; void GetTransformedLogo(float2 screenUV, float2 uv, float distortionStrength, out float3 logoPixel, out float2 distortedUV, out float logoAlpha) { float4 sampledLogo = LogoTexture.Sample(textureSampler, uv); logoAlpha = sampledLogo.a; logoPixel = sampledLogo.rgb; float2 logoUVDistortion = sampledLogo.rg - LogoColorDistortionXY; //This may need to be modified for blue-ish logos logoUVDistortion = logoUVDistortion * distortionStrength; float2 distoredSceneAUV = screenUV + logoUVDistortion; distortedUV = MirrorUV(distoredSceneAUV); } float GetGradient(in float2 screenUV, float percent) { //Create diagnal gradient float gradientX = screenUV.x * Aspect_ratio; float gradientY = screenUV.y; gradientX = gradientX - 0.88; gradientY = gradientY - 0.5; float fullGradient = gradientX + gradientY; float tVal = lerp(1.5, -2.5, percent); //this might need to be adjusted on wider aspect ratios :-/ fullGradient = fullGradient - tVal; fullGradient *= 10; //sharper edge return fullGradient; } void GetWaveyParttern(in float2 centeredScaledUV, in float2 centeredUV, float percent, out float ramp, out float visibility) { float len = length(centeredScaledUV); float tValue = lerp(-2, 2, percent); visibility = len + tValue; float secondaryValue = 1 - visibility; secondaryValue = secondaryValue * secondaryValue; secondaryValue = 1 - secondaryValue; ramp = secondaryValue; visibility = clamp(visibility, 0, 1); } float4 mainImage(VertData v_in) : TARGET { float2 squareImageScaledUVs = GetCenteredUVScaled(v_in.uv, Aspect_ratio); float2 recenteredUVs = GetCenteredUV(v_in.uv); if(transition_time < phase2Start) { float tempT = transition_time / phase2Start; //Transform start and UVs float2 logoPosition = squareImageScaledUVs / float2(Logo_scale, Logo_scale); logoPosition += float2(Logo_positionXY.x * -1.0, Logo_positionXY.y); float2 logoPositionStart = squareImageScaledUVs / float2(Start_Logo_scale, Start_Logo_scale); logoPositionStart += float2(Start_Logo_positionXY.x * -1.0, Start_Logo_positionXY.y); //Use linear interpoation over time and sample pixel float2 finalPosition = lerp(RestoreNormalUVCoordinate(logoPositionStart), RestoreNormalUVCoordinate(logoPosition), SmoothSlowdownCurve(tempT)); float3 logoPixel; float2 distortedLogoPixel; float logoAlpha; GetTransformedLogo(v_in.uv, finalPosition, LogoColorDistortion_Strength, logoPixel, distortedLogoPixel, logoAlpha); //Mix with background float2 combinedUV = lerp(v_in.uv, distortedLogoPixel, logoAlpha); float3 result = image_a.Sample(textureSampler, combinedUV).rgb; if(convert_linear) result = srgb_nonlinear_to_linear(result); return float4(result, 1.0); } else if(transition_time < phase3Start) { float tempT = (transition_time - phase2Start) / (phase3Start - phase2Start); //Transform start and UVs float2 logoPosition = squareImageScaledUVs / float2(Logo_scale, Logo_scale); logoPosition += float2(Logo_positionXY.x * -1.0, Logo_positionXY.y); logoPosition = RestoreNormalUVCoordinate(logoPosition); float sceneVisiblity; float rampVisibility; GetWaveyParttern(squareImageScaledUVs, recenteredUVs, tempT, rampVisibility, sceneVisiblity); float2 distortedUV2 = recenteredUVs * (1- clamp(rampVisibility, 0, 1)); distortedUV2 = distortedUV2 + 0.5; distortedUV2 = v_in.uv + distortedUV2; distortedUV2 = distortedUV2 * 0.5; //Mix with background float3 logoPixel; float2 logoDistoredUV; float logoAlpha; GetTransformedLogo(distortedUV2, logoPosition, LogoColorDistortion_Strength, logoPixel, logoDistoredUV, logoAlpha); float2 sampledBackgroundUV = lerp(v_in.uv, distortedUV2, rampVisibility); sampledBackgroundUV = lerp(sampledBackgroundUV, logoDistoredUV, logoAlpha); float3 distoredBackgroundA = image_a.Sample(textureSampler, sampledBackgroundUV).rgb; float3 distoredBackgroundB = image_b.Sample(textureSampler, sampledBackgroundUV).rgb; float gradientValue = GetGradient(v_in.uv, tempT); float clampedGadient = clamp(gradientValue, 0, 1); float appearLine = clamp(1 - (gradientValue * gradientValue), 0, 1); appearLine = appearLine * logoAlpha; float3 blendedBackgrounds = lerp(distoredBackgroundA, distoredBackgroundB, sceneVisiblity); float3 blendedWithLogo = lerp(blendedBackgrounds, logoPixel, logoAlpha); float3 result = lerp(blendedBackgrounds, blendedWithLogo, clampedGadient); result += appearLine * Logo_Shine_Line; if(convert_linear) result = srgb_nonlinear_to_linear(result); return float4(result, 1.0); } else { float tempT = (transition_time - phase3Start) / (1 - phase3Start); float tempSub1 = clamp(tempT * 2, 0, 1); float tempSub2 = clamp((tempT * 2) - 1, 0, 1); float2 finalPosition = squareImageScaledUVs / float2(Logo_scale, Logo_scale); finalPosition += float2(Logo_positionXY.x * -1.0, Logo_positionXY.y); float3 logoPixel; float2 distortedLogoPixel; float logoAlpha; GetTransformedLogo(v_in.uv, RestoreNormalUVCoordinate(finalPosition), LogoColorDistortion_Strength, logoPixel, distortedLogoPixel, logoAlpha); float2 combinedUV = lerp(v_in.uv, distortedLogoPixel, logoAlpha); float3 distortedBackground = image_b.Sample(textureSampler, combinedUV); float3 blendedWithLogo = lerp(distortedBackground, logoPixel, logoAlpha); float3 clearBackground = image_b.Sample(textureSampler, v_in.uv); float gradientValue = GetGradient(v_in.uv, tempSub1); float clampedGadient = clamp(gradientValue, 0, 1); float appearLine = clamp(1 - (gradientValue * gradientValue), 0, 1); float3 result = lerp(blendedWithLogo, clearBackground, clampedGadient); result = lerp(result, distortedBackground, appearLine); if(convert_linear) result = srgb_nonlinear_to_linear(result); return float4(result, 1.0); } }
-
0451 Vault - No, I'm not a Human (2025)
I guess one could describe ‘‘No, I’m not a Human’’ as kind of advanced choose your own adventure game with a bit of randomization. Concept is simple - some monsters have appeared that will come into your house and kill you if you are alone - so you need to let someone in, but who do you let in? During that FEMA is quickly formed to help to control the crisis and study Visitors offering possibly cluses on how to identify them. A phone number? 0-00-451!
-
0451 Vault - Peripeteia (2025)
Peripeteia, currently in Early Access, is a Polish indie immersive sim inspired by Deus Ex and games from Looking Glass Studios. Unsurprisingly, during the 2nd mission, in which we are asked to eliminate Jakub Franciszek, we may eventually come across a PC that requires us to enter a login and password. While I won’t spoil the login, the password turns out to be 0451.
-
0451 Vault - Home Safety Hotline - Seasonal Worker (2024)
A DLC to Home Safety Hotline has a little 451 easter egg hidden during the last day. When called by Geoffrey, he’ll tell you his location - 451 Grover Lane. OK then.
-
0451 Vault - Silent Hill 2 (2024)
The Silent Hill 2 remake had a pretty high bar to clear. The original 2001 game is a cult classic, filled with complex narrative elements and not-so-obvious symbolism. The remake not only had to attract a new generation of fans, but also satisfy the vast majority of old ones with new content. Did it succeed? In my opinion, yes.
One of the things it added, however, was a little digital safe puzzle at the motel, where one of the notes hides a well-known combination - 0451.
-
0451 Vault - Still Wakes the Deep (2024)
Still Wakes the Deep is a horror game developed by The Chinese Room. It’s far from being an immersive sim or even something with hub-like design similar to Amnesia - just a linear game. In the first hour of so of the game, you end up picking a fire-extinguisher and putting out some fires, which gets you an achievement “Fahrenheit 0451” - which woud be probably accidental if it wasn’t for the 0 at the beginning.
-
0451 Vault - Phantom Fury (2024)
Phantom Fury is a shooter that advertises itself as inspired Half-Life and SiN inspire and while to some extend this is true, it has all of the faults that the other titles of Slipgate Ironworks have. And it features that easter egg - because they implemented keypads ¯\(ツ)/¯
-
0451 Vault - Shadows of Doubt (2023)
Shadows of Doubt is a first-person detective game with a procedurally generated world and cases to solve. 0451 Easter egg is easy to miss - when you start a PC in the game, it displays command line text. One of them says:
SBL>LOADBOOTTOOLS[0451]>CRUNCHCrunch? CRUNCH?! Oh no….
-
0451 Vault - Fortune's Run (2023)
Fortune’s Run is a fast pace first-person shooter mixed with stealth… maybe immersive sim for some (certainly way of im sim than most of the titles that tried to advertise themselves as such in the last year). Whatever you see it as immersive sim or not - it still takes a lot of inspirations from Deus Ex or EYE Divine Cybermancy (per developer’s words) and it just wouldn’t be complete with 0451 put somewhere as an easter egg - here, a classic, which is: Open the door with keypad in a tutorial segment - code is somewhere in the enviroment. Good stuff!
-
0451 Vault - Weird West (2022)
With recent problems of Arkane, we’ve had yet another situation where veteran devs quit to try something on their own. The result of that is 2022’s Weird West by newly formed WolfEye Studios - a very interesting immersive sim with an isometric camera perspective, which once again features good old 0451 easter egg around 1,5h into the game.
For more posts check archive.
subscribe via RSS








