I'm attempting to create an unlit billboard shader for use in DOTS. I'm using quads to host the material
(Some regular, some DOTS for comparison purposes)
I've cribbed together something from this gist and other sources to render a sprite where I want it, however, it's not oriented towards the camera.
Shader"DOTSTest/UnlitWithDotsInstancing"
{
Properties
{
_BaseMap ("Base Texture", 2D) = "white" {}
_BaseColor ("Base Colour", Color) = (1, 1, 1, 1)
}
SubShader
{
Tags
{
"RenderPipeline"="UniversalPipeline"
"Queue"="Geometry"
"RenderType"="Transparent"
"PreviewType"="Plane"
}
//LOD100
Blend SrcAlpha OneMinusSrcAlpha
ZWrite Off
Pass
{
Name"Forward"
Tags {
"LightMode"="UniversalForward"
}
Cull Back
HLSLPROGRAM
#pragma exclude_renderers gles gles3 glcore
#pragma target 4.5
#pragma vertex UnlitPassVertex
#pragma fragment UnlitPassFragment
#pragma multi_compile_instancing
#pragma instancing_options renderinglayer
#pragma multi_compile _ DOTS_INSTANCING_ON
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
struct Attributes
{
float4 positionOS : POSITION;
float2 uv : TEXCOORD0;
float4 color : COLOR;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct Varyings
{
float4 positionCS : SV_POSITION;
float2 uv : TEXCOORD0;
float4 color : COLOR;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
CBUFFER_START(UnityPerMaterial)
float4 _BaseMap_ST;
float4 _BaseColor;
CBUFFER_END
#ifdef UNITY_DOTS_INSTANCING_ENABLED
UNITY_DOTS_INSTANCING_START(MaterialPropertyMetadata)
UNITY_DOTS_INSTANCED_PROP(float4, _BaseColor)
UNITY_DOTS_INSTANCING_END(MaterialPropertyMetadata)
#define _BaseColor UNITY_ACCESS_DOTS_INSTANCED_PROP_WITH_DEFAULT(float4, _BaseColor)
#endif
TEXTURE2D(_BaseMap);
SAMPLER(sampler_BaseMap);
Varyings UnlitPassVertex(Attributes input)
{
Varyings output;
UNITY_SETUP_INSTANCE_ID(input);
UNITY_TRANSFER_INSTANCE_ID(input, output);
const VertexPositionInputs positionInputs = GetVertexPositionInputs(input.positionOS.xyz);
output.positionCS = positionInputs.positionCS;
output.uv = TRANSFORM_TEX(input.uv, _BaseMap);
output.color = input.color;
return output;
}
half4 UnlitPassFragment(Varyings input) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(input);
half4 baseMap = half4(SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv));
return baseMap * _BaseColor * input.color;
}
ENDHLSL
}
}
}
It works (which is to say it renders, I haven't tried varying colors yet). Before I get to that, I want to fix the orientation.
Previously, I've achieved that using something like this [also cribbed from other sources]
v2f vert(appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.uv.xy;
// billboard mesh towards camera
float3 vpos = mul((float3x3)unity_ObjectToWorld, v.vertex.xyz);
float4 worldCoord = float4(unity_ObjectToWorld._m03, unity_ObjectToWorld._m13, unity_ObjectToWorld._m23, 1);
float4 viewPos = mul(UNITY_MATRIX_V, worldCoord) + float4(vpos, 0);
float4 outPos = mul(UNITY_MATRIX_P, viewPos);
o.pos = outPos;
UNITY_TRANSFER_FOG(o, o.vertex);
return o;
}
And while it's worked, I have to admit I don't understand what it's doing in the transformations.
Whilst attempting to wire up that code, I noticed that the shader compiles just fine for use on a regular object, but the DOTS variant throws
Shader error in 'DOTSTest/UnlitWithDotsInstancing': undeclared identifier 'unity_ObjectToWorld' at line 91 (on d3d11)
Compiling Subshader: 0, Pass: Forward, Fragment program with DOTS_INSTANCING_ON Platform defines: SHADER_API_DESKTOP UNITY_COLORSPACE_GAMMA UNITY_ENABLE_DETAIL_NORMALMAP UNITY_ENABLE_REFLECTION_BUFFERS UNITY_LIGHTMAP_FULL_HDR UNITY_LIGHT_PROBE_PROXY_VOLUME UNITY_PBS_USE_BRDF1 UNITY_SPECCUBE_BLENDING UNITY_SPECCUBE_BOX_PROJECTION UNITY_USE_DITHER_MASK_FOR_ALPHABLENDED_SHADOWS Disabled keywords: INSTANCING_ON SHADER_API_GLES30 UNITY_ASTC_NORMALMAP_ENCODING UNITY_FRAMEBUFFER_FETCH_AVAILABLE UNITY_HALF_PRECISION_FRAGMENT_SHADER_REGISTERS UNITY_HARDWARE_TIER1 UNITY_HARDWARE_TIER2 UNITY_HARDWARE_TIER3 UNITY_LIGHTMAP_DLDR_ENCODING UNITY_LIGHTMAP_RGBM_ENCODING UNITY_METAL_SHADOWS_USE_POINT_FILTERING UNITY_NO_DXT5nm UNITY_NO_FULL_STANDARD_SHADER UNITY_NO_SCREENSPACE_SHADOWS UNITY_PBS_USE_BRDF2 UNITY_PBS_USE_BRDF3 UNITY_PRETRANSFORM_TO_DISPLAY_ORIENTATION UNITY_UNIFIED_SHADER_PRECISION_MODEL UNITY_VIRTUAL_TEXTURING
I've tried swapping unity_ObjectToWorld
for _Object2World
to no avail.
If I include UnityCG.cginc
I get a _Time redefined
error (it appears to be clashing with Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl
). Removing that include means I can no longer reference UNITY_VERTEX_INPUT_INSTANCE_ID
.
Assuming this is the right approach, what's the DOTS-equivalent to unity_ObjectToWorld
? And/or where do I need to import it from?
If not, how should I be handling this?
Edit: I've just found this page in the docs, which says
- Declare all built-in engine properties, such as unity_ObjectToWorld or unity_WorldTransformParams, in a single CBUFFER called UnityPerDraw.
So I tried adding a
CBUFFER_START(UnityPerDraw)
float4x4 unity_ObjectToWorld;
CBUFFER_END
Which then fails to compile as unity_ObjectToWorld is being defined twice in the non-DOTS shader.
Shader error in 'DOTSTest/UnlitWithDotsInstancing': redefinition of 'unity_ObjectToWorld' at line 69 (on d3d11)
Compiling Subshader: 0, Pass: Unlit, Vertex program with
Moving it inside a conditional #ifdef UNITY_DOTS_INSTANCING_ENABLED
Compiles fine for non-DOTS (of course) but then fails DOTS with
A BatchDrawCommand is using a pass from the shader "DOTSTest/UnlitWithDotsInstancing" that is not SRP Batcher compatible. Reason: "Builtin cbuffer size is not the same for all stages" (UnityPerDraw)
This is not supported when rendering with a BatchRendererGroup (or Entities Graphics). MaterialID: 4 ("StarIcon"), MeshID: 1 ("Quad"), BatchID: 1.