Here is a script I currently have for object emptsh
to use a compute shader for making a point appear on a screen where every vertex on the BoyMesh
mesh is.
Now I'm trying to, instead of make simple points appear on screen for each BoyMesh
vertex position, spawn cubes from a different position and then have them fly over to each vertex position (one cube per vertex position). Any ideas?
using UnityEngine;
using System.Collections;
public class emptsh : MonoBehaviour
{
public BoyMesh BoyMesh;
public Mesh meshdata;
public ComputeShader cshader;
public Material mat;
private int kernel;
private int num4pos;
private ComputeBuffer posbuffer;
private int num4vertex;
private ComputeBuffer vertexbuffer;
private void meshInfo()
{
Vector3[] vertics = meshdata.vertices;
int[] triangles = meshdata.triangles;
num4vertex = triangles.Length;
Vector3[] newVertics = new Vector3[num4vertex];
for (int i = 0; i < num4vertex; ++i)
{
newVertics[i] = vertics[triangles[i]];
}
vertexbuffer = new ComputeBuffer(num4vertex, 12);
vertexbuffer.SetData(newVertics);
}
void Start()
{
meshdata = BoyMesh.Mesh;
kernel = cshader.FindKernel("CSMain");
num4pos = 1; //this determines how many appear
//num4vertex = ;
meshInfo();
//float3
posbuffer = new ComputeBuffer(num4pos, 12);
}
private void BufferSet()
{
cshader.SetBuffer(kernel, "posbuffer", posbuffer);
mat.SetBuffer("posbuffer", posbuffer);
mat.SetBuffer("vertexbuffer", vertexbuffer);
}
private void OnRenderObject()
{
BufferSet();
//1
cshader.Dispatch(kernel, 1, 1, 1);
//2
mat.SetPass(0);
//3
Graphics.DrawProceduralNow(MeshTopology.Points, num4vertex, num4pos);
}
private void OnDestroy()
{
posbuffer.Release();
vertexbuffer.Release();
}
}
And then the actual shader code:
Shader "Unlit/cshader4"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
CGINCLUDE
#define time _Time.y
float3 Rotx(in float3 p,in float a){
float c,s; float3 q = p;
c = cos(a); s = sin(a);
q.y = c * p.y - s * q.z;
q.z = s * p.y + c * q.z;
return q;
}
float random(float id){
return frac(sin(id)*678.342231);
}
ENDCG
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass{
CGPROGRAM
#pragma target 5.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
StructuredBuffer<float3> vertexbuffer;
StructuredBuffer<float3> posbuffer;
struct vertIN{
uint vID : SV_VertexID;
uint ins : SV_InstanceID;
};
struct vertOUT{
float4 pos : SV_POSITION;
};
vertOUT vert(vertIN i){
vertOUT o = (vertOUT)0;
float4 position = float4(vertexbuffer[i.vID],1);
position.xyz = Rotx(position.xyz,time*(1+random(i.ins)));
position.xyz += posbuffer[i.ins];
o.pos = UnityObjectToClipPos(position);
return o;
}
fixed4 frag(vertOUT ou):SV_Target{
return 1;
}
ENDCG
}
}
}
There are two parts to this.
First, you have to change to rendering triangles instead of points, and give your shader the vertices of all of the triangles that make up your cubes.
Change your MeshInfo
to create a vertex buffer of 36 * the number of vertices in the mesh. This is because you need to make 12 triangles for each vertex (2 triangles for each of the 6 sides of the cube). For each of the 36 vertices, you'll need to alter its position to move it from the center of the cube to one of its corners:
private void meshInfo()
{
float cubeHalfWidth = 0.01f; // determines how big the cube is.
num4vertex = 36 * meshdata.vertices;
Vector3[] newVertics = new Vector3[num4vertex];
for (int i = 0; i < meshdata.vertices; ++i)
{
Vector3 curVertex = meshdata.vertices[i];
// find corner positions
Vector3 bottomBackLeftCorner = new Vector3(curVertex.x - cubeHalfWidth, curVertex.y - cubeHalfWidth, curVertex.z - cubeHalfWidth);
Vector3 bottomFrontLeftCorner = new Vector3(curVertex.x - cubeHalfWidth, curVertex.y - cubeHalfWidth, curVertex.z + cubeHalfWidth_;
Vector3 bottomFrontRightCorner = new Vector3(curVertex.x + cubeHalfWidth, curVertex.y - cubeHalfWidth, curVertex.z + cubeHalfWidth);
Vector3 bottomBackRightCorner = new Vector3(curVertex.x + cubeHalfWidth, curVertex.y - cubeHalfWidth, curVertex.z - cubeHalfWidth);
Vector3 topBackLeftCorner = new Vector3(curVertex.x - cubeHalfWidth, curVertex.y + cubeHalfWidth, curVertex.z - cubeHalfWidth);
Vector3 topFrontLeftCorner = new Vector3(curVertex.x - cubeHalfWidth, curVertex.y + cubeHalfWidth, curVertex.z + cubeHalfWidth);
Vector3 topFrontRightCorner = new Vector3(curVertex.x + cubeHalfWidth, curVertex.y + cubeHalfWidth, curVertex.z + cubeHalfWidth);
Vector3 topBackRightCorner = new Vector3(curVertex.x + cubeHalfWidth, curVertex.y + cubeHalfWidth, curVertex.z - cubeHalfWidth)};
// create triangles, clockwise looking at visible side
int o=i*36;
// back Face
newVertics[o++] = bottomBackLeftCorner;
newVertics[o++] = topBackLeftCorner;
newVertics[o++] = topBackRightCorner;
newVertics[o++] = topBackRightCorner;
newVertics[o++] = bottomBackRightCorner;
newVertics[o++] = bottomBackLeftCorner;
// bottom Face
newVertics[o++] = bottomFrontRightCorner;
newVertics[o++] = bottomFrontLeftCorner;
newVertics[o++] = bottomBackLeftCorner;
newVertics[o++] = bottomBackLeftCorner;
newVertics[o++] = bottomBackRightCorner;
newVertics[o++] = bottomFrontRightCorner;
// front Face
newVertics[o++] = bottomFrontRightCorner;
newVertics[o++] = topFrontRightCorner;
newVertics[o++] = topFrontLeftCorner;
newVertics[o++] = topFrontLeftCorner;
newVertics[o++] = bottomFrontLeftCorner;
newVertics[o++] = bottomFrontRightCorner;
// top Face
newVertics[o++] = topBackRightCorner;
newVertics[o++] = topBackLeftCorner;
newVertics[o++] = topFrontLeftCorner;
newVertics[o++] = topFrontLeftCorner;
newVertics[o++] = topFrontRightCorner;
newVertics[o++] = topBackRightCorner;
// left Face
newVertics[o++] = bottomFrontLeftCorner;
newVertics[o++] = topFrontLeftCorner;
newVertics[o++] = topBackLeftCorner;
newVertics[o++] = topBackLeftCorner;
newVertics[o++] = bottomBackLeftCorner;
newVertics[o++] = bottomFrontLeftCorner;
// right Face
newVertics[o++] = bottomBackRightCorner;
newVertics[o++] = topBackRightCorner;
newVertics[o++] = topFrontRightCorner;
newVertics[o++] = topFrontRightCorner;
newVertics[o++] = bottomFrontRightCorner;
newVertics[o] = bottomBackRightCorner;
}
vertexbuffer = new ComputeBuffer(num4vertex, Marshal.SizeOf(newVertics.GetType().GetElementType()));
vertexbuffer.SetData(newVertics);
}
You'll also need to change the MeshTopology
to Triangles
:
Graphics.DrawProceduralNow(MeshTopology.Triangles, num4vertex, num4pos);
The second part is getting the cubes to move. This is the easier step.
In the shader, add a float _moveCubeT
parameter to the shader, and a lerping from some starting position to the position you have already based on the _moveCubeT
parameter:
Shader "Unlit/cshader4"
{
Properties
{
_moveCubeT ("MoveCubeT", Float) = 0
_MainTex ("Texture", 2D) = "white" {}
}
CGINCLUDE
#define time _Time.y
float3 Rotx(in float3 p,in float a){
float c,s; float3 q = p;
c = cos(a); s = sin(a);
q.y = c * p.y - s * q.z;
q.z = s * p.y + c * q.z;
return q;
}
float random(float id){
return frac(sin(id)*678.342231);
}
ENDCG
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass{
CGPROGRAM
#pragma target 5.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
StructuredBuffer<float3> vertexbuffer;
StructuredBuffer<float3> posbuffer;
float _moveCubeT;
struct vertIN{
uint vID : SV_VertexID;
uint ins : SV_InstanceID;
};
struct vertOUT{
float4 pos : SV_POSITION;
};
vertOUT vert(vertIN i){
vertOUT o = (vertOUT)0;
float3 startingpos = float3(0,0,0); //set starting pos for each cube here
float4 position = float4(vertexbuffer[i.vID],1);
position.xyz = Rotx(position.xyz,time*(1+random(i.ins)));
position.xyz += posbuffer[i.ins];
position.xyz = lerp(startingpos, position.xyz, _moveCubeT); // lerp based on time
o.pos = UnityObjectToClipPos(position);
return o;
}
fixed4 frag(vertOUT ou):SV_Target{
return 1;
}
ENDCG
}
}
}
Then back in your C# code, set this _moveCubeT
float according to where the cubes are in their transit:
private void BufferSet()
{
// Move cubes for 2 seconds and pause for 8 seconds, repeat.
float t = Mathf.Clamp( (Time.time % 10f) / 2f, 0f, 1f);
cshader.SetBuffer(kernel, "posbuffer", posbuffer);
mat.SetBuffer("posbuffer", posbuffer);
mat.SetBuffer("vertexbuffer", vertexbuffer);
mat.SetFloat("_moveCubeT", t);
}
Altogether, this is intended to give you completely unlit, untextured, white cubes that move to where the vertices are on the mesh. If you want to light, texture, or color these cubes, some changes will have to be made but that is more appropriate for another question.
I've never used DrawProceduralNow
so there may be some missing pieces but this should be considered at least a partial answer.