# Simulating neon: Find angle between two vectors with shader nodes

I am attempting to simulate neon inside a glass tube (a real neon light and not those florescent ones), and am having difficulty making the innermost portion of gas look brighter than the outermost portion. This is real neon. You can see that near the sides of the tube, the light is dimmer. That is not an illusion caused by glare, although the glare amplifies it. I have seen it up close, though, and inside the tube it does fade toward the edge.

…and this is what I have (yes it’s too dim, but I want it on low power for now). The neon is just plain Jane emission volumetric, and it of course does not fade near the side of the tube like real neon does: the edge is hard, as if I stuffed the tube with glowing paint instead of gas. There is a bit of fading, but that’s done with compositing via glare, and isn’t realistic enough for me.

The structure of this is two identical curves, both beveled, one larger than the other, and that larger one has a solidify modifier on it: A properly walled glass pipe is around a solid tube of neon, with some margin in between (that vacuum margin is not there in real life, but the neon probably won’t glow there anyway).

I have decided that if I can compare the incoming camera ray to the normal of the neon "gas" tube (not the glass), I can use that to control the emission strength: The greater the difference/angle between them, the dimmer the neon should be. This may not work on the flipside of the neon as the ray exits...

This solution probably comes down to a dot product, but there may be other solutions. Leave your suggestions in the comments! :-)

• Hi :). Could you please add an image to illustrate the problem and your current node setup? For those of us with bad imagination. Thanks :). Sep 21, 2021 at 18:01
• Cheers for that ;) Sep 21, 2021 at 18:03
• @JachymMichal I tried to clarify, but the neon sign I saw in real life was in the daytime, and of course not many people on the Internet photograph neon in the daytime. Sep 21, 2021 at 18:17
• I think the angle you want is given by arccos( (a \dot b) / (||a|| ||b||) ), based on solving the geometric defn of the dot product (en.wikipedia.org/wiki/Dot_product#Geometric_definition) for the angle between the two vectors? Sep 21, 2021 at 18:46
• Try using a Fresnel node and skip the math? Sep 21, 2021 at 18:49

Much the same method as yours.. A geometry-bevelled curve for the glass, with a Solidify modifier to make it a tube. A duplicate of the curve, with a smaller bevel, for the gas.

The glass is just a standard, colourless, almost 0-Roughness, almost 1-Transmission principled BSDF.

The gas uses a the 'Facing' output of a Layer Weight node to make the mask shown on the left, below, to mix between a Transparent and Emission shaders as shown on the right.

..but it still takes a fair bit of tweaking to make it feel 'gaseous'..

A Fresnel node like this maybe:

• I am not sure the index of refraction is quite right, but it’s a good starting point for me :-) thank you. Sep 21, 2021 at 19:03
• As I look at it more an emission shader might be better than a principled bsdf for the gas Sep 21, 2021 at 19:13
• This is the solution I’m working with for now. In the future, I may just use two volumetric tubes, but I’m not sure. Sep 23, 2021 at 17:26

Although I think the Fresnel and Layer Weight solutions are probably the natural way to achieve this in Blender, I thought @TheLabCat's original intuition was interesting enough to want to try to produce directly in shader nodes:

I have decided that if I can compare the incoming camera ray to the normal of the neon "gas" tube (not the glass), I can use that to control the emission strength: The greater the difference/angle between them, the dimmer the neon should be. This may not work on the flipside of the neon as the ray exits...

First, here's an example of how this turned out in my quick test:

And, second, here's the primary node system for computing the inner tube's shading, based on direct calculation of the angle between the incoming camera ray and the normal as arccos( [c \dot n] / [ ||c|| ||n|| ] ), where c is the camera ray, n the normal, arccos the usual inverse of the usual trigonometric cos function, and || || is the Euclidean norm (corresponding to Blender's Length operation in Vector Math nodes):

The Frame labels explain what each of the major operations here is doing. The arccos expression used is just the result of solving the "geometric definition" (which is the equation a \dot b = ||a|| ||b|| cos(theta), for theta the angle formed between the vectors a, b) of the usual vector dot product for the angle between the two vectors. The definition can be found on wikipedia, for example, here, as of the time of this answer: https://en.wikipedia.org/wiki/Dot_product#Geometric_definition

Maybe I should also add that the Map Range in the right-most Frame is used to convert from the range of arccos (which is [0, pi], in Blender's implementation) to [0,1] (which is more useful for using as a mix factor), and that the Vector Transform from Camera to World coordinates in the left-most Frame is really, really important (took me about thirty minutes to realize not having it is was why nothing was making sense visually!).

I'll note that I didn't put any real effort into the outer tube's "glass" material; I've just used a mix of a Glass BSDF and Transparent BSDF here. Improving it might improve the result. Also, neither of these cylinders is beveled; I concentrated on just the materials.

• Nice. +1. I do wonder though, whether the expensive arccos is worth it, since the result goes through a fairly arbitrary Color Ramp anyway, and the whole remapping from dot-product could be done in there? Maybe I'm missing something.. Sep 22, 2021 at 10:22
• I think that's a good point; arccos is only really needed if you want to think in rads or in degrees. It also ensures you know the range of inputs to pass into the Map Range node, but I think (c \dot n) / (||c|| ||n||) should already live in [-1,1], so you could probably optimize it a bit by deleting the middle Frame and entirely and connecting the first Frame output directly to the Color Ramp, like you're saying, I think Sep 22, 2021 at 12:10
• Also.. the View Vector is in Camera space, where the Geometry > Normal is in World space, so you would expect the effect to be view-dependent.. Sep 22, 2021 at 14:11
• Yeah, definitely view-dependent; the View Vector is converted into World space so it isn't expressed in the basis used in Camera space, but the effect will still vary as the view is moved around, of course. I think that's a property it shares with Fresnel (just from playing around with the Fresnel node a bit in Blender), and with Facing too (the 'light' regions seem to turn towards me as I rotate around a target object)? If imposing the effect from a fixed angle were desired, it would be easy enough to replace the View Vector with some user-specified vector of choice, though Sep 23, 2021 at 0:00
• Ahhh.. so sorry, missed your conversion! (I've just slapped my own wrist.) For both of us, since the object is to keep the illusion of a brightness in the middle of a volume, it's lucky that neon tubes are cylindrical :D Sep 23, 2021 at 6:48