I am trying to implement a fluid surface simulation using the paper Fast Hydraulic Erosion Simulation and Visualization on GPU as a template to simulate water. However, I get artifacts like this:
The code for my update function is below, I have followed another post on this subject, but the change did not help.
private void updateHeight (float dt, float dx){
float dhL, dhR, dhF, dhB, DV;
float totalFlux;
float updateConstant;
float fluxConstant = dt*GRAVITY/(2*VISCOUSCOEFFICEINT*dx);
//Update Flux
for (int y=1; y<=N-1; y++){
for(int x=1;x<=N-1;x++){
dhL = this.height[x][y] - this.height[x-1][y];
dhR = this.height[x][y] - this.height[x+1][y];
dhF = this.height[x][y] - this.height[x][y+1];
dhB = this.height[x][y] - this.height[x][y-1];
if (Mathf.Abs(dhL) < 0.0001f){
dhL=0.0f;
}
if (Mathf.Abs(dhR) < 0.0001f){
dhR=0;
}
if (Mathf.Abs(dhF) < 0.0001f){
dhF=0;
}
if (Mathf.Abs(dhB) < 0.0001f){
dhB=0;
}
this.tempFluxArray[x][y].fluxL = Mathf.Max(0.0f, this.fluxArray[x][y].fluxL + fluxConstant*dhL);
this.tempFluxArray[x][y].fluxR = Mathf.Max(0.0f, this.fluxArray[x][y].fluxR + fluxConstant*dhR);
this.tempFluxArray[x][y].fluxF = Mathf.Max(0.0f, this.fluxArray[x][y].fluxF + fluxConstant*dhF);
this.tempFluxArray[x][y].fluxB = Mathf.Max(0.0f, this.fluxArray[x][y].fluxB + fluxConstant*dhB);
totalFlux = this.tempFluxArray[x][y].fluxL + this.tempFluxArray[x][y].fluxR + this.tempFluxArray[x][y].fluxF + this.tempFluxArray[x][y].fluxB;
if(totalFlux > 0){
updateConstant = Mathf.Min(1.0f, this.height[x][y]* dx*dx/(totalFlux * dt));
this.tempFluxArray[x][y].fluxL = updateConstant * this.tempFluxArray[x][y].fluxL;
this.tempFluxArray[x][y].fluxR = updateConstant * this.tempFluxArray[x][y].fluxR;
this.tempFluxArray[x][y].fluxF = updateConstant * this.tempFluxArray[x][y].fluxF;
this.tempFluxArray[x][y].fluxB = updateConstant * this.tempFluxArray[x][y].fluxB;
}
}
}
swap();
//Height Calculation
for (int y=1; y<=N-1; y++){
for(int x=1;x<=N-1;x++){
DV = dt*(this.fluxArray[x-1][y].fluxR + this.fluxArray[x][y-1].fluxF + this.fluxArray[x+1][y].fluxL + this.fluxArray[x][y+1].fluxB - this.fluxArray[x][y].fluxL - this.fluxArray[x][y].fluxR - this.fluxArray[x][y].fluxF - this.fluxArray[x][y].fluxB);
this.height[x][y] = this.height[x][y] + DV/(dx*dx);
if(this.height[x][y] < 1){
// Debug.Log(x);
// Debug.Log(y);
}
}
}
}
Could it be due to using this rather than ref? The way I interact with the water surface written below.
private void waterdrop(int x, int y){
float sqrD = 0.8f*0.8f;
for (int j = 1; j < N; j++) {
for (int i = 1; i < N; i++) {
float sqrDToVert = (float)0.2f*(i - x)*(i-x) + 0.2f*(j - y)*(j-y);
if (sqrDToVert <= sqrD){
float distanceCompensator = 1 - (sqrDToVert/sqrD);
this.fluxArray[i][j].fluxL = this.fluxArray[i][j].fluxL + (0.02f * distanceCompensator);
this.fluxArray[i][j].fluxR = this.fluxArray[i][j].fluxR + (0.02f * distanceCompensator);
this.fluxArray[i][j].fluxF = this.fluxArray[i][j].fluxF + (0.02f * distanceCompensator);
this.fluxArray[i][j].fluxB = this.fluxArray[i][j].fluxB + (0.02f * distanceCompensator);
//this.height[i][j] = this.height[i][j] - 1.0f * distanceCompensator;
Debug.Log(this.height[i][j]);
}
//Debug.Log("x = "+i+"\n y = "+j+" height is "+this.height[i][j]);
}
}
}
Namely just changing the flux at some point at all direction.
Some solutions I have tried was to change the height but that didn't work or clamp on small changes only but it only built up. The boundary conditions are zero flux on all boarders and the height would be the same as the closest point.
Related
I am implementing Yolov4-Tiny (onnx model found here) in Unity with the Windows ML APIs. I can load the model, and begin a session with no issue. I am using a VideoFrame (sized to 416x416) as the input and can access the two output Tensors. The problems arise when I begin to parse the output Tensors. With a confidence threshold of .5, I get between 700 and 1000 detections each frame, way way more than I expect. Also, the bboxes appear to be very small. The NMS and IOU functions below are nearly verbatim from here so I am not using anchors for the bounding boxes. I believe my issues is in the NMS and IOU functions, but I cannot locate the problem. My gut tells me I am manipulating the output Tensors incorrectly. Any ideas?
private List<DetectionResult> ParseResult(float[] boxes, float[] classes)
{
int c_values = 80;
int c_boxes = boxes.Length / 4;
int c_classNames = classes.Length / c_values;
float confidence_threshold = 0.5f;
List<DetectionResult> detections = new List<DetectionResult>();
for (int i_box = 0; i_box < c_classNames; i_box++)
{
float max_prob = 0.0f;
int label_index = -1;
for (int j_confidence = 0; j_confidence < c_values; j_confidence++)
{
int index = i_box * c_values + j_confidence;
if (Sigmoid(classes[index]) > max_prob)
{
max_prob = Sigmoid(classes[index]) ;
label_index = j_confidence;
}
}
if (max_prob > confidence_threshold)
{
//Debug.Log(_labels[label_index]);
List<float> bbox = new List<float>();
bbox.Add(boxes[(i_box * 4) + 0] * 416);
bbox.Add(boxes[(i_box * 4) + 1] * 416);
bbox.Add(boxes[(i_box * 4) + 2] * 416);
bbox.Add(boxes[(i_box * 4) + 3] * 416);
detections.Add(new DetectionResult()
{
label = _labels[label_index],
bbox = bbox,
prob = max_prob
});
}
}
private List<DetectionResult> NMS(IReadOnlyList<DetectionResult> detections,
float IOU_threshold = 0.45f,
float score_threshold = 0.3f)
{
List<DetectionResult> final_detections = new List<DetectionResult>();
for (int i = 0; i < detections.Count; i++)
{
int j = 0;
for (j = 0; j < final_detections.Count; j++)
{
if (ComputeIOU(final_detections[j], detections[i]) > IOU_threshold)
{
break;
}
}
if (j == final_detections.Count)
{
final_detections.Add(detections[i]);
}
}
return final_detections;
}
private float ComputeIOU(DetectionResult DRa, DetectionResult DRb)
{
float ay1 = DRa.bbox[0];
float ax1 = DRa.bbox[1];
float ay2 = DRa.bbox[2];
float ax2 = DRa.bbox[3];
float by1 = DRb.bbox[0];
float bx1 = DRb.bbox[1];
float by2 = DRb.bbox[2];
float bx2 = DRb.bbox[3];
// determine the coordinates of the intersection rectangle
float x_left = Math.Max(ax1, bx1);
float y_top = Math.Max(ay1, by1);
float x_right = Math.Min(ax2, bx2);
float y_bottom = Math.Min(ay2, by2);
if (x_right < x_left || y_bottom < y_top)
return 0;
float intersection_area = (x_right - x_left) * (y_bottom - y_top);
float bb1_area = (ax2 - ax1) * (ay2 - ay1);
float bb2_area = (bx2 - bx1) * (by2 - by1);
float iou = intersection_area / (bb1_area + bb2_area - intersection_area);
return iou;
}
I have problem with normalized values when creating procedural grid in Unity. I have been following great tutorial from catlikecoding and I dumped in to weird behaving when I tried to use normalized values for my vertices. In some cases of xSize and ySize grid combinations all works, but in other combinations mesh get deformed. Let me give you couple of examples
xSize = 35; ySize = 25; // OK
xSize = 350; ySize = 250; // NOT OK
xSize = 150; ySize = 250; // OK
xSize = 350; ySize = 200; // NOT OK
xSize = 1000; ySize = 750; // NOT OK
First 2 cases I illustrated with sphere representing each 10th vertices.
35x25 case
350x250 case
I am using Unity3d 2018.3
private void Generate()
{
GetComponent<MeshFilter>().mesh = mesh = new Mesh();
mesh.name = "Procedural Grid";
vertices = new Vector3[(xSize + 1) * (ySize + 1)];
Vector2[] uv = new Vector2[vertices.Length];
float multX = 1 / (float)xSize;
float multY = 1 / (float)ySize;
for (int i = 0, y = 0; y <= ySize; y++)
{
for (int x = 0; x <= xSize; x++, i++)
{
//vertices[i] = new Vector3(x, y);
var xNormalized = x * multX;
var yNormalized = y * multY;
vertices[i] = new Vector3(xNormalized, yNormalized);
uv[i] = new Vector2(xNormalized, yNormalized);
}
}
mesh.vertices = vertices;
mesh.uv = uv;
var triangles = new int[xSize * ySize * 6];
for (int ti = 0, vi = 0, y = 0; y < ySize; y++, vi++)
{
for (int x = 0; x < xSize; x++, ti += 6, vi++)
{
triangles[ti] = vi;
triangles[ti + 3] = triangles[ti + 2] = vi + 1;
triangles[ti + 4] = triangles[ti + 1] = vi + xSize + 1;
triangles[ti + 5] = vi + xSize + 2;
}
}
mesh.triangles = triangles;
mesh.RecalculateNormals();
}
I expect the mesh be 1x1 in every case, no matter which xSize or ySize of the grid I use. Anybody can advise how to achieve that?
So my friend explained me, that by default meshes have a 65535 vertices limit in Unity. And I have to nicely ask if I want more.
I had to add
mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
after
mesh.name = "Procedural Grid";
here is more..
Suddenly all works as expected. Thank you all for support.
This program is coded in c#. It is supposed to display a 3d Graph of the function Log(x,y). I don't know why but everytime I run it I get the System.OverflowException when it begins to draw the graph and the program stops.
How can I prevent it from happening and why does it happen?
private void Draw_Function_Click(object sender, EventArgs e)
{
int size = 100;
double accuracy = 0.09;
int zoom = 1;
ver = new double[size, size];
xtag = new double[size, size];
ytag = new double[size, size];
function = Insert_Function.Text;
for (int i = 0; i < 100; i++)
{
for (int p = 0; p < 100; p++)
{
ver[i, p] = Math.Log(i,p);
xtag[i, p] = p * accuracy - i * accuracy * Math.Cos(Math.PI / 5);
ytag[i, p] = ver[i, p] - i * accuracy * Math.Sin(Math.PI / 5);
}
}
Graphics g = panel1.CreateGraphics();
for (int i = 0; i < ver.GetLength(0) - 1; i++)
for (int p = 1; p < ver.GetLength(1) - 1; p++)
{
int y0 = (panel1.Height / 2) - (int)(ytag[i, p] * zoom);
int x0 = (int)(zoom * xtag[i, p]) + panel1.Width / 2;
int y1 = (panel1.Height / 2) - (int)(ytag[i + 1, p + 1] * zoom);
int x1 = (int)(zoom * xtag[i + 1, p + 1]) + panel1.Width / 2;
g.DrawLine(Pens.Black,
(float)x0,
(float)y0,
(float)x1,
(float)y1);
}
}
I found the following answer: https://social.msdn.microsoft.com/Forums/windows/en-US/9f2b5bba-f725-45c1-9ada-383151267c13/overflow-exception-on-drawline-method-?forum=winforms
Quote: "Hi, the minimum value allowed for screen coordinate is x = -1073741376 and y = -1073740288, this is the boundary where the desktop surface lies, if you go further, you enter the void, therfore cause an overflow..."
I ran your calculation and besides the fact that it produces NaN and -Infinity values, it also produced very small values like -2147483503. This is the reason why you get the overflow exception.
I'm coding an runtime terrain editor for unity and got stuck.
At first I wanted just to paint with textrue the terrain. I found this code and it worked fine:
SCRIPT: TerrainPainter
void Paint(Vector3 point)
{
mapX = (int)(((point.x - terrainPosition.x) / terrainData.size.x) * heightmapWidth);
mapY = (int)(((point.z - terrainPosition.z) / terrainData.size.z) * heigtmapHeight);
splatmapData[mapY, mapX, 0] = element[0, 0, 0] = 0;
splatmapData[mapY, mapX, 1] = element[0, 0, 1] = 1;
terrain.terrainData.SetAlphamaps(mapX, mapY, element);
}
But now I want to paint with different sizes/thickness. I have another script, named Terrainmodifier, which I use to raise and lower the terrain. There I have this lines for raising:
SCRIPT: Terrainmodifier
public void RaiseTerrain(Terrain terrain, Vector3 location, float effectIncrement)
{
int offset = areaOfEffectSize / 2;
//--1--
Vector3 tempCoord = (location - terrain.GetPosition());
Vector3 coord;
coord = new Vector3(
(tempCoord.x / GetTerrainSize().x),
(tempCoord.y / GetTerrainSize().y),
(tempCoord.z / GetTerrainSize().z)
);
Vector3 locationInTerrain = new Vector3(coord.x * terrainHeightMapWidth, 0, coord.z * terrainHeightMapHeight);
// End --1--
// --2--
int terX = (int)locationInTerrain.x - offset;
int terZ = (int)locationInTerrain.z - offset;
// End --2--
// --3--
float[,] heights = targetTerrainData.GetHeights(terX, terZ, areaOfEffectSize, areaOfEffectSize);
for (int xx = 0; xx < areaOfEffectSize; xx++)
{
for (int yy = 0; yy < areaOfEffectSize; yy++)
{
heights[xx, yy] += (effectIncrement * Time.smoothDeltaTime);
}
}
targetTerrainData.SetHeights(terX, terZ, heights);
}
So I thought I could use this as an aid and transfer it. So I took GetAlphamaps() instead of GetHeights() and added the variable areaOfEffectSize.
SCRIPT: TerrainPainter
void Paint(Vector3 point)
{
// --1--
mapX = (int)(((point.x - terrainPosition.x) / terrainData.size.x) * heightmapWidth);
mapY = (int)(((point.z - terrainPosition.z) / terrainData.size.z) * heigtmapHeight);
// End --1--
// --2--
int terX = (int)mapX - (areaOfEffectSize / 2);
int terY = (int)mapY - (areaOfEffectSize / 2);
// End --2--
// --3--
splatmapData = terrainData.GetAlphamaps(terX, terY, areaOfEffectSize, areaOfEffectSize);
for(int xx = 0; xx < areaOfEffectSize; xx++)
{
for (int yy = 0; yy < areaOfEffectSize; yy++)
{
splatmapData[yy, xx, 1] = element[0, 0, 1] = 1;
}
}
terrain.terrainData.SetAlphamaps(terX, terY, element);
}
Hope sb can help me find my mistake. How can I change the size of my "brush"?
EDIT: I wrote comments into the code to see the transfered/related lines.
Oh guys, I did a stupid mistake. Solved this problem by passing the splatmapData into SetAlphamaps -.-
So the solution is:
[..]
for (int xx = 0; xx < areaOfEffectSize; xx++)
{
for (int yy = 0; yy < areaOfEffectSize; yy++)
{
splatmapData[yy, xx, 0] = 0;
splatmapData[yy, xx, 1] = 1;
}
}
terrain.terrainData.SetAlphamaps(terX, terY, splatmapData);
I am working on Hilbert curve, and I cant rotate the whole figure, just lower rectangle(look at the screenshots, on third and next steps I have a problem)
first 3 steps
I have a Figure class. I use it to store every figure and build next firuge from previous one. Here is my code
public class Fragment
{
public static int PADDING = 50;
public static float sideLength;
private readonly Pen crimsonPen = new Pen(Color.Crimson);
public List<PointF> pointsF = new List<PointF>();
public PointF[] points;
public Fragment(int step, Graphics graphics)
{
sideLength = Form1.sideLenght;
points = new PointF[(int)Math.Pow(4, step + 1)];
if (step.Equals(0))
{
points[0] = new PointF(PADDING, PADDING + sideLength);
points[1] = new PointF(PADDING, PADDING);
points[2] = new PointF(PADDING + sideLength, PADDING);
points[3] = new PointF(PADDING + sideLength, PADDING + sideLength);
graphics.DrawLines(crimsonPen, new[] { points[0], points[1], points[2], points[3] });
}
else
{
var frag = Form1.fragments[step - 1];
for (var i = 0; i < step; i++)
{
PointF tmpPoint;
// left lower #1
for (int j = frag.points.Length - 1; j >= 0; j--)
{
points[frag.points.Length - 1 - j] = frag.points[j];
points[frag.points.Length - 1 - j].Y += sideLength * 2 * (i + 1);
}
//rotate left lower #1
for (int b = 0; b < Math.Pow(4, step) - 1; b++)
{
tmpPoint = points[0];
for (int j = 0; j < frag.points.Length; j++)
{
if (j.Equals(frag.points.Length - 1))
{
points[j] = tmpPoint;
}
else
{
points[j] = points[j + 1];
}
}
}
// left upper #2
for (int j = 0; j < frag.points.Length; j++)
{
points[j + frag.points.Length] = frag.points[j];
}
// right upper #3
for (int j = 0; j < frag.points.Length; j++)
{
points[j + 2 * frag.points.Length] = points[j + frag.points.Length];
points[j + 2 * frag.points.Length].X += sideLength * 2 * (i + 1);
}
//right lower #4
for (int j = frag.points.Length - 1; j >= 0; j--)
{
points[3 * frag.points.Length + j] = points[2 * frag.points.Length + frag.points.Length - j - 1];
points[3 * frag.points.Length + j].Y += sideLength * 2 * (i + 1);
}
tmpPoint = points[3 * frag.points.Length];
//rotate right lower #4
for (int j = 0; j < frag.points.Length; j++)
{
if (j.Equals(frag.points.Length - 1))
{
points[4 * (frag.points.Length) - 1] = tmpPoint;
}
else
{
points[3 * frag.points.Length + j] = points[3 * frag.points.Length + j + 1];
}
}
}
graphics.DrawLines(crimsonPen, points);
}
}
}
Here I use my recursive method to draw figures
private void drawButton_Click(object sender, EventArgs e)
{
canvas.Refresh();
count = 0;
if (Int32.TryParse(stepsTextBox.Text, out steps))
{
sideLenght = (float)((canvas.Width - 100) / (Math.Pow(2, steps) - 1));
fragments = new Fragment[steps];
drawCurve();
}
else
{
MessageBox.Show("Wow, incorrect input", "Try again");
}
}
private void drawCurve()
{
if (count < steps)
{
fragments[count] = new Fragment(count, graphics);
++count;
drawCurve();
}
}
I've tried to rotate points around figure center and use next code but the rotation is incorrect
public PointF rotatePoint(PointF pointToRotate)
{
pointToRotate.X = (float)(Math.Cos(180 * Math.PI / 180) * (pointToRotate.X - centerPoint.X) -
Math.Sin(180 * Math.PI / 180) * (pointToRotate.Y - centerPoint.Y) +
centerPoint.X);
pointToRotate.Y = (float)(Math.Sin(0 * Math.PI / 180) * (pointToRotate.X - centerPoint.X) +
Math.Cos(0 * Math.PI / 180) * (pointToRotate.Y - centerPoint.Y) +
centerPoint.Y);
return pointToRotate;
}
The problem is that you are using the X co-ordinate that you have already rotated when you calculate the rotated Y co-ordinate. Use a temp variable to avoid this:
public PointF rotatePoint(PointF pointToRotate)
{
float rotatedX = (float)(Math.Cos(180 * Math.PI / 180) * (pointToRotate.X - centerPoint.X) -
Math.Sin(180 * Math.PI / 180) * (pointToRotate.Y - centerPoint.Y) +
centerPoint.X);
pointToRotate.Y = (float)(Math.Sin(0 * Math.PI / 180) * (pointToRotate.X - centerPoint.X) +
Math.Cos(0 * Math.PI / 180) * (pointToRotate.Y - centerPoint.Y) +
centerPoint.Y);
pointToRotate.X = rotatedX;
return pointToRotate;
}