- Home /
Constructing a Cube Primitive using Shader
Ive been trying to construct a Cube Primitive using Shaders, but havent really got anything to appear on the screen. Im using Unity Pro 4.3.1f1 at Windows 7 and using a GTX 260 as graphic card. The "Use Direct 3D 11" option at Player Settings is enable.
The reason I need to construct a Cube Primitive using Shader is that I need to render a Point Cloud (10k+ points), where every point has got to be rendered using cubes.
This is my try so far:
Shader "Custom/CubeColor"
{
Properties
{
_MainTex ("TileTexture", 2D) = "white" {}
_PointSize("PointSize", Float) = 10
_MyColor ("Some Color", Color) = (0,1,0,1)
}
SubShader
{
LOD 200
Pass
{
CGPROGRAM
#pragma only_renderers d3d11
#pragma target 4.0
#include "UnityCG.cginc"
#pragma vertex myVertexShader
#pragma geometry myGeometryShader
#pragma fragment myFragmentShader
struct vIn // Into the vertex shader
{
float4 vertex : POSITION;
float4 color : COLOR0;
};
struct gIn // OUT vertex shader, IN geometry shader
{
float4 pos : SV_POSITION;
float4 col : COLOR0;
float size : PSIZE;
};
struct v2f // OUT geometry shader, IN fragment shader
{
float4 pos : SV_POSITION;
float2 uv_MainTex : TEXCOORD0;
float4 col : COLOR0;
};
float4 _MainTex_ST;
sampler2D _MainTex;
float _PointSize;
fixed4 _MyColor;
// ----------------------------------------------------
gIn myVertexShader(vIn v)
{
gIn o; // Out here, into geometry shader
UNITY_INITIALIZE_OUTPUT(gIn,o);
// Passing on color to next shader (using .r/.g there as tile coordinate)
o.col = _MyColor;//v.color;
o.size = _PointSize;
// Passing on center vertex (tile to be built by geometry shader from it later)
o.pos = v.vertex;
return o;
}
[maxvertexcount(10)] // 8 vertex
// ----------------------------------------------------
// Using "point" type as input, not "triangle"
void myGeometryShader(point gIn vert[1], inout TriangleStream<v2f> triStream)
{
const float f = _PointSize/2; //half size
The 8 vertex positions of a CUBE tile
const float4 vc[8] = { float4( -f, -f, -f, 0.0f ),
float4( -f, -f, +f, 0.0f ),
float4( -f, +f, -f, 0.0f ),
float4( -f, +f, +f, 0.0f ),
float4( +f, -f, -f, 0.0f ),
float4( +f, -f, +f, 0.0f ),
float4( +f, +f, -f, 0.0f ),
float4( +f, +f, +f, 0.0f ) };
const int TRI_STRIP_CUBE[9] = { 0, 2, 1, 3, 5, 7, 4, 6, 0 };
v2f v[12]; //for CUBE
// Assign new vertices positions (12 new tile vertices, forming CUBE)
for (int i=0;i<12;i++) { v[i].pos = vert[0].pos + vc[i]; }
// Position in view space
for (int i=0;i<6;i++) { v[i].pos = mul(UNITY_MATRIX_MVP, v[i].pos); }
// Build the CUBE tile by submitting triangle strip vertices
for (int i=0;i<9;i++) triStream.Append(v[TRI_STRIP_CUBE[i]]);
triStream.RestartStrip();
}
// ----------------------------------------------------
half4 myFragmentShader(v2f IN) : COLOR
{
// Not considering normals/light here. Just texture
half4 c = tex2D (_MainTex, IN.uv_MainTex);
return c;
}
ENDCG
}
}
FallBack "Diffuse"
}
And the test Mesh is created with the following method:
void CreateMesh( int startRange, int endRange, Mesh currMesh ) {
Vector3[] points = new Vector3[maxPointsPerMesh];
int[] indexes = new int[maxPointsPerMesh];
Color[] colors = new Color[maxPointsPerMesh];
for(int i=0;i<points.Length;++i) {
points[i] = new Vector3(Random.Range(startRange,endRange), Random.Range (startRange,endRange), Random.Range (startRange,endRange));
indexes[i] = i;
colors[i] = new Color(Random.Range(0.0f,1.0f),Random.Range (0.0f,1.0f),Random.Range(0.0f,1.0f),1.0f);
}
currMesh.vertices = points;
currMesh.colors = colors;
currMesh.SetIndices(indexes, MeshTopology.Points,0);
}
My Game Object has a MeshFilter and a MeshRenderer.
What am I doing wrong?
Thank you all!
Answer by thrmotta · Jul 15, 2014 at 06:31 PM
I finally made it, and it also worked on my previous hardware. Thank you @Bunny85 for your marvelous help! Here is the code that got it working:
Shader "Custom/CubeShader"
{
Properties
{
_MainTex ("TileTexture", 2D) = "white" {}
_PointSize("Point Size", Float) = 1.0
}
SubShader
{
LOD 200
Pass
{
CGPROGRAM
#pragma only_renderers d3d11
#pragma target 4.0
#include "UnityCG.cginc"
#pragma vertex myVertexShader
#pragma geometry myGeometryShader
#pragma fragment myFragmentShader
#define TAM 36
struct vIn // Into the vertex shader
{
float4 vertex : POSITION;
float4 color : COLOR0;
};
struct gIn // OUT vertex shader, IN geometry shader
{
float4 pos : SV_POSITION;
float4 col : COLOR0;
};
struct v2f // OUT geometry shader, IN fragment shader
{
float4 pos : SV_POSITION;
float2 uv_MainTex : TEXCOORD0;
float4 col : COLOR0;
};
float4 _MainTex_ST;
sampler2D _MainTex;
float _PointSize;
// ----------------------------------------------------
gIn myVertexShader(vIn v)
{
gIn o; // Out here, into geometry shader
// Passing on color to next shader (using .r/.g there as tile coordinate)
o.col = v.color;
// Passing on center vertex (tile to be built by geometry shader from it later)
o.pos = v.vertex;
return o;
}
// ----------------------------------------------------
[maxvertexcount(TAM)]
// ----------------------------------------------------
// Using "point" type as input, not "triangle"
void myGeometryShader(point gIn vert[1], inout TriangleStream<v2f> triStream)
{
float f = _PointSize/20.0f; //half size
const float4 vc[TAM] = { float4( -f, f, f, 0.0f), float4( f, f, f, 0.0f), float4( f, f, -f, 0.0f), //Top
float4( f, f, -f, 0.0f), float4( -f, f, -f, 0.0f), float4( -f, f, f, 0.0f), //Top
float4( f, f, -f, 0.0f), float4( f, f, f, 0.0f), float4( f, -f, f, 0.0f), //Right
float4( f, -f, f, 0.0f), float4( f, -f, -f, 0.0f), float4( f, f, -f, 0.0f), //Right
float4( -f, f, -f, 0.0f), float4( f, f, -f, 0.0f), float4( f, -f, -f, 0.0f), //Front
float4( f, -f, -f, 0.0f), float4( -f, -f, -f, 0.0f), float4( -f, f, -f, 0.0f), //Front
float4( -f, -f, -f, 0.0f), float4( f, -f, -f, 0.0f), float4( f, -f, f, 0.0f), //Bottom
float4( f, -f, f, 0.0f), float4( -f, -f, f, 0.0f), float4( -f, -f, -f, 0.0f), //Bottom
float4( -f, f, f, 0.0f), float4( -f, f, -f, 0.0f), float4( -f, -f, -f, 0.0f), //Left
float4( -f, -f, -f, 0.0f), float4( -f, -f, f, 0.0f), float4( -f, f, f, 0.0f), //Left
float4( -f, f, f, 0.0f), float4( -f, -f, f, 0.0f), float4( f, -f, f, 0.0f), //Back
float4( f, -f, f, 0.0f), float4( f, f, f, 0.0f), float4( -f, f, f, 0.0f) //Back
};
const float2 UV1[TAM] = { float2( 0.0f, 0.0f ), float2( 1.0f, 0.0f ), float2( 1.0f, 0.0f ), //Esta em uma ordem
float2( 1.0f, 0.0f ), float2( 1.0f, 0.0f ), float2( 1.0f, 0.0f ), //aleatoria qualquer.
float2( 0.0f, 0.0f ), float2( 1.0f, 0.0f ), float2( 1.0f, 0.0f ),
float2( 1.0f, 0.0f ), float2( 1.0f, 0.0f ), float2( 1.0f, 0.0f ),
float2( 0.0f, 0.0f ), float2( 1.0f, 0.0f ), float2( 1.0f, 0.0f ),
float2( 1.0f, 0.0f ), float2( 1.0f, 0.0f ), float2( 1.0f, 0.0f ),
float2( 0.0f, 0.0f ), float2( 1.0f, 0.0f ), float2( 1.0f, 0.0f ),
float2( 1.0f, 0.0f ), float2( 1.0f, 0.0f ), float2( 1.0f, 0.0f ),
float2( 0.0f, 0.0f ), float2( 1.0f, 0.0f ), float2( 1.0f, 0.0f ),
float2( 1.0f, 0.0f ), float2( 1.0f, 0.0f ), float2( 1.0f, 0.0f ),
float2( 0.0f, 0.0f ), float2( 1.0f, 0.0f ), float2( 1.0f, 0.0f ),
float2( 1.0f, 0.0f ), float2( 1.0f, 0.0f ), float2( 1.0f, 0.0f )
};
const int TRI_STRIP[TAM] = { 0, 1, 2, 3, 4, 5,
6, 7, 8, 9,10,11,
12,13,14, 15,16,17,
18,19,20, 21,22,23,
24,25,26, 27,28,29,
30,31,32, 33,34,35
};
v2f v[TAM];
int i;
// Assign new vertices positions
for (i=0;i<TAM;i++) { v[i].pos = vert[0].pos + vc[i]; v[i].col = vert[0].col; }
// Assign UV values
for (i=0;i<TAM;i++) v[i].uv_MainTex = TRANSFORM_TEX(UV1[i],_MainTex);
// Position in view space
for (i=0;i<TAM;i++) { v[i].pos = mul(UNITY_MATRIX_MVP, v[i].pos); }
// Build the cube tile by submitting triangle strip vertices
for (i=0;i<TAM/3;i++)
{
triStream.Append(v[TRI_STRIP[i*3+0]]);
triStream.Append(v[TRI_STRIP[i*3+1]]);
triStream.Append(v[TRI_STRIP[i*3+2]]);
triStream.RestartStrip();
}
}
// ----------------------------------------------------
float4 myFragmentShader(v2f IN) : COLOR
{
//return float4(1.0,0.0,0.0,1.0);
return IN.col;
}
ENDCG
}
}
}
Answer by Bunny83 · Jul 09, 2014 at 10:35 PM
Well, haven't used a geometry shader yet, mainly because i don't have a dx11 card :D (i'm still on winxp 32bit with dx9.0c).
However your cube creation looks a bit strange:
First of all a cube needs 24 vertices, not 12. You have 6 sides with 4 vertices each.
Next thing is your "vc" array has a size of 8 but you use your for variable "i" which goes up to index 11 so you're out of bounds here.
Why do you only multiply the first 6 elements in your "v" array with the MVP?
Why do you only have 9 indices? shouldn't that be 36 indices? (`6*2*3`)
Finally it seems you map a texture in your fragment shader but you don't set any uvs when you create the new vertices.
Well, as i said i haven't written a geometry shaders yet, but i guess it should be something like that:
[maxvertexcount(24)] // 24 vertices
void myGeometryShader(point gIn vert[1], inout TriangleStream<v2f> triStream)
{
const float f = _PointSize/2; //half size
const float4 vc[8] = { float4( -f, -f, -f, 0.0f ), //0
float4( -f, -f, +f, 0.0f ), //1
float4( -f, +f, -f, 0.0f ), //2
float4( -f, +f, +f, 0.0f ), //3
float4( +f, -f, -f, 0.0f ), //4
float4( +f, -f, +f, 0.0f ), //5
float4( +f, +f, -f, 0.0f ), //6
float4( +f, +f, +f, 0.0f ) };//7
const int VERT_ORDER[24] = {0,1,3,2, // left
0,2,6,4, // front
4,6,7,5, // right
7,3,1,5, // back
2,3,7,6, // top
0,4,5,1 // bottom
};
v2f v[24]; //for CUBE
// Assign new vertices positions (24 new tile vertices, forming CUBE)
for (int i=0;i<24;i++) { v[i].pos = vert[0].pos + vc[VERT_ORDER[i]]; }
// Position in view space
for (int i=0;i<24;i++) { v[i].pos = mul(UNITY_MATRIX_MVP, v[i].pos); }
// Build the CUBE tile by submitting triangle strip vertices
for (int i=0;i<6;i++)
{
triStream.Append(v[i*4 + 0]);
triStream.Append(v[i*4 + 1]);
triStream.Append(v[i*4 + 3]);
triStream.Append(v[i*4 + 2]);
triStream.RestartStrip();
}
}
I can't test it, so if you have some time, feel free to go ahead ;) Oh, i didn't calculated uvs yet, so the cube will have the same color. Since your vertex-struct doesn't have a normal, i'm not sure if the lighting will work as it should.
ps: I would love to play around with a gShader now, but i'll have to wait until i'm ready for some new hardware ;)
Its mainly because this is the first shader Im building =D
After searching GitHub I found a marching cubes sample and modified it to the following
@thrmotta: Edited my answer and added the cube code i've just written from scratch ;) $$anonymous$$aybe i made a mistake when setting up the vert order array (solving rubic's cube in your $$anonymous$$d might helps here...) but in general it should work like that.
The vert order array actually duplicates each vertex 3 times and forms quads. Finally we add two triangles for each quad, so 4 vertices become 6 (or 24 become 36)
Just changed the "maxvertexcount" to 36 since each "point" will emit 36 vertices
Just read somewhere that the topology in a geometry shader is always "strip", so we can actually create quads with a triangle strip. I changed the order to match the usual "strip rules" and also moved RestartStrip inside the for loop, so after each quad we start a new one.