Im creating a pretty simple game in Unity3d where I need to create multiple Meshes. The code that I create is pretty simple and yet after having more than 8 Meshes at the same time, the peerformance reduces considerably to just a couple of fps (~8 fps). The Mesh that I create is just a simple square so I really don´t know where the problem is, here´s my code:
using UnityEngine;
using System.Collections;
public class TetraGenerator : MonoBehaviour {
public int slices;
public GameObject forceSource;
void OnMouseDown(){
var arcLength = Mathf.PI / slices;
var distance = 10;
var height = 1;
var origin = Random.Range(-slices,slices);
Vector3[] vertices = new Vector3[4];
vertices [0] = new Vector3 (Mathf.Cos(origin*arcLength),Mathf.Sin(origin*arcLength));
vertices [1] = new Vector3 (Mathf.Cos(origin*arcLength),Mathf.Sin(origin*arcLength));
vertices [2] = new Vector3 (Mathf.Cos((origin+1)*arcLength),Mathf.Sin((origin+1)*arcLength));
vertices [3] = new Vector3 (Mathf.Cos((origin+1)*arcLength),Mathf.Sin((origin+1)*arcLength));
vertices [0] *= distance;
vertices [1] *= (distance+height);
vertices [2] *= (distance+height);
vertices [3] *= distance;
Vector3 frameRef = new Vector3(Mathf.Cos(origin*arcLength+(arcLength/2)),Mathf.Sin(origin*arcLength+(arcLength/2)));
frameRef *= distance;
vertices [0] -= frameRef;
vertices [1] -= frameRef;
vertices [2] -= frameRef;
vertices [3] -= frameRef;
int[] triangles = new int[]{0,1,2,2,3,0};
Mesh mesh = new Mesh ();
mesh.vertices = vertices;
mesh.triangles = triangles;
GameObject tile = new GameObject("tile",typeof(MeshFilter),typeof(MeshRenderer));
tile.transform.position = frameRef;
MeshFilter meshFilter = tile.GetComponent<MeshFilter> ();
meshFilter.mesh = mesh;
}
}
Your problem is that you are not setting a material or that you dont provide everything the material needs like uv coordinates or vertex colors. I am not sure if its the error messages in the Debug.Log or if the shader itself is causing the low framerate, but to test it you can use:
// enter this at the top and set the material in the inspector
public Material mat;
[...]
// enter this at the bottom
MeshRenderer meshRenderer = tile.GetComponent<MeshRenderer>();
meshRenderer.material = mat;
and as material you create a new one and use a shader with this code:
Shader "SimpleShader"
{
SubShader
{
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
struct vertexInput
{
float4 pos : POSITION;
};
struct vertexOutput
{
float4 pos : SV_POSITION;
float4 col : COLOR0;
};
vertexOutput vert(vertexInput input)
{
vertexOutput output;
output.pos = mul(UNITY_MATRIX_MVP, input.pos);
output.col = float4(1, 0, 0, 1);
return output;
}
float4 frag(vertexOutput input) : COLOR
{
return input.col;
}
ENDCG
}
}
}
Related
I tried to learn from this tutorial : Writing Shaders In Unity
The shader code :
Shader "Custom/TerrainCircle"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
_MainColor("Main Color", Color) = (0, 1, 0)
_CircleColor("Circle Color", Color) = (1, 0, 0)
_Center("Center", Vector) = (0,0,0,0)
_Radius("Radius", Range(0, 100)) = 10
_Thickness("Thickness", Range(0, 100)) = 5
}
SubShader
{
CGPROGRAM
#pragma surface surfaceFunc Lambert
sampler2D _MainTex;
fixed3 _MainColor;
fixed3 _CircleColor;
float3 _Center;
float _Thickness;
float _Radius;
struct Input {
float2 uv_MainTex;
float3 worldPos;
};
void surfaceFunc(Input IN, inout SurfaceOutput o) {
half4 c = tex2D(_MainTex, IN.uv_MainTex);
float dist = distance(_Center, IN.worldPos);
if (dist > _Radius && dist < (_Radius + _Thickness))
o.Albedo = _CircleColor;
else
o.Albedo = c.rgb;
o.Alpha = c.a;
}
ENDCG
}
}
Then I created a Shader file with the code and a material. Added the Shader to the material.
Then dragged the material to the terrain.
Two problems :
When dragging the material to the terrain it put the material only on small part of the terrain on this rock in white. Why it's not applying the material over the whole terrain ?
It's not showing the circle at all. Nothing is drawn. Not even close to the tutorial video in the link.
I moved the terrain to the side since the rocks and cliffs are from another asset. But now I can't drag the material over the terrain. The terrain is not accepting the material at all.
Still not working not drawing the circle on the terrain.
I tried with a new terrain : Terrain (1)
Added the New Material :
This is the material settings :
This is the shader settings :
And the shader code :
Shader "Custom/TerrainCircle"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
_MainColor("Main Color", Color) = (0, 1, 0)
_CircleColor("Circle Color", Color) = (1, 0, 0)
_Center("Center", Vector) = (0,0,0,0)
_Radius("Radius", Range(0, 100)) = 10
_Thickness("Thickness", Range(0, 100)) = 5
}
SubShader
{
CGPROGRAM
#pragma surface surfaceFunc Lambert
sampler2D _MainTex;
fixed3 _MainColor;
fixed3 _CircleColor;
float3 _Center;
float _Thickness;
float _Radius;
struct Input {
float2 uv_MainTex;
float3 worldPos;
};
void surfaceFunc(Input IN, inout SurfaceOutput o) {
half4 c = tex2D(_MainTex, IN.uv_MainTex);
float dist = distance(_Center, IN.worldPos);
if (dist > _Radius && dist < (_Radius + _Thickness))
o.Albedo = _CircleColor;
else
o.Albedo = c.rgb;
o.Alpha = c.a;
}
ENDCG
}
}
The shader type I created is : Standard Surface Shader
Solution I had to create a mono script :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteInEditMode]
public class CircleOnTerrain : MonoBehaviour
{
public Material radiusMaterial;
public float radius = 1;
public Color color = Color.white;
public Color circleColor = Color.blue;
public float thickness = 1;
void Update()
{
radiusMaterial.SetVector("_Center", transform.position);
radiusMaterial.SetFloat("_Radius", radius);
radiusMaterial.SetColor("_MainColor", color);
radiusMaterial.SetColor("_CircleColor", circleColor);
radiusMaterial.SetFloat("_Thickness", thickness);
}
}
Attached the script to a 3d cube and now it's working.
Add your material to the terrain like so:
I made a scene where balls appear in 3D space. Triangle balls spend a lot of resources. So I did this using 2d surfaces (quad) with a ball texture. But now I need to adjust the direction of the shape every time the camera moves. I do this using position transformation and the LookAt method. The question is can I optimize this? If it is possible to rotate the shapes with shader, this will greatly help.
using UnityEngine;
public class WorldSurf : MonoBehaviour
{
GameObject[] matrix;
int xSize = 20;
int ySize = 20;
int zSize = 20;
// Start is called before the first frame update
void Start()
{
matrix = new GameObject[xSize * ySize * zSize];
//var shader = Shader.Find("Legacy Shaders/Diffuse");
var shader = Shader.Find("Sprites/Default");
//var texture = Resources.Load<Texture>("Textures/Ball_01");
var i = 0;
for (var x = 0f; x < xSize; ++x)
{
for (var y = 0f; y < ySize; ++y)
{
for (var z = 0f; z < zSize; ++z)
{
var texture = Resources.Load<Texture>("Textures/Ball_" + ((int)Random.Range(0, 15)).ToString("00"));
matrix[i++] = CreateQuad(x * 3, y * 3, z * 3, shader, texture);
}
}
}
}
static GameObject CreateQuad(float x, float y, float z, Shader shader, Texture texture)
{
var quad = GameObject.CreatePrimitive(PrimitiveType.Quad);
quad.transform.position = new Vector3(x, y, z);
quad.transform.forward = Camera.main.transform.forward;
var rend = quad.GetComponent<Renderer>();
rend.material.shader = shader;
rend.material.mainTexture = texture;
//rend.material.color = Color.red;
return quad;
}
// Update is called once per frame
void Update()
{
var pos = Camera.main.transform.position;
foreach (var itm in matrix)
{
itm.transform.LookAt(pos);
}
}
}
Generally yes, and in this specific case where you want a quad to align with the camera makes doing so very easy.
What you want is called a "billboard shader". Here is an example from Wikibooks:
Shader "Cg shader for billboards" {
Properties {
_MainTex ("Texture Image", 2D) = "white" {}
_ScaleX ("Scale X", Float) = 1.0
_ScaleY ("Scale Y", Float) = 1.0
}
SubShader {
Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// User-specified uniforms
uniform sampler2D _MainTex;
uniform float _ScaleX;
uniform float _ScaleY;
struct vertexInput {
float4 vertex : POSITION;
float4 tex : TEXCOORD0;
};
struct vertexOutput {
float4 pos : SV_POSITION;
float4 tex : TEXCOORD0;
};
vertexOutput vert(vertexInput input)
{
vertexOutput output;
output.pos = mul(UNITY_MATRIX_P,
mul(UNITY_MATRIX_MV, float4(0.0, 0.0, 0.0, 1.0))
+ float4(input.vertex.x, input.vertex.y, 0.0, 0.0)
* float4(_ScaleX, _ScaleY, 1.0, 1.0));
output.tex = input.tex;
return output;
}
float4 frag(vertexOutput input) : COLOR
{
return tex2D(_MainTex, float2(input.tex.xy));
}
ENDCG
}
}
}
And an explanation of how this works:
The basic idea is to transform only the origin ( 0 , 0 , 0 , 1 )
of the object
space to view space with the standard model-view transformation
UNITY_MATRIX_MV. (In homogeneous coordinates all points have a 1 as
fourth coordinate; see the discussion in Section “Vertex
Transformations”.) View space is just a rotated version of world space
with the xy plane parallel to
the view plane as discussed in Section “Vertex Transformations”. Thus,
this is the correct space to construct an appropriately rotated
billboard. We subtract the x y object coordinates (vertex.x and vertex.y) from the transformed
origin in view coordinates and then transform the result with the
projection matrix UNITY_MATRIX_P.
This will produce an output like this:
I have vertex that have a color value.
I'd like to make a mesh using vertex with the same color values.
This picture is an example.
I took pictures with my Android Phone, and I did image segmentation on the object
So I got a color value corresponding to the coordinate value.
I succeeded in just making textures. please check the image.
But I want a mesh object.
Below is making texture code.
var pixel = await this.segmentation.SegmentAsync(rotated, scaled.width, scaled.height);
// int pixel[][]; // image segmentation using tensorflow
Color transparentColor = new Color32(255, 255, 255, 0); // transparent
for (int y = 0; y < texture.height; y++)
{
for (int x = 0; x < texture.width; x++)
{
int class_output = pixel[y][x];
texture.SetPixel(x, y, pixel[y][x] == 0 ? transparentColor : colors[class_output]);
}
}
texture.Apply();
How can I make a mesh object?
1- Set a prefab with a MeshFilter and a MeshRenderer.
2- Variables inside the script that you will need to fill.
// This first list contains every vertex of the mesh that we are going to render
public List<Vector3> newVertices = new List<Vector3>();
// The triangles tell Unity how to build each section of the mesh joining
// the vertices
public List<int> newTriangles = new List<int>();
// The UV list is unimportant right now but it tells Unity how the texture is
// aligned on each polygon
public List<Vector2> newUV = new List<Vector2>();
// A mesh is made up of the vertices, triangles and UVs we are going to define,
// after we make them up we'll save them as this mesh
private Mesh mesh;
3- Initialize the mesh
void Start () {
mesh = GetComponent<MeshFilter> ().mesh;
float x = transform.position.x;
float y = transform.position.y;
float z = transform.position.z;
newVertices.Add( new Vector3 (x , y , z ));
newVertices.Add( new Vector3 (x + 1 , y , z ));
newVertices.Add( new Vector3 (x + 1 , y-1 , z ));
newVertices.Add( new Vector3 (x , y-1 , z ));
newTriangles.Add(0);
newTriangles.Add(1);
newTriangles.Add(3);
newTriangles.Add(1);
newTriangles.Add(2);
newTriangles.Add(3);
newUV.Add(new Vector2 (tUnit * tStone.x, tUnit * tStone.y + tUnit));
newUV.Add(new Vector2 (tUnit * tStone.x + tUnit, tUnit * tStone.y + tUnit));
newUV.Add(new Vector2 (tUnit * tStone.x + tUnit, tUnit * tStone.y));
newUV.Add(new Vector2 (tUnit * tStone.x, tUnit * tStone.y));
mesh.Clear ();
mesh.vertices = newVertices.ToArray();
mesh.triangles = newTriangles.ToArray();
mesh.uv = newUV.ToArray(); // add this line to the code here
mesh.Optimize ();
mesh.RecalculateNormals ();
}
This code will draw a square at the position of the prefab, if you keep adding vertices you can generate a more complex mesh.
The source of information is a tutorial to generate mensh for a terrain like minecrat, check the link for more information.
The answer which has been selected best is, in my opinion, faulty for four reasons. First, it is deprecated. Second, it is more complex than necessary. Third, it offers little explanation, and finally, it is mostly just a copy from someone else's blog post. For that reason, I offer a new suggestion. For more info, view the documentation here.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class meshmaker : MonoBehaviour {
Mesh mesh;
MeshFilter meshFilter;
Vector3[] newVertices;
int[] newTriangles;
// Use this for initialization
void Start () {
//First, we create an array of vector3's. Each vector3 will
//represent one vertex in our mesh. Our shape will be a half
//cube (probably the simplest 3D shape we can make.
newVertices = new Vector3[4];
newVertices [0] = new Vector3 (0, 0, 0);
newVertices [1] = new Vector3 (1, 0, 0);
newVertices [2] = new Vector3 (0, 1, 0);
newVertices [3] = new Vector3 (0, 0, 1);
//Next, we create an array of integers which will represent
//triangles. Triangles are built by taking integers in groups of
//three, with each integer representing a vertex from our array of
//vertices. Note that the integers are in a certain order. The order
//of integers determines the normal of the triangle. In this case,
//connecting 021 faces the triangle out, while 012 faces the
//triangle in.
newTriangles = new int[12];
newTriangles[0] = 0;
newTriangles[1] = 2;
newTriangles[2] = 1;
newTriangles[3] = 0;
newTriangles[4] = 1;
newTriangles[5] = 3;
newTriangles[6] = 0;
newTriangles[7] = 3;
newTriangles[8] = 2;
newTriangles[9] = 1;
newTriangles[10] = 2;
newTriangles[11] = 3;
//We instantiate our mesh object and attach it to our mesh filter
mesh = new Mesh ();
meshFilter = gameObject.GetComponent<MeshFilter> ();
meshFilter.mesh = mesh;
//We assign our vertices and triangles to the mesh.
mesh.vertices = newVertices;
mesh.triangles = newTriangles;
}
Ta da! Your very own half-cube.
I am building a Polygon Tool using Unity 5.5 and WebGL, which I would like to use to crop a selection from an image. The project is using a shader file to define the points and area which make up the polygon. You can see the code for the shader below, as well as the code file.
The problem I am currently experiencing is that the project works fine in the Editor, but the WebGL build shows up pink in the browser. The polygon shader doesn't render, if I set a Fallback shader in the file it loads that one. I've looked up the issue online and I found and tried the following:
Reimport all assets, as something may be missing. Check all game objects in the hierarchy for broken references.
Make sure the shaders are in the ‘Always Included Shaders’ list in Project Graphics Settings.
Potential memory leak? I increased the number of MB allocated to the project to 1024, lowered the size of the textures by compressing them and also ran the Profiler in the Editor. The Profiler recorded a max total use of ~220MB.
Toggled the Quality Settings - I discovered that the Shader does not work if Anti-Aliasing is not on.
Graphics card not supporting the shader? People online have reported similar problems on OS X (which I am also using), but they have not updated their queries.
I should note that there are no errors or warnings in the project. I am unsure where the problem lays and would truly appreciate any take on this issue. Please let me know if there is any further information that I can provide which could ease the process! Thank you in advance for your help :)
Polygon.cs file:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
[ExecuteInEditMode]
public class Polygon : MonoBehaviour {
public Material mat; //Binding materials; Polygon shader applied to it.
Vector3[] worldPos; //Store 3D coordinates
Vector4[] screenPos; //Store to draw the polygon vertices in screen coordinates
int maxPointNum = 6; //Total number of polygon vertices
int currentpointNum = 0; //Current number of points placed
int pointNum2Shader = 0; //Transfer number of vertices to shader
bool InSelection = true; //Vertex gets is in the process
void Start() {
worldPos = new Vector3[maxPointNum];
screenPos = new Vector4[maxPointNum];
}
void Update() {
mat.SetVectorArray("Value", screenPos); //Pass the vertex position information to the screen shader.
mat.SetInt("PointNum", pointNum2Shader); //Transfer number of vertices to shader
//Cameras fired a ray to get to select a 3D location
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, 100)) {
Debug.DrawLine(ray.origin, hit.point);
}
//Use mouse clicks to obtain location information
if (Input.GetMouseButtonDown(0) && InSelection) {
if (currentpointNum < maxPointNum) {
currentpointNum++;
pointNum2Shader++;
worldPos[currentpointNum - 1] = hit.point;
//Vector3 v3 = Camera.main.ScreenToWorldPoint (worldPos [currentpointNum-1]);
Vector3 v3 = Camera.main.WorldToScreenPoint(worldPos[currentpointNum - 1]);
screenPos[currentpointNum - 1] = new Vector4(v3.x, v3.y, v3.z, 0);
}
else {
InSelection = false;
}
}
//Updated in real time screens of 3D point selected location
for (int i = 0; i < maxPointNum; i++) {
Vector3 v3 = Camera.main.WorldToScreenPoint(worldPos[i]);
screenPos[i] = new Vector4(v3.x, v3.y, v3.z, 0);
}
//Detect if 3d point is behind the camera, if you stop drawing
for (int i = 0; i < currentpointNum; i++) {
if (Vector3.Dot(worldPos[i] - Camera.main.transform.position, Camera.main.transform.forward) <= 0) {
pointNum2Shader = 0;
break;
}
pointNum2Shader = currentpointNum;
}
}
//Grab the current rendering of image processing
void OnRenderImage(RenderTexture src, RenderTexture dest) {
Graphics.Blit(src, dest, mat);
}
void OnGUI () {
float btnWidth = 100;
float btnHeight = 50;
float y = 150;
if (GUI.Button(new Rect(100, y, btnWidth, btnHeight), "Crop")) {
Vector2[] vertices2D = new Vector2[maxPointNum];
for (int i = 0; i < currentpointNum; i++) {
vertices2D[i].x = worldPos[i].x;
vertices2D[i].y = worldPos[i].y;
}
// Use the triangulator to get indices for creating triangles
Triangulator tr = new Triangulator(vertices2D);
int[] indices = tr.Triangulate();
GameObject plane = GameObject.Find("Plane");
MeshFilter filter = plane.GetComponent<MeshFilter>();
Vector3[] vertices = filter.mesh.vertices;
// Vector2[] texCoords = new Vector2[currentpointNum];
// for(int i = 0; i < currentpointNum; i++) {
// //There should be as many texture coordinates as vertices.
// //This example does not support textures, so fill with zeros
// texCoords[i] = new Vector2(vertices[i].x, vertices[i].y);
// }
// Create the mesh
Mesh msh = new Mesh();
msh.vertices = worldPos;
msh.triangles = indices;
msh.RecalculateNormals();
msh.RecalculateBounds();
filter.mesh = msh;
}
}
}
Polygon.shader file:
Shader "Unlit/polygon"
{
Properties
{
//Define basic properties can be set from inside the editor variable
//_MainTex ("Texture", 2D) = "white" {}
}
CGINCLUDE
// Upgrade NOTE: excluded shader from DX11 because it uses wrong array syntax (type[size] name)
#pragma exclude_renderers d3d11
//Incoming vertices function from the application data structure definitions
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
//Incoming segment from a vertex function from the data structure definitions
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
//Define mapping variables
sampler2D _MainTex;
// float4 _MainTex_ST;
//Define variables for communicating with the script
uniform float4 Value[6];
int PointNum = 0;
//Function that calculates the distance between two points
float Dis(float4 v1,float4 v2)
{
return sqrt(pow((v1.x-v2.x),2)+pow((v1.y-v2.y),2));
}
//Draw line segments
bool DrawLineSegment(float4 p1, float4 p2, float lineWidth,v2f i)
{
float4 center = float4((p1.x+p2.x)/2,(p1.y+p2.y)/2,0,0);
//Calculate distance between point and line
float d = abs((p2.y-p1.y)*i.vertex.x + (p1.x - p2.x)*i.vertex.y +p2.x*p1.y -p2.y*p1.x )/sqrt(pow(p2.y-p1.y,2) + pow(p1.x-p2.x,2));
//When less than or equal to half the line width, which belongs to the linear range, return true
float lineLength = sqrt(pow(p1.x-p2.x,2)+pow(p1.y-p2.y,2));
if(d<=lineWidth/2 && Dis(i.vertex,center)<lineLength/2)
{
return true;
}
return false;
}
//To draw a polygon, this limits the number of vertices is not more than 6. You can change.
bool pnpoly(int nvert, float4 vert[6], float testx, float testy)
{
int i, j;
bool c=false;
float vertx[6];
float verty[6];
for(int n=0;n<nvert;n++)
{
vertx[n] = vert[n].x;
verty[n] = vert[n].y;
}
for (i = 0, j = nvert-1; i < nvert; j = i++) {
if ( ((verty[i]>testy) != (verty[j]>testy)) && (testx < (vertx[j]-vertx[i]) * (testy-verty[i]) / (verty[j]-verty[i]) + vertx[i]) )
c = !c;
}
return c;
}
v2f vert (appdata v)
{
v2f o;
//Object vertices from model space to the camera cut space, or you can use the shorthand way:
//o.vertex = UnityObjectToClipPos(v.vertex);
o.vertex = mul(UNITY_MATRIX_MVP,v.vertex);
//2D UV coordinate transformation can also use shorthand methods
//o.uv = TRANSFORM_TEX(v.uv, _MainTex);
//o.uv = v.uv.xy * _MainTex_ST.xy + _MainTex_ST.zw;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
//Draw a polygon vertex
for(int j=0;j<PointNum;j++)
{
if(Dis(i.vertex, Value[j])<3)
{
return fixed4(1,0,0,0.5);
}
}
//Draws the edges of the polygon
for(int k=0;k<PointNum;k++)
{
if(k==PointNum-1)
{
if(DrawLineSegment(Value[k],Value[0],2,i))
{
return fixed4(1,1,0,0.5);
}
}
else
{
if(DrawLineSegment(Value[k],Value[k+1],2,i))
{
return fixed4(1,1,0,0.5);
}
}
}
//Within the filled polygon
if(pnpoly(PointNum, Value,i.vertex.x ,i.vertex.y))
{
return fixed4(0,1,0,0.3);
}
return fixed4(0,0,0,0);
//fixed4 col = tex2D(_MainTex, i.uv);
//return col;
}
ENDCG
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
//Select Alpha blend mode
Blend SrcAlpha OneMinusSrcAlpha
//In the CGPROGRAM block of code to write your own processes
CGPROGRAM
//Defined segment function entry and vertex function respectively, Vert and Frag
#pragma vertex vert
#pragma fragment frag
//Contains the basic files, there are some macro definitions and basic functions
#include "UnityCG.cginc"
ENDCG
}
}
}
Pink appearance means your build has missing material assignments on the mesh renderer component or missing shader in the build.
If you are assigning materials or shaders at runtime, make sure you include the shader in the build.
You can force unity to include any shader by adding it to the list in Edit/Project Settings/Graphics
I am attempting to generate a mesh in unity from script. The mesh is generated by raycasting out in a particular direction. Then getting the vertices from the hit point or from where the ray terminates. The mesh generates fine and is working well, however the mesh generates approximately 5 to 10 unity units above the location of the object with the attached script. I will attach me script below.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class Torch : MonoBehaviour {
public GameObject lightmeshholder;
private int RaysToShoot = 128;
private float distance = 50;
private Vector3[] vertices;
private Vector2[] vertices2d;
private int[] triangles;
private Mesh mesh;
private Texture2D texture;
private int screenwidth;
private int screenheight;
private int grab = 0;
private RaycastHit hit;
// Use this for initialization
void Start () {
screenwidth = Screen.width;
screenheight = Screen.height;
texture = new Texture2D (screenwidth, screenheight, TextureFormat.RGB24, false);
vertices = new Vector3[RaysToShoot];
vertices2d = new Vector2[RaysToShoot];
triangles = new int[(RaysToShoot) +1 ];
mesh= lightmeshholder.GetComponent<MeshFilter>().mesh;
}
// Update is called once per frame
void Update () {
float angle =0;
for(int i=0;i<RaysToShoot;i++){
float x = Mathf.Sin(0);
x=-5;
if(Input.GetKey(KeyCode.P)){
x = 5;
}
float y = Mathf.Cos(angle);
if (angle <= 90){
angle += 2*Mathf.PI/RaysToShoot;
}
Vector3 dir = new Vector3(x,y,0);
if (Physics.Raycast (this.transform.position, dir,out hit, distance))
{
Debug.DrawLine (this.transform.position, hit.point,new Color(1,1,0,1));
Vector3 tmp = lightmeshholder.transform.InverseTransformPoint(hit.point);
vertices2d[i] = new Vector2(tmp.x,tmp.y);
}else{
Vector3 tmp = lightmeshholder.transform.InverseTransformPoint(this.transform.position + dir*distance);
vertices2d[i] = new Vector2(tmp.x,tmp.y);
Debug.DrawLine(this.transform.position,dir * distance,Color.red,0);
}
}
// build mesh
Vector2[] uvs = new Vector2[vertices2d.Length +1];
Vector3[] newvertices = new Vector3[vertices2d.Length+1];
for (int n = 0; n<newvertices.Length-1 ;n++)
{
if(n==0){
newvertices[0]=this.transform.position;
newvertices[1] = vertices2d[0];
uvs[0] = new Vector2(this.transform.position.x,this.transform.position.y);
uvs[1] = vertices2d[0];
}else{
newvertices[n+1] = vertices2d[n];
uvs[n+1] = vertices2d[n];
}
if(n==0){
triangles[0] = 0;
triangles[1] = 1;
triangles[2] = 2;
}else if(n<newvertices.Length/3){
triangles[n*3] = 0;
triangles[1+n*3] = n+1;
triangles[2+n*3] = n+2;
}
}
Mesh mesh = new Mesh();
GetComponent<MeshFilter>().mesh = mesh;
mesh.Clear();
mesh.vertices = newvertices;
mesh.uv = uvs;
mesh.triangles = triangles;
mesh.RecalculateNormals();
}
}
The hitpoints you're getting from the raycasts are global. You can either:
1) Make sure the mesh is in a gameobject that is positioned at 0,0,0. Whenever I create a dynamic mesh based on ray hitpoints I usually create a new parent-less gameobject at 0,0,0 and add the mesh to it. (instead of getting a container GameObject externally)
2) Call lightmeshholder.transform.InverseTransformPoint on the hitpoints from the raycast before building the mesh
When you are assigning vertices back to mesh they are calculated in local coordinates with respect to object root. So if you want to bring it back "where it should be" process them with .TransformPoint() call to get proper offsets. This should fix your problem with them being in different location than you expect. I had same problem.