Rebol3 Code Examplex
Draw a sphere
Render a sphere in 2D or 3D projection.
Rebol [
title: "Rosetta code: Draw a sphere"
file: %Draw_a_sphere.r3
url: https://rosettacode.org/wiki/Draw_a_sphere
]
draw-sphere: function/with [size [pair!]][
width: size/x height: size/y p: 1
img: make image! size
ray: make ray! [origin: camera]
repeat row height [
y: height - row + 1
repeat col width [
u: col / width
v: y / height
ray/direction: bottom-left + (u * copy horizontal) + (v * copy vertical)
img/(++ p): pixel-color ray
]
]
img
][
;; --- Scene geometry -------------------------------------------------------
sphere-center: #(f32![0.0 0.0 -1.0])
sphere-radius: 0.5
;; --- Camera / viewport ----------------------------------------------------
camera: #(f32![ 0 0 0])
bottom-left: #(f32![-1 -1 -1])
horizontal: #(f32![ 2 0 0])
vertical: #(f32![ 0 2 0])
;; --- Math helpers ---------------------------------------------------------
dot: func [a b] [(a/1 * b/1) + (a/2 * b/2) + (a/3 * b/3)]
unit-vec: func [a] [a / sqrt (a/1 * a/1) + (a/2 * a/2) + (a/3 * a/3)]
;; --- Ray type -------------------------------------------------------------
ray!: make object! [
origin: _
direction: _
point-at-parameter: func [t [number!]] [origin + (direction * t)]
]
;; --- Shading --------------------------------------------------------------
pixel-color: function [ray [object!]] [
if hit-normal: hit-sphere ray [
light-dir: unit-vec #(f32![-1.5 1.5 1.2])
intensity: 0.5 * (1.0 + dot hit-normal light-dir)
c: to integer! (255.99 * intensity)
return to tuple! reduce [c c c]
]
;; Sky gradient
unit-dir: unit-vec ray/direction
c: 0.5 * (unit-dir/2 + 1.0)
to tuple! reduce [
to integer! (255.99 * ((1.0 - c) + (c * 0.5)))
to integer! (255.99 * ((1.0 - c) + (c * 0.7)))
to integer! (255.99 * ((1.0 - c) + (c * 1.0)))
]
]
;; --- Ray-Sphere intersection ----------------------------------------------
hit-sphere: function [ray [object!]] [
oc: ray/origin - sphere-center
a: dot ray/direction ray/direction
b: 2.0 * dot oc ray/direction
c: (dot oc oc) - (sphere-radius ** 2)
discriminant: (b ** 2) - (4 * a * c)
if discriminant < 0 [return none]
t: (negate b - sqrt discriminant) / (2.0 * a)
if t > 0.001 [
hit-point: ray/origin + (ray/direction * t)
return unit-vec (hit-point - sphere-center)
]
none
]
]
img: draw-sphere 400x400
browse save %draw-sphere.jpg img