Home Technical Talk

"Point Cloud" Shader Technique

Hi Polycount! Long time lurker here, first time posting. I finally landed my first job in the game industry about 8 months ago(!!!) as a 3D artist, it's been awesome and I really owe this community a big thanks for all the wisdom, laughs and inspiration it has given me over the years. So, thank you Polycounters!

But I've now been tasked with learning the art of shaders, which I've always wanted to do, but I think I may be in need of some guidance from you guys...

So I'm trying to achieve the look of a "Point Cloud" much like the following images:

thQCc.jpg
6HlrX.jpg

The Specifics:

I'm trying to achieve this on Shader Model 2.0, and without using the actual geometry verts (the geometry is very low poly, mostly interior walls). Ideally there would be a greater number of points along edges to give the player a fairly accurate representation of the room depth/shape.

On top of all of this, I'm hoping to get this working on iOS. I know this is a really tall order but I'm determined to make this work if it can be done. (Or get as close as possible to the desired effect)

Initially I thought the easiest/cheapest way to go about this was a rim lighting shader that has an alpha channel for the rim to create the illusion of points. (I then planned to distribute points (2D texture) evenly along the rest of the surface on a second pass) However, this did not give me even close to the desired effect, and I'm a bit lost as to what I should try next.

I should mention I'm working in Unity, using the Cg/Shaderlab language. I'm not necessarily looking for actual code or anything (although that is welcome) but more of your guys' opinion on how you would go about achieving such an effect.

Any ideas?

Replies

  • JamesWild
    Offline / Send Message
    JamesWild polycounter lvl 8
    Draw a grid of grey dots on a black background with an additive blend.

    If you have normal maps, use them to shift the UVs around to generate the impression of bunching in seams/etc.
  • Xoliul
    Offline / Send Message
    Xoliul polycounter lvl 16
    Hmm, I think it might work if you use something like modulo on the world coordinates, to divide the thing into a grid. Then your could set the opacity (or color if you use additive blending) for each grid point. The hard thing is probably controlling the size of the point, might have to use some extra math on top, I'm thinking something like get distance values to the closest grid point, so you will get like radial gradients, and then just perform a step on those to control the size...

    Hope it makes some sense?
  • Ace-Angel
    Offline / Send Message
    Ace-Angel polycounter lvl 12
    I would use a texture, and have it tiled, have it face the camera, all on the mesh at Pixel Shader level.

    Also, pending on engine, you should also be able to get the depth distance, to make things up close brighter, and those further away darker, although at 2.0 level, I'm not sure how much math you can push before falling off.
  • Gestalt
    Offline / Send Message
    Gestalt polycounter lvl 11
    3d texture? using masked opacity? UDK comes with shader functions that describe projecting 3d textures if you want to see an implementation. You could project the same repeating dotted texture along each axis and have them add together and clamp to represent where all intersect or something (idk just a thought). edit: you'd probably multiply them
  • Xoliul
    Offline / Send Message
    Xoliul polycounter lvl 16
    Some proper math is much easier guys :)
    Here you go, this works:
    float scale = 0.5;
    float3 grid = float3(0,0,0);
    float3 pos = In.worldposition.xyz;

    grid.x = fmod(pos.x,scale);
    grid.y = fmod(pos.y,scale);
    grid.z = fmod(pos.z,scale);

    float dotsize = 0.2;
    float3 input1 = step(length(grid),dotsize);

    float4 ret = float4(1,1,1, input1);
    That's a pixel shader, make sure to feed in a proper worldposition ( = mul(In.position, WorldMatrix) )

    scale determines the spacing of grid points, dotsize is the size of dots. That last one is really dependant on distance, as I expected it becomes a jittery mess easily...

    edit: you can use UV-space as well, instead of world space. It doesn't look great on curved meshes, since some pixels can fall between the grid. Disadvantage of UV space is your UV density will cause scaling to be all over the place...
  • Ace-Angel
    Offline / Send Message
    Ace-Angel polycounter lvl 12
    Correct me if I'm wrong, but I thought the Length function is not available on the iOS, so they would need to calculate it manually?

    IIRC, this is how it would be;

    Length = Square Root (X^2 + Y^2 + Z^2)

    Correct me if I'm wrong.
  • Xoliul
    Offline / Send Message
    Xoliul polycounter lvl 16
    I have no clue, never worked with IOS before. It can't be a true PS2.0 implementation if it doesn't have basic functions like that though...
  • Ace-Angel
    Offline / Send Message
    Ace-Angel polycounter lvl 12
    I know UDK doesn't have have it declared, so you need to declare it manually (at least for the IOS platform) from my past experience.

    Either way, thanks for the math, looks awesome.
  • Xoliul
    Offline / Send Message
    Xoliul polycounter lvl 16
    I put a little more effort in this, now it works by actually projecting points onto a surface (based on the normal), so it works with eveyrthing.
    float scale = 0.7;
    float3 grid = float3(0,0,0);
    float3 pos = In.worldposition.xyz ;

    grid.x = fmod(pos.x,scale);
    grid.y = fmod(pos.y,scale);
    grid.z = fmod(pos.z,scale);


    float zgrid = length(grid.xy);
    float xgrid = length(grid.yz);
    float ygrid = length(grid.xz);

    float3 normal = In.worldNormal;
    float nyblend = abs(normal.y);
    float nzblend = abs(normal.z);

    float dotsize = 0.1;
    float3 result = step(lerp(lerp(xgrid,ygrid,nyblend),zgrid, nzblend),dotsize);

    float4 ret = float4(1, result);
    return ret;
    Need to pass in the world normal obviously. It's still a bit sloppy where for curved objects the scale and point size isn't uniform, that just comes down to using something more elegant than just the vector length.

    Edit: here's the FX: https://dl.dropbox.com/u/12093849/dump/pointcloud.fx
  • JRoman
    Wow you guys are awesome! First and foremost, thank you all for the helpful replies and suggestions.

    Xoliul, I'm incredibly grateful for your help with achieving this result, and even going as far as improving it and sharing your work. Besides what I've learned in the past 2 weeks from tutorials and experimentation I'm brand new to this so I really appreciate your help. :)

    That being said, I have loaded the .fx file into Max and started playing around with it. It's very very close to effect I'm looking for! At this time I'm attempting to get this working in Unity and then I'm planning on trying to control the size (and possibly color) of the dots by screen distance. Our level designer has some experience with shaders so I'm hoping he'll be able to help with this. Regardless, this is a great start and it's looking awesome. Thank you!

    One more question; do you guys have any suggestions for further reading/learning shaders? I've been drudging through the Cg Tutorial provided by nVidia, but it has been a fairly steep learning curve for an artist with zero C/C++/C# experience. Slowly but surely I'm getting my head wrapped around it, but if there's an easier-for-newbs solution that anyone has experience with I'd love to hear about it.

    Thanks again!
  • Xoliul
    Offline / Send Message
    Xoliul polycounter lvl 16
    I'm planning on writing exactly what you are after sometime, when I have time :) That doesn't help you too much now, but there's a simple article about colormath on my site already. There's also a shader DVD by Eat3D.
  • jerry
    Hi,

    I had some fun converting this to a shader you can use in Unity3D. One strange issue is that the dots aren't dots but quarter circles. I'm not as smart as Xoliul so maybe he knows what's going on.

    Preview:

    Dots_preview.png


    Download package with shader and material: Download

    Would be fun to expand on this.
  • CrazyButcher
    Offline / Send Message
    CrazyButcher polycounter lvl 20
    float zgrid = length(grid.xy - 0.5 * scale);
    float xgrid = length(grid.yz - 0.5 * scale);
    float ygrid = length(grid.xz - 0.5 * scale);
    

    should give you points,

    fmod basically chops values into segments each of the length [0,scale], the grid coordinates. Think of tiles, every tile has a local x,y axis. points within a tile go from (0,0) in one corner (scale,scale) to the other diagonal corner.

    The length operation done gives you distance from origin 0,0. Because every tile has its origin in one corner, and every tile starts "freshly" you only see one quadrant.

    With the step function at the end you get 1 or 0 depending if length (distance to origin) is smaller than the threshold provided (dotsize).

    The code above basically ensures the origin is at center of the tile (half the tile size), therefore should give you all quadrants in the coordinate system of each tile = circles.


    Depending on the normal one "tile wall" is chosen (facing x,y or z).
  • Xoliul
    Offline / Send Message
    Xoliul polycounter lvl 16
    yeah i know, i didn't really bother making them circles, because I though they'd be so small you wouldn't notice. Like I said, it's all a bit sloppy but it showed the point.

    I tried CrazyButcher's edit and it's not working 100%, it needs an extra abs() thrown in, so the correct code for dots would be :
    float zgrid = length(abs(grid.xy) - (0.5 * scale));
    float xgrid = length(abs(grid.yz) - (0.5 * scale));
    float ygrid = length(abs(grid.xz) - (0.5 * scale));

    This is fun :)

    Oh and Jerry, cool to see you here again. Nice job converting! How much work is that for Unity ? I'm not familiar with their shader system/language.
  • jerry
    Updated the package with circle dots: Download

    Converting to Unity wasn't that bad, it can have blocks of CG code inside the shader files so it's pretty straightforward. I just had to look up how to get the worldnormal and worldpos and added the parameters for the inspector etc.
  • JRoman
    Jerry, CrazyButcher, Xoliul! Thank you!

    Jerry, I just realized I never replied to this so I wanted to make sure you knew how much I appreciated you helping me out with this. Your code conversion has been a great help in understanding the differences in Cg code, thank you for taking the time to do this. :)
  • wallaby
    Offline / Send Message
    wallaby null
    Hey everyone, apologies for resurrecting an ancient thread, but I can't quite figure out how to add Color to this.

    I've got quite a bit of coding/scripting experience, but nothing directly in Shaders like this. 

    I've added a color variable in the shader, which is showing up in the Unity editor now, but I can't figure out where white is getting specified, and how you might alter that. 

    I'm assuming I'd have to do something before returning the function in the last couple lines, but Float 4 is a positional call, from what I can tell? 

    float3 result = step(lerp(lerp(xgrid,ygrid,nyblend),zgrid, nzblend),dotsize);
    float4 ret = float4(1, result);
    Any help appreciated! :-)

  • wallaby
    Offline / Send Message
    wallaby null
    Ah stumbled upon answer - didn't know you could just multiply colors together.

    I guess because its just white (1) times the RGB values of whatever you set in the Editor.

    float4 ret = float4(1, result); 
    ret = ret*_Color;
    return ret;

Sign In or Register to comment.