UI Buttons that are tinted black are showing up white… Perhaps the button tints aren’t rendered?
Edit:
For anyone else running into this issue, I Modfied Mnstrspeed’s script with jumpgate_lewis’s modification and then further modified it to resolve the above, I did also have to make a second script to keep track of UnityEngine.UI.Selectables selection states, because their selection state is protected, and i didn’t want to replace button with a class that inherits from button or selectable.
My SelectableStateTracker:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
/// <summary>
/// Unitys default buttons in the normal UI has selectionstate as a protected variable, can't check it to work around tint not showin in video correctly. so alternatively i track it here.
/// </summary>
public class SelectableStateTracker : MonoBehaviour, IPointerDownHandler,IPointerEnterHandler,IPointerExitHandler,IPointerUpHandler,ISelectHandler,IDeselectHandler
{
private bool hovered = false;
private bool pressed = false;
private bool selected = false;
public Selectable selectable;
private void Awake()
{
selectable = GetComponent<Selectable>();
}
public enum SelectionState {highlighted,normal,pressed,disabled,selected};
public SelectionState CurrentSelectionState = SelectionState.normal;
public void OnPointerDown(PointerEventData eventData)
{
pressed = true;
}
public void OnPointerEnter(PointerEventData eventData)
{
hovered = true;
}
public void OnPointerExit(PointerEventData eventData)
{
hovered = false;
}
public void OnPointerUp(PointerEventData eventData)
{
pressed = false;
}
public SelectionState evaluate()
{
if (!selectable.interactable)
{
return SelectionState.disabled;
}
return (hovered, pressed,selected) switch
{
(false, false,false) => SelectionState.normal,
(true, false, false) => SelectionState.highlighted,
(false, true, false) => SelectionState.pressed,
(false, true, true) => SelectionState.pressed,
(true, true, false) => SelectionState.pressed,
(true, true, true) => SelectionState.pressed,
_ => SelectionState.selected
};
}
public void OnSelect(BaseEventData eventData)
{
selected = true;
}
public void OnDeselect(BaseEventData eventData)
{
selected = false;
}
}
And The Modified version of the ManualUIRenderer
using System.Linq;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Rendering;
using UnityEngine.UI;
/// <summary>
/// Component that manually renders world-space UI Graphics to a Camera. Intended for
/// use with Camera.RenderToCubemap which seems to ignore the built-in UI system
/// </summary>
[RequireComponent(typeof(Camera))]
public class ManualUIRenderer : MonoBehaviour
{
private static readonly int MainTexProperty = Shader.PropertyToID("_MainTex");
private static readonly int TextureSampleAddProperty = Shader.PropertyToID("_TextureSampleAdd");
private static readonly int ColorProperty = Shader.PropertyToID("_Color");
private Camera targetCamera;
private CommandBuffer commandBuffer;
private void Awake()
{
this.targetCamera = this.GetComponent<Camera>();
this.commandBuffer = new CommandBuffer();
this.commandBuffer.name = "Manual UI rendering";
this.targetCamera.AddCommandBuffer(CameraEvent.AfterSkybox, commandBuffer);
//this.targetCamera.AddCommandBuffer(CameraEvent.AfterEverything, commandBuffer);
}
private void OnDisable()
{
this.commandBuffer.Clear();
}
private void Update()
{
this.commandBuffer.Clear();
AddUiDrawingCommands(this.targetCamera, this.commandBuffer);
}
private static void AddUiDrawingCommands(Camera cam, CommandBuffer buffer)
{
// Root canvases ordered by screen space depth
var rootCanvases = FindObjectsOfType<Canvas>()
.Where(canvas => canvas.isRootCanvas && canvas.renderMode == RenderMode.WorldSpace)
.OrderByDescending(canvas => cam.WorldToScreenPoint(canvas.transform.position).z);
foreach (var canvas in rootCanvases)
{
// Graphics after culling sorted by depth
var graphics = canvas.GetComponentsInChildren<Graphic>()
.Where(graphic => TestCullingMask(graphic, cam.cullingMask))
.OrderBy(graphic => graphic.depth);
foreach (Graphic graphic in graphics)
{
AddGraphicDrawingCommands(graphic, buffer);
}
}
}
private static bool TestCullingMask(Graphic graphic, int cullingMask)
{
return ((1 << graphic.gameObject.layer) & cullingMask) != 0;
}
private static void AddGraphicDrawingCommands(Graphic graphic, CommandBuffer buffer)
{
// Probably not needed, but let's call it anyway
graphic.Rebuild(CanvasUpdate.PreRender);
// Determine effective alpha from CanvasGroups (probably not how Unity does this)
float effectiveAlpha = graphic.GetComponentsInParent<CanvasGroup>()
.Aggregate(1f, (alpha, group) => alpha * group.alpha);
var material = new Material(graphic.materialForRendering); // material with IMaterialModifiers already applied
material.SetTexture(MainTexProperty, graphic.mainTexture);
material.SetColor(ColorProperty, graphic.color * new Color(1f, 1f, 1f, effectiveAlpha));
if(graphic.GetComponent<SelectableStateTracker>() != null)
{
SelectableStateTracker s = graphic.GetComponent<SelectableStateTracker>();
if (s.selectable.transition == Selectable.Transition.ColorTint)
{
SelectableStateTracker.SelectionState state = s.evaluate();
Color c = (state) switch
{
SelectableStateTracker.SelectionState.highlighted => s.selectable.colors.highlightedColor,
SelectableStateTracker.SelectionState.normal => s.selectable.colors.normalColor,
SelectableStateTracker.SelectionState.pressed => s.selectable.colors.pressedColor,
SelectableStateTracker.SelectionState.disabled => s.selectable.colors.disabledColor,
SelectableStateTracker.SelectionState.selected => s.selectable.colors.selectedColor,
_ => s.selectable.colors.normalColor
};
material.SetColor(ColorProperty,c * new Color(1f, 1f, 1f, effectiveAlpha));
}
}
if (graphic is Text)
{
// Not sure how/when Unity decides to set _TextureSampleAdd, but Text is
// the only component I've encountered that needs it
material.SetVector(TextureSampleAddProperty, new Vector3(1, 1, 1));
}
var mesh = new Mesh();
// Call protected member Graphic.OnPopulateMesh through reflection to
// populate the mesh. Probably really slow and skipping quite a few steps
// in Unity's UI render pipeline, but it seems to work
using (VertexHelper vh = new VertexHelper())
{
graphic.GetType().InvokeMember("OnPopulateMesh",
System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.InvokeMethod |
System.Reflection.BindingFlags.NonPublic,
null, graphic, new object[] { vh });
vh.FillMesh(mesh);
}
if (graphic is TextMeshProUGUI)
{
var textMeshProUGUI = graphic as TextMeshProUGUI;
mesh = textMeshProUGUI.mesh;
material = textMeshProUGUI.fontMaterial;
}
buffer.DrawMesh(mesh, graphic.rectTransform.localToWorldMatrix, material);
}
}