I use this command to draw a mesh
trimesh(F, X, Y, Z,...
'EdgeColor','k','FaceColor','flat','FaceVertexCData',c, ...
'CDataMapping','scaled','FaceLighting','flat','BackFaceLighting','lit');
camlight;
When I do this, most faces are lit as expected, but some appear to be unlit; i.e, evern when I rotate the view to other angles and change the light position (as it is dependent on the camera), I still do not see them lit.
It seems to me like a classical problem with the normals (i.e my normal is in the opposite direction), though I thought that in the call to trimesh the arguments 'BackFaceLighting','lit' take care of that.
Any ideas?
Try to set the renderer to opengl by using the following command:
set(gcf,'Renderer','opengl');
It usually handles 3D better than the standard renderer.
Related
I'm pretty new to shader graph and shaders in general. I'm working on a 2D project and I'm trying to make a shader that rotates an arrow to make a flow-like material and use it on a sprite shape.
Basically what I want to do is make a proper version of this:
What I'm currently doing is multiplying the Y position of the position node by an exposed vector 1 and using it in Rotate node (which I know is pretty hacky and won't work if the shape is not an arc.)
Aligning UV with arbitrary mesh seems bit hard. Why not bend pre-made mesh instead? Graph below bends vertex positions around axis Z at given point and strength (0 makes mesh invisible tho), but, you can easily replace that Position node with UV and plug results into Sample Texture 2D. I just guess bending a mesh will give you better/easier results.
Create a subdivided and well UV-mapped rectangle plane
Bend that plane with a vertex shader (attached graph bends around Z axis)
graph is based on code from Blender source
I would like to texture map a 2D image to a 3D surface. I would like to do it in GUI so the user can rotate/shift/zoom the surface with the camera toolbar and then the image will be mapped to the visible portion of the surface.
I know I can get the camera position with campos command, but how do I get the camera orientation?
Any idea? Or maybe you can suggest a better approach?
Thanks!
To answer the question from your title (from what I understand), you can assign a texture map to 3D data using the accurately named texturemap value of the FaceColor property (original right?) as well as the appropriate CData (here a 2D image).
Simple example:
clear
clc
A = imread('peppers.png');
%// Generate dummy surface plot
[X,Y] = meshgrid([-2:.25:2]);
Z = X.*exp(-X.^2 -Y.^2);
surf(X,Y,Z,'CData',A,'FaceColor','texturemap')
It looks like this:
By default in a figure window you can zoom/move around as you want. For the 2nd part of your question, I think you should carefully read the docs for campos and related functions to get/set the camera position. Since building a GUI to perform that task requires much effort I think your best bet would be to try something on your own and ask questions about it here if you are stuck somewhere.
Hope that helps!
I have a big FEM model from where I can get the "surface" of the model, say the elements and vertex that define the surface of that FEM model. For plotting purposes (nice plots are always a win!) I want to plot it nicely. My approach is just to use
lungs.Vertex=vtx;
lungs.Faces=fcs;
patch(lungs,'facecolor','r','edgecolor','none')
NOTE: I need edgecolor none, as this is 4D data and different FEM have different triangulation, if edges are plotted user will be dizzy.
However this will output everything in a really plain red color, which is not nice (as it can not show the complexity of the figure, which are lungs, for the attentive to details).
therefore I decided to use ligthing:
camlight; camlight(-80,-10); lighting phong;
But again, this is not entirely correct. Actually it seems that the patch nromals are not computed correctly by Matlab.
My supposition is that maybe the patches are not always defined counter-clockwise and therefore some normals go to the wrong direction. However that is something its not straightforward to check.
Anyone has a similar problem, or ahint of how should I aproach this problem in order to have a nice surface ploted here?
EDIT
Just for the shake of plotting, here is the result obtained with #magnetometer answer:
If your model gives you outward oriented normals, you can re-sort the faces of your model so that Matlab can properly calculate it's own normals. The following function works if you have triangular faces and outward oriented normals:
function [FaceCor,nnew]=SortFaces(Faces,Normals,Vertices)
FaceCor=Faces;
nnew=Normals*0;
for jj=1:size(Faces,1)
v1=Vertices(Faces(jj,3),:)-Vertices(Faces(jj,2),:);
v2=Vertices(Faces(jj,2),:)-Vertices(Faces(jj,1),:);
nvek=cross(v2,v1); %calculate normal vectors
nvek=nvek/norm(nvek);
nnew(jj,:)=nvek;
if dot(nvek,Normals(jj,:))<0
FaceCor(jj,:)=[Faces(jj,3) Faces(jj,2) Faces(jj,1)];
nnew(jj,:)=-nvek;
end
end
If your FEM model doesn't give you outward directed normals, one way could be to reconstruct the surface using e.g. a crust algorithm that gives you outward directed normals or correctly oriented patches.
EDIT:
As you don't have normals, the only solution that comes to my mind is to reconstruct the surface. This implementation of the crust algorithm has worked well for me in the past. All you need to do is:
[FacesNew,NormalsNew]=MyRobustCrust(Vertices);
If I remember correctly, FacesNew are not yet oriented counterclockwise, but you can use the SortFaces algorithm I posted above to correct for this, as you now have correctly oriented face normals, i.e. run:
[FaceCor,~]=SortFaces(FacesNew,NormalsNew,Vertices)
If you use Matlab's reducepatch (e.g. reducedmodel=reducepatch(fullmodel,reduction); ) to reduce the number of vertices, you will have to reconstruct the surface again, as reducepatch doesn't seem to keep the correct orientation of the patches.
Is there some way of using the surf() style data format to create a heat map?
I have a bunch of scattered data of the form z=f(x,y) (so I used TriScatteredInterp to make it work with meshgrid) and I'd like to visualize z with a heat map. surf already does something similar to what I want, except you have to manually rotate the graph so the view is top down looking at the XY plane.
Basically, I'd like something similar to this:
But surf gives you this by default:
Although the answers here already show how to do this with surf, rendering the 3d surface feels a little like overkill...
pcolor creates the required images directly
(with slightly better results -surf has a gap next to the axes)
code
figure(1)
pcolor(peaks)
figure(2)
surf(peaks)
view(2)
results
pcolor
surf
Adding to Ben's answer, you can use the view command. view allows you to rotate your plot to whatever camera angle you wish.
In general, you call the command like so:
view(AZ, EL);
AZ is the azimuth or horizontal rotation, while EL is the vertical elevation. These are both in degrees.
In your case, once you plot your surf plot, use view(0, 90); before you go to the next subplot. view(0, 90); is the default 2-D view, and this looks directly overhead.
By doing this, you avoid having to rotate your plot manually, then using campos to determine what the camera position is at given your plot. view(0, 90); should give you what you need.
Sidenote
Doing view(2); also gives you the default 2D view, which is equal to view(0, 90); as we talked about. By doing view(3);, this gives you the default 3D view as seen in your plots. FWIW, the default azimuth and elevation for a 3D plot is AZ = -37.5, EL = 30, in degrees of course.
Rotate the view to what you want. Then type campos in the terminal. This shows you the camera position. You can then use campos( your_desired ) to set the camera position for future plots.
For example, the x, y view is usually:
campos([4.0000 2.5000 86.6025])
I have a surface plot I'm trying to do. x is an 11 element vector, y a 300 element vector and z a 300*11 element matrix.
When I try to plot it like this:
surf(x y z)
The surface plot doesn't show up. The axes are there but there is no surface plot.
However, if for some reason I do a surface plot of a subset of the matrix like this:
surf(x y(1:31) z(1:31,:))
Then it works and the plot shows up.
As soon as I increase the number in the brackets to 32 it stops working. If I change the range from 2:32 then it works, so it's nothing to do with the data just the size of the matrices.
What's going on here? How do I fix it?
P.S I'd attach the code but it's a bit long and complex, and imports .txt files to load into the x and y vectors.
Sometimes, it can help to change Matlab's figure renderer, which is basically the backend that performs the drawing. Options are painters, zbuffer, and OpenGL.
Since it is a figure property, you can apply it to a specific figure, e.g.:
set(gcf(), 'Renderer', 'painters')
or update the default figure properties (if always needed, you could put it in your user-specific startup.m):
set(0, 'Renderer', 'painters')
Similarly, to get the current Renderer state, use get instead of set:
get(gcf(), 'Renderer')
Different renderers have different performance properties (e.g. OpenGL renderer can use hardware acceleration, if supported), but also different quirks (in my experience, frame capturing using getframe() works with some renderers while using remote desktop login, but not all). While I don't know the exact reason for your problem, it may be one of these weird quirks, so try changing the renderer.
From the Renderer property documentation:
Rendering method used for screen and printing.
Selects the method used to render MATLAB graphics. The choices are:
painters — The original rendering method used by MATLAB is faster when the figure contains only simple or small graphics objects.
zbuffer — MATLAB draws graphics objects faster and more accurately because it colors objects on a per-pixel basis and MATLAB renders only those pixels that are visible in the scene (thus eliminating front-to-back sorting errors). Note that this method can consume a lot of system memory if MATLAB is displaying a complex scene.
OpenGL — OpenGL is a renderer that is available on many computer systems. This renderer is generally faster than painters or zbuffer and in some cases enables MATLAB to access graphics hardware that is available on some systems.
Looks at the change in the min/max values of the axis along the left side (y-axis) and the top (z-axis). I think it's still there but it's just very very small.
Try setting the axis afterwards like this:
axis([6E-6 8E-6 9.2E14 10E14 0.96 1.06 -1 1])
Note: the E-6 might be E-8, I can't really tell from the image...
This is based off the code of: axis([xmin xmax ymin ymax zmin zmax cmin cmax])