xan1242 / nfsc_fixstreakflares

NFS Carbon PC - Streak Flare fixer
MIT License
19 stars 2 forks source link

PS3-like streaks? #1

Open TorutheRedFox opened 2 years ago

TorutheRedFox commented 2 years ago

The PS3 version (imo) has nicer looking streaks which, rather than using a texture, filter the flares and lerp the result with the previous frame's result in order to perform the streaks (like UG2 streaks but better looking)

might be a bit of a stretch, but could be worth a shot

xan1242 commented 2 years ago

Not impossible, especially considering this rendering technically already exists for motion blurring (previous frames), except that the flares and particles are omitted from its texture.

Ideally this would be done specifically for the flares into its own texture(s) so we could replicate the effect, but I'm not sure how feasible this is. This would very likely require more code hacks to allow for it, but if anybody has an idea how this works, I'd be willing to give it a shot.

We (technically) have some texture samplers free for this (MISCMAP textures) but I'm not very well versed in D3D coding to achieve the render to texture thing to work.

TorutheRedFox commented 2 years ago

the way I'd implement it (could not be how it's done though) is to render the flares into two color buffers using MRTs, with the first buffer just being the main backbuffer, and second being the streak buffer which gets 'cleared' with a 30%-ish maybe opacity quad (with the second output having 0.8 subtracted from it)

EAGL already has MRT support (as evident by a number of shaders using an output struct for the pixel shader rather than directly outputting to COLOR0)

xan1242 commented 2 years ago

Could you provide an example? I'm not exactly sure where to start with this (except by looking at already existing code in the game).

xan1242 commented 2 years ago

use this visualtreatment shader and wait for it to rain https://pastebin.com/8xuDNRxR

Wrong issue, this is for NFSC_CustomVT. (btw - no need to wait for rain, toggle precipitation debugging with exopts or with my custom ReShade)

TorutheRedFox commented 2 years ago

not sure what you mean by example

as in HLSL?

TorutheRedFox commented 2 years ago

use this visualtreatment shader and wait for it to rain https://pastebin.com/8xuDNRxR

Wrong issue, this is for NFSC_CustomVT. (btw - no need to wait for rain, toggle precipitation debugging with exopts or with my custom ReShade)

yeah i just realised that lmfao

xan1242 commented 2 years ago

not sure what you mean by example

as in HLSL?

Yes, HLSL would be good. It doesn't have to be game compatible or anything, just to see how it would be done.

I'm not very familiar with shader coding or D3D coding so I'd like to know how it is implemented in code to see if it is even feasible. Right now I don't have a good idea of how to actually write a shader which will produce a trail effect at all (because I am basically a noob at this).

TorutheRedFox commented 2 years ago

well this shader would need an extra buffer to be set up and passed to the shader, with the blend set to one on both srcblend and dstblend (and an extra 50% ish alpha pass with the opacity modulated by deltatime so it's not framerate bound), but it'd go something like this:

struct PS_OUTPUT_FLARES
{
    float4 color     : COLOR0;
    float4 streak    : COLOR1;
};

PS_OUTPUT_FLARES pixel_shader_flares(const VtoP_FLARES IN) : COLOR
{
    PS_OUTPUT_FLARES OUT;
    float4 diffuse = tex2Dbias(DIFFUSE_SAMPLER, IN.tex);

    float scaled_fuzz_width = IN.tex1.w;
    float depth = tex2D(HEIGHTMAP_SAMPLER,IN.tex1.xy).x;
    float fuzzz = saturate((scaled_fuzz_width*0.4 - (IN.tex1.z - depth)) / (scaled_fuzz_width));

    float4 result;  
    result = diffuse;
    result *= IN.color;//*cvBaseAlphaRef.y;
    // Apply a tone mapping to fake a HDR
    result.xyz = result.xyz / (1.5-result.xyz);
    //result.xyz = pow(result.xyz, 1.5)*1.5;
    //result.xyz    = result.x > 0.95 ? 1.0f : 0.0f;
    // cvBaseAlphaRef.x is set to 0 in players views so the fuzzz used and 1 to disable the fuzz
    result.w *= saturate(cvBaseAlphaRef.x + fuzzz);

    //result.xyz = fuzzz;//(scaled_fuzz_width - (IN.tex1.z - depth)) / FuzzWidth;
    //result.w = 1;
    //result.xyz = CompressColourSpace(result.xyz);

    // say it's 0.8 as an example, not sure what it'd actually be to look good without seeing it ingame first
    float kFlareStreakThreshold = 0.8; 

    OUT.color = result;
    OUT.streak = result-kFlareStreakThreshold;

    return OUT;
}
TorutheRedFox commented 2 years ago

and here's the DX9 doc for MRTs https://docs.microsoft.com/en-us/windows/win32/direct3d9/multiple-render-targets

TorutheRedFox commented 2 years ago

GPU needs D3DPMISCCAPS_MRTPOSTPIXELSHADERBLENDING support it seems though (as technique pretty much relies on being able to do blending in MRTs)

an alternate method that wouldn't need MRTs would be having a separate technique that'd run a wrapper like this directly into a separate buffer (but still with the 50-ish % opacity black quad clear to keep contents from previous frame but at a lower brightness)

float4 pixel_shader_flares_streak(const VtoP_FLARES IN) : COLOR
{
    // say it's 0.8 as an example, not sure what it'd actually be to look good without seeing it ingame first
    float kFlareStreakThreshold = 0.8; 
    return pixel_shader_flares(IN) - kFlareStreakThreshold;
}
TorutheRedFox commented 2 years ago

seems like a threshold of 0.2 is good (might need to divide it afterwards though too)

TorutheRedFox commented 2 years ago

yeah definitely needs dividing it's bright as hell lmao (currently just adding OUT.streak to OUT.color after computing it to visualize) image

TorutheRedFox commented 2 years ago

OUT.streak = saturate(result-kFlareStreakThreshold) / 4; seems okay

although another thing to note would be that the streaks seem to apply to all particles on PS3 (with sparks being bright enough to get above the threshold)

TorutheRedFox commented 2 years ago

thinking about it they could be also just done in a similar manner to NOS streaks, just remembering screen space position rather than world space, and well, using a separate technique for thresholding (which applies to sparks and other bright particles too it seems)