image thumbnail

Automatic Terrain Generation

by

 

A set a tools for generating artificial terrain and other rough phenomena mapped over a 2D surface.

render_random_scenes.m
%% Render Random Scenes
%
% This script will generate numerous random scenes and save the results in
% the Output directory. This is useful to sample a large number of scene.
% Note that each scene will be saved as an image with a date stamp *and*
% the random number generator seed used to generate the seed. Passing this
% number to |rng| before running |example_scene| will result in the scene
% being recreated exactly. This allows a user to examine numerous scenes,
% select a scene of interest, recreate it, explore it, and potentially
% export it for whatever purpose. In other words, the seed number is all
% that's necessary to completely regenerate a scene. For example, if an
% image is named |scene_2012-12-13-18-38-28_seed_59150.png|, then the
% following completely recreates the scene.
%
% >> rng(59150);
% >> example_scene;
%
% Tucker McClure
% Copyright 2012, The MathWorks, Inc.

%% Options
% Number of scenes to create.
n_images = 50;

%% Initialize the Figure
fprintf('Generating %d scenes...\n', n_images);
tic();

% Create or clear a figure for the rendering.
figure(1);
clf();
set(1, 'PaperPositionMode',     'auto', ...
       'WindowButtonDownFcn',   [], ...
       'WindowButtonUpFcn',     [], ...
       'WindowButtonMotionFcn', [], ...
       'WindowScrollWheelFcn',  []);

% Choose the next random number generator seed.
rng_seed = mod(floor(1e8*now()), 2^32);
rng(rng_seed);

%%
% Add an Output directory if one doesn't exist.
if ~exist('Output', 'dir')
    mkdir('Output');
end

%% Land and Sea

% Create the terrain height map.
[~, ~, ~, hm, xm, ym] = generate_terrain(7, 513, 0, 0.1);

% Generate appropriate colors.
cm = generate_terrain_colors(hm);

% Flatten the oceans.
hmp = max(hm, 0);

% Draw the land and calculate its normal vectors.
h_land = patch(surf2patch(xm, ym, hmp, cm));
[nx, ny, nz] = surfnorm(hmp);
land_normals = [nx(:), ny(:), nz(:)];

% Set it's material properties for interpolated colors and appropriate
% lighting.
set(h_land, 'VertexNormals',    land_normals, ...
            'DiffuseStrength',  0.8, ...      % Reacts to light direction
            'SpecularStrength', 0, ...        % Not shiny
            'AmbientStrength',  0.3, ...      % Reacts to ambient light
            'BackFaceLighting', 'unlit');     % Don't illuminate reverse

% Add a black backdrop beneath the land. Sometimes a vertex seems to miss
% its position slightly, and sky colors creep through, and that's weird.
patch('Faces',           [1 2 3 4], ...
      'Vertices',        [-1 -1 -0.01; ...
                           1 -1 -0.01; ...
                           1  1 -0.01; ...
                          -1  1 -0.01], ...
      'FaceVertexCData', 0.25*ones(4, 3));
  
%% Sky
        
% Draw a random time of day between sunrise and sunset. Get the
% corresponding light color. The sky is always the color of the sun at noon
% (and is then affected by the sun color at the current time).
current_time = 0.24 + 0.52 * rand();
sun_color    = sun_tones(current_time);
sky_color    = sun_tones(0.5);

% Create the sky patch by scaling a sphere. Note that the nothing in the
% scene can be rendered outside of [-100, 100] on any axis due to a
% documented rendering bug. Therefore, the sphere will be scaleld up to
% 99, which is plenty far away since the terrain is limited to [-1, 1].
[xs, ys, zs] = sphere(16);
sky_scale = 99;
sky_patch = surf2patch(sky_scale*xs, sky_scale*ys, sky_scale*zs, ...
                       repmat(reshape(sky_color, [1 1 3]), [17 17 1]));
h_sky = patch(sky_patch);

% Set appropriate lighting options for the sky.
set(h_sky, 'DiffuseStrength',  0.3, ...
           'SpecularStrength', 0, ...
           'AmbientStrength',  1, ...
           'BackFaceLighting', 'unlit');

% Vertex normals are the opposite of what I expect. Reverse them.
set(h_sky,  'VertexNormals', -get(h_sky,  'VertexNormals'));

%% Sun

% Create a light for the sun.
h_light = lightangle(90, 360*(current_time - 0.25));

% Set the light's color.
set(h_light, 'Color',             sun_color);

% Set the ambient color used in the scene.
set(gca(),   'AmbientLightColor', sun_color);

% Use decent lighting.
lighting gouraud;

%% Axes and Global Settings

% Set axes options.
camera_target   = [0 0 mean(hmp(:)) + 0.5*std(hmp(:))];
camera_position = camera_target + [1.15 0 0.5];
set(gca, 'DataAspectRatio', [1 1 1], ...
         'Visible',         'off', ...
         'Projection',      'Perspective', ...
         'Position',        [0 0 1 1], ...
         'CameraTarget',    camera_target, ...
         'CameraViewAngle', 45, ...
         'CameraUpVector',  [0 0 1], ...
         'CameraPosition',  camera_position, ...
         'XLim',            [-100 100], ...
         'YLim',            [-100 100], ...
         'ZLim',            [-100 100]);
camorbit(360*rand(), 0);

% Patches should have no edges and interpolated face colors.
shading interp;

% Force the drawing.
drawnow();


%% Loop, Updating Image Each Time
image_number = 0;
while true    
    
    % Increase image count.
    image_number = image_number + 1;
    
    % Force a drawing.
    drawnow();
    
    % Capture the image.
    image_name = sprintf('scene_%s_seed_%d.png', ...
                         datestr(now(), 'yyyy-mm-dd-HH-MM-SS'), ...
                         rng_seed);
    print('-dpng', ['Output' filesep image_name]);
    
    % If we've generated enough images, stop.
    if image_number == n_images
        break;
    end
    
    % Choose the next random number generator seed.
    rng_seed = mod(floor(1e8*now()), 2^32);
    rng(rng_seed);
    
    % Generate new terrain.
    [~, ~, ~, hm, xm, ym] = generate_terrain(7, 513, 0, 0.1);
    cm = generate_terrain_colors(hm);
    hmp = max(hm, 0);
    current_time = 0.24 + 0.52 * rand();
    [nx, ny, nz] = surfnorm(hmp);
    land_normals = [nx(:), ny(:), nz(:)];

    % Update the patches, lights, and camera.
    land_patch = surf2patch(xm, ym, hmp, cm);
    set(h_land, 'Vertices',        land_patch.vertices, ...
                'Faces',           land_patch.faces, ...
                'FaceVertexCData', land_patch.facevertexcdata, ...
                'VertexNormals',   land_normals);
    lightangle(h_light, 90, 360*(current_time - 0.25));
    sun_color = sun_tones(current_time);
    set(h_light, 'Color', sun_color);
    camera_target   = [0 0 mean(hmp(:)) + 0.5*std(hmp(:))];
    camera_position = camera_target + [1.15 0 0.5];
    set(gca(), 'AmbientLightColor', sun_color, ...
               'CameraTarget',      camera_target, ...
               'CameraPosition',    camera_position, ...
               'CameraUpVector',    [0 0 1]);
    camorbit(360*rand(), 0);
    
end

fprintf('Done.\n');
toc();

Contact us