How do I set a shader setting/property at runtime using MonoBehaviour? - unity3d

I have a shader that displays a mesh's vertex colors and has the ability to clip vertices that are blue. I'd like the user to be able to turn this setting on/off while using the application.
I've tried the suggestions offered here Find and change property name in a shader and here How do I discard pixels based on vertex color and turn that on or off in a MonoBehaviour?
but so far none of those have worked for me.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using HoloToolkit.Unity;
using System;
public class AnchorScript : MonoBehaviour
{
private Renderer rend;
Vector3 scale;
public WorldAnchorManager worldAnchorManager;
// Start is called before the first frame update
void Start()
{
rend = gameObject.GetComponent<Renderer>();
// transform.localScale = scale;
TurnOffContext();
}
private void Update()
{
SaveScale();
TurnOffContext();
}
public void AnchorIt()
{
worldAnchorManager.AttachAnchor(this.gameObject);
this.gameObject.GetComponent<Renderer>().material.color = Color.red;
}
public void ReleaseAnchor()
{
worldAnchorManager.RemoveAnchor(this.gameObject);
this.gameObject.GetComponent<Renderer>().material.color =
Color.green;
}
public void ShowDifferences()
{
gameObject.GetComponent<Renderer>().enabled = true;
}
public void HideAugmentations()
{
gameObject.GetComponent<Renderer>().enabled = false;
}
public void TurnOffContext()
{
rend.material.SetFloat("_Toggle", 1);
Debug.Log("Toggle ");
}
public void SaveScale()
{
scale = gameObject.GetComponent<Transform>().localScale;
}
}
I expect this script to cause my mesh's blue parts to be discarded, because in my shader's fragment I have
clip(_Toggle * (0.5f - IN.vertColor.b));
Specifically the part that doesn't work is the line
rend.material.Setfloat("_Toggle", 1);
The function is being called, but it's not setting the _Toggle value to 1 as expected.
If I manually change the _Toggle setting in the inspector panel from 0 to 1, the blue parts of the mesh are discarded, which is the expected result. The script I pasted above should have the same result as manually changing the setting in the inspector panel.

I don't know why your current solution doesn't work (it looks fine to me), but i know that you can work around this whole thing using shader variants.
Inside your shader, add this line to the pragmas:
#pragma multi_compile __ DISCARD_BLUE
And then change your clip statement to this:
#ifdef DISCARD_BLUE
clip(0.5f - IN.vertColor.b);
#endif
To enable and disable this feature, just change the C# script to use rend.material.EnableKeyword("DISCARD_BLUE") and rend.material.DisableKeyword("DISCARD_BLUE") respectively. This has the added benefit that it saves a few instructions when the keyword is disabled. You can also enable/disable the keyword globally like this:
Shader.Find("Custom/VertexColor").EnableKeyword("DISCARD_BLUE");
Other than that, have you made absolutely sure that your script references the correct renderer?

Related

TryGetFeatureValue always 0 for Unity XR Input's CommonUsages.trigger

I am developing a VR game in Unity (2020.3.15f2) using the XR Interaction Toolkit package (1.0.0-pre.5) for my Oculus Quest 2. At this stage in my development, I am trying to recognize presses to the trigger and grip buttons on the controllers respectively in order to animate some 3D hand models. Here's the script I've written to accomplish this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR;
public class HandPresence : MonoBehaviour {
public InputDeviceCharacteristics controllerCharacteristics;
public GameObject handModelPrefab;
private InputDevice targetDevice;
private GameObject spawnedHandModel;
private Animator handAnimator;
void Start() {
TryInitialize();
}
void TryInitialize() {
List<InputDevice> devices = new List<InputDevice>();
InputDevices.GetDevicesWithCharacteristics(controllerCharacteristics, devices);
if (devices.Count > 0) {
targetDevice = devices[0];
spawnedHandModel = Instantiate(handModelPrefab, transform);
handAnimator = spawnedHandModel.GetComponent<Animator>();
}
}
void UpdateHandAnimation() {
if (targetDevice.TryGetFeatureValue(CommonUsages.trigger, out float triggerValue)) {
handAnimator.SetFloat("Trigger", triggerValue);
} else {
handAnimator.SetFloat("Trigger", 0);
}
if (targetDevice.TryGetFeatureValue(CommonUsages.grip, out float gripValue)) {
handAnimator.SetFloat("Grip", gripValue);
} else {
handAnimator.SetFloat("Grip", 0);
}
}
void Update()
{
if (!targetDevice.isValid) {
TryInitialize();
} else {
spawnedHandModel.SetActive(true);
UpdateHandAnimation();
}
}
}
The issue I'm experiencing is that the values of both triggerValue and gripValue are always 0. The value of targetDevice looks fine. I also tried using triggerButton, gripButton, primaryButton, etc. and they are always 0/false as well. The hand models show up just fine and their movement is in sync with the movement of the controllers, but they just don't seem to want to register any button presses.
I've been stuck on this one for hours and would very much appreciate any insight, thank you!
Is your project setup with the (new) Input System? I have no problem detecting there trigger and grip values.
Also make sure the targetDevice actually uses trigger and grip features, maybe it is another device such as the HMD.

Get Renderer of another game object

I have a 3d cube with a unlit\transparent texture. I'm trying to get access to the material offset parameter.
public class scroll : MonoBehaviour {
public float speed = 0.5f;
public GameObject stars, bg;
public Component ren;
// Use this for initialization
void Start () {
ren = GameObject.Find("stars").GetComponent<Renderer>();
}
// Update is called once per frame
void Update () {
Vector2 offset = new Vector2(0, Time.time * speed);
//here I want to change offset of the texture (shader: unlit\transparent)
}
}
I tried
ren.renderer.material.mainTextureOffeset = offset;
got an Error:
UnityEngine.Material does not contain a definition for
mainTextureOffeset and no extension method mainTextureOffeset of
type UnityEngine.Material could be found. Are you missing an
assembly reference?
structure:
-WORLD (this script attached here)
--stars (3D cube)
You have got a typo in your code: mainTextureOffeset instead of mainTextureOffset. I don't see any other possibilities for UnityEngine.Material not to contain mainTextureOffset.
I think you should use ren.material.mainTextureOffset, since ren is a reference to the renderer of "stars" and not a reference to the GameObject "stars".
EDIT: As #Bagdan Gilevich stated in his answer you should also change mainTextureOffeset to mainTextureOffset.

Unity | webCamScript not displaying

I have the code exact from a tutorial I copied, and the webcam is inserted and works. But when I load the Unity game (In Unity Editor) there is no "No Device connected" error or incorrect scripts. I'm confused as to why it isn't working.
Why isn't it being displayed?
My webCamScript
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class webCamScript : MonoBehaviour {
public GameObject webCameraPlane;
// Use this for initialization
void Start () {
if (Application.isMobilePlatform) {
GameObject cameraParent = new GameObject ("camParent");
cameraParent.transform.position = this.transform.position;
this.transform.parent = cameraParent.transform;
cameraParent.transform.Rotate (Vector3.right, 90);
}
Input.gyro.enabled = true;
WebCamTexture webCameraTexture = new WebCamTexture ();
webCameraPlane.GetComponent<MeshRenderer> ().material.mainTexture = webCameraTexture;
webCameraTexture.Play ();
}
// Update is called once per frame
void Update () {
Quaternion cameraRotation = new Quaternion (Input.gyro.attitude.x, Input.gyro.attitude.y, -Input.gyro.attitude.x, -Input.gyro.attitude.y);
this.transform.localRotation = cameraRotation;
}
}
Solved
I found the problem, I had a custom texture on the plane which was stopping the camera texture from being inserted.
I presume it has something to do with the fact that you have your code wrapped in an if statement that is checking to see if you are running on a mobile platform. The editor will not be classed as a mobile platform and hence that code will be ignored

Video texture Unity 5

I have a problem with getting a video texture to show up in unity 5.2 personal edition. I have applied a material with unlit shader and assigned it as a video texture. I also call the specific video texture through a script attached to the object with the video texture.
using UnityEngine;
using System.Collections;
[RequireComponent(typeof(AudioSource))]
public class CallMovie : MonoBehaviour {
public MovieTexture myMovie;
// Use this for initialization
void Start () {
Debug.Log ("start intro");
GetComponent<Renderer>().material.mainTexture = myMovie;// get movie
myMovie.Play ();// shall play movie
}
void Update(){
myMovie.loop =true;
}
}
When I hit the play button in unity the video texture stays black and nothing happens om screen although the program says it ran the video when checked with debug log.
Since I cant post questions in comment on your initial the following is an attempt to answer with what I know.
In your first statement after the debug call you are setting the maintexture component of the instanced material to myMovie, depending on shader this may or may not work as 'mainTexture' may not be referencing the texture you expect.
You can insure you hit the desired texture using the following method
//Note the diffrence between a material instance and the shared material
//... dont forget to clean up instances if you make them which hapens when you call .material
Material instancedMaterial = gameObject.GetComponent<Renderer>().material;
Material sharedMaterial = gameObject.GetComponent<Renderer>().sharedMaterial;
//_MainTex is the name of the main texture for most stock shaders ... but not all
//You can select the shader by clicking the gear in the inspector of the material
//this will display the shader in the inspector where you can see its properties by name
instancedMaterial.SetTexture("_MainTex", movie);
The following code is from a working class object I use to set a Unity UI object RawImage to render a movie. From what I see in your example you have the movie part correct I suspect your issue is with the shader parameter.
using UnityEngine;
using System.Collections;
public class RawImageMovePlayer : MonoBehaviour
{
public UnityEngine.UI.RawImage imageSource;
public bool play;
public bool isLoop = true;
public MovieTexture movie;
// Use this for initialization
void Start ()
{
movie = (MovieTexture)imageSource.texture;
movie.loop = isLoop;
}
// Update is called once per frame
void Update ()
{
if (!movie.isPlaying && play)
movie.Play();
}
public void ChangeMovie(MovieTexture movie)
{
imageSource.texture = movie;
this.movie = (MovieTexture)imageSource.texture;
this.movie.loop = isLoop;
}
public void OnDisable()
{
if (movie != null && movie.isPlaying)
movie.Stop();
}
}

How to modify the car controllers to do it more Arcade and less realistic in Unity 5 3D

I'm using the Unity 5 Car Asset coming with the Standard Assets. Controls are very hard. The car flips easily even if you are going at quite slow speed.
I have done some "tricks" I have found on the Internet like increasing the mass of the rigid body to 1500, adding the Stabilizer bars (A.K.A. anti-roll bars) script to the car, and setting the gravity center of the car in a fake perfect center. I have included the last versions of those scripts above.
I don't want to simulate perfect physics. I want a fun car easy to ride. Is it possible with Unity?
Script: gravity center of the car in a fake perfect center
using UnityEngine;
using System.Collections;
public class carflipfix : MonoBehaviour {
// Use this for initialization
void Start () {
GetComponent<Rigidbody>().centerOfMass = new Vector3(0, -1, 0);
}
}
Script: stabilizer bars (A.K.A. anti-roll bars).
using UnityEngine;
using System.Collections;
public class AntiRollBar : MonoBehaviour {
public WheelCollider wheelL;
public WheelCollider wheelR;
public float antiRollVal = 5000f;
// Update is called once per frame
void Update () {
WheelHit hit;
float travelL=1.0f;
float travelR=1.0f;
bool groundedL = wheelL.GetGroundHit(out hit);
if (groundedL){
travelL = (-wheelL.transform.InverseTransformPoint(hit.point).y - wheelL.radius) / wheelL.suspensionDistance;
}
bool groundedR = wheelR.GetGroundHit(out hit);
if (groundedR){
travelR = (-wheelR.transform.InverseTransformPoint(hit.point).y - wheelR.radius) / wheelR.suspensionDistance;
}
float antiRollForce = (travelL - travelR) * antiRollVal;
if (groundedL)
GetComponent<Rigidbody>().AddForceAtPosition(wheelL.transform.up * -antiRollForce,
wheelL.transform.position);
if (groundedR)
GetComponent<Rigidbody>().AddForceAtPosition(wheelR.transform.up * antiRollForce,
wheelR.transform.position);
}
}
If we remove the Rigidbody component, we won't be using the Unity physics. Then, we could move the car as we wish getting the input (from the keyboard, gamepad...) to multiply it by the current position of the car. It would be something like:
public class Car: MonoBehaviour
{
public void Update() {
// NOTICE THE FOLLOWING LINE OF CODE IS NOT CORRECT. THE CORRECT WAY SHOULD BE SOMETHING SIMILAR THOUGH:
gameObject.transform.position += input * Time.deltaTime * gameObject.transform.position;
}
}