I am applying a 3D Voronoi pattern on a mesh. Using those loops, I am able to compute the cell position, an id and the distance.
But I would like to compute a normal based on the generated pattern.
How can I generate a normal or reorient the current normal based on this pattern and associated cells ?
The aim is to provide a faced look for the mesh. Each cell's normal should point in the same direction and adjacent cells point in different directions. Those directions should be based on the original mesh normals, I don't want to totally break mesh normals and have those points in random directions.
Here's how I generate the Voronoi pattern.
float3 p = floor(position);
float3 f = frac(position);
float id = 0.0;
float distance = 10.0;
for (int k = -1; k <= 1; k++)
{
for (int j = -1; j <= 1; j++)
{
for (int i = -1; i <= 1; i++)
{
float3 cell = float3(float(i), float(j), float(k));
float3 random = hash3(p + cell);
float3 r = cell - f + random * angleOffset;
float d = dot(r, r);
if (d < distance)
{
id = random;
distance = d;
cellPosition = cell + p;
normal = ?
}
}
}
}
And here's the hash function :
float3 hash3(float3 x)
{
x = float3(dot(x, float3(127.1, 311.7, 74.7)),
dot(x, float3(269.5, 183.3, 246.1)),
dot(x, float3(113.5, 271.9, 124.6)));
return frac(sin(x)*43758.5453123);
}
This looks like a fairly expensive fragment shader, it might more sense to bake out a normal map than to try to do this in real time.
It's hard to tell what your shader is doing but I think it's checking every pixel against a 3x3 grid of voronoi cells. One weird thing is that random is a vec3 that somehow gets assigned to id, which is just a scalar.
Anyway, it sounds like you would like to perturb the mesh-supplied normal by a random vector, but you'd like all pixels corresponding to a particular voronoi cell to be perturbed in the same way.
Since you already have a variable called random which presumably represents a random value generated deterministically as a function of the voronoi cell, you could just use that. For example, the following would perturb the normal by a small amount:
normal = normalize(meshNormal + 0.2 * normalize(random));
If you want to give more weight to the random component, just increase the 0.2 constant.
Related
I have been generating noise textures to use as height maps for terrain generation. In this application, initially there is a 256x256 noise texture that is used to create a block of land that the user is free to roam around. When the user reaches a certain boundary in-game the application generates a new texture and thus another block of terrain.
In the code, a table of 64x64 random values are generated, and the values in the texture are the result of interpolating between these points at various 'frequencies' and 'wavelengths' using a smoothstep function, and then combined to form the final noise texture; and finally the values in the texture are divided through by its largest value to effectively normalize it. When the player is at the boundary and a new texture is created, the random number table that is created re-uses the values from the appropriate edge of the previous texture (eg. if the new texture is for a block of land that is on the +X side of the previous one, the last value in every row of the previous texture is used as the first value in every row of random numbers in the next.)
My problem is this: even though the same values are being used across the edges of adjacent textures, they are nowhere near seamless - some neighboring points on the terrain are mismatched by many many metres. My guess is that the changing frequencies that are used to sample the random number table are probably having a significant effect on all areas of the texture. So how might one generate fractal noise poceduraly, ie. as needed, AND have it look continuous with adjacent values?
Here is a section of the code that returns a value interpolated between the points on the random number table given a point P:
float MainApp::assessVal(glm::vec2 P){
//Integer component of P
int xi = (int)P.x;
int yi = (int)P.y;
//Decimal component ofP
float xr = P.x - xi;
float yr = P.y - yi;
//Find the grid square P lies inside of
int x0 = xi % randX;
int x1 = (xi + 1) % randX;
int y0 = yi % randY;
int y1 = (yi + 1) % randY;
//Get random values for the 4 nodes
float r00 = randNodes->randNodes[y0][x0];
float r10 = randNodes->randNodes[y0][x1];
float r01 = randNodes->randNodes[y1][x0];
float r11 = randNodes->randNodes[y1][x1];
//Smoother interpolation so
//texture appears less blocky
float sx = smoothstep(xr);
float sy = smoothstep(yr);
//Find the weighted value of the 4
//random values. This will be the
//final value in the noise texture
float sx0 = mix(r00, r10, sx);
float sx1 = mix(r01, r11, sx);
return mix(sx0, sx1, sy);
}
Where randNodes is a 2 dimensional array containing the random values.
And here is the code that takes all the values returned from the above function and constructs texture data:
int layers = 5;
float wavelength = 1, frequency = 1;
for (int k = 0; k < layers; k++) {
for (int i = 0; i < stepsY; i++) {
for(int j = 0; j < stepsX; j++){
//Compute value for (stepsX * stepsY) interpolation points
//across the grid of random numbers
glm::vec2 P = glm::vec2((float)j/stepsX * randX, (float)i/stepsY * randY);
buf[i * stepsY + j] += assessVal(P * wavelength) * frequency;
}
}
//repeat (layers) times with different signals
wavelength *= 0.5;
frequency *= 2;
}
for(int i = 0; i < buf.size(); i++){
//divide all data by the largest value.
//this normalises the data to avoid saturation
buf[i] /= largestVal;
}
Finally, here is an example of two textures generated by these functions that should be seamless, but aren't:
The 2 images placed side by side as they are now are obviously mis-matched.
Your code wraps the values only in the domain of the noise texture you read from, but not in the domain of the texture being generated.
For the texture T of size stepX to be repeatable (let's consider 1-d case for simplicity) you must have
T(0) == T(stepX)
Or in your case (substitute j = 0 and j = stepX):
assessVal(0) == assessVal(randX * wavelength)
For when k >= 1 this is clearly not true in your code, because
(randX / pow(2, k)) % randX != 0
One solution is to decrease randX and randY while you go up the frequencies.
But my typical approach would rather be starting from a 2x2 random texture, upscale it to 4x4 with GL_REPEAT, add a bit more per-pixel noise, continue upscaling to 8x8 etc.. till I get to the desired size.
The root cause of course is that your smoothing changes pixels to match their neighbors, but you later add new neighbors and do not re-smooth the pixels who got new neighbors.
One simple and common workaround is to keep an edge of invisible pixels, the width of which is half that of your smoothing kernel. Now, when expanding the area, you can resmooth those invisible pixels just before they're revealed. Don't forget to add a new edge of invisible pixels!
I have a mesh shown in the picture below. Each segment of the mesh has a positive (np) and negative normal (nn). Each pair of normals belongs to a segment from the mesh.
The red dot in the middle is my observation point. I need to find out which normals are visible to this observation point.
The method I have followed so far is to to calculate a normal vector from the observation point to the middle of the each mesh segment (ob_i). I then do the dot product on the ob_i and np or nn. The result of this is either greater than zero or less than. If it is less than the vector is facing the other way and can be regarded as hidden from the observation point.
With this method I get the following result:
Heres how I calculate the dot product and check for visibility:
vector<vector<int>> calculateShadowingMatrix(vector<point> observationNormals, vector<vector<point>> normals){
vector<vector<int>> shadowMatrix;
for(unsigned ii = 0; ii < normals.size(); ii++){
vector<int> visibilty;
for(unsigned jj = 0; jj < normals[ii].size(); jj++) {
double dot = dotProduct(observationNormals[ii], normals[ii][jj]);
if (dot <= 0.0) {
visibilty.push_back(1);
} else {
visibilty.push_back(0);
}
}
shadowMatrix.push_back(visibilty);
}
return shadowMatrix;
}
double dotProduct(point _u, point _v){
return _u.getX()*_v.getX() - _u.getY()*_v.getY();
}
I am trying to get all of the normal vectors which are facing the observation point to be present and zero out the ones which are facing outwards. I get errors, as can be seen in the second image, some of the vector are facing the wrong direction.
Final result should look as follows:
Your dot product calculation is wrong. It should be X * X + Y * Y.
I am implementing a marching cubes algorithm generally based on the implementation of Paul Bourke with some major adjustments:
precalculation of the scalarfield (floating point values)
avoiding duplicated vertices in the final list using a std::map
vertex storing to visualize the final mesh in Ogre3D
Basically I changed nearly 80% of his code. My resulting mesh has some ugly terrasses and I am not sure how to avoid them. I assumed that using floating points for the scalar field would do the job. Is this a common effect? How can you avoid it?
calculating the vertex positions on the edges. (cell.val[p1] contains the scalar value for the given vertex):
//if there is an intersection on this edge
if (cell.iEdgeFlags & (1 << iEdge))
{
const int* edge = a2iEdgeConnection[iEdge];
int p1 = edge[0];
int p2 = edge[1];
//find the approx intersection point by linear interpolation between the two edges and the density value
float length = cell.val[p1] / (cell.val[p2] + cell.val[p1]);
asEdgeVertex[iEdge] = cell.p[p1] + length * (cell.p[p2] - cell.p[p1]);
}
You can find the complete sourcecode here: https://github.com/DieOptimistin/MarchingCubes
I use Ogre3D as library for this example.
As Andy Newmann said, the devil was in the linear interpolation. Correct is:
float offset;
float delta = cell.val[p2] - cell.val[p1];
if (delta == 0) offset = 0.5;
else offset = (mTargetValue - cell.val[p1]) / delta;
asEdgeVertex[iEdge] = cell.p[p1] + offset* (cell.p[p2] - cell.p[p1]);
Language/Compiler: C++ (Visual Studio 2013)
Experience: ~2 months
I am working in a rectangular grid in 3D-space (size: xdim by ydim by zdim) where , "xgrid, ygrid, and zgrid" are 3D arrays of the x,y, and z-coordinates, respectively. Now, I am interested in finding all points that lie within a sphere of radius "r" centered about the point "(vi,vj,vk)". I want to store the index locations of these points in the vectors "xidx,yidx,zidx". For a single point this algorithm works and is fast enough but when I wish to iterate over many points within the 3D-space I run into very long run times.
Does anyone have any suggestions on how I can improve the implementation of this algorithm in C++? After running some profiling software I found online (very sleepy, Luke stackwalker) it seems that the "std::vector::size" and "std::vector::operator[]" member functions are bogging down my code. Any help is greatly appreciated.
Note: Since I do not know a priori how many voxels are within the sphere, I set the length of vectors xidx,yidx,zidx to be larger than necessary and then erase all the excess elements at the end of the function.
void find_nv(int vi, int vj, int vk, vector<double> &xidx, vector<double> &yidx, vector<double> &zidx, double*** &xgrid, double*** &ygrid, double*** &zgrid, int r, double xdim,double ydim,double zdim, double pdim)
{
double xcor, ycor, zcor,xval,yval,zval;
vector<double>xyz(3);
xyz[0] = xgrid[vi][vj][vk];
xyz[1] = ygrid[vi][vj][vk];
xyz[2] = zgrid[vi][vj][vk];
int counter = 0;
// Confine loop to be within boundaries of sphere
int istart = vi - r;
int iend = vi + r;
int jstart = vj - r;
int jend = vj + r;
int kstart = vk - r;
int kend = vk + r;
if (istart < 0) {
istart = 0;
}
if (iend > xdim-1) {
iend = xdim-1;
}
if (jstart < 0) {
jstart = 0;
}
if (jend > ydim - 1) {
jend = ydim-1;
}
if (kstart < 0) {
kstart = 0;
}
if (kend > zdim - 1)
kend = zdim - 1;
//-----------------------------------------------------------
// Begin iterating through all points
//-----------------------------------------------------------
for (int k = 0; k < kend+1; ++k)
{
for (int j = 0; j < jend+1; ++j)
{
for (int i = 0; i < iend+1; ++i)
{
if (i == vi && j == vj && k == vk)
continue;
else
{
xcor = pow((xgrid[i][j][k] - xyz[0]), 2);
ycor = pow((ygrid[i][j][k] - xyz[1]), 2);
zcor = pow((zgrid[i][j][k] - xyz[2]), 2);
double rsqr = pow(r, 2);
double sphere = xcor + ycor + zcor;
if (sphere <= rsqr)
{
xidx[counter]=i;
yidx[counter]=j;
zidx[counter] = k;
counter = counter + 1;
}
else
{
}
//cout << "counter = " << counter - 1;
}
}
}
}
// erase all appending zeros that are not voxels within sphere
xidx.erase(xidx.begin() + (counter), xidx.end());
yidx.erase(yidx.begin() + (counter), yidx.end());
zidx.erase(zidx.begin() + (counter), zidx.end());
return 0;
You already appear to have used my favourite trick for this sort of thing, getting rid of the relatively expensive square root functions and just working with the squared values of the radius and center-to-point distance.
One other possibility which may speed things up (a) is to replace all the:
xyzzy = pow (plugh, 2)
calls with the simpler:
xyzzy = plugh * plugh
You may find the removal of the function call could speed things up, however marginally.
Another possibility, if you can establish the maximum size of the target array, is to use an real array rather than a vector. I know they make the vector code as insanely optimal as possible but it still won't match a fixed-size array for performance (since it has to do everything the fixed size array does plus handle possible expansion).
Again, this may only offer very marginal improvement at the cost of more memory usage but trading space for time is a classic optimisation strategy.
Other than that, ensure you're using the compiler optimisations wisely. The default build in most cases has a low level of optimisation to make debugging easier. Ramp that up for production code.
(a) As with all optimisations, you should measure, not guess! These suggestions are exactly that: suggestions. They may or may not improve the situation, so it's up to you to test them.
One of your biggest problems, and one that is probably preventing the compiler from making a lot of optimisations is that you are not using the regular nature of your grid.
If you are really using a regular grid then
xgrid[i][j][k] = x_0 + i * dxi + j * dxj + k * dxk
ygrid[i][j][k] = y_0 + i * dyi + j * dyj + k * dyk
zgrid[i][j][k] = z_0 + i * dzi + j * dzj + k * dzk
If your grid is axis aligned then
xgrid[i][j][k] = x_0 + i * dxi
ygrid[i][j][k] = y_0 + j * dyj
zgrid[i][j][k] = z_0 + k * dzk
Replacing these inside your core loop should result in significant speedups.
You could do two things. Reduce the number of points you are testing for inclusion and simplify the problem to multiple 2d tests.
If you take the sphere an look at it down the z axis you have all the points for y+r to y-r in the sphere, using each of these points you can slice the sphere into circles that contain all the points in the x/z plane limited to the circle radius at that specific y you are testing. Calculating the radius of the circle is a simple solve the length of the base of the right angle triangle problem.
Right now you ar testing all the points in a cube, but the upper ranges of the sphere excludes most points. The idea behind the above algorithm is that you can limit the points tested at each level of the sphere to the square containing the radius of the circle at that height.
Here is a simple hand draw graphic, showing the sphere from the side view.
Here we are looking at the slice of the sphere that has the radius ab. Since you know the length ac and bc of the right angle triangle, you can calculate ab using Pythagoras theorem. Now you have a simple circle that you can test the points in, then move down, it reduce length ac and recalculate ab and repeat.
Now once you have that you can actually do a little more optimization. Firstly, you do not need to test every point against the circle, you only need to test one quarter of the points. If you test the points in the upper left quadrant of the circle (the slice of the sphere) then the points in the other three points are just mirror images of that same point offset either to the right, bottom or diagonally from the point determined to be in the first quadrant.
Then finally, you only need to do the circle slices of the top half of the sphere because the bottom half is just a mirror of the top half. In the end you only tested a quarter of the point for containment in the sphere. This should be a huge performance boost.
I hope that makes sense, I am not at a machine now that I can provide a sample.
simple thing here would be a 3D flood fill from center of the sphere rather than iterating over the enclosing square as you need to visited lesser points. Moreover you should implement the iterative version of the flood-fill to get more efficiency.
Flood Fill
I need to generate a set of vertices for a simple convex polygon to do a minimum weight triangluation for that polygon using dynamic programming , I thought about taking a circle of radius r and then take 20 vertices moving counter clock wise and then i will form a 20 vertex convex polygon but i how can i do that
How would i know the vertex that lies on a circle of radius r ?
and is there another easier way of generating vertices for convex polygon other than that way
Any help greatly appreciated
Generate your 20 random numbers between 0 and 2*pi, and sort them.
Now use a little basic trigonometry to convert to X,Y coordinates.
for (int i = 0; i < 20; i++)
{
x = x0 + r*cos(angle[i]);
y = y0 + r*sin(angle[i]);
// ...
}
btw. +1 for nice approach with that circle ...
do not care for number of vertexes
{
double x0=50.0,y0=50.0,r=50.0; // circle params
double a,da,x,y;
// [view] // my view engine stuff can skip this
glview2D::_lin l;
view.pic_clear();
l.col=0x00FFFFFF;
// [/view]
for (a=0.0;a<2.0*M_PI;) // full circle
{
x=x0+(r*cos(a));
y=y0+(r*sin(a));
a+=(20.0+(40.0*Random()))*M_PI/180.0; // random angle step < 20,60 > degrees
// here add your x,y point to polygon
// [view] // my view engine stuff can skip this
l.p0=l.p1; // just add line (lust x,y and actual x,y)
l.p1.p[0]=x;
l.p1.p[1]=y;
view.lin.add(l);
// [/view]
}
// [view] // my view engine stuff can skip this
view.lin[0].p0=l.p1; // just join first and last point in first line (was point0,point0)
// [view]
}
if number of vertexes is known = N
Set random step to be on average little less then 2PI / N for example:
da=a0+(a1*Random());
a0=0.75*(2*M_PI/N) ... minimal da
a1=0.40*(2*M_PI/N) ... a0+(0.5*a1) is avg = 0.95 ... is less then 2PI/N
inside for add break if vertex count reach N. If after for the vertex count is not N then recompute all from beginning because with random numbers you cannot take it that you always hit N vertexes this way !!!
sample output from source code above
PS.
You can also use ellipse if the circle shape is not good enough
x=x0+(rx*cos(a));
y=y0+(ry*sin(a));
rx != ry
Here is a flexible and efficient way to generate convex polygon : -
Generate random points on the circle at center point (xc,yc)
tweak any point (xi,yi) in sequence of consecutive points
check if (x(i-1),y(i-1)) , (xi,yi) , (x(i+1),y(i+1)) form a left turn else reject the tweak.
if points are arranged in anti clockwise manner then left turn at point (x2,y2) :-
int crosspro = (x3-x2)*(y2-y1) - (y3-y2)*(x2-x1)
if(crosspro>0) return(left_turn);
else return(right_turn);
This is my version of the circle method in Javascript.
var x = [0];
var y = [0];
var r = 0;
var angle = 0
for (var i = 1; i < 20; i++) {
angle += 0.3 + Math.random() * 0.3
if (angle > 2 * Math.PI) {
break; //stop before it becomes convex
}
r = (5 + Math.random() * 20+Math.random()*50)
x.push(x[i - 1] + r * Math.cos(angle));
y.push(y[i - 1] + r * Math.sin(angle));
}