[OpenGL ES 04] 3D Transformation Practices: translation, rotation, scaling

Source: Internet
Author: User
Tags mathematical functions
ArticleDirectory
    • 1. Create a project
    • 2. Describe the four-pyramid
    • 3. Introduction to the primitive mode:
    • V. rotating and Scaling
    • Vi. Automatic rotation and resetting
    • VII. Summary

[OpenGL ES 04] 3D Transformation Practices: translation, rotation, scaling

Luo chaohui (http://www.cnblogs.com/kesalin)

This article follows the "signature-non-commercial use-consistency" creation public agreement

Preface

In the previous article [OpenGL ES 03] 3D Transformation: model, view, projection, and viewport, we have introduced in detail the mathematical knowledge related to 3D transformation, as well as the basic model transformation: translation, if you have not read the previous article or are not familiar with this knowledge, you must first understand the previous article and continue. If you are ready to continue, I will introduce how to use this knowledge in OpenGL ES to render a pyramid and translate it, in addition, this article also demonstrates how to combine OpenGL view and uiview (Note: This is not recommended in actual use, the combination of OpenGL view and uiview has a great impact on efficiency ).

This articleCodeYou can get it from here: Click here to download (tutorial03), and the final result is as follows:

 

1. Create a project

1. Create a project

From today on, we will no longer create windows and views from the beginning, and create a single view application project named tutorial03:

2. We can see from the previous section that in this tutorial, we need two views: OpenGL view and uiview. Therefore, open mainstoryboard. storyboard and add two views to it. The one above is used for OpenGL view, and the other one is used for control view. Adjust the size and position of the two views and set the background of the following view to light blue for easy differentiation.

3. Copy glesutils in the tutorial02 tutorial. h, glesutils. M openglview. h, openglview. m and fragmentshader. glsl, vertexshader. glsl is added to tutorial03 and to the project. Then, opengles and quartzcore framework are added.

4. In viewcontroller. H, add iboutlet for the two views:

 
# Import "openglview. H" @ interface viewcontroller: uiviewcontroller @ property (nonatomic, strong) iboutlet uiview * controlview; @ property (nonatomic, strong) iboutlet openglview * openglview;

Add under @ implementation viewcontroller in viewcontroller. m

 
@ Synthesize controlview, openglview;

And clear in viewdidunload:

 
-(Void) viewdidunload {[Super viewdidunload]; [self. openglview cleanup]; self. openglview = nil; self. controlview = nil ;}

5. Return to mainstoryboard. storyboard, set the type of the view above to openglview, and associate the relevant iboutlet:

 

2. Describe the four-pyramid

1. In tutorial02, only one triangle is depicted. This time we will depict a real 3D object: a pyramid. Add the following functions to render () in openglview. M:

-(Void) drawtricone {glfloat vertices [] = {0.5f, 0.5f, 0.0f, 0.5f,-0.5f, 0.0f,-0.5f,-0.5f, 0.0f,-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f,-0.707f,}; glubyte indices [] = {0, 1, 1, 2, 2, 3, 3, 0, 4, 0, 4, 1, 4, 2, 4, 3}; glvertexattribpointer (_ positionslot, 3, gl_float, gl_false, 0, vertices); glablevertexattribarray (_ positionslot ); // draw lines // gldrawelements (gl_lines, sizeof (indices)/sizeof (glubyte), gl_unsigned_byte, indices );}

This time we use the vertex Index Array combined with gldrawelements for rendering, while gldrawarrays is used in tutorial02. What are the advantages of using a vertex index array? Obviously, we can reduce the memory consumption for storing duplicate vertices. For example, in the index table in this example, we reuse vertex 0, 1, 2, 3, 4, which correspond to five vertices in the vertices array (three floating point numbers form one vertex ).

GldrawelementsThe function is prototype:

 
Void gldrawelements (glenum mode, glsizei count, glenum type, const glvoid * indices );

The first parameter mode is the mode for describing elements. Its valid values are gl_points, gl_lines, gl_line_strip, gl_line_loop, gl_triangles, gl_triangle_strip, and gl_triangle_fan. The specific meanings of these modes are described below.

The second parameter, Count, is the number of vertex indexes. "type" refers to the Data Type of the vertex index. Because the index is always positive, the index must be a non-floating-point non-Signed index, therefore, it can only be gl_unsigned_byte, gl_unsigned_short, or gl_unsigned_int. To reduce memory consumption, try to use the minimum specification type such as gl_unsigned_byte.

The third parameter, indices, is an array that stores vertex indexes. (Indices is the plural form of index. Many Words in 3D have special plural forms .)

2. Use

Modify the render () function as follows:

 
-(Void) render {glclearcolor (0, 1.0, 0, 1.0); glclear (gl_color_buffer_bit); // setup viewport // glviewport (0, 0, self. frame. size. width, self. frame. size. height); [self drawtricone]; [_ context presentrenderbuffer: gl_renderbuffer];}

Compile and runProgram, You should be able to see a pyramid.

Maybe you will say, why doesn't it look like it? That's because you are looking down from the top of the pyramid, and the top of the index pyramid (that is, the point of the intersection in the middle of the graph) Looks flattened. If we add the rotation function to the program, it will be very obvious. After introducing the primitive mode, we will add the rotation function.

 

3. Introduction to the primitive mode:

There are three ways to draw line:

In addition, you can use the gllinewidth (glfloat width) function to set the line width. To obtain the currently set line width, you can use the following code:

 
Glfloat linewidthrange [2]; glgetfloatv (gl_aliased_line_width_range, linewidthrange );

In the tutorial02 tutorial, we use the gl_triangles pattern to depict a triangle. There are also three models for triangles:

OpenGL ES also supports the dot (sprite) mode: gl_points. In addition, you can use the built-in variable gl_pointsize to set the dot genie size, to obtain the size of the currently set vertex genie, use the following code:

 
Glfloat pointsizerange [2];
Glgetfloatv (gl_aliased_point_size_range, pointsizerange );

 

4. Translation

1. Add controls

To add the translation control, we need to add a slider on the view below to adjust the translation distance. To this end, open mainstoryboard. storyboard and add a uilabel and uislider to the uiview of the light blue background.

Set the Slider's maximum value 3, minimum value-3, and current value 0:

 

2. Add the control association code

Add the iboutlet and ibaction of the slider to viewcontroller. h and associate it with uislider.

 
@ Property (nonatomic, strong) iboutlet uislider * posxslider;-(ibaction) xslidervaluechanged :( ID) sender;

Add the following to viewcontroller. M:

 
@ Synthesize posxslider;-(ibaction) xslidervaluechanged :( ID) sender {uislider * slider = (uislider *) sender; float currentvalue = [slider value]; nslog (@ "> current X is % F", currentvalue );}

If you drag the slider, You can output the current value of the slider. Do not forget to clear the slider in viewdidunload:

 
Self. posxslider = nil;

 

3. Add the translation code.

In order to allow the three-pyramid to move on the X axis, open openglview. h and add the following to @ interface:

 
Float _ posx;

Then add the property:

 
@ Property (nonatomic, assign) float posx;

Open openglview. M and add:

 
@ Synthesize posx = _ posx;

 

4. Modify the vertex shader code.

Open vertexshader. glsl and modify the content as follows:

 
Uniform mat4 projection; Uniform mat4 modelview; Attribute vec4 vposition; void main (void) {gl_position = projection * modelview * vposition ;}

In the code above, we performed custom projection and Model View transformation on the input vertex. Do you still remember what we described earlier? Especially the left multiplication. Here, the vertex first changes the Model View and then implements the projection transformation.

5. added the Transform code.

Return to openglview. h and add the corresponding projection matrix and Model View matrix:

 
Ksmatrix4 _ modelviewmatrix; ksmatrix4 _ projectionmatrix;

Ksmatrix4 is a matrix defined in the mathematical auxiliary library glesmath using a 4*4 two-dimensional array. You can obtain glesmath. h and glesmath. C here and add them to the project. This mathematical auxiliary library contains some common 3D computing, which will be mentioned later.

In openglview. M, add slots for obtaining the Model View matrix and projection matrix variables at the end of setupprogram:

 
// Get the attribute position slot from program // _ positionslot = glgetattriblocation (_ programhandle, "vposition "); // get the uniform Model-View matrix slot from program // _ modelviewslot = glgetuniformlocation (_ programhandle, "modelview "); // get the uniform projection matrix slot from program // _ projectionslot = glgetuniformlocation (_ programhandle, "projection ");

Add the following two function declarations to openglview's anonymous category:

 
-(Void) setupprojection;-(void) updatetransform;

Then implement them in @ implemetion:

-(Void) setupprojection {// generate a perspective matrix with a 60 degree FOV // float aspect = self. frame. size. width/self. frame. size. height; ksmatrixloadidentity (& _ projectionmatrix); ksperspective (& _ projectionmatrix, 60.0, aspect, 1.0f, 20366f); // load projection matrix gluniformmatrix4fv (_ projectionslot, 1, gl_false, (glfloat *) & _ projectionmatrix. M [0] [0]);}

In the above Code, ksmatrixloadidentity is a mathematical function in glesmath to reset the specified matrix to a matrix of units. ksperspective works the same as glupersoective mentioned earlier. Here, we set the distance from the near-cropping surface of the cone to the observer to 1, the distance from the far-cropping surface to the observer to 20, and the angle of view to 60 degrees, and then load the projection matrix. The default observer position is at the origin point, and the line of sight is in the-z direction. Therefore, the near cropping area is actually in the z =-1 place, and the far cropping area is in the z =-20 place, objects whose Z value is not between (-1,-20) cannot be seen. Do not forget to call this function in the initialization code:

 
-(ID) initwithcoder :( nscoder *) adecoder {self = [Super initwithcoder: adecoder]; If (Self) {[self setuplayer]; [self setupcontext]; [self setupprogram];[Self setupprojection];} Return self ;}

The following code updates the Model View matrix to respond to user control.

 
-(Void) updatetransform {// generate a model view matrix to rotate/translate/scale // ksmatrixloadidentity (& _ modelviewmatrix ); // translate away from the viewer // kstranslate (& _ modelviewmatrix, self. posx, 0.0,-5.5); // rotate the triangle // ksrotate (& _ modelviewmatrix, 0.0, 1.0, 0.0, 0.0 ); // scale the triangle ksscale (& _ modelviewmatrix, 1.0, 1.0, 1.0); // load the Model-View matrix gluniformmatrix4fv (_ modelviewslot, 1, gl_false, (glfloat *) & _ modelviewmatrix. M [0] [0]);}

In the above Code, kstranslate, ksrotate, and ksscale are also mathematical functions in glesmath. They are used in the same way as gltranslate, glrotate, and glscale, respectively for Pan, scroll around, and zoom. Here, it is set to translate in the X direction, and the translation volume is determined by self. posx control, and this attribute is ultimately controlled by the slider, so that the user can move the object; set Z to-5.5 (between (-1,-20, as mentioned above). In addition, it is also set to rotate around the X axis to 0 degrees, and scale twice in the X, Y, and Z directions, these two functions are called for the next rotation and scaling control. Finally, load the Model View matrix.

Because we need to update the Model View matrix in time when the posx value changes, and re-describe it so that we can see the effect in time, we have to implement the setter and getter methods of the property posx:

 
-(Void) setposx :( float) X {_ posx = x; [self updatetransform]; [self render];}-(float) posx {return _ posx ;}

6. Now, the lower-layer control code is ready. Let's implement the response code of the Upper-layer UI. Modify the xslidervaluechanged method in viewcontroller. m as follows:

 
-(Ibaction) xslidervaluechanged :( ID) sender {uislider * slider = (uislider *) sender; float currentvalue = [slider value]; openglview. posx = currentvalue; nslog (@ "> current X is % F", currentvalue );}

7. Compile and run the slider, and you will be able to control the four-pyramid moving back and forth in the X direction. (Do you still remember that the position of X in front of the slider can only be between (-3, 3, this is to prevent the pyramid from going outside the screen ). Smart, you should easily implement how to control the movement in the Y and Z directions. Note that the value range of the Y slider is (-3, 3), the default value is 0, and the value range of the Z slider is (-10, -1). The default value is "-5.5. This setting ensures that the object is not completely out of the range defined by the cone, because objects outside the cone are invisible. For example, the top part of the four-pyramid runs out of the near cropping surface of the video cone, so the top part is cropped.

V. rotating and Scaling

1. Implement lower-level control code

With the foundation of control translation, we can easily control rotation and scaling. Let's start from the lower layer and modify the code in openglview. H (I put the code translated in the Y and Z directions here, and hope you can implement it yourself ):

 
Float _ posx; float _ posy; float _ posz; float _ rotatex; float _ scalez;} @ property (nonatomic, assign) float posx; @ property (nonatomic, assign) float posy; @ property (nonatomic, assign) float posz; @ property (nonatomic, assign) float scalez; @ property (nonatomic, assign) float rotatex;

Modify the code in openglview. M:

 
@ Synthesize posx = _ posx; @ synthesize posy = _ posy; @ synthesize posz = _ posz; @ synthesize scalez = _ scalez; @ synthesize rotatex = _ rotatex;

As mentioned above, we need to implement the setter and getter methods of these attributes on our own. They are not listed here. They are similar to the setposx and posx implementations. We need to set these attributes to control the Model View matrix. Therefore, the updatetransform method must be updated as follows:

-(Void) updatetransform {// generate a model view matrix to rotate/translate/scale // ksmatrixloadidentity (& _ modelviewmatrix ); // translate away from the viewer // kstranslate (& _ modelviewmatrix, self. posx, self. posy, self. posz); // rotate the triangle // ksrotate (& _ modelviewmatrix, self. rotatex, 1.0, 0.0, 0.0); // scale the triangle ksscale (& _ modelviewmatrix, 1.0, 1.0, self. scalez); // load the Model-View matrix gluniformmatrix4fv (_ modelviewslot, 1, gl_false, (glfloat *) & _ modelviewmatrix. M [0] [0]);}

In this way, the pyramid can rotate the rotatex angle around the X axis and scale the scalez times in the z direction. Note: The default scalez attribute value is 0, so we need to initialize it. In addition, to facilitate the resetting of the model view matrix, we have implemented resettransform, which is called at the end of the initwithcoder method.

 
-(Void) resettransform {_ posx = 0.0; _ posy = 0.0; _ posz =-5.5; _ scalez = 1.0; _ rotatex = 0.0; [self updatetransform];}

2. Add upper-level control code

Open mainstoryboard. storyboard, and add the slider and button in the control view below ,:

Then modify viewcontroller. h:

 
@ Interface viewcontroller: uiviewcontroller @ property (nonatomic, strong) iboutlet uiview * controlview; @ property (nonatomic, strong) iboutlet openglview * openglview; @ property (nonatomic, strong) iboutlet publish * posxslider; @ property (nonatomic, strong) iboutlet uislider * posyslider; @ property (nonatomic, strong) iboutlet uislider * poszslider; @ property (nonatomic, strong) iboutlet uislider * scalezslider; @ property (nonatomic, strong) iboutlet uislider * rotatexslider;-(ibaction) Consumer :( ID) sender;-(ibaction) yslidervaluechanged :( ID) sender; -(ibaction) yslidervaluechanged :( ID) sender;-(ibaction) scalezslidervaluechanged :( ID) sender;-(ibaction) rotatexslidervaluechanged :( ID) sender;-(ibaction) autobuttonclick :( ID) sender; -(ibaction) resetbuttonclick :( ID) sender; @ end

Add the property-related code to viewcontroller. M:

 
@ Synthesize posxslider, posyslider, poszslider; @ synthesize scalezslider, rotatexslider;-(void) viewdidunload {[Super viewdidunload]; [self. openglview cleanup]; self. openglview = nil; self. posxslider = nil; self. posyslider = nil; self. poszslider = nil; self. scalezslider = nil; self. rotatexslider = nil; self. controlview = nil ;}

And event response methods:

-(Ibaction) yslidervaluechanged :( ID) sender {uislider * slider = (uislider *) sender; float currentvalue = [slider value]; openglview. posy = currentvalue; nslog (@ "> current y is % F", currentvalue);}-(ibaction) zslidervaluechanged :( ID) sender {uislider * slider = (uislider *) sender; float currentvalue = [slider value]; openglview. posz = currentvalue; nslog (@ "> current Z is % F", currentvalue);}-(ibaction) scalezslidervaluechanged :( ID) sender {uislider * slider = (uislider *) sender; float currentvalue = [slider value]; openglview. scalez = currentvalue; nslog (@ "> current Z scale is % F", currentvalue);}-(ibaction) rotatexslidervaluechanged :( ID) sender {uislider * slider = (uislider *) sender; float currentvalue = [slider value]; openglview. rotatex = currentvalue; nslog (@ "> current x rotation is % F", currentvalue);}-(ibaction) autobuttonclick :( ID) sender {}-(ibaction) resetbuttonclick :( ID) sender {}

The above code is very simple, basically similar to the Code for translating X, so it will not be described here. Return to mainstoryboard. storyboard and use the drag-and-drop technique to associate related attributes and events with controls.

3. Compile and run the program and slide different slider to experience the effect of 3D transformation.

 

Vi. Automatic rotation and resetting

1. Reset to restore the default status

Remember that I mentioned a method to reset the Model View matrix. To restore an object to its default state, you only need to call this method and re-plot it. Therefore, the resetbuttonclick implementation is as follows:

 
-(Void) resetcontrols {[posxslider setvalue: Self. openglview. posx]; [posyslider setvalue: Self. openglview. posy]; [poszslider setvalue: Self. openglview. posz]; [scalezslider setvalue: Self. openglview. scalez]; [rotatexslider setvalue: Self. openglview. rotatex];}-(ibaction) resetbuttonclick :( ID) sender {[openglview resettransform]; [openglview render]; [self resetcontrols];}

In the tutorial02 tutorial, the render method is a private method. Here, you need to change it to a public method. Resetcontrols is used to set the default value of the control to synchronize the values of the model matrix and the control. Remember to call this function in viewdidload. Compile and run. No matter how complicated the changes you have made, you only need to press the reset button to restore everything to its original state.

2. Automatic Rotation

Next we will add some fun points to make the object automatically rotate around the X axis. This is usually implemented through timer, but Apple provides us with a better solution: cadisplaylink (in fact it also uses timer internally ), it keeps our applications synchronized with the screen update rate, and calls the callback function provided by us whenever the screen is refreshed. Next we will use it to add a member variable to the anonymous category in openglview. M:

 
@ Interface openglview () {cadisplaylink * _ displaylink ;}

And add the callback function declaration:

 
-(Void) displaylinkcallback :( cadisplaylink *) displaylink;

Then implement this callback in @ implemetion below. If the screen is not refreshed once, the four-pyramid rotates around the X axis for a certain angle:

 
-(Void) displaylinkcallback :( cadisplaylink *) displaylink {self. rotatex + = displaylink. Duration * 90 ;}

Add the public method to openglview. h:

 
-(Void) toggledisplaylink;

Then implement it in openglview. M:

-(Void) toggledisplaylink {If (_ displaylink = nil) {_ displaylink = [cadisplaylink displaylinkwithtarget: Self selector: @ selector (displaylinkcallback :)]; [_ displaylink addtorunloop: [nsunloop currentrunloop] formode: Unknown];} else {[_ displaylink invalidate]; [_ displaylink removefromrunloop: [nsunloop currentrunloop] formode: Unknown]; _ displaylink = nil ;}}

In the above Code, we implemented a toggle switch method. If there is no automatic rotation, the automatic rotation will be started; if there is already an automatic rotation, it will be stopped. Cadisplaylink is easy to use. Create an object, set its target and callback function, and add it to the nsunloop to start running. To stop it, call the invalidate method first, then remove it from the nsunloop.

Finally, we add a call to toggledisplaylink in the response method of the button:

-(Ibaction) autobuttonclick :( ID) sender {[openglview toggledisplaylink]; uibutton * button = (uibutton *) sender; nsstring * text = button. titlelabel. text; If ([text isequaltostring: @ "Auto"]) {[Button settitle: @ "stop" forstate: uicontrolstatenormal];} else {[Button settitle: @ "Auto" forstate: uicontrolstatenormal];}

Compile and run the code. Click auto to automatically rotate the four-pyramid. Click it again to stop automatic rotation. Is it amazing?

 

VII. Summary

This article describes how to implement translation, rotation, and scaling through 3D transformation to better understand the theoretical knowledge introduced earlier; at the same time, it also realizes how to control objects in OpenGL view through uicontrol. In addition, it also introduces in detail the primitive mode and vertex index mode. At the end of the article, we will introduce how to use cadisplaylink to achieve animation effects.

Finally, the four-pyramid looks a little ugly.CubeHow is it? (Cuboid), this task is handed over to the viewer. I believe you can implement it.

 

 
 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.