- Home /
How to properly draw a mesh using CommandBuffer.DrawMesh?
I’ve seen the examples provided by Unity, but I must be missing something because I’m not getting the results I expect.
In my initial test, I’m simply trying to draw a non-moving, user defined mesh, with a user defined material. (New scene, new project). Unfortunately, while I can get the mesh to show up on camera, it looks like it has no lighting on it, as it’s completely black. (Lighting not applied is confirmed; tested using unlit shader materials, which DO show up properly.)
The main question is: how do I fix this script to allow the material to show up properly, with lighting?
(Note on script: it is necessary to add (but not configure) a renderer component, in order for OnWillRenderObject to be executed. I used a meshRenderer, without a meshfilter. Can this be avoided, if I want to move the mesh?)
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
[ExecuteInEditMode]
public class AllCameraCommandRender : MonoBehaviour
{
List<int> assignedCameraGUIDs;
public Mesh meshToRender;
public Material matierialForMesh;
CommandBuffer cb;
private void Start()
{
if (assignedCameraGUIDs == null) assignedCameraGUIDs = new List<int>();
else assignedCameraGUIDs.Clear();
}
public void OnWillRenderObject()
{
if (assignedCameraGUIDs == null) assignedCameraGUIDs = new List<int>();
Camera cam= Camera.current;
int id = cam.gameObject.GetInstanceID();
if (!assignedCameraGUIDs.Contains(id))
{
if (cb == null)
{
Matrix4x4 M = transform.localToWorldMatrix;
//Matrix4x4 V = cam.worldToCameraMatrix;
//Matrix4x4 P = GL.GetGPUProjectionMatrix(cam.projectionMatrix, false);
//Matrix4x4 I = Matrix4x4.identity;
cb = new CommandBuffer();
// cb.SetRenderTarget(BuiltinRenderTextureType.CameraTarget);
cb.DrawMesh(meshToRender, M, matierialForMesh);
}
cam.AddCommandBuffer(CameraEvent.BeforeForwardOpaque, cb);
// cam.AddCommandBuffer(CameraEvent.BeforeLighting, cb);
assignedCameraGUIDs.Add(id);
}
}
}
I was also experimenting with changing the CameraEvent: I tried to use BeforeLighting, like the decals example does, but this change prevented the object from being visible at all. Why is that? (I saw a post in the forum indicating that CameraEvent might be using the matrix differently, so I experimented with some variants: none, except the transform.localToWorldMatrix, seemed to work- but thats why they are in the code above.)
I also tried using the SetRenderTarget command, but this seemed to have no effect. I assume that if I make no changes to the target, the default render target will be the BuiltinRenderTextureType.CameraTarget. Is that correct?
This is due to commandBuffer activating the shader's forwardpass for a deferred camera. I actually need the right shader but I don't know how to active it in a Draw$$anonymous$$eshInstancedIndirect :/
I would create the CommandBuffer at the Start() or Awake() method and then use cb.Clear() to assign new commands so you have more performance
Answer by SgtOkiDoki · May 06, 2020 at 12:49 PM
I have found a solution.
It's a Unity issue and they provide a solution around this. (Took me a while to find the solution)
---What is the problem--- Unity doesn't assign the 'SH coefficients' to the target material and that leads to no ambient lighting.
---How to solve it--- Calculate it in CPU once -> Assing it to 'MaterialPropertyBlock', -> Assing that to the material.
Github link for the solution example https://github.com/keijiro/LightProbeUtility
Answer by hexagonius · Oct 28, 2018 at 09:36 PM
Funny, according to this link you just do:
cb.DrawMesh(meshToRender, M, matierialForMesh, 0, 0);
Thanks for finding that hexagonius, I just gave up on this, so missed that post six months after my Q was posted ;) This DOES light the object, but if using a transparent material, it places it behind everything else (edit: fixed by changing event to AfterForwardAlpha). Also, still unsure how to cast/receive shadows.
Why am I supposed to draw only shader pass "0" rather than all shader passes (like the default parameter of -1 is supposed to)?