POV-Ray : Newsgroups : povray.advanced-users : L*C*h(uv) color solid Server Time
29 Jun 2024 04:39:36 EDT (-0400)
  L*C*h(uv) color solid (Message 41 to 50 of 82)  
<<< Previous 10 Messages Goto Latest 10 Messages Next 10 Messages >>>
From: Mike Horvath
Subject: Re: L*C*h(uv) color solid
Date: 24 Nov 2016 14:09:50
Message: <58373afe$1@news.povray.org>
On 11/23/2016 8:51 PM, clipka wrote:
 > Am 24.11.2016 um 01:27 schrieb Mike Horvath:
 >> On 11/23/2016 7:22 PM, Christian Froeschlin wrote:
 >>> No threshold will give the desired result using the color
 >>> conversion function directly, as clipka explained you need
 >>> a define a "distance" function for this purpose.
 >>>
 >>> Alternatively you could try to render the clipped function
 >>> (set to 0 outside the gamut) as strongly scattering media but
 >>> it may be fiddly and you don't get the look of a "surface"
 >>> regarding finish and lighting etc.
 >>>
 >>
 >> What is a "distance" function?
 >>
 >> The "convertLCH2RGBb2(y*100,sqrt(x*x+z*z)*100,atan2d(x,z))" bit is what
 >> clipka showed me.
 >
 > You may need to deconstruct the problem into easy-to-handle building
 > blocks, and tackle each of them separately.
 >
 > One building block you need is a function that maps a single RGB colour
 > component to a distance-ish scalar telling you how "far away" the colour
 > component is from the valid range (this time, just for giggles, I'm
 > contructing the function in such a way that <0 is inside, >0 is outside):
 >
 >     #declare fD = function(C) { abs(C-0.5)-0.5 }
 >
 > Another building block you need is a function that combines three such
 > distance-ish scalars, such that you get a positive value if one or more
 > parameters is positive, or a negative value if all are negative, in a
 > manner that avoid steps:
 >
 >     #declare fDist = function(Dr,Dg,Db) { max(Dr,Dg,Db) }
 >
 >
 > That's kind of all you need to know about the "distance" functions;
 > however, those functions alone don't get you anywhere.
 >
 >
 > Another necessary building block is a set of functions that map Lch 
to RGB:
 >
 >     #declare fR = function(L,c,h) { ... }
 >     #declare fG = function(L,c,h) { ... }
 >     #declare fB = function(L,c,h) { ... }
 >
 > And the final building block is a set of functions that map 3D cartesian
 > space to Lch:
 >
 >     #declare fL = function(x,y,z) { y*100 }
 >     #declare fc = function(x,y,z) { sqrt(x*x+z*z)*100 }
 >     #declare fh = function(x,y,z) { atan2d(x,z) }
 >
 >
 > Once you have all these building blocks, all that's left is to plug them
 > all together.
 >
 > The final result to be compared to the threshold by the isosurface is
 > the result of the `fDist` function, so that's where you start:
 >
 >     isosurface {
 >       threshold 0
 >       function { fDist (Dr,Dg,Db) }
 >     }
 >
 > The isosurface doesn't know what `Dr`, `Dg` and `Db` are, but we know
 > they are supposed to be results of the `fD` function, using the three
 > colour channels as input:
 >
 >     isosurface {
 >       threshold 0
 >       function { fDist (
 >         fD (R),
 >         fD (G),
 >         fD (B)
 >       ) }
 >     }
 >
 > Again the isosurface doesn't know `R`, `G` and `B`:
 >
 >     isosurface {
 >       threshold 0
 >       function { fDist (
 >         fD ( fR (L,c,h) ),
 >         fD ( fG (L,c,h) ),
 >         fD ( fB (L,c,h) )
 >       ) }
 >     }
 >
 > Still not done yet, as the isosurface doesn't know `L`, `c` and `h` 
either:
 >
 >     isosurface {
 >       threshold 0
 >       function { fDist (
 >         fD ( fR (fL(x,y,z),fc(x,y,z),fh(x,y,z)) ),
 >         fD ( fG (fL(x,y,z),fc(x,y,z),fh(x,y,z)) ),
 >         fD ( fB (fL(x,y,z),fc(x,y,z),fh(x,y,z)) )
 >       ) }
 >     }
 >
 > Now we have only `x`, `y` and `z` left as parameters, which is what the
 > isosurface can handle.
 >
 >
 > For bonus points, you could eliminate the multiple invocation of
 > `fL(x,y,z)`, `fc(x,y,z)` and `fh(x,y,z)`. To this end, you need to
 > provide a function that does not compute these values itself, but rather
 > takes them as parameters:
 >
 >     #declare fFinal = function(L,c,h) { fDist (
 >       fD ( fR (L,c,h) ),
 >       fD ( fG (L,c,h) ),
 >       fD ( fB (L,c,h) )
 >     ) }
 >
 >     isosurface {
 >       threshold 0
 >       function { fFinal (fL(x,y,z),fc(x,y,z),fh(x,y,z)) }
 >     }
 >
 >
 > And of course you'll want to use different names; I chose the above for
 > brevity.
 >

Next issue: What is the best way to create an accurate pigment to paint
the isosurface?

I was thinking of repurposing the following:

//------------------------------
// HSL Cylinder

#declare CSolid_HSLCylinder_Hue = pigment
{
	function {-f_th(x,y,z)/pi/2}
	color_map
	{
		// need to replace these with calls to CHSL2RGB() or CH2RGB(), then
increase the number of steps
		[0/6 srgb <1,0,0,>]
		[1/6 srgb <1,1,0,>]
		[2/6 srgb <0,1,0,>]
		[3/6 srgb <0,1,1,>]
		[4/6 srgb <0,0,1,>]
		[5/6 srgb <1,0,1,>]
		[6/6 srgb <1,0,0,>]
	}
}
#declare CSolid_HSLCylinder_Saturation = pigment
{
	cylindrical
	pigment_map
	{
		[0 CSolid_HSLCylinder_Hue]
		[1 color srgb 1/2]
	}
	scale	(1 + CSolid_Offset)
}
#declare CSolid_HSLCylinder_Lightness = pigment
{
	gradient y
	pigment_map
	{
		[0/2 color srgb 0]
		[1/2 CSolid_HSLCylinder_Saturation]
		[2/2 color srgb 1]
	}
	scale		(1 + CSolid_Offset)
	translate	-y * CSolid_Offset/2
}
#declare CSolid_HSLCylinder_Pigment = pigment {CSolid_HSLCylinder_Lightness}

Except, in this case using LCH conversion formula to create the gradients:

	[0/6 srgb <convertLCH2RGBb1(50, 50, 0),convertLCH2RGBb2(50, 50,
0),convertLCH2RGBb3(50, 50, 0)>]
	[1/6 srgb <convertLCH2RGBb1(50, 50, 60),convertLCH2RGBb2(50, 50,
60),convertLCH2RGBb3(50, 50, 60)>]
	[2/6 srgb <convertLCH2RGBb1(50, 50, 120),convertLCH2RGBb2(50, 50,
120),convertLCH2RGBb3(50, 50, 120)>]
	[3/6 srgb <convertLCH2RGBb1(50, 50, 180),convertLCH2RGBb2(50, 50,
180),convertLCH2RGBb3(50, 50, 180)>]
	[4/6 srgb <convertLCH2RGBb1(50, 50, 240),convertLCH2RGBb2(50, 50,
240),convertLCH2RGBb3(50, 50, 240)>]
	[5/6 srgb <convertLCH2RGBb1(50, 50, 300),convertLCH2RGBb2(50, 50,
300),convertLCH2RGBb3(50, 50, 300)>]
	[6/6 srgb <convertLCH2RGBb1(50, 50, 360),convertLCH2RGBb2(50, 50,
360),convertLCH2RGBb3(50, 50, 360)>]


Is this maybe a bad idea?

Mike


Post a reply to this message

From: clipka
Subject: Re: L*C*h(uv) color solid
Date: 24 Nov 2016 14:33:30
Message: <5837408a$1@news.povray.org>
Am 24.11.2016 um 20:10 schrieb Mike Horvath:

> Next issue: What is the best way to create an accurate pigment to paint
> the isosurface?

The main problem to solve is that POV-Ray's mechanisms to generate
gradient-ish pigments only supports 1-dimensional gradients.

A common solution is to construct three 1-dimensional gradient-ish
pigments -- one for each colour component -- and then mix them together
using the `average` pseudo-pattern:

    #declare FinalPigment = pigment {
      average
      pigment_map {
        [ 1.0 RedPigment   ]
        [ 1.0 GreenPigment ]
        [ 1.0 BluePigment  ]
      }
    }

Each of the `RedPigment`, `GreenPigment` and `BluePigment` component
patterns is designed in such a way that it only has the corresponding
component set to non-zero.

An important thing to note here is that the `average` pseudo-pattern
does not add colours, but averages them; so in order to achieve the full
range of colours, the component patterns must have colour ranges from 0
to 3 for the corresponding channel:

    #declare RedPigment = pigment {
      function { fR(fL(x,y,z),fc(x,y,z),fh(x,y,z), }
      colour_map {
        [ 0.0 colour rgb <0,0,0> ]
        [ 1.0 colour rgb <3,0,0> ]
      }
    }

Note that there is no need for clipping the function result: This is
automatically achieved by the colour_map.


Post a reply to this message

From: Mike Horvath
Subject: Re: L*C*h(uv) color solid
Date: 24 Nov 2016 15:50:13
Message: <58375285$1@news.povray.org>
Here's what I have:

#declare pigmentR = pigment
{
	function {fR (fL(x,y,z),fC(x,y,z),fH(x,y,z))}
	color_map
	{
		[0 color srgb <0,0,0>]
		[1 color srgb <3,0,0>]
	}
}
#declare pigmentG = pigment
{
	function {fG (fL(x,y,z),fC(x,y,z),fH(x,y,z))}
	color_map
	{
		[0 color srgb <0,0,0>]
		[1 color srgb <0,3,0>]
	}
}
#declare pigmentB = pigment
{
	function {fB (fL(x,y,z),fC(x,y,z),fH(x,y,z))}
	color_map
	{
		[0 color srgb <0,0,0>]
		[1 color srgb <0,0,3>]
	}
}


isosurface
{
	function { fFinal (fL(x,y,z),fC(x,y,z),fH(x,y,z)) }
	threshold	0
	accuracy	0.01
	contained_by
	{
		box {<-1,0,-1>,<+1,+1,+1>}
	}
	max_gradient	20000
//	[evaluate P0, P1, P2]
//	open
//	[max_trace INTEGER] | [all_intersections]
	pigment
	{
		average
		pigment_map
		{
			[1 pigmentR]
			[1 pigmentG]
			[1 pigmentB]
		}
	}
}

However, the result is much to white on top, and there are no blues or 
greens whatsoever. I've attached the result of the render. Here's 
roughly what the colors are supposed to look like:

https://commons.wikimedia.org/wiki/File:Cielch_color_solid_cylinder.png

Mike


Post a reply to this message


Attachments:
Download 'cielch_color_solid_cylinder_isosurface_2.png' (17 KB)

Preview of image 'cielch_color_solid_cylinder_isosurface_2.png'
cielch_color_solid_cylinder_isosurface_2.png


 

From: Mike Horvath
Subject: Re: L*C*h(uv) color solid
Date: 24 Nov 2016 16:50:20
Message: <5837609c$1@news.povray.org>
It occurred to me that the color solid should not extend beyond a 
cylinder with radius 1 and height 1. I have attached an overhead view.

I'm guessing the simplest thing to do was intersect the isosurface with 
the cylinder. Is that correct?

Mike


Post a reply to this message


Attachments:
Download 'cielch_color_solid_cylinder_isosurface_2.png' (16 KB)

Preview of image 'cielch_color_solid_cylinder_isosurface_2.png'
cielch_color_solid_cylinder_isosurface_2.png


 

From: Mike Horvath
Subject: Re: L*C*h(uv) color solid
Date: 24 Nov 2016 17:56:40
Message: <58377028$1@news.povray.org>
On 11/24/2016 3:50 PM, Mike Horvath wrote:
> Here's what I have:
>
> #declare pigmentR = pigment
> {
>     function {fR (fL(x,y,z),fC(x,y,z),fH(x,y,z))}
>     color_map
>     {
>         [0 color srgb <0,0,0>]
>         [1 color srgb <3,0,0>]
>     }
> }
> #declare pigmentG = pigment
> {
>     function {fG (fL(x,y,z),fC(x,y,z),fH(x,y,z))}
>     color_map
>     {
>         [0 color srgb <0,0,0>]
>         [1 color srgb <0,3,0>]
>     }
> }
> #declare pigmentB = pigment
> {
>     function {fB (fL(x,y,z),fC(x,y,z),fH(x,y,z))}
>     color_map
>     {
>         [0 color srgb <0,0,0>]
>         [1 color srgb <0,0,3>]
>     }
> }
>
>
> isosurface
> {
>     function { fFinal (fL(x,y,z),fC(x,y,z),fH(x,y,z)) }
>     threshold    0
>     accuracy    0.01
>     contained_by
>     {
>         box {<-1,0,-1>,<+1,+1,+1>}
>     }
>     max_gradient    20000
> //    [evaluate P0, P1, P2]
> //    open
> //    [max_trace INTEGER] | [all_intersections]
>     pigment
>     {
>         average
>         pigment_map
>         {
>             [1 pigmentR]
>             [1 pigmentG]
>             [1 pigmentB]
>         }
>     }
> }
>
> However, the result is much to white on top, and there are no blues or
> greens whatsoever. I've attached the result of the render. Here's
> roughly what the colors are supposed to look like:
>
> https://commons.wikimedia.org/wiki/File:Cielch_color_solid_cylinder.png
>
> Mike
>


Okay, the problem is that I want to use sRGB instead of RGB in the 
pigments. However, it seems to not be possible to multiply the sRGB 
colors and then average them like you can with regular RGB. Or is there 
a way to get this to work?

Mike


Post a reply to this message

From: clipka
Subject: Re: L*C*h(uv) color solid
Date: 24 Nov 2016 18:44:27
Message: <58377b5b@news.povray.org>
Am 24.11.2016 um 23:56 schrieb Mike Horvath:

> Okay, the problem is that I want to use sRGB instead of RGB in the
> pigments. However, it seems to not be possible to multiply the sRGB
> colors and then average them like you can with regular RGB. Or is there
> a way to get this to work?

Don't use sRGB when mucking around with colour spaces, unless you know
/exactly/ what you are doing. Colour space experts are smart enough to
specify colours in terms of linear light intensity, or otherwise
explicitly inform the readers.


Post a reply to this message

From: clipka
Subject: Re: L*C*h(uv) color solid
Date: 24 Nov 2016 18:47:01
Message: <58377bf5$1@news.povray.org>
Am 24.11.2016 um 22:50 schrieb Mike Horvath:
> It occurred to me that the color solid should not extend beyond a
> cylinder with radius 1 and height 1. I have attached an overhead view.

May I ask the stupid question, "why not"?

I would expect the Lch colour space to encompass all the RGB colour
space, so the boundaries of the latter should be sufficient to bound the
shape.

My guess is that the issues you see with the shape are due to bogosities
in your colour conversion functions.


Post a reply to this message

From: Mike Horvath
Subject: Re: L*C*h(uv) color solid
Date: 24 Nov 2016 19:46:51
Message: <583789fb$1@news.povray.org>
Thank you clipka! Everything is working perfectly now! Part of the 
problem was a bug in my conversion functions. Once I fixed it, I started 
getting the correct results.

Do you mind if I put a LGPL license on the code an upload it to Wikipedia?


Mike


Post a reply to this message

From: Mike Horvath
Subject: Re: L*C*h(uv) color solid
Date: 24 Nov 2016 19:50:31
Message: <58378ad7$1@news.povray.org>
On 11/24/2016 6:43 PM, clipka wrote:
> Am 24.11.2016 um 23:56 schrieb Mike Horvath:
>
>> Okay, the problem is that I want to use sRGB instead of RGB in the
>> pigments. However, it seems to not be possible to multiply the sRGB
>> colors and then average them like you can with regular RGB. Or is there
>> a way to get this to work?
>
> Don't use sRGB when mucking around with colour spaces, unless you know
> /exactly/ what you are doing. Colour space experts are smart enough to
> specify colours in terms of linear light intensity, or otherwise
> explicitly inform the readers.
>

The functions output sRGB colors with Illuminant = D65 and Observer = 2° 
(1931) according to one of the conversion websites that uses these formulas.

I'd like the POV code to use sRGB as well.


Mike


Post a reply to this message

From: Mike Horvath
Subject: Re: L*C*h(uv) color solid
Date: 24 Nov 2016 20:28:20
Message: <583793b4$1@news.povray.org>
On 11/24/2016 6:46 PM, clipka wrote:
> Am 24.11.2016 um 22:50 schrieb Mike Horvath:
>> It occurred to me that the color solid should not extend beyond a
>> cylinder with radius 1 and height 1. I have attached an overhead view.
>
> May I ask the stupid question, "why not"?
>
> I would expect the Lch colour space to encompass all the RGB colour
> space, so the boundaries of the latter should be sufficient to bound the
> shape.
>
> My guess is that the issues you see with the shape are due to bogosities
> in your colour conversion functions.
>

L, C and H are supposed to form a cylinder. There are some very small 
sRGB bits poking outside of the cylinder. I guess I could show the bits 
as well, as long as I explain what they are.

Also, I *did* make a mistake in the color conversion code. I was 
clamping L, C and H to 0..100, 0..100 and 0..360 incorrectly. Once I 
stopped clamping, the shape turned into what I have been expecting based 
on earlier tests.

I've attached the final shape, with the outside sRGB bits trimmed off.

Mike


Post a reply to this message


Attachments:
Download 'cielch_color_solid_cylinder_isosurface.png' (24 KB)

Preview of image 'cielch_color_solid_cylinder_isosurface.png'
cielch_color_solid_cylinder_isosurface.png


 

<<< Previous 10 Messages Goto Latest 10 Messages Next 10 Messages >>>

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