We submitted a paper to EGSR (Eurographics Symposium on Rendering), and still waiting to see if the paper is accepted or not.
In the mean time, here's a little result. We used a Perlin sum, to sum up the first four images, to create the last one. The output is very interesting.
Here's the octaves:
Here's the output (also used as the new backgroud for this blog):
Note that each octave is different, which is different to Perlin noises. But if we wanted, we could have used the exact approach described by Perlin.
In future post, I'll give more detail about the method used to create these textures. All images are tileable. Eventually, a GPU version of this framework should be implemented. Either as a fragment shader or on CUDA/OpenCL.
I use this blog to share (presentable) results from my research as a PhD student in computer science.
Showing posts with label cuda. Show all posts
Showing posts with label cuda. Show all posts
2012/04/23
2011/11/04
My clouds are so fluffy, you're gonna die!
Today I'm showing some cloud textures that I managed to create with a simple algorithm using a Poisson-disk sampling process to distribute some points. What is nice with this process to distribute point is that it's very portable and efficient. It can even be done in parallel on the GPU (with CUDA or OpenCL), this paper actually shows one way to do it: Efficient Maximal Poisson-disk Sampling.
The rendering process for now is done completely on the CPU because we are still in a phase where we need to be able to add, change or remove some part quite fast to adjust the renderer to our needs. It's a bit harder to do that on the GPU. But when we will be completely sure of the product that we want, we will adapt it to the GPU, probably with OpenCL for portability reasons.
So here some of these outputs:
There's still a little bit of work to do to remove some isolated points that we can see in some images. I might have some idea to solve this, just need to implement it. So, let me know what you think about those images.
The rendering process for now is done completely on the CPU because we are still in a phase where we need to be able to add, change or remove some part quite fast to adjust the renderer to our needs. It's a bit harder to do that on the GPU. But when we will be completely sure of the product that we want, we will adapt it to the GPU, probably with OpenCL for portability reasons.
So here some of these outputs:
There's still a little bit of work to do to remove some isolated points that we can see in some images. I might have some idea to solve this, just need to implement it. So, let me know what you think about those images.
Labels:
clouds,
cuda,
GPGPU,
GPU,
layers,
OpenCL,
point distribution,
poisson disk sampling,
procedural texturing,
smoke texture,
texture
2011/09/30
research news
I don't have new stuff to show right now, but I've been busy in the last week. I'm currently implementing a more dynamic version of my renderer. CUDA is very nice, but when you need to add quickly new modules or do some rapid tweaks, it's not the best solution. So, for now the plan is to render to have a slower renderer that would be easier to change and adapt. When we're sure of the features that we really want to keep and we'll know the full limitation of our system, an implementation in CUDA would be possible.
One of the reason to change the renderer is that I needed some C++ features like virtual functions, which is very useful for designing rapidly a new system. I think there's some language that allows to create virtual function in CUDA or OpenCL by simply adding some hidden piece of code.
If it doesn't exist, here's how I'd do it:
First, in CUDA, there's no function pointers. But if we take a look at how polymorphism works, it's really just a way to disguise procedural code. Actually, the whole object oriented programming scheme is just a way to disguise procedural code by hiding some information to the programmer so (s)he doesn't have to worry about it. Just like many other languages do (Java is the most popular about that one because it hides a lot of security features like buffer overflows done during the execution to avoid major problems).
So, if we have:
But, without any function pointers, we need to recreate that illusion. So, one way that can be done in CUDA/OpenCL, is to add a unique ID for each class. And each instance of that class will have that ID assigned in the constructor. So:
I think that such compilers exist already, but I never used one of them. But it's probably using a method similar to the one above to emulate the virtual tables.
And for those of you who didn't know how polymorphism worked, now you have a better idea of the process the compiler has to do to convert classes and their virtual functions in machine bytecode. So, I hoped that post helps a bit for that. Otherwise, there's a couple of resources available on the web that would clearly describe that process.
If any of you know a (free if possible) compiler that convert basic C++ code in CUDA or OpenCL, but also has integrated some of the features of those languages like the synchronization between threads of the same bloc and atomic operations, I'd really like to see that and use it eventually. It must work on Linux!
One of the reason to change the renderer is that I needed some C++ features like virtual functions, which is very useful for designing rapidly a new system. I think there's some language that allows to create virtual function in CUDA or OpenCL by simply adding some hidden piece of code.
If it doesn't exist, here's how I'd do it:
First, in CUDA, there's no function pointers. But if we take a look at how polymorphism works, it's really just a way to disguise procedural code. Actually, the whole object oriented programming scheme is just a way to disguise procedural code by hiding some information to the programmer so (s)he doesn't have to worry about it. Just like many other languages do (Java is the most popular about that one because it hides a lot of security features like buffer overflows done during the execution to avoid major problems).
So, if we have:
class AThen this piece of code is actually converted into something that look basically like this:
{
private:
int value;
public:
A(void);
virtual ~A(void);
virtual void test(T param1, U param2);
};
// prototypesAnd then, with a subclass B, we have:
struct class_A;
void __class_A_constructor(struct class_A *this);
void __class_A_destructor(struct class_A *this);
void __class_A_test(struct class_A *this, T param1, U param2);
struct class_A_virtual_table
{
void (*class_A_destructor)(struct class_A *this);
void (*class_A_test)(struct class_A *this, T param1, U param2);
};
// create a single instance of the virtual table and assign the values for the function pointersstruct class_A_virtual_table class_A_virtual_table_only_instance_needed =
{
__class_A_destructor,
__class_A_test
};
struct class_A
{
class_A_virtual_table *VT;
int value;
};
void __class_A_constructor(struct class_A *this)
{
this->value = 0; // or some other default value
this->VT = &class_A_virtual_table_only_instance_needed;
}
void __class_A_destructor(struct class_A *this)
{
// stuff to destroy
}
void __class_A_test(struct class_A *this, T param1, U param2)
{
// stuff to test
}
// virtual functions
void virtual_class_A_destructor(struct class_A *this)
{
this->VT.class_A_destructor(this);
}
void virtual_class_A_test(struct class_A*this, T param1, U param2)
{
this->VT.class_A_test(this, param1, param2);
}
class B: public A
{
private:
double value;
public:
B(void);
~B(void);
void test(T param1, U param2); // version of test but for class B
void function_in_B(void);
};
int main(void)Then, all of this become:
{
B b;
A *a = &b;
a->test(1, 2); // let way that type T and U are integers...
b.function_in_B();
}
struct class_BSo, as you can see, polymorphism is using function pointers to create the illusion that the right function is called each time.
{
struct class_A super;
double value;
};
// prototypes for class B
void __class_B_constructor(struct class_B *this);
void __class_B_destructor(struct class_B *this);
void __class_B_test(struct class_B *this, T param1, U param2);
void __class_B_function_in_B(struct class_B *this);
// virtual table for B, with the virtual functions for B
struct class_A_virtual_table class_B_virtual_table_only_instance_needed =
{
__class_B_destructor,
__class_B_test
};
void __class_B_constructor(struct class_B *this)
{
__class_B_constructor(&(this->super));
this->VT = &class_B_virtual_table_only_instance_needed; // replace the virtual table
this->value = 0.0; // or some other default value
}
void __class_B_destructor(struct class_B *this)
{
// stuff to destroy in B
__class_A_destructor(&(this->super)); // then, destroy the stuff in the parent
}
void __class_B_test(struct class_B *this, T param1, U param2)
{
// stuff to test, but for B
}
void __class_B_function_in_B(struct class_B *this)
{
// whatever that function does!
}
int main(void)
{
struct class_B b;
__class_B_constructor(&b);
struct class_A *a = &(b.super); // the address of the parent in struct class_B
virtual_class_A_test(a, 1, 2);
__class_B_function_in_B(&b);
__class_B_destructor(&b);
}
But, without any function pointers, we need to recreate that illusion. So, one way that can be done in CUDA/OpenCL, is to add a unique ID for each class. And each instance of that class will have that ID assigned in the constructor. So:
enum CLASS_IDS { CLASS_A_ID, CLASS_B_ID, ..., CLASS_X_ID };Finally, write the virtual functions like this:
struct class_A
{
int ID;
int value;
};
void __class_A_constructor(struct class_A *this)
{
this->ID = CLASS_A_ID;
this->value = 0; // or some other default value
}
struct class_B
{
struct class_A super;
double value;
};
void __class_B_constructor(struct class_B *this)
{
__class_A_constructor(&(this->super)); // always call the parent constructor first!
this->ID = CLASS_B_ID; // overwrite the ID
this->value = 0.0; // or some other default value
}
void virtual_class_A_destructor(struct class_A *this)So, with a switch statement, it's possible to replace the virtual table. So, a compiler that would be able to take C++ code and convert it in CUDA/OpenCL would be able to do polymorphism with that approach. It's much more slower than a function pointer, but the result of the computation would be the same.
{
switch(this->ID)
{
case CLASS_A_ID:
__class_A_destructor(this);
break;
case CLASS_B_ID:
__class_B_destructor((class_B*)this);
break;
// ...
case CLASS_X_ID:
__class_X_destructor((class_X*)this);
break;
};
}
I think that such compilers exist already, but I never used one of them. But it's probably using a method similar to the one above to emulate the virtual tables.
And for those of you who didn't know how polymorphism worked, now you have a better idea of the process the compiler has to do to convert classes and their virtual functions in machine bytecode. So, I hoped that post helps a bit for that. Otherwise, there's a couple of resources available on the web that would clearly describe that process.
If any of you know a (free if possible) compiler that convert basic C++ code in CUDA or OpenCL, but also has integrated some of the features of those languages like the synchronization between threads of the same bloc and atomic operations, I'd really like to see that and use it eventually. It must work on Linux!
Labels:
C++,
C++ to CUDA,
C++ to OpenCL,
classes,
cuda,
function pointers,
GPGPU,
GPU,
inheritance,
Khronos,
new renderer,
nvidia,
OpenCL,
polymorphism,
programming,
switch statement,
virtual functions
2011/08/25
first pattern
So, yesterday I mentioned two steps that we need to work on to finish the hierarchy project. And one of them is to work on patterns or structures to show what is possible to do with that product.
Today, I managed to to a first structure, a flower. Very simple to do. Just needed some adjustment first, but I got that result in few minutes. So here it is:
Naturally, I just showed the final product. I had other images created before I managed to do the right effect that I wanted. I think that this is a nice preview of what that product can do.
Let me know what you think about that output!
Today, I managed to to a first structure, a flower. Very simple to do. Just needed some adjustment first, but I got that result in few minutes. So here it is:
Naturally, I just showed the final product. I had other images created before I managed to do the right effect that I wanted. I think that this is a nice preview of what that product can do.
Let me know what you think about that output!
Labels:
cuda,
flower,
hierarchy,
pattern,
procedural texturing,
rendering,
texture,
voronoi diagram
2011/08/24
Hierarchy textures
So, it's been a while. I was so busy at SIGGRAPH that I never had the time to share anything.
In this post, I'm showing you some of the last textures I've been working on. These are build as a hierarchy. We use again our variation of the Voronoi diagram to create weird shapes. But this time, instead of using simply one level, we introduce many levels.
On the first level, again, the Voronoi cells are computed and once we know in which cell we are, if that cell has a sub-level, we can then continue the visit in the structure into that sub-level. Each level has can have a small or big influence on the final color of the pixel.
So, here's some outputs:
One of the first output made. It has two cells on the first level. One is the circle-like shape in the middle and then the rest. In the "rest", there's a sub level with a simple small division of the plane in two.
Exactly as the previous one, but here, there's a sub-level in the circle-like shape. As you can see, the fact that there's a new sub-level only affects the part where that sub-level is.
The actual first output that I got with the hierarchy.
In the next images, I start playing with colors inside the cells. Naturally, all of these images where made from random point distribution.
Finally, there's two big step to do before we consider that project finished. First, we want to investigate various patterns. So, how can we do a particular shape for a cell. For this part, we don't need the hierarchy because we know it works. When we are able to control properly the patterns, we will be able to used them at various level.
Second, we want to be more dynamic for the color. Right now, each cell use a function f:[0;1]->[0;1] to control its value that will influence all the other levels. But those functions are a bit static. I can add more, but it will never be enough. So we turn to use something like the shaders in OpenGL. Literally, each cell could have its own function (coded in CUDA-C) and that function would be used to control the contribution of that cell and its sub-level to the final color of the pixel.
In this post, I'm showing you some of the last textures I've been working on. These are build as a hierarchy. We use again our variation of the Voronoi diagram to create weird shapes. But this time, instead of using simply one level, we introduce many levels.
On the first level, again, the Voronoi cells are computed and once we know in which cell we are, if that cell has a sub-level, we can then continue the visit in the structure into that sub-level. Each level has can have a small or big influence on the final color of the pixel.
So, here's some outputs:
One of the first output made. It has two cells on the first level. One is the circle-like shape in the middle and then the rest. In the "rest", there's a sub level with a simple small division of the plane in two.
Exactly as the previous one, but here, there's a sub-level in the circle-like shape. As you can see, the fact that there's a new sub-level only affects the part where that sub-level is.
The actual first output that I got with the hierarchy.
In the next images, I start playing with colors inside the cells. Naturally, all of these images where made from random point distribution.
In these images, I simply played with the hierarchy. Each cell has a random chance to get a sub-level and so on. Until a maximal depth was reached. In some of them, I played also with the feature that at each level, you can influence the final color by accumulating a value.
Finally, there's two big step to do before we consider that project finished. First, we want to investigate various patterns. So, how can we do a particular shape for a cell. For this part, we don't need the hierarchy because we know it works. When we are able to control properly the patterns, we will be able to used them at various level.
Second, we want to be more dynamic for the color. Right now, each cell use a function f:[0;1]->[0;1] to control its value that will influence all the other levels. But those functions are a bit static. I can add more, but it will never be enough. So we turn to use something like the shaders in OpenGL. Literally, each cell could have its own function (coded in CUDA-C) and that function would be used to control the contribution of that cell and its sub-level to the final color of the pixel.
Labels:
cuda,
cuda-c,
GPU,
hierarchy,
opengl,
procedural texturing,
shader,
SIGGRAPH,
texture,
voronoi diagram
2011/07/08
merging voronoi
Here we are playing again with the Voronoi diagram (VD). We wanted to see what happen when sites are merged together (MVD, merged VD).
VD partitions the plane in cells where each of these cell represents the closest part of the plane to a particular site. So, if you have sites on a map of various fast food restaurants, the Voronoi diagram tells you which one is physically the closest to your location.
But imagine that we want to consider all restaurant from a particular chain as one entity. We want to know the influence or the domination of a particular chain.
The following images are in pair. The first one is the influence of a chain of restaurant and the second is the actual VD that everyone knows. Cells with the same color represents a chain of restaurant.
In the images, you can see some brighter points (particularly in the first two images), this is the actual location of the site (or restaurant for in our example).
And just like the VD is well known for its role in texture synthesis, we hope to find a way to use the MVD to create some interesting results.
As for the diagram itself, we don't really know what to do with it or how to interpret it. VD has a dual graph called the Delaunay Triangulation. But what would be the dual graph of MVD ? Where do we put the edges ? That's an open question.
VD partitions the plane in cells where each of these cell represents the closest part of the plane to a particular site. So, if you have sites on a map of various fast food restaurants, the Voronoi diagram tells you which one is physically the closest to your location.
But imagine that we want to consider all restaurant from a particular chain as one entity. We want to know the influence or the domination of a particular chain.
The following images are in pair. The first one is the influence of a chain of restaurant and the second is the actual VD that everyone knows. Cells with the same color represents a chain of restaurant.
In the images, you can see some brighter points (particularly in the first two images), this is the actual location of the site (or restaurant for in our example).
And just like the VD is well known for its role in texture synthesis, we hope to find a way to use the MVD to create some interesting results.
As for the diagram itself, we don't really know what to do with it or how to interpret it. VD has a dual graph called the Delaunay Triangulation. But what would be the dual graph of MVD ? Where do we put the edges ? That's an open question.
Labels:
cell,
cuda,
Delaunay Triangulation,
diagram,
domination,
DT,
dual graph,
fast food,
merged voronoi diagram,
MVD,
nvidia,
partition,
plane,
restaurant,
site,
synthesis,
texture,
VD,
voronoi diagram
2011/07/04
More texture synthesis
In this post, I present some new result that we really like. Again, it's a variation of the Voronoi diagram. You can observe the Voronoi cells interacting with each other. The colors help to see clearly the cells. And because of the few number of color, some cells might share the same color but are not related at all.
Here's some of the results:
This method, if it produces the result we're expecting, can be used to create texture by simply specifying some parameters or an artist can use a program with that method implemented to place some control points and control other parameters.
Again, all these images can be computed with CUDA because each pixel is unique.
Here's some of the results:
This method, if it produces the result we're expecting, can be used to create texture by simply specifying some parameters or an artist can use a program with that method implemented to place some control points and control other parameters.
Again, all these images can be computed with CUDA because each pixel is unique.
2011/06/27
mosaic
Yesterday I mentioned using images as a source for the points and orientations. And I managed to do something.
First, the original image (a South Park version of myself, before I lose some weight):
And here's the various mosaic:
I used various effects to control the orientation in the cell. Plus I tried a random point selection, except for the first output.
Which one do you prefer ?
Naturally, to produce the output, I'm still using CUDA. I didn't used any data structure to optimize, so it can be computed a bit faster.
First, the original image (a South Park version of myself, before I lose some weight):
And here's the various mosaic:
I used various effects to control the orientation in the cell. Plus I tried a random point selection, except for the first output.
Which one do you prefer ?
Naturally, to produce the output, I'm still using CUDA. I didn't used any data structure to optimize, so it can be computed a bit faster.
Labels:
cuda,
mosaic,
nvidia,
orientation,
point,
South Park,
voronoi
More textures
In my research, I'm still working on my textures. Here's some results I produced today that I really like.
Those effects might be implemented in a next version of my live-wallpaper. I haven't decided yet. For the first two images, I put the orientation of the points right on the plane. Plus I tried a new function to compute the distance. This one allows some distance pixels to actually have a distance of zero when it's perfectly aligned with another points.
Next, I will try to use images as a source of points and orientations. To see if with this method it's possible to create mosaic from an image. Creating mosaic with the normal Voronoi diagram is something well known in the literature. But with this method of oriented points that creates cells that are not convex polygons might allow us to do something very unique.
Let me know what you think of these images.
Those effects might be implemented in a next version of my live-wallpaper. I haven't decided yet. For the first two images, I put the orientation of the points right on the plane. Plus I tried a new function to compute the distance. This one allows some distance pixels to actually have a distance of zero when it's perfectly aligned with another points.
Next, I will try to use images as a source of points and orientations. To see if with this method it's possible to create mosaic from an image. Creating mosaic with the normal Voronoi diagram is something well known in the literature. But with this method of oriented points that creates cells that are not convex polygons might allow us to do something very unique.
Let me know what you think of these images.
2011/06/21
Yin Yang Texture
Here's a post on texture.
Previously, we were using a method to control images with points and normals. Alone, a point and a normal split the plane in two, a positive and negative part. But combined with other points and normals, we can actually control curve shapes. To visualize it, we assign white to the positive part and black for the other.
The textures proposed here are made by first splitting the plane in two parts. The two parts must be symmetrical just like the Yin Yang symbol.
We then distribute points and normals inside one of the part. We then reflect the point into the second part, but not the normal.
The method create a contrast and a perfect symmetry. Here's some results:
I won't describe the method to render those images because it's very complex. Also, the process is very slow. Unlike the live-wallpaper I'm working on, or the previous images I showed, we cannot compute those in real-time.
Unfortunately, we haven't implement this with CUDA. With CUDA, it wouldn't be in real-time, but it would be much faster. Specially the per-pixel rendering part. Let me know what you think about these Yin-Yang images.
Previously, we were using a method to control images with points and normals. Alone, a point and a normal split the plane in two, a positive and negative part. But combined with other points and normals, we can actually control curve shapes. To visualize it, we assign white to the positive part and black for the other.
The textures proposed here are made by first splitting the plane in two parts. The two parts must be symmetrical just like the Yin Yang symbol.
We then distribute points and normals inside one of the part. We then reflect the point into the second part, but not the normal.
The method create a contrast and a perfect symmetry. Here's some results:
I won't describe the method to render those images because it's very complex. Also, the process is very slow. Unlike the live-wallpaper I'm working on, or the previous images I showed, we cannot compute those in real-time.
Unfortunately, we haven't implement this with CUDA. With CUDA, it wouldn't be in real-time, but it would be much faster. Specially the per-pixel rendering part. Let me know what you think about these Yin-Yang images.
Labels:
color distribution,
cuda,
normal,
nvidia,
point,
reflection,
rendering,
symmetry,
synthesis,
texture,
Yang,
Yin
Subscribe to:
Posts (Atom)

















































