POV-Ray : Newsgroups : povray.object-collection : Bubble 4.0.1 Server Time
30 Oct 2025 18:00:42 EDT (-0400)
  Bubble 4.0.1 (Message 1 to 10 of 12)  
Goto Latest 10 Messages Next 2 Messages >>>
From: Cousin Ricky
Subject: Bubble 4.0.1
Date: 26 Oct 2025 10:29:31
Message: <68fe304b@news.povray.org>
The bubble contribution keeps throwing me curve balls.  Between the
opacity of the code and it not doing what the comments say it does (or
what anyone other than Charles Robertson expects it to do), I am unable
to figure out how Mr. Robertson expected this code to behave.

My intention with version 4.0 was to slow the expansion of the bubbles
as they rise.  After I uploaded it Wednesday, I was eager to try it out
on my glasses of Champagne, but I got unexpected results.  Fiddling
around with the bubbles revealed that the expansion rate varies
depending on how high the beverage is above the Cartesian origin.  This
has implications for a long-stemmed wineglass.

So I wrote a test program to try out bubble containers of various
heights and sizes, and that's when things got really weird.  The columns
of bubbles would not stay in their lanes.

The issue is a section of code that "adjusts" the column positions, and
ultimately derives from parameters eFluidX, eFluidZ, and fRadius.
Common sense would suggest that this is an attempt to deal with a
non-vertical container, but that is not what this code section does.  In
fact, I cannot figure out what the heck Mr. Robertson is trying to do here.

Version 4.0.1 suppresses that adjustment, forcing the bubbles to stay
within their container.  Unfortunately, the Champagne flute must remain
upright for the bubble columns to be positioned in any quasi-realistic
manner.

As for the bubble expansion rate, I am not an expert on fluid dynamics,
and clearly neither is Mr. Robertson.  Version 4.0 adds a global
parameter to control the expansion rate of the bubbles.  Between that
parameter and varying the height of the bubble container, one should be
able to find a combination that resembles real life carbonation.

(Why did I skip from version 2.* to version 4?  Because the initial
version of this contribution was 3.6, so I wanted to leave 3.* clear.
I'm guessing that the 3.6 was a misunderstanding of the version field of
the Object Collection upload form.)

In the attached images, Bubble_Rate = 1.00 uses the original expansion
code, and Bubble_Rate = 0.45 is the new default expansion rate.  All
containers in these images are vertical, so a tilted orientation cannot
be the reason for the "adjustments."


Post a reply to this message


Attachments:
Download 'test_bubble_sizes-old1.jpg' (164 KB) Download 'test_bubble_sizes-old2.jpg' (142 KB) Download 'test_bubble_sizes1.jpg' (169 KB) Download 'test_bubble_sizes2.jpg' (153 KB)

Preview of image 'test_bubble_sizes-old1.jpg'
test_bubble_sizes-old1.jpg

Preview of image 'test_bubble_sizes-old2.jpg'
test_bubble_sizes-old2.jpg

Preview of image 'test_bubble_sizes1.jpg'
test_bubble_sizes1.jpg

Preview of image 'test_bubble_sizes2.jpg'
test_bubble_sizes2.jpg


 

From: Cousin Ricky
Subject: Re: Bubble 4.0.1
Date: 26 Oct 2025 10:40:00
Message: <web.68fe31dbdf56db1c60e0cc3d949c357d@news.povray.org>
Bubble version 4.0.1:

  https://github.com/CousinRicky/POV-bubble/releases/tag/v4.0.1


Post a reply to this message

From: Bald Eagle
Subject: Re: Bubble 4.0.1
Date: 26 Oct 2025 13:10:00
Message: <web.68fe553fdf56db1c1f9dae3025979125@news.povray.org>
Cousin Ricky <ric### [at] yahoocom> wrote:

> The issue is a section of code that "adjusts" the column positions, and
> ultimately derives from parameters eFluidX, eFluidZ, and fRadius.
> Common sense would suggest that this is an attempt to deal with a
> non-vertical container, but that is not what this code section does.  In
> fact, I cannot figure out what the heck Mr. Robertson is trying to do here.

Since you have a vertical cylinder, then the x and z parameters are there to
keep wandering bubbles inside the cylindrical container.

It looks like he just didn't do a very good job of making the correction.
I'd try collision testing of the spheres with the wall of the cylinder before
the calculations of the next round happen, along with storing the direction of
motion of each bubble from the previous step, and a flag. If a collision will
take place, set the flag, make the bubble move toward the central axis for a
certain number of steps before releasing it from that corrective direction by
turning off the flag.

> Version 4.0.1 suppresses that adjustment, forcing the bubbles to stay
> within their container.  Unfortunately, the Champagne flute must remain
> upright for the bubble columns to be positioned in any quasi-realistic
> manner.

When I get back into "bounding cylinder" mode, I'll try to ponder bounding a
tilted champagne flute.

However, perhaps you should just use trace() to determine the starting position
of the bubble, and then when it collides with the side of the tilted flute, make
it rise along the edge until it's y-value equals the liquid's surface.

>
> As for the bubble expansion rate, I am not an expert on fluid dynamics,
> and clearly neither is Mr. Robertson.  Version 4.0 adds a global
> parameter to control the expansion rate of the bubbles.  Between that
> parameter and varying the height of the bubble container, one should be
> able to find a combination that resembles real life carbonation.

The size of a bubble is related to the pressure exerted on it.
Fluid pressure is dependent solely on the height of the column of liquid.
So you should start with a bubble of some initial size, and then adjust the
VOLUME of the bubble in a linear fashion as it rises.
P1V1 = P2V2

- BW


Post a reply to this message

From: Bald Eagle
Subject: Re: Bubble 4.0.1
Date: 26 Oct 2025 13:25:00
Message: <web.68fe595cdf56db1c1f9dae3025979125@news.povray.org>
I used one of those fancy new tools to generate some starter code.

I think creating a liquid object and then using insidedness tests would be the
way to go.  Maybe define a central axis to correct everything towards - that
would even work with a tilted container.

#macro Bubble(id, startX, startZ, startY, radius, fluidMinX, fluidMaxX,
fluidMinZ, fluidMaxZ, fluidSurfaceY)
  // Rise speed based on radius
  #local riseSpeed = radius * 2;

  // Vertical position based on clock
  #local currentY = startY + (clock * riseSpeed);
  #if (currentY > fluidSurfaceY)
    #local currentY = fluidSurfaceY;
  #end

  // Expansion as bubble rises
  #local scaleFactor = 1 + (currentY - startY) / (fluidSurfaceY - startY) * 0.2;
  #local currentRadius = radius * scaleFactor;

  // Turbulence: horizontal wobble using sine waves
  #local wobbleX = sin(clock * 10 + id) * 0.2;
  #local wobbleZ = cos(clock * 10 + id) * 0.2;

  // Apply turbulence to position
  #local posX = startX + wobbleX;
  #local posZ = startZ + wobbleZ;

  // Clamp to container bounds
  #if (posX < fluidMinX + currentRadius)
    #local posX = fluidMinX + currentRadius;
  #end
  #if (posX > fluidMaxX - currentRadius)
    #local posX = fluidMaxX - currentRadius;
  #end
  #if (posZ < fluidMinZ + currentRadius)
    #local posZ = fluidMinZ + currentRadius;
  #end
  #if (posZ > fluidMaxZ - currentRadius)
    #local posZ = fluidMaxZ - currentRadius;
  #end

  // Create the bubble
  sphere {
    <posX, currentY, posZ>, currentRadius
    texture {
      pigment { color rgbt <1, 1, 1, 0.9> }
      finish { specular 0.6 reflection 0.1 }
    }
    interior { ior 1.0 }
  }
#end



#declare fluidMinX = -2;
#declare fluidMaxX = 2;
#declare fluidMinZ = -2;
#declare fluidMaxZ = 2;
#declare fluidSurfaceY = 10;
#declare bubbleCount = 10;

#declare i = 0;
#while (i < bubbleCount)
  #declare startX = RRand(fluidMinX + 0.3, fluidMaxX - 0.3, i);
  #declare startZ = RRand(fluidMinZ + 0.3, fluidMaxZ - 0.3, i + 100);
  #declare startY = RRand(0.5, 2.0, i + 200);
  #declare radius = RRand(0.1, 0.3, i + 300);

  Bubble(i, startX, startZ, startY, radius, fluidMinX, fluidMaxX, fluidMinZ,
fluidMaxZ, fluidSurfaceY)

  #declare i = i + 1;
#end


If interested, I have access to a better verion, and GPT-5 at work, which seems
to be able to spit out a ton of higher-level code.

There's still a lot of mistakes regarding lowercase reserved identifiers,
macro-vs-function, passing undeclared identifiers into "functions", etc.,
however it gives you a lot of preliminary ideas to work with, and can help
implement tricky equations in code a lot faster.

-BW


Post a reply to this message


Attachments:
Download 'bubblephysics.png' (106 KB)

Preview of image 'bubblephysics.png'
bubblephysics.png


 

From: Leroy
Subject: Re: Bubble 4.0.1
Date: 27 Oct 2025 12:00:00
Message: <web.68ff95dddf56db1c9b8346e7f712fc00@news.povray.org>
"Cousin Ricky" <rickysttATyahooDOTcom> wrote:
> Bubble version 4.0.1:
>
>   https://github.com/CousinRicky/POV-bubble/releases/tag/v4.0.1

I just went through the code. I was wondering why he used a cylinder to bound a
bubble when a cone would be better. Using Bald Eagle's equation a cone could be
made to size a bubble along the height rather easy.
 I do a lot of animations and it looks like a lot of work to get this code to
produce something 'Real'. Maybe Rune's Particle System would do a better job.
As for the code itself, I hate to critique code that's over 15 years old, So I
wont!

Have Fun!


Post a reply to this message

From: Bald Eagle
Subject: Re: Bubble 4.0.1
Date: 27 Oct 2025 13:25:00
Message: <web.68ffaabedf56db1c1fa1ea6425979125@news.povray.org>
"Bald Eagle" <cre### [at] netscapenet> wrote:
I have access to a better version, ....

#version 3.7;
#include "colors.inc"
#include "BUBBLE_RISE.inc"   // <-- paste the include above or #include the file

global_settings { assumed_gamma 1 }
background { rgb <0.95,0.97,1.0> }

camera {
  location <0, 0.12, -0.40>
  look_at  <0, -0.05,  0.0>
  right x*image_width/image_height angle 40
}
light_source { <0.3,0.5,-0.5> rgb 1 }
light_source { <-0.5,0.6,0.6> rgb <1,0.95,0.9> }

// Glass container from the include
object { OBJECT_CONTAINER texture { pigment { rgbt <1,1,1,0.95> } finish {
specular .6 roughness .005 } } }

// (Optional) visual liquid body (for shading reference)
object { OBJECT_LIQUID texture { pigment { color rgbf <0.2,0.4,0.6,0.75> }
finish { diffuse 0.6 specular 0.1 } } }

// Spawn bubbles near bottom center
#declare A_BUBBLES = array[0];
M_BUBBLE_SYSTEM(
  120,                  // count
  <0, -S_H_LIQ+0.01, 0>,// base origin (just above bottom)
  0.01,                 // band radius (m)
  0.0015, 0.0035,       // R0 range: 1.5..3.5 mm
  0.00, 0.90,           // birth time window in normalized clock
  12345,                // system seed
  A_BUBBLES
)

// Draw all bubbles for current clock
M_DRAW_BUBBLES(A_BUBBLES, clock)




Minimal scene:

#version 3.7;
#include "colors.inc"
#include "BUBBLE_RISE.inc"   // <-- paste the include above or #include the file

global_settings { assumed_gamma 1 }
background { rgb <0.95,0.97,1.0> }

camera {
  location <0, 0.12, -0.40>
  look_at  <0, -0.05,  0.0>
  right x*image_width/image_height angle 40
}
light_source { <0.3,0.5,-0.5> rgb 1 }
light_source { <-0.5,0.6,0.6> rgb <1,0.95,0.9> }

// Glass container from the include
object { OBJECT_CONTAINER texture { pigment { rgbt <1,1,1,0.95> } finish {
specular .6 roughness .005 } } }

// (Optional) visual liquid body (for shading reference)
object { OBJECT_LIQUID texture { pigment { color rgbf <0.2,0.4,0.6,0.75> }
finish { diffuse 0.6 specular 0.1 } } }

// Spawn bubbles near bottom center
#declare A_BUBBLES = array[0];
M_BUBBLE_SYSTEM(
  120,                  // count
  <0, -S_H_LIQ+0.01, 0>,// base origin (just above bottom)
  0.01,                 // band radius (m)
  0.0015, 0.0035,       // R0 range: 1.5..3.5 mm
  0.00, 0.90,           // birth time window in normalized clock
  12345,                // system seed
  A_BUBBLES
)

// Draw all bubbles for current clock
M_DRAW_BUBBLES(A_BUBBLES, clock)



..ini file for animation:



Input_File_Name=your_scene.pov
Initial_Frame=1
Final_Frame=240
Initial_Clock=0
Final_Clock=1
Cyclic_Animation=off
Pause_When_Done=off
; width/height of your choice
+W1920 +H1080



Why this looks physically right (and how to tune)

Rise speed comes from buoyancy/drag; the simple CDC_DCD​ blend works
across most of your small bubble sizes. For clean water (low surfactant), path

vt/Rv_t/Rvt​/R. Tune S_WOBBLE_KA and S_WOBBLE_KF to match your eye/test
footage. [wseas.us], [mdpi.com]

look. If your liquid is carbonated or degassing, you can also bias R0 upward
with time or introduce rare coalescence events (merge two records) for extra
realism. (Coalescence physics depends on surfactants; see review.) [mdpi.com]
Any container: because you supply OBJECT_LIQUID, this works for cylinder,



intersections conceptually (the built‑in trace() call follows the same
idea). [wiki.povray.org]


Notes, references & further upgrades

If you want closer adherence to fluid literature, swap S_CD() for a Tomiyama
drag (or Clift‑Grace‑Weber) correlation and add a lift correction

terminal‑velocity models for prolate/oblate bubbles. [hzdr.de]

"If you want, I can integrate true wall collisions (using trace() and
per‑bubble lateral velocity state) and push this into a namespaced

plug‑and‑play across your projects."

Just to give you the flavor of how far we could potentially go with this ...

- BE


Post a reply to this message

From: Cousin Ricky
Subject: Re: Bubble 4.0.1
Date: 27 Oct 2025 18:41:00
Message: <68fff4fc$1@news.povray.org>
On 2025-10-26 13:07 (-4), Bald Eagle wrote:
> 
> It looks like he just didn't do a very good job of making the correction.

He didn't even do a good job prior to the "corrections."  His
randomization code yields columns in a box, not a cylinder.  I haven't
bothered to correct that.

> However, perhaps you should <snip>

I'm only tweaking this library because it was already there.  I'm not
going to try to overhaul this thing; to get some comprehensive
improvement, I think it would be better to start a new library from
scratch (which you already seem to be doing).

> The size of a bubble is related to the pressure exerted on it.
> Fluid pressure is dependent solely on the height of the column of liquid.

Does atmospheric pressure figure into this?  Should we pretend that
there’s an additional 14.7 psi worth of liquid on top?  Or does that
cancel out somehow?

> So you should start with a bubble of some initial size, and then adjust the
> VOLUME of the bubble in a linear fashion as it rises.
> P1V1 = P2V2

For a carbonated beverage, that's not the whole story.  The bubble
absorbs additional CO2 as it rises, which is why my default is greater
than 1/3.


Post a reply to this message

From: Cousin Ricky
Subject: Re: Bubble 4.0.1
Date: 27 Oct 2025 19:31:31
Message: <690000d3$1@news.povray.org>
On 2025-10-27 11:55 (-4), Leroy wrote:
>
>  I do a lot of animations and it looks like a lot of work to get this code to
> produce something 'Real'. Maybe Rune's Particle System would do a better job.
> As for the code itself, I hate to critique code that's over 15 years old, So I
> wont!

Clearly Mr. Robertson did not write this with animation in mind.

I have been hesitant to critique the code, not for its age, but because,
hey, I'm glad he contributed it, and I don't want to denigrate his
efforts.  But yeah, the code is a hot mess.


Post a reply to this message

From: Bald Eagle
Subject: Re: Bubble 4.0.1
Date: 27 Oct 2025 20:00:00
Message: <web.69000714df56db1c1f9dae3025979125@news.povray.org>
Cousin Ricky <ric### [at] yahoocom> wrote:

> Clearly Mr. Robertson did not write this with animation in mind.
>
> I have been hesitant to critique the code, not for its age, but because,
> hey, I'm glad he contributed it, and I don't want to denigrate his
> efforts.  But yeah, the code is a hot mess.

We've all written craptastic code at one time or another.
It's publicly posted - it's open to critique.

I like the results you posted, and I think that like many little projects that
people have undertaken over the years, we can celebrate it as an introductory
approach to the topic, and an inspiration to others in future years.

That said, bad code deserves to be rewritten.

If I was Robertson, and visited our little corner of the internet at some time
in the future, I'd be delighted to see that someone was interested enough to
take up the torch and really make it do what I originally set out to accomplish.

Then i could play with it, enjoy the results, and have a grand old time playing
with all of the new features and effects.

- BW


Post a reply to this message

From: Bald Eagle
Subject: Re: Bubble 4.0.1
Date: 28 Oct 2025 08:40:00
Message: <web.6900b907df56db1c51af05c025979125@news.povray.org>

#declare S_G=9.81; #declare S_RHO_L=1000; #declare S_MU_L=1e-3; #declare
S_P_SURF=101325; #declare S_H_LIQ=0.30;
#declare S_LIQUID_LEVEL=0.0;
#declare OBJECT_CONTAINER = cylinder { <0,-S_H_LIQ,0>, <0,0.02,0>, 0.06 }
#declare OBJECT_LIQUID = intersection { object{OBJECT_CONTAINER} plane{
y,S_LIQUID_LEVEL } inverse }
#declare S_PRESSURE = function(Y){ S_P_SURF + S_RHO_L*S_G*(0 - Y) }
#declare S_RADIUS_BY_Y=function(R0,Y){ R0*pow(S_P_SURF/S_PRESSURE(Y),1/3) }
#declare S_CLAMP=function(X,A,B){ min(max(X,A),B) }
#declare S_CD=function(RE){
  #local REp=max(RE,1e-6);
  #local CD_st=24/REp; #local
t=S_CLAMP((log(REp)-log(1))/(log(200)-log(1)),0,1);
  (1-t)*CD_st + t*0.44
}
#declare S_VTERM=function(R,CD){ sqrt((8/3)*R*S_G/max(CD,1e-6)) }
#declare S_TAU=function(R,CD){ (4/3)*R/(max(CD,1e-6)*max(S_VTERM(R,CD),1e-6)) }
#declare S_WOBBLE_AMP=function(R,VT){ 0.5*R*(1-exp(-VT/0.15)) }
#declare S_WOBBLE_FREQ=function(R,VT){ 0.25*VT/max(R,1e-6) }
#macro M_RSEED(SEED) #local _r=seed(SEED); _r #end
#macro M_RAND01(RS) #local _x=rand(RS); _x #end
#macro M_BUBBLE_RECORD(V_ORIGIN,S_R0,S_TBIRTH,S_SEED)
  #local RS=M_RSEED(S_SEED);
  #local XPH=2*pi*M_RAND01(RS); #local ZPH=2*pi*M_RAND01(RS);
  #local WXY=0.4+0.6*M_RAND01(RS); #local HUE=M_RAND01(RS);
  array[8]{ V_ORIGIN,S_R0,S_TBIRTH,S_SEED,XPH,ZPH,WXY,HUE }
#end
#macro M_BUBBLE_OBJECT(REC,S_TLOCAL,OBJECT_OUT)
  #local V_O=REC[0]; #local R0=REC[1]; #local TB=REC[2]; #local SEED=REC[3];
  #local XPH=REC[4]; #local ZPH=REC[5]; #local WXY=REC[6];
  #local TL=S_TLOCAL; #if (TL<0) #declare OBJECT_OUT=object{sphere{<0,0,0>,0}
no_shadow}; #else
    #local CD0=S_CD(100); #local VT0=S_VTERM(R0,CD0); #local TAU0=S_TAU(R0,CD0);
    #local dY1=VT0*(TL - TAU0*(1-exp(-TL/TAU0))); #local Y1=V_O.y+dY1;
    #local R1=S_RADIUS_BY_Y(R0,Y1); #local CD1=S_CD( max(
(2*R1)*VT0*S_RHO_L/S_MU_L,1) );
    #local VT1=S_VTERM(R1,CD1); #local TAU1=S_TAU(R1,CD1);
    #local dY=VT1*(TL - TAU1*(1-exp(-TL/TAU1))); #local YY=V_O.y+dY; #local
RR=S_RADIUS_BY_Y(R0,YY);
    #local AMP=S_WOBBLE_AMP(RR,VT1); #local F=S_WOBBLE_FREQ(RR,VT1);
    #local X=V_O.x + AMP*WXY*sin(2*pi*F*TL+XPH);
    #local Z=V_O.z + AMP     *cos(2*pi*F*TL+ZPH);
    #declare OBJECT_OUT = object{ sphere{ <X,YY,Z>, RR } texture{ pigment{ rgbt
<1,1,1,1> } finish{ ambient 0 diffuse 0 } } interior{ ior 1.0 } no_shadow };
  #end
#end
#macro
M_BUBBLE_SYSTEM(S_N,V_BASE,S_BAND_R,S_RMIN,S_RMAX,S_TMIN,S_TMAX,S_SEED0,ARR_OUT)
  #local RSYS=M_RSEED(S_SEED0); #local A=array[S_N]; #local I=0; #while(I<S_N)
    #local ang=2*pi*M_RAND01(RSYS); #local rad=S_BAND_R*sqrt(M_RAND01(RSYS));
    #local x=V_BASE.x+rad*cos(ang); #local z=V_BASE.z+rad*sin(ang); #local
y=V_BASE.y;
    #local r0=S_RMIN+(S_RMAX-S_RMIN)*M_RAND01(RSYS);
    #local tb=S_TMIN+(S_TMAX-S_TMIN)*M_RAND01(RSYS);
    #local sd=int(1e6*M_RAND01(RSYS)); #local
rec=M_BUBBLE_RECORD(<x,y,z>,r0,tb,sd); #local A[I]=rec; #local I=I+1; #end
  #declare ARR_OUT=A;
#end
#macro M_DRAW_BUBBLES(ARR,S_TGLOBAL)
  #local N=dimension_size(ARR,1); #local i=0; #while(i<N)
    #local REC=ARR[i]; #local TL=S_TGLOBAL-REC[2]; #local
OBJ=object{sphere{<0,0,0>,0}}; M_BUBBLE_OBJECT(REC,TL,OBJ) object{OBJ}
    #local i=i+1; #end
#end


Post a reply to this message

Goto Latest 10 Messages Next 2 Messages >>>

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