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
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.
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)
float averageCameraPos = 1 / ((abs(cameraPos.x) + abs(cameraPos.y) + abs(cameraPos.z)) / 3);
s.albedo = vec4(uColor, s.albedo.a) * screenMap;
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:)
float distance = length( cameraPos - s.vertexPosition );
Rather than the 'averageCameraPos' thing you're doing now. I bet this will work
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.
here is the error text:
any help would be appreciated!
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.