Herramientas de usuario

Herramientas del sitio


wiki2:cpp:libraries:openframeworks:creating_graphics

Diferencias

Muestra las diferencias entre dos versiones de la página.

Enlace a la vista de comparación

Ambos lados, revisión anterior Revisión previa
Próxima revisión
Revisión previa
wiki2:cpp:libraries:openframeworks:creating_graphics [2015/10/08 16:32]
alfred
wiki2:cpp:libraries:openframeworks:creating_graphics [2020/05/09 09:25] (actual)
Línea 45: Línea 45:
 } }
 </​code>​ </​code>​
 +ofPolyline is really a class meant to be used to do operations over polylines, like simplifications,​ smoothing... Also ofPolyline can only draw outlines, not filled shapes.
  
  
 +==== ofPath ====
 +<code cpp>
 +ofPath path;
  
 +void ofApp::​setup(){
 +    path.moveTo(20,​20);​
 +    path.lineTo(40,​20);​
 +    path.lineTo(40,​40);​
 +    path.lineTo(20,​40);​
 +    path.close();​
 +}
  
 +void ofApp::​draw(){
 +    path.draw();​
 +}
 +</​code>​
 +
 +
 +ofPath draws filled shapes by default, however ofFill and ofNoFill do not effect ofPolyline or ofPath. That's because they also follow the more modern openGL paradigm where most global state values are deprecated. Instead, you can set the color per vertex on every shape or use a shader to specify a color or line thickness.
 +
 +ofPath allows us to specify if we want to draw it with outline, fill, color, and width per path, so those properties are local to each path instead of specifying them globally:
 +
 +<code cpp>
 +ofPath path;
 +
 +void ofApp::​setup(){
 +    path.moveTo(20,​20);​
 +    path.lineTo(40,​20);​
 +    path.lineTo(40,​40);​
 +    path.lineTo(20,​40);​
 +    path.close();​
 +    path.setStrokeColor(ofColor::​blue);​
 +    path.setFillColor(ofColor::​red);​
 +    path.setFilled(true);​
 +    path.setStrokeWidth(2);​
 +}
 +
 +void ofApp::​draw(){
 +    path.draw();​
 +}
 +</​code>​
 +
 +When we draw a path the first time, ofPath internally calculates it's tessellation and stores it in an ofVboMesh, keeping its vertices in the GPU. If the vertices haven'​t changed when we draw an ofPath the next time, the vertices don't need to be uploaded again to the graphics card. This makes things really fast. You can actually access that tessellation using:
 +<code cpp>
 +
 +ofPath path;
 +ofVboMesh tessellation;​
 +
 +
 +void ofApp::​setup(){
 +    path.moveTo(20,​20);​
 +    path.lineTo(40,​20);​
 +    path.lineTo(40,​40);​
 +    path.lineTo(20,​40);​
 +    path.close();​
 +    path.setStrokeColor(ofColor::​blue);​
 +    path.setFillColor(ofColor::​red);​
 +    path.setFilled(true);​
 +    path.setStrokeWidth(2);​
 +    tessellation = path.getTessellation();​
 +}
 +
 +void ofApp::​draw(){
 +    tessellation.drawWireframe();​
 +}
 +</​code>​
 +
 +The tessellation only represents the fill of our shape. If the path has no fill, it'll return an empty mesh. We can also access the outlines of an ofPath as a vector of ofPolylines using ''​path.getOutline()''​
 +
 +''​path.setMode(ofPath::​POLYLINES)''​ which will make that path override the creation of primitives and work directly with ofPolylines which can be slightly faster in certain cases, mostly if you are creating a really high number of paths and modifying them frequently.
 +
 +=== Notes ===
 +
 +  * If you do not close the path then the last point won't join the first one.
 +
 +===== 3D =====
 +
 +==== Posicioning things ====
 +You can use the next functions...
 +<code cpp>
 +ofTranslate(20,​20);​
 +ofRotate(45);​
 +ofRect(20,​20,​20,​20);​
 +</​code>​
 +... enclosed that between ''​ofPushMatrix''​ or ''​ofPopMatrix''​. But they are also deprecated. Each call to them, or the gl equivalents for that matter, are doing a multiplication of 4x4 matrices. But we can avoid it somehow. Instead of doing all of the multiplications of the matrices every frame, we can use an ofMatrix4x4 for each shape we use, do all of that shape'​s transformations once (or every time the shape moves), and apply them later when we want to draw that frame:
 +<code cpp>
 +ofPath path
 +ofMatrix4x4 m;
 +
 +//ofApp.cpp
 +void ofApp::​setup(){
 +    path.moveTo(20,​20);​
 +    path.lineTo(40,​20);​
 +    path.lineTo(40,​40);​
 +    path.lineTo(20,​40);​
 +    path.close();​
 +    m.rotate(45);​
 +    m.translate(20,​20);​
 +}
 +
 +void ofApp::​draw(){
 +    ofMultMatrix(m);​
 +    path.draw();​
 +}
 +</​code>​
 +
 +In openFrameworks,​ there'​s a utility class called ofNode, that allows you to apply complex transformations like set an object to look to another, set a hierarchy of nodes... When working with 3D it's useful to keep an ofNode along with every mesh that represents it's transformations,​ so when you draw each mesh, instead of using ofTranslate,​ rotate, scale you can just apply the transformation of it's node using node.transformGL(). This will multiply the current matrix by the one in the node. When you are done you can use node.restoreTransformGL()to go back to the previous state. Instead of using ofTranslate/​Rotate/​Scale,​ it is usually easier to implement an ofNode associated to each mesh or shape that you draw. 
 +
 +==== ofCamera ====
 + The FOV, or field of view, is the angle that the virtual camera, that we are looking through, can see. We also need the near and far clip planes. These define the distance at which things begin and end to be drawn. Finally, we need the width and height of the viewport. All of those parameters define a frustrum, a 6 sides polyhedra that defines the bounding box of things that will appear in the screen as well as how they'​ll be projected from 3D into 2D.
 +
 +We also have a second matrix, called the model view, which defines the location of the virtual camera through which we look at the scene. The view matrix is actually the inverse of the matrix that defines the position of the camera, so when we alter it we actually transform the position, rotation and scale of things being drawn. It's this matrix that gets modified by default when we use ofTranslate,​ ofRotate and ofScale.
 +
 +By default, openFrameworks sets a projection matrix with a FOV of 60, width and height of the screen, and clip planes automatically calculated from the other parameters. It then calculates a model view that "moves the virtual camera"​ back from 0,0 to a position where the top left of the screen matches with 0,0 and the bottom right with width,​height.
 +
 +OpenFrameworks changes the way to work with coordinates that OpenGL uses to make it easier to work with images or mouse coordinates in openFrameworks. You can change that by calling: ''​ofSetOrientation(OF_ORIENTATION_DEFAULT,​ false)''​ being false a false vertical flip so y will grow upwards.
 +
 +When using an ofCamera, 0,0 will be at the center of the screen, and y will grow upwards. With the default settings, the top,left of the screen will be (-w/2,h/2) and the bottom,​right (w/2,-h/2). camera.begin() draws depending on the camera configuration and then calling camera.end() stops using that camera and go back to the perspective that openFrameworks sets by default.
 +<code cpp>
 +ofCamera camera;
 +
 +// ofApp.cpp
 +
 +void ofApp::​setup(){
 +    camera.setFov(60); ​ // this will actually do nothing since 60 is the default
 +}
 +
 +void ofApp::​draw(){
 +    camera.begin();​
 +    // draw something
 +    camera.end();​
 +}
 +</​code>​
 +
 +==== ofMesh ====
 +The ofMesh class allows us to represent a 3D model. Internally, it's just a bunch of vectors. Each vector represents one mesh attribute. Those attributes are: vertices, colors, texture coordinates and normals.
 +<code cpp>
 +ofMesh mesh;
 +
 +
 +void ofApp::​setup(){
 +    mesh.addVertex(ofVec3f(20,​20));​
 +    mesh.addColor(ofColor::​red);​
 +    mesh.addVertex(ofVec3f(40,​20));​
 +    mesh.addColor(ofColor::​red);​
 +    mesh.addVertex(ofVec3f(40,​40));​
 +    mesh.addColor(ofColor::​red);​
 +    mesh.addVertex(ofVec3f(20,​40));​
 +    mesh.addColor(ofColor::​red);​
 +    mesh.setMode(OF_PRIMITIVE_TRIANGLE_FAN);​
 +}
 +
 +void ofApp::​draw(){
 +    mesh.draw();​
 +}
 +</​code>​
 +Is the same than...
 +<code cpp>
 +ofMesh mesh;
 +
 +// ofApp.cpp
 +
 +void ofApp::​setup(){
 +    mesh.addVertex(ofVec3f(20,​20));​
 +    mesh.addVertex(ofVec3f(40,​20));​
 +    mesh.addVertex(ofVec3f(40,​40));​
 +    mesh.addVertex(ofVec3f(20,​40));​
 +    mesh.addColor(ofColor::​red);​
 +    mesh.addColor(ofColor::​red);​
 +    mesh.addColor(ofColor::​red);​
 +    mesh.addColor(ofColor::​red);​
 +    mesh.setMode(OF_PRIMITIVE_TRIANGLE_FAN);​
 +}
 +
 +void ofApp::​draw(){
 +    mesh.draw();​
 +}
 +</​code>​
 +
 +
 +When we add texture coordinates,​ we probably want to use a texture while drawing that mesh, to use a texture we use bind() on an ofImage or ofTexture and call unbind() when we are done using it. We can even draw several meshes that use the same texture by calling bind/unbind once and drawing all of them. We could even combine color and texture tinting the texture with the color we apply to each vertex.
 +
 +<code cpp>
 +ofMesh mesh;
 +ofImage img;
 +
 +mesh.addVertex(ofVec3f(200,​200));​
 +mesh.addTexCoord(ofVec2f(0,​0));​
 +mesh.addVertex(ofVec3f(400,​200));​
 +mesh.addTexCoord(ofVec2f(20,​0));​
 +mesh.addVertex(ofVec3f(400,​400));​
 +mesh.addTexCoord(ofVec2f(20,​20));​
 +mesh.addVertex(ofVec3f(200,​400));​
 +mesh.addTexCoord(ofVec2f(0,​20));​
 +mesh.setMode(OF_PRIMITIVE_TRIANGLE_FAN);​
 +img.loadImage("​img.png"​);​
 +
 +img.bind();
 +mesh.draw();​
 +img.unbind();​
 +</​code>​
 +
 +==== ofVboMesh ====
 +
 +ofVboMesh is a simple class that encapsulates a vbo and inherits from ofMesh. When it's drawn, instead of uploading all the vertices to the graphics card every time call draw on it, it uploads them once when we draw for the first time and only uploads them again if they change. Usually when working with openGL it is advised to use ofVboMesh instead of ofMesh.
 +
 +There'​s a case where using an ofVboMesh might be slower, and that's if we want to draw an ofVboMesh, modify it's vertices and then draw it again in the same frame. The problem here is that openGL doesn'​t really draw things as soon as we tell it to draw. Instead, it stores all the drawing commands and then draws all of them at once and in parallel with the execution of our program. When we try to draw a vbo, modify it's contents and then draw it again in the same frame, openGL would need to really draw the vbo at that exact moment, which means drawing everything else up to that point. That would slow things down a lot. If you need to do something like this, make a copy of the vbo and modify the copy instead of the original. In general don't draw, modify and redraw a vbo in the same frame. So do not do this:
 +<code cpp>
 +// ofApp.h
 +
 +ofVboMesh mesh;
 +
 +// ofApp.cpp
 +
 +void ofApp::​setup(){
 +    mesh.addVertex(ofVec3f(20,​20));​
 +    mesh.addVertex(ofVec3f(40,​20));​
 +    mesh.addVertex(ofVec3f(40,​40));​
 +    mesh.addVertex(ofVec3f(20,​40));​
 +    mesh.setMode(OF_PRIMITIVE_TRIANGLE_FAN);​
 +}
 +
 +void ofApp::​draw(){
 +    mesh.draw();​
 +    mesh.getVertices()[1].x+=0.1;​
 +    mesh.draw();​ // slow!!
 +}
 +</​code>​
 +... And do:
 +<code cpp>
 +// ofApp.h
 +
 +ofVboMesh mesh;
 +ofVboMesh mesh2;
 +
 +// ofApp.cpp
 +
 +void ofApp::​setup(){
 +    mesh.addVertex(ofVec3f(20,​20));​
 +    mesh.addVertex(ofVec3f(40,​20));​
 +    mesh.addVertex(ofVec3f(40,​40));​
 +    mesh.addVertex(ofVec3f(20,​40));​
 +    mesh.setMode(OF_PRIMITIVE_TRIANGLE_FAN);​
 +    mesh2 = mesh;
 +}
 +
 +void ofApp::​update(){
 +    mesh.getVertices()[1].x+=0.1;​
 +    mesh2.getVertices()[1].x=mesh.getVertices()[1].x + 0.1;
 +}
 +
 +void ofApp::​draw(){
 +    mesh.draw();​
 +    mesh2.draw();​ // fast!!
 +}
 +</​code>​
 +
 +You can change the way the mesh is drawn using the next constants for the ''​setMode''​ method: OF_PRIMITIVE_TRIANGLES,​ OF_PRIMITIVE_TRIANGLE_STRIP,​ OF_PRIMITIVE_TRIANGLE_FAN,​ OF_PRIMITIVE_LINES,​ OF_PRIMITIVE_LINE_STRIP,​ OF_PRIMITIVE_LINE_LOOP,​ OF_PRIMITIVE_POINTS.
 +
 +You can draw the wireframe, the points using the ''​drawWireframe()''​ and ''​drawVertices()''​ methods.
 +
 +==== of3dPrimitive ====
 +of3dPrimitive is a helper class that encapsulates an ofVboMesh and inherits from ofNode. You can call any method you would call on an ofNode, because of how inheritance works, it is actually an ofNode so we can change it's position, rotate it, make it look to some other node, add it to a node hierarchy... And when you call draw on it, it'll draw the mesh it contains applying the transformation defined by it's node.
 +
 +There'​s several predefined 3D primitives, like...
 +  * ofPlanePrimitive
 +  * ofSpherePrimitive
 +  * ofIcoSpherePrimitive
 +  * ofCylinderPrimitive
 +
 +Also you can create your own:
 +<code cpp>
 +of3dPrimitive primitive;
 +
 +// ofApp.cpp
 +
 +void ofApp::​setup(){
 +    primitive.getMesh().addVertex(ofVec3f(20,​20));​
 +    primitive.getMesh().addVertex(ofVec3f(40,​20));​
 +    primitive.getMesh().addVertex(ofVec3f(40,​40));​
 +    primitive.getMesh().addVertex(ofVec3f(20,​40));​
 +    primitive.getMesh().setMode(OF_PRIMITIVE_TRIANGLE_FAN);​
 +}
 +
 +void ofApp::​update(){
 +    primitive.move(ofVec3f(10,​0,​0));​
 +}
 +
 +void ofApp::​draw(){
 +    primitive.draw();​
 +}
 +</​code>​
wiki2/cpp/libraries/openframeworks/creating_graphics.1444321939.txt.gz · Última modificación: 2020/05/09 09:25 (editor externo)