How to use CommandBuffers to render an object as Unity does

Hello, would anyone have an example of code regarding how to draw an object in a custom pass of the rendering pipeline? My project’s setup is Deferred rendering path, Linear color space, and the main camera is in HDR.

I already know I have to create a CommandBuffer, add it to the camera, and setup a OnWillRenderObject or OnPreRender (if attached on camera) callback.
What I don’t know is how to configure everything to make it look like the object was simply drawn by the Unity engine. Right now the object just renders black when there’s a light.
Do I have to setup several commands for each CameraEvent ? (I also tried to combine the main three, GBuffer, Lighting and FinalPass)

Another fuzzy variable here is how to setup the RenderTargetIdentifier (BuiltinRenderTextureType) for each CommandBuffer.SetRenderTarget(). Would anyone have more info about that, please?
I may have missed some docs, but it would be greatly beneficial if a clear sample of a classic DrawMesh scenario were to be made accessible to everyone.
(The context of this question is about to use ProjectedDeferredDecals which works great but I need to draw some objects on top of these decals.
So I disable the mesh renderer for these objects so they’re not drawn in default passes, and I want to draw them after the decal pass (BeforeLighting), in my own custom pass)

Thank you so much for reading (and hopefully answering!)…

Ok that could look like an obvious bump for the thread, but does anyone have already tried to do some custom rendering pipeline using CommandBuffers to render classic objects ?
I know that we could have some great FX with that tool, but the only way I found to exclude objects from it is by creating a post-fx pass to render excluded objects…

To render a normal object, I suppose you simple have to create a commandbuffer, do commandbuffer.drawmesh() with the proper things on it, and add that to the camera at an event somewhere early on like AfterSkybox / BeforeDepthTexture / BeforeGBuffer

Thanks, I totally agree with you, but I think I’m lacking about this “proper things on it”.
There is a LOT of different combinations:

Camera.main.AddCommandBuffer(CameraEvent.XXX, customCommandBuffer);
RenderTargetIdentifier[] customRenderTarget = { BuiltinRenderTextureType.YYY, BuiltinRenderTextureType.ZZZ,...};
customCommandBuffer.DrawMesh(_MeshToDraw, _TransformToDraw.localToWorldMatrix, _MaterialToDraw);

Everytime I see a sample of CommandBuffer code it is used for special FX, but in my case I just want a default rendering of a simple object on top of an existing one.

Well, after some testing, using this:

CommandBuffer buf2 = new CommandBuffer ();
buf2.DrawMesh (lightSphereMesh, Matrix4x4.TRS (Vector3.up * 3f, Quaternion.identity, Vector3.one * 3f), new Material (Shader.Find ("Standard")));
camera.AddCommandBuffer (CameraEvent.BeforeLighting, buf2);

renders a sphere @ 0,3,0 and it does get lighting applied. (Crappy lighting due to standard shader defaults making it some weird black metal though). Shadows won’t work though, as those would require an event to queue it up for the shadowmap generation, and that doesn’t exist in default unity.

There’s lighting from the main dir light. But the rendering process is totally different.

if change scene unique dir light to spot/point light, the lighting is no applied. _LightColor0 is black.
I tried use “ForwardBase” and “ForwardAdd” but CommandBuffer.DrawRender is simply iterating all passes and ignoring most lighting params.

How to iterate all lights as exactly Unity’s routine does?


possibly like this?
1.use first dir light in scene and use it to manually set params, then draw forward base pass.
2.for all other lights in scene, set params and draw forward add pass for each light.

By “tried use forwardbase / forwardadd but ignored”, you mean using the pass parameter of DrawRender? I suppose that would work, but the parameters wouldn’t be set (those that unity would fill in normally). I think DrawRender will override a lot of parameters using the renderer, so you may have to use DrawMesh instead (with some .SetGlobal* calls before it to set up the variables).

Making shadows work in forward will be big effort as well.

I found that CommandBuffer.DrawRenderer() can use the non dir light, but it’s weird, it depend on last object before command buffer invoked.

There’s another object in scene just drawn before command buffer, for this object
If its last pass in shader has tag “ForwardBase”, then the command buffer cannot do correct lighting if scene unique light is not directional.
If its last pass has tag “ForwardAdd”, the command buffer rendering is correct too.

I just adjust the passes order with different tag and it works.

p.s the last object before commandbuffer, its shader has multiple passes, i.e. 2 forward base pass & 2 forward add pass, the passes with the same type(base/add) are in strict order, different types with random order, Since I guess unity will select pass based on tag.

Which gives me another question, is it true that forward-add pass MUST written after forward-base pass?

thanks for the tip, I’ll try it.:slight_smile:

Yes unity won’t set paramters. I figure it that command buffer will just reuse last normal-drawn object’s parameters.

I’ve read this whole thread, but I’m still in the dark. does anyone have a clear answer to the OP?

6 Likes