# Rendering light balls: Where to do perspective calculations?

After rewatching Bisqwit's starfield renderer video, I've decided to try to rewrite the program in order to learn more about compute shaders and shader programming in general. My knowledge at this point is limited to moderately simple vertex and pixel shaders. The original program is a software renderer written in some variant of BASIC that draws each frame roughly like so:

for every rendered star:
update the star's position in the world
calculate the star's position on the screen
calculate the star's size on the screen

for every pixel of the screen:
for every star of the screen:
calculate the influence of the current star on the color of the current pixel
do dithering
draw the pixel in the resulting color


I'd try to translate this to a compute shader that draws to a texture which is then rendered using a fullscreen quad. Obvious issues with that approach aside, I'm a bit lost w.r.t. the perspective calulations. Where would I do the them, on the CPU or GPU? What would be a suitable mechanism to prevent having to do the calculation multiple times over? I wasn't able to find one through my research, so every pointer toward the solution is appreciated.

EDIT:
I think I might have been a bit unclear about what a "light ball"/"star" is: It's just some point in the world that is rendered in a fancy way, not a model.

EDIT 2: Thanks to all of the pointers presented in the answer and comments, I've decided to use two compute shaders. One does the perspective calulation and the other draws the result onto a texture, which is rendered onto a fullscreen quad. Judging from the suboptimal performance, using a different approach like the one outlined in @Thomas' comments might be better overall but that's a topic for another question.

• The perspective matrix is usually used in the vertex shader as part of the model view projection (MVP matrix). Mar 7 at 20:45
• @mindoverflow about your edit: you can use one vertex/point for one star to calculate the position... the thing is to render the pixels which are around the position. The pixel/fragment shader only is executed for this one pixel which got hit. You can change the point size if you like to get executions of fragments which are around the point and fade with respect to the center of the star. Or you use billboards and store a glare texture inside the quad. It is up to you which technique you want to use. Mar 9 at 8:39
• for example in OpenGL within the vertex shader there is the output variable gl_PointSize which can be used to enlarge the star with respect to the z-position (distance to camera) Mar 9 at 8:43
• Even a billboard has an MVP matrix and a vertex shader. Mar 9 at 13:19
• This question is making us guess as to the implementation. Is it implemented as a shadertoy(www.shadertoy.com), a fullscreen quad, billboards, halo's, to name a few methods to achieve what is described in the video which I skip scanned, we really shouldn't have to watch a 15 minute video to find out what the actual question is. Mar 9 at 14:32

First of all a small introduction about the matrices you need.

1. viewMatrix: This matrix is a 4x4 matrix which stores the position and orientation of your camera with respect to the world origin. Multiplying a vector 4D to this matrix transforms this point with respect to your camera coordinate / orientation. So it is a basis change from world coordinate system to camera coordinate system.
2. projectionMatrix: This matrix is a 4x4 matrix which stores the projection aspect of your camera. There are multiple projection types which I don't want to go into detail... you are interested in perspective projection. So your camera has a frustum formed like a truncated pyramid.The tip of the pyramid is inside your camera. The foot of the pyramid is where you look to. Everything within the pyramid is visible, everything outside the pyramid is NOT visible. Information like field of viewaspect rationear clipping plane and far clipping plane is encoded into that matrix. As long as these parameters don't change, you can reuse this matrix.
3. modelMatrix: This matrix is a 4x4 matrix which stores information about the position, scaling, sheering and orientation of your model. So when having a model with tons of vertices, you don't need to move all vertices around. This matrix is doing it for you. When multiplying a vertex to this matrix, it will be transformed to world space.

When having a point (vertex) in 3D space, you can store it as a 4D vector by adding the number (1) to the 4th dimension. Like this: vector4d(positionX, positionY, positionZ, 1). This is called the homogeneous coordinate

As you noticed, we have 4x4 matrices and a 4 component vector. So we can multiply them to receive another 4D vector. You can do it this way:

$$vertexWorldSpace = modelMatrix * vertex4D$$ this transforms your vertex from model space to world space.

$$vertexViewSpace = viewMatrix * vertexWorldSpace$$ This transforms your vertex from world coordinate space to view space.

$$vertexScreenSpace = projectionMatrix * vertexViewSpace$$ This transforms your view space coordinate to a screen space coordinate. usually you have to divide the result by its 4th component, but OpenGL and direct3D is doing this for you when passing the position from vertex to pixel/fragment shader.

Now you can write this multiplications into one equation:

$$vertexScreenSpace = projectionMatrix * viewMatrix * modelMatrix * vertex4D$$

And of cause all matrices can be combined to one matrix

$$modelViewProjectionMatrix = projectionMatrix * viewMatrix * modelMatrix$$

This modelViewProjectionMatrix can be loaded to the GPU and each vertex within that model can be multiplied by this matrix to receive the screen coordinate.

$$vertexScreenSpace = modelViewProjectionMatrix * vertex4D$$

Which matrices will change over time?

• As long as your camera parameters don't change, the projection matrix will not change. Camera position and orientation are NOT stored inside the projection matrix! But there is a near and a far clipping plane which is stored inside the projection matrix. everything in front of the near clipping plane and everything behind the far clipping plane will be clipped! So it is NOT visible. Have that in mind.
• The viewMatrix will only change, when moving or rotating the camera.
• The modelMatrix will change, when moving, rotating, scaling or sheering the model.

Matrix multiplications should be avoided as mush as you can on GPU. That means, do not move all these matrices to the GPU to multiply them there... You can do the multiplication on CPU to receive the modelViewProjectionMatrix. Then store this matrix to the GPU to do only one matrix multiplication per vertex. Rule of thump: Combine the parts of the matrices to one matrix which will be equal to all vertices in the render call. Have in mind: Matrix multiplications are NOT cummutative. So A * B is not B * A!

There is a very nice OpenGL tutorial about the matrix calculus in computer graphics OpenGL matrix tutorial. Be aware, that direct3D and vulkan uses different coordinates... So in case of using direct3D or vulkan please use another tutorial. But in principle it works the same way.

In your case the compute shader should calculate each stars new position(world space). Afterwards you can render the result by using vertex shader to apply the modelViewProjectionMatrix and pixel/fragment shader to color the result.

• I don't think that you need the modelMatrix for your case. I would implement it in this way: A star has only one vertex. This vertex is a position in world space. So a transformation from model space to world space is not needed. Your compute shader reads this one vertex/position and calculates the next position of that star. Then your rendering call can be used via instanced rendering of a quad (4 vertices (2 triangles) formed as a quad) this is called billboard. there you can put a glare texture inside. The pixel shader can change the color and you receive these star looking geometry Mar 8 at 14:39