<p>Here are a few steps to (hopefully) help get you going:</p>
<p>Rendering the cube:
For the geometry a simple cube is needed, so really the first step is just to get a cube rendering just like you would expect, I suggest placing the cube at the origin in both world and object space.</p>
<p>After you are happy with the way the cube is rendered, change the winding direction for triangles the easiest way to do that is just a quick call to the API. So if the API is set to render counter clockwise as the front face, just change it so the front face is clockwise. This makes the &quot;interior&quot; of the cube visible.</p>
<p>At this point you aren't actually trying to render the halo, just the interior of the cube. So the fragment shader should just return a color. Once that is all set up and you are happy with it, the next step will be the actual rendering of halo's and is where you would call the halo functions in the fragment shader.</p>
<p>Thinking about the object spaces:
The cube itself is in &quot;object space&quot; normally there is a Model to World matrix that we multiply the cube by that puts it in world space. When the book refers to &quot;object space&quot; it is referring to the cube's object space. (I think that book actually covers object/world spaces pretty well you may want to look up the chapter that goes over this to help clarify the subject)</p>
<p>No fog is needed, a halo will render correctly without any special medium.</p>
<p>Alpha blending will need to be enabled for the final halo, but you can skip that until the basic outline of the halo is rendering correctly.</p>
<p>Edit: Here is some code to help with cameraPosition and cameraView</p>
<pre><code>mat4 Camera2World = inverse(view_matrix); // The view matrix 
mat4 World2Object = inverse(object_matrix); // The object2world matrix
vec3 cameraPosition = (World2Object * Camara2World[3]).xyz;
vec3 cameraView = (World2Object * Camera2World[2]).xyz;
</code></pre>
<p>Note that this is using column major matrices.</p>
<p>This isn't the most efficient code, but hopefully it will help get you going.</p>

CC BY-SA 4.0
2021-10-13T20:40:42.680Z
Thank you so much for the explanation! After writing this post I did come to the conclusion that I was supposed to render a box "inside out" but the "inside out" part I wasn't sure what was meant, so your advice to flip the normals clarified that. I am also additively blending.

Now at this point I have the box rendering at world position (0,0,0) with the normals flipped. I am using the camera view and position in world space for now just to be safe and since the box is at (0, 0, 0) I should be fine.  (continued)
mbl
CC BY-SA 4.0
2021-10-13T20:40:47.073Z
Now the last question I have is, what value am I supposed to pass to `pobject`? From the book explanation, it sounds like I need to pass the current pixel position of the pixel that the box shader is currently rendering. But reading the comment above the description of the function it says: "This shader renders a halo effect of radius R at the object-space position pobject." so it makes it sound like in my case, I should pass (0,0,0). I tried both and neither give the result of rendering the basic shape of the halo.
mbl
CC BY-SA 4.0
2021-10-13T20:52:36.830Z
Also I should mention, if I pass (0,0,0) to `pobject`, the result I get seem to be correct in the case where the camera is inside the radius `R`. If the camera steps outside of this radius, nothing is rendered.
mbl
CC BY-SA 4.0
2021-10-13T21:30:53.353Z
pobject is the position values of the object which is the cube that is being rendered. So compute gl_Position using the mvp matrix and pass the the pobject values down to the fragment shader.
pmw1234
CC BY-SA 4.0
2021-10-14T05:11:21.263Z
I used the pixel world position and it seems to (almost) work! I updated my post with the details.
mbl
CC BY-SA 4.0
2021-10-14T12:30:51.410Z
This is usually related to the geometry of the cube being obscured in some way. Halo's need depth testing disabled to render correctly. Another issue is related to the placement of the halo within the cube, be careful to make sure the cube is centered on the origin rather then being offset from it. Often a cube has only 1 corner touching the origin, this would cause the halo to appear and disappear because it ofset from the center of the cube.
pmw1234
CC BY-SA 4.0
2021-10-14T20:18:13.113Z
Turns out I'm pretty sure there's a bug in the book. If I normalize `vdir` at the line `float t0 = 1.f + depth / dot(cam_view, vdir);` it all works.
mbl
CC BY-SA 4.0
2021-10-14T20:59:49.197Z
I couldn't find an issue in your code, but, the magnitude of vdir represents the view distance along the ray passing through the halo, by normalizing it that distance is being  forced to a value of 1 which would work in some cases but not others.
pmw1234
CC BY-SA 4.0
2021-10-14T22:37:47.227Z
Yea I spoke too soon there. The equation in the book clearly doesn't normalize `vdir` in order to get `t0`. If you normalize it the results are wrong, because a length of 1 is too short, and it scales down the depth value too much, which means the halo looks too opaque. But somehow I have to do that in order to see the halo from any distance in my app. Something must be wrong with my data but I can't tell what it might be. For some reason the camera has to be at a distance of about 1 for the halo to show up.
mbl
CC BY-SA 4.0
2021-10-15T08:36:05.807Z
Is it possible that I have to linearize my depth buffer? I am getting values for ‘t0’ that are too high and I am confident that all my inputs are correct. Really running out of ideas :(, but at least I understand the algorithm much better now
mbl
CC BY-SA 4.0
2021-10-15T09:37:31.753Z
Yes, this algorithm requires linear depths.
pmw1234
CC BY-SA 4.0
2021-10-15T21:46:43.560Z
Finally it all comes together :) I definitely learned a lot from implementing this and I probably would have gave up without your help. Thank you!!!
mbl
CC BY-SA 4.0
2021-10-13T13:19:17.077Z
2021-10-13T13:32:08.247Z
2021-10-13T13:32:08.247Z
pmw1234
<p>My goal is to render a volumetric point light as explained in this book's chapter 10: <a href="https://www.amazon.ca/Foundations-Game-Engine-Development-Rendering/dp/0985811757/ref=sr_1_2?crid=2U5PN7NJQ31S7&amp;dchild=1&amp;keywords=foundations%20of%20game%20engine%20development&amp;qid=1634091891&amp;sprefix=foundations%20of%20game%20engine%20deve%2Caps%2C174&amp;sr=8-2" rel="nofollow noreferrer">FGED Rendering</a>
The book explains the math pretty well, but I'm not sure I understand what is the required setup for a volumetric point light halo to be visible. The book describes the following code snippet to calculate the brightness of the volumetric halo:</p>
<pre><code>float R = 5.f; // Radius of the volumetric point light halo.
float R2 = R * R;
float recipR2 = 1.f / R2;
float recip3R2 = 1.f / (3.f * R2);
float normalizer = 3.f / (4.f * R);

// This shader renders a halo effect of radius R at the object-space position pobject.
// The object-space camera position c and view direction z are given by cameraPosition 
// and cameraView. The pixelCoord parameter specifies the viewport coordinates of the
// pixel being processed, and it is used to read from the depth buffer.
float CalculateHaloBrightness(float3 pobject, float2 pixelCoord)
{
    float3 vdir = cameraPosition - pobject;
    float v2 = dot(vdir, vdir);
    float p2 = dot(pobject, pobject);
    float pv = -dot(pobject, vdir);
    float m = sqrt(max(pv * pv - v2 * (p2 - R2), 0.0));

     // Read z0 from the depth buffer.
    float2 depth = texture(depthBuffer, pixelCoord);
    float t0 = 1.0 + depth / dot(cameraView, vdir);

     // Calculate clamped limits of integration.
    float t1 = clamp((pv - m) / v2, t0, 1.0);
    float t2 = clamp((pv + m) / v2, t0, 1.0);
    float u1 = t1 * t1;
    float u2 = t2 * t2;

     // Evaluate density integral, normalize, and square.
    float B = ((1.0 - p2 * recipR2) * (t2 - t1) + pv * recipR2 * (u2 - u1) - v2 * recip3R2 * (t2 * u2 - t1 * u1)) * normalizer;
    return (B * B * v2);
}

</code></pre>
<p>There's a few things that I don't understand:</p>
<ol>
<li><p>What is meant exactly by <code>pobject</code>, <code>cameraPosition</code> and <code>cameraView</code> being in object-space? Does it just mean that they all have to be in world-space for this function to work correctly?</p>
</li>
<li><p>On what surface am I supposed to execute this pixel shader on? From what I read, it sounds like it has to be run on a cube centered at <code>pobject</code> but I'm not sure.</p>
</li>
<li><p>Is a light scattering medium required, such as fog? It would make sense that it does, but it's not really mentioned in the book's explanation.</p>
</li>
</ol>
<p>I realize this is a difficult question to answer, since I haven't included the entire explanation present in the book here. I assume the author would not like that. So I am hoping that maybe somebody here might roughly understand what's going on and point me in the right direction.</p>
<p>edit:
I am almost there! Here's my current HLSL implementation:</p>
<pre><code>ConstantBuffer&lt;pass_data&gt; cb_pass : register(b0);
Texture2D&lt;float&gt; Depth : register(t3);
SamplerState linear_wrap_sampler : register(s0);

float4 ps_main(vertex_out pin) : SV_Target
{
    float2 uv = pin.posh.xy / cb_pass.sceen_size;
    float depth = Depth.SampleLevel(linear_wrap_sampler, uv, 0.f);

    // Volumetric halo variables.
    float halo = 0.f;
    float halo_radius = cb_pass.radius;
    float halo_radius2 = halo_radius * halo_radius;
    float rcp_halo_radius2 = 1.f / halo_radius2;
    float rcp_3halo_radius2 = 1.f / (3.f * halo_radius2);
    float density_integral_normalizer = 3.f / (4.f * halo_radius);

    // Render volumetric halo.
    float4 world_space_pixel = float4(pin.posw, 1.f);
    float3 pobject = float3(mul(world_space_pixel, cb_pass.world_to_object).xyz);
    float3 cam_pos = mul(float4(cb_pass.eye_pos, 1.f), cb_pass.world_to_object).xyz;
    float3 vdir = cam_pos - pobject;
    float v2 = dot(vdir, vdir);
    float p2 = dot(pobject, pobject);
    float pv = -dot(pobject, vdir);
    float m = sqrt(max(pv * pv - v2 * (p2 - halo_radius2), 0.f));

    float3 cam_view = mul(float4(cb_pass.eye_forward, 0.f), cb_pass.world_to_object).xyz;
    float t0 = 1.f + depth / dot(cam_view, vdir);
    float t1 = clamp((pv - m) / v2, t0, 1.f);
    float t2 = clamp((pv + m) / v2, t0, 1.f);
    float u1 = t1 * t1;
    float u2 = t2 * t2;

    float B = ((1.f - p2 * rcp_halo_radius2) * (t2 - t1) + pv * rcp_halo_radius2 * (u2 - u1) - v2 * rcp_3halo_radius2 * (t2 * u2 - t1 * u1)) * density_integral_normalizer;
    halo = (B * B * v2);

    return float4(halo, halo, halo, 1.f);
}

</code></pre>
<p>The only problem remaining is that the halo only appears when the camera gets very close to the halo. When the camera is close enough it does show up correctly though. I noticed that scaling <code>cb_pass.eye_forward</code> will make the halo show up from further away but I'm not sure what is causing this.</p>

<opengl><rendering><lighting><volumetric>
Trying to understand how to render a volumetric point light
mbl
# Trying to understand how to render a volumetric point light

(Asked by: username_0 on 2021-10-13)

My goal is to render a volumetric point light as explained in this book's chapter 10: [FGED Rendering](https://www.amazon.ca/Foundations-Game-Engine-Development-Rendering/dp/0985811757/ref=sr_1_2?crid=2U5PN7NJQ31S7&dchild=1&keywords=foundations%20of%20game%20engine%20development&qid=1634091891&sprefix=foundations%20of%20game%20engine%20deve%2Caps%2C174&sr=8-2)
The book explains the math pretty well, but I'm not sure I understand what is the required setup for a volumetric point light halo to be visible. The book describes the following code snippet to calculate the brightness of the volumetric halo:

```
float R = 5.f; // Radius of the volumetric point light halo.
float R2 = R * R;
float recipR2 = 1.f / R2;
float recip3R2 = 1.f / (3.f * R2);
float normalizer = 3.f / (4.f * R);

// This shader renders a halo effect of radius R at the object-space position pobject.
// The object-space camera position c and view direction z are given by cameraPosition 
// and cameraView. The pixelCoord parameter specifies the viewport coordinates of the
// pixel being processed, and it is used to read from the depth buffer.
float CalculateHaloBrightness(float3 pobject, float2 pixelCoord)
{
    float3 vdir = cameraPosition - pobject;
    float v2 = dot(vdir, vdir);
    float p2 = dot(pobject, pobject);
    float pv = -dot(pobject, vdir);
    float m = sqrt(max(pv * pv - v2 * (p2 - R2), 0.0));

     // Read z0 from the depth buffer.
    float2 depth = texture(depthBuffer, pixelCoord);
    float t0 = 1.0 + depth / dot(cameraView, vdir);

     // Calculate clamped limits of integration.
    float t1 = clamp((pv - m) / v2, t0, 1.0);
    float t2 = clamp((pv + m) / v2, t0, 1.0);
    float u1 = t1 * t1;
    float u2 = t2 * t2;

     // Evaluate density integral, normalize, and square.
    float B = ((1.0 - p2 * recipR2) * (t2 - t1) + pv * recipR2 * (u2 - u1) - v2 * recip3R2 * (t2 * u2 - t1 * u1)) * normalizer;
    return (B * B * v2);
}

```

There's a few things that I don't understand:

1. What is meant exactly by `pobject`, `cameraPosition` and `cameraView` being in object-space? Does it just mean that they all have to be in world-space for this function to work correctly?
2. On what surface am I supposed to execute this pixel shader on? From what I read, it sounds like it has to be run on a cube centered at `pobject` but I'm not sure.
3. Is a light scattering medium required, such as fog? It would make sense that it does, but it's not really mentioned in the book's explanation.

I realize this is a difficult question to answer, since I haven't included the entire explanation present in the book here. I assume the author would not like that. So I am hoping that maybe somebody here might roughly understand what's going on and point me in the right direction.

edit:
I am almost there! Here's my current HLSL implementation:

```
ConstantBuffer<pass_data> cb_pass : register(b0);
Texture2D<float> Depth : register(t3);
SamplerState linear_wrap_sampler : register(s0);

float4 ps_main(vertex_out pin) : SV_Target
{
    float2 uv = pin.posh.xy / cb_pass.sceen_size;
    float depth = Depth.SampleLevel(linear_wrap_sampler, uv, 0.f);

    // Volumetric halo variables.
    float halo = 0.f;
    float halo_radius = cb_pass.radius;
    float halo_radius2 = halo_radius * halo_radius;
    float rcp_halo_radius2 = 1.f / halo_radius2;
    float rcp_3halo_radius2 = 1.f / (3.f * halo_radius2);
    float density_integral_normalizer = 3.f / (4.f * halo_radius);

    // Render volumetric halo.
    float4 world_space_pixel = float4(pin.posw, 1.f);
    float3 pobject = float3(mul(world_space_pixel, cb_pass.world_to_object).xyz);
    float3 cam_pos = mul(float4(cb_pass.eye_pos, 1.f), cb_pass.world_to_object).xyz;
    float3 vdir = cam_pos - pobject;
    float v2 = dot(vdir, vdir);
    float p2 = dot(pobject, pobject);
    float pv = -dot(pobject, vdir);
    float m = sqrt(max(pv * pv - v2 * (p2 - halo_radius2), 0.f));

    float3 cam_view = mul(float4(cb_pass.eye_forward, 0.f), cb_pass.world_to_object).xyz;
    float t0 = 1.f + depth / dot(cam_view, vdir);
    float t1 = clamp((pv - m) / v2, t0, 1.f);
    float t2 = clamp((pv + m) / v2, t0, 1.f);
    float u1 = t1 * t1;
    float u2 = t2 * t2;

    float B = ((1.f - p2 * rcp_halo_radius2) * (t2 - t1) + pv * rcp_halo_radius2 * (u2 - u1) - v2 * rcp_3halo_radius2 * (t2 * u2 - t1 * u1)) * density_integral_normalizer;
    halo = (B * B * v2);

    return float4(halo, halo, halo, 1.f);
}

```

The only problem remaining is that the halo only appears when the camera gets very close to the halo. When the camera is close enough it does show up correctly though. I noticed that scaling `cb_pass.eye_forward` will make the halo show up from further away but I'm not sure what is causing this.

**Tags:** opengl, rendering, lighting, volumetric


---

## 1 answers

### Answer by username_1 on 2021-10-13. Score: 1 | Accepted answer

Here are a few steps to (hopefully) help get you going:

Rendering the cube:
For the geometry a simple cube is needed, so really the first step is just to get a cube rendering just like you would expect, I suggest placing the cube at the origin in both world and object space.

After you are happy with the way the cube is rendered, change the winding direction for triangles the easiest way to do that is just a quick call to the API. So if the API is set to render counter clockwise as the front face, just change it so the front face is clockwise. This makes the "interior" of the cube visible.

At this point you aren't actually trying to render the halo, just the interior of the cube. So the fragment shader should just return a color. Once that is all set up and you are happy with it, the next step will be the actual rendering of halo's and is where you would call the halo functions in the fragment shader.

Thinking about the object spaces:
The cube itself is in "object space" normally there is a Model to World matrix that we multiply the cube by that puts it in world space. When the book refers to "object space" it is referring to the cube's object space. (I think that book actually covers object/world spaces pretty well you may want to look up the chapter that goes over this to help clarify the subject)

No fog is needed, a halo will render correctly without any special medium.

Alpha blending will need to be enabled for the final halo, but you can skip that until the basic outline of the halo is rendering correctly.

Edit: Here is some code to help with cameraPosition and cameraView

```
mat4 Camera2World = inverse(view_matrix); // The view matrix 
mat4 World2Object = inverse(object_matrix); // The object2world matrix
vec3 cameraPosition = (World2Object * Camara2World[3]).xyz;
vec3 cameraView = (World2Object * Camera2World[2]).xyz;

```

Note that this is using column major matrices.

This isn't the most efficient code, but hopefully it will help get you going.

> **Comments:**
> **username_0**: Thank you so much for the explanation! After writing this post I did come to the conclusion that I was supposed to render a box "inside out" but the "inside out" part I wasn't sure what was meant, so your advice to flip the normals clarified that. I am also additively blending.  Now at this point I have the box rendering at world position (0,0,0) with the normals flipped. I am using the camera view and position in world space for now just to be safe and since the box is at (0, 0, 0) I should be fine.  (continued)  
> **username_0**: Now the last question I have is, what value am I supposed to pass to `pobject`? From the book explanation, it sounds like I need to pass the current pixel position of the pixel that the box shader is currently rendering. But reading the comment above the description of the function it says: "This shader renders a halo effect of radius R at the object-space position pobject." so it makes it sound like in my case, I should pass (0,0,0). I tried both and neither give the result of rendering the basic shape of the halo.  
> **username_0**: Also I should mention, if I pass (0,0,0) to `pobject`, the result I get seem to be correct in the case where the camera is inside the radius `R`. If the camera steps outside of this radius, nothing is rendered.  
> **username_1**: pobject is the position values of the object which is the cube that is being rendered. So compute gl_Position using the mvp matrix and pass the the pobject values down to the fragment shader.  
> **username_0**: I used the pixel world position and it seems to (almost) work! I updated my post with the details.  
> **username_1**: This is usually related to the geometry of the cube being obscured in some way. Halo's need depth testing disabled to render correctly. Another issue is related to the placement of the halo within the cube, be careful to make sure the cube is centered on the origin rather then being offset from it. Often a cube has only 1 corner touching the origin, this would cause the halo to appear and disappear because it ofset from the center of the cube.  
> **username_0**: Turns out I'm pretty sure there's a bug in the book. If I normalize `vdir` at the line `float t0 = 1.f + depth / dot(cam_view, vdir);` it all works.  
> **username_1**: I couldn't find an issue in your code, but, the magnitude of vdir represents the view distance along the ray passing through the halo, by normalizing it that distance is being  forced to a value of 1 which would work in some cases but not others.  
> **username_0**: Yea I spoke too soon there. The equation in the book clearly doesn't normalize `vdir` in order to get `t0`. If you normalize it the results are wrong, because a length of 1 is too short, and it scales down the depth value too much, which means the halo looks too opaque. But somehow I have to do that in order to see the halo from any distance in my app. Something must be wrong with my data but I can't tell what it might be. For some reason the camera has to be at a distance of about 1 for the halo to show up.  
> **username_0**: Is it possible that I have to linearize my depth buffer? I am getting values for ‘t0’ that are too high and I am confident that all my inputs are correct. Really running out of ideas :(, but at least I understand the algorithm much better now  
> **username_1**: Yes, this algorithm requires linear depths.  
> **username_0**: Finally it all comes together :) I definitely learned a lot from implementing this and I probably would have gave up without your help. Thank you!!!  

---

