Newbie of Theory wrote:
> I have to plot a 3-D curves z=f(x,y). Anyone did this before?
Yes, I just did exactly this. :-)
The following OCaml program loads a flat list representing 257x257 samples
of a height surface which is then used to compute vertices and normals
which are rendered via a display list of triangle strips, lit and rotated
in real time (on my laptop):
-----
open Printf
(* Get the time since the animation was first entered. *)
let time =
let t = ref None in
fun () -> match !t with
None -> t := Some (Unix.gettimeofday ()); 0.
| Some t -> Unix.gettimeofday () -. t
(* Hardcoded number of samples per edge in the height field. *)
let n = 257
(* Load the height field as a n x n list of floats. *)
let height =
let ch = open_in "3d.mx" in
let rec aux l =
try aux (float_of_string (input_line ch) :: l)
with End_of_file -> l in
let heights = Array.of_list (aux []) in
close_in ch;
fun i j -> heights.(i + n * j)
(* 3D vector definitions. *)
type vec3 = { x: float; y: float; z: float }
let neg r = { x = -. r.x; y = -. r.y; z = -. r.z }
let add r1 r2 = { x = r1.x +. r2.x; y = r1.y +. r2.y; z = r1.z +. r2.z }
let sub r1 r2 = add r1 (neg r2)
let dot r1 r2 = r1.x *. r2.x +. r1.y *. r2.y +. r1.z *. r2.z
let cross r1 r2 = { x = r1.y *. r2.z -. r1.z *. r2.y;
y = r1.z *. r2.x -. r1.x *. r2.z;
z = r1.x *. r2.y -. r1.y *. r2.x }
let length2 r = dot r r
let length r = sqrt (length2 r)
let scale s r = { x = s *. r.x; y = s *. r.y; z = s *. r.z }
let string_of_vec r =
"("^string_of_float r.x^", "^
string_of_float r.y^", "^
string_of_float r.z^")"
let print_vec r = print_endline (string_of_vec r)
let glVertex { x = x; y = y; z = z } = GlDraw.vertex ~x ~y ~z ()
let glNormal { x = x; y = y; z = z } = GlDraw.normal ~x ~y ~z ()
(* Compute explicit vertex coordinates. *)
let vertex =
let aux i j =
{ x = float i /. float n; z = float j /. float n; y = height i j } in
Array.init n (fun i -> Array.init n (fun j -> aux i j))
(* Numerically approximate the normal vector at each vertex. *)
let normal =
let normal = Array.make_matrix n n {x=0.; y=1.; z=0.} in
for i = 1 to n - 2 do
for j = 1 to n - 2 do
let dx = height (i-1) j -. height (i+1) j in
let dz = height i (j-1) -. height i (j+1) in
let n = { x = dx; y = 2. /. float n; z = dz } in
normal.(i).(j) <- scale (1. /. length n) n
done;
done;
normal
(* The plot is memoized in this display list. *)
let dl = ref None
(* Render a lit, 3D, spinning height field. *)
let render () =
Gl.enable `depth_test;
(* Initialise lighting. *)
Gl.enable `lighting;
GlLight.light_model (`two_side true);
Gl.enable `light0;
GlLight.light ~num:0 (`position (1., 0., 0., 0.));
GlLight.light ~num:0 (`diffuse (0.25, 0.5, 1., 1.));
GlLight.light ~num:0 (`specular (0., 0., 0., 1.));
Gl.enable `light1;
GlLight.light ~num:1 (`position (0., -1., 0., 0.));
GlLight.light ~num:1 (`diffuse (1., 0.5, 0.25, 1.));
GlLight.light ~num:1 (`specular (0., 0., 0., 1.));
GlDraw.color (1., 1., 1.);
GlLight.material `both (`ambient (0.1, 0.1, 0.1, 1.));
GlLight.material `both (`diffuse (1., 1., 1., 1.));
GlLight.material `both (`specular (1., 1., 1., 1.));
GlLight.material `both (`shininess 25.);
GlMat.rotate3 (30. *. time ()) (0., 1., 0.);
GlMat.scale ~x:1.5 ~y:0.5 ~z:1.5 ();
GlMat.translate ~x:(-0.5) ~z:(-0.5) ();
begin match !dl with
Some dl -> GlList.call dl
| None ->
let dl' = GlList.create `compile in
for i = 1 to n - 3 do
GlDraw.begins `triangle_strip;
for j = 1 to n - 2 do
glNormal (normal.(i).(j));
glVertex (vertex.(i).(j));
glNormal (normal.(i+1).(j));
glVertex (vertex.(i+1).(j));
done;
GlDraw.ends ();
done;
GlList.ends ();
GlList.call dl';
dl := Some dl';
end;
Gl.disable `lighting;
Gl.disable `cull_face;
(* Render the axes. *)
Gl.enable `blend;
GlFunc.blend_func `src_alpha `one_minus_src_alpha;
Gl.enable `line_smooth;
GlDraw.begins `line_loop;
List.iter GlDraw.vertex3 [0., 0., 0.; 1., 0., 0.; 1., 0., 1.; 0., 0., 1.];
GlDraw.ends ();
GlDraw.begins `line_loop;
List.iter GlDraw.vertex3 [0., 1., 0.; 1., 1., 0.; 1., 1., 1.; 0., 1., 1.];
GlDraw.ends ();
GlDraw.begins `lines;
List.iter GlDraw.vertex3 [0., 0., 0.; 0., 1., 0.;
1., 0., 0.; 1., 1., 0.;
1., 0., 1.; 1., 1., 1.;
0., 0., 1.; 0., 1., 1.];
GlDraw.ends ();
Gl.disable `line_smooth;
Gl.disable `blend;
Gl.disable `depth_test
(* Display the scene using a 3D perspective projection. *)
let display data =
GlMat.mode `projection;
GlMat.load_identity ();
let sqr x = x *. x in
let fov = 45. +. (179. -. 45.) /. (1. +. sqr (time ())) in
GluMat.perspective fov (float data#width /. float data#height) (0.1,
100.);
GluMat.look_at (0., 2.4, 3.) (-0.7, 0.5, 0.) (0., 1., 0.);
GlMat.mode `modelview;
GlMat.load_identity ();
GlClear.clear [`depth];
render ()
-----
You could rewrite this program in C or C++ easily enough, it would just be
quite a bit more tedious.
HTH.
--
Dr Jon D Harrop, Flying Frog Consultancy
http://www.ffconsultancy.com
|