Home Marmoset

Custom Shader - Any way to access screen resolution / aspect ratio?

venain
polycounter lvl 12
Offline / Send Message
venain polycounter lvl 12
Hey guys,

I'm currently writing a custom shader that makes heavy use of screen space UV's. The problem is, I can't seem to find a way to access the screen resolution/aspect ratio, so  the textures I run in screenspace are stretched horizontally. I'd also like to scale the UV's based on distance from camera, which I also couldnt figure out. I'm pretty new to doing shaders in code, so there might be something obvious i'm missing?

Thanks!

Replies

  • jeffdr
    Options
    Offline / Send Message
    jeffdr polycounter lvl 11
    Hi,

    These things might be a tad tricky, but I think they're possible. So first, regarding screen resolution, we haven't directly exposed that for custom shader authors (sorry), BUT there may be a workaround. The value is available in mat.frag (the central material shader) in uScreenTexCoordScaleBias. So if you could define your subroutine macros to receive this value, you'd have access to it. Something like this:

    void myFunc( FragmentState s, vec2 screenResInv ) {
       //your shader here. screenResInv contains { 1 / width, 1 / height }
    }
    #define Albedo(s)  myFunc(s, uScreenTexCoordScaleBias.xy)

    (This example presumes you're adding an Albedo shader, but the same approach works with any other subroutine). The idea here is you're passing a sort of secret value to your custom shader, one that the main shader architecture usually uses exclusively.

    Lastly you can get camera distance from the screenDepth member of FragmentState. The tricky part here is that the value is post-perspective depth, and highly nonlinear. You'd probably need to reconstruct depth from this value with a term like:

    float camDepthSorta = c / s.screenDepth;  //where 'c' is a constant of your choosing (something that looks good)

    I hope this is helpful. I realize this is a bit tricky, especially if you're new to shader development, but these are the best answers in Toolbag right now.
  • venain
    Options
    Offline / Send Message
    venain polycounter lvl 12
    Hey Jeff,

    Thanks for the reply!

    I was able to get the aspect ratio correction working!
    Scaling the texture by depth proved a lot tricker though, your suggestion did work in theory, but since its doing it per fragment, the texture starts to get insane at certain distances/angles. What I really want is to scale the texture by distance to object, which I don't think is currently possible?

    I did find an extremely hacky solution using "uLightSpaceCameraPosition" from mat.frag. I don't actually know what this is, and the results were... strange... but kinda worked. If I had access to just straight up camera position I think my solution would work fine for my needs, but with uLightSpaceCameraPosition I get huge value swings when rotating, especially close up. Here's my code for reference (I just made a new custom shader for testing this stuff, I haven't merged any of this into my real shader yet)

    #include "../state.frag"

    USE_TEXTURE2D(tScreenMap);
    uniform vec3 uColor; //color name Albedo default 1 1 1
    uniform float uTilingRate; //name "Tiling Rate" min 0 max 10 default 1
    uniform float uDepthOffset; //name "Depth Offset" min 0 max 20 default 1

    void customAlbedo( inout FragmentState s, vec2 screenRes, vec3 cameraPos )
    {
    float aspectMod = screenRes.x / screenRes.y;

    float averageCameraPos = 1 / ((abs(cameraPos.x) + abs(cameraPos.y) + abs(cameraPos.z)) / 3);
    float cameraDistance = smoothstep(0, 1, averageCameraPos);
    float depthMod = lerp(uDepthOffset, (uDepthOffset / 5), cameraDistance);

    vec2 fixedScreenTexCoord = vec2(s.screenTexCoord.x, (s.screenTexCoord.y * aspectMod));
    vec4 screenMap = texture2D(tScreenMap, (fixedScreenTexCoord * uTilingRate * depthMod));

    s.albedo = vec4(uColor, s.albedo.a) * screenMap;
    }
    #ifdef Albedo
    #undef Albedo
    #define Albedo(s) customAlbedo(s, uScreenTexCoordScaleBias.xy, uLightSpaceCameraPosition.xyz)


    the next thing I want to accomplish is to scale the UV's from the center instead of the corner, which I'm pretty sure is info that's out there and should be doable in toolbag.

    If there is a way for me to access camera position or distance to object or something like that, let me know! Thanks again:)
  • jeffdr
    Options
    Offline / Send Message
    jeffdr polycounter lvl 11
    Oh, good call with passing uLightSpaceCameraPosition; that's a better idea. To get distance to the camera from the object, you probably want:

       float distance = length( cameraPos - s.vertexPosition );

    Rather than the 'averageCameraPos' thing you're doing now. I bet this will work :)
  • venain
    Options
    Offline / Send Message
    venain polycounter lvl 12
    oh! great! I'll try that when I get home tonight.

    I might also have more questions, the main part of the shader I'm making is in the diffusion subroutine. I was trying to pass uScreenTexCoordScaleBias into my function as I did in the albedo one above, and it was failing every time. I'm not exactly sure what I was doing wrong, as I did it exactly like above, just replacing albedo with diffusion basically. Sorry I don't have a code snippet, i'll post one later tonight if I keep hitting this wall.
  • venain
    Options
    Offline / Send Message
    venain polycounter lvl 12
    Ok, so I definitely hit a wall. No matter what I try I cannot pass uScreenTexCoordScaleBias into my function once i'm trying to do it in the diffusion subroutine instead of albedo. I'm sure it has something to do with the way I wrote the macro because I don't really understand how macros work... Either way, here's the code I'm trying to get to work, i've stripped it down to almost nothing just to try and get it to run at all, and I've tried every combination of things that make sense to me in that macro line, but I'm at a loss:(

    #include "../state.frag"
    #include "../other/lightParams.frag"

    #if defined(MATERIAL_PASS_LIGHT) || defined(MATERIAL_PASS_VOXELIZATION)

    void customDiffusion( inout FragmentState s, LightParams l, vec2 screenRes )
    {
    s.diffuseLight = s.diffuseLight;
    }
    #ifdef Diffusion
    #undef Diffusion
    #define Diffusion(s,l) customDiffusion(s, l, uScreenTexCoordScaleBias.xy)

    #endif 


    here is the error text:

    opening code C:/Program Files/Marmoset/Toolbag 3/data/shader/mat/custom/Lightingtestbed.frag
    CPR Error (cpr::D3D11ShaderCode::onCreate line 95):
    C:\Program Files\Marmoset\Toolbag 3\Shader@0x0000022D2D2FDBA0(1822,5-19): error X3004: undeclared identifier 'uScreenTexCoordScaleBias'

    CPR Error (cpr::logSourceWithLineNumbers line 329):
    B18| #ifdef Diffusion
    B19| #ifdef DiffusionParams
    B20| Diffusion(s, p, diffParams);
    B21| #else
    B22| Diffusion(s, p);
    B23| #endif
    B24| #endif
    B25|
    B26| #ifdef Reflection

    CPR Error (cpr::D3D11ShaderCode::onCreate line 128):
    D3D11CompileShader failed
    CPR Error (cpr::D3D11ShaderCode::onCreate line 95):
    C:\Program Files\Marmoset\Toolbag 3\Shader@0x0000022D2D2FDBA0(1673,5-19): error X3004: undeclared identifier 'uScreenTexCoordScaleBias'

    CPR Error (cpr::logSourceWithLineNumbers line 329):
    @69| #ifdef Diffusion
    @70| #ifdef DiffusionParams
    @71| Diffusion(s, p, diffParams);
    @73| Diffusion(s, p);
    @77| #ifdef Reflection

    CPR Error (cpr::D3D11ShaderCode::onCreate line 128):
    D3D11CompileShader failed


    any help would be appreciated!


  • jeffdr
    Options
    Offline / Send Message
    jeffdr polycounter lvl 11
    Ah yes, that's going to be tough, because Diffusion... won't have access to that uniform (it's called within another lighting function, so passing uScreenTexCoordScaleBias to it directly won't work, as you've noticed).

    Another workaround: define the Premerge() subroutine in addition to your diffusion function, and in there, store the light vector for later retrieval in the fragment state. Something like:

    void PrefetchCamPos( FragmentState s, vec2 screenSize ) {
       s.generic3.xy = 
    screenSize;
    }
    #define Premerge(s) 
    PrefetchCamPos(s, uScreenTexCoordScaleBias )

    and then later in your diffusion function, instead of trying to pass the screen size, just read s.generic3.xy to get the value :)

    I realize this is a bit hacky but it's kind of cool that it's possible even though we didn't foresee support for this. Thanks for your patience.
  • venain
    Options
    Offline / Send Message
    venain polycounter lvl 12
    I was able to get everything working, thanks a bunch for your help!
  • jeffdr
    Options
    Offline / Send Message
    jeffdr polycounter lvl 11
    Glad to hear it! Once your shader is done, if you feel like sharing it with the world drop us a line and we can link it from our page: https://marmoset.co/toolbag/add-ons/
  • venain
    Options
    Offline / Send Message
    venain polycounter lvl 12
    Will do! We'll see how long it takes to get all the features I want in there, I'm sure I'll have more questions. Here's a quick preview of where I'm going with it, this is with just direct lighting. I'm planning on doing some other stylized effects for the other lighting passes as well, but it's also a big learning project for me, so it'll be slow.


Sign In or Register to comment.