Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Matlab stops interpolating colors on a mesh correctly if it is larger than 120 triangles

Tags:

plot

matlab

mesh

3d

I'm trying to draw a large mesh in Matlab using the trimesh function, with the z coordinate of the vertices controlling the color. Unfortunately, Matlab stops interpolating colors correctly when the size of the mesh exceeds 120 triangles. Here's a picture demonstrating the problem, with 120 triangles on the left, and 121 triangles on the right.

A picture demonstrating the problem

As you can see, for large meshes, Matlab interpolates directly from the color of one vertex to the color of the other vertex. This was probably done for performance reasons, but I'm trying to generate nice pictures for my thesis, and I don't care how long it takes to compute them. Is there a way to disable this approximation?

Here's the code to generate the picture:

function test(n)
    %%% Generate a mesh with n triangles.

    oneTriVerts = [0 0 0;
                   1 0 0;
                   1 0 1];

    offset = [0 (1/n) 0;
              0 (1/n) 0;
              0 (1/n) 0];

    verts = zeros(0,3);
    tris  = zeros(0,3);
    for i = 0:(n-1)
        verts = [verts; (oneTriVerts + i * offset)];
        tris = [tris; i*3+1, i*3+2, i*3+3];
    end

    %%% Draw the mesh, with color corresponding to the z coordinate.

    trimesh(tris, verts(:,1), verts(:,2), verts(:,3), verts(:,3));
    title(sprintf('n = %d', n))
    shading interp
    axis equal
like image 701
Neil Forrester Avatar asked Sep 21 '13 20:09

Neil Forrester


1 Answers

I think that after a certain threshold, MATLAB switched to the OpenGL renderer for better performance (hardware acceleration). Unfortunately, it is not without bugs.

I haven't closely looked at how you are building the triangular faces (there might a problem with how they are ordererd), but an easy solution is to explicitly set the rendering method. Simply add the following call at the end of your function:

set(gcf, 'Renderer','zbuffer')

EDIT

The workaround above should do just fine. Now the real problem is not a buggy OpenGL, but rather a documented limitation:

OpenGL does not do colormap interpolation. If you create a surface or patch using indexed color and interpolated face or edge coloring, OpenGL interpolates the colors through the RGB color cube instead of through the colormap.

Note that the TRIMESH call is equivalent to the following:

patch('Faces',tris, 'Vertices',verts, 'FaceVertexCData',verts(:,3), ...
    'EdgeColor','none', 'FaceColor','interp', 'CDataMapping','scaled')

So for each vertex you specify a color equal to its z-coordinate (you only have two unique values, either 0 or 1). This is interpreted as an indexed color into the current figure's colormap using scaled mapping (the default is the jet colormap). So the two colors end up being:

clr = jet(64);    % default colormap
clr(1,:)          % blueish color [0 0 0.5625] mapped from 0
clr(end,:)        % reddish color [0.5 0 0] mapped from 1

Unfortunately as the quote above explains, OpenGL renderer will not do the interpolation using the colors of colormap palette, rather perform the interpolation in the RGB colorspace between the two colors above. Thus we get the blue-red gradient you saw.

So your only option is to use one of the two other renderers, zbuffer being the best method here.


Here is the code to see the difference between the two rendering methods:

% plot patch
clf
patch('Faces',tris, 'Vertices',verts, 'FaceVertexCData',verts(:,3), ...
    'EdgeColor','none', 'FaceColor','interp', 'CDataMapping','scaled')
view(3)
axis vis3d
colorbar

% choose one of the two
set(gcf, 'Renderer','opengl')
set(gcf, 'Renderer','zbuffer')

OpenGL

opengl renderer

Z-Buffer

zbuffer renderer

like image 190
Amro Avatar answered Oct 23 '22 11:10

Amro