Create a 3D space for random moving dots

18 Ansichten (letzte 30 Tage)
Kahina
Kahina am 25 Jun. 2019
Kommentiert: Bjorn Gustavsson am 27 Jun. 2019
I have a code that animates dots moving in random directions in a 2D space (x and y axis). I tried to create a 3rd dimension (z axis) using spherical coordinates In order to draw and display the dots in a 3D space, I used the Psychtoolbox function moglDrawDots3D, as it takes 3d coordinates XYZ, but I am still getting a 2D animation, the dots on the z don't appear. I have no clue on the cause. I am very new to Matlab and animations, I'd be very grateful to get your feedback and insight. I tried to explain my code step by step below for the sake of clarity. Thank you in advance for your help!
PS: The use of the Psychtoolbox is not a necessity, if you have any other solution, I'd be happy to try it out.
AssertOpenGL;
InitializeMatlabOpenGL;
display = OpenWindow()
dots.nDots = 531; % number of dots
dots.color = [255,255,255]; % color of the dots
dots.size = 10; % size of dots (pixels)
dots.center = [0,0,0]; % center of the field of dots (x,y,z)
dots.apertureSize = [50.8,28.5,50.8]; % size of rectangular aperture [w,h,depth] in degrees
First, random position within the aperture for each of the dots. 'dots.x' and 'dots.y' will hold the x and y positions for each dot.
[dots.x,dots.y,dots.z] = CreateUniformDotsIn3DFrustum(dots.nDots, 25, 1/screen_ratio, 0.1, 100);
Then I converted these dot positions from a visual angle into pixel coordinates using a created function 'angle2pix'
tmp = Screen('Resolution',0); % (1) Screen's 'Resolution' function determine the screen resolution.
display.resolution = [tmp.width,tmp.height];
display.width = 50.8; % (2) Width of the screen in cm.
display.dist = 50; % (3) Distance of the screen from the observer in cm.
This generates pixel positions, but they're centered at [0,0], which is the top left corner
pixpos.x = angle2pix(display,dots.x); % Convert the x position of the dots from visual angle to pixel.
pixpos.y = angle2pix(display,dots.y); % Convert the y position of the dots from visual angle to pixel.
pixpos.z = ones(1, dots.nDots) * -1;
I defined some timing and motion parameters for the animation
dots.speed = 3; % degrees/second
dots.duration = 10; % seconds
dots.theta_deg = randi(360,1,dots.nDots); % degrees
dots.phi_deg = 30; % degrees
dots.theta_rad = dots.theta_deg * pi /180; % direction converted to radians
dots.phi_rad = dots.phi_deg * pi /180; % direction converted to radians
I calculated the distance travelled by the dot, by determining the x,y and z positions using spherical coordinates and then their derivate.
dx = dots.speed* sin(-dots.phi_rad-dots.theta_rad)/display.frameRate;
dy = -dots.speed* cos(dots.phi_rad + dots.theta_rad)/display.frameRate;
dz = -dots.speed*cos(dots.theta_rad)/display.frameRate;
The total number of frames for the animation is determined by the duration (seconds) multiplied by the frame rate (frames/second). The function secs2frames performs the calculation
nFrames = secs2frames(display,dots.duration);
I try to fit the dots within the aperture by calculating the left, right top, bottom and depth(forward and backward) of the aperture (in degrees)
l = dots.center(1)-dots.apertureSize(1)/2;
r = dots.center(1)+dots.apertureSize(1)/2;
b = dots.center(2)-dots.apertureSize(2)/2;
t = dots.center(2)+dots.apertureSize(2)/2;
d_forward = dots.center(3)- dots.apertureSize(3)/2;
d_backward = dots.center(3)+ dots.apertureSize(3)/2;
New random starting positions
[dots.x,dots.y,dots.z] = CreateUniformDotsIn3DFrustum(dots.nDots, 25, 1/screen_ratio, 0.1, 100);
Make the dots move
try
for i=1:nFrames
%convert from degrees to screen pixels
pixpos.x = angle2pix(display,dots.x)+ display.resolution(1)/2;
pixpos.y = angle2pix(display,dots.y)+ display.resolution(2)/2;
pixpos.z = ones(1, dots.nDots) * -1;
moglDrawDots3D(display.windowPtr, [pixpos.x;pixpos.y;pixpos.z],dots.size, dots.color, dots.center,1);
update the dot position
dots.x = dots.x + dx;
dots.y = dots.y + dy;
dots.z = dots.z + dz;
Move the dots that are outside the aperture back one aperture width
dots.x(dots.x<l) = dots.x(dots.x<l) + dots.apertureSize(1);
dots.x(dots.x>r) = dots.x(dots.x>r) - dots.apertureSize(1);
dots.y(dots.y<b) = dots.y(dots.y<b) + dots.apertureSize(2);
dots.y(dots.y>t) = dots.y(dots.y>t) - dots.apertureSize(2);
dots.z(dots.z<d_forward) = dots.z(dots.z<d_forward) + dots.apertureSize(3);
dots.z(dots.z>d_backward) = dots.z(dots.z>d_backward) - dots.apertureSize(3);
Screen('Flip',display.windowPtr);
end
catch ME
Screen('CloseAll');
rethrow(ME)
end
Screen('CloseAll');
  16 Kommentare
Kahina
Kahina am 27 Jun. 2019
I have tried to plot the dots, remove the axis and put a black background, and it does not make a difference - I see do not observe or get the feeling of 3D (dots moving in z axis) probably because of the orthographic projection.
Here is the code :
function Demo3dDotsMovie
%% Generate some data
N_OBJS = 20;
P = rand(N_OBJS,3) .* ones(1,1,3);
%% Create the figure
figure('Color', 'k');
hP(1) = plot3(P(:,1,1), P(:,2,1), P(:,3,1), '.', 'Color', [0 0 0]+0.2, 'MarkerSize', 20); hold on;
hP(2) = plot3(P(:,1,2), P(:,2,2), P(:,3,2), '.', 'Color', [0 0 0]+0.4, 'MarkerSize', 20);
hP(3) = plot3(P(:,1,3), P(:,2,3), P(:,3,3), '.', 'Color', [0 0 0]+1.0, 'MarkerSize', 20);
AX_LIMS = [-1 1]*3;
drawnow; set(gca, 'XLim', AX_LIMS, 'YLim', AX_LIMS, 'ZLim', AX_LIMS, 'Color', 'k',...
'XColor', 'k', 'YColor', 'k', 'ZColor', 'k');
%% Animate:
FPS = 12;
for ind1 = 1:500
% Update coordinates:
dP = randn(N_OBJS,3)*0.1;
P(:,:,1) = P(:,:,2);
P(:,:,2) = P(:,:,3);
P(:,:,3) = P(:,:,3) + dP;
% Update plot objects:
for ind2 = 1:3
set( hP(ind2), 'XData', P(:,1,ind2), 'YData', P(:,2,ind2), 'ZData', P(:,3,ind2) );
end
pause(FPS^-1);
end
I am guessing that a way to get the feeling of depth would be a change in the size of dots when they move from the axis z (3rd dimension) to the axis XY (2nd dimension). But the problem of orthographic projection might remain, right? I might only observe dots becoming bigger in a 2D dimension (XY) rather than an increase of size through the trajectory from the 3rd dimension to XY.
Bjorn Gustavsson
Bjorn Gustavsson am 27 Jun. 2019
Oh, you want to get a 3-D feeling when looking at your figure. That is another task altogether.
You might get a bit of a feel for 3-D motion if you do change the size depending on distance from your eye. I would still do that with plain matlab-coding, the trick would be to calculate size depending on distance from your eye, the angular diameter of your dots would decrease as , where l is distance from your eye. The question is how to properly scale the distance from the nearest to the farthes point relative to the distance between the screen and your eye. My guess is that you should try for dimensions close to reality, perhaps 0.4 m from eye to screen, and another .15 m from nearest to farthest corner of your cube. It "seems reasonable" that our visual perception would handle such a setting better than some completely different ratio.
The reason I write eye and not eyes is obviously that since you will only plot the points in one figure at the physical screen surface, and at that distance objects are close enough that we determine 3-D position from paralax. Because of that your eyes might actually be too clever to be fooled by your size-variation technique, they might see the points and determine by stereoscopic triangulation that they are at the screen-surface and simply interpret the size-variation as just that, The 3-D illusion might have a bigger chans if you only look with one eye. The ideal way to go about this would be to use one frame for the left eye and another frame for the right eye and then use VR-techniques to display the sequence - once uppon a time I worked with a SGI Onyx where you could separate frames for the left/right eyes with polarization and it worked really well.

Melden Sie sich an, um zu kommentieren.

Antworten (0)

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by