
Post by mariog on Oct 10, 2019 18:53:31 GMT
Hi,
After a lot of head scratching I discovered a bug in the gradient pattern function which should return a linearly interpolated colour between a starting colour and an ending colour for point.x, where x ranges from 1 to +1. The problem is that the function as described in the book is incorrect and produces the wrong interpolation. Here is my fix in Java. I hope you can read it,
and translate it into whatever language you are using:
public class GradientPattern extends Pattern { Colour a; Colour b; public GradientPattern() { a = Colour.rgb(1.0,0,0); b = Colour.rgb(0, 0, 0); }
public Colour patternAt(Tuple point) { // Linearly interpolate *each* of the rgb components with respect // to point.x. a = colour a, b = colour b, the start and end colours // for the gradient. double red = lerp(a.r, b.r, point.x); double green = lerp(a.g, b.g, point.x); double blue = lerp(a.b, b.b, point.x); return Colour.rgb(red, green, blue); }
public static double lerp(double start, double end, double t) { return (1  t) * start + t * end; } }
Once coded, your sphere with a gradient pattern of red to black and a light positioned at 10,10,10 should look like this:



Post by Jamis on Oct 14, 2019 17:27:25 GMT
Thanks for sharing your solution! The two solutions (the book, and yours) are actually functionally equivalent, though.
Here's how. Let's say you have two colors, a and b, and some value t for the position you want to interpolate between them. Your solution (which is perfectly accurate) says:
c = (1  t) * a + t * b
Expanding the formula, you get:
c = a  at + bt c = a + t(a + b) c = a + t(b  a)
Which latter formula is what the book describes, by first finding the distance between the two colors (b  a), multiplying that by t, and finally adding a.
I'm curious what issues you ran into when you implemented it the first time through. Regardless, though, I'm glad you got it working. The sphere looks great!



Post by cloose on Dec 13, 2020 16:41:43 GMT
I have also issues with the gradient pattern. I think the difference is the value of "t" here. The OP seems to use point.x directly for "t" while the book uses "px  floor(px)". I use the following python implementation: def pattern_at(self, point): distance = subtract(self.color_b, self.color_a) fraction = point[0]  floor(point[0]) return add(self.color_a, multiply(distance, fraction))
For negative x values like 0.2 the fraction is 0.8 (0.2  1). So the x range is between 0 and 1 instead of 1 to 1. The result does not look good: I used the formula (x + 1) * 0.5 to map (1, 1) to (0, 1): def pattern_at(self, point): distance = subtract(self.color_b, self.color_a) fraction = (point[0] + 1.0) * 0.5 return add(self.color_a, multiply(distance, fraction))
And this was the result: Code to draw the above images: def render_scene(): floor = Plane() floor.set_transform(scaling(10, 0.01, 10)) floor.material = Material() floor.material.color = color(0.9, 0.9, 0.9) floor.material.specular = 0
sphere = Sphere() sphere.set_transform(translation(0.0, 1, 0.0)) sphere.material = Material() sphere.material.pattern = GradientPattern(color(1.0, 1.0, 1.0), color(0.1, 0.1, 0.1)) sphere.material.ambient = 1.0 sphere.material.diffuse = 0 sphere.material.specular = 0
world = World() world.light = PointLight(point(0, 5, 10), color(1, 1, 1)) world.objects.append(sphere)
camera = Camera(600, 500, pi / 3) camera.set_transform( view_transform(point(0, 1.5, 5), point(0, 1, 0), vector(0, 1, 0)))
canvas = RayTracer().render(camera, world) ppm = canvas.to_ppm() outf = open('test_gradient.ppm', 'w') outf.write(ppm)
I hope I could explain the issue. :)



Post by alexandre on Dec 14, 2020 8:47:05 GMT
I have the same issue using the book version: It generates a gradient like this: However, using the following version: c = a + t(b  a) It generates the correct gradient:

