POV-Ray : Newsgroups : povray.advanced-users : Calculating normals and perturbing them. Server Time
28 Nov 2025 04:06:18 EST (-0500)
  Calculating normals and perturbing them. (Message 1 to 4 of 4)  
From: Bald Eagle
Subject: Calculating normals and perturbing them.
Date: 26 Nov 2025 09:55:00
Message: <web.6927144c88506bcb9839f6ab25979125@news.povray.org>
I've been digging down to the foundations of computing normals, and I was
re-reading the following threads with fresh eyes.

https://news.povray.org/povray.binaries.images/thread/%3C609265e2%40news.povray.org%3E/?mtop=434022

http://news.povray.org/povray.binaries.images/thread/%3C5ff4b8da%241%40news.povray.org%3E/

https://iquilezles.org/articles/normalsSDF/
(Original pouet.net link: https://www.pouet.net/topic.php?which=5604 )

This thread recently got updated due to my inquiries, with no explanations, thus
the pyramid vectors straight from POV-Ray's normals.cpp

https://rodolphe-vaillant.fr/entry/87/normal-to-an-implicit-surface

I was also trying to re-code an old scene where I was initially trying to
replicate source-code methods in SDL, and I believe I was trying to do what
Cousin Ricky was doing - which is trying to use a scalar value to create a
"normal".

A surface normal is, by definition, a vector perpendicular to the surface at
that point.   It's a vector, not a scalar, so using the scalar result from an
SDL function as a normal really doesn't make any sense.

I believe one would need to use a spline function, a pigment function, or some
other vector-valued function to properly use a function {} in a normal statement
to get meaningful if not proper results.

Perhaps what Richard is seeing is the result of vector promotion or some other
under-the-hood modification of the scalar function, and that' why the raytraced
result is wrong.

I myself am currently trying to unravel the requirements and limitations of
trying to create normals in sdf using the pyramid vector method.

All I can say is that there is no inherent bias introduced by the pyramid vector
method, since everything cancels out to give straightforward central differences
in any orientation.

- BE


Post a reply to this message

From: Cousin Ricky
Subject: Re: Calculating normals and perturbing them.
Date: 26 Nov 2025 15:50:54
Message: <6927682e$1@news.povray.org>
On 2025-11-26 10:53 (-4), Bald Eagle wrote:
> 
> I was also trying to re-code an old scene where I was initially trying to
> replicate source-code methods in SDL, and I believe I was trying to do what
> Cousin Ricky was doing - which is trying to use a scalar value to create a
> "normal".
> 
> A surface normal is, by definition, a vector perpendicular to the surface at
> that point.   It's a vector, not a scalar, so using the scalar result from an
> SDL function as a normal really doesn't make any sense.

So far as I understand, the scalar is an offset from the surface, and as
such, a vector perpendicular to the unperturbed surface is implied.
POV-Ray then computes a normal vector by comparing adjacent
perturbations, "adjacent" being defined by the 'accuracy' attribute.

I haven't examined the source code, so I could be wrong, but I would
expect "adjacent" samples to be free from bias, at least from opposite
directions.

> I believe one would need to use a spline function, a pigment function, or some
> other vector-valued function to properly use a function {} in a normal statement
> to get meaningful if not proper results.

This actually sounds like an excellent way to *introduce* directional
bias if you're not careful with the function.  ISTR some of POV-Ray's
built-in normals already have that problem.


Post a reply to this message

From: Bald Eagle
Subject: Re: Calculating normals and perturbing them.
Date: 26 Nov 2025 18:15:00
Message: <web.6927891629c138871f9dae3025979125@news.povray.org>
Cousin Ricky <ric### [at] yahoocom> wrote:

> So far as I understand, the scalar is an offset from the surface, and as
> such, a vector perpendicular to the unperturbed surface is implied.

I mean, that sounds great.
But as far as I am presently aware, POV-Ray enters the Perturb_Normal() function
in normal.cpp with no geometric information other than the "EPoint".
So in what direction do we offset from that point?  The normal?
I believe that the whole reason for using the pyramid vectors is to compute the
normal in the first place so that it can then be perturbed by a vector-valued
function of amplitude bump_size or scale <x, y, z>.

I could very well be missing some key point, which is why I've been digging into
this when I have the time / remember to.

> POV-Ray then computes a normal vector by comparing adjacent
> perturbations, "adjacent" being defined by the 'accuracy' attribute.

It uses the pyramid vectors to define the sampling points offset from the EPoint
by a value "Delta" which Nathan Kopp hard-codes to 0.02.

> I haven't examined the source code, so I could be wrong, but I would
> expect "adjacent" samples to be free from bias, at least from opposite
> directions.

Yes, it's taken me some time, but I believe that the tetrahedral arrangement of
the sample points ensures exactly that, no matter the orientation.

> > I believe one would need to use a spline function, a pigment function, or some
> > other vector-valued function to properly use a function {} in a normal statement
> > to get meaningful if not proper results.
>
> This actually sounds like an excellent way to *introduce* directional
> bias if you're not careful with the function.  ISTR some of POV-Ray's
> built-in normals already have that problem.

But directional bias is exactly how we are creating the normal pattern to begin
with.  If we just multiplied all of the pyramid vectors by a scalar, they would
all cancel out.  Because there's no inherent directional bias.   In order to
"apply a normal" to a surface, we have to introduce some sort of _directional
bias_ in the form of a vector.

That vector comes from the normal pattern functions, and so I think that in
order to create a real and meaningful normal pattern like we think of them, it
needs to be a 3-dimensional vector.  Otherwise, I think that "1" gets
transformed to <1, 1, 1>, "-1" gets transformed to <-1, -1, -1>, etc.

I may or may not get the opportunity to try this, but it would be useful to plot
expected normal scalar values as an overlay, and also render the potential
vector-promoted scalar values to see if they give the same results.

Definitely a lot to be learned here, on several fronts.

- BW


Post a reply to this message

From: Bald Eagle
Subject: Re: Calculating normals and perturbing them.
Date: 26 Nov 2025 19:45:00
Message: <web.69279ed729c138871f9dae3025979125@news.povray.org>
"Bald Eagle" <cre### [at] netscapenet> wrote:

> I may or may not get the opportunity to try this, but it would be useful to plot
> expected normal scalar values as an overlay, and also render the potential
> vector-promoted scalar values to see if they give the same results.

Well I did.  And it was as (unnecessarily) annoying, confusing, frustrating, and
difficult as these things always are.

I'm very interested in what the/your expected result is.

(very cool version statement - you have a lot of clever little things in your
scenes)


#version max (3.5, min (3.8, version));

global_settings { assumed_gamma 1 }

light_source { <(frame_number = 2? 1: -1), 1, -1.4> * 1000, rgb 1 }

camera
{ location -6 * z
   right x
   angle 30
}

#declare OriginalFunction =
function
{ max
 ( select (x - 1, 1, select (x - 2, 0, 1)),
   select (y - 1, 1, select (y - 2, 0, 1))
 )
}

#declare Pattern = function {
 spline {
  0, <0, 0, 0>,
  1, <1, 1 ,1>
 }
}

#declare VectorValues =
normal {
 average
 normal_map {
  [function { Pattern (OriginalFunction(x, y, z)).red}]
  [function { Pattern (OriginalFunction(x, y, z)).green}]
  [function { Pattern (OriginalFunction(x, y, z)).blue}]
 }
}


#declare Function = 1;

#switch (Function)

 #case (0)
  box
  { 0, 3
     pigment { rgb <1, 0.5, 0.5> }
     normal
     {
      function {OriginalFunction (x, y, z)}
       accuracy 0.1
     }
     translate <-1.5, -1.5, 0>
  }
 #break

 #case (1)
  box
  { 0, 3
     pigment { rgb <1, 0.5, 0.5> }
     normal
     {
      VectorValues
       accuracy 0.1
     }
     translate <-1.5, -1.5, 0>
  }
 #break

#end

#declare Ext = 5;
#for (Y, -Ext, Ext)
 #for (X, -Ext, Ext)
  text { ttf "arial.ttf", str (OriginalFunction (X, Y, 0), 0, 0), 0.02, 0.0
translate <-0.25, -0.25, 0> scale 0.5 translate <X, Y, -0.01> pigment {rgb
z*0.5} no_shadow}
 #end
#end


Post a reply to this message


Attachments:
Download 'cousinricky_normals.png' (19 KB)

Preview of image 'cousinricky_normals.png'
cousinricky_normals.png


 

Copyright 2003-2023 Persistence of Vision Raytracer Pty. Ltd.