Reputation: 11597
I need to set a variable of a shader without a material that wraps around it.
I'll explain the problem and why it's not like the question "How can I access shader variables from script?".
Problem
My shader is similar to this (99% of irrelevant boilerplate code removed):
Shader "Hidden/XShader"
{
Properties
{
_x ("", float) = 0
}
SubShader
{
Pass
{
float _x;
half4 frag(v2f i) : SV_Target
{
// "col" and "wpos" have been correctly defined
if (wpos.x >= _x)
{
col.r = 1;
} else {
col.r = 0;
}
return col;
}
}
}
}
This shader is set through the Edit->Project Settings->Graphics->Deferred
option. It is the default shader that the main camera uses.
Now I need to set the _x
value from code attached to the camera:
public class XCameraController : MonoBehaviour
{
public float x;
void Update()
{
<something>.SetFloat("_x", x);
}
}
The <something>
placeholder would normally be a material, as SetFloat()
is defined there. But the camera and shader do not have a material. The concept of material doesn't even apply to the default shader.
I've searched online and in the documentation for hours. I admit I failed and am at a loss here. I guess it must be simple but I can't find any documentation.
I don't expect an implemented solution, a pointer where I can find help will suffice!
Upvotes: 1
Views: 1219
Reputation: 1
I couldn't get the above answer from Programmer to work. Using Unity 2022.3 URP here is another solution:
Add a custom material to the camera via a script (CameraEffect.cs) with the shader set to that material, see code below (tutorial for this step here: https://www.youtube.com/watch?v=HW8UePVtU5M).
In the shader, expose the variable you need to set as a property (i.e. '_toShader("To Shader", Float) = 0.0') as well as declaring it in the shader body (i.e. 'float _toShader;'). This property will then appear in the inspector of the custom material.
In CameraEffect.cs expose a public variable (myValue) and do a SetFloat/Color/etc on the material (as below code).
In a separate game object controller script create a public variable of type CameraEffect (i.e. 'public cameraEffect camShaderControl;') which will appear in the game object's inspector. Then in the editor drag the main camera into camShaderControl box in the inspector. myValue can then be accessed in the game object script through camShaderControl.myValue which is passed into the shader.
This solution may be obsolete with post processing volumes in new versions of Unity, but the whole issue is still murky as far as I can tell.
public class CameraEffect : MonoBehaviour
{
public Material material;
public float myValue = 0.0f;
private void OnRenderImage(RenderTexture source, RenderTexture destination)
{
if (material == null)
{
Graphics.Blit(source, destination);
return;
}
Graphics.Blit(source, destination, material);
material.SetFloat("_toShader", myValue);
}
Upvotes: 0
Reputation: 125245
But the camera and shader do not have a material. The concept of material doesn't even apply to the default shader.
True but materials simply exposes all properties from a shader so it is relevant here since you want to change the shader properties.
You have a custom shader but that's not used to render a GameObject but the camera. A material is still needed to change the shader. If you don't want to use a material then you can use the Shader.SetGlobalXXX
functions such as Shader.SetGlobalFloat("_x", 3)
but it will change all the shader properties. This is unrealistic.
The proper way to do this is to create a temporary material you will use to modify the shader, change the shader properties, then update the shader the camera is using. To do this, you have to:
Find the shader or get a reference of the shader with a public variable:
Shader camShader = Shader.Find("Hidden/XShader");
Create material from the shader
Material camMat = new Material(camShader);
Modify the property as you wish
camMat.SetFloat("_x", 3);
Apply to the modified shader property to the camera
Camera.main.SetReplacementShader(camShader, "RenderType");
If you're manually rendering the camera then use Camera.main.RenderWithShader(camShader, "RenderType")
instead of Camera.main.SetReplacementShader(camShader, "RenderType")
.
Upvotes: 2