Because of the way properties work in shader code, un-exposed properties can not be initialized unless you do so through a script, which means they always have default values (identity matrix for matrices and zero vector for vectors, black for colors) in the materials. The purpose of unexposed properties is to set them through code, so if you don’t plan on doing so, it’s best to leave them exposed.
Thanks for clarifying that. That definitely means I can’t use unexposed properties as I’d hoped to.
One thing I find fairly annoying about either Shader Graph, or the way things work in HDRP right now, is the way that material instances lock on to a value for an exposed properly, and don’t change if the default changes in the shader unless I manually “Reset” the material. As a simple example, if I add a “Base Color” property to a shader, and choose a blue color for the default, materials instances will have blue as their default. But if I decide I really wanted red to be the default, and I change that in the Shader Graph, any materials that exist will not change to red. They’ll keep their blue value.
This is annoying because I usually want to drive values via properties on the shader, so it’s natural to add those properties as soon as I see I’ll need them. But as soon as I add a property, I’ve actually made it a lot more annoying to experiment on that shader, since now I need to go reset values on the materials that use that shader. Does that make sense?
One thing I find missing about this process would be an approach like the way HDRP lets you override post-processing settings. For each property, you first opt-in to overriding it or not. If you don’t override it, it uses the default value. Anyway, I’m not sure if there’s currently any way to avoid the situation I often find myself in, where I want to edit the Shader, save it, and see the impact in the Scene view. I currently can’t do that without having to reset the material’s properties each time, assuming I’m tweaking a property. That’s why I was hoping exposed properties would keep everything within the shader, so I can tweak everything in the shader, and only “expose” it once I’m ready for the material to lock to a single value.
So the default values set in the Shadergraph blackboard are only used if the property is exposed?
Why aren’t they all initialized per the blackboard defaults, and then simply hidden in the Inspector window when not exposed?
I agree with @nikefootbag . All properties should be set to their default values regardless if it’s tagged with “exposed” or “unexposed”. Not doing so is confusing for the developer as it leads to unexpected behavior. For example, what we see in the shader graph Preview Window does not match what is seen for objects with the same material in game.
“Unexposed” should mean that the shader doesn’t required constant buffer storage and can copy a raw value into the shader code that get’s compiled at the line the variable is declared. If a property is marked as “exposed”, then the shader code can still set the default value where the variable is declared, but then later, in the generated code, read the property’s new value from a constant buffer that stores values coming from inspector overrides.
The fact that “Unexposed” is meant for setting values through script should not be our concern when authoring shaders in the shader graph.
Well this was frustrating and cumbersome to discover, not obvious whatsoever.
If a property is exposed, I can’t use a MaterialPropertyBlock. But if it is not exposed, I can’t modify it’s value in the GUI while building/testing. So I need to set all my properties to exposed while testing, then go back and uncheck all of them when I want to set through script… (and back and forth again if more testing is needed)
It is also really frustrating that values ‘latch’ when setting them on the Material through the Inspector, so they can no longer be modified in the Shader…
My workaround for that is to create a sub-graph with the internal variables I want to export, then add that subgraph to my shader and connect them whever I need.
There is also this thingy, exposing properties while using them as Global variables, doesn’t make sense at all, as exposed properties are always treated as per material properties.
Non-exposed properties are always treated as global properties regardless of the declaration. Initially, I was thinking that this combination will use an [HideInInspector] attribute, but no.
This is with SG 10, not sure if anything changed.
Amplify uses property types: Constant / Property (per material) / Global / Instanced and instead of the “Exposed” option, you have Attributes ([HideInInspector] for instance) with the ability to add your own if you want to create some custom drawers.
I don’t understand why the Unity Shader Graph isn’t following Unity’s own property system. Within the generated shader, there are properties that use [HideInInspector] without issue. When making a property hidden (click off exposed), it removes it from the property list and just has a declaration in the pass. It doesn’t utilize the HideInInspector. There’s certainly a use case, but this doesn’t seem to be the proper behavior nor is it communicated. At the moment, I don’t see any way within the node system to just add the HideInInspector prefix.
In my use case, I need a property hidden, with default values, and script editable. I believe most people trying to use this button need the same. Without the HideInInspector, the property isn’t properly serializable which leads to it constantly forgetting values and complete lack of default values. So, now I need to expose a sensitive value because the functionality is broken otherwise.
thanks @Kevin-VFX for bringing this up again.
We are looking into it, if you want to share more details on your use cases, we now have a card on the roadmap.
Well this explains the hours I just wasted over the past couple days
This should definitely be redesigned in the sense that it’s super misleading - to me unmarking a parameter as exposed would just hide it from the material’s inspector and assign it the default value I set inside the graph editor.
Why isn’t the “Default” input box greyed out when “Exposed” is unchecked? It’s not obvious at all that it will be ignored:
This is made worse by the fact that the preview works perfectly fine, just not the actual in-scene render:
This is exactly what I expected and I believe would be common sense obvious behavior.
Hi @marcospgp , this was indeed misleading and this is why we fixed this in 2023.3 (alpha 11 and above).
The value field label will now read “Preview Value” when the scope is set to Global.
See this other thread for more info.
Thanks for your feedback.
I’m still on 2023.2.5f1 so I haven’t tested out these changes in that release, but based on what I’m reading in your thread it appears you’re implying that the “Default” property in the Node Settings only ever functioned as a way to drive the Preview value within the Shader Graph window. However, the Default property does in-fact apply a default value to any new material created with a Shader Graph (as long as Exposed is checked). I just tested it in 2023.2.5f1 and sure enough, if I add a Color property and set it to Red in the Shader Graph, the corresponding material property will default to Red as well.
What all the devs including myself are requesting here is simply that the Default value continue to function the same when Exposed is un-checked as it does when Exposed is checked. In my particular case, I’m creating a Shader Graph which has a couple of properties I’d like to be globally defaulted to all materials which implement it. I don’t intend to set them via script because it’s a fixed value I want them all to share, ideally retroactively if I decide to change it after creating materials which implement it. I’d like to uncheck “Exposed” for the same reason I’d set a Private access modifier on a class member in a C# script, to inform myself and others that this value is not intended to be changed and to declutter any inspectors that would otherwise display this value if it were public (or in this case “Exposed”). Additionally, it is intuitive to assume that the Default values of Exposed properties will not be retroactively changed on materials which implement the Shader Graph (which is how it works now), but that Un-Exposed properties should inherit retractive changes.
I sincerely hope that I read that wrong or that you are still open to feedback on this as it will be extremely disappointing if the requests to expand the usage of the Default property has instead led to it’s usage being effectively nerfed by changing it to only drive the preview in the Shader Graph window.
That is simply not possible, since Globals (previously called un-exposed) do not serialize their value anywhere. Shader Graph has had that default value being used in previews, which was a bit misleading. When a property is Global, the color field label now reads “Preview Value” to avoid confusion.
We are always open to feedback. You can provide more feedback on Shader Globals feature request here.
Couldn’t you just allow us to serialize any properties in the material? I mean, doesn’t the engine simply set all of the stored properties through some internal method similar to material.SetVector? In that case it would work with unexposed properties as long as they are defined in the shader. This is not a problem with shadergraph either, just a limiting design choice on the material api, which makes it impossible to serialize custom properties if they are not in the properties shader block.
Well, if you serialize a value in an instance, does this not break the idea of this value being global?
It’s the same paradigm as static values in C#.
It’s more HLSL than ShaderLab. Uniforms don’t have a default value.
A Uniform with no property is considered a Global and need to be set with Shader.SetGlobal__().
With a property it becomes Local/Per Instance and it can store its default value, visible or not in the Material Inspector.
With all that said, I do acknowledge the pain of working with Shader Globals.
You can provide feedback on the feature here.
I’m wondering: could you elaborate on this more? I’m sure that a lot of deeper meaning is wrapped up in that first sentence. I suppose I’m just confused about what’s preventing this kind of thing (if a parameter is left not-exposed, to treat all its values used in the shader graph as constants, as if I had just manually input them).
I suppose that it has to do with assumptions that are already being made on the back end, that developers wish to have parameters accessible by script, but not in the inspector.
Given the convenience of working with various constants that are used throughout a shader, it would be nice if there were some third option for the parameter list, such as “hide in inspector, don’t treat as a parameter, just populate the shader graph values with this one as if it’s a constant.”
Often times lots of visual effects are modified by iterative tweaking of constants which affect several things at once, but in such a way that it is not desired to have them as configurable parameters at runtime (which is why, I assume, that I can set any constant variable type directly in the graph). Naming the constants for our own internal use would help to keep things organized, without worrying about whether it’s going to have to also be a configurable parameter in the shader (like defining a constant within a text-based shader)
to summarize, Shader Graph ultimately outputs a Shader using ShaderLab syntax.
Shader Properties are serialized (their value is stored in a material instance).
Which makes them editable per instance in the Inspector (unless marked with a [HideInInspector] attribute).
As of Unity 6, Shader Graph adds that attribute to the property when you uncheck “Visible In Inspector”.
This option is only available when the property scope is set to Per Material or Per Instance.
The property is tied to a uniform declared within the shader code. Shader Graph does that for you by adding both the property and uniform.
If you set the property scope to Global (or prior to Unity 6 set the property to not exposed), there is no property declaration in the Shader.
As a result, there is only a Uniform declaration. And since uniforms cannot be initialized with any value (that’s how HLSL works), that property defaults to the type default, usually zero.
Unity allows you to set those uniforms globally from C# using Shader.SetGlobalFloat() etc.
Properties set to Per Material or Per Instance, generate a Material Property, which is then accessible from C# with material.GetColor() / material.SetColor().
This is how we differentiate Local Properties from Global ones, but to your point, ultimately, these are just uniform values, either set from the material or globally.
Now for constants, you can already set constants with a Shader Graph by just adding any Input node such as a Float or Color.
I appreciate that since we lack portals (aka Get/Set node), you cannot easily reuse the same constant in many places in a graph. Also, even with portals, you couldn’t reuse the same constant in several graphs.
So my suggestion is to make sub-graphs to gather those constants in meaningful collections. That way you can use them everywhere you want and edit them all in one place.