Hi
I have a plane that i divide into “random” triangles (struct v0, v1, v2) in a compute shader. Now i’m trying to create instanced particles from this data.
I have a position matrix buffer in world space for the triangle and the vertex coordinates in local space as triangle structs.
So i made a geometry shader where i have set both buffers. I do the transformations in the vert function and store in the v2g, the use triangle as primitive type and assign v0, v1, v2 to the inputs and append to the strip.
And kind of makes sense that i get 3 triangles at the vert positions of my instanced quad.
How do i go about just making 1 triangle out of my data?
I tried using point and strangely enough ended up with same result. I think i lack some insight in whats happening between vert and geo function. As i understand the geo runs once per vertex? Same as vert function, but then we can access all vertices?
(code below is testing with just one same triangle for all, not the “random” coordinates i have.)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class InstancedFrag : MonoBehaviour
{
public Mesh mesh;
public Material material;
public ComputeShader compute;
public Vector3Int dimensions;
public float triangleSize;
[SerializeField]
private Matrix4x4[] mats;
[SerializeField]
private Triangle[] tris;
private int posKernel, trisKernel;
private ComputeBuffer trisBuff, posBuff, argsBuff;
private int amount;
private uint gx, gy, gz;
private Bounds bounds;
private Vector2Int threadGroupSize;
private void Start()
{
posKernel = compute.FindKernel("PositionKernel");
bounds = new Bounds(this.transform.position + dimensions / 2, dimensions);
int x = dimensions.x;
int y = dimensions.y;
compute.GetKernelThreadGroupSizes(posKernel, out gx, out gy, out gz);
threadGroupSize.x = Mathf.CeilToInt(x / (float)gx);
threadGroupSize.y = Mathf.CeilToInt(y / (float)gy);
x = Mathf.CeilToInt(threadGroupSize.x * (float)gx);
y = Mathf.CeilToInt(threadGroupSize.y * (float)gy);
amount = x * y;
mats = new Matrix4x4[amount];
posBuff = new ComputeBuffer(amount, sizeof(float) * 4 * 4, ComputeBufferType.Structured);
posBuff.SetData(mats);
compute.SetBuffer(posKernel, "_Matrix", posBuff);
compute.SetInts("_Area", new int[] {dimensions.x, dimensions.y, dimensions.z });
material.SetBuffer("_Matrix", posBuff);
compute.Dispatch(posKernel, threadGroupSize.x, threadGroupSize.y, 1);
trisKernel = compute.FindKernel("TriangleKernel");
tris = new Triangle[amount];
trisBuff = new ComputeBuffer(amount, Triangle.Size(), ComputeBufferType.Structured);
trisBuff.SetData(tris);
compute.SetBuffer(trisKernel, "_Triangles", trisBuff);
material.SetBuffer("_Triangles", trisBuff);
compute.GetKernelThreadGroupSizes(posKernel, out gx, out gy, out gz);
compute.Dispatch(trisKernel, Mathf.CeilToInt(amount /gx), 1, 1);
argsBuff = new ComputeBuffer(1, 5 * sizeof(uint), ComputeBufferType.IndirectArguments);
argsBuff.SetData(SetupArgs(mesh, amount));
posBuff.GetData(mats);
trisBuff.GetData(tris);
}
private void Update()
{
Graphics.DrawMeshInstancedIndirect(mesh, 0, material, bounds, argsBuff);
}
uint[] SetupArgs(Mesh mesh, int size)
{
uint[] args = new uint[5] { 0, 0, 0, 0, 0 };
// 0 == number of triangle indices, 1 == population, others are only relevant if drawing submeshes.
args[0] = (uint)mesh.GetIndexCount(0);
args[1] = (uint)(size);
args[2] = (uint)mesh.GetIndexStart(0);
args[3] = (uint)mesh.GetBaseVertex(0);
return args;
}
private void OnDisable()
{
if (posBuff != null)
{
posBuff.Release();
}
posBuff = null;
if (trisBuff != null)
{
trisBuff.Release();
}
trisBuff = null;
if (argsBuff != null)
{
argsBuff.Release();
}
argsBuff = null;
}
private void OnDrawGizmosSelected()
{
Gizmos.color = Color.green;
Gizmos.DrawWireCube(bounds.center, bounds.size);
}
}
[System.Serializable]
public struct Triangle
{
public Vector3 v0, v1, v2;
public static int Size()
{
return sizeof(float) * 3 * 3;
}
}
// Each #kernel tells which function to compile; you can have many kernels
#pragma kernel PositionKernel
#pragma kernel TriangleKernel
struct Triangle
{
float3 v0, v1, v2;
};
RWStructuredBuffer<float4x4> _Matrix;
RWStructuredBuffer<Triangle> _Triangles;
int3 _Area;
int to1D(int x, int y, int z) {
return (z * _Area.x * _Area.y) + (y * _Area.x) + x;
}
[numthreads(8,8,1)]
void PositionKernel (uint3 id : SV_DispatchThreadID)
{
float4x4 translation = float4x4(
1, 0, 0, id.x - (float)_Area.x * .5 + .5,
0, 1, 0, id.y - (float)_Area.y * .5 + .5,
0, 0, 1, id.z - (float)_Area.z * .5 + .5,
0, 0, 0, 1
);
uint i = to1D(id.x, id.y, id.z);
_Matrix[i] = translation;
}
[numthreads(8, 1, 1)]
void TriangleKernel(uint3 id : SV_DispatchThreadID)
{
Triangle t;
t.v0 = float3(0, .5, 0);
t.v1 = float3(.5,-.5, 0);
t.v2 = float3(-.5, -.5, 0);
_Triangles[id.x] = t;
}
Shader "Unlit/FragInstanced"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
// Upgrade NOTE: excluded shader from DX11, OpenGL ES 2.0 because it uses unsized arrays
#pragma require geometry
#pragma vertex vert
#pragma fragment frag
#pragma geometry geom
#pragma target 4.5
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
float2 uv : TEXCOORD0;
float3 v0 : TEXCOORD1;
float3 v1 : TEXCOORD2;
float3 v2 : TEXCOORD3;
uint id : TEXCOORD4;
};
sampler2D _MainTex;
float4 _MainTex_ST;
struct Triangle
{
float4 v0, v1, v2;
};
StructuredBuffer<Triangle> _Triangles;
StructuredBuffer<float4x4> _Matrix;
v2f vert(appdata v, uint instanceID: SV_InstanceID)
{
v2f o;
UNITY_INITIALIZE_OUTPUT(v2f, o);
float4 pos = mul(_Matrix[instanceID], v.vertex);
o.vertex = UnityObjectToClipPos(pos);
o.id = instanceID;
o.v0 = mul(_Matrix[instanceID], float4(_Triangles[instanceID].v0.xyz, 1));
o.v1 = mul(_Matrix[instanceID], float4(_Triangles[instanceID].v1.xyz, 1));
o.v2 = mul(_Matrix[instanceID], float4(_Triangles[instanceID].v2.xyz, 1));
return o;
}
[maxvertexcount(3)]
void geom(point v2f input[1], inout TriangleStream<v2f> triStream)
{
v2f o0 = input[0];
/*v2f o1 = input[1];
v2f o2 = input[2];*/
v2f vertex0;
v2f vertex1;
v2f vertex2;
UNITY_INITIALIZE_OUTPUT(v2f, vertex0);
UNITY_INITIALIZE_OUTPUT(v2f, vertex1);
UNITY_INITIALIZE_OUTPUT(v2f, vertex2);
vertex0.vertex = UnityObjectToClipPos(o0.v0);
vertex1.vertex = UnityObjectToClipPos(o0.v1);
vertex2.vertex = UnityObjectToClipPos(o0.v2);
triStream.Append(vertex0);
triStream.Append(vertex1);
triStream.Append(vertex2);
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
return col;
}
ENDCG
}
}
Fallback "VertexLit"
}