,Implementing a Geometry shader for a pointcloud
Asked Answered
F

2

0

I am working on a project for college in Unity that involves displaying a very big pointcloud. I have implemented the point cloud as a mesh with 1 vertex per point and no triangles. I want to implement a shader in Unity that creates a triangle for each vertex and colors it of the color of the vertex.

I have no experience with shaders and I have developed the code trying to follow multiple resources online.

At the current state the code simply makes the pointcloud disappear and nothing is displayed.

Thank you!

 Shader "Custom/SpherePoint" {
Properties {
    _Color ("Color", Color) = (1,1,1,1)
    _Radius ("Sphere Radius", float) = 0.01
}
 SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
Pass {
    CGPROGRAM
    #pragma vertex vert
    #pragma fragment frag
    #pragma geometry geom
    #pragma target 4.0                  // Use shader model 3.0 target, to get nicer looking lighting
    #include "UnityCG.cginc"

    struct vertexIn {
        float4 pos : POSITION;
        float4 color : COLOR;
        float3 normal : NORMAL;
    };

    struct vertexOut {
        float4 pos : POSITION;
        float4 color : COLOR0;
        float3 normal : NORMAL;
    };

    struct geomOut {
        float4 pos : POSITION;
        float4 color : COLO0R;
        float3 normal : NORMAL;
    };

    //Vertex shader: computes normal wrt camera
    vertexOut vert (vertexIn i) {
        vertexOut o;
        o.pos = UnityObjectToClipPos(i.pos);
        //o.pos = mul(unity_ObjectToWorld, i.pos);
        o.color = i.color;
        //o.normal = normalize(_WorldSpaceCameraPos - mul(unity_ObjectToWorld, o.pos).xyz); //normal is towards the camera
        o.normal = ObjSpaceViewDir(o.pos);
        return o;
    }

    float _Radius;  

    //Geometry shaders: Creates an equilateral triangle with the original vertex in the orthocenter
    [maxvertexcount(3)]
    void geom(point vertexOut i[1], inout TriangleStream<geomOut> OutputStream)
    {
        geomOut o, p;
        float3 perp = float3(i[0].normal.y - i[0].normal.x, i[0].normal.x, i[0].normal.x) * _Radius;

        o.color = i[0].color;
        o.normal = i[0].normal;
        o.pos = i[0].pos;
        o.pos = float4(i[0].pos.x * _Radius, i[0].pos.yz, 1);
        //o.pos = float4(normalize(perp + i[0].pos) * _Radius , 1); //Generic perpendicular to the normal
        OutputStream.Append(o);

        p.color = i[0].color;
        p.normal = i[0].normal;
        p.pos = float4(i[0].pos.x, i[0].pos.y * _Radius, i[0].pos.z, 1);
        //p.pos = float4(   (normalize(cross(i[0].pos.xyz, perp.xyz)) - perp.xyz / 2 + i[0].pos) * _Radius,1);
        OutputStream.Append(p);

        o.color = i[0].color;
        o.normal = i[0].normal;
        o.pos = float4(i[0].pos.x * _Radius, i[0].pos.yz, 1);
        //p.pos = float4(-1 * (normalize(cross(i[0].pos.xyz, perp.xyz)) * _Radius) /* - perp.xyz / 2 + i[0].pos*/, 1);
        OutputStream.Append(p);

        OutputStream.RestartStrip();
    }

    float4 frag(geomOut i) : COLOR
    {
        return i.color;
    }
    ENDCG
}
}
   FallBack "Diffuse"
   }

,I am working on a project for college in Unity that involves displaying a very big pointcloud. I have implemented the point cloud as a mesh with 1 vertex per point and no triangles. I want to implement a shader in Unity that creates a triangle for each vertex and colors it of the color of the vertex.

I have no experience with shaders and I have developed the code trying to follow multiple resources online.

At the current state the code simply makes the pointcloud disappear and nothing is displayed.

Thank you!

   Shader "Custom/SpherePoint" {
Properties {
	_Color ("Color", Color) = (1,1,1,1)
	_Radius ("Sphere Radius", float) = 0.01
}
SubShader {
	Tags { "RenderType"="Opaque" }
	LOD 200
	Pass {
		CGPROGRAM
		#pragma vertex vert
		#pragma fragment frag
		#pragma geometry geom

		#pragma target 4.0					// Use shader model 3.0 target, to get nicer looking lighting
		#include "UnityCG.cginc"

		struct vertexIn {
			float4 pos : POSITION;
			float4 color : COLOR;
			float3 normal : NORMAL;
		};

		struct vertexOut {
			float4 pos : POSITION;
			float4 color : COLOR0;
			float3 normal : NORMAL;
		};

		struct geomOut {
			float4 pos : POSITION;
			float4 color : COLO0R;
			float3 normal : NORMAL;
		};

		//Vertex shader: computes normal wrt camera
		vertexOut vert (vertexIn i) {
			vertexOut o;
			o.pos = UnityObjectToClipPos(i.pos);
			//o.pos = mul(unity_ObjectToWorld, i.pos);
			o.color = i.color;
			//o.normal = normalize(_WorldSpaceCameraPos - mul(unity_ObjectToWorld, o.pos).xyz);	//normal is towards the camera
			o.normal = ObjSpaceViewDir(o.pos);
			return o;
		}

		float _Radius;	

		//Geometry shaders: Creates an equilateral triangle with the original vertex in the orthocenter
		[maxvertexcount(3)]
		void geom(point vertexOut i[1], inout TriangleStream<geomOut> OutputStream)
		{
			geomOut o, p;
			float3 perp = float3(i[0].normal.y - i[0].normal.x, i[0].normal.x, i[0].normal.x) * _Radius;

			o.color = i[0].color;
			o.normal = i[0].normal;
			o.pos = i[0].pos;
			o.pos = float4(i[0].pos.x * _Radius, i[0].pos.yz, 1);
			//o.pos = float4(normalize(perp + i[0].pos) * _Radius , 1);	//Generic perpendicular to the normal
			OutputStream.Append(o);

			p.color = i[0].color;
			p.normal = i[0].normal;
			p.pos = float4(i[0].pos.x, i[0].pos.y * _Radius, i[0].pos.z, 1);
			//p.pos = float4(	(normalize(cross(i[0].pos.xyz, perp.xyz)) - perp.xyz / 2 + i[0].pos) * _Radius,1);
			OutputStream.Append(p);

			o.color = i[0].color;
			o.normal = i[0].normal;
			o.pos = float4(i[0].pos.x * _Radius, i[0].pos.yz, 1);
			//p.pos = float4(-1 * (normalize(cross(i[0].pos.xyz, perp.xyz)) * _Radius) /* - perp.xyz / 2 + i[0].pos*/, 1);
			OutputStream.Append(p);

			OutputStream.RestartStrip();
		}

		float4 frag(geomOut i) : COLOR
		{
			return i.color;
		}
		ENDCG
	}
}
FallBack "Diffuse"
  }
Fairbanks answered 15/11, 2023 at 19:14 Comment(1)

Hi, I'm also working on a real-time kinect point-cloud visualization for an academic project. I've tried both of the provided answers, but they don't render any point at all. Did you found a working point cloud shader? Would be very helpful! thanks!

Culpable
S
0

Sorry but your geometry shader code doesn’t seem to make much sense to me. Keep in mind that the incoming position is in clipspace (range -1 to 1, the center point of the screen is 0,0). You multiply some components the position by radius. Your first and thrid point is identical so the triangle you generate is a degenerated triangle as all 3 points lie on the same line.

You may want to work with worldspace coordinates instead and do the clipspace conversion inside the geometry shader. However from the question it’s not clear what the desired output should be.

edit
Well, try something like this instead:

static const fixed SQRT3_6 = sqrt(3)/6;
float _Radius;  
float4 _Color;
[maxvertexcount(3)]
void geom(point vertexOut i[1], inout TriangleStream<geomOut> OutputStream)
{
    geomOut p;
    float a = _ScreenParams.x / _ScreenParams.y; // aspect ratio
    float s = _Radius;
    float s2 = s*SQRT3_6 *a;
    
    p.color = i[0].color * _Color;
    p.normal = float3(0,0,1);
    p.pos = i[0].pos + float4(0,s2*2,0,0);
    OutputStream.Append(p);
    p.pos = i[0].pos + float4(-s*0.5f,-s2,0,0);
    OutputStream.Append(p);
    p.pos = i[0].pos + float4(s*0.5f,-s2,0,0);
    OutputStream.Append(p);
    OutputStream.RestartStrip();
}

The rest should stay the same.

Signatory answered 6/6, 2023 at 2:13 Comment(2)

Thank you I am trying to create a triangle facing the camera for every input vertex, possibly centered on the vertex itself.

Fairbanks

Hmm, maybe use raw data, its whole array of colors. and then modify only needed colors. But you will need somehow to "convert" texture position (x,y) to array index. [Texture2D.GetRawTextureData][1] [1]: https://docs.unity3d.com/ScriptReference/Texture2D.GetRawTextureData.html

Lamond
R
0

Ya you dont need to use the normal to calculate the positions in the geometry shader because they are already in clipspace as Bunny83 pointed out. You could use the normal to do some more complex lighting in the fragment shader if youre interested in that though.

I made a similar geometry shader a while ago so I changed up your shader a bit and added in some comments based on how i did it. Mainly changed your geom function, also added in random rotations of each particle and code for transparency if you want that. Hope this helps!

Shader "Custom/SpherePoint" {
 Properties {
     _Radius ("Sphere Radius", float) = 1.0
 }
  SubShader {
 LOD 200
 Tags { "RenderType"="Opaque" }
 //if you want transparency
 //Tags { "Queue" = "Transparent" "RenderType" = "Transparent" }
 //Blend SrcAlpha OneMinusSrcAlpha
 Pass {
     CGPROGRAM
     #pragma vertex vert
     #pragma fragment frag
     #pragma geometry geom
     #pragma target 4.0                  // Use shader model 3.0 target, to get nicer looking lighting
     #include "UnityCG.cginc"
     struct vertexIn {
         float4 pos : POSITION;
         float4 color : COLOR;
     };
     struct vertexOut {
         float4 pos : SV_POSITION;
         float4 color : COLOR0;
         float3 normal : NORMAL;
		 float r : TEXCOORD0; // not sure if this is good to do lol
     };
     struct geomOut {
         float4 pos : POSITION;
         float4 color : COLO0R;
         float3 normal : NORMAL;
     };

	 float rand(float3 p){
		 return frac(sin(dot(p.xyz, float3(12.9898, 78.233, 45.5432))) * 43758.5453);
	 }
     float2x2 rotate2d(float a){
		float s = sin(a);
		float c = cos(a);
		return float2x2(c,-s,s,c);
	 }
     //Vertex shader: computes normal wrt camera
     vertexOut vert (vertexIn i) {
         vertexOut o;
         o.pos = UnityObjectToClipPos(i.pos);
         o.color = i.color;
         o.normal = ObjSpaceViewDir(o.pos);
		 o.r = rand(i.pos);// calc random value based on object space pos
		 // from world space instead (particles will spin when mesh moves, kinda funny lol)
		 //o.r = rand(mul(unity_ObjectToWorld,i.pos));
         return o;
     }

     float _Radius;  
     //Geometry shaders: Creates an equilateral triangle with the original vertex in the orthocenter
     [maxvertexcount(3)]
     void geom(point vertexOut IN[1], inout TriangleStream<geomOut> OutputStream)
     {
		float2 dim = float2(_Radius,_Radius);

		float2 p[3];	// equilateral tri
		p[0] = float2(-dim.x, dim.y * .57735026919);
		p[1] = float2(0., -dim.y * 1.15470053838);
		p[2] = float2(dim.x, dim.y * .57735026919);

		float2x2 r = rotate2d(IN[0].r * 3.14159);

		geomOut OUT;
		OUT.color = IN[0].color;
		OUT.normal = IN[0].normal;

		for (int i = 0; i < 3; i++) {
			p _= mul(r,p*);	// apply rotation*_

p.x = _ScreenParams.y / ScreenParams.x; // make square
OUT.pos = IN[0].pos + float4(p,0,0) / 2.;
OutputStream.Append(OUT);
}
}
float4 frag(geomOut i) : COLOR
{
return i.color;
// could do some additional lighting calculation here based on normal*

}
ENDCG
}
}
FallBack “Diffuse”
}_

Rivulet answered 1/12, 2017 at 23:28 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.