Home 3D Art Showcase & Critiques

UDK Physically Based Lighting!

2

Replies

  • almighty_gir
    Offline / Send Message
    almighty_gir ngon master
    it worked when i tried it out... if you plug it in exactly as i said then it will have the effect that you want.

    the custom node just contains the calculation i'd found for the way roughness is iterpolated from 0-1. so, all you need to input into that node is a value from 0-1. by placing the one-minus as i said, you're still feeding it a 0-1 value, it's just reversed.

    i don't know why it isn't working for you, when it does work for me?

    i'll take another look in a short while, i'm working on making the specularity energy conservant right now.
  • MooseCommander
    Alright, I guess words aren't cutting it! To images!

    First, a clear guide to picking your roughness color and how it affects the texture.
    pbl_scale.jpg

    Now below, I am previewing the shader as it is regularly set up. No adjustments made. The preview on the left is coming from the custom node that sets up the specular D.

    pbl_normal.jpg

    We can see that the BLACK section of the material is creating a strong highlight. According to the PBL graph above, this is inverted. The white section should create this highlight.

    Below, I've added a OneMinus node in the section that you suggest. You can see the outcome does not fix the problem, because all we are doing is inverting the roughness map. The specular values are still being applied wrong. White still has a broad, diffused highlight. Black still has a strong, localized highlight.

    pbl_inverted.jpg

    Honestly, is it that big of a deal? Not really. But the theory in practice is incorrect, which is why I suggest addressing it vs. ignoring it. If the goal is to make a PBL shader, you'd want it to interpret roughness correctly.
  • almighty_gir
    Offline / Send Message
    almighty_gir ngon master
    you're looking at one part of the whole sum. and that's why it doesn't look like it's working to you.

    the whole sum for the BRDF is:
    (D*F*G)/(4 * NoL * NoV)

    where D = that custom node (Specular Distribution), G = geometric shadowing, F = fresnel.

    see supporting images here for the the full formula node.
    with one-minus
    http://i.imgur.com/yD14cuh.png
    http://i.imgur.com/mh0a22a.png

    without one-minus
    http://i.imgur.com/ow1grcI.png
    http://i.imgur.com/Hf4Ff8c.png

    here's why you're getting confused:
    there's nothing wrong with the math. the math is fine. read through the documentation and he says:
    an interesting comment that has followed is: “Roughness feels
    inverted.” It turns out that artists want to see the texture they author as brighter texels equals brighter specular highlights.

    which is exactly what you've just said. the way they fixed it was to invert the input, which is what i've suggested to you to do.

    plug a constant into the roughness instead of a texture, and you'll see what i'm talking about.
  • MooseCommander
    Cool, thanks for the breakdown. I appreciate you taking the time to explain it out more thoroughly. I definitely "get it" now, which I obviously didn't before. Pardon my ignorance!

    I'll mess around with it a bit more.

    I also wanted to show off really quickly some results I've got from making some minor adjustments. I changed some of the HLSL in my version to be node based as Epic suggests it is better for performance, and added a few constant clamps because some of the values were getting blown out. I also moved the bulk of the shader into a Material Function so that it isn't as bulky to work with in day-to-day use. Some other minor adjustments to the network to trim some fat and lower the instruction count - currently at 120 or so instructions.

    Here's my results so far.

    pbl_test.jpg
  • almighty_gir
    Offline / Send Message
    almighty_gir ngon master
    hey man that's really cool!

    would you mind sharing your version? (if you want to do so privately, my email is lee_devonald@crazyferretstudios.com ). i'm pretty novice to this stuff, so any opportunity to learn is gratefully received!
  • MooseCommander
    Of course! Let me work on it a little bit more (in terms of clean up and such). I'll gladly post it here once I do.

    If you don't mind, I'll probably include my version in the Master Material Kit I posted recently, with credit due where it is deserved, of course.
  • Vrav
    Offline / Send Message
    Vrav polycounter lvl 11
    Pretty cool. But almighty_gir, why not put metalness in the alpha channel so it can be exported as one bit per pixel, anyway?
  • almighty_gir
    Offline / Send Message
    almighty_gir ngon master
    i can do that, sure. will do that in the next update.
  • almighty_gir
    Offline / Send Message
    almighty_gir ngon master
    [ame="http://www.youtube.com/watch?v=yk7zUa9tu7E"]Inside Unreal Engine 4 - Layered Materials Demo HD - YouTube[/ame]

    going to be adding this functionality over the next couple of days (layering materials).
  • almighty_gir
    Offline / Send Message
    almighty_gir ngon master
    BIG OOOOOOOL' UPDATE!

    http://crazyferretstudios.com/public/PhysicallyBasedLighting_v1_03.upk

    Have just put in a basic layering system! you can now have four different material layers which can be defined in the following ways!

    - Albedo Colour: choose your materials base albedo. use texture on/off set to 0.
    - Albedo Texture: if you want your material to have a texture, put it here. use texture on/off set to 1.
    - Metalness: is now a constant (not a mask/map) please use 0 for dielectric, and 1 for metals.

    You now need two different mask textures. the first is the material mask, which works in the following ways:
    R: Black = layer 1, White = layer 2.
    G: Black = layers 1 + 2, White = layer 3.
    B: Black = layers 1 + 2 + 3, White = layer 4.

    think of it as building up from a base. if you were texturing a metal pipe made of copper, with black glossy paint, and a little surface rust. this would only require 3 layers. so you would start off with the copper, and mask that off with black in the red channel. then add surface rust in the second material, and mask that with white in the red channel. then add black gloss paint to the third material, and mask that with white in the green channel.

    the second mask is roughness. you will need one roughness mask for each material.
    R = material 1,
    G = material 2,
    B = material 3,
    A = material 4.


    there is a (very very basic and bad) set of example textures in the package to demonstrate. i only spent a couple of minutes making them.

    this system should hoooopefully allow people to use tiled textures on the various channels and come up with some cool effects. it also allows you to choose whether or not you want to use a basic constant coloured material, or texture per material, or even mix between them.

    current functionality removed:
    emission, cavity map. these will be added in again soon when i can figure out an efficient way of doing it.

    i'll make a good example asset and some maps over the next couple of days to demonstrate better, unless someone beats me to it!

    cheers guys, have fun!
  • ysalex
    Offline / Send Message
    ysalex interpolator
    Amazing, thanks a ton for this.
  • supaclueless
    Offline / Send Message
    supaclueless polycounter lvl 13
    It might be my incompetence but the current build of the shader does not seem to work with your preset textures.

    Wanted to test out the new build and from what I can understand; it seems that some nodes are disconnected, making the material incomplete and unable to work.

    Regardless, awesome work for making this shader, allowing us to dip our hands into PBL type shaders! Hopefully i can make something to show off your shader. :D

    EDIT: Just found out that it was the master mat functions that were broken
  • almighty_gir
    Offline / Send Message
    almighty_gir ngon master
    hey man,

    if you have that problem again, you should be able to resolve it by just making a new material instance constant.

    also, i've just made a quick fix to an issue i forgot about (it's 4am... so bear with me lol). where normalmaps could't be blended between materials.

    this is now fixed! please redownload from the same link.

    cheers!
  • supaclueless
    Offline / Send Message
    supaclueless polycounter lvl 13
    No problem! I just wanted to note this to you ASAP so that other ppl who suck at shader editing (me) will know whats going on and how to fix it.

    Keep up the great work!
  • Santewi
    Your material functions that you used in the material are missing from the upk (I suppose they are the ones included but you just forgot to change the path in the master material?). Also, you're loading stuff from the v. 1.01 and 1.02 packages, so you'll get a ton of errors if you only have the latest upk. You might want to put all the necessary stuff in the latest upk.

    5ymxJsD.png
  • JordanLeigh
    Offline / Send Message
    JordanLeigh polycounter lvl 9
    I'm new to all this and don't really know what I'm doing tbh but how do i even load the upk? Everytime I try I get " unable to load package..."
  • almighty_gir
    Offline / Send Message
    almighty_gir ngon master
    Santewi wrote: »
    Your material functions that you used in the material are missing from the upk (I suppose they are the ones included but you just forgot to change the path in the master material?). Also, you're loading stuff from the v. 1.01 and 1.02 packages, so you'll get a ton of errors if you only have the latest upk. You might want to put all the necessary stuff in the latest upk.

    5ymxJsD.png

    thanks for the heads up man!

    have resolved all of those issues, redownloading the latest file should fix them for you!

    I'm new to all this and don't really know what I'm doing tbh but how do i even load the upk? Everytime I try I get " unable to load package..."

    place the .upk file somewhere in your udk directory, i'd advise "UDK\UDK-2013-07\UDKGame\Content". then, when you open UDK, find that directory in the content browser, right click on the package and click "fully load". it should load fine, as long as you're running the latest version of udk!
  • JordanLeigh
    Offline / Send Message
    JordanLeigh polycounter lvl 9
    ahh didn't have the latest version. Thanks for getting back to me :D
  • Santewi
    Still missing 3 functions inside the BRDF function.
  • almighty_gir
  • Santewi
    Seems to work now, although when a material's metalness is set to 1, all specular lighting is gone and it just leaves a very dim cubemap... Is this intended behavior or not? At least to me, a material with a metalness value of .9 looks more metallic than one with 1.
  • almighty_gir
    Offline / Send Message
    almighty_gir ngon master
    hey Santewi, that was absolutely not intended, and it has now been fixed.

    the issue was that i hadn't lerped a couple of textures properly. please redownload for the fix.

    i'll spend some time this evening cleaning everything up a little more, and trying to get the instruction count down where possible.
  • Drew++
    Offline / Send Message
    Drew++ polycounter lvl 14
    Good job so far!
    I do have some input here... It looks like you're using the G term that was described in the paper, which seems fine. It's not 100% right though in practice/use, since we make a substitution of G, and introduce something called V, for visibility. This was what Lazarov did in his 2011 paper...

    So V is literally " Gterm(NoL, m) * Gterm(NoV, m) "

    And your final specular is " D * V * F " rather than " (D * G * F) / (4*NoV*NoL) "

    Also I see you applying this term to your IBL/reflections... The G term described by Karis is not meant for IBL, but is only used for analytic light sources. If you apply it to IBL, at glancing angles it will be too dark. You can stick with a regular Smith-Schlick in this case.
  • almighty_gir
    Offline / Send Message
    almighty_gir ngon master
    thanks for the feedback Drew.

    i'm reaching a point where i'm not sure how much further i can take this, unfortunately. i'm way more of an artist first, than a tech guy. shaders started out as a fun experiment but my in depth knowledge is actually very shallow.

    i've learned a lot so far and i'm sure i'll continue to do so, but right now there are a couple of brick walls i'm running into that my own limitations are stopping me getting past.

    that said, i'll definitely look into what you've just pointed out. i'll work visibility into it. cheers for that!

    i'm fairly sure i used the Smith-Schlick for the IBL fresnel, i used:
    float Fc = pow( 1 - VoH, 5 );
    float3 F = (1 - Fc) * SpecularColor + Fc;
    
  • Drew++
    Offline / Send Message
    Drew++ polycounter lvl 14

    i'm fairly sure i used the Smith-Schlick for the IBL fresnel, i used:

    Yeah, the Fresnel term is fine. Meant the G term that was used on the IBL :poly124::thumbup:
  • Esselle
    Offline / Send Message
    Esselle polycounter lvl 10
    Are your shader equations simplified versions of UE4's ones?
  • almighty_gir
    Offline / Send Message
    almighty_gir ngon master
    I've tried to stick as closely to their equations as possible.
  • Santewi
    One more thing... You're including your cubemap reflections in your customlighting input, so you don't get reflections (and apparently metallic surfaces are only "lit" by the cubemaps?) on surfaces that aren't facing a light source.

    It doesn't really make sense that a surface would have to be lit directly to have indirect lighting (reflections). Right now if you have a metallic material it's completely back unless it's lit by direct light.
  • almighty_gir
    Offline / Send Message
    almighty_gir ngon master
    so are you suggesting that i put that into the diffuse instead? that would make no sense, as metals have black albedo, and all of their colour information comes from reflectance.
  • almighty_gir
    Offline / Send Message
    almighty_gir ngon master
    Drew++ wrote: »
    So V is literally " Gterm(NoL, m) * Gterm(NoV, m) "

    just a quick question regarding this, Drew:
    float a2 = roughness * roughness
    float G_V = NoV + sqrt( (NoV - NoV * a2) * NoV + a2 );
    float G_L = NoL + sqrt( (NoL - NoL * a2) * NoL + a2 );
    return  G_L * G_V;
    

    would that do the job?
  • Santewi
    Emissive would most likely be the best one, and that's how reflections have been done for a long time.

    Reflections are basically just light that has bounced off the environment, and it shouldn't depend on the artificial lights in a game.

    This presentation has some pretty good information on how much light is reflected at different angles on page 39. In this case you'd multiply your cubemap by the reflectance and plug it into emissive.
  • Esselle
    Offline / Send Message
    Esselle polycounter lvl 10
    Santewi wrote: »
    Reflections are basically just light that has bounced off the environment, and it shouldn't depend on the artificial lights in a game.
    Umh, but in this case doesn't the material shine also in totally black room whitout lights that can bounce?
  • Santewi
    Esselle wrote: »
    Umh, but in this case doesn't the material shine also in totally black room whitout lights that can bounce?

    Yes, it would, if the cubemap that was used incorrectly represented an environment that was lit. The cubemaps would actually have to somewhat accurately represent the amount of light coming from the environment for it to work properly.
  • Esselle
    Offline / Send Message
    Esselle polycounter lvl 10
    Uh right, i thought of static cubemap and not of those captured, that indeed would result black if there is no light... my bad :)

    Anyway almighty, if you don't mind, i was asking that because i found a missed node in G(V) (you plugged in the multiply instead of the divide) and also i found that K is only divided by 2 instead of 8, and i just wanted to know it was intentionally to obtain better results
  • Drew++
    Offline / Send Message
    Drew++ polycounter lvl 14
    just a quick question regarding this, Drew:
    float a2 = roughness * roughness
    float G_V = NoV + sqrt( (NoV - NoV * a2) * NoV + a2 );
    float G_L = NoL + sqrt( (NoL - NoL * a2) * NoL + a2 );
    return  G_L * G_V;
    
    would that do the job?

    This is the one for ambient specular? Hard to say how bright it is. Looks like a refactored Smith GGX? Could work. You can experiment, maybe even try a regular Smith-Schlick
        float a = 1.0f / sqrt(0.78539816f * roughness + 1.57079632f); // square "roughness" if you need to :P
        float G = 1.0f / (NoL * (1.0 - a) + a) * (NoV * (1.0 - a) + a);
    

    e: you can square the roughness first, like UE4 does.
  • almighty_gir
    Offline / Send Message
    almighty_gir ngon master
    have just corrected both issues you guys are talking about. haven't changed the Gsmith for the IBL yet, i'm working on redoing the entire IBL line anyway so i'll save that for another update.

    file here!
    http://crazyferretstudios.com/public/PhysicallyBasedLighting_v1_04.upk


    one thing that concerns me greatly about having the IBL in the emissive channel though, is that when you look at the slides demonstrating the lighting functions, you can see that they very definitely move to black when there is no light present. i honestly don't think they use the emissive channel in UE4 for this.
  • MooseCommander
    have just corrected both issues you guys are talking about. haven't changed the Gsmith for the IBL yet, i'm working on redoing the entire IBL line anyway so i'll save that for another update.

    file here!
    http://crazyferretstudios.com/public/PhysicallyBasedLighting_v1_04.upk


    one thing that concerns me greatly about having the IBL in the emissive channel though, is that when you look at the slides demonstrating the lighting functions, you can see that they very definitely move to black when there is no light present. i honestly don't think they use the emissive channel in UE4 for this.

    In my variation of the shader, I plugged the cubemap into the emissive, but it is multiplied by a scalar param to control how much comes through. Maybe not the most efficient way since the cubemap is being applied twice, but it looks nice, so take that for what you will.
  • almighty_gir
    Offline / Send Message
    almighty_gir ngon master
    now that i've fixed up the math, it looks the same as if it were in the emissive channel, except you get a nice falloff into darkness in the absence of light.

    so... yeah! i don't think they use the emissive!
  • Drew++
    Offline / Send Message
    Drew++ polycounter lvl 14
    Santewi wrote: »
    One more thing... You're including your cubemap reflections in your customlighting input, so you don't get reflections (and apparently metallic surfaces are only "lit" by the cubemaps?) on surfaces that aren't facing a light source.

    It doesn't really make sense that a surface would have to be lit directly to have indirect lighting (reflections). Right now if you have a metallic material it's completely back unless it's lit by direct light.

    Where we are talking about "IBL" here, we should be calling it "Ambient". There is Ambient Specular and Ambient Diffuse. Both should be part of the custom lighting, not the emmisive slot. This is an approximation we're doing here, so sometimes we have light sources that don't exactly match our cubemap. Another term for this is called "Punctual" lighting.

    So do your direct lighting ( D*G*F ) then do your ambient lighting(specular, and diffuse) and simply add that to your direct lighting, and you're done. Do not put it in the emmisive. Emmisive is strictly for self-illuminating materials/light emitting. :P

    If you don't add a Ambient Diffuse to your Custom lighting slot, don't really worry... "Custom lighting diffuse" is for objects that are in shadow, so it can apply the Ambient Lighting that is calculated by UDK. Just make sure you have the normal map applied to the Normal slot so it takes in account the normal map :V
  • MooseCommander
    I just checked out the new version, and it is looking good! I'm comparing your changes with some of mine. I like that you've broken the equations into material functions. I had originally stuffed it all inside one material function, but it's a nice way to break up the whole thing by putting function inside function.

    With the new shader, I'm at 135 instructions. Not terrible, and runs fine in my game.

    One issue I am running into is if I disconnect the emissive function, as you now suggest I do.

    In true IBL formats, the brightness of the cubemap would dictate the brightness of the model. So, I get a smooth falloff like you do, but it isn't realistic when I am using a cubemap of a real background. Maybe this is a specific case, but it is impossible to create the lighting of the scene, thus an emissive is the only way to provide "accurate" lighting to the object, using a cubemap.

    pbl_emissive.jpg

    Maybe I need to throw this into a game scene and generate a cubemap from there, instead of using my "fake" Marmoset style scene, and see how it goes with more than one light source.
  • Santewi
    Drew++ wrote: »
    Where we are talking about "IBL" here, we should be calling it "Ambient". There is Ambient Specular and Ambient Diffuse. Both should be part of the custom lighting, not the emmisive slot. This is an approximation we're doing here, so sometimes we have light sources that don't exactly match our cubemap. Another term for this is called "Punctual" lighting.

    So do your direct lighting ( D*G*F ) then do your ambient lighting(specular, and diffuse) and simply add that to your direct lighting, and you're done. Do not put it in the emmisive. Emmisive is strictly for self-illuminating materials/light emitting. :P

    If you don't add a Ambient Diffuse to your Custom lighting slot, don't really worry... "Custom lighting diffuse" is for objects that are in shadow, so it can apply the Ambient Lighting that is calculated by UDK. Just make sure you have the normal map applied to the Normal slot so it takes in account the normal map :V

    But if you apply the cubemap reflections to your customlighting/customlightingdiffuse, the reflections that are in the shadow of the main light source will be much darker than the faces that are facing the light source. The environment could bounce more light from the "shadowed side" and therefore result in incorrect lighting, yeah?


    I'll draw a picture...

    0cB2r6s.png

    So, the box on the left wouldn't be directly lit on the right side, and therefore UDK would use customlightingdiffuse for that face and you'd get darker reflections, even though the middle box would be brighter as it's being hit directly by the light.

    Same with the middle box. The face facing left is lit by the light, but it'd get darker reflections as the box it'd reflect is only hit by indirect lighting.


    What I'm trying to say is, if the cube already has good enough approximation of the scene's lighting (from the point of the mesh it is applied to), the lighting from the cube shouldn't depend on the lights on the scene, so why not plug it into the emissive (of course after getting proper reflection amounts with a fresnel function)?
  • almighty_gir
    Offline / Send Message
    almighty_gir ngon master
    Drew++ wrote: »
    This is the one for ambient specular? Hard to say how bright it is. Looks like a refactored Smith GGX? Could work. You can experiment, maybe even try a regular Smith-Schlick
        float a = 1.0f / sqrt(0.78539816f * roughness + 1.57079632f); // square "roughness" if you need to :P
        float G = 1.0f / (NoL * (1.0 - a) + a) * (NoV * (1.0 - a) + a);
    

    e: you can square the roughness first, like UE4 does.

    thanks for this, one problem though is that it's giving me this problem:

    BmROUQ4.jpg

    the last time i had this problem it was because NdotL needed to be clamped. but that isn't fixing the issue this time.
  • almighty_gir
    Offline / Send Message
    almighty_gir ngon master
    I've isolated the above issue to the following formula:
    float3 IBL =  SampleColor * F * G * VoH / (NoH * NoV);
    

    in particular, NoH is not working. if i plug a constant into it, it looks fine regardless of the value. so now i'm a little stuck.


    edit - i think i've solved it by renormalizing NoH.
  • MooseCommander
    I've isolated the above issue to the following formula:
    float3 IBL =  SampleColor * F * G * VoH / (NoH * NoV);
    

    in particular, NoH is not working. if i plug a constant into it, it looks fine regardless of the value. so now i'm a little stuck.


    edit - i think i've solved it by renormalizing NoH.

    Is there an instruction count or visual improvement to these changes?
  • Serial Lens
    you're looking at one part of the whole sum. and that's why it doesn't look like it's working to you.

    the whole sum for the BRDF is:
    (D*F*G)/(4 * NoL * NoV)

    where D = that custom node (Specular Distribution), G = geometric shadowing, F = fresnel.

    see supporting images here for the the full formula node.
    with one-minus
    http://i.imgur.com/yD14cuh.png
    http://i.imgur.com/mh0a22a.png

    without one-minus
    http://i.imgur.com/ow1grcI.png
    http://i.imgur.com/Hf4Ff8c.png

    here's why you're getting confused:
    there's nothing wrong with the math. the math is fine. read through the documentation and he says:


    which is exactly what you've just said. the way they fixed it was to invert the input, which is what i've suggested to you to do.

    plug a constant into the roughness instead of a texture, and you'll see what i'm talking about.

    By default a one-minus node in UDK will not give you the expected inverted values of a texture sample, because the one-minus node works in linear space whereas the texture sample is in gamma space.

    Here is a graphic illustrating the problem and the fix:

    DlsEhyT.jpg

    It appears you are only getting correct inverted results because you are using absolute black or absolute white. Mid values will not invert correctly.

    My apologies if you've already considered this. :)
  • almighty_gir
    Offline / Send Message
    almighty_gir ngon master
    @MooseCommander - there is a visual improvement. but as with everything i do i'm sure there is a lot of room to optimize!

    @serial lens - i did not know that! i'll get that in asap =]
    edit: i'm assuming that since the mask is just one channel of a texture, i'd need to do power 2.2 for each other channel as well?
  • Santewi
    edit: i'm assuming that since the mask is just one channel of a texture, i'd need to do power 2.2 for each other channel as well?

    Yes, otherwise one of your channels would be in gamma space and the others in linear space.
  • Serial Lens
    What Santewi said.

    Also, not sure if it's user error, but I'm testing out your shader and the specular highlight does not appear to be losing brightness as it spreads over rougher surfaces. The center of the highlight maintains what appears to be the same brightness.

    gCP9teF.jpg

    In reality the width of the specular highlight is inversely correlated with the brightness of it. I would expect it to look like this:

    SuXgfTu.jpg

    edit - I should clarify that this is with ver .04.
  • Santewi
    Serial Lens, it's most likely an error in the shader. I use the same specular term in my shader and mine works how it's supposed to work.
  • almighty_gir
    Offline / Send Message
    almighty_gir ngon master
    i'll be releasing a new version over the next 24 hours or so, with quite a few fixes and clean-ups =]
2
Sign In or Register to comment.