views:

47

answers:

1

I'm adding lines to my 3D world like we see in 3D studio max. To draw lines I'm using a cylinder mesh and simply stretching/rotating it appropriately. That's all working fine but my problem is scale. Since it's 3D geometry rendering in perspective its size changes, from a distance it's small to invisible, up close it's huge.

I want to make it so the size of the line geometry stays the same. I tried toying around with orthographic projection but came up with nothing. Any ideas?

+1  A: 

Well you could easily write a shader to get round that problem. Basically you need to push it out proportionally to the w value you generate. ie if the cylinder has a width of r. then you can cancel out the perspective by pushing it out to (r * w). This way when the w divide occurs it will ALWAYS give you r.

A cylinder, though, could be a tad excessive you could get a similar effect by drawing a billboarded line and applying a texture to it.

I wrote a shader in DX8 many years ago to do this (mind this is with perspective). Basically I defined the vertex data as follows:

struct BillboardLineVertex
{
    D3DXVECTOR3  position;
    D3DXVECTOR3  otherPosition;
    DWORD        colour;
    D3DXVECTOR2  UV;
};

Assuming the line goes from A to B then position is A and otherPosition is B for the first 2 vertices. Furthermore I encoded into the V (or y) of the UV either a -1 or 1. This told me whether I would push out from the line up or down the screen. Finally the 3rd coordinate for the triangle had the A & B in position and otherPosition the other way round (I'll leave you to figure out how to build the other triangle. Note that the U texture coord (or x) was settable to allow for texture repeating along the line.

I then had the following bit of shader assembly to build the lines ... This had the added bonus that it took exactly 2 triangles to do one line... I could then pack them all into 1 big vertex buffer and render several hundred in one Draw call.

asm
{
    vs.1.1

// Create line vector.
    mov r1, v0
    sub r3, r1, v4

// Get eye to line vector
    sub r6, v0, c20

// Get Tangent to line vector lieing on screen plane.
    mul r5, r6.yzxw, r3.zxyw
    mad r5, -r3.yzxw, r6.zxyw, r5

// Normalise tangent
    dp3 r4.w, r5.xyz, r5.xyz
    rsq r4.w, r4.w
    mul r5.xyz, r5.xyz, r4.w

// Multiply by 1 or -1 (y part of UV)
    mul r5.xyz, r5.xyz, -v9.y

// Push tangent out by "thickness"
    mul r5.xyz, r5.xyz, c16.x
    add r1.xyz, r1.xyz, r5.xyz

// Transform position
    m4x4 oPos, r1, c0

// Work out UV (c16.y is assumed to contain 0.5, c16.z is assumed to contain 1)
    mov r2.xy, v9.xy
    mul r2.y, v9.y, v9.x
    add r2.xy, r2.xy, c16.z
    mul oT0.xy, r2.xy, c16.y

// Move colour into diffuse output channel.
    mov oD0, v3
};

Such a setup would be easily modifiable to give you the same size regardless of distance from the camera.

Goz
Brillant.. and fun to see il asm code! I did about same thing but using Geometry Shaders. It avoid the cost of duplication of otherPosition. Also supported arbitrary line width and line types ala `glLineStipple`.
Stringer Bell
@Stringer: I DID write this code back in 2001-ish ;) I agree if you can target DX10 geometry shaders are your friend :)
Goz