[EDIT] Sorry, somehow the title was cut off. This question is about Procedural Drawing on single-pass (multi-instance) stereo
Hello!
I’m trying to use purely procedural rendering (without a mesh, generating triangles in a geom shader on-the-fly based on a data buffer) in single-pass stereo with Direct3D.
However, the rendering only works on the left eye. The right eye is empty.
In the shader, use the UNITY_SETUP_INSTANCE_ID / UNITY_TRANSFER_INSTANCE_ID / UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO macros [1,2].
If I change the shader to render a mesh instead, then it works on both eyes.
Also, in multi-pass stereo, both eyes are rendered correctly.
So I believe that the problem is in the way the draw call is made.
I tried using CommandBuffer.DrawProcedural() and CommandBuffer.DrawProceduralIndirect(),
and for both functions I tried setting the instance count to either 1 or 2.
I also tried CommandBuffer.SetInstanceMultiplier() to double the number of instances.
But it always only renders on the left eye, never on the right.
This is both in the Editor (with MockHMD) and on the Hololens,
and happens in both Unity 2020.1 and Unity2020.2.
Is there something I’m missing?
Is there a working example on how to use procedural drawing in single-pass stereo?
UPDATE:
After downgrading to Unity 2019.4 the shader renders on both eyes in the Unity Editor, so it might be a bug in Unity2020.
But on the Hololens it still only renders on the left eye.
Interestingly, there are shadow rendering artifacts visible on the right eye as well (if I remove the shadow rendering in Graphics.DrawProcedural(), the artifacts disappear), so it seems the shader is rendered but the fragment color does not end up in the image.
I could not find any difference in graphics settings.
I managed to get Graphics.DrawProceduralIndirect() to work in 2019.4 with single pass instancing by setting the number of instances in the args buffer to 2, and then in the shader something like this:
struct Attributes
{
uint vertexID : SV_VertexID;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
// The order that triangles are defined in my compute shader
struct Triangle {
float3 vertices[3];
float3 normals[3];
};
// Append buffer containing output of compute shader
StructuredBuffer<Triangle> _Triangles;
// This structure for vertex output
struct VertexOutput {
float3 positionWS : TEXCOORD0; // Position in world space
float3 normalWS : TEXCOORD1; // Normal vector in world space
float4 positionCS : SV_POSITION; // Position in clip space
UNITY_VERTEX_OUTPUT_STEREO
};
VertexOutput Vert(Attributes input)
{
VertexOutput output = (VertexOutput)0;
UNITY_SETUP_INSTANCE_ID(input);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
Triangle tri = _Triangles[input.vertexID / 3];
float3 vert = tri.vertices[input.vertexID % 3];
float3 norm = tri.normals[input.vertexID % 3];
output.positionWS = vert;
output.normalWS = norm;
// Apply shadow caster logic to the CS position
output.positionCS = TransformWorldToHClip(vert, norm);
return output;
I am quite happy to have found this post, I have almost exactly the same problem.
Trying to use DrawProceduralIndirect with VR in single-stereo-instanced mode, on an Oculus Rift S.
I can confirm that it only renders in the left eye within the Oculus and the Unity Mock HMD, in Unity 2020.2.1f1 (HDRP 10.2.2).
I tried all the macros about instancing and stereo → no change.
I tried to double the number of instanceCount in the indirect args buffer, but all it did was to render my geometry two times in the left eye…
After seeing this post, I tried Unity 2020.3.0f1, with HDRP 10.3.2.
Now I have SHADOWS in the right eye, and all the rendering in the left. That is some progress
I will try other HDRP versions in this version of Unity and the alpha/beta of the next versions. I will post any findings.
FYI, in my shaders, I only do a simple vertex shader that extracts the input vertex data from the compute buffers I filled previously, and pass it all the VertMesh() functions of every pass. In this case, all I should have to do is give the AttributeMesh struct the instance_ID of my vertex shader, and the rest of the HDRP pipeline calls all the stereo/instance macros.
In both cases, Multi Draw is working, but Single Pass Instanced is not (only left eye).
I would really like to know if I am doing something wrong, or if it is a bug and we just have to wait.
On top of that, the viewport and the left eye are flickering (alternate render and black screen). I guess that may be coming from my shaders. Indirect rendering can mess things up
UPDATE:
Tested the 2019.4.22f1, HDRP 7.5.3.
Same problem in the Oculus in Single Pass Instanced. Multi ok. And no flickering in scene view.
That’s way too many versions in which it does not work for it to be a Unity bug. Something must be missing in our meddling with HDRP.
Hi again.
I managed to make it work!!
FYI, this is what I had to add in order for it to work.
This is my vertex shader, sourcing vertices from the compute buffer, and calling HDRP VertMesh for a number of passes (shadowcaster, depth_only, gbuffer and forward):
PackedVaryingsMeshToPS GrassVert(VertexShaderInput input)
{
// 1) EXTRACT info from the ComputeBuffer.
DrawTriangle tri = _DrawTriangles[input.vertexID / 3];
DrawVertex vertex = tri.vertices[input.vertexID % 3];
float3 positionRWS = GetCameraRelativePositionWS(vertex.positionWS);
// 2) CALL the HDRP vertex shader with the extracted info.
AttributesMesh inputMesh = (AttributesMesh)0;
inputMesh.positionOS = TransformWorldToObject(positionRWS);
#if defined(ATTRIBUTES_NEED_NORMAL)
inputMesh.normalOS = ...;
#endif
#if defined(ATTRIBUTES_NEED_TANGENT)
inputMesh.tangentOS = ...;
#endif
#if defined(ATTRIBUTES_NEED_TEXCOORD0)
inputMesh.uv0 = float4(vertex.uv, 0, 0);
#endif
UNITY_TRANSFER_INSTANCE_ID(input, inputMesh); // copy .instanceID from input to output
VaryingsMeshToPS varyingsType = VertMesh(inputMesh);
// VertMesh already does:
//UNITY_SETUP_INSTANCE_ID(input); // setup "unity_InstanceID" and "unity_StereoIndex" from input.instanceID
//UNITY_TRANSFER_INSTANCE_ID(input, output); // copy .instanceID from input(AttributeMesh) to output(VaryingsMeshToPS)
// THIS IS WHAT I HAD TO ADD, HERE.
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(varyingsType); // output.stereoTargetEyeIndexAsRTArrayIdx = unity_StereoEyeIndex
// 3) PACK the result for the rest of the HDRP Pipeline.
PackedVaryingsMeshToPS packed = PackVaryingsMeshToPS(varyingsType);
// Fragment shader does this:
// UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(packedInput); // unity_StereoEyeIndex = input.stereoTargetEyeIndexAsRTArrayIdx;
return packed;
}
So I had to add that macro, and add the missing struct variable to every input/output struct (and call a macro inside the Pack/Unpack generated functions), like this:
For some reasons, I thought that the struct were good, and the original Vert/Frag shaders did all the required plumbing. After all, when you directly use a generated shader from a shadergraph, it works in VR.
So this is my solution, it requires more editing of the generated shader, but it works in 2020.2.