tag:blogger.com,1999:blog-37657955902919191482024-03-12T16:14:39.705-07:00Back up of Simon Maurice's iPhone OpenGL ES TutorialUnknownnoreply@blogger.comBlogger23125tag:blogger.com,1999:blog-3765795590291919148.post-76014668106554070902012-01-31T20:26:00.000-08:002012-04-09T22:22:11.048-07:00Simon Maurice's iPhone OpenGL ES Tutorial -Back up<div dir="ltr" style="text-align: left;" trbidi="on">
<br />
<span style="font-size: x-large;"><b style="color: #990000;"></b></span><br />
<h1>
<span style="font-size: x-large;"><b style="color: #990000;">Simon Maurice's iPhone OpenGL ES Tutorial -Back up</b></span></h1>
<br />
<br />
<br />
<a href="http://simon-maurice-iphone-opengl-tutorial.blogspot.in/2012/01/opengl-es-00-xcode-project-set-up_31.html"><b>OpenGL ES 00 - Xcode Project Set Up</b></a><br />
<b><br /></b><br />
<a href="http://simon-maurice-iphone-opengl-tutorial.blogspot.in/2012/01/opengl-es-01-drawing-primitives-1.html"><b>OpenGL ES 01 - Drawing Primitives 1 - Triangles</b></a><br />
<b><br /></b><br />
<a href="http://simon-maurice-iphone-opengl-tutorial.blogspot.in/2012/01/opengl-es-02-drawing-primitives-2.html"><b>OpenGL ES 02 - Drawing Primitives 2 - Squares</b></a><br />
<b><br /></b><br />
<a href="http://simon-maurice-iphone-opengl-tutorial.blogspot.in/2012/01/opengl-es-03-transformations.html"><b>OpenGL ES 03 - Transformations</b></a><br />
<b><br /></b><br />
<a href="http://simon-maurice-iphone-opengl-tutorial.blogspot.in/2012/01/opengl-es-04-colour-and-shading.html"><b>OpenGL ES 04 - Color and Shading</b></a><br />
<b><br /></b><br />
<a href="http://simon-maurice-iphone-opengl-tutorial.blogspot.in/2012/01/opengl-es-05-texture-mapping-our-square.html"><b>OpenGL ES 05 - Texture Mapping Our Square</b></a><br />
<b><br /></b><br />
<script type="text/javascript">
<!--
google_ad_client = "ca-pub-9566972421287894";
/* 336x280 text AD-1 */
google_ad_slot = "7015408442";
google_ad_width = 336;
google_ad_height = 280;
//-->
</script>
<script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript">
</script>
<br />
<a href="http://simon-maurice-iphone-opengl-tutorial.blogspot.in/2012/01/opengl-es-06-objects-in-3d.html"><b>OpenGL ES 06 - Objects in 3D</b></a><br />
<b><br /></b><br />
<a href="http://simon-maurice-iphone-opengl-tutorial.blogspot.in/2012/01/opengl-es-07-translating-objects.html"><b>OpenGL ES 07 - Translating Objects Independently</b></a><br />
<b><br /></b><br />
<a href="http://simon-maurice-iphone-opengl-tutorial.blogspot.in/2012/01/opengl-es-08-final-primitives-points.html"><b>OpenGL ES 08 - The Final Primitives: Points and Lines in a Stride</b></a><br />
<b><br /></b><br />
<a href="http://simon-maurice-iphone-opengl-tutorial.blogspot.in/2012/01/opengl-es-09-blending-without-mr-buzzy.html"><b>OpenGL ES 09 - Blending Without Mr. Buzzy Part 1</b></a><br />
<b><br /></b><br />
<a href="http://simon-maurice-iphone-opengl-tutorial.blogspot.in/2012/01/opengl-es-10-multiple-textures.html"><b>OpenGL ES 10 - Multiple Textures, Repeating Textures, and The End of the Book Era</b></a><br />
<b><br /></b><br />
<a href="http://simon-maurice-iphone-opengl-tutorial.blogspot.in/2012/01/opengl-es-11-single-texture-multiple.html"><b>OpenGL ES 11 - Single Texture, Multiple Looks, Render to Texture, and Getting Inspired in Maths</b></a><br />
<b><br /></b><br />
<a href="http://simon-maurice-iphone-opengl-tutorial.blogspot.in/2012/01/opengl-es-12-landscape-view-handling.html"><b>OpenGL ES 12 - Landscape View & Handling Touches Part 1 - 2D World</b></a><br />
<b><br /></b><br />
<a href="http://simon-maurice-iphone-opengl-tutorial.blogspot.in/2012/01/opengl-es-13-moving-in-3d.html"><b>OpenGL ES 13 - Moving in 3D</b></a><br />
<b><br /></b><br />
<span id="goog_1182908233"></span><a href="http://www.blogger.com/"><b>OpenGL ES 13.5 - Moving in 3D Part 2: Some Theory that I Should Have Explained</b></a><span id="goog_1182908234"></span><br />
<b><br /></b><br />
<a href="http://simon-maurice-iphone-opengl-tutorial.blogspot.in/2012/01/opengl-es-14-blender-models-part-1.html"><b>OpenGL ES 14 - Blender Models Part 1: Learning Some Blender Internals</b></a><br />
<b><br /></b><br />
<a href="http://simon-maurice-iphone-opengl-tutorial.blogspot.in/2012/01/opengl-es-15-blender-models-part-2.html"><b>OpenGL ES 15 - Blender Models Part 2: Loading and Rendering</b></a><br />
<b><br /></b><br />
<a href="http://simon-maurice-iphone-opengl-tutorial.blogspot.in/2012/01/opengl-es-16-blender-models-part-3.html"><b>OpenGL ES 16 - Blender Models Part 3: Textures and UV Mapped Objects</b></a><br />
<b><br /></b><br />
<a href="http://simon-maurice-iphone-opengl-tutorial.blogspot.in/2012/01/opengl-es-17-collision-detection.html"><b>OpenGL ES 17 - Collision Detection</b></a><br />
<b><br /></b><br />
<a href="http://simon-maurice-iphone-opengl-tutorial.blogspot.in/2012/01/opengl-es-18-monkeys-on-your-back-and.html"><b>OpenGL ES 18 - Monkeys on Your Back and Geometric Shapes</b></a><br />
<b><br /></b><br />
<a href="http://simon-maurice-iphone-opengl-tutorial.blogspot.in/2012/01/opengl-es-19-so-you-wanna-be-iphone.html"><b>OpenGL ES 19 - So You Wanna Be an iPhone Games Programmer?</b></a><br />
<b><br /></b><br />
<b><a href="https://github.com/mauriceatron" target="_blank"><span style="font-size: large;">Source codes can be found here</span></a></b><br />
<br />
Since the original website of Simon Maurice seems not available, many people have been searching for this cool tutorial. This one is pretty good to start learning Open GL ES for iPhone programming . It gives a detailed explanation for every code and also gives sample source codes. Hope it may help you. All the best!<br />
<br />
<br />
<script type="text/javascript">
<!--
google_ad_client = "ca-pub-9566972421287894";
/* 728x15 LINK AD-3 */
google_ad_slot = "8880455539";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript">
</script>
<br />
<br />
<div style="color: #999999;">
<b><span style="color: #990000;"><span style="color: #cc0000;">Disclaimer:</span> <span style="color: #444444;">This
post and the whole blog is just a copy of
iPhone-OpenGL-ES-tutorial-Series (seems to have ) written by Simon
Maurice. This blog does NOT have a written permission or any kind of
permission from the original author (Simon Maurice). Please use it at
your own risk. If you have any complaints regarding any content of this
blog, please let me know I will remove it right away. Just hoping that
it would be helpful for people who are trying to learn OpenGL. Happy
coding!</span></span></b> </div>
<div style="color: #999999;">
<b><br /></b></div>
<div style="color: #999999;">
<b><br /></b></div>
<div style="color: #999999;">
<b><br /></b></div>
<div style="color: #999999;">
<b>Copyright 2009 Simon Maurice. All Rights Reserved.
</b></div>
<div style="color: #999999;">
<b>The code provided in these pages are for educational purposes only and
may not be used for commercial purposes without Simon Maurice’s
expressed permission in writing. Information contained within this site
cannot be duplicated in any form without Simon Maurice’s expressed
permission in writing; this includes, but is not limited to, publishing
in printed format, reproduced on web pages, or other forms of electronic
distribution.
</b></div>
<b><br /></b><br />
<b><a href="https://github.com/mauriceatron" target="_blank"><br /> </a></b><br />
<div style="color: #666666;">
<b><br /></b></div>
</div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-3765795590291919148.post-78637067764415574972012-01-31T20:23:00.001-08:002012-04-09T22:21:04.825-07:00OpenGL ES 00 - Xcode Project Set Up<div dir="ltr" style="text-align: left;" trbidi="on">
<script type="text/javascript">
<!--
google_ad_client = "ca-pub-9566972421287894";
/* 728x15 LINK AD-1 */
google_ad_slot = "1092558380";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript">
</script>
<br />
<h1 class="firstHeading">
<span style="color: #990000;">OpenGL ES 00 - Xcode Project Set Up</span></h1>
Since the original website of Simon Maurice seems not available, many
people have been searching for this cool tutorial. This one is pretty
good to start learning Open GL ES for iPhone programming . It gives a
detailed explanation for every code and also gives sample source codes.
Hope it may help you. All the best!<br />
<br />
<br />
<div style="color: #999999;">
<span style="color: #990000;"><b><span style="color: #cc0000;">Disclaimer:</span></b> <b><span style="color: #444444;">This
post and the whole blog is just a copy of
iPhone-OpenGL-ES-tutorial-Series (seems to have )written by Simon
Maurice. This blog does NOT have a written permission or any kind of
permission from the original author (Simon Maurice). Please use it at
your own risk. If you have any complaints regarding any content of this
blog, please let me know I will remove it right away. Just hoping that
it would be helpful for people who are trying to learn OpenGL. Happy
coding!</span></b></span> </div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<b>Copyright 2009 Simon Maurice. All Rights Reserved.
</b></div>
<div style="color: #999999;">
The code provided in these pages are for educational purposes only and
may not be used for commercial purposes without Simon Maurice’s
expressed permission in writing. Information contained within this site
cannot be duplicated in any form without Simon Maurice’s expressed
permission in writing; this includes, but is not limited to, publishing
in printed format, reproduced on web pages, or other forms of electronic
distribution.
</div>
<br />
<a href="https://github.com/mauriceatron" target="_blank">Source codes can be found here </a><br />
<div style="color: #666666;">
<b><br /></b></div>
<b><span style="color: #666666;">Original tutorial starts from here onwards..</span></b><br />
<br />
<br />
OpenGL ES on the iPhone is a cinch to set up with Xcode, especially
since Apple introduced the template with the SDK release. All we really
need is a place where you can get to and start adding in code quickly
and easily. This is what we’re going to do today.
<br />
<br />
<br />
To be quite honest, I really don’t think that it’s necessary to
type everything in from this and if you just want to get into the
meatier OpenGL tutorials which follow, skip this by all means. In this
case, there’s nothing wrong with skipping the set up as I’m not going to
go into the detail of it. Just head down to the end of this tutorial
and download the project file.
<br />
<br />
<br />
Start by firing up Xcode and creating a new project. Choose the
template called: “OpenGL ES Application” (Figure 1) and save the project
somewhere convenient for you.
<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglcikbA9Dx5g3BRnoCUAYhUONnnrc3NFZhIjFwaTytBGFbqpQ2oQxzTuk-RBBt25Yl-F1Y4cLDRFQJx0Yfp_9TLVy-Qj6_vHYyqzDd-6naCLVRt1OM-dvxQtN2DKqZ8VER4El1egZg_bG9/s1600/00-1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="468" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglcikbA9Dx5g3BRnoCUAYhUONnnrc3NFZhIjFwaTytBGFbqpQ2oQxzTuk-RBBt25Yl-F1Y4cLDRFQJx0Yfp_9TLVy-Qj6_vHYyqzDd-6naCLVRt1OM-dvxQtN2DKqZ8VER4El1egZg_bG9/s640/00-1.jpg" width="640" /></a></div>
<br />
<br />
<br />
<br />
OK, I’m going to assume at this point that you’ve probably already had a
quick look around this template at some point in the past, probably had
a look at the running application. What we’re going to do is to remove
the code for the spinning coloured square and convert the view to use a
depth buffer (ie make it “true” 3D). This will give us a space for our
tutorials.
<br />
<br />
<br />
2D in a 3D Space
<br />
<br />
<br />
Like this Apple template, most OpenGL tutorials start off by
ignoring depth, usually using a 2 point co-ordinate system (X, Y) rather
than the more standard 3 co-ordinate system (X, Y, Z). You may have
noticed that the vertices for the square in Apple’s template only uses
the (X, Y) co-ordinates, that’s because they are not using depth!
<br />
<br />
<br />
This is called Orthographic projection. As the goal of this
tutorial series is to get you to 3D, I’l not going to cover orthographic
projection at this point; maybe later in the series but for now, we’re
going to keep heading into 3D.
<br />
<br />
<br />
Enabling Depth Buffer
<br />
<br />
The first thing we need to do is to enable the depth buffer.
Apple’s example square is only a 2D object so doesn’t actually need a
depth buffer. We need depth so we’ll enable it. Apple has kindly
provided the code to setup the depth buffer so we’ll just take advantage
of it.
<br />
<br />
<br />
Open EAGLView.m in the editor and look for the following line:
<br />
<br />
<br />
<pre>#define USE_DEPTH_BUFFER 0
</pre>
Needless to say, change that 0 to a 1. This will turn on the code in
the view setup area to create the depth buffer. This code is contained
in the createFrameBuffer method. Don’t worry too much about this method
at the moment. It’s written by Apple so just accept it does what it’s
supposed to.
<br />
<br />
Now, we need to enable depth testing within OpenGL. To do this,
we do need to create a new method which will be called once to enable
our view to work. Start by creating a new method called setupView and
insert the following code into this new method:
<br />
<br />
<br />
<pre>- (void)setupView {
const GLfloat zNear = 0.1, zFar = 1000.0, fieldOfView = 60.0;
GLfloat size;
glEnable(GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION);
size = zNear * tanf(DEGREES_TO_RADIANS(fieldOfView) / 2.0);
// This give us the size of the iPhone display
CGRect rect = self.bounds;
glFrustumf(-size, size, -size / (rect.size.width / rect.size.height), size / </pre>
<pre>(rect.size.width / rect.size.height), zNear, zFar);
glViewport(0, 0, rect.size.width, rect.size.height);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
}
</pre>
<br />
All this code does is set up the conditions in which OpenGL will operate
by creating a View Port which will be mapped to the actual size of our
display. Like I said, I’ll go into detail about this later on but for
now, just take note of the following:
<br />
<br />
<br />
glEnable(GL_DEPTH_TEST);
<br />
This enables the Depth Testing in OpenGL. You need to remember
that once you turn something “on” in OpenGL, you need to turn it off
later if you don’t want to continue using it. As we will only turn on
depth testing and never turn it off, it can go here in the view setup.
<br />
<br />
<br />
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
<br />
What we’re doing here is defining what colour to use when we
clear the screen. All colours in OpenGL ES must be RGBA values (ie Red,
Green, Blue, Alpha), and cannot just be RGB values as in OpenGL. So,
later on when we clear our screen, OpenGL ES will already know we want
it cleared in a black colour. It will continue to use this colour until
we change it.
<br />
<br />
<br />
The colour values can be 0 -> 1 if using floating point or 0
-> 255 if using unsigned bytes. The higher the value, the higher the
colour intensity.
<br />
<br />
<br />
OK, back to the top of the file and find the #define we had
changed earlier. We just need to install a macro here which is needed
for the setupView method we just inserted.
<br />
<br />
<br />
<pre>#define USE_DEPTH_BUFFER 1
#define DEGREES_TO_RADIANS(__ANGLE) ((__ANGLE) / 180.0 * M_PI)
</pre>
Draw View - The drawing method
<br />
<br />
<br />
Now, go down to the drawView method. This is where everything
happens in our tutorials. You can see here where Apple has even given us
a pointer to the fact this is where we’re going to be doing our work.
<br />
<br />
<br />
First of all, delete everything in this method, and replace the contents with the following code:
<br />
<br />
<pre>- (void)drawView {
[EAGLContext setCurrentContext:context];
glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
glViewport(0, 0, backingWidth, backingHeight);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
[context presentRenderbuffer:GL_RENDERBUFFER_OES];
}
</pre>
<br />
The first three lines of the code above, just sets up our drawing space.
The last two lines swaps our drawing space with the space which is
currently viewed on screen. If you’ve ever done animation or game
programming before, you’ll know that this is what’s called “double
buffered”.
<br />
<br />
<br />
For those who don’t know, what this means is that we create two
identical buffers: one which is displayed to the user, the other is
cleared and we draw on it. When we finish drawing, we swap the views so
the user sees the new view. The reason why we go to all this trouble is
to make the animation smooth.
<br />
<br />
<br />
We’re not going to add any more code to make this display anything at the moment, we’ve still got some more set up to do.
<br />
<br />
<br />
Firstly, right before the dealloc method, insert the following code:
<br />
<br />
<pre>- (void)checkGLError:(BOOL)visibleCheck {
GLenum error = glGetError();
switch (error) {
case GL_INVALID_ENUM:
NSLog(@"GL Error: Enum argument is out of range");
break;
case GL_INVALID_VALUE:
NSLog(@"GL Error: Numeric value is out of range");
break;
case GL_INVALID_OPERATION:
NSLog(@"GL Error: Operation illegal in current state");
break;
case GL_STACK_OVERFLOW:
NSLog(@"GL Error: Command would cause a stack overflow");
break;
case GL_STACK_UNDERFLOW:
NSLog(@"GL Error: Command would cause a stack underflow");
break;
case GL_OUT_OF_MEMORY:
NSLog(@"GL Error: Not enough memory to execute command");
break;
case GL_NO_ERROR:
if (visibleCheck) {
NSLog(@"No GL Error");
}
break;
default:
NSLog(@"Unknown GL Error");
break;
}
}
</pre>
<br />
OpenGL has an error checking method as called above (glGetError()) but
the error code returned needs to be manually converted to a human
readable message from the error code. That is what the above method
does.
<br />
<br />
The BOOL parameter “visibleCheck” is only there so that sometimes
you can check and see that the routine was called and when there are no
errors.
<br />
<br />
The last thing we need to do in EAGLView.m is to go to the
initWithCoder method and actually call the “setupView” method we created
earlier. Below the setting of the variable “animationInterval”, insert
the following to call the setupView method:
<br />
<br />
<pre> [self setupView];
</pre>
<br />
Note of course that we could have put the setupView method’s code in the
initWithCoder method rather than create a new method, as generally it’s
only ever called once.
<br />
<br />
OK, done in EAGLView.m; Switch to EAGLView.h!
<br />
<br />
EAGLView.h
<br />
Fortunately, there is not much work to be done here. All we need
to do is to create the prototypes for the two methods we created as
follows:
<br />
<br />
<pre>- (void)setupView;
- (void)checkGLError:(BOOL)visibleCheck;
</pre>
And that’s us done for this tutorial.
<br />
<br />
Next Steps...
<br />
<br />
If you were to build and run this in the simulator, you’d just
get a blank screen and nothing interesting would happen. In the next
tutorial, we’re going to start drawing primitives on the screen,
primitives are just basic drawings like points, lines and triangles.
<br />
<br />
For those of you who just want to download the project files and not type it all in, here it is:
<br />
<br />
<a class="external text" href="http://web.me.com/smaurice/AppleCoder/iPhone_OpenGL/Entries/2009/3/28_OpenGL_ES_00_-_Xcode_Project_Set_Up_files/AppleCoder-OpenGLES-00.zip" rel="nofollow" title="http://web.me.com/smaurice/AppleCoder/iPhone_OpenGL/Entries/2009/3/28_OpenGL_ES_00_-_Xcode_Project_Set_Up_files/AppleCoder-OpenGLES-00.zip">AppleCoder-OpenGLES-00.zip</a>
<br />
The home of the tutorials is in the “Tutorials” section of the iphonedevsdk.com forums. Check out the thread there.
<br />
<br />
Until then, hooroo!
<br />
<br />
Simon Maurice
<br />
<br />
Copyright 2009 Simon Maurice. All Rights Reserved.
<br />
The code provided in these pages are for educational purposes
only and may not be used for commercial purposes without Simon Maurice’s
expressed permission in writing. Information contained within this site
cannot be duplicated in any form without Simon Maurice’s expressed
permission in writing; this includes, but is not limited to, publishing
in printed format, reproduced on web pages, or other forms of electronic
distribution.
<br />
Linking to these pages on other websites is permitted.</div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-3765795590291919148.post-962605847939530692012-01-31T20:21:00.000-08:002012-04-09T22:21:16.559-07:00OpenGL ES 01 - Drawing Primitives 1 - Triangles<div dir="ltr" style="text-align: left;" trbidi="on">
<script type="text/javascript">
<!--
google_ad_client = "ca-pub-9566972421287894";
/* 728x15 LINK AD-1 */
google_ad_slot = "1092558380";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript">
</script>
<br />
<h1 class="firstHeading">
<span style="color: #990000;">OpenGL ES 01 - Drawing Primitives 1 - Triangles</span> </h1>
<div style="color: #999999;">
Since the original website of Simon Maurice seems not available, many
people have been searching for this cool tutorial. This one is pretty
good to start learning Open GL ES for iPhone programming . It gives a
detailed explanation for every code and also gives sample source codes.
Hope it may help you. All the best!<span style="color: #990000;"><b><span style="color: #cc0000;"> </span></b></span><br />
<br />
<span style="color: #990000;"><b><span style="color: #cc0000;">Disclaimer:</span></b> <b><span style="color: #444444;">This
post and the whole blog is just a copy of
iPhone-OpenGL-ES-tutorial-Series (seems to have )written by Simon
Maurice. This blog does NOT have a written permission or any kind of
permission from the original author (Simon Maurice). Please use it at
your own risk. If you have any complaints regarding any content of this
blog, please let me know I will remove it right away. Just hoping that
it would be helpful for people who are trying to learn OpenGL. Happy
coding!</span></b></span> </div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<b>Copyright 2009 Simon Maurice. All Rights Reserved.
</b></div>
<div style="color: #999999;">
The code provided in these pages are for educational purposes only and
may not be used for commercial purposes without Simon Maurice’s
expressed permission in writing. Information contained within this site
cannot be duplicated in any form without Simon Maurice’s expressed
permission in writing; this includes, but is not limited to, publishing
in printed format, reproduced on web pages, or other forms of electronic
distribution.
</div>
<br />
<a href="https://github.com/mauriceatron" target="_blank">Source codes can be found here </a><br />
<div style="color: #666666;">
<b><br /></b></div>
<b><span style="color: #666666;">Original tutorial starts from here onwards..</span></b><br />
<br />
<br />
Primitives are the basic drawing elements which make up complex
objects. In OpenGL ES the primitives you can use are Points, Lines,
& Triangles. These are pretty self explanatory and I doubt you need
an explanation of what these look like.
<br />
<br />
First of all, let’s look at some code and then we can talk about
what’s going on so you can then use it to create some of your own code.
<br />
<br />
Primitive #1 - Triangles
<br />
<br />
Triangles are the most “complex” of the primitives but they’re so
easy to use and so useful, this will be the first OpenGL primitive that
you’ll draw. When drawing a triangle, all we need to do is to feed
OpenGL with the three co-ordinates in 3D space for the triangle and it
will render it quite happily.
<br />
<br />
To get started, make a copy of the project from the OpenGL ES 00 tutorial or just download it from here: <a class="external text" href="http://web.me.com/smaurice/AppleCoder/iPhone_OpenGL/Entries/2009/3/28_OpenGL_ES_01_-_Drawing_Primitives_1_-_Triangles_files/AppleCoder-OpenGLES-00.tar.gz" rel="nofollow" title="http://web.me.com/smaurice/AppleCoder/iPhone_OpenGL/Entries/2009/3/28_OpenGL_ES_01_-_Drawing_Primitives_1_-_Triangles_files/AppleCoder-OpenGLES-00.tar.gz">AppleCoder-OpenGLES-00.tar.gz</a> Open it up in Xcode and go straight to the EAGLView.m file and find the drawView method. This is where the magic starts!
<br />
<br />
First, we need to define the triangle. In order to do this, you
need to understand there are two types of co-ordinates that we are going
to deal with: Model and World. The Model co-ordinates are in reference
to the actual primitive that we are drawing, the world co-ordinates tell
OpenGL where it is in relation to viewer (the viewer is always at world
co-ordinates of (0.0, 0.0, 0.0).
<br />
<br />
This first example will illustrate this. First, we define the triangle in the model space using 3 x 3D co-ordinates (X, Y, Z):
<br />
<br />
<pre>const GLfloat triangleVertices[] = {
0.0, 1.0, -6.0,// Triangle top centre
-1.0, -1.0, -6.0,// bottom left
1.0, -1.0, -6.0,// bottom right
};
</pre>
As to be expected, there are 3 co-ordinates giving the triangle and
note that they are described sequentially in an anti-clockwise
direction. Whilst they can be described in a clockwise or anti-clockwise
direction, they must be described sequentially and be consistent.
However, I would encourage you to start off using anti-clockwise
co-ordinates as it’s needed for some more advanced functions much later
on.
<br />
<br />
While this tutorial is supposed to be purely iPhone OpenGL ES,
for the beginners, I’ll describe the 3D co-ordinate system briefly here.
Take a look at this picture:
<br />
<br />
<img alt="5_627_5a28c5bfe492dc5.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_5a28c5bfe492dc5.jpg" />
<br />
<br />
Sorry about my drawing skills but this is a representation of what
either the model space or world space looks like. Just imagine this is
you’re computer screen with X and Y being horizontal and vertical as you
would expect, and Z being depth. The centre co-ordinates being (0.0,
0.0, 0.0).
<br />
<br />
So, looking at our triangle as described in the vertices above,
the first point (0.0, 1.0, -6.0) would be dead centre on the Y axis, up 1
point and back into the screen 6 points. The second co-ordinate is to
the right of the Y axis 1.0 points, below the X axis (hence -1.0 for the
Y value), and still back into the screen -6.0 points. The same applies
to the third co-ordinate.
<br />
<br />
The reason why we have set the object back (ie negative Z value)
is so that it will be visible (remember, our viewer or “camera” is at
(0.0, 0.0, 0.0) so it would “fail” OpenGL’s depth test and not render it
at all.
<br />
<br />
I can hear you screaming “Hey, I thought you said this was Model
co-ordinates which are not world co-ordinates!!”. Yes, that’s true, but
when we get to rendering this triangle next, OpenGL will simply place
the object at (0.0, 0.0, 0.0). So we set it back into the screen so it
is visible. When we get into transformations (moving, rotating etc),
you’ll see ways that you don’t need to set the object into a negative Z
value to make it visible. Until then, leave the Z co-ordinate at -6.0.
<br />
<br />
The Drawing Code
<br />
<br />
So all we’ve done so far is to describe the triangle. We now need
to tell OpenGL where that data is stored and how to draw it. This is
accomplished with only a few lines of code. Go back to the drawView
method and implement it as follows:
<br />
<br />
<pre>- (void)drawView {
const GLfloat triangleVertices[] = {
0.0, 1.0, -6.0, // Triangle top centre
-1.0, -1.0, -6.0, // bottom left
1.0, -1.0, -6.0 // bottom right
};
[EAGLContext setCurrentContext:context];
glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
glViewport(0, 0, backingWidth, backingHeight);
// -- BEGIN NEW CODE
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glVertexPointer(3, GL_FLOAT, 0, triangleVertices);
glEnableClientState(GL_VERTEX_ARRAY);
glDrawArrays(GL_TRIANGLES, 0, 3);
// -- END NEW CODE
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
[context presentRenderbuffer:GL_RENDERBUFFER_OES];
[self checkGLError:NO];
}
</pre>
As you can see, in 4 lines of code we can render a triangle. Let me
break this down line by line and you’ll see it’s actually quite simple.
<br />
<br />
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
<br />
<br />
This line simply clears the screen. The control bits we feed it tells
OpenGL to use the colour we set up in the setupView method from the last
tutorial (black colour) and to clear the depth buffer. Note that if we
did not clear the depth buffer and have depth buffer turned on (as we
do), the scene would not render. If we did not enable the depth buffer,
we would not need to pass glClear() the GL_DEPTH_BUFFER_BIT.
<br />
<br />
So, we’ve cleared whatever was previously drawn on this buffer
(remember, it’s double buffered animation; draw on one buffer while
another buffer is displayed).
<br />
<br />
glVertexPointer(3, GL_FLOAT, 0, triangleVertices);
<br />
<br />
This function tells OpenGL where our data is and what format it is in.
There are 4 parameters for this function and is quite simply broken down
as:
<br />
<br />
1.Size - this is the number of values in each co-ordinate. In our
case it is 3 for the values being (X, Y, Z). If you were doing 2D
drawing and didn’t have a depth (ie Z value), then you could pass 2
here.
<br />
<br />
2.Data Type - GL_FLOAT means that we are passing floating point
values. You could also use integer values if you want but you need to
get used to floating point values as 3D worlds are floating point.
<br />
<br />
3.Stride - the stride tell OpenGL to ignore a certain number of
bytes between each co-ordinate. Don’t worry about this, keep it as zero.
You only use this when you are loading vertex data from file in a
format which has additional padding data or colour data from, say, a 3D
program like Blender.
<br />
<br />
4.Pointer to the Data - exactly as it appears, the data itself.
<br />
<br />
So, we’ve told OpenGL to clear the buffer, told it where the data
is for our object and it’s format, now we need to tell OpenGL something
quite important:
<br />
<br />
<br />
<br />
<pre>glEnableClientState(GL_VERTEX_ARRAY);
</pre>
<br />
OpenGL is a “State” machine. This means that you turn on and off
functionality by calling enable and disable commands. Previously, we had
used glEnable() which affects the “server” side of OpenGL.
glEnableClientState() affects our program side (ie the client side). So,
what we’ve done is told OpenGL that our vertex data is in a vertex
array and turned on OpenGL’s functionality for drawing a vertex. A
vertex could be a colour array in which case we would call
glEnableClientState(GL_COLOR_ARRAY) or perhaps a texture co-ordinate
array in the case of texture mapping (stop salivating! You need to get
through the basics before I cover texture mapping!!).
<br />
<br />
As we get further into OpenGL, we’ll use the different client states and this will become clearer with use.
<br />
<br />
Now comes the the command to make OpenGL render a triangle:
<br />
<br />
<pre>glDrawArrays(GL_TRIANGLES, 0, 3);
</pre>
<br />
Once this function is called, OpenGL takes the information which we had
fed it from the previous two functions and executes it. On the screen
will be a triangle solid white in colour (white is the default drawing
colour). Triangles are filled objects, if you needed an unfilled
triangle, then you need to draw it differently.
<br />
<br />
Breaking down this function, the three arguments are:
<br />
<br />
1.Drawing Method - in this case, we have passed GL_TRIANGLES
which seems fairly obvious as we are drawing a triangle. However, the
power of this first argument will become apparent when we use this
function to draw a square.
<br />
<br />
2.First Vertex - our array consists of only three points so we
want OpenGL to draw from the first co-ordinate in our array which is
specified as Zero just as in accessing a standard array. If we had
multiple primitives in our vertex array, we could put the offset in
here. I’ll cover the use of this in a later tutorial when I show you how
to build complex objects. For now, just use 0 here.
<br />
<br />
3.Vertex Count - This tells OpenGL how many vertices are in our
array that it needs to draw. Again, we are drawing a triangle so only
three points are required. A square would have 4 points, a line 2 points
(or more) and a Point would be 1 or more (in the case of rendering
multiple Points).
<br />
<br />
Once you’ve entered the code and you’re drawView method looks
like mine above, hit “Build and Go” running it in the simulator. You’re
simulator should look like this:
<br />
<br />
<img alt="5_627_3016897660d33fc.png" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_3016897660d33fc.png" />
<br />
<br />
<br />
As promised, there’s a solid white triangle in the centre of the screen.
<br />
<br />
Before we go onto other primitives, try changing the Z value and
you will see what I mean if you change it to 0.0. Nothing will render.
<br />
<br />
There’s been a lot of typing over a couple of lines of code, but I
hope it’s been worth it for you to see just how OpenGL ES works. If
you’ve ever tried to follow a “standard” OpenGL tutorial and just got
stuck, I hope you can start to see the difference between OpenGL and
OpenGL ES.
<br />
<br />
Looking Forward
<br />
<br />
The next tutorial will focus on expanding the code above and
producing a square. Below is the link to the project code for you to
download if you couldn’t get yours working.
<br />
<br />
If you have any questions, just email me (link below). And here’s the source code for this tutorial:
<br />
<br />
<a class="external text" href="http://web.me.com/smaurice/AppleCoder/iPhone_OpenGL/Entries/2009/3/28_OpenGL_ES_01_-_Drawing_Primitives_1_-_Triangles_files/AppleCoder-OpenGLES-01.zip" rel="nofollow" title="http://web.me.com/smaurice/AppleCoder/iPhone_OpenGL/Entries/2009/3/28_OpenGL_ES_01_-_Drawing_Primitives_1_-_Triangles_files/AppleCoder-OpenGLES-01.zip">AppleCoder-OpenGLES-01.zip</a>
<br />
The home of the tutorials is in the “Tutorials” section of the iphonedevsdk.com forums. Check out the thread there.
<br />
<br />
Until then, hooroo!
<br />
<br />
Simon Maurice
<br />
<br />
Copyright 2009 Simon Maurice. All Rights Reserved.
<br />
The code provided in these pages are for educational purposes
only and may not be used for commercial purposes without Simon Maurice’s
expressed permission in writing. Information contained within this site
cannot be duplicated in any form without Simon Maurice’s expressed
permission in writing; this includes, but is not limited to, publishing
in printed format, reproduced on web pages, or other forms of electronic
distribution.
<br />
Linking to these pages on other websites is permitted.</div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-3765795590291919148.post-44200559027499410402012-01-31T05:13:00.000-08:002012-04-09T22:21:26.512-07:00OpenGL ES 02 - Drawing Primitives 2 - Squares<div dir="ltr" style="text-align: left;" trbidi="on">
<script type="text/javascript">
<!--
google_ad_client = "ca-pub-9566972421287894";
/* 728x15 LINK AD-1 */
google_ad_slot = "1092558380";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript">
</script>
<br />
<h1 class="firstHeading" style="color: #990000;">
OpenGL ES 02 - Drawing Primitives 2 - Squares</h1>
<div style="color: #999999;">
Since the original website of Simon Maurice seems not available, many
people have been searching for this cool tutorial. This one is pretty
good to start learning Open GL ES for iPhone programming . It gives a
detailed explanation for every code and also gives sample source codes.
Hope it may help you. All the best!<span style="color: #990000;"><b><span style="color: #cc0000;"> </span></b></span><br />
<br />
<span style="color: #990000;"><b><span style="color: #cc0000;">Disclaimer:</span></b> <b><span style="color: #444444;">This
post and the whole blog is just a copy of
iPhone-OpenGL-ES-tutorial-Series (seems to have ) written by Simon
Maurice. This blog does NOT have a written permission or any kind of
permission from the original author (Simon Maurice). Please use it at
your own risk. If you have any complaints regarding any content of this
blog, please let me know I will remove it right away. Just hoping that
it would be helpful for people who are trying to learn OpenGL. Happy
coding!</span></b></span> </div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<b>Copyright 2009 Simon Maurice. All Rights Reserved.
</b></div>
<div style="color: #999999;">
The code provided in these pages are for educational purposes only and
may not be used for commercial purposes without Simon Maurice’s
expressed permission in writing. Information contained within this site
cannot be duplicated in any form without Simon Maurice’s expressed
permission in writing; this includes, but is not limited to, publishing
in printed format, reproduced on web pages, or other forms of electronic
distribution.
</div>
<br />
<a href="https://github.com/mauriceatron" target="_blank"><b>Source codes can be found here</b> </a><br />
<div style="color: #666666;">
<b><br /></b></div>
<b><span style="color: #666666;">Original tutorial starts from here onwards..</span></b><br />
<br />
<br />
<br />
Strictly speaking, squares are not a primitive in OpenGL ES but,
let’s face it, they’re pretty handy and just as easy as rendering a
triangle. In this tutorial, we’re going to take the code from the
triangle primitive and turn it into a square. Again, it will be rendered
statically but we’re going to get into transformations (ie moving them
around) quickly enough. Of course, once we’ve done a square, we can then
make a cube, then even a texture mapped cube...
<br />
<br />
<br />
Quick Recap and Details of This Tutorial
<br />
<br />
<br />
Last tutorial, we took our “blank canvas” Xcode project and
rendered a solid white triangle. In doing so, you created a vertex
array, told OpenGL about the data and it’s format using
glVertexPointer(), put it in the state for rendering the vertex array,
and then rendered it using glDrawArrays().
<br />
<br />
<br />
Today, we’re going to take that code, and make a square from the
triangle. In order to do that, we will only need to change a couple of
lines of code. The first one is probably obvious, we need to specify 4
points for the square instead of 3 for a triangle. Then we need to tell
OpenGL to draw it differently by passing a different drawing methodology
to glDrawArrays().
<br />
<br />
<br />
Let’s get going.
<br />
<br />
Defining the Square’s Vertices
<br />
<br />
Open up the Xcode project from the last tutorial and go to the
drawView method. Comment out the triangleVertices[] constant - don’t
discard it as we’ll use it when we get into transformations - and add in
the following code:
<br />
<br />
<pre>const GLfloat squareVertices[] = {
-1.0, 1.0, -6.0, // Top left
-1.0, -1.0, -6.0, // Bottom left
1.0, -1.0, -6.0, // Bottom right
1.0, 1.0, -6.0 // Top right
};
</pre>
<br />
That describes our square. Note the anti-clockwise direction of the vertices (the four points) of the square?
<br />
<br />
Now go down into the body and comment out the drawing code for
the triangle, again we’ll bring this code back to life later on. So
comment out the three function calls of glVertexArray(),
glEnableClientState(), and glDrawArrays() and add the following code:
<br />
<br />
<pre>glVertexPointer(3, GL_FLOAT, 0, squareVertices);
glEnableClientState(GL_VERTEX_ARRAY);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
</pre>
<br />
It’s the same three functions but just slightly different.
<br />
<br />
<pre>glVertexPointer(3, GL_FLOAT, 0, squareVertices);
</pre>
The only change here is that we are telling OpenGL to use a different set of vertices; the square now and not the triangle.
<br />
<br />
glEnableClientState() is the same as we are telling OpenGL to draw from a vertex array (not a colour array or something else).
<br />
<br />
<pre>glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
</pre>
This is the main change. In the last tutorial, we used GL_TRIANGLES
as the first parameter and 3 as the 3rd parameter. Remember the second
parameter is the offset from the start of the array to being from, again
this is zero because our vertex array only contains the vertices for
the square.
<br />
<br />
<br />
The first argument is the drawing mode and you’ve now seen two
possible drawing modes for OpenGL. I do want to take the time now to
discuss the different drawing modes. They are:
<br />
<br />
<br />
GL_POINTS
<br />
GL_LINES
<br />
GL_LINE_LOOP
<br />
GL_LINE_STRIP
<br />
GL_TRIANGLES
<br />
GL_TRIANGLE_STRIP
<br />
GL_TRIANGLE_FAN
<br />
<br />
We haven’t discussed points or lines yet so I’ll stick to the last three
which refer to triangles. Before I commence, I want to remind you that a
vertex array may contain more than one triangle so while you have only
seen one object per vertex array, you’re not limited to that.
<br />
<br />
<br />
GL_TRIANGLES - passing this parameter means that OpenGL treats
the vertex array in sets of 3 vertices. So, for the first three, it
makes a triangle bound by vertex 1, vertex 2, and vertex 3. Then it will
process the next three vertices and so on to the end of the array.
<br />
<br />
<br />
GL_TRIANGLE_STRIP - OpenGL will start off using the first two
vertices, then for each successive vertex, it will use the previous 2
vertices to make a triangle. That is, for squareVertices[6~8], a
triangle is formed with squareVerticies[0~2] and squareVerticies[3~5].
For squareVertices[9~11], a triangle is formed with squareVertices[3~5]
and squareVertices[6~8]. And so on through the array.
<br />
<br />
<br />
Note: the reference to squareVertices[0~2] refers to:
<br />
<br />
<br />
squareVertices[0] - X co-ordinate
<br />
<br />
squareVertices[1] - Y co-ordinate
<br />
<br />
squareVertices[2] - Z co-ordinate
<br />
<br />
I’ll cover this in an example further on in this tutorial if that doesn’t make sense.
<br />
<br />
GL_TRIANGLE_FAN - After the first 2 vertices, for each successive
vertex, OpenGL will form a triangle with with the previous vertex and
the first vertex. So, for squareVertices[6~8], a triangle is formed with
squareVertices[3~5] (previous vertex) and squareVertices[0~2] (first
vertex).
<br />
<br />
<br />
Since we used GL_TRIANGLES_FAN, we will get a square on the
display. Hit “Build & Go”, and you should be rewarded with a white
square on the screen like this:
<br />
<br />
<br />
<img alt="5_627_1951b4ea6ee611e.png" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_1951b4ea6ee611e.png" />
<br />
<br />
Go back and have a look at your vertex array. Try to imagine the
triangles being drawn to make the square. OpenGL renders it like this.
<br />
<br />
<br />
Triangle Point 1: squareVertices[0~2] -- Top left of square
<br />
<br />
Triangle Point 2: squareVertices[3~5] -- Bottom left of square
<br />
<br />
Triangle Point 3: squareVertices[6~8] -- Bottom right of square
<br />
<br />
<br />
Taking the above 3 points, OpenGL draws a triangle which will make up
the bottom left half of the square. Imagine the square is divided in two
with a diagonal line starting at the top left corner of the square to
the bottom right corner. Notice how two triangles form? OpenGL just drew
the bottom left half.
<br />
<br />
<br />
Note: the reference to squareVertices[0~2] refers to:
<br />
<br />
<br />
squareVertices[0] - X co-ordinate
<br />
<br />
squareVertices[1] - Y co-ordinate
<br />
<br />
squareVertices[2] - Z co-ordinate
<br />
<br />
Triangle Point 1: squareVertices[9~11] -- Top right of square
<br />
<br />
Triangle Point 2: squareVertices[6~8] -- Previous vertex, bottom right
<br />
<br />
Triangle Point 3: squareVertices[0~2] -- First vertex, top left
<br />
<br />
<br />
Taking only 1 new point, OpenGL is able render a triangle to complete the square.
<br />
<br />
GL_TRIANGLE_STRIP
<br />
Go back to the code and change the first parameter into glDrawArrays() from GL_TRIANGLE_FAN to GL_TRIANGLE_STRIP:
<br />
<br />
<br />
<br />
<br />
<pre>glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
</pre>
Hit “Build & Go” and you’ll get the following image:
<br />
<br />
<img alt="5_627_b343c3eb00ab83c.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_b343c3eb00ab83c.jpg" />
<br />
Let’s have a look and see why we didn’t get a square due to the
single change in the drawing method. OpenGL processes our vertex array
in the following manner:
<br />
<br />
<br />
Triangle Point 1: squareVertices[0~2] -- top left
<br />
<br />
<br />
Triangle Point 2: squareVertices[3~5] -- bottom left
<br />
<br />
<br />
Triangle Point 3: squareVertices[6~8] -- bottom right
<br />
<br />
<br />
OpenGL now renders a triangle using the first three points,
hence, the bottom left half of the previous square rendered as per the
previous example.
<br />
<br />
<br />
Triangle Point 1: squareVertices[9~11] -- top right
<br />
<br />
Triangle Point 2: squareVertices[6~8] -- Previous Vertex, bottom right
<br />
<br />
Triangle Point 3: squareVertices[3~5] -- 2nd Previous, bottom left
<br />
<br />
OpenGL now renders a triangle using these three points. In this
example, it has rendered a triangle 90º away from what we want to make a
square.
<br />
<br />
<br />
If we had of provided our vertex array differently, we could have
produced a correct square using GL_TRIANGLE_STRIP just as well as we
did we GL_TRIANGLE_FAN. Just remember the drawing method and your vertex
array need to be considered together otherwise you’ll get odd results
like we did when we changed from FAN to STRIP.
<br />
<br />
<pre>const GLfloat stripSquare[] = {
-1.0, -1.0, -6.0, // bottom left
1.0, -1.0, -6.0, // bottom right
-1.0, 1.0, -6.0, // top left
1.0, 1.0, -6.0 // top right
};
</pre>
So working with the above, we can see the first triangle will be
formed through the first three vertices, producing a triangle as
follows:
<br />
<br />
<br />
<img alt="5_627_9c0a6b7a31bc4bb.png" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_9c0a6b7a31bc4bb.png" />
<br />
Now, by specifying the point of the top right vertex (P4), a new
triangle will be formed with the top left (P3) and the 2nd previous
vertex (P2) which is at the bottom right. The new vertices are shown in
Orange, Green and Red below:
<br />
<br />
<img alt="5_627_0fcbb48e1c13663.png" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_0fcbb48e1c13663.png" />
<br />
<br />
As a result, we have produced a square. The end result is still the same
but it’s just a reminder that you need to match your drawing
methodology to the way you have specified your vertices.
<br />
<br />
So you’ve now seen triangles and squares. We still need to cover
points and lines yet. Both are quite simple and will be covered next. As
we’re building upon what we’ve already covered, we’ll add some colour
into the mix next time.
<br />
<br />
<br />
Once we can colour our objects, we’ll move them around and then
texture map them in 3D. Granted, it won’t be Doom 3 but you’ll start to
be able to build 3D objects and then, I’ll start to cover 3D worlds.
<br />
<br />
Here’s the code for this tutorial:
<br />
<br />
<br />
<a class="external text" href="http://web.me.com/smaurice/AppleCoder/iPhone_OpenGL/Entries/2009/3/28_OpenGL_ES_02_-_Drawing_Primitives_2_-_Squares_files/AppleCoder-OpenGLES-02.zip" rel="nofollow" title="http://web.me.com/smaurice/AppleCoder/iPhone_OpenGL/Entries/2009/3/28_OpenGL_ES_02_-_Drawing_Primitives_2_-_Squares_files/AppleCoder-OpenGLES-02.zip">AppleCoder-OpenGLES-02.zip</a>
<br />
The home of the tutorials is in the “Tutorials” section of the iphonedevsdk.com forums. Check out the thread there.
<br />
<br />
As always, feel free to email me. Home base for these tutorials
is over at the iphonedevsdk.com forum under the tutorials section.
<br />
<br />
Until next time, hooroo!
<br />
<br />
Simon Maurice
<br />
<br />
Copyright 2009 Simon Maurice. All Rights Reserved.
<br />
The code provided in these pages are for educational purposes
only and may not be used for commercial purposes without Simon Maurice’s
expressed permission in writing. Information contained within this site
cannot be duplicated in any form without Simon Maurice’s expressed
permission in writing; this includes, but is not limited to, publishing
in printed format, reproduced on web pages, or other forms of electronic
distribution.
<br />
<br />
Linking to these pages on other websites is permitted.</div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-3765795590291919148.post-1210311152408658772012-01-31T05:08:00.002-08:002012-04-09T22:21:36.146-07:00OpenGL ES 03 - Transformations<div dir="ltr" style="text-align: left;" trbidi="on">
<script type="text/javascript">
<!--
google_ad_client = "ca-pub-9566972421287894";
/* 728x15 LINK AD-1 */
google_ad_slot = "1092558380";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript">
</script>
<br />
<h1 class="firstHeading" style="color: #990000;">
OpenGL ES 03 - Transformations</h1>
<br />
<div style="color: #999999;">
Since the original website of Simon Maurice seems not available, many
people have been searching for this cool tutorial. This one is pretty
good to start learning Open GL ES for iPhone programming . It gives a
detailed explanation for every code and also gives sample source codes.
Hope it may help you. All the best!<span style="color: #990000;"><b><span style="color: #cc0000;"> </span></b></span><br />
<br />
<span style="color: #990000;"><b><span style="color: #cc0000;">Disclaimer:</span></b> <b><span style="color: #444444;">This
post and the whole blog is just a copy of
iPhone-OpenGL-ES-tutorial-Series (seems to have )written by Simon
Maurice. This blog does NOT have a written permission or any kind of
permission from the original author (Simon Maurice). Please use it at
your own risk. If you have any complaints regarding any content of this
blog, please let me know I will remove it right away. Just hoping that
it would be helpful for people who are trying to learn OpenGL. Happy
coding!</span></b></span> </div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<b>Copyright 2009 Simon Maurice. All Rights Reserved.
</b></div>
<div style="color: #999999;">
The code provided in these pages are for educational purposes only and
may not be used for commercial purposes without Simon Maurice’s
expressed permission in writing. Information contained within this site
cannot be duplicated in any form without Simon Maurice’s expressed
permission in writing; this includes, but is not limited to, publishing
in printed format, reproduced on web pages, or other forms of electronic
distribution.
</div>
<br />
<a href="https://github.com/mauriceatron" target="_blank">Source codes can be found here </a><br />
<div style="color: #666666;">
<b><br /></b></div>
<b><span style="color: #666666;">Original tutorial starts from here onwards..</span></b><br />
<br />
<br />
Today, we’re going to build on what’s been covered so far and put
both the triangle and square on the screen at the same time. To do this,
we are going to move them. Moving an object is one thing of what’s
called Transformation.
<br />
<br />
With OpenGL ES, there are three different types of transformations which you can use on your model (object). They are:
<br />
<br />
<br />
<pre> 1.
</pre>
<pre> •Translate - Move the object within 3D space.
2.
</pre>
<pre> •Rotate - Rotation around either the X, Y, or Z axis.
3.
</pre>
<pre> •Scale - Alter the size of the object. Primarily this is used in 2D orthographic projection systems as in 3D the farther an object is away (ie the more negative the Z co-ordinate) the smaller the object is rendered. But can of course be used for “special effects”.
</pre>
<br />
To demonstrate these different functions, what we’re going to do is to
bring both the square and the triangle onto the screen at the same time
using the translate function and then proceed to the other two.
<br />
<br />
Translate
<br />
In order to effect a translation, OpenGL ES gives us a single
function which we can use, aptly called glTranslatef(). Note the “f” at
the end of the word translate? That just means that we are going to feed
OpenGL floating point data. OpenGL ES also provides the opportunity to
use fixed point data and thus call the function glTranslatex(). Fixed
point data would be used on hardware without a dedicated floating point
maths co-processor but as the iPhone has one built in, we don’t need to
use fixed-point maths and we can just stick to floating point maths.
<br />
<br />
I just wanted to mention that in case you’re Xcode code completion
prompts you to using glTranslatex() and you weren’t sure what the
difference is.
<br />
<br />
OK, time to start cutting some code. Fire up Xcode and open your
project. I hope you took my advice and commented out the triangle data
and rendering calls and didn’t delete them otherwise you’re going to
have to re-type them again.
<br />
<br />
First of all, let’s look at the two vertex arrays. We are going to make a
change to the vertex data but only for the Z co-ordinates. Change all
the Z co-ordinates to 0.0 like the following:
<br />
<br />
const GLfloat triangleVertices[] = {
<br />
<pre> 0.0, 1.0, 0.0, // Triangle top centre
</pre>
<pre> -1.0, -1.0, 0.0, // bottom left
</pre>
<pre> 1.0, -1.0, 0.0 // bottom right
</pre>
<pre> };
</pre>
<br />
const GLfloat squareVertices[] = {
<br />
<pre> -1.0, 1.0, 0.0, // Top left
</pre>
<pre> -1.0, -1.0, 0.0, // Bottom left
</pre>
<pre> 1.0, -1.0, 0.0, // Bottom right
</pre>
<pre> 1.0, 1.0, 0.0 // Top right
</pre>
<pre> };
</pre>
<br />
Do you remember why we had the Z co-ordinates as -6.0? It was because we
needed to set the objects back into the screen because our “camera” is
at (0.0, 0.0, 0.0). What we’re going to do is to use the glTranslatef()
function to set them back 6 points instead of specifying it in our
vertex arrays.
<br />
<br />
First, we need to tell OpenGL what we’re going to be translating: either
the Projection (view of the world) or the Objects (models within that
world). In this case, it’s the square and the triangle so we need to
tell OpenGL this. Below the call to glClear() in your drawView method,
call the following OpenGL function:
<br />
<br />
<br />
<pre> glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
</pre>
<br />
<br />
<pre> glMatrixMode(GL_MODELVIEW);
</pre>
<br />
The call to glMatrixMode tells OpenGL to work on our vertex data rather
than the projection. In our setupView method, we call this same function
but use the GL_PROJECTION enum as the parameter. OpenGL is an engine
which remains in the same “state” until you tell it otherwise. So, the
Matrix Mode will stay in GL_PROJECTION until we say go to the model view
by calling glMatrixMode(GL_MODELVIEW). Now that we have set the OpenGL
state to GL_MODELVIEW, it will remain there until we tell it otherwise.
<br />
<br />
So, in fact, we could actually make this call at the end of the
setupView method we created in the first tutorial if we wanted maximum
performance. However, we are in tutorial world and not real world at the
moment so just leave it in the drawView method.
<br />
<br />
I know I haven’t really covered using OpenGL ES in projection mode yet
so don’t panic if you don’t fully understand the above. All we’re doing
is putting some objects onto a screen and playing around with them while
you learn OpenGL ES.
<br />
<br />
Now, remove the comments around the draw triangle code which looks like this:
<br />
<br />
glVertexPointer(3, GL_FLOAT, 0, triangleVertices);
<br />
<pre> glEnableClientState(GL_VERTEX_ARRAY);
</pre>
<pre> glDrawArrays(GL_TRIANGLES, 0, 3);
</pre>
<br />
Before these three lines of code, add the following two lines:
<br />
<br />
glLoadIdentity();
<br />
glTranslatef(-1.5, 0.0, -6.0);
<br />
glVertexPointer(3, GL_FLOAT, 0, triangleVertices);
<br />
<pre> glEnableClientState(GL_VERTEX_ARRAY);
</pre>
<pre> glDrawArrays(GL_TRIANGLES, 0, 3);
</pre>
<br />
glLoadIdentity() is just a connivence function to basically reset
everything back to the original conditions. If we didn’t call this
function, the glTranslatef() call would continue to move the object to
the left and back into the screen until it disappeared. I’m actually
going to cover a better way to do this later on (the tutorial after next
in fact) so just accept for now that we’re just resetting the object
data.
<br />
<br />
The next function call is where the action happens.
<br />
<br />
glTranslatef() takes three parameters:
<br />
<br />
<br />
<pre> glTranslatef(GLfloat xtrans, GLfloat ytrans, GLfloat Ztrans);
</pre>
<br />
Just review my drawing of the 3D world space before continuing.
<br />
<br />
<img alt="5_627_d524bd7fb47f8de.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_d524bd7fb47f8de.jpg" />
<br />
<br />
Remember the “camera” is at (0.0, 0.0, 0.0). Our call to glTranslatef() above has the following values:
<br />
<br />
<br />
<pre> xtrans = -1.5
</pre>
<pre> ytrans = 0.0
</pre>
<pre> ztrans = -6.0
</pre>
<br />
The next thing you need to recall is that when we draw both the square
and the triangle, the appeared in the centre of the screen. If we were
to draw them as per the previous tutorials, they would be drawn on top
of each other.
<br />
<br />
So, in order to get around this, I have moved the X co-ordinate of the
centre of the triangle to the left 1.5 points. Referring to the image
above of the co-ordinate space, you can see that left of screen centre
is negative, hence the negative 1.5.
<br />
<br />
The -6.0 for the Z transformation replaces the -6.0 which we had originally in the object’s vertex array.
<br />
<br />
We have moved the triangle to the left 1.5 and back 6.0 points.
<br />
<br />
Moving Onto the Square
<br />
The code for the square is almost the same as the triangle. It is as follows:
<br />
<br />
<br />
<pre> glLoadIdentity();
</pre>
<pre> glTranslatef(1.5, 0.0, -6.0);
</pre>
<pre> glVertexPointer(3, GL_FLOAT, 0, squareVertices);
</pre>
<pre> glEnableClientState(GL_VERTEX_ARRAY);
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
</pre>
<br />
This time, we have moved the square to the right by passing a positive value to the xtrans value in glTranslatef().
<br />
<br />
Hit “Build and Go” and have a look at what’s on the screen, you should get this image:
<br />
<br />
<img alt="5_627_e07938f8d5ae803.png" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_e07938f8d5ae803.png" />
<br />
<br />
Note that the Y co-ordinate is still centred on the screen, the sizing
is the same as before because we have set them back -6.0, and they are
nicely placed side-by-side.
<br />
<br />
Before We Move On
<br />
Experiment by changing values of the xtrans, ytrans, and ztrans
in glTranslatef() and see what happens. I spent many an hour just
changing values and watching what happens. You can even try commenting
out glLoadIdentity() and see what happens.
<br />
<br />
Rotation
<br />
Get your code back to the way it is above after your
experimentation and let’s have a quick look at rotation. We’ll rotate in
2D as our objects are only 2D (but in a 3D world). Later on, when we
create a full 3D object, we can rotate in full 3D (and yes, we can
texture map it...).
<br />
<br />
Rotation is as simple as:
<br />
<br />
glRotatef(GLfloat angle, GLfloat x, GLfloat y, GLfloat z);
<br />
<br />
This function is simple to use. First you supply how much of an angle to
rotate which is the first argument, then we simply specify which axis
or axes to rotate.
<br />
<br />
I will demonstrate rotation in two ways. First, we will do a static
rotation, then animate it by keeping the rotation going; making our
square and cube spin in effect.
<br />
<br />
First, let’s just do a simple rotation. Go to the drawView method and
add the change the drawing code for the triangle and the square as
follows:
<br />
<br />
<br />
<pre> glLoadIdentity();
</pre>
<pre> glTranslatef(-1.5, 0.0, -6.0);
</pre>
<pre> glRotatef(45.0, 0.0, 0.0, 1.0); // Add this line
</pre>
<pre> glVertexPointer(3, GL_FLOAT, 0, triangleVertices);
</pre>
<pre> glEnableClientState(GL_VERTEX_ARRAY);
</pre>
<pre> glDrawArrays(GL_TRIANGLES, 0, 3);
</pre>
<br />
<br />
<pre> glLoadIdentity();
</pre>
<pre> glTranslatef(1.5, 0.0, -6.0);
</pre>
<pre> glRotatef(45.0, 0.0, 0.0, 1.0); // Add this line
</pre>
<pre> glVertexPointer(3, GL_FLOAT, 0, squareVertices);
</pre>
<pre> glEnableClientState(GL_VERTEX_ARRAY);
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
</pre>
<br />
All we’ve done is rotated the triangle and square by 45º along the Z
axis. See the value 1.0 for the Z parameter? That tells OpenGL to rotate
our object 45º along the Z axis.
<br />
<br />
Make the changes to the code and hit “Build and Run” and you should get the following on screen:
<br />
<br />
<img alt="5_627_6bb72746dd59c77.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_6bb72746dd59c77.jpg" />
<br />
<br />
Rotation along the Z axis “spins” the object on the screen - much like
looking at a car tyre spinning when you’re side onto the car. Remember,
the Z axis goes into the screen so it is rotating around this axis.
<br />
<br />
Rotating the X axis, would be like looking at a spinning car tyre with
the car heading towards you (ie you facing the car’s front grill).
Rotating the Y axis is what the tyre would look like as the driver turns
the steering wheel to avoid you (hopefully!). Don’t panic if you’re a
bit confused by that, this next example will allow you to play with
rotation and really see what’s going on.
<br />
<br />
Spinning Our Objects
<br />
In order to make our square and triangle “spin”, we need to
increase the angle each time we draw the frame. Switch to EAGLView.h and
add the following variable:
<br />
<br />
<br />
<pre> GLfloat rota;
</pre>
<br />
Then switch back to EAGLView.m and, in the initWithCoder method, add the following line below the animationInterval assignment:
<br />
<br />
<br />
<pre> rota = 0.0;
</pre>
<br />
All we’ve done is to create a variable to hold the current rotation angle.
<br />
<br />
Now, head back to drawView, and add the following line of code right before the first glLoadIdentity() function call:
<br />
<br />
<br />
<pre> rota += 0.5;
</pre>
<br />
All we’re doing is increasing the rotation angle by 0.5º each time we
draw our two objects. Finally, change both the glRotatef() function
calls to the following:
<br />
<br />
<br />
<pre> glRotatef(rota, 0.0, 0.0, 1.0);
</pre>
<br />
So, what we’re doing is increasing the rotation angle every time we draw
the objects, resulting in a spinning object. The first time we draw the
objects, they will be rotated through 0.5º; the second time they are
drawn, they will be rotated 1.0º etc.
<br />
<br />
Hit build and run and the two objects should spin like a car tyre when viewed side on.
<br />
<br />
For Your Experimentation
<br />
Before leaving this tutorial, I want you to do a couple of things and note what happens:
<br />
<br />
<br />
<pre> 1.
</pre>
<pre> 1.Change the axis which you rotate around. Make the Z axis 0.0 and turn on each the X axis and Y axis one axis at a time and note how the objects rotate so you can get an understanding of rotation along each individual axis.
2.
</pre>
<pre> 2.Change the 1.0 on the current rotation axis to -1.0. Note that they now rotate in the opposite direction.
3.
</pre>
<pre> 3.Change the rota parameter in glRotatef() to -rota. What happens?
</pre>
<br />
I hope you got something out of this. Here’s the completed code for the tutorial:
<br />
<br />
AppleCoder-OpenGLES-03.zip
<br />
<br />
The home of the tutorials is in the “Tutorials” section of the iphonedevsdk.com forums. Check out the thread there.
<br />
<br />
Now, I don’t know about you but I’m a bit bored with white objects. Next
thing we’re going to do is to add some colour to these objects.
<br />
<br />
Until next time, hooroo!
<br />
Simon Maurice
<br />
<br />
<br />
Copyright 2009 Simon Maurice. All Rights Reserved.
<br />
The code provided in these pages are for educational purposes
only and may not be used for commercial purposes without Simon Maurice’s
expressed permission in writing. Information contained within this site
cannot be duplicated in any form without Simon Maurice’s expressed
permission in writing; this includes, but is not limited to, publishing
in printed format, reproduced on web pages, or other forms of electronic
distribution.
<br />
<br />
Linking to these pages on other websites is permitted.</div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-3765795590291919148.post-19602690717334559882012-01-31T05:08:00.000-08:002012-01-31T05:08:00.718-08:00OpenGL ES 04 - Colour and Shading<script type="text/javascript">
<!--
google_ad_client = "ca-pub-9566972421287894";
/* 728x15 LINK AD-1 */
google_ad_slot = "1092558380";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript">
</script>
<br />
<h1 class="firstHeading" style="color: #990000;">
OpenGL ES 04 - Colour and Shading</h1>
<br />
<div style="color: #999999;">
<span style="color: #990000;"><b><span style="color: #cc0000;">Disclaimer:</span></b> <b><span style="color: #444444;">This
post and the whole blog is just a copy of
iPhone-OpenGL-ES-tutorial-Series (seems to have )written by Simon
Maurice. This blog does NOT have a written permission or any kind of
permission from the original author (Simon Maurice). Please use it at
your own risk. If you have any complaints regarding any content of this
blog, please let me know I will remove it right away. Just hoping that
it would be helpful for people who are trying to learn OpenGL. Happy
coding!</span></b></span> </div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<b>Copyright 2009 Simon Maurice. All Rights Reserved.
</b></div>
<div style="color: #999999;">
The code provided in these pages are for educational purposes only and
may not be used for commercial purposes without Simon Maurice’s
expressed permission in writing. Information contained within this site
cannot be duplicated in any form without Simon Maurice’s expressed
permission in writing; this includes, but is not limited to, publishing
in printed format, reproduced on web pages, or other forms of electronic
distribution.
</div>
<br />
<a href="https://github.com/mauriceatron" target="_blank">Source codes can be found here </a><br />
<div style="color: #666666;">
<b><br /></b><br />
<b><br /></b></div>
<b><span style="color: #666666;">Original tutorial starts from here onwards..</span></b><br />
<br />
Like I said in the last tutorial, I’m getting a bit bored with plain
white objects on the screen, let’s add some colour just like the
original Apple template had before we removed it. Do pay attention to
this one because some of the concepts that I will introduce will also
come into play when we start texture mapping (which will be Real Soon
Now).
<br />
<br />
In OpenGL ES, colour can be set as a single block colour for the entire
object, or can be multi-coloured and have shading so the colours drift
through the spectrum from one colour to the next. Single colour is not
very complicated so let’s colour up our objects in a single colour.
<br />
<br />
Like all things with OpenGL, changing the colour puts OpenGL in a
“state” where all following drawing operations will be in that colour,
even if we call our “reset” being glLoadIdentity() (this is because
glLoadIdentity() operates on the actual vertices only). So by adding a
single line of code, we can make our two objects appear in any colour;
anything’s better than white, but I’m just going to go with blue for
now.
<br />
<br />
Fire up Xcode and head off to drawView. After the first glLoadIdentity() call, add the following GL function call:
<br />
<br />
<br />
<pre> glLoadIdentity();
</pre>
<pre> glColor4f(0.0, 0.0, 0.8, 1.0);
</pre>
<br />
This function call, glColor4f() tell OpenGL to commence drawing (and
filling) in this colour which is a blue colour. The parameters are:
<br />
<br />
<br />
<pre> glColor4f( GLfloat red,
</pre>
<pre> GLfloat green,
</pre>
<pre> GLfloat blue,
</pre>
<pre> GLfloat alpha);
</pre>
<br />
In OpenGL ES, you must provide colours with these four parameters (ie
RGBA), there is no option for RGB colours. In case you don’t know, alpha
is the level of transparency with 1.0 being fully solid, down to 0.0
which is fully transparent.
<br />
<br />
The red, green, & blue parameters are floating point values being
between 0.0 and 1.0 with 0.0 being nil intensity, and 1.0 being full
intensity. Therefore white would be (1.0, 1.0, 1.0, 1.0).
<br />
<br />
Add the line and hit “Build and Go”. Our two objects will look like this:
<br />
<br />
<img alt="5_627_9cbe267c93ed244.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_9cbe267c93ed244.jpg" />
<br />
<br />
Better than plain white but not very inspiring. The psychedelic colours
of the Apple rotating square was more interesting so let’s have a look
at how to create that.
<br />
<br />
Multiple Colours
<br />
Having an object with multiple colours is not too much more work.
We need to define and array just like the vertex arrays we have been
using and then tell OpenGL to get it’s colours from that array. Each
colour in the colour array, represents a colour at each vertex (point)
within our object’s vertex array.
<br />
<br />
Let me make that a bit clearer as we colour up the square. Have a look
at the following code where I have defined a colour array to go with the
square’s vertex array:
<br />
<br />
const GLfloat squareVertices[] = {
<br />
<pre> -1.0, 1.0, 0.0, // Top left
</pre>
<pre> -1.0, -1.0, 0.0, // Bottom left
</pre>
<pre> 1.0, -1.0, 0.0, // Bottom right
</pre>
<pre> 1.0, 1.0, 0.0 // Top right
</pre>
<pre> };
</pre>
<br />
<br />
<pre> const GLfloat squareColours[] = {
</pre>
<pre> 1.0, 0.0, 0.0, 1.0,// Red - top left - colour for squareVertices[0]
</pre>
<pre> 0.0, 1.0, 0.0, 1.0, // Green - bottom left - squareVertices[1]
</pre>
<pre> 0.0, 0.0, 1.0, 1.0, // Blue - bottom right - squareVerticies[2]
</pre>
<pre> 0.5, 0.5, 0.5, 1.0 // Grey - top right- squareVerticies[3]
</pre>
<pre> };
</pre>
I hope this illustrates that each colour we have given, represents a
vertex for the square. Before we can run this though, we need to add
some more code for colouring the square:
<br />
<br />
<br />
<pre> glLoadIdentity();
</pre>
<pre> glTranslatef(1.5, 0.0, -6.0);
</pre>
<pre> glRotatef(rota, 0.0, 0.0, -1.0);
</pre>
<pre> glVertexPointer(3, GL_FLOAT, 0, squareVertices);
</pre>
<pre> glEnableClientState(GL_VERTEX_ARRAY);
</pre>
<pre> glColorPointer(4, GL_FLOAT, 0, squareColours); // NEW
</pre>
<pre> glEnableClientState(GL_COLOR_ARRAY); // NEW
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
</pre>
<pre> glDisableClientState(GL_COLOR_ARRAY); // NEW
</pre>
<br />
There are three new lines of code. Let’s deal with them one-by-one:
<br />
<br />
<br />
<pre> glColorPointer(4, GL_FLOAT, 0, squareColours);
</pre>
<br />
This is in fact similar to the function which we call to set up our co-ordinate vertex array. The four parameters are:
<br />
<br />
<br />
<pre> 1.
</pre>
<pre> •Size - the number of colours in the array
2.
</pre>
<pre> •Data Format - we’ve used GL_FLOAT here because our vertex array contains floating point numbers. You can also use bytes if you want to specify the colours in 0-255 format.
3.
</pre>
<pre> •Stride - Again, this tells OpenGL to skip a number of bytes between each value if your data contains other information.
4.
</pre>
<pre> •Array Points - where the data is stored.
</pre>
<br />
Note that when specifying the data format, GL_FLOAT is the parameter
format (an enumeration) telling OpenGL what format; GLfloat is the data
type for declaring a floating point number for OpenGL.
<br />
<br />
OK, so that function call tells OpenGL where the data is and what format
it is in. However, like the co-ordinate vertex array which tells OpenGL
the co-ordinates of the object, we need to put OpenGL into the required
“state” which will make OpenGL use our colours when rendering the
object.
<br />
<br />
So this call:
<br />
<br />
<br />
<pre> glEnableClientState(GL_COLOR_ARRAY);
</pre>
<br />
This enables the appropriate state in the OpenGL engine. Instead of
passing GL_VERTEX_ARRAY, we just tell OpenGL that it’s a colour array
with GL_COLOR_ARRAY.
<br />
<br />
Next we simply draw the square as per normal. After the square has been
drawn, we need to then disable the colour array. If we don’t, the next
time we draw the triangle, it too will be coloured like the square. So
we call:
<br />
<br />
<br />
<pre> glDisableClientState(GL_COLOR_ARRAY);
</pre>
<br />
This takes the colour array off the list of OpenGL’s current state. If
we didn’t do this, the first call to drawView will have the triangle in
blue, the second call to drawView will use the colour array to colour
the triangle. However, there being only three vertices in the triangle’s
co-ordinate array (triangleVerticies[]), it will only use the first
three colours.
<br />
<br />
So, make the changes to drawView, adding those three new lines as shown
above, then hit “Build and Go” and you’re display should like this:
<br />
<br />
<img alt="5_627_c189fca22983172.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_c189fca22983172.jpg" />
<br />
<br />
If you like, you can turn off rotation (comment the glRotatef() function
call) so you can see the relation of the square’s vertex array vertices
with the colour array.
<br />
<br />
Shading
<br />
Notice how the square gradually goes from one colour to the next?
OpenGL achieves this through shading. There are two shading models
which can be used by OpenGL: GL_FLAT & GL_SMOOTH. What you’re seeing
already is the GL_SMOOTH shading which is the default.
<br />
<br />
Just to show the difference, before the glLoadIdentity() for the square, insert the following line:
<br />
<br />
<br />
<pre> glShadeModel(GL_FLAT);
</pre>
<br />
The glShadeModel() function changes the OpenGL state to the flat shading
model from the smooth shading model. Again, OpenGL changes it’s state
and retains this state until you tell it otherwise so you could put that
in the setupView method if you like. After doing a “Build and Go”, the
shading of the square will change to the following:
<br />
<img alt="5_627_dbe6693d3cb9a96.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_dbe6693d3cb9a96.jpg" />
<br />
<br />
Let me explain what’s happening here.
<br />
<br />
The triangle is being rendered as per normal. Being a flat colour,
shading does not affect the drawing of the triangle. With the square,
you can clearly see now the two triangles which OpenGL uses to make up
the square. Due to the flat shading model, OpenGL only uses the last
colour for filling the each triangle, being squareColours[2] (blue) and
squareColours[3] (grey). Please review the Square Primitives tutorial if
you’re unsure why these two colours represent the last two points in
rendering the two triangles which make up the square.
<br />
<br />
As a recap: GL_SMOOTH is smooth shading, which means that when it comes
to filing the square, OpenGL takes the defined colour in our
squareColours[] array for each vertex in our squareVertices[] array, and
uses interpolation for each pixel in the square between the points to
smoothly change the colour between each of the four points. In other
words, it gives us that coloured square shown originally.
<br />
<br />
GL_FLAT uses the colour defined for the last vertex of the object and
fills the entire primitive with that colour. Squares are made up of two
triangles so we have the square coloured in two halves.
<br />
<br />
Conculsion
<br />
Well I hope this has been useful for you. In reality, you
probably just want to leave the shading as GL_SMOOTH unless you’re doing
one of those retro-3D games from the C64 days. GL_SMOOTH is the default
so you don’t need to enable it.
<br />
<br />
Also, please note, that what you used above for the colour points is
also used for texture mapping so I’ll be coming back to this in a
tutorial or two.
<br />
<br />
Texture mapping is just around the corner now. I’m going to show you how
to create a 3D object in the next tutorial. It will be flat coloured
but that’s okay because we’ll texture map it in the following tutorial.
<br />
<br />
Here’s the finished code for today’s tutorial.
<br />
<br />
AppleCoder-OpenGLES-04.zip
<br />
<br />
The home of the tutorials is in the “Tutorials” section of the iphonedevsdk.com forums. Check out the thread there.
<br />
<br />
Until next time, hooroo!
<br />
Simon Maurice
<br />
<br />
<br />
<br />
<br />
Copyright 2009 Simon Maurice. All Rights Reserved.
<br />
The code provided in these pages are for educational purposes
only and may not be used for commercial purposes without Simon Maurice’s
expressed permission in writing. Information contained within this site
cannot be duplicated in any form without Simon Maurice’s expressed
permission in writing; this includes, but is not limited to, publishing
in printed format, reproduced on web pages, or other forms of electronic
distribution.
<br />
<br />
Linking to these pages on other websites is permitted.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-3765795590291919148.post-75337864692732617932012-01-31T05:07:00.000-08:002012-01-31T05:07:05.065-08:00OpenGL ES 05 - Texture Mapping Our Square<script type="text/javascript">
<!--
google_ad_client = "ca-pub-9566972421287894";
/* 728x15 LINK AD-1 */
google_ad_slot = "1092558380";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript">
</script>
<br />
<h1 class="firstHeading" style="color: #990000;">
OpenGL ES 05 - Texture Mapping Our Square</h1>
<br />
<div style="color: #999999;">
<span style="color: #990000;"><b><span style="color: #cc0000;">Disclaimer:</span></b> <b><span style="color: #444444;">This
post and the whole blog is just a copy of
iPhone-OpenGL-ES-tutorial-Series (seems to have )written by Simon
Maurice. This blog does NOT have a written permission or any kind of
permission from the original author (Simon Maurice). Please use it at
your own risk. If you have any complaints regarding any content of this
blog, please let me know I will remove it right away. Just hoping that
it would be helpful for people who are trying to learn OpenGL. Happy
coding!</span></b></span> </div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<b>Copyright 2009 Simon Maurice. All Rights Reserved.
</b></div>
<div style="color: #999999;">
The code provided in these pages are for educational purposes only and
may not be used for commercial purposes without Simon Maurice’s
expressed permission in writing. Information contained within this site
cannot be duplicated in any form without Simon Maurice’s expressed
permission in writing; this includes, but is not limited to, publishing
in printed format, reproduced on web pages, or other forms of electronic
distribution.
</div>
<br />
<a href="https://github.com/mauriceatron" target="_blank">Source codes can be found here </a><br />
<div style="color: #666666;">
<b><br /></b></div>
<b><span style="color: #666666;">Original tutorial starts from here onwards..</span></b><br />
<br />
<br />
I’ve decided to bring forward texture mapping because it’s probably
easier to texture map a single faced object rather than a multi-faced
(or 3D object). Also, it seems this is where most iPhone OpenGL ES
programmers seem to get a bit stuck so I’ll run through texture mapping
now.
<br />
<br />
I know that I have skipped a lot of the detail of OpenGL in favour of
enabling you to get objects on the screen and experiment with them
rather than go through pages and pages of OpenGL history, differences
between OpenGL ES and OpenGL etc etc. However, this time I am going to
go through some of the technical details that I have skipped in the
past.
<br />
<br />
That, coupled with the fact I need to cover quite a bit of detail, means that this is going to be a long tutorial.
<br />
<br />
Having said that, the majority of the code has to do with loading the
texture into our program and getting it into the OpenGL engine so OpenGL
can use it. It’s not complicated, it just requires a bit of work in the
iPhone SDK.
<br />
<br />
Getting Ready for the Texture
<br />
Before we can use a texture, we need to load it into our
application, format it for OpenGL, and then tell OpenGL where to find
it. Once we have done that, the rest is as easy as colouring our square
was in the last tutorial.
<br />
<br />
Fire up Xcode and open EAGLView.h in the editor. First of all we need to
provide a variable which OpenGL requires. Add the following
declaration:
<br />
<br />
<br />
<pre> GLuint textures[1];
</pre>
<br />
Obviously, this is an array of 1 GLuint. You’ve seen me use GLfloat
before and, once again, GLuint is just an OpenGL connivence typedef for
an unsigned integer. You should always use the GLxxxx typedefs rather
than the Objective C types because the OpenGL typedefs have been defined
for the OpenGL implementation, not the development environment.
<br />
<br />
Later on, we will call the OpenGL function glGenTextures() to populate
this variable. Just remember you’ve defined it for now and we’ll cover
glGenTextures() and this variable later on.
<br />
<br />
Down in the method prototypes, add the following method prototype:
<br />
<br />
- (void)loadTexture;
<br />
<br />
This is where we’re going to put the code to actually load the texture.
<br />
<br />
Add CoreGraphics Framework to Your Project
<br />
In order to load the texture and process it, we will use the
CoreGraphics framework as it provides all the methods we need without
needing to write all that low level code you see in Windows OpenGL
tutorials.
<br />
<br />
In the Xcode “Groups & Files” side bar, right click on the “Frameworks” group and choose Add -> Existing Frameworks...
<br />
<br />
In the search box, enter “CoreGraphics.framework” and look for the
folder in the results which matches your application target (iPhone SDK
2.2.1 in my case). Click on the folder, and add it to your project (the
folder icon for the framework is fine.
<br />
<br />
Next, we need to add the texture image to our project so it is included
in the application’s bundle. Download the texture checkerplate.png and
save it to your project directory. Add the image to your project’s
Resources group by right clicking on the Resources group and selecting
Add -> Existing Files... Choose the image and it should appear in
the resources group.
<br />
<br />
Loading the Texture into Our Application and OpenGL
<br />
Switch now to EAGLView.m and we’ll implement the loadTexture method.
<br />
<br />
- (void)loadTexture {
<br />
<br />
<br />
}
<br />
<br />
The following code all goes sequentially in this method so just keep
adding each line after the other. The first thing we need to do is to
get the image into our application by using the following code:
<br />
<br />
CGImageRef textureImage = [UIImage imageNamed:@"checkerplate.png"].CGImage;
<br />
if (textureImage == nil) {
<br />
<pre> NSLog(@"Failed to load texture image");
</pre>
<pre> return;
</pre>
}
<br />
<br />
A CGImageRef is a CoreGraphics data type which collects all the
information about the image. To get this information all we do is use
the UIImage class method imageNamed: which creates an autorelease’d
UIImage finding the file by it’s name in our Application’s main bundle.
UIImage automatically creates the CGImageRef and is accessible by the
UIImage property CGImage.
<br />
<br />
Now, we just need to get the size of the image for later reference:
<br />
<br />
<br />
<pre> NSInteger texWidth = CGImageGetWidth(textureImage);
</pre>
<pre> NSInteger texHeight = CGImageGetHeight(textureImage);
</pre>
<br />
The CGImageRef data contains the image’s width and height but we can’t
access it directly, we need to use the above two accessor functions.
<br />
<br />
The CGImageRef, just like the data type’s name suggests, does not hold
the image data, only a reference to the image’s data. So we need to
allocate some memory to hold the actual image data:
<br />
<br />
<br />
<pre> GLubyte *textureData = (GLubyte *)malloc(texWidth * texHeight * 4);
</pre>
<br />
The correct amount of data to allocate is the width multiplied by the
height, multiplied by 4. Remember from the last tutorial that OpenGL
only accepts RGBA values? Each pixel is 4 bytes in size, one byte for
each of the RGBA values.
<br />
<br />
Now, we have some absolute mouthfuls of function calls:
<br />
<br />
<br />
<pre> CGContextRef textureContext = CGBitmapContextCreate(
</pre>
<pre> textureData,
</pre>
<pre> texWidth,
</pre>
<pre> texHeight,
</pre>
<pre> 8, texWidth * 4,
</pre>
<pre> CGImageGetColorSpace(textureImage),
</pre>
<pre> kCGImageAlphaPremultipliedLast);
</pre>
<br />
<br />
<pre> CGContextDrawImage(textureContext,
</pre>
<pre> CGRectMake(0.0, 0.0, (float)texWidth, (float)texHeight),
</pre>
<pre> textureImage);
</pre>
<br />
<br />
<pre> CGContextRelease(textureContext);
</pre>
<br />
Firstly, as the function name suggests, this is a CoreGraphics function
that returns a Quartz2D graphics context reference. Basically what we’re
doing is pointing CoreGraphics at our texture data and telling it the
format and size of our texture.
<br />
<br />
Then, we actually draw the image into our allocated data (textureData
pointer) from the data pointed to context reference we created before.
This context contains all the information it needs to copy the data into
our malloc()’d space in the right format for OpenGL.
<br />
<br />
We’re really finished now with CoreGraphics and we can release the textureContext handle which we created.
<br />
<br />
I know I’ve sped through the above code, but we’re more interested in
the OpenGL side of things. You can reuse that code for any PNG format
graphics texture that you add to your project in this way.
<br />
<br />
Now, onto the OpenGL programming.
<br />
<br />
Now, remember that variable we declared originally in the header file?
We are now going to use it. Have a look at the next line of code:
<br />
<br />
<br />
<pre> glGenTextures(1, &textures[0]);
</pre>
<br />
We are going to copy the texture data from our application into the
OpenGL engine so we need to tell OpenGL to allocate memory for it (we
can’t do it directly). Remember textures[] was defined as a GLuint? Once
we call glGenTextures, OpenGL creates a “handle” or “pointer” which is a
unique reference to each individual texture we load into OpenGL. The
value that OpenGL returns to us isn’t important to us, just every time
we want to refer to this checkerplate.png texture, we just need to refer
to textures[0]. We know what we’re talking about and so does OpenGL.
<br />
<br />
We can allocate space for multiple textures at one time. For example, if
we needed 10 textures for our application, we can do the following:
<br />
<br />
<br />
<pre> GLuint textures[10];
</pre>
<pre> glGenTextures(10, &textures[0]);
</pre>
<br />
For this example, we only need one texture so we’re allocating one.
<br />
<br />
Next we need to activate the texture which we just generated:
<br />
<br />
<br />
<pre> glBindTexture(GL_TEXTURE_2D, textures[0]);
</pre>
<br />
The second parameter is obvious, it’s the texture we just created. The
first parameter is always GL_TEXTURE_2D because that’s all OpenGL ES
accepts at this point. “Full” OpenGL allows for 1D and 3D textures but
I’m sure this is still required in OpenGL ES for future compatibility.
<br />
<br />
Just remember to use it to activate textures.
<br />
<br />
Next, we send our texture data (pointed to by textureData) into OpenGL.
OpenGL manages the texture data over on “it’s” side (the server side) so
the data is converted in the required format for the hardware
implementation, and copied into OpenGL’s space. It’s a bit of a
mouthful but most parameters will always be the same due to the
limitations of OpenGL ES:
<br />
<br />
<br />
<pre> glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureData);
</pre>
<br />
Going through the parameters, they are:
<br />
<br />
<br />
<pre> 1.
</pre>
<pre> •target - basically, this is always GL_TEXTURE_2D
2.
</pre>
<pre> •level - specifies the level of detail of the texture. 0 means the full detail </pre>
<pre>the image allows, higher numbers goes into nth level mipmap image reduction
3.
</pre>
<pre> •internal_format - the internal format and format listed below must be the same. Hence GL_RGBA for both.
4.
</pre>
<pre> •width - width of the image
5.
</pre>
<pre> •height - height of the image
6.
</pre>
<pre> •border - must always be set to 0 as OpenGL ES does not support texture borders.
7.
</pre>
<pre> •format - must be the same as internal_format
8.
</pre>
<pre> •type - the type of each pixel. Remember there were four bytes to a pixel? </pre>
<pre>Therefore each pixel is made of 1 unsigned byte (RGBA remember).
9.
</pre>
<pre> •pixels - point to the actual image data
</pre>
<br />
So, while there are quite a few parameters, most are either common
sense, always the same, or just require you to type in the variables you
defined previously (textureData, texWidth, & texHeight). And just
remember all you’re doing is handing control of you’re texture data over
to OpenGL.
<br />
<br />
Now that you’ve passed the data to OpenGL, you can free the textureData that we allocated earlier:
<br />
<br />
<br />
<pre> free(textureData);
</pre>
<br />
There are only three more function calls to make:
<br />
<br />
<br />
<pre> glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
</pre>
<pre> glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
</pre>
<pre> glEnable(GL_TEXTURE_2D);
</pre>
<br />
These three calls just make final settings to OpenGL and then puts OpenGL in the texture mapping “state”.
<br />
<br />
The first two calls tell OpenGL how to process the textures when
magnifying (up close - GL_TEXTURE_MAG_FILTER) and minimising (far away -
GL_TEXTURE_MIN_FILTER). You do need to specify at least one of these
for texture mapping to work and the GL_LINEAR option has been set for
both.
<br />
<br />
Finally, we just call glEnable() to ask OpenGL to use textures when you tell it to in the drawing code.
<br />
<br />
Finally, we need to add a call to this method within the initWithCoder initialiser.
<br />
<br />
<br />
<pre> [self setupView];
</pre>
<pre> [self loadTexture];// Add this line
</pre>
<br />
Just add the second line after the call to the setupView method.
<br />
<br />
Adjustments to drawView
<br />
That’s the hard work done. Changes to drawView method are no
harder than colouring a the square from the previous tutorial. First,
comment out the squareColours[] array as we won’t use it.
<br />
<br />
Now, remember when we were colouring the square, for each vertex of the
square, we provided a colour value. When it comes to texture mapping, we
need to do exactly the same thing but instead of telling OpenGL the
colour for each vertex, we tell OpenGL what co-ordinate of the texture
corresponds to that vertex.
<br />
<br />
Before we can do that, we need to know what are the co-ordinates of the
texture. OpenGL locates the origin of a texture (0, 0) at the lower
left, with increasing values from 0 -> 1 along each axis. Have a look
at the following image of our texture:
<br />
<br />
<img alt="5_627_bbe0d7e04c0c9b9.png" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_bbe0d7e04c0c9b9.png" />
<br />
<br />
Refer this back to our squareVertices[].
<br />
<br />
const GLfloat squareVertices[] = {
<br />
<pre> -1.0, 1.0, 0.0, // Top left
</pre>
<pre> -1.0, -1.0, 0.0, // Bottom left
</pre>
<pre> 1.0, -1.0, 0.0, // Bottom right
</pre>
<pre> 1.0, 1.0, 0.0 // Top right
</pre>
<pre> };
</pre>
<br />
Can you see that the first texture co-ordinate we need to specify is for
the top left of the texture? That would be texture co-ordinate (0, 1).
Our second vertex is the bottom left of the square, therefore it is
texture co-ordinate (0, 0). We then go to the bottom right and that’s
texture co-ordinate (1, 0), finally ending on the top right we end on
texture co-ordinate (1, 1). Thus, we specify the squareTextureCoords[]
as follows:
<br />
<br />
<br />
<pre> const GLshort squareTextureCoords[] = {
</pre>
<pre> 0, 1, // top left
</pre>
<pre> 0, 0, // bottom left
</pre>
<pre> 1, 0, // bottom right
</pre>
<pre> 1, 1 // top right
</pre>
<pre> };
</pre>
<br />
Note we have used GLshort instead of GLfloat for this. Add the above code to your project.
<br />
<br />
Can you see how this is similar to what we did with the colour array?
<br />
<br />
Right, now we need to modify the drawing code. Leave the triangle
drawing code as is and start from the glLoadIdentity() before the square
drawing code. The square drawing code now looks like the following:
<br />
<br />
<br />
<pre> glLoadIdentity();
</pre>
<pre> glColor4f(1.0, 1.0, 1.0, 1.0); // NEW
</pre>
<pre> glTranslatef(1.5, 0.0, -6.0);
</pre>
<pre> glRotatef(rota, 0.0, 0.0, 1.0);
</pre>
<pre> glVertexPointer(3, GL_FLOAT, 0, squareVertices);
</pre>
<pre> glEnableClientState(GL_VERTEX_ARRAY);
</pre>
<br />
<br />
<pre> glTexCoordPointer(2, GL_SHORT, 0, squareTextureCoords); // NEW
</pre>
<pre> glEnableClientState(GL_TEXTURE_COORD_ARRAY); // NEW
</pre>
<br />
<br />
<pre> glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
</pre>
<pre> glDisableClientState(GL_TEXTURE_COORD_ARRAY); // NEW
</pre>
<br />
OK, there are four new lines of code, and I’ve deleted the code for
colouring the square from the previous tutorial. The first line is a
call to glColor4f() which I’ll cover in detail below.
<br />
<br />
The next three should be quite familiar to you by now. Instead of
referring to object vertices or colours, we are just referring to
textures instead.
<br />
<br />
<br />
<pre> glTexCoordPointer(2, GL_SHORT, 0, squareTextureCoords); // NEW
</pre>
<pre> glEnableClientState(GL_TEXTURE_COORD_ARRAY); // NEW
</pre>
<br />
The first call is to tell OpenGL where our texture co-ordinate array is
stored and what format it is in. The difference is that we say there are
2 values for each co-ordinate (it’s a 2D texture of course), the data
type we used was a GLushort which translates to GL_SHORT here, there is
no stride (0), and the pointer to our co-ordinates.
<br />
<br />
Next we just tell OpenGL to enable the client state for texture mapping with the co-ordinate array we’ve just specified.
<br />
<br />
Next the unchanged glDrawArrays() is called before:
<br />
<br />
<br />
<pre> glDisableClientState(GL_TEXTURE_COORD_ARRAY); // NEW
</pre>
<br />
Remember when we were colouring the square differently to the triangle
we turned off the colour array? Again, we need to do this for texture
mapping otherwise OpenGL will use the texture for the triangle.
<br />
<br />
Make the changes to the code and hit “Build and Go”, you should get the following:
<br />
<br />
<img alt="5_627_57e9a43a66fec3b.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_57e9a43a66fec3b.jpg" />
<br />
<br />
Our checkerplate texture in now mapped onto the square and the triangle is as per normal.
<br />
<br />
Further Experimentation
<br />
First of all, let me talk about the new glColor4f() line which we added to the square drawing code:
<br />
<br />
<br />
<pre> glColor4f(1.0, 1.0, 1.0, 1.0); // NEW
</pre>
<br />
This of course changes the drawing colour to white, fully opaque. Can
you guess why I added this line? OpenGL is of course a “state” machine
so once we set something, it stays in that state until we tell it
otherwise. So the colour was set as the blue until we made it white.
<br />
<br />
OK, now when texture mapping, OpenGL performs a multiplication between
the current colour set (the blue) and the current texture pixel. That
is:
<br />
<pre> R G B A
</pre>
<pre> Colour Set: 0.0, 0.0, 0.8, 1.0
</pre>
<pre> Texture Pixel Colour: 1.0, 1.0, 1.0, 1.0
</pre>
<br />
So, when OpenGL draws that pixel, it multiples
<br />
<br />
<br />
<pre> Colour_Red * Pixel_Colour_Red = Rendered_colour
</pre>
<pre> 0.0 * 1.0 = 0.0
</pre>
<pre> Colour_Green * Pixel_Colour_Green
</pre>
<pre> 0.0 * 0.0 = 0.0
</pre>
<pre> Colour_Blue * Pixel_Colour_Blue
</pre>
<pre> 0.8 * 1.0 = 0.8
</pre>
<br />
Comment out that glColor4f() before the square drawing code, you should get something like this:
<br />
<br />
<img alt="5_627_1b956c5df9c5994.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_1b956c5df9c5994.jpg" />
<br />
<br />
When we had white, the multiplation would have been:
<br />
<br />
Set Colour : 1.0, 1.0, 1.0, 1.0
<br />
<pre> is mulitplied by
</pre>
Pixel Colour : 0.8, 0.8, 0.8, 1.0
<br />
<br />
<br />
<pre> Result: 0.8, 0.8, 0.8, 1.0
</pre>
<br />
So that’s why we need to set the colour as white.
<br />
<br />
OK, That’s It!
<br />
That will do for this tutorial, I know I’ve covered a lot but I
hope you can see that the actual texture mapping code is not really
significant, it’s more the loading and setting up the texture that
really takes the time. Next time, we’re off into texture mapped 3D
objects, blending and other fun stuff.
<br />
<br />
As usual, here’s the code and you can email me with any questions.
<br />
<br />
AppleCoder-OpenGLES-05.zip
<br />
<br />
The home of the tutorials is in the “Tutorials” section of the iphonedevsdk.com forums. Check out the thread there.
<br />
<br />
Until next time, hooroo!
<br />
Simon Maurice
<br />
<br />
Copyright 2009 Simon Maurice. All Rights Reserved.
<br />
The code provided in these pages are for educational purposes
only and may not be used for commercial purposes without Simon Maurice’s
expressed permission in writing. Information contained within this site
cannot be duplicated in any form without Simon Maurice’s expressed
permission in writing; this includes, but is not limited to, publishing
in printed format, reproduced on web pages, or other forms of electronic
distribution.
<br />
<br />
Linking to these pages on other websites is permitted.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-3765795590291919148.post-20554319703441903862012-01-31T05:05:00.000-08:002012-01-31T05:05:13.485-08:00OpenGL ES 06 - Objects in 3D<script type="text/javascript">
<!--
google_ad_client = "ca-pub-9566972421287894";
/* 728x15 LINK AD-1 */
google_ad_slot = "1092558380";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript">
</script>
<br />
<h1 class="firstHeading" style="color: #990000;">
OpenGL ES 06 - Objects in 3D</h1>
<div style="color: #999999;">
<span style="color: #990000;"><b><span style="color: #cc0000;">Disclaimer:</span></b> <b><span style="color: #444444;">This
post and the whole blog is just a copy of
iPhone-OpenGL-ES-tutorial-Series (seems to have )written by Simon
Maurice. This blog does NOT have a written permission or any kind of
permission from the original author (Simon Maurice). Please use it at
your own risk. If you have any complaints regarding any content of this
blog, please let me know I will remove it right away. Just hoping that
it would be helpful for people who are trying to learn OpenGL. Happy
coding!</span></b></span> </div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<b>Copyright 2009 Simon Maurice. All Rights Reserved.
</b></div>
<div style="color: #999999;">
The code provided in these pages are for educational purposes only and
may not be used for commercial purposes without Simon Maurice’s
expressed permission in writing. Information contained within this site
cannot be duplicated in any form without Simon Maurice’s expressed
permission in writing; this includes, but is not limited to, publishing
in printed format, reproduced on web pages, or other forms of electronic
distribution.
</div>
<br />
<a href="https://github.com/mauriceatron" target="_blank">Source codes can be found here </a><br />
<div style="color: #666666;">
<b><br /></b></div>
<b><span style="color: #666666;">Original tutorial starts from here onwards..</span></b><br />
<br />
<br />
<br />
So far, we have done a fair bit with our 2D objects. Now is the time
to head off and create 3D objects. Whilst they are not significantly
more complicated, they do require more vertices (if you create them
using a vertex array) or more transformations if you wanted to use our
square and duplicate it into a cube.
<br />
<br />
I suppose I should have covered points and lines first, but hey, we’re
on a roll so far with our little texture mapped square and coloured
triangles so let’s not slow down to less interesting shapes!
<br />
<br />
Also I do need to return to transformations and cover rotation in some
more detail. And then there’s the really basic beginner stuff which I
haven’t covered.... Well, all it means is that I’ve got a lot of
tutorials to write yet!!
<br />
<br />
To Begin With, Gut the drawView Method
<br />
Say good-bye to all your hard coding work, it’s time to cut
everything out and return the drawView method to it’s most basic state.
<br />
<br />
Make the drawView method look like this:
<br />
<br />
- (void)drawView {
<br />
<br />
// Our new object definition code goes here
<br />
<br />
<br />
<pre> [EAGLContext setCurrentContext:context];
</pre>
<pre> glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
</pre>
<pre> glViewport(0, 0, backingWidth, backingHeight);
</pre>
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
<br />
glMatrixMode(GL_MODELVIEW);
<br />
<br />
// Our new drawing code goes here
<br />
<br />
<br />
<pre> glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
</pre>
<pre> [context presentRenderbuffer:GL_RENDERBUFFER_OES];
</pre>
<pre> [self checkGLError:NO];
</pre>
}
<br />
<br />
You can thank me right now for making everything work in a 3D space up
front because otherwise we’d be working on the depth buffer etc and
adding lots of new code. By now the above should be quite familiar to
you.
<br />
<br />
Defining a 3D Object
<br />
We’re going to produce a cube because they’re easy to
conceptualise, quite common in 3D graphics, and look cool rotating in 3D
with texture mapping. Before we can draw a cube in 3D, we just need to
remember that a 3D cube is just 6 of our squares that we’ve been using
all along. It’s easy but I’ll break the definition down for you all.
Let’s start by defining the front face:
<br />
<br />
<br />
<pre> const GLfloat cubeVertices[] = {
</pre>
<br />
<br />
<pre> // Define the front face
</pre>
<pre> -1.0, 1.0, 1.0, // top left
</pre>
<pre> -1.0, -1.0, 1.0, // bottom left
</pre>
<pre> 1.0, -1.0, 1.0, // bottom right
</pre>
<pre> 1.0, 1.0, 1.0, // top right
</pre>
<br />
<br />
Almost identical to the original square except that I’ve brought
the face forward one unit. I’ll mention why in the closing of this
tutorial. Now the top face:
<br />
<br />
<br />
<pre> // Top face
</pre>
<pre> -1.0, 1.0, -1.0, // top left (at rear)
</pre>
<pre> -1.0, 1.0, 1.0, // bottom left (at front)
</pre>
<pre> 1.0, 1.0, 1.0, // bottom right (at front)
</pre>
<pre> 1.0, 1.0, -1.0, // top right (at rear)
</pre>
<br />
<br />
Note that I’ve drawing the top face not only in the same
direction as the front face (vertices specified in a anti-clockwise
direction when viewed from the rear face’s front, not the front of the
object) but I’ve also started off at the same point? If we were to
rotate the finished cube 90º along the X axis so now the top face is the
front face, the first vertex specified is the top left, followed by the
top right?
<br />
<br />
Next, the rear face:
<br />
<br />
<br />
<pre> // Rear face
</pre>
<pre> 1.0, 1.0, -1.0, // top right (when viewed from front)
</pre>
<pre> 1.0, -1.0, -1.0, // bottom right
</pre>
<pre> -1.0, -1.0, -1.0, // bottom left
</pre>
<pre> -1.0, 1.0, -1.0, // top left
</pre>
<br />
Notice the continuation of the order and starting point of the vertices? We continue to do this throughout the remaining faces.
<br />
<br />
<br />
<pre> // Bottom Face
</pre>
<pre> -1.0, -1.0, 1.0, // Bottom left front
</pre>
<pre> 1.0, -1.0, 1.0, // right front
</pre>
<pre> 1.0, -1.0, -1.0, // right rear
</pre>
<pre> -1.0, -1.0, -1.0, // left rear
</pre>
<br />
Can you still see that I’m following the same order and starting point.
Imagine rotating the face to the front in your mind and seeing where the
points line up.
<br />
<br />
Finally, we have the left and the right face:
<br />
<br />
<br />
<pre> // Left face
</pre>
<pre> -1.0, 1.0, -1.0, // top left
</pre>
<pre> -1.0, 1.0, 1.0, // top right
</pre>
<pre> -1.0, -1.0, 1.0, // bottom right
</pre>
<pre> -1.0, -1.0, -1.0, // bottom left
</pre>
<br />
<br />
<pre> // Right face
</pre>
<pre> 1.0, 1.0, 1.0, // top left
</pre>
<pre> 1.0, 1.0, -1.0, // top right
</pre>
<pre> 1.0, -1.0, -1.0, // right
</pre>
<pre> 1.0, -1.0, 1.0 // left
</pre>
<br />
Here’s the full definition of the cube:
<br />
<br />
<br />
<pre> const GLfloat cubeVertices[] = {
</pre>
<br />
<br />
<pre> // Define the front face
</pre>
<pre> -1.0, 1.0, 1.0, // top left
</pre>
<pre> -1.0, -1.0, 1.0, // bottom left
</pre>
<pre> 1.0, -1.0, 1.0, // bottom right
</pre>
<pre> 1.0, 1.0, 1.0, // top right
</pre>
<br />
<br />
<pre> // Top face
</pre>
<pre> -1.0, 1.0, -1.0, // top left (at rear)
</pre>
<pre> -1.0, 1.0, 1.0, // bottom left (at front)
</pre>
<pre> 1.0, 1.0, 1.0, // bottom right (at front)
</pre>
<pre> 1.0, 1.0, -1.0, // top right (at rear)
</pre>
<br />
<br />
<pre> // Rear face
</pre>
<pre> 1.0, 1.0, -1.0, // top right (when viewed from front)
</pre>
<pre> 1.0, -1.0, -1.0, // bottom right
</pre>
<pre> -1.0, -1.0, -1.0, // bottom left
</pre>
<pre> -1.0, 1.0, -1.0, // top left
</pre>
<br />
<br />
<pre> // bottom face
</pre>
<pre> -1.0, -1.0, 1.0,
</pre>
<pre> -1.0, -1.0, -1.0,
</pre>
<pre> 1.0, -1.0, -1.0,
</pre>
<pre> 1.0, -1.0, 1.0,
</pre>
<br />
<br />
<pre> // left face
</pre>
<pre> -1.0, 1.0, -1.0,
</pre>
<pre> -1.0, 1.0, 1.0,
</pre>
<pre> -1.0, -1.0, 1.0,
</pre>
<pre> -1.0, -1.0, -1.0,
</pre>
<br />
<br />
<pre> // right face
</pre>
<pre> 1.0, 1.0, 1.0,
</pre>
<pre> 1.0, 1.0, -1.0,
</pre>
<pre> 1.0, -1.0, -1.0,
</pre>
<pre> 1.0, -1.0, 1.0
</pre>
<pre> };
</pre>
<br />
<br />
If you’re having problems with the co-ordinate system, then
please really make an effort to visualise it in your mind. If you have
problems with that, grab out a piece of paper and draw it in an
isometric view. You really now need to start to get a grasp on objects
in 3D.
<br />
<br />
Put the cubeVertices below the comment I made in the drawView method stating “New object definition goes here”.
<br />
<br />
OK, now we need to draw this sucker.
<br />
<br />
Drawing the Cube
<br />
The easiest way for me to teach you to draw this cube is to use
only the code which you have seen previously. Later on, we’re going to
go into some more advanced (but ultimately easier once you understand
it) ways to draw 3D objects. However, for now, let me just introduce
drawing in 3D first.
<br />
<br />
Let’s start with some code which will need no explanation. Below where
I’ve put “Our new drawing code goes here, add the following lines:
<br />
<br />
<br />
<pre> glLoadIdentity();
</pre>
<pre> glTranslatef(0.0, 0.0, -6.0);
</pre>
<pre> glVertexPointer(3, GL_FLOAT, 0, cubeVertices);
</pre>
<pre> glEnableClientState(GL_VERTEX_ARRAY);
</pre>
<br />
Nothing new here. We just reset our vertex state, move the cube back
into the screen away from us so we can see it, tell OpenGL about our
vertex array and the format it’s in, then enable OpenGL to use it.
<br />
<br />
The follow code is almost the same as you’ve used before:
<br />
<br />
// Draw the front face in Red
<br />
glColor4f(1.0, 0.0, 0.0, 1.0);
<br />
<pre> glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
</pre>
<br />
That’s nothing really new, we’ve set the drawing colour to red and told
OpenGL to draw the vertices 0 to 4 in our array as a square. Now let’s
draw the top face which is the next 4 vertices in our array:
<br />
<br />
// Draw the top face in green
<br />
glColor4f(0.0, 1.0, 0.0, 1.0);
<br />
glDrawArrays(GL_TRIANGLE_FAN, 4, 4);
<br />
<br />
Look at glDrawArrays(). If you remember me describing it to you, I said
that the second parameter was the start offset? Well, because we’re
drawing the second square or face in our cube, we need to tell OpenGL to
start at the offset 4 (which is cubeVertices[4], the offsets 0~3 are
the front face), and then draw another four vertices.
<br />
<br />
Now we can draw the rear face:
<br />
<br />
<br />
<pre> // Draw the rear face in Blue
</pre>
<pre> glColor4f(0.0, 0.0, 1.0, 1.0);
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 8, 4);
</pre>
<br />
Same deal, this time we’re starting at cubeVertices[8]. The same pattern continues for the final 3 faces:
<br />
<br />
<br />
<pre> // Draw the bottom face
</pre>
<pre> glColor4f(1.0, 1.0, 0.0, 1.0);
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 12, 4);
</pre>
<br />
<br />
<pre> // Draw the left face
</pre>
<pre> glColor4f(0.0, 1.0, 1.0, 1.0);
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 16, 4);
</pre>
<br />
<br />
<pre> // Draw the right face
</pre>
<pre> glColor4f(1.0, 0.0, 1.0, 1.0);
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 20, 4);
</pre>
<br />
All we’ve done once again is to change the colour, and change the starting offset for glDrawArrays().
<br />
<br />
Now, if you were to hit “Build and Go” right now, all you’d get in an
inanimate red square which would be nothing further along from where you
started. In order to see all 6 sides in all their glory, let’s rotate
this cube along all three axes.
<br />
<br />
Right before glLoadIdentity(), add the following assignment:
<br />
<br />
<br />
<pre> rota += 0.5;
</pre>
<br />
Our old friend Mr. rota has returned. Now we need our other old friend,
glRotatef(). After the glTranslatef(), add the following line:
<br />
<br />
<br />
<pre> glRotatef(rota, 1.0, 1.0, 1.0);
</pre>
<br />
Previously, we’d only used glRotatef() along one axis. Now we’re using it to rotate all three axes at the same time.
<br />
<br />
Now you can hit “Build and Go” and this is what you get:
<br />
<br />
<img alt="5_627_3697b1365347f9a.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_3697b1365347f9a.jpg" />
<br />
<br />
Welcome to 3D objects.
<br />
<br />
What About Texture Mapping It?
<br />
I think we’re a bit past plain coloured objects now aren’t we?
Let’s use the texture from the last tutorial and texture map all six
sides and make it more interesting.
<br />
<br />
OK, we kept our texture in the project and we kept the loading code so
all we need to do is change the drawView method. Now you get to see just
how quick texture mapping is once it’s already there!
<br />
<br />
First of all, remember this from the last tutorial:
<br />
<br />
<br />
<pre> const GLshort squareTextureCoords[] = {
</pre>
<pre> // Front face
</pre>
<pre> 0, 1, // top left
</pre>
<pre> 0, 0, // bottom left
</pre>
<pre> 1, 0, // bottom right
</pre>
<pre> 1, 1, // top right
</pre>
<br />
Well, that was good when all we had to texture map was one face. We need
to expand it. However, it’s easy. Remember above how I was pedantic on
keeping the starting co-ordinates and the order of the co-ordinates the
same when I was defining the cube? Well now you find out why.
<br />
<br />
When OpenGL renders a texture on a face of our cube, because we are
starting everything after the first face at an offset (the 4, 8, 12 etc
from above to glDrawArrays()), the texture mapping side of the rendering
will also go to the same offset when looking up the texture
co-ordinates for the each face. So therefore, in order to texture map 6
faces, all we need to do is to duplicate the above 4 co-ordinates 5
more times to produce the texture co-ordinate array as follows:
<br />
<br />
<br />
<pre> const GLshort squareTextureCoords[] = {
</pre>
<pre> // Front face
</pre>
<pre> 0, 1, // top left
</pre>
<pre> 0, 0, // bottom left
</pre>
<pre> 1, 0, // bottom right
</pre>
<pre> 1, 1, // top right
</pre>
<br />
<br />
<pre> // Top face
</pre>
<pre> 0, 1, // top left
</pre>
<pre> 0, 0, // bottom left
</pre>
<pre> 1, 0, // bottom right
</pre>
<pre> 1, 1, // top right
</pre>
<br />
<br />
<pre> // Rear face
</pre>
<pre> 0, 1, // top left
</pre>
<pre> 0, 0, // bottom left
</pre>
<pre> 1, 0, // bottom right
</pre>
<pre> 1, 1, // top right
</pre>
<br />
<br />
<pre> // Bottom face
</pre>
<pre> 0, 1, // top left
</pre>
<pre> 0, 0, // bottom left
</pre>
<pre> 1, 0, // bottom right
</pre>
<pre> 1, 1, // top right
</pre>
<br />
<br />
<pre> // Left face
</pre>
<pre> 0, 1, // top left
</pre>
<pre> 0, 0, // bottom left
</pre>
<pre> 1, 0, // bottom right
</pre>
<pre> 1, 1, // top right
</pre>
<br />
<br />
<pre> // Right face
</pre>
<pre> 0, 1, // top left
</pre>
<pre> 0, 0, // bottom left
</pre>
<pre> 1, 0, // bottom right
</pre>
<pre> 1, 1, // top right
</pre>
<pre> };
</pre>
<br />
<br />
A bit of cut and paste action and job’s done!
<br />
<br />
Now, all we need is a few lines of drawing code and we’re ready to render our texture mapped cube:
<br />
<br />
Before drawing the first face, add the following code:
<br />
<br />
<br />
<pre> glTexCoordPointer(2, GL_SHORT, 0, squareTextureCoords);
</pre>
<pre> glEnableClientState(GL_TEXTURE_COORD_ARRAY);
</pre>
<br />
It’s that easy. Don’t delete the glColour4f()’s before each drawing
code, just hit “Build and Go” first so you get the following:
<br />
<br />
<img alt="5_627_a8fd75a192deaa1.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_a8fd75a192deaa1.jpg" />
<br />
<br />
Hey presto, a texture mapped 3D object, rotating in full 3D.
<br />
<br />
A Word on Texture Map Images
<br />
I forgot to mention this last tutorial. Whilst I encourage you
experiment with your own textures for texture mapping, just be aware
that the texture sizes must be a power of two. That is the width and
height of the image has to be 1, 2, 4, ... 32, ... 512, 1024... The
width and height don’t have to be equal but they must be a power of 2.
So 32 x 512 is a valid size just the same as 64 x 64 is. But 30 x 30 is
not.
<br />
<br />
A Word on glRotatef() and Your Object’s Vertices
<br />
Again, I encourage you to create your own objects. Have you
noticed that I’ve always had the centre of my triangles, squares and now
cube at 0,0,0? The extremes of each object are equally spaced apart
from 0,0,0? That’s because when it comes to rotation, OpenGL will rotate
quite naturally around the object’s centre as specified in relation to
0,0,0 in the model matrix. It won’t adjust your model to 0,0,0 and then
rotate it. If your object is not centred at 0,0,0 in you’re object
matrix, your object will rotate “lop-sided”.
<br />
<br />
That’s it for Today
<br />
That’s the way today’s tutorial is ending. I hope you’ve found in
beneficial and it’s been as much fun for you as I’ve had creating it.
As always, click on the Email Me link at the bottom if you have any
questions.
<br />
<br />
Actually, in hindsight, I should have switched to anti-clockwise
ordering of the vertices now we’re in 3D objects. Never mind, I’ll go
into detail on using anti-clockwise versus clockwise in the next
tutorial and we’ll create a cube going anti-clockwise.
<br />
<br />
Here’s the code for this tutorial:
<br />
<br />
AppleCoder-OpenGLES-06.zip
<br />
<br />
The home of the tutorials is in the “Tutorials” section of the iphonedevsdk.com forums. Check out the thread there.
<br />
<br />
Until next time, hooroo!
<br />
Simon Maurice
<br />
<br />
<br />
Copyright 2009 Simon Maurice. All Rights Reserved.
<br />
The code provided in these pages are for educational purposes
only and may not be used for commercial purposes without Simon Maurice’s
expressed permission in writing. Information contained within this site
cannot be duplicated in any form without Simon Maurice’s expressed
permission in writing; this includes, but is not limited to, publishing
in printed format, reproduced on web pages, or other forms of electronic
distribution.
<br />
<br />
Linking to these pages on other websites is permitted.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-3765795590291919148.post-43591521706357877172012-01-31T05:04:00.000-08:002012-01-31T05:04:08.859-08:00OpenGL ES 07 - Translating Objects Independently<script type="text/javascript">
<!--
google_ad_client = "ca-pub-9566972421287894";
/* 728x15 LINK AD-1 */
google_ad_slot = "1092558380";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript">
</script>
<br />
<h1 class="firstHeading" style="color: #990000;">
OpenGL ES 07 - Translating Objects Independently</h1>
<div style="color: #999999;">
<span style="color: #990000;"><b><span style="color: #cc0000;">Disclaimer:</span></b> <b><span style="color: #444444;">This
post and the whole blog is just a copy of
iPhone-OpenGL-ES-tutorial-Series (seems to have )written by Simon
Maurice. This blog does NOT have a written permission or any kind of
permission from the original author (Simon Maurice). Please use it at
your own risk. If you have any complaints regarding any content of this
blog, please let me know I will remove it right away. Just hoping that
it would be helpful for people who are trying to learn OpenGL. Happy
coding!</span></b></span> </div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<b>Copyright 2009 Simon Maurice. All Rights Reserved.
</b></div>
<div style="color: #999999;">
The code provided in these pages are for educational purposes only and
may not be used for commercial purposes without Simon Maurice’s
expressed permission in writing. Information contained within this site
cannot be duplicated in any form without Simon Maurice’s expressed
permission in writing; this includes, but is not limited to, publishing
in printed format, reproduced on web pages, or other forms of electronic
distribution.
</div>
<br />
<a href="https://github.com/mauriceatron" target="_blank">Source codes can be found here </a><br />
<div style="color: #666666;">
<b><br /></b><br />
<b><br /></b></div>
<b><span style="color: #666666;">Original tutorial starts from here onwards..</span></b><br />
<br />
<br />
Originally, I thought I would head into lighting next but then I had
another thought. We still haven’t completed much of the basics with
objects and transformations, particularly if we want to transform
(animate) two different objects differently within our worldspace.
<br />
<br />
Remember how we’ve just applied glTranslatef() and glRotatef() to the
whole scene? If we wanted to do something differently, I used a
convenience function, glLoadIdentity(). However, glLoadIdentity is
“expensive” in terms of computational power and scene rendering so now,
I’ll introduce a more efficient method.
<br />
<br />
To do this, I am going to introduce a new object as well. We are going
to add a pyramid, then move it with glTranslatef(), and rotate it with
glRotatef() independently to the cube without making another call to
glLoadIdentity() to reset our verticies.
<br />
<br />
Adding a Pyramid to Our World
<br />
Rather than just add a triangle, let’s stick with 3D objects and
we’ll add a pyramid (just think of the big stone things on the plains of
Giza).
<br />
<br />
One thing I avoided saying in the last tutorial was was that the cube we
created was a complex object; i.e. an object made up of more than one
primitive. Technically speaking, our square was also a complex object
but since we drew it with a single OpenGL function call, we can treat it
as a simple object. I avoided calling it a complex object so as not to
make it sound like “hard work”.
<br />
<br />
Now that you’ve done one complex object, congratulations! However, it’s time to create your second!
<br />
<br />
Pyramids are not hard to do. They are quite simply made up of a square
for the base attached to four triangles which meet at the centre of the
square above it. Once you break down in your mind the shape you wish to
create into more simple objects, you’ll have no problem creating
whatever you want. The only thing which changes is the quantity of
primitives which go into creating your object.
<br />
<br />
So, fire up Xcode and open the project from the last tutorial. Nothing
to delete this time, we’re adding code and changing a couple of lines of
code.
<br />
<br />
In order to help you with the pyramid, I’m going to break it down into it’s individual components, let’s start with the base:
<br />
<br />
<br />
<pre> const GLfloat pyramidVertices[] = {
</pre>
<pre> // Our pyramid consists of 4 triangles and a square base.
</pre>
<pre> // We'll start with the square base
</pre>
<pre> -1.0, -1.0, 1.0, // front left of base
</pre>
<pre> 1.0, -1.0, 1.0, // front right of base
</pre>
<pre> 1.0, -1.0, -1.0, // rear left of base
</pre>
<pre> -1.0, -1.0, -1.0, // rear right of base
</pre>
<br />
So, we’ve just started creating a new object as we have done in the
past. The square is the base so all vertices are set at the same Y
co-ordinate of -1.0.
<br />
<br />
Now we can create the front face of the pyramid, this time we are creating a triangle instead of a square:
<br />
<br />
<br />
<pre> // Front face
</pre>
<pre> -1.0, -1.0, 1.0, // bottom left of triangle
</pre>
<pre> 1.0, -1.0, 1.0, // bottom right
</pre>
<pre> 0.0, 1.0, 0.0, // top centre -- all triangle vertices
</pre>
<pre> // will meet here
</pre>
<br />
The only real difference to the triangles we have created in the past is
that this one is set on an angle, tilting backwards toward the centre
of the square (i.e. rotated around the X axis).
<br />
<br />
Then we can continue specifying our other three triangular faces just in
the same manner. Here’s the resulting full declaration of the pyramid:
<br />
<br />
// Our new object definition code goes here
<br />
<pre> const GLfloat pyramidVertices[] = {
</pre>
<pre> // Our pyramid consists of 4 triangles and a square base.
</pre>
<pre> // We'll start with the square base
</pre>
<pre> -1.0, -1.0, 1.0, // front left of base
</pre>
<pre> 1.0, -1.0, 1.0, // front right of base
</pre>
<pre> 1.0, -1.0, -1.0, // rear left of base
</pre>
<pre> -1.0, -1.0, -1.0, // rear right of base
</pre>
<br />
<br />
<pre> // Front face
</pre>
<pre> -1.0, -1.0, 1.0, // bottom left of triangle
</pre>
<pre> 1.0, -1.0, 1.0, // bottom right
</pre>
<pre> 0.0, 1.0, 0.0, // top centre -- all triangle vertices
</pre>
<pre> // will meet here
</pre>
<br />
<br />
<pre> // Rear face
</pre>
<pre> 1.0, -1.0, -1.0, // bottom right (when viewed through front face)
</pre>
<pre> -1.0, -1.0, -1.0, // bottom left
</pre>
<pre> 0.0, 1.0, 0.0, // top centre
</pre>
<br />
<br />
<pre> // left face
</pre>
<pre> -1.0, -1.0, -1.0, // bottom rear
</pre>
<pre> -1.0, -1.0, 1.0, // bottom front
</pre>
<pre> 0.0, 1.0, 0.0, // top centre
</pre>
<br />
<br />
<pre> // right face
</pre>
<pre> 1.0, -1.0, 1.0, // bottom front
</pre>
<pre> 1.0, -1.0, -1.0, // bottom rear
</pre>
<pre> 0.0, 1.0, 0.0 // top centre
</pre>
<pre> };
</pre>
<br />
Add the above pyramid definition to the drawView method at the same spot as the cubeVerticies[] definition.
<br />
<br />
Just before we move on, I do want to make some points about the pyramid definition.
<br />
<br />
Firstly, this is the first object we are going to render made up of
squares and triangles. The base of our pyramid is a square and the four
sides are triangles. Since we are calling glDrawArrays() independently
for each primitive, it doesn’t matter that we have different primitives
in a single definition. All will become clear when we draw the pyramid
below.
<br />
<br />
Secondly, note that once again, I have specified all vertices in an
anti-clockwise direction. Even though the rear face “appears” to be
specified in a clockwise direction when viewed “through” the front face,
from OpenGL’s perspective, it is written from an anti-clockwise
direction.
<br />
<br />
Actually, I think I will introduce a 3D graphics term at the end of this tutorial to discuss this further.
<br />
<br />
Drawing the Pyramid
<br />
Now head down to the drawing code for the cube. Firstly, delete the glLoadIdentity() line, that’s no longer required.
<br />
<br />
After the assignment to rota += 0.5, we’re going to add the code for
drawing the pyramid. Now, what we want to do is to move the pyramid
using glTranslatef() and then rotate it using glRotatef() without
affecting the cube. In other words, we need to be able to call
glTranslatef() and glRotatef() without affecting the translation and
drawing operations performed on any other object drawn after it.
<br />
<br />
OpenGL provides us with a convenient way to do this using the following function pair:
<br />
<br />
<br />
<pre> glPushMatrix();
</pre>
<br />
<br />
<pre> // Translation and drawing code goes here....
</pre>
<br />
<br />
<pre> glPopMatrix();
</pre>
<br />
What OpenGL is doing for us is allowing us to use a stack which our
matrices are copied onto with the call to glPushMatrix(). I know I’ve
avoided most 3D terms but just think of our pyramidVertices[] and
cubeVertices[] objects as the matrices we are “pushing” onto OpenGL’s
stack.
<br />
<br />
Note: If you’re unsure what I mean by a stack, check this out or, better
yet, you really need to get yourself a good C or Objective C
programming book.
<br />
<br />
So, with our data safely copied and out of the way, we can transform
away like the OpenGL gurus we are! Let’s start drawing the the pyramid:
<br />
<br />
<br />
<pre> glPushMatrix();
</pre>
<pre> {
</pre>
<pre> glTranslatef(-2.0, 0.0, -8.0);
</pre>
<pre> glRotatef(rota, 1.0, 0.0, 0.0);
</pre>
<pre> glVertexPointer(3, GL_FLOAT, 0, pyramidVertices);
</pre>
<pre> glEnableClientState(GL_VERTEX_ARRAY);
</pre>
<br />
First thing to note is the curly bracket after the glPushMatrix(). This
is not required at all but as you’re learning this, it just makes it
clear where the matrices are pushed and then popped off the stack.
<br />
<br />
The first four lines of code after the glPushMatrix() call should be
self explanatory to you by now. All we’re doing is calling
glTranslatef() to move the pyramid away from (0, 0, 0) to the left and
back 8 points into the screen (further away from the viewer). Then we
are rotating the pyramid around the X axis only, rather than all three
axes as per the cube example in the last tutorial. Finally, we just tell
OpenGL about the data and enables it to be used.
<br />
<br />
Now, after we’ve done our transformations, we can start to draw the pyramid:
<br />
<br />
<br />
<pre> // Draw the pyramid
</pre>
<pre> // Draw the base -- it's a square remember
</pre>
<pre> glColor4f(1.0, 0.0, 0.0, 1.0);
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
</pre>
<br />
The first four co-ordinates of our pyramid declaration above contains
the square base. So, we will colour it in red (it will still be texture
mapped as the code for texture mapping is already there - see the full
implementation of drawView below), and draw a square using the
GL_TRIANGLE_FAN methodology. We 3-co-ordinate vertex at vertex 0 in the
array (pyramidVerticies[0~3]), and use 4 vertices.
<br />
<br />
Ok, that’s the square done, let’s now deal with the first triangle:
<br />
<br />
<br />
<pre> // Front Face
</pre>
<pre> glColor4f(0.0, 1.0, 0.0, 1.0);
</pre>
<pre> glDrawArrays(GL_TRIANGLES, 4, 3);
</pre>
<br />
Apart from the change in colour, we are changing the drawing methodology
being GL_TRIANGLES for obvious reasons, we’re starting at array element
4 (pyramidVerticies[4~6]), and then proceeding to draw three vertices.
So you can see that a square and triangles can easily co-exist in the
one data structure.
<br />
<br />
Next, we can just continue drawing our remaining three triangles:
<br />
<br />
<br />
<pre> // Rear Face
</pre>
<pre> glColor4f(0.0, 0.0, 1.0, 1.0);
</pre>
<pre> glDrawArrays(GL_TRIANGLES, 7, 3);
</pre>
<br />
<br />
<pre> // Right Face
</pre>
<pre> glColor4f(1.0, 1.0, 0.0, 1.0);
</pre>
<pre> glDrawArrays(GL_TRIANGLES, 10, 3);
</pre>
<br />
<br />
<pre> // Left Face
</pre>
<pre> glColor4f(1.0, 0.0, 1.0, 1.0);
</pre>
<pre> glDrawArrays(GL_TRIANGLES, 13, 3);
</pre>
<pre> }
</pre>
<pre> glPopMatrix();
</pre>
<br />
Each time, we’re just changing the colour, and changing the starting
offset. If you recall, OpenGL knows that each vertex has 3 co-ordinates
(X, Y, Z) as we passed that to glVertexPointer() above.
<br />
<br />
Finally, we close the curly brackets and call glPopMatrix().
<br />
<br />
Now, at this point we can draw the cube. However, note that you can
repeat drawing the same object over and over again just by surrounding
your transformations around glPushMatrix() and glPopMatrix().
<br />
<br />
Drawing the Cube - Revised
<br />
Here is the full drawing code for the cube. The most obvious
change is the surrounding of the code with a glPushMatrix() and
glPopMatrix() function calls:
<br />
<br />
<br />
<pre> glPushMatrix();
</pre>
<pre> {
</pre>
<pre> glTranslatef(2.0, 0.0, -8.0);
</pre>
<pre> glRotatef(rota, 1.0, 1.0, 1.0);
</pre>
<pre> glVertexPointer(3, GL_FLOAT, 0, cubeVertices);
</pre>
<pre> glEnableClientState(GL_VERTEX_ARRAY);
</pre>
<br />
<br />
<pre> // Draw the front face in Red
</pre>
<pre> glColor4f(1.0, 0.0, 0.0, 1.0);
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
</pre>
<br />
<br />
<pre> // Draw the top face in green
</pre>
<pre> glColor4f(0.0, 1.0, 0.0, 1.0);
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 4, 4);
</pre>
<br />
<br />
<pre> // Draw the rear face in Blue
</pre>
<pre> glColor4f(0.0, 0.0, 1.0, 1.0);
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 8, 4);
</pre>
<br />
<br />
<pre> // Draw the bottom face
</pre>
<pre> glColor4f(1.0, 1.0, 0.0, 1.0);
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 12, 4);
</pre>
<br />
<br />
<pre> // Draw the left face
</pre>
<pre> glColor4f(0.0, 1.0, 1.0, 1.0);
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 16, 4);
</pre>
<br />
<br />
<pre> // Draw the right face
</pre>
<pre> glColor4f(1.0, 0.0, 1.0, 1.0);
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 20, 4);
</pre>
<pre> }
</pre>
<pre> glPopMatrix();
</pre>
<br />
The only other change is the information passed to glTranslatef(),
moving our cube off the centre of our display, 2.0 units to the right
and back into the screen 8.0 units.
<br />
<br />
Just for completeness, here is the full drawView method code (below is a few notes):
<br />
<br />
- (void)drawView {
<br />
<br />
<br />
<pre> // Our new object definition code goes here
</pre>
<pre> const GLfloat pyramidVertices[] = {
</pre>
<pre> // Our pyramid consists of 4 triangles and a square base.
</pre>
<pre> // We'll start with the square base
</pre>
<pre> -1.0, -1.0, 1.0, // front left of base
</pre>
<pre> 1.0, -1.0, 1.0, // front right of base
</pre>
<pre> 1.0, -1.0, -1.0, // rear right of base
</pre>
<pre> -1.0, -1.0, -1.0, // rear left of base
</pre>
<br />
<br />
<pre> // Front face
</pre>
<pre> -1.0, -1.0, 1.0, // bottom left of triangle
</pre>
<pre> 1.0, -1.0, 1.0, // bottom right
</pre>
<pre> 0.0, 1.0, 0.0, // top centre -- all triangle vertices
</pre>
<pre> // will meet here
</pre>
<br />
<br />
<pre> // Rear face
</pre>
<pre> 1.0, -1.0, -1.0, // bottom right (when viewed through front face)
</pre>
<pre> -1.0, -1.0, -1.0, // bottom left
</pre>
<pre> 0.0, 1.0, 0.0, // top centre
</pre>
<br />
<br />
<pre> // left face
</pre>
<pre> -1.0, -1.0, -1.0, // bottom rear
</pre>
<pre> -1.0, -1.0, 1.0, // bottom front
</pre>
<pre> 0.0, 1.0, 0.0, // top centre
</pre>
<br />
<br />
<pre> // right face
</pre>
<pre> 1.0, -1.0, 1.0, // bottom front
</pre>
<pre> 1.0, -1.0, -1.0, // bottom rear
</pre>
<pre> 0.0, 1.0, 0.0 // top centre
</pre>
<pre> };
</pre>
<br />
<br />
<pre> const GLfloat cubeVertices[] = {
</pre>
<br />
<br />
<pre> // Define the front face
</pre>
<pre> -1.0, 1.0, 1.0, // top left
</pre>
<pre> -1.0, -1.0, 1.0, // bottom left
</pre>
<pre> 1.0, -1.0, 1.0, // bottom right
</pre>
<pre> 1.0, 1.0, 1.0, // top right
</pre>
<br />
<br />
<pre> // Top face
</pre>
<pre> -1.0, 1.0, -1.0, // top left (at rear)
</pre>
<pre> -1.0, 1.0, 1.0, // bottom left (at front)
</pre>
<pre> 1.0, 1.0, 1.0, // bottom right (at front)
</pre>
<pre> 1.0, 1.0, -1.0, // top right (at rear)
</pre>
<br />
<br />
<pre> // Rear face
</pre>
<pre> 1.0, 1.0, -1.0, // top right (when viewed from front)
</pre>
<pre> 1.0, -1.0, -1.0, // bottom right
</pre>
<pre> -1.0, -1.0, -1.0, // bottom left
</pre>
<pre> -1.0, 1.0, -1.0, // top left
</pre>
<br />
<br />
<pre> // bottom face
</pre>
<pre> -1.0, -1.0, 1.0,
</pre>
<pre> -1.0, -1.0, -1.0,
</pre>
<pre> 1.0, -1.0, -1.0,
</pre>
<pre> 1.0, -1.0, 1.0,
</pre>
<br />
<br />
<pre> // left face
</pre>
<pre> -1.0, 1.0, -1.0,
</pre>
<pre> -1.0, 1.0, 1.0,
</pre>
<pre> -1.0, -1.0, 1.0,
</pre>
<pre> -1.0, -1.0, -1.0,
</pre>
<br />
<br />
<pre> // right face
</pre>
<pre> 1.0, 1.0, 1.0,
</pre>
<pre> 1.0, 1.0, -1.0,
</pre>
<pre> 1.0, -1.0, -1.0,
</pre>
<pre> 1.0, -1.0, 1.0
</pre>
<pre> };
</pre>
<br />
<br />
<pre> const GLshort squareTextureCoords[] = {
</pre>
<pre> // Front face
</pre>
<pre> 0, 1, // top left
</pre>
<pre> 0, 0, // bottom left
</pre>
<pre> 1, 0, // bottom right
</pre>
<pre> 1, 1, // top right
</pre>
<br />
<br />
<pre> // Top face
</pre>
<pre> 0, 1, // top left
</pre>
<pre> 0, 0, // bottom left
</pre>
<pre> 1, 0, // bottom right
</pre>
<pre> 1, 1, // top right
</pre>
<br />
<br />
<pre> // Rear face
</pre>
<pre> 0, 1, // top left
</pre>
<pre> 0, 0, // bottom left
</pre>
<pre> 1, 0, // bottom right
</pre>
<pre> 1, 1, // top right
</pre>
<br />
<br />
<pre> // Bottom face
</pre>
<pre> 0, 1, // top left
</pre>
<pre> 0, 0, // bottom left
</pre>
<pre> 1, 0, // bottom right
</pre>
<pre> 1, 1, // top right
</pre>
<br />
<br />
<pre> // Left face
</pre>
<pre> 0, 1, // top left
</pre>
<pre> 0, 0, // bottom left
</pre>
<pre> 1, 0, // bottom right
</pre>
<pre> 1, 1, // top right
</pre>
<br />
<br />
<pre> // Right face
</pre>
<pre> 0, 1, // top left
</pre>
<pre> 0, 0, // bottom left
</pre>
<pre> 1, 0, // bottom right
</pre>
<pre> 1, 1, // top right
</pre>
<pre> };
</pre>
<br />
<br />
<pre> [EAGLContext setCurrentContext:context];
</pre>
<pre> glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
</pre>
<pre> glViewport(0, 0, backingWidth, backingHeight);
</pre>
<pre> glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
</pre>
<pre> glMatrixMode(GL_MODELVIEW);
</pre>
<br />
<br />
<pre> glTexCoordPointer(2, GL_SHORT, 0, squareTextureCoords);
</pre>
<pre> glEnableClientState(GL_TEXTURE_COORD_ARRAY);
</pre>
<br />
<br />
<pre> // Our new drawing code goes here
</pre>
<pre> rota += 0.5;
</pre>
<br />
<br />
<pre> glPushMatrix();
</pre>
<pre> {
</pre>
<pre> glTranslatef(-2.0, 0.0, -8.0);
</pre>
<pre> glRotatef(rota, 1.0, 0.0, 0.0);
</pre>
<pre> glVertexPointer(3, GL_FLOAT, 0, pyramidVertices);
</pre>
<pre> glEnableClientState(GL_VERTEX_ARRAY);
</pre>
<br />
<br />
<pre> // Draw the pyramid
</pre>
<pre> // Draw the base -- it's a square remember
</pre>
<pre> glColor4f(1.0, 0.0, 0.0, 1.0);
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
</pre>
<br />
<br />
<pre> // Front Face
</pre>
<pre> glColor4f(0.0, 1.0, 0.0, 1.0);
</pre>
<pre> glDrawArrays(GL_TRIANGLES, 4, 3);
</pre>
<br />
<br />
<pre> // Rear Face
</pre>
<pre> glColor4f(0.0, 0.0, 1.0, 1.0);
</pre>
<pre> glDrawArrays(GL_TRIANGLES, 7, 3);
</pre>
<br />
<br />
<pre> // Left Face
</pre>
<pre> glColor4f(1.0, 1.0, 0.0, 1.0);
</pre>
<pre> glDrawArrays(GL_TRIANGLES, 10, 3);
</pre>
<br />
<br />
<pre> // Right Face
</pre>
<pre> glColor4f(1.0, 0.0, 1.0, 1.0);
</pre>
<pre> glDrawArrays(GL_TRIANGLES, 13, 3);
</pre>
<pre> }
</pre>
<pre> glPopMatrix();
</pre>
<br />
<br />
<pre> glPushMatrix();
</pre>
<pre> {
</pre>
<pre> glTranslatef(2.0, 0.0, -8.0);
</pre>
<pre> glRotatef(rota, 1.0, 1.0, 1.0);
</pre>
<pre> glVertexPointer(3, GL_FLOAT, 0, cubeVertices);
</pre>
<pre> glEnableClientState(GL_VERTEX_ARRAY);
</pre>
<br />
<br />
<pre> // Draw the front face in Red
</pre>
<pre> glColor4f(1.0, 0.0, 0.0, 1.0);
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
</pre>
<br />
<br />
<pre> // Draw the top face in green
</pre>
<pre> glColor4f(0.0, 1.0, 0.0, 1.0);
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 4, 4);
</pre>
<br />
<br />
<pre> // Draw the rear face in Blue
</pre>
<pre> glColor4f(0.0, 0.0, 1.0, 1.0);
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 8, 4);
</pre>
<br />
<br />
<pre> // Draw the bottom face
</pre>
<pre> glColor4f(1.0, 1.0, 0.0, 1.0);
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 12, 4);
</pre>
<br />
<br />
<pre> // Draw the left face
</pre>
<pre> glColor4f(0.0, 1.0, 1.0, 1.0);
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 16, 4);
</pre>
<br />
<br />
<pre> // Draw the right face
</pre>
<pre> glColor4f(1.0, 0.0, 1.0, 1.0);
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 20, 4);
</pre>
<pre> }
</pre>
<pre> glPopMatrix();
</pre>
<br />
<br />
<br />
<br />
<pre> glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
</pre>
<pre> [context presentRenderbuffer:GL_RENDERBUFFER_OES];
</pre>
<br />
<br />
<pre> [self checkGLError:NO];
</pre>
}
<br />
<br />
Note that we have not added a new texture co-ordinate array for the
pyramid? We are just going to use the same co-ordinate array because it
will just work for this demonstration.
<br />
<br />
OK, make the changes to your project (only need to change drawView) and
hit “Build & Go”. You should get the following on screen:
<br />
<br />
<img alt="5_627_c74a912ffecdf5d.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_c74a912ffecdf5d.jpg" />
<br />
<br />
All you need to notice is that the two objects are now being transformed
independently of each other. The pyramid is being rotated around the X
axis only while the cube continues to rotate around all three axes. Once
again, the colours only make it easier for you to identify each face.
<br />
<br />
Introducing a 3D Concept: Normals
<br />
Remember earlier I was making the note about the co-ordinates for
each vertex (point) of the rear triangle being specified in an
anti-clockwise direction despite it “appearing” when viewed from the
front that they were specified in a clockwise direction?
<br />
<br />
Well, that’s because you should always specify your data in reference to
the face’s “normal”. Simply put, the normal of an object’s face is an
imaginary line drawn out perpendicular to the object’s face. To
illustrate, have a look at the following image:
<br />
<br />
<img alt="5_627_0e5123e48569f37.png" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_0e5123e48569f37.png" />
<br />
<br />
In the above image, the triangle’s normal is represented by the arrow
coming out of the face. Now that the triangle’s normal has been
represented, you can now see that the 3 vertices are specified in an
anti-clockwise direction. We will use normals extensively when I cover
lighting so I do want you to be aware of what a normal is now.
<br />
<br />
That’s It for this Topic
<br />
As usual, here’s the source code for this tutorial.
<br />
<br />
AppleCoder-OpenGLES-07.zip
<br />
<br />
I had been planning to cover lighting next but I might actually create a
3D world and show you how to move through it. Then I think I can use
that 3D world to add lighting. Let me sleep on it and we’ll see what
comes up next.
<br />
<br />
The home of the tutorials is in the “Tutorials” section of the iphonedevsdk.com forums. Check out the thread there.
<br />
<br />
Until then, hooroo!
<br />
Simon Maurice
<br />
<br />
<br />
<br />
<br />
<br />
Copyright 2009 Simon Maurice. All Rights Reserved.
<br />
The code provided in these pages are for educational purposes
only and may not be used for commercial purposes without Simon Maurice’s
expressed permission in writing. Information contained within this site
cannot be duplicated in any form without Simon Maurice’s expressed
permission in writing; this includes, but is not limited to, publishing
in printed format, reproduced on web pages, or other forms of electronic
distribution.
<br />
<br />
Linking to these pages on other websites is permitted.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-3765795590291919148.post-53259503375101714562012-01-31T05:02:00.001-08:002012-01-31T05:02:54.744-08:00OpenGL ES 08 - The Final Primitives: Points and Lines in a Stride<script type="text/javascript">
<!--
google_ad_client = "ca-pub-9566972421287894";
/* 728x15 LINK AD-1 */
google_ad_slot = "1092558380";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript">
</script>
<br />
<h1 class="firstHeading" style="color: #990000;">
OpenGL ES 08 - The Final Primitives: Points and Lines in a Stride</h1>
<div style="color: #999999;">
<span style="color: #990000;"><b><span style="color: #cc0000;">Disclaimer:</span></b> <b><span style="color: #444444;">This
post and the whole blog is just a copy of
iPhone-OpenGL-ES-tutorial-Series (seems to have )written by Simon
Maurice. This blog does NOT have a written permission or any kind of
permission from the original author (Simon Maurice). Please use it at
your own risk. If you have any complaints regarding any content of this
blog, please let me know I will remove it right away. Just hoping that
it would be helpful for people who are trying to learn OpenGL. Happy
coding!</span></b></span> </div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<b>Copyright 2009 Simon Maurice. All Rights Reserved.
</b></div>
<div style="color: #999999;">
The code provided in these pages are for educational purposes only and
may not be used for commercial purposes without Simon Maurice’s
expressed permission in writing. Information contained within this site
cannot be duplicated in any form without Simon Maurice’s expressed
permission in writing; this includes, but is not limited to, publishing
in printed format, reproduced on web pages, or other forms of electronic
distribution.
</div>
<br />
<a href="https://github.com/mauriceatron" target="_blank">Source codes can be found here </a><br />
<div style="color: #666666;">
<b><br /></b><br />
<b><br /></b></div>
<b><span style="color: #666666;">Original tutorial starts from here onwards..</span></b><br />
<br />
OK, let’s get a real quick tutorial out of the way. The final
primitives which we have not covered is points and lines. They’re so
easy but, after answering a few questions on them already, I thought I’d
better cover them.
<br />
<br />
I am going to go through these two at velocity because I think once
they’re introduced, you’ll pretty much think: “that makes sense” and be
ready to move on. That’s pretty much the goal.
<br />
<br />
However, don’t skim this too quickly. I’m going to use that cryptic
“stride” parameter in the glVertexPointer() call. I’m going to cover
this now as I am writing a tutorial to use Blender objects in our iPhone
code so we can get a stupidly large quantity of objects on the screen
without too much work.
<br />
<br />
Stride is worth covering as most people simply skim over it (as I have
done so far) or state things like “it’s a really advanced topic and
you’re not ready yet” (probably because they haven’t got it to work
yet...). It’s got a real gotcha but once you’re aware of it, it’s easy.
<br />
<br />
Points
<br />
In OpenGL ES, a point is a dot in 3D space. It will be rendered
as a circle if you have enabled GL_POINT_SMOOTH, otherwise will be
rendered as a square.
<br />
<br />
Now you could actually render a quick and dirty point using two triangles rather than using a point but I don’t recommend it.
<br />
<br />
In fact, I’ve just finished a discussion with someone who stated that he
always drew his points using two triangles, after all, “that is the way
the hardware implements it”. While I won’t say that this approach is
specifically wrong, but really isn’t right either.
<br />
<br />
The argument was that the underlying hardware is implementing the point
drawing using 2 triangles to form a square (true in many cases) so by
sending the hardware GL point data, it’s adding to the hardware’s
workload. However that ignores such basic questions such as: What is
more efficient on the hardware: GL_TRIANGLE_STRIP or GL_TRIANGLE_FAN?
<br />
<br />
In short, don’t second guess the hardware. In our case on the iPhone, we
could write our code to maximise the throughput of the OpenGL ES chip,
but we probably don’t need to.
<br />
<br />
Anyway, let’s get on with drawing a point. We’re going to render four
points on the display, in four different colours. To define a point, all
you need is an X, Y, and Z co-ordinate, and a size.
<br />
<br />
First, download our basic project: AppleCoder-OpenGLES-00.zip
<br />
<br />
To draw four points, we just specify the point co-ordinates in a vertex
array, and our colours in a colour array. So, in the drawView[] method,
we need to add the following structure:
<br />
<br />
<br />
<pre> const GLfloat points[] = {
</pre>
<pre> 1.0, 1.0, -6.0, 1.0, 0.0, 0.0, 1.0, // First point, Red
</pre>
<pre> 1.0, -1.0, -6.0, 0.0, 1.0, 0.0, 1.0, // Second point, Green
</pre>
<pre> -1.0, -1.0, -6.0, 0.0, 0.0, 1.0, 1.0, // Third point, Blue
</pre>
<pre> -1.0, 1.0, -6.0, 1.0, 1.0, 0.0, 1.0 // Fourth point, Yellow
</pre>
<pre> };
</pre>
<br />
I hope you can see straight away that all I’ve done is to combine the
vertex array (our co-ordinates) and our colour array (RGBA values) into a
single array. No real great dramas?
<br />
<br />
The drawing code will look almost the same and there’s only one line I
will go into detail about. There are a couple of steps which we want to
follow in order to draw our points but all will be familiar except for
setting the point size.
<br />
<br />
Here’s the drawing code for these four points:
<br />
<br />
<br />
<pre> glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
</pre>
<br />
<br />
<pre> glPointSize(50.0);
</pre>
<pre> glVertexPointer(3, GL_FLOAT, 28, points);
</pre>
<pre> glColorPointer(4, GL_FLOAT, 28, &points[3]);
</pre>
<pre> glEnableClientState(GL_COLOR_ARRAY);
</pre>
<pre> glEnableClientState(GL_VERTEX_ARRAY);
</pre>
<pre> glDrawArrays(GL_POINTS, 0, 4);
</pre>
<br />
glPointSize() sets the size of the point (obviously). However, we don’t
definitely need this as OpenGL ES has a default point size which it will
render if you don’t set it. I’ve just chosen a nice large value.
<br />
<br />
The next two lines are a departure from what we normally do:
<br />
<br />
<br />
<pre> glVertexPointer(3, GL_FLOAT, 28, points);
</pre>
<pre> glColorPointer(4, GL_FLOAT, 28, &points[3]);
</pre>
<br />
Now, I have covered these before but the third parameter, stride, has
always been zero, and the pointer to the data has always just referred
to the arrayName[0] element. Now, since we’ve stored our colour data in
our vertex array, we need to tell OpenGL to ignore the colours for
locating each point, and also ignore each point’s location when looking
up the colour.
<br />
<br />
That’s where stride comes into play (parameter 3 is stride). From the man page, the value for stride is defined as:
<br />
<br />
Specifies the byte offset between consecutive vertices. If stride is 0,
the vertices are understood to be tightly packed in the array. The
initial value is 0.
<br />
<br />
This is the gotcha I mentioned earlier and the single most problem caused by people using stride. Read this carefully:
<br />
<br />
The stride is the offset between consecutive vertices, not the padding between each co-ordinate!
<br />
<br />
So, if you were to look at the result of: sizeof(GLfloat), you’d see
that it’s 4 bytes. Therefore, with 3 GLfloat’s for each vertex, and 4
GLfloat’s for each colour, each point’s definition (location and colour)
occupies 28 bytes. That is our stride value!
<br />
<br />
So, it’s the difference between points[0] and points[7]. Can you see
that points[7] represents the start of the next vertex co-ordinates? See
also that it is stored 28 bytes further into the array from the
previous vertex start?
<br />
<br />
That’s stride, in a nutshell. You’re going to feel like a guru answering
a million questions of people who can’t seem to get this working.
<br />
<br />
Finally, we need to tell OpenGL that the colour array begins at:
points[3] which is the first value for the RGBA colour (OpenGL colour’s
processor cannot second guess your structure so you can’t just point it
at points[0] expect it to work things out with the vertex processor to
know where to start). Stride remains the same.
<br />
<br />
OK, so hit Build and Go and you’ll get the following in the simulator:
<br />
<br />
<img alt="5_627_c69779fd65f02a3.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_c69779fd65f02a3.jpg" />
<br />
<br />
There we have it, four 50.0 sized points which haven’t been smoothed, so
they’re squares. What to make them round? It’s easy; just one line of
code:
<br />
<br />
<br />
<pre> glEnable(GL_POINT_SMOOTH);
</pre>
<br />
Add this line right before glPointSize(). Run it again and you’ll have smoothed (round) points:
<br />
<br />
<img alt="5_627_0df7c93002e4779.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_0df7c93002e4779.jpg" />
<br />
<br />
Have a play around with the point sizes, change the colours, and
generally experiment with these. To start you off, put the following
line:
<br />
<br />
<br />
<pre> glRotatef(0.5, 0.0, 0.0, 1.0);
</pre>
<br />
right before the glEnable(GL_POINT_SMOOTH) we just added. Then start to
use glTranslatef() to change their Z location. Keep experimenting until
you know this cold.
<br />
<br />
Lines
<br />
Lines are just as easy as anything else we’ve covered: it’s only
mysterious until you’ve been shown the way. Let’s start by adding the
following code:
<br />
<br />
<br />
<pre> const GLfloat lines[] = {
</pre>
<pre> 2.0, 2.0, -6.0, 1.0, 1.0, 0.0, 1.0, // Starting point in yellow
</pre>
<pre> 2.0, -2.0, -6.0, 0.0, 1.0, 1.0, 1.0, // second point in aqua
</pre>
<pre> -2.0, -2.0, -6.0, 1.0, 0.0, 0.0, 1.0, // third point in red
</pre>
<pre> -2.0, 2.0, -6.0, 0.0, 0.0, 1.0, 1.0 // fourth point in blue
</pre>
<pre> };
</pre>
<br />
All straight forward after the rendering the points above.
<br />
<br />
Now, to draw the lines, let’s have a look at the code (from drawView[]):
<br />
<br />
<br />
<pre> // Setup and render the points
</pre>
<pre> glEnable(GL_POINT_SMOOTH);
</pre>
<pre> glPointSize(50.0);
</pre>
<pre> glVertexPointer(3, GL_FLOAT, 28, points);
</pre>
<pre> glColorPointer(4, GL_FLOAT, 28, &points[3]);
</pre>
<pre> glEnableClientState(GL_COLOR_ARRAY);
</pre>
<pre> glEnableClientState(GL_VERTEX_ARRAY);
</pre>
<pre> glDrawArrays(GL_POINTS, 0, 4);
</pre>
<br />
<br />
<pre> // Setup and render the lines
</pre>
<pre> glVertexPointer(3, GL_FLOAT, 28, lines);
</pre>
<pre> glColorPointer(4, GL_FLOAT, 28, &lines[3]);
</pre>
<pre> glDrawArrays(GL_LINES, 0, 4);
</pre>
<br />
After setting up and drawing the points, you can see that all we need
to do is to tell OpenGL about the new array for the lines’ vertices and
colours, then tell OpenGL to draw it with a call to glDrawArrays().
<br />
<br />
Hit Build and Go and you’ll get the following:
<br />
<br />
<img alt="5_627_b289692359f488e.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_b289692359f488e.jpg" />
<br />
<br />
Seems pretty straight forward doesn’t it? 4 points in the array so two
lines, each with a starting and ending location. Yes, well, that’s
exactly what we’ve got. However, I can’t get away with leaving it there.
Have a look at the glDrawArrays() first parameter. It’s simply
GL_LINES. Like drawing triangles, there are a couple of methodologies
for drawing lines.
<br />
<br />
Here they are described.
<br />
<br />
GL_LINES - Each vertex pair is used to make a line.
<br />
<img alt="5_627_b289692359f488e.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_b289692359f488e.jpg" />
<br />
<br />
This is how OpenGL looks at it:
<br />
<br />
<img alt="5_627_c3d295fe906c20c.png" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_c3d295fe906c20c.png" />
<br />
<br />
GL_LINE_STRIP - The first vertex specifies the starting point, then each
successive vertex is the source point for a line which is rendered
between each and the previous vertex.
<br />
<br />
<br />
<pre> glDrawArrays(GL_LINE_STRIP, 0, 4);
</pre>
<br />
Here’s how our project looks:
<br />
<img alt="5_627_2887bda1a421729.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_2887bda1a421729.jpg" />
<br />
<br />
and this is how OpenGL looks at it:
<br />
<br />
<img alt="5_627_68eaab679c0fb8e.png" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_68eaab679c0fb8e.png" />
<br />
<br />
GL_LINE_LOOP - The first vertex specifies the starting point, then each
successive vertex is the source point for a line which is rendered
between each and the previous vertex. The final vertex is then connected
to the starting point.
<br />
<br />
So our code looks like:
<br />
<br />
<br />
<pre> glDrawArrays(GL_LINE_LOOP, 0, 4);
</pre>
<br />
Our simulator displays:
<br />
<br />
<img alt="5_627_8c0f05b688b1051.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_8c0f05b688b1051.jpg" />
<br />
<br />
And from OpenGL’s perspective.
<br />
<br />
<img alt="5_627_103b47ee36e330a.png" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_103b47ee36e330a.png" />
<br />
<br />
Note that we don’t need to stop (or start) at 4 lines, there can be any
number of lines but obvious we need at least 2 co-ordinates to form a
line.
<br />
<br />
And that’s lines in a nutshell. To make thicker lines, you can
experiment with glLineWidth(GLfloat lineWidth) calls to make the lines
thicker if you like. Note that you may need to adjust start and end
co-ordinates to make a “true” square with changes in line thickness.
<br />
<br />
Also, you can try turning on GL_LINE_SMOOTH to do some basic antialiasing of the lines with this call:
<br />
<br />
glEnable(GL_LINE_SMOOTH);
<br />
<br />
It won’t make a big difference for a variety of reasons. I’ll treat
antialiasing as a separate topic in it’s own right later on in this
series.
<br />
<br />
Lines and points don’t just need to be coloured, they can be texture
mapped as well which can make some really good special effects.
<br />
<br />
Conclusion of another tutorial...
<br />
Well, that was a lot longer than I expected but, once again, I
hope you’ve learnt something. Even if it’s just the information on
stride value for glVertexArray(); trust me it won’t be long before
someone comes asking how to make it work!! I’ll be getting into some
more complex tutorials again next so keep an eye out.
<br />
<br />
Here’s the code for this tutorial:
<br />
<br />
AppleCoder-OpenGLES-08.zip
<br />
<br />
Thanks to all who’ve pointed out the couple of errors that crept in to
the web version of the tutorials, the source code, of course, has been
fine, just a few errors on the web pages.
<br />
<br />
Also, all those who’ve sent me emails of encouragement and support of
this series. It’s those emails that keeps everything rocking here.
<br />
<br />
The home of the tutorials is in the “Tutorials” section of the iphonedevsdk.com forums. Check out the thread there.
<br />
<br />
Until next time, hooroo!
<br />
Simon Maurice
<br />
<br />
<br />
<br />
<br />
<br />
Copyright 2009 Simon Maurice. All Rights Reserved.
<br />
The code provided in these pages are for educational purposes
only and may not be used for commercial purposes without Simon Maurice’s
expressed permission in writing. Information contained within this site
cannot be duplicated in any form without Simon Maurice’s expressed
permission in writing; this includes, but is not limited to, publishing
in printed format, reproduced on web pages, or other forms of electronic
distribution.
<br />
<br />
Linking to these pages on other websites is permitted.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-3765795590291919148.post-76817188707236639602012-01-31T04:59:00.000-08:002012-01-31T05:00:07.922-08:00OpenGL ES 09 - Blending Without Mr. Buzzy Part 1<script type="text/javascript">
<!--
google_ad_client = "ca-pub-9566972421287894";
/* 728x15 LINK AD-1 */
google_ad_slot = "1092558380";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript">
</script>
<br />
<h1 class="firstHeading" style="color: #990000;">
OpenGL ES 09 - Blending Without Mr. Buzzy Part 1</h1>
<br />
<br />
<div style="color: #999999;">
<span style="color: #990000;"><b><span style="color: #cc0000;">Disclaimer:</span></b> <b><span style="color: #444444;">This
post and the whole blog is just a copy of
iPhone-OpenGL-ES-tutorial-Series (seems to have )written by Simon
Maurice. This blog does NOT have a written permission or any kind of
permission from the original author (Simon Maurice). Please use it at
your own risk. If you have any complaints regarding any content of this
blog, please let me know I will remove it right away. Just hoping that
it would be helpful for people who are trying to learn OpenGL. Happy
coding!</span></b></span> </div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<b>Copyright 2009 Simon Maurice. All Rights Reserved.
</b></div>
<div style="color: #999999;">
The code provided in these pages are for educational purposes only and
may not be used for commercial purposes without Simon Maurice’s
expressed permission in writing. Information contained within this site
cannot be duplicated in any form without Simon Maurice’s expressed
permission in writing; this includes, but is not limited to, publishing
in printed format, reproduced on web pages, or other forms of electronic
distribution.
</div>
<br />
<a href="https://github.com/mauriceatron" target="_blank">Source codes can be found here </a><br />
<div style="color: #666666;">
<b><br /></b></div>
<b><span style="color: #666666;">Original tutorial starts from here onwards..</span></b><br />
<br />
<br />
NOTE: I’m not particularly happy with this tutorial at the moment but
I have posted it here in it’s current state so you can get what you can
out of it rather than hold back. Don’t worry if there are errors, but
the code all works. I’ll come back and revise this shortly as well as
writing part 2.
<br />
<br />
<br />
What’s Mr. Buzzy? That’s my kitchen blender! We’re obviously not
talking about my kitchen blender or the Blender 3D software, we’re
talking OpenGL blending. However, this is going to be part one of two or
three tutorials. The effect of blending is quite simple but the detail
can take quite a bit to cover. This will be an overview of blending with
the detail to follow at a later stage.
<br />
<br />
I remember the first time I used blending in code. Fortunately, I didn’t
have to write the algorithm myself, but the effect was really cool. It
was on an SGL Indigo II with a GR3-Elan graphics board (if you’ve ever
seen inside an SGI Indigo, or other of their 1990’s era graphics
powerhouses, you’ll know it’s a full sized graphics board!). The
processing power of that box is practically miniscule compared to this
Mac that I’m using right now, but for the time, it was spectacular!
<br />
<br />
The main reason it was an exciting moment was that, at the time, none of
us had ever seen blending executed real time on texture mapped objects,
with translations through a scene and lighting effects. Needless to
say, we’d seen it in LightWave and other ray-tracing software, but that
was hardly real time. Yes, those were the days when coding was something
that you did because you wanted to do something new and cool; not
because you thought you could output a game in a couple of weeks and
make some coin off it.
<br />
<br />
Anyway, let’s get onto learning blending. Blending, is the process of
combining two images together so that the front image appears partially
transparent. For example, just say you have a piece of red perspex and
look through it. The world will appear red because the colour of the
perspex will alter the colours of all objects you view through it.
<br />
<br />
So, to make blending work, we need an object in the foreground which is
partially transparent (ie an alpha value of less than 1.0 in our RGBA
colour definition) and something behind it which it will “blend” with.
<br />
<br />
That is the critical point about blending: you must have two objects
with the foreground object being partially transparent. Blending is not
transparency. Blending cannot work without some transparency but a
partially transparent object can be blended with an object behind it.
<br />
<br />
I was always taught that the correct term for blending is in fact “alpha
compositing”. I won’t call it that (never have) but you may see the
term around. Computer graphics has jumped a long way since my formal
studies but when I was taught this, I believe that blending was just one
form of alpha compositing, and alpha compositing is a method of drawing
single or combined images with and alpha channel.
<br />
<br />
Getting Started
<br />
Download our project from tutorial #7: AppleCoder-OpenGLES-07.zip
<br />
<br />
We’ll use this project and use the texture mapped pyramid and cube in
the background for our blending effects to be superimposed upon. Fire
this up in Xcode.
<br />
<br />
Blending in OpenGL
<br />
To make blending work in OpenGL, all you need to turn on the
“state” within OpenGL that makes it use blending. I’m sure you know how
to do this:
<br />
<br />
<br />
<pre> glEnable(GL_BLEND);
</pre>
<br />
The place to put this line in the drawView[] method right before we draw
our partially transparent objects and then turn it off again with
glDisable().
<br />
<br />
OK, so blending is switched on (remember to turn it off if you don’t
need it). One of the great things about OpenGL, is that the blending can
be executed using different blending methodologies to produce different
blending effects. These are called blend functions. I’ll just turn one
on and discuss them in detail below.
<br />
<br />
Now, go to the drawView[] method. We are going to define a rectangle
which we will put onto the display in front of our two objects.
<br />
<br />
<br />
<pre> const GLfloat blendRectangle[] = {
</pre>
<pre> 1.0, 1.0, -2.0,
</pre>
<pre> -1.0, 1.0, -2.0,
</pre>
<pre> -1.0, -1.0, -2.0,
</pre>
<pre> 1.0, -1.0, -2.0
</pre>
<pre> };
</pre>
<br />
We want to render two of these. So, in the drawing code will use the
glPushMatrix() and glPopMatrix() pair to translate them independently.
<br />
<br />
Scroll down in the drawView[] method until you get past the drawing code
for the pyramid and the cube. In order to correctly render the blending
effects, we need to draw the opaque objects first, then the transparent
ones just like the old painter’s algorithm you’ve probably come across
in graphics programming.
<br />
<br />
Now, think about the current state of OpenGL. We currently have texture
mapping turned on (of course) so do we want this same texture on our
rectangles? Not for this example so let’s turn off texture mapping with
this line:
<br />
<br />
<br />
<pre> glDisableClientState(GL_TEXTURE_COORD_ARRAY);
</pre>
<br />
Now, turn on blending:
<br />
<br />
<br />
<pre> glEnable(GL_BLEND);
</pre>
<pre> glBlendFunc(GL_ONE, GL_ONE);
</pre>
<br />
This turns on blending with the glEnable() function. Turning on blending
is not enough, we need to tell OpenGL how we want the blending to work
otherwise the default state will do nothing interesting. That’s where
glBlendFunc() comes into play. Don’t worry about this for now, I’m going
to discuss the blending function in detail below; right now, let’s just
get it working and then we can experiment.
<br />
<br />
So, texture mapping is turned off, blending is turned on, and we’ve told
OpenGL how to blend with the glBlendFunc() call. It’s time to draw two
rectangles.
<br />
<br />
<br />
<pre> glPushMatrix();
</pre>
<pre> {
</pre>
<pre> glTranslatef(0.0, 1.0, -4.0);
</pre>
<pre> glVertexPointer(3, GL_FLOAT, 0, blendRectangle);
</pre>
<pre> glEnableClientState(GL_VERTEX_ARRAY);
</pre>
<pre> glColor4f(1.0, 0.0, 0.0, 0.4);
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
</pre>
<pre> }
</pre>
<pre> glPopMatrix();
</pre>
<br />
<br />
<pre> glPushMatrix();
</pre>
<pre> {
</pre>
<pre> glTranslatef(0.0, -1.0, -4.0);
</pre>
<pre> glColor4f(1.0, 1.0, 0.0, 0.4);
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
</pre>
<pre> }
</pre>
<pre> glPopMatrix();
</pre>
<pre> glDisable(GL_BLEND);
</pre>
<br />
Apart from the call to glDisable() at the end, nothing is new here.
We’ve just taken our blendRectangle definition, positioned it on the
display, coloured it and drawn it. We then need to disable blending
otherwise OpenGL will blend our pyramid and cube next time this function
is called (you can experiment later on if you like).
<br />
<br />
That’s it! Hit “Build and Go” and you’ll get the following:
<br />
<br />
<img alt="5_627_704a4bca9e56266.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_704a4bca9e56266.jpg" />
<br />
<br />
Note the colours of the pyramid and cube change as they cross the two
colours of our rectangles? That’s blending at work. The colours with the
two front rectangles which are partially transparent (40% transparent)
and combining or blending with the colour on the objects to create a new
colour.
<br />
<br />
Hence, the yellow rectangle “brightens” the colours slightly compared to the red rectangle (just a bit more colour saturation).
<br />
<br />
If you got just a red and yellow rectangle and no objects, you’ve put
the drawing code in the wrong spot: you need to draw the transparent
objects after drawing the opaque objects; or you haven’t enabled
blending properly.
<br />
<br />
That’s blending in it’s most basic form. Nothing particularly special, but we’re getting somewhere in learning about blending.
<br />
<br />
Quick Recap Before Continuing
<br />
So, to use blending, we need a partially transparent object in
the foreground of our scene. We draw the solid objects first. Then we
enable blending with a call to glEnable(), set the blend factor with
glBlendFunc(), and finally draw our partially transparent objects from
back to front.
<br />
<br />
Now we need to get into the single real complexity of blending. Setting the blend factors.
<br />
<br />
Blending Factors
<br />
I’ve just deleted several pages of text which I had just written.
It was all about calculating the final pixel colour and was loaded full
of what, is essentially, matrix maths (although it was not presented in
that way). Nope, I don’t think that’s the right thing to do otherwise I
might as well be regurgitating the man page.
<br />
<br />
It’s time for me to get more creative and more practical but you do need some theory first: how does blending actually work.
<br />
<br />
As I described earlier, you need a partially transparent object, and
something in the background for blending to work against. You can
actually do blending with nothing more than the background as we call
glClear() every time we redrew a scene in the tutorials so far so you
can just blend against that if you like.
<br />
<br />
So, for each pixel on the display, OpenGL knows a couple of things:
<br />
<br />
<br />
<pre>
</pre>
<pre> 1.There is a source pixel. This is the new pixel we are currently drawing from </pre>
<pre>the partially transparent object (the red and yellow rectangles in our case).
</pre>
<pre> 2.The destination pixel. This is the pixel which is currently in our display </pre>
<pre>buffer. It is what our partially transparent object is being drawn over. In our </pre>
<pre>example above, it is either an object (cube or pyramid), or the background.
</pre>
<pre> 3.The source pixel blend factor specifier. This is the first parameter passed </pre>
<pre>to glBlendFunc(). It tells OpenGL how to process the new pixel.
</pre>
<pre> 4.The destination blend factor specifier. This is the second parameter passed </pre>
<pre>to glBlendFunc() and tells OpenGL how to process the destination pixel.
</pre>
<br />
Knowing the above four pieces of information, OpenGL will calculate new
source and destination blend factors and apply it to the destination
buffer. That is, in a nutshell, all the crap I just deleted!
<br />
<br />
The key is the two parameters we pass to glBlendFunc().
<br />
<br />
In the above example, we used the following code to set the blending function:
<br />
<br />
<br />
<pre> glBlendFunc(GL_ONE, GL_ONE);
</pre>
<br />
The two parameters are the same: GL_ONE. The first parameter affects the
source or incoming pixel value and the second parameter affects the
destination pixel value.
<br />
<br />
There are a whole host of different blend factor specifiers which can be
passed to glBlendFunc(). What I need you to understand right now is
that it’s not what they individually mean, it’s how they are used in
combination that affects our blending effects.
<br />
<br />
That’s an important point. I know I haven’t listed the possible options
yet but I need you to get through this bit of theory first.
<br />
<br />
It needs to be clear in your mind if you’re to get the most out of
blending that, because there is the interaction of the new colour being
the partially transparent object (our rectangles), and the pre-existing
solid objects (our cube, pyramid or our background), that it is the
combination of the two blending factors that control the finished
effect.
<br />
<br />
To get this point across, let’s get some examples out of the way. What
the two parameters means right now is not important! What I am trying to
show you is what happens when you change parameters
<br />
<br />
This one is the OpenGL default when you turn on blending with glEnable():
<br />
<br />
<br />
<pre> glBlendFunc(GL_ONE, GL_ZERO);
</pre>
<br />
We’ve changed the second parameter to GL_ZERO. Remember the second
parameter affects our pre-existing “solid” objects in the buffer and not
the partially transparent objects. It gives us the following:
<br />
<br />
<img alt="5_627_edb7d918b0b0ceb.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_edb7d918b0b0ceb.jpg" />
<br />
<br />
So, what’s happened to our background objects. Without much explanation, the GL_ZERO is one hell of a clue.
<br />
<br />
This is not a “no blend” situation, but the destination pixel is being
multiplied by the RGBA colour (0, 0, 0, 0) resulting in nothing being
rendered. The source (the two front rectangles are being multiplied
GL_ONE by the RGBA One (1, 1, 1, 1). That is a bit of an
oversimplification but it should get the point across.
<br />
<br />
This would be the same result if you did not make any call to glBlendFunc() as it is the default state when blending is enabled.
<br />
<br />
Swap the two parameters around like this:
<br />
<br />
<br />
<pre> glBlendFunc(GL_ZERO, GL_ONE);
</pre>
<br />
<img alt="5_627_f6b63a39efe46da.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_f6b63a39efe46da.jpg" />
<br />
<br />
Not surprisingly, the two rectangles have vanished (it’s what you get when you multiply things by zero).
<br />
<br />
OK, so you know that GL_ZERO and GL_ONE obviously do what their names
suggest, what about the other options. It’s time to list the full set of
possible parameters for glBlendFunc().
<br />
<br />
GL_ZERO
<br />
GL_ONE
<br />
GL_SRC_COLOR
<br />
GL_ONE_MINUS_SRC_COLOR
<br />
GL_DST_COLOR
<br />
GL_ONE_MINUS_DST_COLOR
<br />
GL_SRC_ALPHA
<br />
GL_ONE_MINUS_SRC_ALPHA
<br />
GL_DST_ALPHA
<br />
GL_ONE_MINUS_DST_ALPHA
<br />
GL_SRC_ALPHA_SATURATE
<br />
<br />
I’ll go into the detail of each definition in later tutorials. Some of
these are source only, some are destination only, check the man page if
you’re interested in working out what’s what.
<br />
<br />
Blending Examples
<br />
I’m going to fire through a few examples now. Just to give you an idea to experiment with different blend functions.
<br />
<br />
<br />
<pre> glBlendFunc(GL_SRC_ALPHA, GL_ONE);
</pre>
<br />
<img alt="5_627_2e6982aa46c2ac6.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_2e6982aa46c2ac6.jpg" />
<br />
<br />
Note that the front two rectangles are more transparent? Compared to the
original blend we did, the objects “appear” brighter and the rectangles
“appear” darker. This is your eye being tricked a little.
<br />
<br />
<br />
<pre> glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE);
</pre>
<br />
<img alt="5_627_affc3febaf5d3aa.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_affc3febaf5d3aa.jpg" />
<br />
<br />
Our rectangles have disappeared! Well, not exactly. Their alpha value is
being factored by One Minus Destination Alpha. So, our destination has
the alpha of 1.0 so 1.0 - 1.0 = 0.0.
<br />
<br />
Notice one thing though: the background colour is being multiplied
against the colour set by glClearColor() which also has an alpha of 1.0.
Go back to the setupView[] method and change glClearColor() function
call to:
<br />
<br />
<br />
<pre> glClearColor(0.0, 0.0, 0.0, 0.0);
</pre>
<br />
Only change this one line. Now look what you get:
<br />
<br />
<img alt="5_627_258c3b79e77f48e.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_258c3b79e77f48e.jpg" />
<br />
<br />
Can you work out what’s going on? Whilst it looks like the rectangles
have moved to the background, it’s really blending at work again. From
the previous example, wherever OpenGL renders against a pixel (ie the
pixels of our objects) which have an alpha of 1.0, the rectangles become
100% transparent. Now that the background has an alpha of 0.0, the
alpha level of our rectangles are then 1.0 - 0.0 which renders a colour.
<br />
<br />
Let’s look at that a little closer:
<br />
<br />
Our red rectangle has a colour of: (1.0, 0.0, 0.0, 0.4). So against a
black background with zero alpha, the rendered or blended colour is
(1.0, 0.0, 0.0, 0.4). While that is simplistic due to the background
being black, let’s change the clear colour to blue. Go to setupView[]
and change the glClearColor() call to:
<br />
<br />
<br />
<pre> glClearColor(0.0, 0.0, 1.0, 0.0);
</pre>
<br />
This sets the background to blue, full intensity. Hit “Build & Go” and you’ll get:
<br />
<br />
<img alt="5_627_22c0723158ebf5a.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_22c0723158ebf5a.jpg" />
<br />
<br />
The blue background is obvious. Now the cube and the pyramid are still
being rendered without blending effects (ie the alpha of the front two
rectangles are zero where they intersect with our objects) but the
blending effect against our background colour has caused the red to
change to purple and the yellow to a grey/blue.
<br />
<br />
I am going to go through one final example, then go through the calculations and we’ll work out a pixel colour together.
<br />
<br />
<br />
<pre> glBlendFunc(GL_SRC_ALPHA, GL_DST_ALPHA);
</pre>
<br />
<img alt="5_627_375e915dc9db090.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_375e915dc9db090.jpg" />
<br />
<br />
One Final Blending Combination...
<br />
Let’s now look at what is the most common parameter pair:
<br />
<br />
<br />
<pre> glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
</pre>
<br />
In our test project here, the result would look like the following:
<br />
<br />
<img alt="5_627_df27cbbb7ae5eec.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_df27cbbb7ae5eec.jpg" />
<br />
<br />
If there was panacea for choosing the right parameters for
glBlendFunc(), this would it. It’s your bog standard blend and probably
the most utilised. If in doubt, start with that!
<br />
<br />
Adding Additional Blends
<br />
You don’t need to only blend one object per pixel. You can have
many partially transparent objects layered on top of each other. Add the
following code to the drawView[] method after the drawing of the second
rectangle:
<br />
<br />
<br />
<pre> glPushMatrix();
</pre>
<pre> {
</pre>
<pre> glTranslatef(0.0, 0.0, -3.0);
</pre>
<pre> glScalef(1.0, 0.3, 1.0);
</pre>
<pre> glColor4f(1.0, 1.0, 1.0, 0.6);
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
</pre>
<pre> }
</pre>
<pre> glPopMatrix();
</pre>
<br />
All we’re doing is adding a third rectangle in front of the other two.
OpenGL will combine the blending together quite happily producing the
following:
<br />
<br />
<img alt="5_627_e14ee3c1161425b.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_e14ee3c1161425b.jpg" />
<br />
<br />
Of course, I’ve put something in there I haven’t covered before: glScalef().
<br />
<br />
Scaling - Something I Haven’t Covered Yet!
<br />
Scaling is simply resizing of an object, much like what you do in
a paint program (except that’s 2D). It takes three parameters: xscale,
yscale, and zscale.
<br />
<br />
In the example above, xscale and zscale have a scaling factor of 1.0
meaning original size. Numbers larger than 1.0 magnify or make them
larger, smaller values shrink them. So with the yscale value of 0.3, the
height of the rectangle has been reduced to 30% of it’s original size. I
did that so the cube and the pyramid would pass in and out of the
“double blended” area to produce maximum effect.
<br />
<br />
It’s pretty straight forward and should have covered it earlier when dealing with transformations.
<br />
<br />
Conclusion to this Tutorial
<br />
That will do me for this tutorial for now. Whilst I know there’s a
lot more to cover in blending, particularly the detail in how the
different blending combinations work, I’m going to leave that for
another day. What I do want you to get out of this lesson is how to turn
blending on, and the basic operation of the glBlendFunc() function
call.
<br />
<br />
As usual, here’s the code for this tutorial:
<br />
<br />
AppleCoder-OpenGLES-09.zip
<br />
<br />
The home of the tutorials is in the “Tutorials” section of the iphonedevsdk.com forums. Check out the thread there.
<br />
<br />
Until next time, hooroo!
<br />
Simon Maurice
<br />
<br />
<br />
<br />
<br />
Copyright 2009 Simon Maurice. All Rights Reserved.
<br />
The code provided in these pages are for educational purposes
only and may not be used for commercial purposes without Simon Maurice’s
expressed permission in writing. Information contained within this site
cannot be duplicated in any form without Simon Maurice’s expressed
permission in writing; this includes, but is not limited to, publishing
in printed format, reproduced on web pages, or other forms of electronic
distribution.
<br />
<br />
Linking to these pages on other websites is permitted.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-3765795590291919148.post-12738102974103230422012-01-31T04:57:00.000-08:002012-01-31T04:58:05.811-08:00OpenGL ES 10 - Multiple Textures, Repeating Textures, and The End of the Book Era<script type="text/javascript">
<!--
google_ad_client = "ca-pub-9566972421287894";
/* 728x15 LINK AD-1 */
google_ad_slot = "1092558380";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript">
</script>
<br />
<h1 class="firstHeading" style="color: #990000;">
OpenGL ES 10 - Multiple Textures, Repeating Textures, and The End of the Book Era</h1>
<br />
<div style="color: #999999;">
<span style="color: #990000;"><b><span style="color: #cc0000;">Disclaimer:</span></b> <b><span style="color: #444444;">This
post and the whole blog is just a copy of
iPhone-OpenGL-ES-tutorial-Series (seems to have )written by Simon
Maurice. This blog does NOT have a written permission or any kind of
permission from the original author (Simon Maurice). Please use it at
your own risk. If you have any complaints regarding any content of this
blog, please let me know I will remove it right away. Just hoping that
it would be helpful for people who are trying to learn OpenGL. Happy
coding!</span></b></span> </div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<b>Copyright 2009 Simon Maurice. All Rights Reserved.
</b></div>
<div style="color: #999999;">
The code provided in these pages are for educational purposes only and
may not be used for commercial purposes without Simon Maurice’s
expressed permission in writing. Information contained within this site
cannot be duplicated in any form without Simon Maurice’s expressed
permission in writing; this includes, but is not limited to, publishing
in printed format, reproduced on web pages, or other forms of electronic
distribution.
</div>
<br />
<a href="https://github.com/mauriceatron" target="_blank">Source codes can be found here </a><br />
<div style="color: #666666;">
<b><br /></b></div>
<b><span style="color: #666666;">Original tutorial starts from here onwards..</span></b><br />
<br />
<br />
<br />
My friend stopped by last night and we had a couple of beers and
chatted about various things. Then I told him I was writing these
tutorials. All the time I was talking, he had this “what the f%#k?” look
on his face, gradually getting worse as I said I was putting them for
all to read on the internet. Putting information on the internet for
free just seemed too weird for him. He just didn’t get it. He, being a
consultant for a large IT company, declared he would never do anything
like that because it would reduce the amount of “billable hours” he
could charge clients. Why don’t I “just write a book”? After all, I
could earn some money too.
<br />
<br />
You see, I’m happy to share my knowledge with you for free. I’m not
going to do a book and charge money for my knowledge, I don’t think
that’s appropriate in modern times. It was fine in the 80’s and 90’s
before the majority of the population was on the internet and before
AltaVista made full text search of web pages possible (yes, they did it
before Google).
<br />
<br />
Whilst on my bookshelf contains such classics as Computer Graphics:
Principles and Practice, various editions of Graphics Gems, and
Programming in 3 Dimensions, I haven’t purchased a book in 10 years or
more. Not because I’m not learning; I’ve learned two new platforms in
the past decade, written thousands of lines of code for photorealistic
rendering tools that I only started using since 2000, and even learnt
assembly language for a series of micro-controllers.
<br />
<br />
No books required anymore. People who came before me shared their
information in blogs or websites as badly designed and edited as mine.
No charge. I learnt Mac OS X and the iPhone OS from information from
Apple and websites like iphonedevsdk.com or icodeblog.com. Didn’t need a
book. Last I looked there were no decent books on iPhone programming.
Two of them that I know of include Apple undocumented code which is 100%
guaranteed to get your app rejected from the app store. It’s not right
that people can charge money for such bad practice.
<br />
<br />
Teaching something to a fellow programming doesn’t detract from my
value; in fact it probably enhances things for me as there can be robust
discussions as a result of this series and even new friendships forged.
It does make me feel good when I get your emails thanking me for series
which helps my karma. Besides, I love to code.
<br />
<br />
It’s my time to put back into the community. I know I’m not a teacher, but at least you don’t pay for it.
<br />
<br />
Now, getting on with today’s tutorial and, as promised, I’m answering a
request to use multiple textures. We’re going to texture map our cube
again but I’m going to get rid of the checkerplate image. Instead we are
going to load 6 new textures, one for each side of the cube. Then we’re
going to look at texture wrapping and texture clamping.
<br />
<br />
The new textures for this tutorial are located here: Tutorial10Textures.zip
<br />
<br />
loadTexture[] Changes
<br />
The first thing we need to do is to make the loadTexture[] method more flexible. Basically, we’re going to change the call to
<br />
<br />
- (void)loadTexture:(NSString *)name intoLocation:(GLuint)location;
<br />
<br />
Pretty straight forward. We’re going to change the loadTexture[] method
so we can tell the method which texture to load and where to put it.
Next, switch to the implementation and change the loadTexture[] method
as follows:
<br />
<br />
- (void)loadTexture:(NSString *)name intoLocation:(GLuint)location {
<br />
<br />
<br />
<pre> CGImageRef textureImage = [UIImage imageNamed:name].CGImage;
</pre>
<br />
Don’t forget to remove the @”checkerplate.png” and replace it with our
variable name. Go down and delete the glGenTextures() method and change
the glBindTexture() method to the following:
<br />
<br />
glBindTexture(GL_TEXTURE_2D, location);
<br />
<br />
That’s it. Now this method is far more useful. In order to use it, we
need to make a call to glGenTextures() in our initWithCoder[] method,
then we just make the call to loadTexture[] for each texture we’re going
to load. In this tutorial, we’re going to load 6 textures, so, in
initWithCoder[] after the call to setupView, add the following lines:
<br />
<br />
<br />
<pre> [self setupView];
</pre>
<pre> glGenTextures(6, &textures[0]);
</pre>
<pre> [self loadTexture:@"bamboo.png" intoLocation:textures[0]];
</pre>
<pre> [self loadTexture:@"flowers.png" intoLocation:textures[1]];
</pre>
<pre> [self loadTexture:@"grass.png" intoLocation:textures[2]];
</pre>
<pre> [self loadTexture:@"lino.png" intoLocation:textures[3]];
</pre>
<pre> [self loadTexture:@"metal.png" intoLocation:textures[4]];
</pre>
<pre> [self loadTexture:@"schematic.png" intoLocation:textures[5]];
</pre>
<br />
Also, switch over to the header file and change the definition of out textures variable from 1 to 6 like this:
<br />
<br />
GLuint textures[6];
<br />
<br />
Getting on With the Rendering
<br />
OK, now down to drawView[]. Delete eveything relating to the
pyramid, we’re not using it any more. We do need to edit the cube
drawing code. All we need to do is to delete the colours which we used
to tint our previous texture. So make it look like this:
<br />
<br />
<br />
<pre> // Our new drawing code goes here
</pre>
<pre> rota += 0.2;
</pre>
<br />
<br />
<pre> glPushMatrix();
</pre>
<pre> {
</pre>
<pre> glTranslatef(0.0, 0.0, -4.0); // Change this line
</pre>
<pre> glRotatef(rota, 1.0, 1.0, 1.0);
</pre>
<pre> glVertexPointer(3, GL_FLOAT, 0, cubeVertices);
</pre>
<pre> glEnableClientState(GL_VERTEX_ARRAY);
</pre>
<br />
<br />
<pre> // Draw the front face
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
</pre>
<br />
<br />
<pre> // Draw the top face
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 4, 4);
</pre>
<br />
<br />
<pre> // Draw the rear face
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 8, 4);
</pre>
<br />
<br />
<pre> // Draw the bottom face
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 12, 4);
</pre>
<br />
<br />
<pre> // Draw the left face
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 16, 4);
</pre>
<br />
<br />
<pre> // Draw the right face
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 20, 4);
</pre>
<pre> }
</pre>
<pre> glPopMatrix();
</pre>
<br />
You can leave the glPushMatrix() and glPopMatrix() there. It doesn’t
matter but if you can’t sleep at night knowing that you’re code isn’t
100% optimised, remove them.
<br />
<br />
Now that we’ve made some changes, let’s stop for a second and think
about about OpenGL’s state. I keep coming back to the “state” of OpenGL
because that’s the nature of this beast. Turn something on and it’s
stays on until you tell it otherwise.
<br />
<br />
So, remember way back when loading textures, we would use
glBindTexture() to tell OpenGL which texture we want to work on? So,
after loading the six textures, OpenGL will be “bound” to the last
texture we loaded. If you were to hit “Build and Go”, you’d get the cube
with all faces rendered in the one texture.
<br />
<br />
Since we have a texture for each side of our cube, we need to tell
OpenGL which texture we want it to use each time we make a call to
glDrawArrays(). Thus, our texturing code looks like this:
<br />
<br />
<br />
<pre> glPushMatrix();
</pre>
<pre> {
</pre>
<pre> glTranslatef(0.0, 0.0, -4.0);
</pre>
<pre> glRotatef(rota, 1.0, 1.0, 1.0);
</pre>
<pre> glVertexPointer(3, GL_FLOAT, 0, cubeVertices);
</pre>
<pre> glEnableClientState(GL_VERTEX_ARRAY);
</pre>
<br />
<br />
<pre> // Draw the front face
</pre>
<pre> glBindTexture(GL_TEXTURE_2D, textures[0]);
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
</pre>
<br />
<br />
<pre> // Draw the top face
</pre>
<pre> glBindTexture(GL_TEXTURE_2D, textures[1]);
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 4, 4);
</pre>
<br />
<br />
<pre> // Draw the rear face
</pre>
<pre> glBindTexture(GL_TEXTURE_2D, textures[2]);
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 8, 4);
</pre>
<br />
<br />
<pre> // Draw the bottom face
</pre>
<pre> glBindTexture(GL_TEXTURE_2D, textures[3]);
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 12, 4);
</pre>
<br />
<br />
<pre> // Draw the left face
</pre>
<pre> glBindTexture(GL_TEXTURE_2D, textures[4]);
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 16, 4);
</pre>
<br />
<br />
<pre> // Draw the right face
</pre>
<pre> glBindTexture(GL_TEXTURE_2D, textures[5]);
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 20, 4);
</pre>
<pre> }
</pre>
<pre> glPopMatrix();
</pre>
<br />
Hit “Build and Go” and you’ll be rewarded with six different textures on our cube.
<br />
<br />
<img alt="5_627_4f72917bc5d993e.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_4f72917bc5d993e.jpg" />
<br />
<br />
Hope that answers the questions for those of you who wasn’t sure how this was done.
<br />
<br />
Texture Wrapping
<br />
The next request which I’ve been asked for is repeating or
wrapping textures. Repeating a texture is simply just having the same
texture repeat multiple times. It’s quite useful for using smaller
elements to texture a wall (provided they all fit together neatly that
is).
<br />
<br />
What we’ll do is to just take one of the sides of our cube and repeat
the texture four times. I’m going to change the bamboo texture which is
the texture rendered onto the front face. You can do any face you like.
<br />
<br />
It’s very easy. Do you recall how OpenGL decides which pixel from our
image to render on our objects? It uses the texture co-ordinate array
which I told you before had to be between 0.0 and 1.0. If we want an
image to repeat twice on an object, we just change the 1.0 to a 2.0 and
voila, it will be repeated.
<br />
<br />
So, in our texture co-ordinate array, change the front face to the following:
<br />
<br />
<br />
<pre> const GLshort squareTextureCoords[] = {
</pre>
<pre> // Front face
</pre>
<pre> 0, 2, // top left
</pre>
<pre> 0, 0, // bottom left
</pre>
<pre> 2, 0, // bottom right
</pre>
<pre> 2, 2, // top right
</pre>
<br />
<br />
<pre> // Top face
</pre>
<pre> 0, 1, // top left
</pre>
<pre> 0, 0, // bottom left
</pre>
<pre> 1, 0, // bottom right
</pre>
<pre> 1, 1, // top right
</pre>
<br />
Just hit “Build and Go” and you’ll be rewarded with:
<br />
<br />
<img alt="5_627_a38861acfbc7cd6.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_a38861acfbc7cd6.jpg" />
<br />
<br />
I just did the front face but you can do any (or all) faces if you like.
<br />
<br />
This is the default texture mapping state for OpenGL, when it hits
values greater than 1.0 in the co-ordinate array, it texture wraps. The
Opposite to this is texture clamping. You can enable texture clamping
with a call to glTexParameterf(). For example, if we were to change the
drawing code for the front face to:
<br />
<br />
<br />
<pre> // Draw the front face
</pre>
<pre> glBindTexture(GL_TEXTURE_2D, textures[0]);
</pre>
<pre> glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
</pre>
<pre> glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
</pre>
<br />
The above code, coupled with our texture co-ordinates of > 1.0 will cause OpenGL to render the following:
<br />
<br />
<img alt="5_627_d022a97d643b682.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_d022a97d643b682.jpg" />
<br />
<br />
The edge texture pixel has been extended outwards from the only
correctly drawn texture in the top left. Hence, GL_CLAMP_TO_EDGE.
<br />
<br />
Let me just explain these two lines quickly:
<br />
<br />
<br />
<pre> glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
</pre>
<pre> glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
</pre>
<br />
We used a call to glTexParameterf() when we loaded the textures to set
the texture filtering. Like many OpenGL functions, it does “double
duty”. There are three parameters but OpenGL ES only two come into play,
the first parameter is always GL_TEXTURE_2D as this is the only texture
format OpenGL ES supports (the three parameter function is kept for
compatibility with OpenGL.
<br />
<br />
The second parameter tells OpenGL what “variable” we are going to change. In the above case it is GL_TEXTURE_WRAP_x.
<br />
<br />
The third parameter is just the value we want to set.
<br />
<br />
By setting the OpenGL “variable” or “setting” GL_TEXTURE_WRAP_S to
GL_CLAMP_TO_EDGE, we are telling OpenGL not to repeat the textures along
the s co-ordinates (yes “s”, will explain below). We have then also
done the same for the t co-ordinates. Try commenting one or both out and
running it again in the simulator.
<br />
<br />
The default value for both of these settings is GL_REPEAT which is what
was happing before we changed these to the clamped setting.
<br />
<br />
s t Co-ordinates?
<br />
No, not joking, OpenGL uses an s t co-ordinate system for it’s
textures. It’s really there so as not to confuse you because you have to
remember our textures do not live in our 3D world, they are something
we apply to objects in our 3D world. So you can think of s as horizontal
and t as vertical but as soon as you rotate the object that you mapped
it onto, will it still apply? :)
<br />
<br />
Also, note that the full OpenGL supports 3D textures so you may also come across r & q co-ordinates.
<br />
<br />
That’s it!
<br />
I’m going to leave it here for today as I’m going to be putting
something else up tomorrow. I’ve now realised I’ve been making some
personal mistakes in the past tutorials and I’m going to take everything
in a new direction. We’re going to have some fun!
<br />
<br />
As usual, here’s the code for this tutorial:
<br />
<br />
AppleCoder-OpenGLES-10.zip
<br />
<br />
The home of the tutorials is in the “Tutorials” section of the iphonedevsdk.com forums. Check out the thread there.
<br />
<br />
Until next time, hooroo!
<br />
Simon Maurice
<br />
<br />
<br />
<br />
<br />
Copyright 2009 Simon Maurice. All Rights Reserved.
<br />
The code provided in these pages are for educational purposes
only and may not be used for commercial purposes without Simon Maurice’s
expressed permission in writing. Information contained within this site
cannot be duplicated in any form without Simon Maurice’s expressed
permission in writing; this includes, but is not limited to, publishing
in printed format, reproduced on web pages, or other forms of electronic
distribution.
<br />
<br />
Linking to these pages on other websites is permitted.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-3765795590291919148.post-16299491016998138472012-01-31T04:53:00.000-08:002012-01-31T04:53:26.021-08:00OpenGL ES 11 - Single Texture, Multiple Looks, Render to Texture, and Getting Inspired in Maths<script type="text/javascript">
<!--
google_ad_client = "ca-pub-9566972421287894";
/* 728x15 LINK AD-1 */
google_ad_slot = "1092558380";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript">
</script>
<br />
<h1 class="firstHeading" style="color: #cc0000;">
<span style="color: #990000;">OpenGL ES 11 - Single Texture, Multiple Looks, Render to Texture, and Getting Inspired in Maths</span></h1>
<div style="color: #999999;">
<span style="color: #990000;"><b><span style="color: #cc0000;">Disclaimer:</span></b> <b><span style="color: #444444;">This
post and the whole blog is just a copy of
iPhone-OpenGL-ES-tutorial-Series (seems to have )written by Simon
Maurice. This blog does NOT have a written permission or any kind of
permission from the original author (Simon Maurice). Please use it at
your own risk. If you have any complaints regarding any content of this
blog, please let me know I will remove it right away. Just hoping that
it would be helpful for people who are trying to learn OpenGL. Happy
coding!</span></b></span> </div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<b>Copyright 2009 Simon Maurice. All Rights Reserved.
</b></div>
<div style="color: #999999;">
The code provided in these pages are for educational purposes only and
may not be used for commercial purposes without Simon Maurice’s
expressed permission in writing. Information contained within this site
cannot be duplicated in any form without Simon Maurice’s expressed
permission in writing; this includes, but is not limited to, publishing
in printed format, reproduced on web pages, or other forms of electronic
distribution.
</div>
<br />
<a href="https://github.com/mauriceatron" target="_blank">Source codes can be found here </a><br />
<div style="color: #666666;">
<b><br /></b></div>
<b><span style="color: #666666;">Original tutorial starts from here onwards..</span></b><br />
<br />
<br />In my last year of high school, I was doing an advanced maths subject
which was as interesting as watching paint dry. There were quite a few
times I just didn’t bother turning up because teaching me those advanced
concepts in a classroom environment just didn’t inspire me.
<br />
<br />
One day, sat up the back of the class as always, and thinking about
something more interested, I hadn’t even heard what was being taught
that day. Wasn’t interested as I knew I could learn it in my own time.
Except this day. I looked up at the whiteboard and my jaw must have
dropped. What was on the whiteboard was essentially this:
<br />
<br />
<br />
<pre> [ a b c ] . [ d e f ]
</pre>
<br />
I sat bolt upright. I knew that was matrix maths, something which I had
been exposed to in reading about graphics programs but I hadn’t quite
understood. Well, I was interested once again, and inspired to pay
attention and even enjoy myself.
<br />
<br />
Inspiration is a funny thing. I mention this because no matter how hard I
tried, I couldn’t quite get that tutorial on blending right. So I just
posted it and decided to come back later when I was all “inspired”.
<br />
<br />
The reality will be that I’ll never be inspired looking at that cube and
pyramid, trying to deal with increasingly complex OpenGL ES concepts.
Its time to start to (slowly) increase the complexity of the scenes in
what we’re experimenting in. That way, the inspiration shouldn’t run
dry.
<br />
<br />
The increase in scene complexity will move along slowing, we can’t make
any huge jumps at any single time for fear of leaving people behind. But
increase the complexity we will and the outcome for you will be
learning scene management, as well as concepts in OpenGL ES.
<br />
<br />
Today’s tutorial will just set us in a tunnel. But before we hit the
scene, we have some other bits of housekeeping to do. First of all, we
need cover one final topic on loading textures in the iPhone, something I
left until last as it was the least important (read: I forgot about
it). Then we’re going to draw our tunnel using 4 different textures, but
loaded from a single texture file. Finally, we’ll do some basic render
to texture, touching base with blending once again.
<br />
<br />
Let’s get cracking. Here’s the base project for this tutorial, the completed project is at the end. OpenGLES11 Start.zip
<br />
<br />
iPhone’s Y != OpenGL’s Y
<br />
OpenGL’s Y co-ordinates are at odds with many platforms,
including the iPhone’s. What I mean is that when we use CoreGraphics to
load a texture, it’s actually rendered by OpenGL upside down compared to
what we want (ie rotated 180º around the X axis).
<br />
<br />
To fix this, do not pass glRotatef() function calls. That would get the
result on the screen but it would also waste CPU/GPU time.
<br />
<br />
The only resolutions to this would be:
<br />
<br />
<br />
<pre>
</pre>
<pre> 1.Alter the texture before bundling in an image editor so they are loaded the </pre>
<pre>right way. This works but has the potential of reducing cross platform compatibility.</pre>
<pre> Also requires more work. I would not do this.
</pre>
<pre> 2.Alter your texture co-ordinate array so that it references the right </pre>
<pre>co-ordinates. That is, the bottom left of the image would be (0, 1). OpenGL doesn’t </pre>
<pre>care how the data is for your texture, it just needs to know where to find the </pre>
<pre>texture co-ordinate for each vertex. Again, it works but doesn’t really give a solid </pre>
<pre>solution.
</pre>
<br />
Change the loadTexture[] method so that the image is flipped when
loaded. This works but adds loading time to your app. Each time you load
the textures for a level of a game for example, the textures are
altered either by the CPU or GPU (depending on implementations).
<br />
<br />
“Best practice” is probably option 3 because that is where the platform
specific code for the textures is contained. You may not necessarily be
programming on a platform which has to deal with this, or may need to
deal with it differently.
<br />
<br />
The real world answer depends on what you’re doing. If you’re writing an
iPhone app which may go to another platform later, then I’d just set
the texture co-ordinate array to deal with it. There will be no
performance penalty on loading or rendering, and no extra work
beforehand in an image editor.
<br />
<br />
I’m going to change the loadTexture[] method. For the purposes of these
tutorials, it’s all good. Just don’t make OpenGL do the work for you.
<br />
<br />
The only changes we need to make to the loadTexture[] is call
<br />
<br />
<br />
<pre> CGContextRef textureContext = CGBitmapContextCreate(textureData,
</pre>
<pre> texWidth, texHeight,
</pre>
<pre> 8, texWidth * 4,
</pre>
<pre> CGImageGetColorSpace(textureImage),
</pre>
<pre> kCGImageAlphaPremultipliedLast);
</pre>
<br />
// Rotate the image -- These two lines are new
<br />
CGContextTranslateCTM(textureContext, 0, texHeight);
<br />
CGContextScaleCTM(textureContext, 1.0, -1.0);
<br />
<br />
That gets our images set the right way around.
<br />
<br />
Multiple Textures from a Single Texture
<br />
As part of the fun today, we’re going to use just one single
texture (ie one single call to the loadTexture[] method) but get four
separate textures out of it: two wall textures, a floor texture, and a
ceiling texture.
<br />
<br />
In the last tutorial, we loaded six individual textures, one for each
side of the cube. Each time we load a texture and send it to OpenGL, it
makes a copy of it so the texture resides in OpenGL’s controlled space.
Hence we are able to free() the memory we had reserved with malloc() for
the texture image in the loadTexture[] method. Obviously, each time we
copy a texture it takes up time, we need to execute a call to bind
OpenGL to a new texture each time we want to change it etc.
<br />
<br />
This method does give us some efficiencies but it’s not really about
performance. Whilst is a handy skill, I just want to show you ways you
can use parts of textures.
<br />
<br />
The first thing I did was to create a new blank image in the Gimp with a
size of 512x512 pixels, room enough for four 256x256 textures. Then I
just placed each of the four textures onto this canvas so they didn’t
overlap. Saving it as a PNG file, I of course got this:
<br />
<br />
<img alt="5_627_7531380660c7707.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_7531380660c7707.jpg" />
<br />
<br />
At least some of you out there by now (hopefully all of you) will have
worked out that I’ll be able to access each individual texture by
controlling my texture co-ordinates.
<br />
<br />
Before we can define our texture co-ordinates, we need to define what
the vertices are going to be. We only need to describe a single square
for all of them like follows:
<br />
<br />
<br />
<pre> const GLfloat elementVerticies[] = {
</pre>
<pre> -1.0, 1.0, 0.0, // Top left
</pre>
<pre> -1.0, -1.0, 0.0, // Bottom left
</pre>
<pre> 1.0, -1.0, 0.0, // Bottom right
</pre>
<pre> 1.0, 1.0, 0.0 // Top right
</pre>
<pre> };
</pre>
<br />
OK, so if we wanted to render the wood texture (that’s the top left
square from the combined texture image above), we would need to specify
the following texture co-ordinates:
<br />
<br />
<br />
<pre> const GLfloat combinedTextureCoordinate[] = {
</pre>
<pre> // The wood wall texture
</pre>
<pre> 0.0, 1.0, // Vertex[0~2] top left of square
</pre>
<pre> 0.0, 0.5, // Vertex[3~5] bottom left of square
</pre>
<pre> 0.5, 0.5, // Vertex[6~8] bottom right of square
</pre>
<pre> 0.5, 1.0, // Vertex[9~11] top right of square
</pre>
<br />
As before, they are just co-ordinate pairs. Each co-ordinate pair
indicates to OpenGL where in the texture to obtain the texture pixel for
each vertex (OpenGL just interpolates between each vertex to fill it).
<br />
<br />
Since the “bottom” of the wood texture is half the height of our full
sized texture, the Y co-ordinate is 0.5 instead of 0.0 like previously.
Actually in the past we used GLshort data for the texture co-ordinates
but now we need to use GLfloats to represent fractional values.
<br />
<br />
Similarly, the X co-ordinate which represents the end of the wood
texture (the rightmost edge) is half way along our loaded texture and
thus has a co-ordinate of 0.5.
<br />
<br />
Here’s the full co-ordinate array for our texture:
<br />
<br />
<br />
<pre> const GLfloat combinedTextureCoordinate[] = {
</pre>
<pre> // The wood wall texture
</pre>
<pre> 0.0, 1.0, // Vertex[0~2] top left of square
</pre>
<pre> 0.0, 0.5, // Vertex[3~5] bottom left of square
</pre>
<pre> 0.5, 0.5, // Vertex[6~8] bottom right of square
</pre>
<pre> 0.5, 1.0, // Vertex[9~11] top right of square
</pre>
<br />
<br />
<pre> // The brick texture
</pre>
<pre> 0.5, 1.0,
</pre>
<pre> 0.5, 0.5,
</pre>
<pre> 1.0, 0.5,
</pre>
<pre> 1.0, 1.0,
</pre>
<br />
<br />
<pre> // Floor texture
</pre>
<pre> 0.0, 0.5,
</pre>
<pre> 0.0, 0.0,
</pre>
<pre> 0.5, 0.0,
</pre>
<pre> 0.5, 0.5,
</pre>
<br />
<br />
<pre> // Ceiling texture
</pre>
<pre> 0.5, 0.5,
</pre>
<pre> 0.5, 0.0,
</pre>
<pre> 1.0, 0.0,
</pre>
<pre> 1.0, 0.5
</pre>
<pre> };
</pre>
<br />
I hope that’s straight forward now you can see the full co-ordinate
array. The co-ordinate array refers to the co-ordinates inside the
texture so they don’t even need to be edge bound by the four textures we
started with.
<br />
<br />
One final thing. I’ve made some #define’s to make code reading easier
for getting the correct offset in the array for each texture’s
co-ordinates:
<br />
<br />
<br />
<ol>
<li>define WOOD_TC_OFFSET 0
</li>
</ol>
<ol>
<li>define BRICK_TC_OFFSET 8
</li>
</ol>
<ol>
<li>define FLOOR_TC_OFFSET 16
</li>
</ol>
<ol>
<li>define CEILING_TC_OFFSET 24
</li>
</ol>
<br />
Drawing the Tunnel
<br />
I’m probably going to disappoint some of you right now. The
tunnel you see is not dynamically rendered. It’s static. Soon enough,
we’ll be drawing from a 3D world map and be able to move inside the
tunnel (and other rooms) but we’re going to build up to it, introducing
new OpenGL concepts on the way. But for now, we’ll just draw it in a
straightforward manner.
<br />
<br />
This is what the rendered tunnel looks like:
<br />
<br />
<img alt="5_627_019b45fc3ba83e5.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_019b45fc3ba83e5.jpg" />
<br />
<br />
<br />
The first thing to note is that we only have one object. The
drawing code for the tunnel doesn’t create any more objects, we just use
the single one that we have and manipulate it to look like there
multiple. In fact there are 5 on the left wall, 5 on the right wall, and
10 each for the floor and ceiling (only the centre halves for each are
viewable).
<br />
<br />
So I guess it’s fair to say this tutorial covers a fair bit of code reuse!
<br />
<br />
Before we can draw anything, we need to set the state of OpenGL to draw
texture mapped squares. The code, you should be familiar with by now is:
<br />
<br />
<br />
<pre> glBindTexture(GL_TEXTURE_2D, textures[0]);
</pre>
<pre> glVertexPointer(3, GL_FLOAT, 0, elementVerticies);
</pre>
<pre> glEnableClientState(GL_VERTEX_ARRAY);
</pre>
<pre> glEnable(GL_TEXTURE_2D);
</pre>
<pre> glEnableClientState(GL_TEXTURE_COORD_ARRAY);
</pre>
<br />
All we’ve done is told OpenGL about our vertex array, told it to use it,
turned on texture mapping and told it to use it. We can’t tell OpenGL
about our texture co-ordinate array because that will change depending
on what structure in the scene we are drawing (eg floor, ceiling, or
walls).
<br />
<br />
Firstly, we’ll draw the floor. The floor consists of two of the squares
we defined earlier, moved down to the lower half of the screen, rotated
90º so is like a floor, and drawn side by side. The code looks like
this:
<br />
<br />
<br />
<pre> // Draw the Floor
</pre>
<pre> // First, point the texture co-ordinate engine at the right offset
</pre>
<pre> glTexCoordPointer(2, GL_FLOAT, 0, &combinedTextureCoordinate[FLOOR_TC_OFFSET]);
</pre>
<pre> for (int i = 0; i < 5; i++) {
</pre>
<pre> glPushMatrix();
</pre>
<pre> {
</pre>
<pre> glTranslatef(-1.0, -1.0, -2.0+(i*-2.0));
</pre>
<pre> glRotatef(-90.0, 1.0, 0.0, 0.0);
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
</pre>
<pre> }
</pre>
<pre> glPopMatrix();
</pre>
<br />
<br />
<pre> glPushMatrix();
</pre>
<pre> {
</pre>
<pre> glTranslatef(1.0, -1.0, -2.0+(i*-2.0));
</pre>
<pre> glRotatef(-90.0, 1.0, 0.0, 0.0);
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
</pre>
<pre> }
</pre>
<pre> glPopMatrix();
</pre>
<pre> }
</pre>
<br />
We have a for loop which repeats the process five times. Each time the
loop executes, two squares of our floor are drawn. It’s just worth
pointing out the change to the call to glTexCoordPointer():
<br />
<br />
glTexCoordPointer(2, GL_FLOAT, 0,
<br />
<pre> &combinedTextureCoordinate[FLOOR_TC_OFFSET]);
</pre>
<br />
The parameter for the pointer to the array needs to be passed the
correct address of the starting co-ordinate. Hence the #defines earlier
to make reading the code easier.
<br />
<br />
If you’re following along in Xcode, you can hit “Build & Go” and you’ll see the following in the simulator:
<br />
<br />
<img alt="5_627_019b45fc3ba83e5.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_019b45fc3ba83e5.jpg" />
<br />
<br />
If you’re not following along in Xcode, then you’ll just have to take my word for it... :)
<br />
<br />
Now some walls. The process is the same but this time we move them along the X axis only and rotate them on the Y axis.
<br />
<br />
<br />
<pre> // Draw the walls
</pre>
<pre> // This time we'll change the texture coordinate array during the drawing
</pre>
<pre> for (int i = 0; i < 5; i++) {
</pre>
<pre> glPushMatrix();
</pre>
<pre> {
</pre>
<pre> glTexCoordPointer(2, GL_FLOAT, 0, &combinedTextureCoordinate</pre>
<pre>[BRICK_TC_OFFSET]);</pre>
<pre></pre>
<pre> glTranslatef(-1.0, 0.0, -2.0+(i*-2.0));
</pre>
<pre> glRotatef(-90.0, 0.0, 1.0, 0.0);
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
</pre>
<pre> }
</pre>
<pre> glPopMatrix();
</pre>
<br />
<br />
<pre> glPushMatrix();
</pre>
<pre> {
</pre>
<pre> glTexCoordPointer(2, GL_FLOAT, 0, &combinedTextureCoordinate</pre>
<pre>[WOOD_TC_OFFSET]);</pre>
<pre></pre>
<pre> glTranslatef(1.0, 0.0, -2.0+(i*-2.0));
</pre>
<pre> glRotatef(-90.0, 0.0, 1.0, 0.0);
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
</pre>
<pre> }
</pre>
<pre> glPopMatrix();
</pre>
<pre> }
</pre>
<br />
Note where I’ve put the call to change the texture co-ordinate array’s
offset? That’s because I have used a different texture between the left
and the right walls.
<br />
<br />
<img alt="5_627_a52a6411653fd3e.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_a52a6411653fd3e.jpg" />
<br />
<br />
<br />
Finally it’s time to draw the ceiling. Same deal as the floor, just increased in the Y direction rather than decreased:
<br />
<br />
<br />
<pre> // Draw the ceiling
</pre>
<pre> // Start by setting the texture coordinate pointer
</pre>
<pre> glTexCoordPointer(2, GL_FLOAT, 0, &combinedTextureCoordinate[CEILING_TC_OFFSET]);
</pre>
<pre> for (int i = 0; i < 5; i++) {
</pre>
<pre> glPushMatrix();
</pre>
<pre> {
</pre>
<pre> glTranslatef(-1.0, 1.0, -2.0+(i*-2.0));
</pre>
<pre> glRotatef(90.0, 1.0, 0.0, 0.0);
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
</pre>
<pre> }
</pre>
<pre> glPopMatrix();
</pre>
<pre> glPushMatrix();
</pre>
<pre> {
</pre>
<pre> glTranslatef(1.0, 1.0, -2.0+(i*-2.0));
</pre>
<pre> glRotatef(90.0, 1.0, 0.0, 0.0);
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
</pre>
<pre> }
</pre>
<pre> glPopMatrix();
</pre>
<pre> }
</pre>
<br />
That gives us our completed tunnel.
<br />
<br />
<img alt="5_627_e33c346f5ad367b.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_e33c346f5ad367b.jpg" />
<br />
<br />
<br />
Granted, its nothing special at the moment but will give us a
decent place in which to begin experimenting. From here we can start to
deal with topics such as scene management, rendering floors (or
landscapes) and walls from a world map etc.
<br />
<br />
Render to Texture: The Easy Way
<br />
Render to texture is the process of creating a new texture
(exactly like one we would get from loadTexture[]) from what has already
been rendered in our scene. So instead of loading a texture from our
app’s bundle, we let OpenGL render a scene, then we copy all or part of
that scene into a buffer, then add it back it before presenting it as a
finished screen.
<br />
<br />
Whew! Take a deep breath. It’s not as complex as what it sounds, and more useful than it sounds.
<br />
<br />
An easy example of render to texture is to produce a reflection of part
of your render buffer. You would want to do this because when it’s been
rendered to the buffer, effects such as lighting, transparency and
blending have already been done. So if you were to copy that and use it
for a reflection effect, you would not need to recalculate all the
lighting, shadows etc for the reflected image.
<br />
<br />
I wasn’t going to cover render to texture yet as it’s probably a bit
further on than where I’m up to so far, but there’s been a few requests
so I will cover the easy way to achieve this effect now. We won’t see
the best yet because we have not yet covered details such as lighting
but you’ll get the idea.
<br />
<br />
In our tunnel world, we’re going to add a couple more texture mapped
squares at the end of the tunnel and use render to texture to produce a
reflection effect (albeit cheaply).
<br />
<br />
First thing, we’ll add the new objects.
<br />
<br />
That’s Not Light; There’s Romo at the End of my Tunnel
<br />
First thing we’re going to do is to load two new textures. One is
just for a background effect, then we’re going to blend a second
texture of Tony Romo, our hero, in front of it, just so I can show the
render to texture effect at least with blending (which so far I have
covered very poorly but covered quickly nonetheless).
<br />
<br />
Add the two new lines to your initWithCoder[] method:
<br />
<br />
[self loadTexture:@"bluetex.png" intoLocation:textures[1]];
<br />
[self loadTexture:@"romo.png" intoLocation:textures[2]];
<br />
<br />
With our textures loaded, after drawing the ceiling, we will then
generate the image for the end of the tunnel. First we need to set up a
“standard” texture co-ordinate array for both of our textures, then we
draw the opaque texture’s square first, followed by the partially
transparent textured square using blending.
<br />
<br />
<br />
<pre> const GLfloat standardTextureCoordinates[] = {
</pre>
<pre> 0.0, 1.0,
</pre>
<pre> 0.0, 0.0,
</pre>
<pre> 1.0, 0.0,
</pre>
<pre> 1.0, 1.0
</pre>
<pre> };
</pre>
<pre> // Draw the Blue texture
</pre>
<pre> glBindTexture(GL_TEXTURE_2D, textures[1]);
</pre>
<pre> glTexCoordPointer(2, GL_FLOAT, 0, standardTextureCoordinates);
</pre>
<pre> glPushMatrix();
</pre>
<pre> {
</pre>
<pre> glTranslatef(0.0, 0.0, -6.0);
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
</pre>
<pre> }
</pre>
<pre> glPopMatrix();
</pre>
<pre> glBindTexture(GL_TEXTURE_2D, textures[2]);
</pre>
<pre> glEnable(GL_BLEND);
</pre>
<pre> glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
</pre>
<pre> glPushMatrix();
</pre>
<pre> {
</pre>
<pre> glTranslatef(0.0, 0.0, -5.9);
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
</pre>
<pre> }
</pre>
<pre> glPopMatrix();
</pre>
<pre> glDisable(GL_BLEND);
</pre>
<br />
Having had an abortive tutorial on blending, this is a much better
example. Remember from the blending tutorial that you needed to draw the
opaque object first followed by the partially transparent object in
front of it? That’s all we did. The image of Tony Romo is just one which
I quickly deep etched so that despite being the same image size as the
blue texture, doesn’t cover it up.
<br />
<br />
The above code produces the following image:
<br />
<br />
<img alt="5_627_75c3bf5610eb750.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_75c3bf5610eb750.jpg" />
<br />
<br />
By way of an example of this blending. Go back to the code and comment
out the glEnable(GL_BLEND), you only need to comment out the one line.
Here’s what you get:
<br />
<img alt="5_627_736c240c4287d54.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_736c240c4287d54.jpg" />
<br />
<br />
Notice now that the textured image of Romo obscures the blue textured
square behind it? That’s non-blended. I need you to see this to show you
that render to texture takes part of an image we have rendered already.
Including all effects.
<br />
<br />
Un-comment the line glEnable(GL_BLEND). We’ll leave this effect in for
the render to texture. Now that we’re set up, I’ll talk you through the
render to texture process.
<br />
<br />
Render to Texture: The Process
<br />
The process for executing render to texture is straightforward
(well, the method we’re covering today is). It’s a 4 step process:
<br />
<br />
<br />
<pre>
</pre>
<pre> 1.Create the render texture which is our target texture to which we copy the </pre>
<pre>contents of our rendered image. You do this in almost exactly the same way as you do</pre>
<pre> for a texture you would load with loadTexture[].
</pre>
<pre> 2.Render your scene. We’ve done that; we’ve got Tony Romo (our hero) in a tunnel</pre>
<pre> with some blending effect to prove that we’re copying from a rendered section of our</pre>
<pre> buffer.
</pre>
<pre> 3.Copy the portion of the image that we want (or the entire image) into our</pre>
<pre> texture that we set up in step 1.
</pre>
<pre> 4.Render the new texture.
</pre>
<br />
Now that you know the four steps, only step 3 is completely new, the others will have new elements but will be very familiar.
<br />
<br />
Create the Render Texture
<br />
Normally for any image we want to use as a texture, it’s just a
case of loading it, formatting it, then sending it to OpenGL via a call
to glTexImage2D(). In the case of a render texture, we don’t need to do
much of that but we still need to get OpenGL to allocate space in it’s
operational area to store the pixels copied from the rendered scene.
It’s easy. It’s simply a case of heading to initWithCoder[] and, after
the loading of the textures, add the following lines of code for the
render texture:
<br />
<br />
// Render to Texture texture buffer setup
<br />
glBindTexture(GL_TEXTURE_2D, textures[3]);
<br />
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 128, 128, 0, GL_RGBA,
<br />
<pre> GL_UNSIGNED_BYTE, nil);
</pre>
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
<br />
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
<br />
<br />
The first thing we do is, as per normal, tell OpenGL which texture we
are working with by choosing the next available texture id. Then, the
next line, we are creating the memory for it in almost exactly the same
way as we created a texture which we loaded from a file.
<br />
<br />
There are two differences. First, we’ve hard coded the texture size of
128x128 pixels; just a size which sounded good to me at the time, you
can choose your own texture size if you like (note, the texture size
must still a power of 2).
<br />
<br />
The final parameter I’ve set to nil (or NULL if you prefer). The final
parameter normally contains a buffer which contains our image data.
Since we have no image data, we just let OpenGL know that and it will
create the texture for itself but not fill any data into the allocated
memory.
<br />
<br />
Finally we just set the filter parameters and we’re done with step one.
<br />
<br />
Render the Scene
<br />
This part is self explanatory. All we do is write everything to our render buffer and then go to step 3.
<br />
<br />
Copy the Portion of Our Render Buffer to Our Render Texture
<br />
There are multiple ways to do this. In this tutorial I’m only
going to discuss one way which is almost the fastest but guaranteed to
work on all implementations (ie does not require extensions, even if
those extensions are now fairly commonplace). It also has the advantage
of being easy to implement.
<br />
<br />
We’re going to copy a subsection of our render buffer, at the same time
grabbing all the effects from blending, and, if we had others, those
effects too.
<br />
<br />
Down in the draw view method after all the previous drawing code we can add the following lines of code:
<br />
<br />
<br />
<pre> glBindTexture(GL_TEXTURE_2D, textures[3]);
</pre>
<pre> glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 100, 150, 128, 128);
</pre>
<br />
Believe or not, you’ve just done a render to texture. The first line is
well known, we just need to tell OpenGL which texture we want it to work
on. The call to glCopyTexSubImage2D() is what does the heavy lifting.
This function takes a subsection of the current render buffer and copies
a sub-portion of a size and origin into the active texture.
<br />
<br />
In the case above, we’ve copied a portion at origin (100, 150) of size
128x128 pixels into our texture. The parameters for this are:
<br />
<br />
glCopyTexSubImage2D(
<br />
<pre> target always GL_TEXTURE_2D in OpenGL ES
</pre>
<pre> level detail level, will cover when we hit MIP </pre>
<pre>maps</pre>
<pre></pre>
<pre> xoffset &
</pre>
<pre> yoffset in the target, specifies if you want the </pre>
<pre>copied</pre>
<pre></pre>
<pre> pixels to start somewhere other than </pre>
<pre>(0, 0) </pre>
<pre></pre>
<pre> x & y this is the source (render buffer) origin </pre>
<pre>co-
</pre>
<pre> ordinates originating at the bottom left </pre>
<pre>(0,0)
</pre>
<pre> width & height Size of the image to copy
</pre>
);
<br />
<br />
Just remember that since you’re copying from a 2D view, there is no Z
co-ordinates. Also you’re 3D world co-ordinates do not apply here, you
need to convert them back into view space. That’s something for another
day as this one is getting really long already and iWeb is starting to
lag behind as I type!! :)
<br />
<br />
Anyway, texture created from render, now we can just use as we please.
<br />
<br />
Rendering our New Texture
<br />
The final step should require no explanation. Here’s the code:
<br />
<br />
<br />
<pre> glPushMatrix();
</pre>
<pre> {
</pre>
<pre> glTranslatef(0.0, -1.0, -2.0);
</pre>
<pre> glRotatef(-75.0, 1.0, 0.0, 0.0);
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
</pre>
<pre> }
</pre>
<pre> glPopMatrix();
</pre>
<br />
All we’ve done is moved it around a little and rotated it so it’s floating in space in front of us. Here’s what we get:
<br />
<br />
<img alt="5_627_d5de7b7e49f4c62.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_d5de7b7e49f4c62.jpg" />
<br />
<br />
There we have Mr Romo, our hero, render to texture. Done.
<br />
<br />
<br />
Conclusion of Another
<br />
OK, that’s it for today. As usual you can check in at the
iphonedevsdk.com forum website in the tutorials section to keep up with
this series. Emails directly to me a are of course welcome (link below).
<br />
<br />
Soon enough, we’ll have this done in complete 3D.
<br />
<br />
As usual, here’s the code for this tutorial:
<br />
<br />
OpenGLES11.zip
<br />
<br />
Until next time, hooroo!
<br />
Simon Maurice
<br />
<br />
<br />
<br />
<br />
<br />
Copyright 2009 Simon Maurice. All Rights Reserved.
<br />
The code provided in these pages are for educational purposes
only and may not be used for commercial purposes without Simon Maurice’s
expressed permission in writing. Information contained within this site
cannot be duplicated in any form without Simon Maurice’s expressed
permission in writing; this includes, but is not limited to, publishing
in printed format, reproduced on web pages, or other forms of electronic
distribution.
<br />
<br />
Linking to these pages on other websites is permitted.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-3765795590291919148.post-37432178429178829022012-01-31T04:47:00.000-08:002012-01-31T04:48:34.865-08:00OpenGL ES 12 - Landscape View & Handling Touches Part 1 - 2D World<script type="text/javascript">
<!--
google_ad_client = "ca-pub-9566972421287894";
/* 728x15 LINK AD-1 */
google_ad_slot = "1092558380";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript">
</script>
<br />
<h1 class="firstHeading" style="color: #cc0000;">
OpenGL ES 12 - Landscape View & Handling Touches Part 1 - 2D World</h1>
<br />
<div style="color: #999999;">
<span style="color: #990000;"><b><span style="color: #cc0000;">Disclaimer:</span></b> <b><span style="color: #444444;">This
post and the whole blog is just a copy of
iPhone-OpenGL-ES-tutorial-Series (seems to have )written by Simon
Maurice. This blog does NOT have a written permission or any kind of
permission from the original author (Simon Maurice). Please use it at
your own risk. If you have any complaints regarding any content of this
blog, please let me know I will remove it right away. Just hoping that
it would be helpful for people who are trying to learn OpenGL. Happy
coding!</span></b></span> </div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<b>Copyright 2009 Simon Maurice. All Rights Reserved.
</b></div>
<div style="color: #999999;">
The code provided in these pages are for educational purposes only and
may not be used for commercial purposes without Simon Maurice’s
expressed permission in writing. Information contained within this site
cannot be duplicated in any form without Simon Maurice’s expressed
permission in writing; this includes, but is not limited to, publishing
in printed format, reproduced on web pages, or other forms of electronic
distribution.
</div>
<br />
<a href="https://github.com/mauriceatron" target="_blank">Source codes can be found here </a><br />
<div style="color: #666666;">
<b><br /></b></div>
<b><span style="color: #666666;">Original tutorial starts from here onwards..</span></b><br />
<br />
Had a funny conversation with my boss the other day. He’s a much more
learned OpenGL and general graphics gentleman than I am despite the
fact we’ve both been working professionally in OpenGL for the same
length of time roughly. He’d seen these tutorials and joked that “If
that’s really what I thought about OpenGL, I’d never have hired you!”.
<br />
<br />
The reason for him saying that was the fact that I use terms like
“OpenGL will render this like so...” or “send your vertex array to
OpenGL”. I know, and some others who read these tutorials will also
know, that when I refer to OpenGL existing as an entity, its like me
telling you that Santa Claus is real!! Whilst I’m not trying to mislead
you, I’m just trying to make things simpler and, in reality, teaching
the way that I think I would have liked to be taught.
<br />
<br />
Just as a side note to my conversation with my boss, he said he would
find some good modern books to recommend for beginners since he thought
the book era was not really dead, and I just think that search is the
answer to the world’s problems including world hunger, global peace etc
etc etc. Anyway he’s going to dig something up for me. If they’re any
good, I’ll let you know.
<br />
<br />
My first exposure to OpenGL was at my first job in Government research.
We’d just got budget approval for some frighteningly expensive SGI
hardware and myself and another colleague were sent off for a week of
intensive OpenGL introduction before these boxes arrived.
<br />
<br />
For the first three and a half days of this week, we didn’t see a single
line of code. It was nothing more than hours and hours talking about
the specification, the state machine and all that core information that,
while at some stages were very interesting, was fair dinkum boring! I
was often just nodding and pretending to understand, and I know my
colleague was too. But it wasn’t until we were introduced to the code
that things started to come together in a small way.
<br />
<br />
When a brand new SGI Indy arrived on my desk some months later (yes,
that was my computer, not shared!) I really started to learn OpenGL
probably in much the same way I now show here. Put things on the screen
and then experiment with them. Didn’t care if the perspective is out, or
things are looking squashed. Didn’t worry about being optimising for
speed. Just get things happening.
<br />
<br />
So when you read me saying things that you know are not true but show
concept as an oversimplification, it’s because for many people out
there, I still need them to believe Santa Claus exists rather than
explain the long detail about how those presents appeared under the
Christmas tree.
<br />
<br />
Tangent Time...
<br />
Anyway, today I’m off on a tangent. I wanted to continue on with
the tunnel from the last tutorial where I was going to break that down
into a 3D world where we would draw the floor from a map, move around
and then build the walls and rooms to explore. I’m going to put that on
hold for a minute.
<br />
<br />
I’ve started to get quite a few requests for handling touches which, on
the surface is quite easy, but there’s lots of potential frustration in
the detail. Some guys who have been really good to me through this
tutorial series with comments and notifying me of errors in my text etc
want to know this. So as a general nod and thank you to them (you know
who you are), here it comes.
<br />
<br />
I do need to cover this in two stages though. Working in a 2D world for
something like a slide scrolling arcade game like Defender, or a
orthographic perspective game like Syndicate, is really different to a
3D game. Whilst touches may seem less important than the accelerometer
in a 3D game, you still need a fire button, pause game button etc for
user input so the principles still do apply.
<br />
<br />
I’ll deal with 3D next time. In OpenGL (ie not OpenGL ES) it’s quite
easy to do because we have handy utility functions to take the grunt
work out of it which aren’t available to us on the iPhone. So that’s for
next time.
<br />
<br />
The Starting Point - Going Horizontal
<br />
What we’re going to do very simply first up is to bring up a 2D
display in landscape mode. I’m going to use landscape mode because I
think most games run in this mode and, if you want a portrait interface,
I’m sure you’ll see the things we need to leave out. So to begin with,
we’ll go from square one again to show you the steps you need to do to
make an app appear in landscape mode.
<br />
<br />
There’s no starting template, so fire up Xcode and create a new project using Apple’s OpenGL ES template.
<br />
<br />
First off, we’ll set the device to go straight into landscape mode and
get rid of the status bar. Under the “Groups & Files” navigation
area, expand the “Resources” folder, and open the app’s .plist file (ie
mine was called OpenGLES12-info.plist because my project name is
OpenGLES12).
<br />
<br />
You’ll get this on the screen:
<br />
<br />
<br />
<br />
Click on the last item so that the “+” icon appears on the right hand
side and click on the “+” icon to add a new item. In the drop down which
appears under the “Key” heading, select “Initial Interface
Orientation”. TAB across to the “Value” heading and select “Landscape
(right home button)”.
<br />
<br />
This will bring our app open in landscape mode with the home button on the right hand side.
<br />
<br />
Now to get rid of the status bar so we have a full screen, press the “+”
button again and select the key: “Status bar is initially hidden”, and
click the check box under the value field so that a tick appears.
<br />
<br />
Drawing Something to Fondle
<br />
We can’t really do a touch event without something on the screen
to move around with our finger. So, g=head straight to the EAGLView.m
file and the drawView[] method. We’ll delete the square drawing code so
make the draw view method look like this:
<br />
<br />
- (void)drawView {
<br />
<br />
<br />
<pre> [EAGLContext setCurrentContext:context];
</pre>
<br />
<br />
<pre> glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
</pre>
<pre> glViewport(0, 0, backingWidth, backingHeight);
</pre>
<br />
<br />
<pre> glMatrixMode(GL_PROJECTION);
</pre>
<pre> glLoadIdentity();
</pre>
<pre> glOrthof(-1.0f, 1.0f, -1.5f, 1.5f, -1.0f, 1.0f);
</pre>
<pre> glMatrixMode(GL_MODELVIEW);
</pre>
<br />
<br />
<pre> glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
</pre>
<pre> glClear(GL_COLOR_BUFFER_BIT);
</pre>
<br />
<br />
<pre> glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
</pre>
<pre> [context presentRenderbuffer:GL_RENDERBUFFER_OES];
</pre>
}
<br />
<br />
There are two steps to drawing something on screen in landscape mode.
First of all, I’ll just do the drawing code and then we need to make
some adjustments to the view to make things look right in landscape
mode. I’ll do the drawing code first so you can do a “Build and Go” to
make sure everything is working right before we get to the tricky stuff.
<br />
<br />
We’ll draw a point, so first add the following after the call to glClear() and before the buffer swap:
<br />
<br />
<br />
<pre> const GLfloat pointLocation[] = {
</pre>
<pre> 0.0, 0.0
</pre>
<pre> };
</pre>
<br />
<br />
<pre> glPointSize(32.0);
</pre>
<pre> glColor4f(1.0, 1.0, 0.0, 1.0);
</pre>
<pre> glVertexPointer(2, GL_FLOAT, 0, pointLocation);
</pre>
<pre> glEnableClientState(GL_VERTEX_ARRAY);
</pre>
<pre> glDrawArrays(GL_POINTS, 0, 1);
</pre>
<br />
Hit “Build and Go” and the simulator should start in landscape mode with the following on screen:
<br />
<br />
<img alt="5_627_e34b1aeaaaaea81.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_e34b1aeaaaaea81.jpg" />
<br />
<br />
So far so good. All we’ve done is to draw a point.
<br />
<br />
Make Sure You Have Some Caffeine...
<br />
Now to deal with the biggest source of confusion of working in
landscape mode. Despite the fact that the iPhone knows we are in
landscape mode, it won’t tell OpenGL this. So right now, if you try and
translate this point to a higher Y value (ie towards the top of the
display), it will actually move towards the top of the iPhone, towards
the earpiece.
<br />
<br />
Try it!
<br />
<br />
There are two ways I can think of to get around this but the easiest way
is just to rotate the projection matrix through 90º. The other way is
changing everything from below the surface of the EAGLView class but I
haven’t tried going that deep yet and I’m pretty sure that there’s a
roadblock in there somewhere.
<br />
<br />
So, in order to make this work, we need rotate our projection matrix. This is achieved as follows:
<br />
<br />
<br />
<pre> glMatrixMode(GL_PROJECTION);
</pre>
<pre> glLoadIdentity();
</pre>
<pre> glRotatef(-90.0, 0.0, 0.0, 1.0);
</pre>
<pre> glOrthof(-1.0, 1.0, 1.5, -1.5, -1.0, 1.0);
</pre>
<pre> glTranslatef(-1.0, -1.5, 0.0);
</pre>
<br />
The first line of code after the glLoadIdentity() just rotates our
projection so that when we specify a change in the X co-ordinate, it
does that rather than change the Y-co-ordinate. The second line is just a
connivence for us whilst working on a 2D view. Instead of making the
centre of the screen (0, 0), we have moved the co-ordinates of (0, 0)
(or the origin if you think in those terms) to the bottom left of the
screen, ie:
<br />
<br />
<img alt="5_627_fc111298c4a6df9.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_fc111298c4a6df9.jpg" />
<br />
<br />
If you wanted the home button on the left, you would rotate through +90º instead of negative.
<br />
<br />
We’re not done yet. We need to ensure that our aspect ratio is right.
Currently it’s not because we’re originally set up for a display that is
taller than it is wider (portrait mode). So what we need to do look a
bit more closely at our call to glOrthof().
<br />
<br />
<br />
<pre> glOrthof(-1.0f, 1.0f, -1.5f, 1.5f, -1.0f, 1.0f);
</pre>
<br />
It’s the first four parameters that we’re interested in. This does two
things for us. It tells OpenGL where our objects get clipped from our
display and the aspect ratio.
<br />
<br />
The clipping part is defined by each individual parameter passed to the
function. It’s the left, right, bottom, top (near and far are the last
two, they don’t matter in 2D). So, an object with an X co-ordinate of
less than -1.0 (ie more negative) is not on the screen. Greater than 1.0
is also not displayed (it’s off to the right). Same applies for the Y
co-ordinates.
<br />
<br />
Now, the aspect ratio is the combination of the X pair and the Y pair.
You can see that the overall width of our display is 2.0 units (ie
abs(-1.0) + abs(1.0) = 2.0) and the overall height is 3.0 units. But
since we’re in landscape mode we need to swap these around otherwise
rendered objects will look “squashed”.
<br />
<br />
So, we can change the glOrthof() and the subsequent glTranslatef() function to these two to get these two issues fixed:
<br />
<br />
<br />
<pre> glOrthof(-1.5, 1.5, 1.0, -1.0, -1.0, 1.0);
</pre>
<pre> glTranslatef(-1.5, -1.0, 0.0);
</pre>
<br />
That’s better. Now you can see that we’ve also changed the translation to move the origin point of (0, 0) to the bottom left.
<br />
<br />
Now, I’ve shown it to you in this way to break it down a bit so as to
introduce this concept a bit more slowly. However, we can actually do
away with the glTranslatef() function simply by changing he parameters
to glOrthof(). Here’s our final set up code:
<br />
<br />
<br />
<pre> glMatrixMode(GL_PROJECTION);
</pre>
<pre> glLoadIdentity();
</pre>
<pre> glRotatef(-90.0, 0.0, 0.0, 1.0);
</pre>
<pre> glOrthof(0.0, 3.0, 0.0, 2.0, -1.0, 1.0);
</pre>
<br />
Our width is still 3 units and our height is still 2 units, but we’ve
just specified new clipping boundaries. Note the order of the functions?
You cannot do the rotation after the call to glOrthof() because you
always rotate around (0, 0).
<br />
<br />
OK, so there we are, in landscape mode and all of our transformations
will now work as we expect. Time to move on. But before we can move to
touches, let’s look at...
<br />
<br />
World to Screen Co-ordinates
<br />
First up, a quick demonstration. Switch to the definition and add the new variable as follows:
<br />
<br />
<br />
<pre> GLfloat newLocation[2];
</pre>
<br />
Now, switch back to the implementation and in the initWithCoder[] method, define the variable’s initial location as:
<br />
<br />
<br />
<pre> newLocation[0] = 1.5;
</pre>
<pre> newLocation[1] = 1.0;
</pre>
<br />
New newLocation[0] is our X co-ordinate and newLocation[1] is our Y
co-ordinate. Where do you think the point will appear on the screen?
<br />
<br />
Hold that thought! Let’s add some code and find out. In the drawView[] method, we’ll change the drawing code to:
<br />
<br />
<br />
<pre> glPushMatrix();
</pre>
<pre> glPointSize(32.0);
</pre>
<pre> glColor4f(1.0, 1.0, 0.0, 1.0);
</pre>
<pre> glTranslatef(newLocation[0], newLocation[1], 0.0);
</pre>
<pre> glVertexPointer(2, GL_FLOAT, 0, pointLocation);
</pre>
<pre> glEnableClientState(GL_VERTEX_ARRAY);
</pre>
<pre> glDrawArrays(GL_POINTS, 0, 1);
</pre>
<pre> glPopMatrix();
</pre>
<br />
Apart from pushing and popping the current (object) matrix, we’ve added a
line to move the point to the X and Y co-ordinates held by our variable
newLocation.
<br />
<br />
Hit “Build and Go” to see if you were right on the point’s final location.
<br />
<br />
Were you right? The point is now back in the centre of our screen. So what does that tell us about world to screen co-ordinates?
<br />
<br />
We now know that OpenGL will only display objects which are currently
located in our world bound by the rectangle which has an origin of (0,
0) and extends 3.0 world units in the positive X direction and 2.0 world
units in the positive Y direction. Thus:
<br />
<br />
<img alt="5_627_23ecaf31bc5cef3.png" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_23ecaf31bc5cef3.png" />
<br />
<br />
From the diagram above, that’s a representation of what we see on our
iPhone with these clipping co-ordinates. The green area represents the
viewable section of our world.
<br />
<br />
Therefore we can now map these to screen co-ordinates. I’ll show you how
to do this below but I’m sure you’re already starting to work it out.
<br />
<br />
Handling Touches
<br />
This is not hard to do thankfully. The EAGLView class is a
subclass of UIView, which, in turn, is a subclass of UIResponder. The
UIResponder class defines but does not implement the following 4 methods
to handle touches:
<br />
<br />
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
<br />
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
<br />
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
<br />
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
<br />
<br />
touchesBegan is fired when the user’s finger first touches the screen.
<br />
<br />
touchesMoved occurs with dragging the finger after a touchesBegan[]
<br />
<br />
touchesEnded is fired when the user’s finger is lifted
<br />
<br />
touchesCancelled is an even that happens after touchesBegan but is
interrupted by the system events such as memory warnings or those blue
“network lost” messages I frequently seem to get
<br />
<br />
We do not have access to the UIControlEvents such as TouchUpInside etc because we are not using UIControls.
<br />
<br />
Those are the four methods you have access to to receive touches. For
starters, and to show how we can translate the screen co-ordinates into
world co-ordinates, let’s look first at touchesBegan.
<br />
<br />
Add a new method to the EAGLView implementation. You don’t need to put
it in the header as it’s already defined in the UIResponder super class.
<br />
<br />
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
<br />
<br />
}
<br />
<br />
It’s in this method that we’re adding the new code. First of all, we
need to get the co-ordinates of where the touch event occurred. These
are obtained as easily as:
<br />
<br />
<br />
<pre> UITouch *t = [[touches allObjects] objectAtIndex:0];
</pre>
<pre> CGPoint touchPos = [t locationInView:t.view];
</pre>
<br />
Please read the API reference if you need to know how I got this
information. I’m going to convert these into a “percentage” of the
screen. That is, for half way along either axis, instead of getting a
co-ordinate, I want 0.5 or 50%. That’s as easy as:
<br />
<br />
CGRect bounds = [self bounds];
<br />
CGPoint p = CGPointMake((touchPos.x - bounds.origin.x) / bounds.size.width,
<br />
<pre> (touchPos.y - bounds.origin.y) / bounds.size.height);
</pre>
<br />
<br />
Finally. convert the percentage value to the viewable world
co-ordinates. In order to do this, we need to switch the UITouch
supplied X and Y co-ordinates. Even though the iPhone knows it’s in
landscape format, it still supplies the X and Y co-ordinates as though
it is in portrait view. So the point made above p.x contains our Y value
and vice versa.
<br />
<br />
<br />
<pre> newLocation[0] = 3.0 * p.y;
</pre>
<pre> newLocation[1] = 2.0 * p.x;
</pre>
<br />
Remember above that the X width of the viewable area was 3.0 units? So
if a touch is 50% of the X value (ie w.x = 0.5), then our world
co-ordinate is 3.0 * 0.5 = 240.0. Same applies for the Y value.
<br />
<br />
Once you’ve added the above code, hit “Build and Go”. Click anywhere on
the simulator’s screen area and the point will follow the mouse click.
<br />
<br />
<img alt="5_627_17326f3b5f6fb8c.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_17326f3b5f6fb8c.jpg" />
<br />
<br />
Just so long as you remember, anything supplied to you as an X
co-ordinate by UI-anything will need to be treated as a Y co-ordinate. Y
co-ordinate from UI-anything is really an X co-ordinate.
<br />
<br />
OK, so that’s fine but we can also make it follow our touches by putting the same code into touchesMoved[] like this:
<br />
<br />
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
<br />
<pre> UITouch *t = [[touches allObjects] objectAtIndex:0];
</pre>
<pre> CGPoint touchPos = [t locationInView:t.view];
</pre>
<br />
<br />
<pre> CGRect bounds = [self bounds];
</pre>
<br />
<br />
<pre> // This takes our point and makes it into a "percentage" of the screen
</pre>
<pre> // That is 0.85 = 85%
</pre>
<pre> CGPoint p = CGPointMake((touchPos.x - bounds.origin.x) /
</pre>
<pre> bounds.size.width,
</pre>
<pre> (touchPos.y - bounds.origin.y) /
</pre>
<pre> bounds.size.height);
</pre>
<br />
<br />
<pre> newLocation[0] = 3.0 * p.y;
</pre>
<pre> newLocation[1] = 2.0 * p.x;
</pre>
}
<br />
<br />
That is exactly the same as the touches began function. If you hit
“Build and Go” now, click and drag in the simulator, the point will
follow your mouse clicks.
<br />
NOTE: At the time of writing this, I’ve noticed that when you
drag to the left, it stops just shy of the screen’s edge and won’t allow
you to drag the point all the way to the edge. Also the simulator stops
sending touch events. This is limited to the simulator only based on an
Apple technical note. I’ll check this when I get a chance and post
confirmation here.
<br />
<br />
[Edit: Updated: Seems to work ok on the device but the problem appears
to continue to exist in simulator 3.0. No biggie, just so long as the
device works ok]
<br />
<br />
Finger Co-ordinate to Object Co-ordinate Detection
<br />
I know you guys won’t let me end this without discussing whether
or not the user’s finger has actually touched an object or missed. In
the 2D world, it’s fairly straight forward and we’ll make the changes to
the touchesBegan method.
<br />
<br />
First of all, we need to have some basic understanding from the user’s
point of view. A mouse is very easy to click with but a lumpy finger is a
bit different. Mobile Safari often thinks I’ve clicked on one link
instead of the one I’ve clicked on because once your finger gets over
the general screen area, you can’t see the exact target anymore.
<br />
<br />
Further, our objects are defined very specifically. (1.445, 1.444) is
not (1.444, 1.444) so we have to allow some common sense in the touch
detection otherwise you’ll be clicking for hours on end trying to get
the exact co-ordinate of our point.
<br />
<br />
So, let’s start modifying this so that the touchesMoved method only
responds if we touched the point itself, and not anywhere else on the
screen. First, add a new variable in the definition being:
<br />
<br />
BOOL fingerOnObject;
<br />
<br />
and in the initWithCoder method, set this to a value of NO:
<br />
<br />
fingerOnObject = NO;
<br />
<br />
Now, in the touchesBegan method, delete the last two lines where we
assign values to the newLocation variable, and replace them with the
following:
<br />
<br />
CGRect touchArea = CGRectMake((3.0 * p.y) - 0.1, (2.0 * p.x) - 0.1, 0.2, 0.2);
<br />
<br />
if ((newLocation[0] > touchArea.origin.x) &&
<br />
<pre> (newLocation[0] < (touchArea.origin.x + touchArea.size.width))) {
</pre>
<pre> if ((newLocation[1] > touchArea.origin.y) &&
</pre>
<pre> (newLocation[1] < (touchArea.origin.y + touchArea.size.height))) {
</pre>
<pre> fingerOnObject = YES;
</pre>
<pre> }
</pre>
<pre> }
</pre>
<br />
So what I’ve done here was to create a rectangle which is larger than
the touch area by offsetting both the X and Y co-ordinates by 0.1 units
and making it 0.2 units wide and high. Note that you need to make the
width and height double the size of the X and Y offset value. Then, all
the if statements do is to check that our point is within that touch
area.
<br />
<br />
Then at the start of the touchesMoved method, add the following if statement at the very start of the method:
<br />
<br />
<br />
<pre> if (!fingerOnObject) {
</pre>
<pre> return;
</pre>
<pre> }
</pre>
<br />
So if the fingerOnObject variable is not set to YES, then we don’t care
if the user is dragging their finger. Finally, implement the
touchesEnded method to reset the fingerOnObject variable:
<br />
<br />
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
<br />
<pre> fingerOnObject = NO;
</pre>
}
<br />
<br />
That’s it. Hit “Build and Go” and click on the point to make it move only when you’ve got within the touch area.
<br />
<br />
On first drag, it does jump a bit depending on where you clicked but
partly, that’s ok because the user’s finger is in the way and that would
be partly lost. However, depending on your touch area target, you can
make it smaller or, what I would do would be to move half way in the
touchesBegan method and let the first call to touchesMoved make the
second half which, given their finger is in the way would be enough to
cover that initial jump.
<br />
<br />
Detecting Touches Onto A Square
<br />
Touching a square is easier than touching a point. You know the
origin of the square by the variable newLocation. You know what offset
to use from the square vertex array, as well as the width and the
height. So all you need to do for a square is the comparison.
<br />
<br />
I’ve not done the code as I’m typing this on my lunch break and it’s
nearly time to get back to work!! Given that, I’ll call it quits for
this tutorial.
<br />
<br />
Thanks to All Those Who Commented and Helped in this Series
<br />
So, to the guys who gave me lots of feedback and helped me fix
errors in the text and code from this series and who requested this
topic, its a thank you to you all. Hope this answered at least some of
your questions and got you going in the right direction.
<br />
<br />
As usual, the download link for the code:
<br />
<br />
OpenGLES12.zip
<br />
<br />
This was a bit of fun for me actually and I’ll continue this again
adding some more of the features you’d want for a 2D game as I know
that’s what a lot of you guys are writing.
<br />
<br />
Until the next 2D or 3D instalment, hooroo!
<br />
Simon Maurice
<br />
<br />
<br />
<br />
<br />
Copyright 2009 Simon Maurice. All Rights Reserved.
<br />
The code provided in these pages are for educational purposes
only and may not be used for commercial purposes without Simon Maurice’s
expressed permission in writing. Information contained within this site
cannot be duplicated in any form without Simon Maurice’s expressed
permission in writing; this includes, but is not limited to, publishing
in printed format, reproduced on web pages, or other forms of electronic
distribution.
<br />
<br />
Linking to these pages on other websites is permitted.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-3765795590291919148.post-16550395234677889152012-01-31T04:42:00.000-08:002012-01-31T04:42:52.348-08:00OpenGL ES 13 - Moving in 3D<script type="text/javascript">
<!--
google_ad_client = "ca-pub-9566972421287894";
/* 728x15 LINK AD-1 */
google_ad_slot = "1092558380";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript">
</script>
<br />
<h1 class="firstHeading" style="color: #990000;">
OpenGL ES 13 - Moving in 3D</h1>
<div style="color: #999999;">
<span style="color: #990000;"><b><span style="color: #cc0000;">Disclaimer:</span></b> <b><span style="color: #444444;">This
post and the whole blog is just a copy of
iPhone-OpenGL-ES-tutorial-Series (seems to have )written by Simon
Maurice. This blog does NOT have a written permission or any kind of
permission from the original author (Simon Maurice). Please use it at
your own risk. If you have any complaints regarding any content of this
blog, please let me know I will remove it right away. Just hoping that
it would be helpful for people who are trying to learn OpenGL. Happy
coding!</span></b></span> </div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<b>Copyright 2009 Simon Maurice. All Rights Reserved.
</b></div>
<div style="color: #999999;">
The code provided in these pages are for educational purposes only and
may not be used for commercial purposes without Simon Maurice’s
expressed permission in writing. Information contained within this site
cannot be duplicated in any form without Simon Maurice’s expressed
permission in writing; this includes, but is not limited to, publishing
in printed format, reproduced on web pages, or other forms of electronic
distribution.
</div>
<br />
<a href="https://github.com/mauriceatron" target="_blank">Source codes can be found here </a><br />
<div style="color: #666666;">
<b><br /></b><br />
<b><br /></b></div>
<b><span style="color: #666666;">Original tutorial starts from here onwards..</span></b><br />
<br />
Winter is almost upon us here in Sydney and I’ve been knocked down
with the flu pretty bad. Nope, not the swine flu, just your standard
every winter flu. The last time I had the flu this bad was way back in
college when I was off for a week or so. I remember that time pretty
well because I was reading the Stephen King book “The Stand” and that
book, of course, deals with the super-flu taking over the world.
<br />
<br />
Lucky I’m not a Reg Barclay, or a Rodney McKay otherwise I may have been
experiencing many sensations of anxiety reading that novel and feeling
that way...
<br />
<br />
So the flu, busyness, mother’s day, and a few other things have meant
that I was absent from this blog for a while. So now it’s time to get
things going again.
<br />
<br />
Moving in 3D
<br />
I’m a bit pressed for time so this is going to be a real quick
one here today. What we’re going to do is to start to build a “genuine”
3D world, building everything up from the floor up. However, before I do
that, I do want to introduce moving in a 3D world.
<br />
<br />
Today, what we’re going to do is to add in new code to handle touches to
walk around a “floor”. By using touches, we can turn left, turn right,
walk forwards and walk backwards. No running, dodging or head bobbing as
we walk, they’re easily added. I left them out because I wanted to keep
it easy on me and still allow for people who don’t have an iPod Touch
or iPhone to be able to use this in the simulator.
<br />
<br />
So to get started, download the base for this project which is right here:
<br />
<br />
OpenGL ES 13 - Starter
<br />
<br />
There’s not much coding going on, it’s more about what and why we’re going to do what we do.
<br />
<br />
The Mythical Camera
<br />
I think I’ve mentioned before that even though most of us refer
to the 3D world as looking through a camera, there really is no camera
in OpenGL per se. So if you want to make it appear that you are moving
through a scene, you translate all the objects; the feeling of movement
is created not by moving a camera like in the movies, but by moving the
world relative to the (0, 0, 0) origin.
<br />
<br />
Whilst that sounds like a lot of work, it’s not really. Depending on
your application, there’s a number of ways to do this and many, many
more ways of optimising this for use on really large worlds. I’ll make
mention of that later on.
<br />
<br />
To make things a bit easier, I’ve brought with this tutorial’s project a
handy toy from OpenGL ES’ big brother’s GLU library: the gluLookAt()
function.
<br />
<br />
While I normally don’t refer to OpenGL in this series, I think that most
of you should know what is the GLU library. Unfortunately it is not as
part of the OpenGL ES specification but that doesn’t mean that there are
useful functions in there that we can’t use. Porting these functions is
easy and you don’t need to port the entire library, just the select few
that you need.
<br />
<br />
I’ve lifted the gluLookAt() function from the SGI’s Open Source release,
simply because that is what I had at hand and I know it works. There
are other alternatives if you like. It’s under a non-restrictive licence
so it is OK to use here. Note that the code is not my copyright. The
licence is there in the source code and, if you find it offensive, then
there are other Open Source alternatives you can use.
<br />
<br />
If you want to use a different code base, or port over some other
functions, the main thing that you have to do is change any GLdouble’s
to GLfloat’s, change any associated gl calls to the float version, and
I’d say generally avoid anything which is user interface orientated (any
windowing or input functions). There are many other things you need to
watch out for (such has hardware related) but I find them to be fairly
obvious.
<br />
<br />
I chose the SGI Open Source release as I had that on hand. For
production use go for a free version which is up to date. Don’t use the
Mesa version, even the Mesa guys don’t recommend it. Mesa isn’t up to
date and no longer being actively developed. I know there’s code out
there on the internet for the iPhone based on the Mesa GLU but it cannot
be considered for production use (read: contains bugs).
<br />
<br />
Refer to the link on the Mesa home page titled SGI’s GLU for more
information on why they recommend the SGI or other library than their
own.
<br />
<br />
Using gluLookAt()
<br />
This function is so simple to use and makes perfect sense once you get to know it. Let’s have a look at the prototype:
<br />
<br />
void gluLookAt( GLfloat eyex,
<br />
<pre> GLfloat eyey,
</pre>
<pre> GLfloat eyez,
</pre>
<pre> GLfloat centerx,
</pre>
<pre> GLfloat centery,
</pre>
<pre> GLfloat centerz,
</pre>
<pre> GLfloat upx,
</pre>
<pre> GLfloat upy,
</pre>
<pre> GLfloat upz)
</pre>
<br />
I know 9 parameters can seem a little daunting at times but you just
need to break it down a bit. The first three refer to the eye position,
where you’re looking from. Simply just an X, Y, Z co-ordinate.
<br />
<br />
The second three refer to where you want to look at, again an X, Y, Z co-ordinate trio.
<br />
<br />
Finally, we can group the last three into the “up” vector. We’ll not go
into this trio now as it’s really the first two co-ordinates which
really gets us the effect we want.
<br />
<br />
So, the eye co-ordinates is that mythical camera position. They are in
reference to your world co-ordinates of course. It’s where you are
looking from in your world. The “center” co-ordinates are where you are
facing, your target for want of a better term. If the Y co-ordinate of
the center is higher than your eye Y co-ordinate, you’re looking up. If
the Y is less than the eye Y co-ordinate, you’re looking down and so on.
<br />
<br />
So, in our base project, we have it already set up but we do no
translations. All that happens is that we just draw a floor and look out
into nothing-ness like so:
<br />
<br />
<img alt="5_627_0e77896a74c4b12.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_0e77896a74c4b12.jpg" />
<br />
<br />
If you hit “Build and Go”, that’s what you’ll get.
<br />
<br />
First of all let’s use the glLookAt() function for the first time. Go to
the drawView: method and add the following line right after the call to
glLoadIdentity():
<br />
<br />
<br />
<pre> glLoadIdentity();
</pre>
<pre> gluLookAt(5.0, 1.5, 2.0, // Eye location, look “from”
</pre>
<pre> -5.0, 1.5, -10.0, // Target location, look “to”
</pre>
<pre> 0.0, 1.0, 0.0); // Ignore for now
</pre>
<br />
Hit “Build and Go” again to satisfy yourself that all is working. You should get the following in the simulator:
<br />
<br />
<img alt="5_627_9db79fc65ba39aa.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_9db79fc65ba39aa.jpg" />
<br />
<br />
With a single function call, we’ve shifted our looking from location to
one corner and looking towards the opposite corner. Go ahead and have a
play around with the parameters to glLookAt(). See what happens.
<br />
<br />
Movement in 3D
<br />
Now that you’ve got a grasp on gluLookAt(), let’s use it to
simulate walking on this floor. We’re only going to move really along
two axis (X and Z, in other words no height changes) and use turning in
order to change direction.
<br />
<br />
So, thinking back to the gluLookAt() function, what pieces of
information do you think you’ll need in order to walk in a 3D world?
<br />
<br />
You need:
<br />
<pre> Your viewing location or “eye”
</pre>
<pre> Your facing direction (target) or “centre”
</pre>
<br />
Once you know those two, then you’re ready to handle user input to allow the user to control where you are.
<br />
<br />
Let’s assume that we want to start from the two values for eye and
centre which we used earlier. That’s that part sorted but of course hard
coding them doesn’t allow for movement so let’s first head straight to
the interface and add the following variables:
<br />
<br />
GLfloat eye[3];// Where we are viewing from
<br />
GLfloat center[3];// Where we are looking towards
<br />
<br />
I’ve just named them eye and center but you can call the “position” and
“facing” if you like. It doesn’t really matter; I just chose these
because they match the gluLookAt() function.
<br />
<br />
These two variables hold the X, Y, and Z co-ordinates. I could have
hard-coded the Y value as it’s not changing but hey, why bother.
<br />
<br />
Next hit up the initWithCoder: method. Somewhere in there, initialise
these two variables with the values we used previously for the call to
gluLookAt():
<br />
<br />
<br />
<pre> eye[0] = 5.0;
</pre>
<pre> eye[1] = 1.5;
</pre>
<pre> eye[2] = 2.0;
</pre>
<br />
<br />
<pre> center[0] = -5.0;
</pre>
<pre> center[1] = 1.5;
</pre>
<pre> center[2] = -10.0;
</pre>
<br />
Now, let’s go back to the drawView: method. Change the call to gluLookAt() to:
<br />
<br />
<br />
<pre> gluLookAt(eye[0], eye[1], eye[2], center[0], center[1], center[2],
</pre>
<pre> 0.0, 1.0, 0.0);
</pre>
<br />
Hit “Build & Go” just to satisfy yourself. that everything has worked.
<br />
<br />
Getting Ready for Movement
<br />
Before we can start handling touch events to move around the
world, we need to set a few things up for us in the header file. Switch
to the header file so we can set some defaults and create a new enum
type.
<br />
<br />
First, make some settings for how fast we walk and how fast we turn:
<br />
<br />
<br />
<ol>
<li>define WALK_SPEED 0.005
</li>
</ol>
<ol>
<li>define TURN_SPEED 0.01
</li>
</ol>
<br />
I did find these a little slow but you can change them to suit your own preferences once you’ve seen how they operate.
<br />
<br />
Next, we want to create an enumerated type so we can store exactly what we are doing. Add the following:
<br />
<br />
typedef enum __MOVMENT_TYPE {
<br />
MTNone = 0,
<br />
MTWalkForward,
<br />
MTWAlkBackward,
<br />
MTTurnLeft,
<br />
MTTurnRight
<br />
} MovementType;
<br />
<br />
So, at any point while we’re running the app, we can be standing still
(MTNone), walking forward, backward, turning left, or turning right.
That’s it I’m afraid for this tutorial, nothing too speccy yet.
<br />
<br />
Finally, we define a variable which will hold the current movement:
<br />
<br />
MovementType currentMovement;
<br />
<br />
Don’t forget to go to the initWithCoder: method and set the default value for the currentMovement variable:
<br />
<br />
currentMovement = MTNone;
<br />
<br />
It should default to that being a variable but it’s good practice to do.
<br />
<br />
Get Touchy Feely
<br />
Right, now we’ve got the basics out of the way, let’s start
working on the actual handling of the touches. If you remember back to
the last tutorial, I introduced all four methods for handling touches.
In this tutorial, we’re only going to use two: touchesBegan and
touchesEnded. Just going to keep it simple.
<br />
<br />
In order to determine what action to take, I’ve divided the iPhone’s screen up into four segments:
<br />
<br />
<img alt="5_627_8813f25cc6baa6c.png" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_8813f25cc6baa6c.png" />
<br />
<br />
Sorry for my poor drawing skills. Basically, the screen is 480 pixels
high. Therefore, I can split it up into 3 even segments of 160 pixels.
Pixels 0~160 is for going forward, 320~480 is for going backwards, and
the middle 160 have been halved for the left and the right turning
operations.
<br />
<br />
So, with that in mind, here’s the first of the touches methods:
<br />
<br />
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
<br />
<br />
<br />
<pre> UITouch *t = [[touches allObjects] objectAtIndex:0];
</pre>
<pre> CGPoint touchPos = [t locationInView:t.view];
</pre>
<br />
<br />
<pre> // Determine the location on the screen. We are interested in iPhone
</pre>
<pre> // Screen co-ordinates only, not the world co-ordinates
</pre>
<pre> // because we are just trying to handle movement.
</pre>
<pre> //
</pre>
<pre> // (0, 0)
</pre>
<pre> // +-----------+
</pre>
<pre> // | |
</pre>
<pre> // | 160 |
</pre>
<pre> // |-----------| 160
</pre>
<pre> // | | |
</pre>
<pre> // | | |
</pre>
<pre> // |-----------| 320
</pre>
<pre> // | |
</pre>
<pre> // | |
</pre>
<pre> // +-----------+ (320, 480)
</pre>
<pre> //
</pre>
<br />
<br />
<pre> if (touchPos.y < 160) {
</pre>
<pre> // We are moving forward
</pre>
<pre> currentMovement = MTWalkForward;
</pre>
<br />
<br />
<pre> } else if (touchPos.y > 320) {
</pre>
<pre> // We are moving backward
</pre>
<pre> currentMovement = MTWAlkBackward;
</pre>
<br />
<br />
<pre> } else if (touchPos.x < 160) {
</pre>
<pre> // Turn left
</pre>
<pre> currentMovement = MTTurnLeft;
</pre>
<pre> } else {
</pre>
<pre> // Turn Right
</pre>
<pre> currentMovement = MTTurnRight;
</pre>
<pre> }
</pre>
}
<br />
<br />
So, when the user starts touching, all we do is to record the segment
and then set the variable so that when it comes time to calculating our
new location, we know what we’re doing. Also remember that we don’t have
to put a method definition in our interface; these methods are
inherited.
<br />
<br />
Now for the touchesEnded method:
<br />
<br />
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
<br />
<br />
<br />
<pre> currentMovement = MTNone;
</pre>
}
<br />
<br />
Which is self explanatory. Finally we want a method to process these
touch events. This time we need to add a method declaration in our
interface. Switch to the header file, and add the following method
definition:
<br />
<br />
- (void)handleTouches;
<br />
<br />
Then, switch back and start implementing this method. It’s in this
method that we’re going to calculate our movements through the 3D world.
<br />
<br />
The Theory of Moving in 3D
<br />
I’ve just got to cover some basics first of all. I’m sure you’re
not going to be surprised when I say that this is only one of the ways
in which to calculate new locations in a 3D world given n units of
movement along any vector v. I tried to remember who originally wrote
this but can’t locate the source. I thought it was Arvo but now I don’t
think so. I’ll remember; it was a long time ago that’s for sure and well
before Wolf 3D even made people aware that you can do this in real
time.
<br />
<br />
Let’s look at walking first. If the user tells us to walk forward, we
need to be aware of not only our looking from location, but also our
target location. The looking from location tells us where we are
currently, and our looking to target gives is the direction in which we
are walking.
<br />
<br />
Since a picture tells a thousand words, have a look at the following picture showing our look from point and our look to target.
<br />
<br />
<img alt="5_627_0ab127ec0fdd9ae.png" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_0ab127ec0fdd9ae.png" />
<br />
<br />
With this method of movement, we know the distance between the two
points as an delta of the X co-ordinates and a delta of the Z
co-ordinates. All we need to do to get the new X and Z values is the
multiply the current co-ordinates by “speed” value. Like this:
<br />
<br />
<img alt="5_627_21de5efe9537afc.png" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_21de5efe9537afc.png" />
<br />
<br />
We can easily solve the new co-ordinates for the red point.
<br />
<br />
We start with the deltaX and deltaZ:
<br />
<br />
deltaX = 1.5 - 1.0 = 0.5
<br />
deltaZ = -10 - (- 5.0) = -5.0
<br />
<br />
So now we multiply by our walk speed:
<br />
<br />
xDisplacement = deltaX * WALK_SPEED
<br />
<pre> = 0.5 * 0.01
</pre>
<pre> = 0.005
</pre>
<br />
zDisplacement = deltaZ * WALK_SPEED
<br />
<pre> = -5.0 * 0.01
</pre>
<pre> = 0.05
</pre>
<br />
Therefore the new co-ordinate indicated by the red dot in the image above is: eyeC + CDisplacement
<br />
<br />
<br />
<pre> (eyex + xDisplacement, eyey, eyez + zDisplacement)
</pre>
= (0.005+1.0, eyey,(-10)+ 0.05)
<br />
= (1.005, eyey, -9.95)
<br />
<br />
Now, this method is not without it’s drawbacks. The main issue being the
bigger the distance between the look from and look to locations the
faster the “walk speed” will be. It can be managed though and the cost
of managing this is less CPU intensive than more many other movement
algorithms.
<br />
<br />
For a world this small, the relative size of the difference between our
look from location and our look at location is in reality too large so
do experiment with this when you’re done. You’ll find the relationship
between the distance between the two points, and our defined WALK_SPEED
does make a difference in the apparent walk speed as we move around.
<br />
<br />
Now, let’s look at turning left and right.
<br />
<br />
It’s not uncommon for me to see code where the programmer will dutifully
record the angle at which the scene is being rendered from. Not today.
We know the angle that we are working with because we know the two
points (remember Pythagorus, we have a right angle triangle).
<br />
<br />
Have a look at the following:
<br />
<br />
<img alt="5_627_7653240857dfec5.png" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_7653240857dfec5.png" />
<br />
<br />
As we want to simulate turning around. We don’t actually need to do
anything except to move our looking at or target location around in a
circle. Our definition for TURN_SPEED really is the angle at which we
are turning.
<br />
<br />
Here’s the key to understanding what I have just said. You don’t make
changes to your eye co-ordinates; you change what you’re looking at. By
plotting a new location (ie the next increment of our angle as specified
by TURN_SPEED) on a virtual circle around our eye, you get a new “angle
of rotation”.
<br />
<br />
So, if we are just going to define turning as essentially drawing a
circle around our centre being our eye or looking from location, then we
just need to remember how to draw a circle.
<br />
<br />
In other words, it’s just:
<br />
<br />
newX = eyeX + radius * cos(TURN_SPEED)*deltaX -
<br />
<pre> sin(TURN_SPEED)*deltaZ
</pre>
<br />
newZ = eyeZ + radius * sin(TURN_SPEED)* deltaX +
<br />
<pre> cos(TURN_SPEED)*deltaZ
</pre>
<br />
Handling Touches and Converting these into Movements
<br />
OK, now let ‘s put this into practice.
<br />
<br />
Back in the implementation, we need to start working out from the
touches what to do as far as getting new parameters for gluLookAt().
First, start off with just the method implementation and some basics:
<br />
<br />
- (void)handleTouches {
<br />
<br />
<br />
<pre> if (currentMovement == MTNone) {
</pre>
<pre> // We're going nowhere, nothing to do here
</pre>
<pre> return;
</pre>
<pre> }
</pre>
<br />
First, just check to see if we’re moving. If not, there’s nothing to do.
<br />
<br />
Now, no matter if we’re moving or turning, we need to know the deltaX
and deltaZ values. I’m just storing these in a variable called vector:
<br />
<br />
<br />
<pre> GLfloat vector[3];
</pre>
<br />
<br />
<pre> vector[0] = center[0] - eye[0];
</pre>
<pre> vector[1] = center[1] - eye[1];
</pre>
<pre> vector[2] = center[2] - eye[2];
</pre>
<br />
I did calculate the Y delta but it’s not needed.
<br />
<br />
Now, we need to work out which movement action we need to do. That’s just in a switch statement:
<br />
<br />
<br />
<pre> switch (currentMovement) {
</pre>
<pre> case MTWalkForward:
</pre>
<pre> eye[0] += vector[0] * WALK_SPEED;
</pre>
<pre> eye[2] += vector[2] * WALK_SPEED;
</pre>
<pre> center[0] += vector[0] * WALK_SPEED;
</pre>
<pre> center[2] += vector[2] * WALK_SPEED;
</pre>
<pre> break;
</pre>
<br />
<br />
<pre> case MTWAlkBackward:
</pre>
<pre> eye[0] -= vector[0] * WALK_SPEED;
</pre>
<pre> eye[2] -= vector[2] * WALK_SPEED;
</pre>
<pre> center[0] -= vector[0] * WALK_SPEED;
</pre>
<pre> center[2] -= vector[2] * WALK_SPEED;
</pre>
<pre> break;
</pre>
<br />
<br />
<pre> case MTTurnLeft:
</pre>
<pre> center[0] = eye[0] + cos(-TURN_SPEED)*vector[0] -
</pre>
sin(-TURN_SPEED)*vector[2];
<br />
<pre> center[2] = eye[2] + sin(-TURN_SPEED)*vector[0] +
</pre>
cos(-TURN_SPEED)*vector[2];
<br />
<pre> break;
</pre>
<br />
<br />
<pre> case MTTurnRight:
</pre>
<pre> center[0] = eye[0] + cos(TURN_SPEED)*vector[0] - sin(TURN_SPEED)*vector[2];
</pre>
<pre> center[2] = eye[2] + sin(TURN_SPEED)*vector[0] + cos(TURN_SPEED)*vector[2];
</pre>
<pre> break;
</pre>
<pre> }
</pre>
}
<br />
<br />
And that’s it for the handle touches method. The implementation is just the algorithm we went through earlier.
<br />
<br />
Bringing it All Together
<br />
Head back to the drawView method and add the following line before the call to gluLookAt():
<br />
<br />
<br />
<pre> [self handleTouches];
</pre>
<br />
And that’s it, we’re done!
<br />
<br />
Hit “Build and Go” now! Right now!!
<br />
<br />
Not exactly 6 degrees of freedom but given the above code, you can do
it. It’s not that much more work. The how to is all here in this
tutorial.
<br />
<br />
OK That’s all for Today
<br />
That’s all for today. As per usual here’s the completed code. As
always, you can get me on the email if you have any questions. If
you’ve emailed me and I haven’t responded, it’s been because I’ve been
too sick and will get back to you eventually.
<br />
<br />
As usual, here’s the source code for today’s tutorial:
<br />
<br />
OpenGLES13 Project.zip
<br />
<br />
I’ll ensure the gap between today’s tutorial and the next one won’t be
as long as this last one. Coming up next, we’re going to load up walls
and floors from a map rather than statically, and move around in a world
loaded like a real game level. If you’re really lucky, I’ll show you
object collision detection as well!
<br />
<br />
Until next time, take care. Hooroo!
<br />
Simon Maurice
<br />
<br />
Copyright 2009 Simon Maurice. All Rights Reserved.
<br />
The code provided in these pages are for educational purposes
only and may not be used for commercial purposes without Simon Maurice’s
expressed permission in writing. Information contained within this site
cannot be duplicated in any form without Simon Maurice’s expressed
permission in writing; this includes, but is not limited to, publishing
in printed format, reproduced on web pages, or other forms of electronic
distribution.
<br />
<br />
Linking to these pages on other websites is permitted.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-3765795590291919148.post-34842779583862978852012-01-31T04:36:00.000-08:002012-01-31T04:36:44.930-08:00OpenGL ES 13.5 - Moving in 3D Part 2: Some Theory that I Should Have Explained<script type="text/javascript">
<!--
google_ad_client = "ca-pub-9566972421287894";
/* 728x15 LINK AD-1 */
google_ad_slot = "1092558380";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript">
</script>
<br />
<h1 class="firstHeading" style="color: #990000;">
OpenGL ES 13.5 - Moving in 3D Part 2: Some Theory that I Should Have Explained</h1>
<br />
<div style="color: #999999;">
<span style="color: #990000;"><b><span style="color: #cc0000;">Disclaimer:</span></b> <b><span style="color: #444444;">This
post and the whole blog is just a copy of
iPhone-OpenGL-ES-tutorial-Series (seems to have )written by Simon
Maurice. This blog does NOT have a written permission or any kind of
permission from the original author (Simon Maurice). Please use it at
your own risk. If you have any complaints regarding any content of this
blog, please let me know I will remove it right away. Just hoping that
it would be helpful for people who are trying to learn OpenGL. Happy
coding!</span></b></span> </div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<b>Copyright 2009 Simon Maurice. All Rights Reserved.
</b></div>
<div style="color: #999999;">
The code provided in these pages are for educational purposes only and
may not be used for commercial purposes without Simon Maurice’s
expressed permission in writing. Information contained within this site
cannot be duplicated in any form without Simon Maurice’s expressed
permission in writing; this includes, but is not limited to, publishing
in printed format, reproduced on web pages, or other forms of electronic
distribution.
</div>
<br />
<a href="https://github.com/mauriceatron" target="_blank">Source codes can be found here </a><br />
<div style="color: #666666;">
<b><br /></b><br />
<b><br /></b></div>
<b><span style="color: #666666;">Original tutorial starts from here onwards..</span></b><br />
<br />
I meant to get this tutorial up yesterday but last night was the
first State of Origin match for 2009. For those of you who don’t know,
State of Origin is Rugby League’s superbowl, except that it’s three
matches a year instead of one and is played between the Australian
states of New South Wales (where I live) and Queensland every year.
There’s no better spectacle in the rugby league world and it really
defines the world champions. There’s no other side in the world, club or
national team, that can better the State of Origin winners. So last
night was a matter of a few too many pints after work in the pub and me
not writing this...
<br />
<br />
This is just a quick part 2 to the last tutorial. It’s a bit of a back
to maths basics as some people have emailed me with questions on just
what I meant by drawing a circle and how that relates to turning or
rotating in 3D graphics.
<br />
<br />
I’m starting from scratch, talking right-angled triangles and leading
right through to how that circle and triangle thing make turning
possible with the gluLookAt() function.
<br />
<br />
The Right-Angled Triangle
<br />
No, I’m not going to explain this to you. I need you to know what
a right angled triangle is, what the hypotenuse is, and what an
opposite and adjacent sides are. Just in case in your part of the world,
you use different terminology, I am going to put the picture here just
to be sure that when I say adjacent, you know exactly what I am
referring to:
<br />
<br />
<img alt="5_627_78d40b8b78d8039.png" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_78d40b8b78d8039.png" />
<br />
<br />
I believe in North America, you guys refer to this as a Right Triangle.
That’s just an example of the differences in terminology that I want to
avoid before moving on.
<br />
<br />
I’m not going to explain these, I’m just reminding you of the stuff you
learned in maths in high school but have forgotten because it wasn’t
really applicable.
<br />
<br />
The length of any side can be worked out as easily as:
<br />
<br />
Hypotenuse: hypotenuse² = adjacent² + opposite²
<br />
in C: hypoenuse = sqrt(adjacent*adjacent + opposite*opposite);
<br />
<br />
Adjacent length: cosine (Ɵ) * hypotenuse.
<br />
in C: adjacent = cos(angleInRadians) * hypotenuse;
<br />
<br />
Opposite Length: sine(Ɵ) * hypotenuse
<br />
in C: oppositeLength = sin(angleInRadians) * hypotenuse;
<br />
<br />
The first thing to remember is that the C maths functions work in
radians, not degrees. Don’t worry too much about radians, just remember
how to covert between the two with these two macros:
<br />
<br />
<br />
<ol>
<li>define DEGREES_TO_RADIANS(__ANGLE) ((__ANGLE) / 180.0 * M_PI)
</li>
</ol>
<ol>
<li>define RADIANS_TO_DEGREES(__RADIANS) ((__RADIANS) * 180 / M_PI)
</li>
</ol>
<br />
The easy way to remember the relationship between radians and degrees,
you already know there are 360º in a circle so just remember there are 2
x Pi radians in a circle. Or Pi radians in a semi-circle.
<br />
<br />
With the GCC maths library, M_PI is defined in the math.h file as being
3.14-whatever. It’s not actually to the C standard. The C standard
doesn’t allow for a definition of Pi. You’re technically supposed to
compute it for yourself so you can have it as accurate as possible for
your hardware implementation (it’s just acos(-1) from memory). I’ve
never found a time when I’ve needed to use anything other than the
defined value when coding for many years now.
<br />
<br />
If you can’t remember what is sin(), cos(), & tan() do, don’t worry
about the theory behind them. It’s just like driving a car. You don’t
need to know how an internal combustion engine works to drive a car, you
only need to know how (and when) to use it.
<br />
<br />
Linking this Back to Graphics Programming
<br />
Remember in the last tutorial, I discussed moving in 3D and
turning. I said it’s just like drawing a circle. Let me explain this in
more detail first by showing you how you can draw a circle.
<br />
<br />
Here is a picture of a circle with 2 points of the circle plotted.
<br />
<br />
<img alt="5_627_bf55f923ae5056d.png" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_bf55f923ae5056d.png" />
<br />
<br />
Did you notice something? Have another look if you can’t see it yet.
<br />
<br />
Yes, they are right-angled triangles!
<br />
<br />
So, we can now proceed to plot any point on the circle by knowing only
the radius and angle. Therefore, we can now actually draw a circle.
<br />
<br />
To draw a circle, we know the length of the hypotenuse because that’s
our circle’s radius. All we need to solve is the X and the Y. Given the
functions above and referring to the blue triangle, we can work out the X
co-ordinate as simply the adjacent side and the Y as the opposite side.
<br />
<br />
Of course, the adjacent and opposite sides change in depending on each
quadrant but we don’t need to concern ourselves with that. The code to
draw the circle is just:
<br />
<br />
for (angle = 0; angle < 2*M_PI; angle += 0.01) {
<br />
<pre> point[0] = RADIUS * cos(angle);
</pre>
<pre> point[1] = RADIUS * sin(angle);
</pre>
<pre> glDrawArrays(GL_POINTS, 0, 1);
</pre>
}
<br />
<br />
Just remember that we need to pass sin() & cos() radians, not
degrees. There are 2 * pi radians in 360º. The finer grade to the angle
increment in the for loop, the neater the circle will be. In most cases,
you are likely to use very short lines instead of points to get a nice
neat circle.
<br />
<br />
Linking Circles Back to Turning in the Last Tutorial
<br />
Right. Remember that we used glLookAt() in the last tutorial to
decide where we were going to be be looking at from any point in our 3D
world? I said it was just like drawing a circle.
<br />
<br />
Now, if the user decides to turn in an anti-clockwise direction, the view would be like this:
<br />
<br />
<img alt="5_627_81d42a52c3f7d89.png" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_81d42a52c3f7d89.png" />
<br />
<br />
I’m sure you know where I’m going with this. Right-angled triangles describing a circle. :)
<br />
<br />
So, if you recall, we had our look from position and our look at
position. Our look from position doesn’t change, we just move our look
at position around the circle’s circumference as we rotate.
<br />
<br />
The code was:
<br />
<br />
facing[0] = position[0] + cos(-TURN_SPEED)*vector[0] -
<br />
<pre> sin(-TURN_SPEED)*vector[2];
</pre>
facing[2] = position[2] + sin(-TURN_SPEED)*vector[0] +
<br />
<pre> cos(-TURN_SPEED)*vector[2];
</pre>
<br />
The variable facing is where we are looking at, our look at point on the
circle. The position variable is where we are looking from. It just
looks more complicated because it’s in a 3D world rather than a 2D paper
drawing. Even though I didn’t calculate Y co-ordinates because I fixed
it, you still need to calculate bot the X and Z co-ordinates.
<br />
<br />
That’s it for Today
<br />
I just wanted to cover this topic in a bit of detail for you.
I’ll be putting up the next tutorial in a few days or so which is back
on the main track as I mentioned before. The only issue is not the
rendering but actually loading a file. I tried to get something under a
free licence to use but nothing seemed suitable. Once I’ve got that
done, I’ll get it posted.
<br />
<br />
No code for this tutorial as it is more theory based. Hope that rounds off the last tutorial now.
<br />
<br />
Until next time, hooroo!
<br />
Simon Maurice
<br />
<br />
<br />
<br />
<br />
Copyright 2009 Simon Maurice. All Rights Reserved.
<br />
The code provided in these pages are for educational purposes
only and may not be used for commercial purposes without Simon Maurice’s
expressed permission in writing. Information contained within this site
cannot be duplicated in any form without Simon Maurice’s expressed
permission in writing; this includes, but is not limited to, publishing
in printed format, reproduced on web pages, or other forms of electronic
distribution.
<br />
<br />
Linking to these pages on other websites is permitted.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-3765795590291919148.post-77889502798604981072012-01-31T04:33:00.000-08:002012-01-31T04:33:49.434-08:00OpenGL ES 14 - Blender Models Part 1: Learning Some Blender Internals<script type="text/javascript">
<!--
google_ad_client = "ca-pub-9566972421287894";
/* 728x15 LINK AD-1 */
google_ad_slot = "1092558380";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript">
</script>
<br />
<h1 class="firstHeading" style="color: #990000;">
OpenGL ES 14 - Blender Models Part 1: Learning Some Blender Internals</h1>
<div style="color: #999999;">
<span style="color: #990000;"><b><span style="color: #cc0000;">Disclaimer:</span></b> <b><span style="color: #444444;">This
post and the whole blog is just a copy of
iPhone-OpenGL-ES-tutorial-Series (seems to have )written by Simon
Maurice. This blog does NOT have a written permission or any kind of
permission from the original author (Simon Maurice). Please use it at
your own risk. If you have any complaints regarding any content of this
blog, please let me know I will remove it right away. Just hoping that
it would be helpful for people who are trying to learn OpenGL. Happy
coding!</span></b></span> </div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<b>Copyright 2009 Simon Maurice. All Rights Reserved.
</b></div>
<div style="color: #999999;">
The code provided in these pages are for educational purposes only and
may not be used for commercial purposes without Simon Maurice’s
expressed permission in writing. Information contained within this site
cannot be duplicated in any form without Simon Maurice’s expressed
permission in writing; this includes, but is not limited to, publishing
in printed format, reproduced on web pages, or other forms of electronic
distribution.
</div>
<br />
<a href="https://github.com/mauriceatron" target="_blank">Source codes can be found here </a><br />
<div style="color: #666666;">
<b><br /></b><br />
<b><br /></b></div>
<b><span style="color: #666666;">Original tutorial starts from here onwards..</span></b><br />
<br />
<br />
G’day everyone. Been a while since I last posted. I’ve been knee deep
in Blender and busy writing my own game at the moment so that’s the
reason for the delay.
<br />
<br />
In light of how much time I have at the moment, I’m going to be making a
minor change to the way I’m posting here. Rather than doing lengthy
individual tutorials, I’m going to increase this to a shorter, more
frequent format. Essentially to “chunk things down” as IT consultants
love to say. I’ll get back to posting a few times a week this way.
<br />
<br />
Anyway, like I’ve said, I’ve been having a go at Blender. Normally, I’m a
LightWave user so I’ve had to go and learn Blender which is something
which I just simply was avoiding. In the past two weeks, I’ve got
control of the user interface as well as the scripting side of the
application to export models in formats which are suitable for different
projects.
<br />
<br />
Using Blender isn’t what these tutorials are about. Using the models (or
objects) and scenes you create in Blender is what this is about. I’m
not going to teach you Blender, there are a whole plethora of resources
on the web for that, but I will show you the things which I have learnt
in getting what you create in Blender to work for you.
<br />
<br />
Whilst using Blender is quite well documented (despite the different
nuances of the different versions), documentation for the scripting side
of things is a bit on thin ground. So I will go into some detail on the
exporting from Blender rather than just serve up a “black box” for you
to use.
<br />
<br />
I won’t be presenting exporting to OBJ, 3ds, COLLADA etc formats. Not
only are there plenty of resources on the internet already for working
with these objects, I find them a bit too heavy-weight for the iPhone.
Whilst these are not too bad on the desktop with those powerful
multi-core thingys that we all have these days, loading up the iPhone’s
processor with unnecessary work only makes your app/game/whatever slower
to load, slower to work/play, & limits you more on scene/world size
and so on.
<br />
<br />
All of that leads to a less than ideal user experience.
<br />
<br />
If you want to go down the LightWave/3ds/COLLADA/whatever format anyway.
That’s fine. There’s be enough in this Blender series for you to be
able to work with those files as well. I do have working COLLADA,
LightWave, and 3ds loaders for the desktop which I have written (hence
no copyright problems with providing it here), once I’ve ported them to
the iPhone they will be presented in due course.
<br />
<br />
What I will be showing you first is how to get models or objects out of
Blender whilst writing as little code as possible both on the Blender
export side, and the loading side on the iPhone. Less code = less work.
Less testing. Less debugging. Less problems.
<br />
<br />
I know some of you reading this right now will be thinking: “what,
another file format? Aren’t there enough already?”. Well, here’s the
thing. File formats for modelling and rendering software (Blender,
LightWave, Maya, 3ds etc) are designed to hold far, far more information
than you would use. Further, these file formats are optimised for those
individual applications and care nothing for your application.
<br />
<br />
Handling those files at run time really loads up the processor as we
need to not only read the data in from those files, we need to handle
many special case situations in order to get these into a format
suitable for rendering with OpenGL ES on the iPhone.
<br />
<br />
Do that pre-processing on the desktop, not on the iPhone. Leverage the
multi-core thingy’s power advantage over the iPhone’s CPU. Make the data
ready to go once loaded and you’ll write less code.
<br />
<br />
A Quick Word on Choosing File Formats
<br />
Go to any Blender or similar forum and check out answers to
questions such as “how to get Blender objects into OpenGL”. The answers
are varied depending on what the responder’s preferences are. Some say
3ds. Some say OBJ. Others say they wrote an export script to a C header
file. Then someone will say “OBJ is only easy to use but useless for
real world work as doesn’t support this or that”. Someone else will then
respond saying “all those binary file formats are nasty! Work with
non-proprietary text files”.
<br />
<br />
No wonder there’s such a state of confusion.
<br />
<br />
Ultimately, I think the least accepted answer is the most correct
answer. Roll your own exporter. Or, at least, use one that you can just
load the data with no or limited app side processing.
<br />
<br />
Look, I’m not going to tell you which file format to use. I’m just
trying to show you there’s another way to using someone else’s file
format. If you need your own file format, then you’ll love this. If you
use a heavyweight file format because you want the flexibility of app
side editing, then you’ll at least get an understanding of what those
Blender exporters do. You may need to know the detail as importers and
exporters are not perfect.
<br />
<br />
What I present here is just one man’s point of view, just like someone
responding on a forum recommending someone to use LightWave’s file
format. I’m not saying this is the only way, but at least you can have
enough information to make up your own mind with all the information.
<br />
<br />
Anyway, enough of a rant, let’s get on with it.
<br />
<br />
Blender Models and How to Extract Them
<br />
Blender’s a damn fine tool. Coming from Lightwave (and
Quicksilver & Imagine before that), I’ve found it a little quirky at
first, but, once I got the hang of it, it’s a really efficient
modeller. I’d pay money for it. Fortunately we don’t have to because of
the dedicated work of lots of individuals in the Blender community. It
actually is an Open Source project that I wouldn’t mind contributing to
(and may do).
<br />
<br />
Our primary use of a 3D software package is for modelling rather than
rendering. Blender can give LightWave a run for it’s money in many
regards as far as modelling goes. So much so that ultimately, I think
with perseverance, anything I can do in LightWave, I can do in Blender.
It will just be a different process and take more steps.
<br />
<br />
I should point out that whilst I do have much experience with LightWave,
I’m no artist. I’m sure I only use a small amount of it’s power.
<br />
<br />
Like all good 3D programs, Blender comes with an interface to work with
programatically (not just the graphical environment). This allows us
access to the “internals” of Blender which means we can control the
scene, adjust models and animations, and of course, export them.
Thankfully, the Blender developers chose a common scripting language in
the form of Python rather than roll their own custom scripting language
(eg LightWave uses LScript, or it’s C API can also be used). On less
thing to learn!
<br />
<br />
Before we start, there’s one more thing...
<br />
When it comes to working with Blender, I’ve decided to take a
“from step one” approach. I’m going to show it to you the way that I
learned to do what I now can do with object exporting. There are
probably things which I’ve learnt along the way which I would normally
skip over covering in this blog, resulting in you needing to fill the
gaps yourself.
<br />
<br />
Whilst this may be a little annoying to some who are a little further
along in Blender, I think it’s beneficial because it can show people who
are wanting to write their own scripts or to roll their own custom
formats, just how things work.
<br />
<br />
Getting Started
<br />
OK, here we go at last (so much for shorter blogs). First, I’m
using Blender 2.49 which, at the time of typing, is the latest version.
You will need to have a basic understanding of how Blender works. For
that, I read the Wikibook “Blender 3D: From Noob to Pro” which is an
excellent resource. If you haven’t looked at this yet, don’t walk, run
to check this out.
<br />
<br />
Wikibooks are really good for actively developed Open Source projects.
They are far better than printed books because they’re not etched in
stone. As Blender changes, the books are updated to keep pace with
development. If I needed to pay for access to such a book, I would have
no problems doing so.
<br />
<br />
So, get Blender, learn a few basics of it, then you’re ready for what comes next.
<br />
<br />
The next thing which would help is a little understanding of the
scripting language Python. About 10 years ago, I bought the O’Reilly
book Mastering Python (I think that was the title) to learn the
language. Today, there are plenty of resources at the Python website to
learn.
<br />
<br />
I haven’t used Python as I prefer Perl for scripting but that’s just me;
I’m not much of a fan of object oriented programming (don’t get me
started on C++, that language is pure evil). Despite not having used
Python over the years, it is easy to learn and use. If you don’t know
the language, I think you’ll still find the explanations below quite
self explanatory, especially if you’ve ever used an ALGOL based language
like Pascal, Modula-2 or Modula-3 (now there’s a couple of really fine
languages!).
<br />
<br />
Discovering the Python Interactive Console
<br />
When you launch Blender, you’re presented with a cube which, in
most cases, you delete straight away. In this case, leave it there.
<br />
<br />
Right click up the top of the screen like the image below and click split area.
<br />
<br />
<img alt="5_627_24e4b37fcf8cc00.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_24e4b37fcf8cc00.jpg" />
<br />
<br />
That should split the screen in to two and give you the cube in both.
<br />
<br />
Over to the left half of the screen, that is where I normally set my scripts window, so click on the little hash icon
<img alt="5_627_3146235914e3439.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_3146235914e3439.jpg" />
at the top of the window thingy at the bottom and choose: “Scripts Window”.
<br />
<br />
<img alt="5_627_d1dd8cf45dda07a.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_d1dd8cf45dda07a.jpg" />
<br />
<br />
This gives us a blank grey view in this half of the Blender window.
Finally, create a new interactive console by clicking on the “Scripts”
menu beside the green snake icon, and selecting “System ->
Interactive Python Console”.
<br />
<br />
<img alt="5_627_95734cbe37a665c.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_95734cbe37a665c.jpg" />
<br />
<br />
Righto, now you should have an console which will look something like this:
<br />
<br />
<img alt="5_627_4f8a8aae49aa56e.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_4f8a8aae49aa56e.jpg" />
<br />
<br />
This window is where you can type in Python commands directly to
interact with Blender immediately. This is different to a script only in
that commands are executed one by one rather than in a single run like a
script file.
<br />
<br />
Right now, you can see that there are a few “import” statements. This is
just like Pascal or Modula-2 where you imported units or modules to
bring functionality (such as PrintLn in Pascal’s IO unit). In this case,
the console has imported Blender specific modules. This is one good
thing about Python, you use less imports to get basic things done than
Pascal or Modula-2.
<br />
<br />
Getting Information About Our Current Scene
<br />
First thing to type in is: print Blender.Object.Get() and press return. You should get this:
<br />
<br />
<img alt="5_627_e98480741f71efe.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_e98480741f71efe.jpg" />
<br />
<br />
Note: One thing I should have pointed out, Python is case sensitive so “blender.object.get()” will not work.
<br />
<br />
What we’ve done is asked Blender to give us a print out of all objects
in our current scene. You can see the object heirachy: most objects that
you work with will be accessed via the Blender module. The object we’re
interested in is the one named “Cube”, so grab a reference to it by
entering:
<br />
<br />
cube = Blender.Object.Get(“Cube”)
<br />
<br />
So, already you can see that if we pass nothing to the Get() call, we get all objects. If we name an object, we get that object.
<br />
<br />
The variable cube now points to the cube in the centre of the right hand
side window. Let’s start to discover some information about how Blender
organises it’s objects.
<br />
<br />
An object that we would take an render in OpenGL is made up from Mesh
data (not strictly true I know, but will suffice for now). So, the first
thing we need to do with any object that we grab a hold of is to
actually get the mesh data. This is done with the following command:
<br />
<br />
mesh = cube.getData()
<br />
<br />
Again, this command will not print out any response. The variable “mesh”
now holds all the vertex, normal etc information about the cube.
<br />
<br />
Some, not all, objects are made up of Faces. To see if an object has any faces, you can enter:
<br />
<br />
print len(mesh.faces)
<br />
<br />
<img alt="5_627_9c9e64856094878.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_9c9e64856094878.jpg" />
<br />
<br />
I don’t think I need to point out that a cube does have 6 faces...
<br />
<br />
So we have a cube, and we know that it’s made up of 6 faces. So, for
each face, there should be a set of vertex co-ordinates. Shouldn’t
there? How else can the size and shape of any object be known? It’s
these co-ordinates that we are interested in, so let’s find out
something about them.
<br />
<br />
Here’s going to be the first example of entering a loop in the interactive console. Type in the following:
<br />
<br />
for face in mesh.faces:
<br />
<pre> print len(face.v)
</pre>
<br />
Note that you will need to press return twice to get the for loop to
execute. Also note that after you pressed return after the first line,
the console immediately indented the next line. This is because Python
uses white space indentation instead of curly braces to signify blocks
of code.
<br />
<br />
The code above just says for each face in the object’s array of faces,
held in memory at mesh.faces, print out the quantity of vertices.
Obviously, this is just like Objective-C’s properties where v is the
name of the variable for vertices.
<br />
<br />
You should get the following:
<br />
<br />
<img alt="5_627_b0088c336bfbcc7.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_b0088c336bfbcc7.jpg" />
<br />
<br />
This tells us that the cube’s faces are made up of quads. There are 4
vertices to each face. Therefore to draw this cube in OpenGL ES, we
would need to use either GL_TRIANGLE_STRIP or GL_TRIANGLE_FAN with the
four co-ordinates.
<br />
<br />
Let’s drill down a bit deeper into the cube object and discover information about each face. Type in the following:
<br />
<br />
f = mesh.faces[0]
<br />
<br />
We’ve just created a new variable f which refers to the faces array,
object 0. Just like an Objective-C property once again. Press return and
there is once again no output (unless you’ve made an error).
<br />
<br />
Now type:
<br />
<br />
for vertex in f.v:
<br />
<pre> print vertex.co
</pre>
<br />
Press return twice (once to get a new line, once again to execute the code) and you should get this:
<br />
<br />
<img alt="5_627_264bdf594a09d9c.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_264bdf594a09d9c.jpg" />
<br />
<br />
The output us simply the co-ordinates for each vertex in this cube’s
face. That’s the co-ordinates we need to draw this face. In other words,
this is the vertex array which, so far, we have entered by hand in
Xcode.
<br />
<br />
To get all faces’ vertices in one hit, we could type:
<br />
<br />
for face in mesh.faces:
<br />
<pre> for vertex in face.v:
</pre>
<pre> print vertex.co
</pre>
<br />
And you’ll get this:
<br />
<br />
<img alt="5_627_e302cd5350c41ee.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_e302cd5350c41ee.jpg" />
<br />
<br />
Vertex information isn’t all that’s stored. We also have normals, UV
co-ordinates. For example, to get the normals, instead of saying:
<br />
<br />
print vertex.co
<br />
<br />
type
<br />
<br />
print vertex.no
<br />
<br />
This will give you the vertex normal for each vertex.
<br />
<br />
Not Everything is Quads
<br />
Blender, by default, will organise the object into quads because
this is what 3D artists like. On the other hand, we programmers prefer
triangles because that’s what graphics hardware likes to use in realtime
3D rendering.
<br />
<br />
So, let’s have a look at another object and see how it gets stored in Blender.
<br />
<br />
Move your mouse over to the object display (right hand split in my case)
and press A-key to select all vertices (if they are not already
selected, when selected they are highlighted in pink). Press the forward
delete key and enter to delete the cube. Doesn’t matter if you take the
camera and light source with it.
<br />
<br />
Put the cursor <img alt="5_627_de84e33eff4cd35.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_de84e33eff4cd35.jpg" />
at the intersection of the green and red grid lines so it is centred. If
you have a blue and red grid lines, you’re in a different view so press
the NumbPad-7 key to switch to top view or, go View-Top if you’ve got
one of those new iMac keyboards without the numeric keypad (that would
annoy me to death not having a numeric keypad).
<br />
<br />
Press Space to bring up the menu, choose “Add->Mesh->Circle” like this:
<br />
<br />
<img alt="5_627_cbadca410d763d0.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_cbadca410d763d0.jpg" />
<br />
<br />
In the Add Circle window, click on the “Fill” button so it’s selected
(it’s the darker of the two colours and leave the rest as is.
<br />
<br />
<img alt="5_627_d08ac4278ffa0ad.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_d08ac4278ffa0ad.jpg" />
<br />
<br />
Click OK.
<br />
<br />
This gives us a “filled” circle. Press the TAB key to go into Edit Mode
so we can have a look at how Blender has constructed this. You should
see this:
<br />
<br />
<img alt="5_627_b6c828a8d339345.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_b6c828a8d339345.jpg" />
<br />
<br />
You may notices straight away that the circle is divided into triangles
as opposed to quads. Let’s confirm this in the interactive python
console.
<br />
<br />
Discovering the Circle
<br />
With your mouse over the split with the console (left hand side
for me), let’s start by doing the discovery as we did before with the
cube. Here’s what I entered:
<br />
<br />
<img alt="5_627_f490067f7939729.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_f490067f7939729.jpg" />
<br />
<br />
OK, so quite similar to what we did with the cube. The difference being
is that Blender has constructed the circle out of triangles rather than
quads. This is significant when it comes to drawing the circle in
OpenGL.
<br />
<br />
Note also that there are 32 faces. The circle was built from 32 vertices, each one ended up as a face.
<br />
<br />
You can then go on to discover the vertex co-ordinates etc like we did before.
<br />
<br />
Faceless Objects
<br />
Right, before we start doing a simple export, let’s look at an
object without a faces and see how they’re stored. Delete the circle and
create a new circle using the same method as before but this time, do
not select “Fill”, make sure the “Fill” button is the lighter colour.
<br />
<br />
Press TAB to enter edit mode and you’ll see the difference straight away.
<br />
<br />
<img alt="5_627_388d30abc2665e0.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_388d30abc2665e0.jpg" />
<br />
<br />
Now, without my help, go into the Python console, and discover how many faces there are.
<br />
<br />
Who was surprised by the output. I got this:
<br />
<br />
<img alt="5_627_55406b6cad9fe39.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_55406b6cad9fe39.jpg" />
<br />
<br />
So this is an object without face data. Therefore, in order to get at
the vertex co-ordinates, we can simply access them at the mesh level
rather than the face level.
<br />
<br />
<img alt="5_627_ffc3f9b40000e30.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_ffc3f9b40000e30.jpg" />
<br />
<br />
Quick Wrap Up
<br />
So, you’ve seen some of the different types of objects in Blender
and the differences in exporting them. Fortunately, for our purposes,
this is where we get an advantage over people who “just use” a pre-made
format like LWO, 3ds etc. You see, it is very unlikely that you would
want all three different styles that we’ve seen in a single file.
<br />
<br />
For example, the game that I’m writing at the moment, I only need the
last style for several of my models; objects without faces because I am
extruding them out at run time for different purposes. In other cases, I
need to bring out pre-made quads for other objects which are unrelated
to the simple co-ordinate types which I use different code for. That’s
they key.
<br />
<br />
Even if I used the same file format, I would still need different code
for loading and rendering the two different object types even within the
same program.
<br />
<br />
That’s true even if I used a commercial file format such as 3ds or LWO.
<br />
<br />
By keeping to code short, to the point, and efficient, I get a distinct advantage over the heavyweight file formats.
<br />
<br />
That’s It for This Tutorial
<br />
iWeb has obviously got enough for one blog as it’s slowing down
again as I type. So time to bring this one to a close and to write the
next entry.
<br />
<br />
Don’t worry, it’s uploaded now as I’m uploading them both at the same
time. So for now, no need to wait, let’s load something in part 2...
<br />
<br />
See you then. Hooroo!
<br />
Simon Maurice
<br />
<br />
Copyright 2009 Simon Maurice. All Rights Reserved.
<br />
The code provided in these pages are for educational purposes
only and may not be used for commercial purposes without Simon Maurice’s
expressed permission in writing. Information contained within this site
cannot be duplicated in any form without Simon Maurice’s expressed
permission in writing; this includes, but is not limited to, publishing
in printed format, reproduced on web pages, or other forms of electronic
distribution.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-3765795590291919148.post-64198553523710101542012-01-31T04:30:00.000-08:002012-01-31T04:34:35.632-08:00OpenGL ES 15 - Blender Models Part 2: Loading and Rendering<script type="text/javascript">
<!--
google_ad_client = "ca-pub-9566972421287894";
/* 728x15 LINK AD-1 */
google_ad_slot = "1092558380";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript">
</script>
<br />
<div style="color: #990000;">
<span style="font-size: x-large;"><b></b></span><br />
<h1>
<span style="font-size: x-large;"><b>OpenGL ES 15 - Blender Models Part 2: Loading and Rendering</b></span></h1>
</div>
<div style="color: #990000;">
<br /></div>
<div style="color: #990000;">
<span style="color: black; font-size: small;">I could not take a back up copy of the original tutorial chapter-15. Sorry for the inconvenience!. </span></div>
<div style="color: #990000;">
<br /></div>
<div style="color: #990000;">
<span style="color: black; font-size: small;"><b>These links may help you :</b></span></div>
<div style="color: #990000;">
<br /></div>
<div style="color: #990000;">
<span style="color: black; font-size: small;"><b>1. <a href="http://www.youtube.com/watch?v=t5hhXFcUGw4" rel="nofollow" target="_blank">MD2 File loading and rendering using OpenGL 3.3 - YouTube</a></b></span></div>
<div style="color: #990000;">
<span style="color: black; font-size: small;"><b>2. <a href="http://www.youtube.com/watch?v=2CmxVuFkhVM" rel="nofollow" target="_blank">OpenGL Rendering OBJ Models - YouTube </a></b></span></div>
<div style="color: #990000;">
<span style="color: black; font-size: small;"><b>3. <a href="http://www.codeproject.com/Articles/148034/Loading-and-rendering-Milkshape-3d-models-with-ani" rel="nofollow" target="_blank">Loading and rendering milk-shape 3D models</a></b></span></div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-3765795590291919148.post-66638243636132413442012-01-31T04:25:00.000-08:002012-01-31T04:34:39.669-08:00OpenGL ES 16 - Blender Models Part 3: Textures and UV Mapped Objects<script type="text/javascript">
<!--
google_ad_client = "ca-pub-9566972421287894";
/* 728x15 LINK AD-1 */
google_ad_slot = "1092558380";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript">
</script>
<br />
<div style="color: #990000;">
<span style="font-size: x-large;"><b></b></span><br />
<h1>
<span style="font-size: x-large;"><b>OpenGL ES 16 - Blender Models Part 3: Textures and UV Mapped Objects</b></span></h1>
</div>
<div style="color: #990000;">
<br /></div>
<div style="color: #990000;">
<span style="color: black; font-size: small;">I could not take a back up copy of the original tutorial chapter-16. Sorry for the inconvenience!. </span></div>
<div style="color: #990000;">
<br /></div>
<div style="color: #990000;">
<span style="color: black; font-size: small;"><b>These links may help you :</b></span></div>
<div style="color: #990000;">
<br /></div>
<div style="color: #990000;">
<span style="color: black; font-size: small;"><b>1. <a href="http://www.youtube.com/watch?v=zv86PSPycPA" rel="nofollow" target="_blank">Blender 2.5: UV Mapping - YouTube Link</a></b></span></div>
<div style="color: #990000;">
<span style="color: black; font-size: small;"><b>2. <a href="http://www.videotutorialsrock.com/opengl_tutorial/textures/text.php" rel="nofollow" target="_blank">Texture tutorial Link </a></b></span></div>
<div style="color: #990000;">
<span style="color: black; font-size: small;"><b>3. <a href="http://web.engr.oregonstate.edu/%7Emjb/cs553/Handouts/Texture/texture.pdf" rel="nofollow" target="_blank">OpenGL Texture Mapping PDF </a></b></span></div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-3765795590291919148.post-6534624564981245832012-01-31T04:08:00.000-08:002012-01-31T04:12:58.020-08:00OpenGL ES 17 - Collision Detection<h1 class="firstHeading" style="color: #990000;">
OpenGL ES 17 - Collision Detection</h1>
<br />
<div style="color: #999999;">
<span style="color: #990000;"><b><span style="color: #cc0000;">Disclaimer:</span></b> <b><span style="color: #444444;">This
post and the whole blog is just a copy of
iPhone-OpenGL-ES-tutorial-Series (seems to have )written by Simon
Maurice. This blog does NOT have a written permission or any kind of
permission from the original author (Simon Maurice). Please use it at
your own risk. If you have any complaints regarding any content of this
blog, please let me know I will remove it right away. Just hoping that
it would be helpful for people who are trying to learn OpenGL. Happy
coding!</span></b></span><br />
<span style="color: #990000;"><b><span style="color: #444444;"> </span></b></span> </div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<div style="color: #999999;">
<b>Copyright 2009 Simon Maurice. All Rights Reserved.
</b></div>
<div style="color: #999999;">
The code provided in these pages are for educational purposes only and
may not be used for commercial purposes without Simon Maurice’s
expressed permission in writing. Information contained within this site
cannot be duplicated in any form without Simon Maurice’s expressed
permission in writing; this includes, but is not limited to, publishing
in printed format, reproduced on web pages, or other forms of electronic
distribution.
</div>
</div>
<br />
<br />
<a href="https://github.com/mauriceatron" target="_blank">Source codes can be found here </a><br />
<br />
<b><span style="color: #666666;">Original tutorial starts from here onwards..</span></b><br />
<b><span style="color: #666666;"> </span></b><br />
<br />
It’s been a bit too long since my last post. I’ve been involved in
community theatre and just gone through the season meaning I’ve had very
little or no time to do anything but work, theatre, eat and sleep.
Right now, it’s actually quite late at night (early morning actually,
it’s nearly 2AM) but I’m just going to try and get this out now.
Actually, part of the delay since the season ended last weekend has been
a few decisions on what to write.
<br />
<br />
I wrote everything which loaded a map or 3D world which, using the
walking code in a previous tutorial, you could walk around in but then
move through walls which is quite annoying. So I added collision
detection to it and it resulted in, what would be, a huge tutorial!
Especially the collision detection routines which are more maths than
programming and I’m not really much of a maths fan.
<br />
<br />
Then.... I wrote 3 different collision detection routines trying to
figure out which one to present. M&T, point-plane etc. And even
then.... I began looking at the Blender file thinking about adding
multiple objects, ramps to walk up and just ground to a halt. Things
were getting a bit out of hand so I’ve decided to do the following
roadmap over the next couple of tutorials:
<br />
<br />
<br />
<pre>
</pre>
<pre> 1.This one is collision detection. Damn basic and simple in execution.
</pre>
<pre> 2.Back into Blender maps next tutorial, this time with multiple objects per </pre>
<pre>file so we can create a more interesting static world to work in. That should round </pre>
<pre>up most of the Blender work.
</pre>
<pre> 3.Add in more collision detection to the new world. Not just walking into walls </pre>
<pre>but walking up ramps, stairs, falling off ledges etc. Not just first person but third</pre>
<pre> person as well.
</pre>
<pre> 4.Use the environment to create OpenGL effects such as decals on walls, mirrors,</pre>
<pre>water and fluids etc.
</pre>
<br />
So that’s where I’m headed. The first three are really already
implemented, I just need to pull the separate details out of the
mega-project which I had been adding things to. Number 4 will be
probably a bit more random and feedback driven.
<br />
<br />
Despite leaving the last tutorial in a bit of limbo, I am going to move
on because I don’t think you need me to do a more complex example with
the simple skinning or UV mapping. I do want to load a map file which
we’ll do in the next tutorial, or the one after. It just depends on
whether or not this tutorial is one or two.
<br />
<br />
All I’m going to do is in this tutorial is to go back to working with a
single triangle. Once you learn how to collide with a single triangle,
you can test collision with a million. The principles are the same and
the code can be the same, you perhaps just need to be aware of some
different aspects of the type of collision you’re dealing with. For
example, if you are doing collision testing for walking on a landscape,
you want to raise the height of the walking man as you go up a ramp but
if you’re collision testing for a rocket, you want it to blow that ramp
up!!
<br />
<br />
I’ve chosen a way of collision detection here that is easier to break
down into simple steps than perhaps an optimised algorithm. That’s not
to say it’s not fast, but the fastest way isn’t necessarily either the
easiest to learn or suited to “most” applications. It is going to deal
with triangles but can easily be expanded to deal with an object which
is made up of more than one triangle (treated like a polygon if you
like).
<br />
<br />
In this tutorial, we’re going be using more maths than OpenGL. Actually,
all maths because the OpenGL stuff you’ll already know by now. There
will be no fancy maths notations though, I tend not to use them as I’m a
programmer, not a mathematician. My notation is C. I always prefer to
look at things from a code perspective because I’d have a great deal of
trouble trying to get the compiler to understand this:
<br />
<br />
C = A . B
<br />
<br />
However, the compiler easily understands this:
<br />
<br />
<br />
<ol>
<li>define DOT(v1, v2) (v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2])
</li>
</ol>
<br />
float A = { 0, 1, 0 };
<br />
float B = { 1, 0, 1 };
<br />
float C = DOT(A, B);
<br />
<br />
You get my drift.
<br />
<br />
So, this is going to be about collision detection and I’m going to
pretend it’s a “wall” of some sort. We’re going to move towards the wall
and, instead of passing right though it, we’re going to be forced to
stop.
<br />
<br />
To get you started, here’s the base project for this tutorial if you
want to follow along. I’ll try not to leave any thing out like I think
I’ve done in a few recent tutorials.... ;-)
<br />
<br />
If you load this code up, hit “Build & Go”, you’ll see a red
triangle. Click in the top part of the screen and you’ll start moving
toward it and, eventually, pass right through it. So we’re going to
detect this collision and bring ourselves to a halt. So it’s the walking
man walking into a wall scenario.
<br />
<br />
The First Concepts of Detection
<br />
Just before I start, I do want to apologise in advance for any
stoopid mistakes I make. Like I said at the outset, it’s late here.
<br />
<br />
There are a whole plethora of ways of testing collision detection and if
you’ve done any reading on the subject, you’re bound to have found
things like bounding boxes and other concepts. For the purposes of our
simple testing, we’re just going to do testing on our eye location. In
terms of the code, that’s the “look from” position in what we pass to
gluLookAt().
<br />
<br />
The movement code is identical to that which I wrote in that
“introduction to moving in 3D” tutorial. So, in other words, each time
we execute a “walk forward” movement, we are advanced a certain number
of units determined by our WALK_SPEED.
<br />
<br />
To illustrate what happens in relation to collision detection, have a look at the following picture:
<br />
<br />
<img alt="5_627_b007d98799ae2ac.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_b007d98799ae2ac.jpg" />
<br />
<br />
This is exaggerated of course, but if you can see, the guy on the right
hand side is the position of the man before we execute the walk and he
is short of the wall. After we execute the walk, he is on the other side
(dude on the left). The length of the red arrow represents the
displacement of the moment.
<br />
<br />
To stop the machine gun dude from walking through a wall, I’m going to
break this down into a three step process. Whilst overkill for a single
triangle, it can be scaled up to a larger triangle set and be used for
other types of collision tests.
<br />
<br />
Our test method will perform the following three steps:
<br />
<br />
First, test to see if we cross the plane on which the triangle lies. If not, then there is no chance of collision.
<br />
<br />
Next, since we know we are going to cross the plane, get the intersect point with the plane.
<br />
<br />
Finally, determine if that intersect point is in the triangle we are currently testing.
<br />
<br />
The first test is just a quick and dirty way of deciding whether or not
we need to examine this triangle in detail. If there’s no need, then we
don’t need to waste CPU time testing it with some more expensive
function calls. The second test tells us the exact point of intersection
which, whilst not 100% necessary for a walk test, but it does allow for
the use of this same method with things other than a “go/no go” walk
test. Once we know where we intersect with the triangle’s plane, we then
just need to test to see if that point is inside the triangle.
<br />
<br />
The reason for the last test is just that a plane is a mathematical
concept. It’s a flat surface which extends out in all directions to
infinity. But the triangle only occupies a section of the plane.
<br />
<br />
First Test: Do We Cross the Plane
<br />
Testing crossing of the plane initially requires us to know two
values: our position before the move, and our position after the move.
We already know our position before the move, that’s in the variable
position. So to calculate the destination, we just use the same maths as
we did before to execute a move like this:
<br />
<br />
destLocation[0] = position[0] + vector[0] * WALK_SPEED;
<br />
destLocation[1] = position[1] + vector[1] * WALK_SPEED;
<br />
destLocation[2] = position[2] + vector[2] * WALK_SPEED;
<br />
<br />
Remember that position is just our original location (before we move),
vector is just our movement direction which we already worked out, and
WALK_SPEED is how fast we walk. That’s all back in tutorial 13.
<br />
<br />
So, graphically we now have this information:
<br />
<br />
<img alt="5_627_f0b8c4ecfeaf9f6.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_f0b8c4ecfeaf9f6.jpg" />
<br />
<br />
The location pointed to by the red arrow is our destLocation.
<br />
<br />
Ha-ha! I’ve just realised where I’ve put the arrow in relation to the
rest of his anatomy!! :D Actually, that arrow should be at the eye level
which is where we are testing.
<br />
<br />
Anyway, knowing those two pieces of information, we can test if we cross
the plane. To do that, we have a couple of pieces of information
already at our disposal. Have a look at this picture:
<br />
<br />
<img alt="5_627_59ab52c925e9e1f.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_59ab52c925e9e1f.jpg" />
<br />
<br />
(Apart from moving the red arrow upwards... :), there is a green arrow
coming out of the wall. That is the wall’s normal. The normal of the
triangle which sits on the plane, will have the same normal as the
plane. In our export from Blender, we’ll grab the normal so we won’t
need to calculate this in a more “real” example, but in this tutorial,
I’ve calculated the triangle’s normal in a C function included in the
project. That’s just in case you guys are going to do something like
changing the angle of the triangle which would change the normal.
<br />
<br />
The red arrow is stored in the vector variable. It’s like our machine gun dude’s normal for his current movement.
<br />
<br />
So if we move our machine gun dude closer to the wall, and then project
out our movement so we have the movement before and movement after
locations. For this example, we are going to cause the collision. So
it’s going to look like the picture below where the starting location is
the eye location and the destination location is the yellow sphere:
<br />
<br />
<img alt="5_627_91b70ccbf004fa6.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_91b70ccbf004fa6.jpg" />
<br />
<br />
Location Plane Scalar
<br />
Now we can do a calculation to determine if we cross the plane. Basically what we do is:
<br />
<br />
<br />
<pre>
</pre>
<pre> 1.For the start location, calculate the dot product between our location and the plane’s normal, then add the plane shift constant.
</pre>
<pre> 2.Repeat step 1 for the destination location.
</pre>
<pre> 3.If the sign of the two values are different, we have crossed the plane </pre>
<pre>(ie Result#1 * Result#2 yields a negative value).
</pre>
<br />
Taking step one, we can do it like this:
<br />
<br />
<br />
<ol>
<li>define DOT(v1, v2) (v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2])
</li>
</ol>
<br />
float locationToPlane(float triangle[3], float againstLocation[3]) {
<br />
<br />
<br />
<pre> // Calculate the normal of the triangle. Usually, you'd store this in the data </pre>
<pre>//array.
</pre>
<pre> float normal[3];
</pre>
<pre> triangleNormal(triangle, normal);
</pre>
<br />
<br />
<pre> // Since we are treating the triangle as a plane, we need the plane's special </pre>
<pre>//"D".
</pre>
<pre> float A = normal[0] * triangle[0];
</pre>
<pre> float B = normal[1] * triangle[1];
</pre>
<pre> float C = normal[2] * triangle[2];
</pre>
<pre> float D = A + B + C; // This is the equation for a plane.
</pre>
<br />
<br />
<pre> return DOT(normal, againstLocation) + D;
</pre>
}
<br />
The first two parts of the function could well be pre-calculated
and stored in a data file if the mesh is static (eg a world map). Most
walls and floors don’t move so it would make sense to pre-calculate what
you can.
<br />
<br />
The triangle’s normal is calculated, then we need to work out the
plane’s plane shift constant, represented by the variable D. What I’ve
done there is use the formula for a plane as calculated with a point and
a normal, to solve for the plane shift constant.
<br />
<br />
The formula for a plane with a point on the plane and it’s normal vector is:
<br />
<br />
float planeNormal[3] = { A, B, C };
<br />
float pointOnPlane[3] = {x, y, z};
<br />
<br />
D = A * x + B * y + C * z;
<br />
<br />
PlaneNormal[0] = X value of the plane’s normal. planeNormal[1] = Y value of plane’s normal etc.
<br />
<br />
Does something look familiar back up in the function above? Dot product perhaps?
<br />
<br />
Then, it’s just a matter of the dot product with our location and the
plane’s normal, and add in the plane shift constant (a reference point
of the plane which gives it’s distance from the origin) to get a
reference distance to the plane.
<br />
<br />
That needs to be repeated for the destination location.
<br />
<br />
Due to the use of the plane’s normal and the plane shift constant, if we
have passed through the triangle, we will get a different sign on each
of the two results.
<br />
<br />
Intersection Point with the Plane
<br />
Now, remember that the plane is larger than the triangle: it goes
off to infinity in all directions. So just because we have intersected
the plane, it doesn’t mean that we’ve intersected the triangle.
Therefore, we need to grab the intersection point and then (later) test
it to discover if the intersection point is within the triangle.
<br />
<br />
void intersectWithPlane(float fromPos[], float toPos[], float triangle[], float intPoint[]) {
<br />
<pre> float normal[3];
</pre>
<pre> triangleNormal(triangle, normal);
</pre>
<pre> // Now we want the vector for the line.
</pre>
<pre> float lineVector[3];
</pre>
<pre> lineVector[0] = toPos[0] - fromPos[0];
</pre>
<pre> lineVector[1] = toPos[1] - fromPos[1];
</pre>
<pre> lineVector[2] = toPos[2] - fromPos[2];
</pre>
<br />
<br />
<pre> // Now we can start processing all this information. We have the
</pre>
<pre> // normal of the triangle and a vector
</pre>
<pre> // indicating our direction. Now we can begin to detect if our
</pre>
<pre> // movement will pass through the triangle.
</pre>
<pre> // Start by calculating the dot product between the plane's normal
</pre>
<pre> // and the movement vector
</pre>
<pre> float a = DOT(lineVector, normal);
</pre>
<br />
<br />
<pre> // This should not occur but is here to avoid any possible divide by
</pre>
<pre> // zero errors. Actually, the only
</pre>
<pre> // way this can occur is if we are travelling parallel to the plane
</pre>
<pre> // so it means that this function
</pre>
<pre> // has been called without passing the plane intersect test.
</pre>
<pre> if (a == 0) {
</pre>
<pre> intPoint[0] = fromPos[0];
</pre>
<pre> intPoint[1] = fromPos[1];
</pre>
<pre> intPoint[2] = fromPos[2];
</pre>
<pre> return;
</pre>
<pre> }
</pre>
<br />
<br />
<pre> float b[3];
</pre>
<br />
<br />
<pre> float distance = locationToPlane(triangle, fromPos);
</pre>
<pre> b[0] = lineVector[0] * (distance / a);
</pre>
<pre> b[1] = lineVector[1] * (distance / a);
</pre>
<pre> b[2] = lineVector[2] * (distance / a);
</pre>
<pre> VECT_SUB(intPoint, fromPos, b)
</pre>
}
<br />
<br />
Testing if the Intersection Point with the Plane is within the Triangle
<br />
Finally, we discover if we’ve collided with the actual triangle. Have a look at the picture below:
<br />
<br />
<img alt="5_627_5f31c30d28dd15d.png" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_5f31c30d28dd15d.png" />
<br />
<br />
These are all triangles with different points on it which we could call
intersection points which we calculated in the last step. Note that no
matter where the intersection point is, we can divide the triangle up
into three separate segments formed from the intersection point and the
three vertices of the triangle?
<br />
<br />
So, there will be three segments, regardless of whether or not we’re in
the triangle or not. The first thing we do is to calculate the vectors
for each of these three segments (ie work out the edges as a vector).
Then we work out the length of the segments (needed especially for the
expensive calls at the end) and do a quick elimination test if the we
are on the boundary of the triangle; the boundary is included for
collision detection.
<br />
<br />
The final steps are to determine the cosine of the angle between the two
segments, then add the angle to an angle sum. If the angles sum up to
360º (actually 2 * PI because we pass radians as parameters), we have a
collision. Note that a little bit of tolerance is allowed for due to
rounding with floating point numbers.
<br />
<br />
If the point is outside the triangle, you will still get three angles but they will not sum up to 360º.
<br />
<br />
int pointInTriangle(float triangle[], float intPoint[]) {
<br />
<pre> float angleSum = 0.0;
</pre>
<pre> float segment1[4], segment2[4], segment3[4];
</pre>
<pre> // There will be three segments, one for each point on the triangle
</pre>
<pre> // There are four elements to each array. First three are the vertex,
</pre>
<pre> // fourth one is the length
</pre>
<pre> float *triVert;
</pre>
<pre> triVert = &triangle[3];
</pre>
<pre> VECT_SUB(segment1, triangle, intPoint)
</pre>
<pre> VECT_SUB(segment2, triVert, intPoint)
</pre>
<pre> triVert = &triangle[6];
</pre>
<pre> VECT_SUB(segment3, triVert, intPoint)
</pre>
<br />
<br />
<pre> // Calculate the segment length. Stored in the 4th element
</pre>
<pre> segment1[3] = sqrt(segment1[0]*segment1[0] + segment1[1]*segment1[1] +
</pre>
<pre> segment1[2]*segment1[2]);
</pre>
<br />
<br />
<pre> segment2[3] = sqrt(segment2[0]*segment2[0] + segment2[1]*segment2[1] +
</pre>
<pre> segment2[2]*segment2[2]);
</pre>
<br />
<br />
<pre> segment3[3] = sqrt(segment3[0]*segment3[0] + segment3[1]*segment3[1] +
</pre>
<pre> segment3[2]*segment3[2]);
</pre>
<br />
<br />
<pre> if ((segment1[3] * segment2[3]) <= 0) {
</pre>
<pre> // Point is on boundary so include
</pre>
<pre> return 1;
</pre>
<pre> }
</pre>
<pre> if ((segment2[3] * segment3[3]) <= 0) {
</pre>
<pre> // Point is on boundary
</pre>
<pre> return 1;
</pre>
<pre> }
</pre>
<pre> if ((segment3[3] * segment1[3]) <= 0) {
</pre>
<pre> return 1;
</pre>
<pre> }
</pre>
<br />
<br />
<pre> // Now all the cheap calls are out of the way, time for the heavy
</pre>
<pre> // lifting
</pre>
<pre> float cosAngle = (DOT(segment1, segment2)) / (segment1[3] *
</pre>
<pre> segment2[3]);
</pre>
<pre> angleSum = acos(cosAngle);
</pre>
<br />
<br />
<pre> cosAngle = (DOT(segment2, segment3)) / (segment2[3] * segment3[3]);
</pre>
<pre> angleSum += acos(cosAngle);
</pre>
<br />
<br />
<pre> cosAngle = (DOT(segment3, segment1)) / (segment3[3] * segment1[3]);
</pre>
<pre> angleSum += acos(cosAngle);
</pre>
<br />
<br />
<pre> // Now we look for 360º (in radians). Need to allow a little tolerance
</pre>
<pre> if ((angleSum >= (2*M_PI - 0.0001)) && (angleSum <= (2*M_PI+0.0001))){
</pre>
<pre> return 1;
</pre>
<pre> }
</pre>
<pre> return 0;
</pre>
}
<br />
<br />
That’s it in a nutshell. Well, a rather short nutshell but never the less, works.
<br />
<br />
One final thing, when I tested the code, I found that I would collide
with the triangle but the triangle would no longer appear. A quick check
revealed the problem. When I’ve been setting up the near clipping
plane, I had been using a value of 0.1 as a distance. If we are stopped
with the triangle in-between the near clipping plane and the origin, it
of course isn’t going to get rendered, is it? So I just changed the near
clipping plane to 0.01 to fix it rather than go back and re-work some
other code. Just remember your world co-ordinates are in units that you
decide. 1.0 can mean 1cm, 1inch, 1 metre or 1 yard. So probably the
movement code is a little fine for real world use at the moment. We can
look at that later once we’ve rendered a complete map and see just how
we want things to be.
<br />
<br />
Bringing Everything Together
<br />
The project download below contains all the code. All I’ve done
is put the collision test in the handleTouches method and I only handled
it for walking in the forward direction. The code looks like this:
<br />
<br />
<br />
<pre> switch (currentMovement) {
</pre>
<pre> case MTWalkForward:
</pre>
<br />
<br />
<pre> // First get our destination location as if we have just
</pre>
<pre> // completed the move.
</pre>
<pre> destLocation[0] = position[0] + vector[0] * WALK_SPEED;
</pre>
<pre> destLocation[1] = position[1] + vector[1] * WALK_SPEED;
</pre>
<pre> destLocation[2] = position[2] + vector[2] * WALK_SPEED;
</pre>
<br />
<br />
<pre> // Now check to see if we cross the triangle's plane
</pre>
<pre> float distanceFrom = locationToPlane((float *)triangleVerts,
</pre>
<pre> position);
</pre>
<pre> float distanceAfter = locationToPlane((float *)triangleVerts,
</pre>
<pre> destLocation);
</pre>
<pre> if ((distanceFrom * distanceAfter) < 0) {
</pre>
<pre> // They differ in sign. Therefore, we cross the plane
</pre>
<pre> float intPoint[3];
</pre>
<pre> intersectWithPlane(position, destLocation,
</pre>
<pre> (float *) triangleVerts, intPoint);
</pre>
<pre> if (pointInTriangle((float *)triangleVerts, intPoint)) {
</pre>
<pre> NSLog(@"Collision: %f %f %f", position[0],
</pre>
<pre> position[1], position[2]);
</pre>
<pre> return; // Don't move.
</pre>
<pre> }
</pre>
<pre> }
</pre>
<br />
<br />
<pre> position[0] += vector[0] * WALK_SPEED;
</pre>
<pre> position[2] += vector[2] * WALK_SPEED;
</pre>
<pre> facing[0] += vector[0] * WALK_SPEED;
</pre>
<pre> facing[2] += vector[2] * WALK_SPEED;
</pre>
<pre> break;
</pre>
<br />
The procedure is simple. First calculate our destination location as
though we completed the move. Then test to see if that crosses the plane
of the triangle. If not, then we can just go ahead and move. If yes,
then we need to decide if the “ray” passes through the triangle by first
getting the intersection point with the plane and then test to see if
that point is within the triangle.
<br />
<br />
If it’s in the triangle, we just return out. Otherwise we just perform the walk as per normal.
<br />
<br />
Conclusion
<br />
All of the above seems like a lot of work, especially compared
with say, Moller & Trumbore’s Fast Ray/Triangle Intersection.
Actually it is and I probably should have done Moller and Trumbore!! :)
However, with multiple objects in a 3D world, plane intersection tests
become quite a handy thing to know how to do. I have a hunch we’ll be
using this method into the future but if it becomes a case that we don’t
need to, we’ll switch algorithms.
<br />
<br />
If that seems like overload, then wait until we put it into the context
of a 3D environment where you can see it properly in action.
<br />
<br />
The algorithm will change as there is so much that we can pre-calculate
and speed up. Things like the triangle’s normal vector would come with
the map file, then there’s distance testing using an object tree
structure but that’s all for later.
<br />
<br />
Here is the project for this tutorial: Tutorial17.zip
<br />
<br />
I know it’s a bit rushed but it is late here. Better to get it out than just to sit on it I always say.
<br />
<br />
Until next time, take care.
<br />
Hooroo!
<br />
Simon Maurice
<br />
<br />
<br />
Copyright 2009 Simon Maurice. All Rights Reserved.
<br />
The code provided in these pages are for educational purposes
only and may not be used for commercial purposes without Simon Maurice’s
expressed permission in writing. Information contained within this site
cannot be duplicated in any form without Simon Maurice’s expressed
permission in writing; this includes, but is not limited to, publishing
in printed format, reproduced on web pages, or other forms of electronic
distribution.
<br />
<br />
Linking to these pages on other websites is permitted.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-3765795590291919148.post-56728224306011688692012-01-31T04:03:00.000-08:002012-01-31T04:12:49.160-08:00OpenGL ES 18 - Monkeys on Your Back and Geometric Shapes<script type="text/javascript">
<!--
google_ad_client = "ca-pub-9566972421287894";
/* 728x15 LINK AD-1 */
google_ad_slot = "1092558380";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript">
</script>
<br />
<h1 class="firstHeading" style="color: #990000;">
OpenGL ES 18 - Monkeys on Your Back and Geometric Shapes</h1>
<div style="color: #999999;">
<span style="color: #990000;"><b><span style="color: #cc0000;">Disclaimer:</span></b> <b><span style="color: #444444;">This
post and the whole blog is just a copy of
iPhone-OpenGL-ES-tutorial-Series (seems to have )written by Simon
Maurice. This blog does NOT have a written permission or any kind of
permission from the original author (Simon Maurice). Please use it at
your own risk. If you have any complaints regarding any content of this
blog, please let me know I will remove it right away. Just hoping that
it would be helpful for people who are trying to learn OpenGL. Happy
coding!</span></b></span> </div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<br /></div>
<br />
<div style="color: #999999;">
<b>Copyright 2009 Simon Maurice. All Rights Reserved.
</b></div>
<div style="color: #999999;">
The code provided in these pages are for educational purposes only and
may not be used for commercial purposes without Simon Maurice’s
expressed permission in writing. Information contained within this site
cannot be duplicated in any form without Simon Maurice’s expressed
permission in writing; this includes, but is not limited to, publishing
in printed format, reproduced on web pages, or other forms of electronic
distribution.
</div>
<br />
<a href="https://github.com/mauriceatron" target="_blank">Source codes can be found here </a><br />
<br />
<div style="color: #666666;">
<b><br />
</b></div>
<b><span style="color: #666666;">Original tutorial starts from here onwards..</span></b><br />
<br />
<br />
The past week....
<br />
I’ve made a sudden change of direction. You see, I’d been working
on a game for the iPhone, sort of like a flying game where you could do
such god-like things as deform landscape in real time etc. Well, I got
it up and running on the device with most of the engine complete and it
hummed along at 60fps (how I got there is another story, one I’ll
certainly be sharing) with many frames needing to deal with over 100k
textured triangles.
<br />
<br />
The only trouble is, it just didn’t work as a game.
<br />
<br />
The whole lack of keyboard issue reared it’s ugly head. Whilst I brought
in an on screen keyboard (icon board actually) as needed, it was just
too awkward without the physical keyboard. The small screen really
didn’t bring out the best in the rendering engine either. Ditch that
game idea for the iPhone at least. I’ll press that code into service on
the desktop though some time soon, it will work there.
<br />
<br />
Never mind. You need to bounce back from these things and that very
night I did. With the next day off work and my wife already fast asleep
in bed (it was around 11:30PM), I realised the kind of game for the
iPhone that I should be writing were the kinds of games that I like to
play on the device.
<br />
<br />
So it was like: 2D scrolling shoot ‘em up, here I come!
<br />
<br />
By the time I went to bed at 4AM (chased into bed actually as wifey woke
up wondering where I was), I had most of the engine going. I was
loading a depth sorted tile based map, parallax scrolling was working
nicely, sprites were taking form nicely; you get the idea. It was coming
together.
<br />
<br />
Now I just need to make a game out of it. I already know what I’m doing
for the game design, I just need to make all the graphics for it which
I’m doing at the moment. Right now, I think I’ll have it completed in
around 4 weeks and submitted into the App Store. Once that’s done, I’ll
release the code under the GNU GPL and post it here somewhere.
<br />
<br />
I know I’m on the right track because, despite the fact I haven’t even
finished this game, I’ve already got the next one going in my head so
once that’s done, the next one will be under development straight away.
Actually, it already is. Again, watch this space for the code release.
<br />
<br />
In the meantime, I’ve got to cut down the volume of emails that I’m
getting. I get way too many at the moment so I’ve started a FAQ page on
this website and I’ve got to get a few common questions out of the way.
And here’s one monkey that I want to get off my back:
<br />
<br />
Those Damn Geometric Shapes Questions
<br />
I do get quite a volume of emails from this tutorial series as
you can probably imagine. I don’t like the idea of putting myself out
there on a pedestal, pontificating to all but “don’t call me if you
don’t understand”. That doesn’t work for me so that’s why I have no
problem putting an email me link on every tutorial.
<br />
<br />
The only thing has been this constant damn request for “how do I do a
cylinder”, or “how about a sphere”, or “how about a
penta-poly-hecto-hedrogram”?
<br />
<br />
So far I’ve been fairly resolute. You see, drawing these kind of objects
is not a graphics problem, it’s a maths problem. Also, you tend not to
draw a lot of these from code in the “real world”; most would come from
say Tutorial #15 where you would load up a sphere in Blender and export
that bad boy. To hell with computing that on the fly unless you really
need to.
<br />
<br />
I can’t remember the last time I drew a sphere programatically. I know I
have done it but I think it was several years ago. It’s more classroom
stuff rather than something useful. It’s like going through a tutorial
and drawing a polyhedron. Yeah, nice, but whoopeee. What are you going
to do with it?
<br />
<br />
Like I said, the requests keep coming so that’s what’s happening here. White flag is raised and today I’m covering:
<br />
<br />
Circles
<br />
Well, circles are actually really handy to know. That’s all a
cylinder is: just a circle which has been extruded along it’s length.
One of the challenging things for the beginner in OpenGL ES is that you
can’t just call up a circle like you can a triangle or quad. You’ve got
to create it. For that, you do need to know primarily what you want in
the circle (eg filled, unfilled, smoother etc) and a little bit of maths
principles and you can get what you want.
<br />
<br />
I know I’ve mentioned this before, but this is how you can describe a
circle with origin (centre) at point (Cx, xy), with radius r:
<br />
<br />
x = Cx + r * cos(angle)
<br />
y = Cy + r * sin(angle)
<br />
<br />
So, given only the origin and the radius, you can draw a circle. It’s at
this point that usually people stop me and say “yes but you also need
to know the angle which is a final unknown in the equation!”. No, it’s
not unknown. You simply start at the angle of a 0º (actually radians)
and continue to trace out the circumference.
<br />
<br />
So we can start by filling out a vertex array with the individual points on the circumfrence of a circle that we want to plot.
<br />
<br />
<br />
<pre> GLfloat points[20];
</pre>
<pre> glEnableClientState(GL_VERTEX_ARRAY);
</pre>
<pre> glVertexPointer(2, GL_FLOAT, 0, points);
</pre>
<pre> glColor4f(1.0, 1.0, 0.0, 1.0);
</pre>
<pre> glPointSize(10.0);
</pre>
<pre> int i = 0;
</pre>
<pre> float radius = 0.75;
</pre>
<pre> for (float angle = 0; angle < 2*M_PI; angle += 0.630) {
</pre>
<pre> points[i++] = radius * cos(angle);
</pre>
<pre> points[i++] = radius * sin(angle);
</pre>
<pre> }
</pre>
<pre> glDrawArrays(GL_POINTS, 0, 10);
</pre>
<br />
<img alt="5_627_74a34bd823d5eb6.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_74a34bd823d5eb6.jpg" />
<br />
<br />
Obviously, by shrinking the increment on the angle variable will bring the points closer together.
<br />
<br />
<br />
<pre> GLfloat points[722];
</pre>
<pre> glEnableClientState(GL_VERTEX_ARRAY);
</pre>
<pre> glVertexPointer(2, GL_FLOAT, 0, points);
</pre>
<pre> glColor4f(1.0, 1.0, 0.0, 1.0);
</pre>
<pre> glPointSize(3.0);
</pre>
<pre> int i = 0;
</pre>
<pre> float radius = 0.75;
</pre>
<pre> for (float angle = 0; angle < 2*M_PI; angle += (2*M_PI)/360) {
</pre>
<pre> points[i++] = radius * cos(angle);
</pre>
<pre> points[i++] = radius * sin(angle);
</pre>
<pre> }
</pre>
<pre> glDrawArrays(GL_POINTS, 0, 360);
</pre>
<br />
By rendering 360 2.0 sized points, you’d get this:
<br />
<br />
<img alt="5_627_a224bd0c96a47b3.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_a224bd0c96a47b3.jpg" />
<br />
<br />
I actually made the array 722 because I was concerned about a rounding
situation which would give an extra point so I just made the array large
enough just in case. In reality, 720 was fine.
<br />
<br />
Switching the code back to the original circle and changing GL_POINTS to GL_LINE_LOOP, you get this:
<br />
<br />
<br />
<pre> GLfloat points[20];
</pre>
<pre> glEnableClientState(GL_VERTEX_ARRAY);
</pre>
<pre> glVertexPointer(2, GL_FLOAT, 0, points);
</pre>
<pre> glColor4f(1.0, 1.0, 0.0, 1.0);
</pre>
<pre> int i = 0;
</pre>
<pre> float radius = 0.75;
</pre>
<pre> for (float angle = 0; angle < 2*M_PI; angle += 0.630) {
</pre>
<pre> points[i++] = radius * cos(angle);
</pre>
<pre> points[i++] = radius * sin(angle);
</pre>
<pre> }
</pre>
<pre> glDrawArrays(GL_LINE_LOOP, 0, 10);
</pre>
<br />
<img alt="5_627_8eb76338ac820a9.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_8eb76338ac820a9.jpg" />
<br />
<br />
Filled Circles
<br />
So far, nothing really too hard. Just say we want to fill the
circles in above? This is where our old rendering friend GL_TRIANGLE_FAN
comes into play.
<br />
<br />
Now, remember passing the parameter GL_TRIANGLE_FAN will render the
first three points of the vertex array as a triangle, then render a
triangle formed by each subsequent vertex and the first vertex.
<br />
<br />
So, to make the circle above filled, the first point in the vertex array
becomes the centre. The first two points form the first triangle. Then
each indvidual vertex after will form a triangle with the centre and the
previous vertex.
<br />
<br />
So this code:
<br />
<br />
<br />
<pre> GLfloat points[22];
</pre>
<pre> glEnableClientState(GL_VERTEX_ARRAY);
</pre>
<pre> glVertexPointer(2, GL_FLOAT, 0, points);
</pre>
<pre> glColor4f(1.0, 1.0, 0.0, 1.0);
</pre>
points[0] = 0.0;// Circle centre
<br />
points[1] = 0.0;
<br />
<pre> int i = 2;
</pre>
<pre> float radius = 0.75;
</pre>
<pre> for (float angle = 0; angle < 2*M_PI; angle += 0.630) {
</pre>
<pre> points[i++] = radius * cos(angle);
</pre>
<pre> points[i++] = radius * sin(angle);
</pre>
<pre> }
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 0, 11);
</pre>
<br />
generates this:
<br />
<br />
<img alt="5_627_9635a9e4d5355b3.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_9635a9e4d5355b3.jpg" />
<br />
<br />
Errrrrr.... opps! Looks like Pacman! In reality, if you think about it,
we need another vertex co-ordinate in order to get GL_TRIANGLE_FAN to
fill the circle completely. We need to link the last vertex back with
the first vertex to make that last triangle. So what we need to do is:
<br />
<br />
<br />
<pre> GLfloat points[24];
</pre>
<pre> glEnableClientState(GL_VERTEX_ARRAY);
</pre>
<pre> glVertexPointer(2, GL_FLOAT, 0, points);
</pre>
<pre> glColor4f(1.0, 1.0, 0.0, 1.0);
</pre>
points[0] = 0.0;// Circle centre
<br />
points[1] = 0.0;
<br />
<pre> int i = 2;
</pre>
<pre> float radius = 0.75;
</pre>
<pre> for (float angle = 0; angle < 2*M_PI; angle += 0.630) {
</pre>
<pre> points[i++] = radius * cos(angle);
</pre>
<pre> points[i++] = radius * sin(angle);
</pre>
<pre> }
</pre>
points[22] = points[2];
<br />
points[23] = points[3];
<br />
<pre> glDrawArrays(GL_TRIANGLE_FAN, 0, 11);
</pre>
<br />
<img alt="5_627_6c6f01464591f23.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_6c6f01464591f23.jpg" />
<br />
<br />
Whilst that’s not the only way to fill a circle, it’s probably the
easiest and best on most OpenGL platforms. Other methods such as using
lines don’t really work that well on the iPhone as the line drawing
engine is fairly poor (might be improved on the 3GS though).
<br />
<br />
Circles into Cylinders
<br />
Now, we can take a circle and turn it into a cylinder fairly
easily. I’m going to switch to a 3D view though just to make the depth
work for me.
<br />
<br />
So, use this project base:
<br />
<br />
<br />
<pre> GLfloat frontCircle[30]; // Now have X, y, Z
</pre>
<pre> GLfloat rearCircle[30];
</pre>
<pre> float radius = 0.5;
</pre>
<pre> GLfloat origin[2] = {
</pre>
<pre> 0.0, 0.0
</pre>
<pre> };
</pre>
<pre> int i = 0;
</pre>
<pre> for (float angle = 0; angle < 2*M_PI; angle += 0.630) {
</pre>
<pre> frontCircle[i] = origin[0] + radius * cos(angle); // X
</pre>
<pre> frontCircle[i+1] = origin[1] + radius * sin(angle); // Y
</pre>
<pre> frontCircle[i+2] = 4.0; // Z, somewhere off behind the viewer
</pre>
<br />
<br />
<pre> rearCircle[i] = frontCircle[i];
</pre>
<pre> rearCircle[i+1] = frontCircle[i+1];
</pre>
<pre> rearCircle[i+2] = -4.0; // Off in front of us
</pre>
<pre> i += 3;
</pre>
<pre> }
</pre>
<pre> rota += 0.05;
</pre>
<pre> if (rota > 360) rota = 0;
</pre>
<pre> glRotatef(1, 0.0, 1.0, 0.0);
</pre>
<br />
<br />
<pre> glEnableClientState(GL_VERTEX_ARRAY);
</pre>
<pre> glVertexPointer(3, GL_FLOAT, 0, frontCircle);
</pre>
<pre> glColor4f(0.0, 0.0, 1.0, 1.0);
</pre>
<pre> glDrawArrays(GL_LINE_LOOP, 0, 10);
</pre>
<br />
<br />
<pre> glVertexPointer(3, GL_FLOAT, 0, rearCircle);
</pre>
<pre> glColor4f(1.0, 0.0, 0.0, 1.0);
</pre>
<pre> glDrawArrays(GL_LINE_LOOP, 0, 10);
</pre>
<br />
<br />
<pre> GLfloat line[6];
</pre>
<pre> glVertexPointer(3, GL_FLOAT, 0, line);
</pre>
<pre> glColor4f(1.0, 1.0, 0.0, 1.0);
</pre>
<pre> for (int i = 0; i < 10; i++) {
</pre>
<pre> line[0] = frontCircle[i*3];
</pre>
<pre> line[1] = frontCircle[i*3+1];
</pre>
<pre> line[2] = frontCircle[i*3+2];
</pre>
<pre> line[3] = rearCircle[i*3];
</pre>
<pre> line[4] = rearCircle[i*3+1];
</pre>
<pre> line[5] = rearCircle[i*3+2];
</pre>
<pre> glDrawArrays(GL_LINES, 0, 2);
</pre>
<pre> }
</pre>
<br />
<br />
OK, so that’s a wireframe cylinder, you can fill it in by drawing
the circles like we did last time and the sides can be filled by
converting them into quads like so:
<br />
<br />
<br />
<pre> GLfloat frontCircle[33]; // Now have X, y, Z
</pre>
<pre> GLfloat rearCircle[33];
</pre>
<pre> float radius = 0.5;
</pre>
<pre> GLfloat origin[2] = {
</pre>
<pre> 0.0, 0.0
</pre>
<pre> };
</pre>
<pre> int i = 0;
</pre>
<pre> for (float angle = 0; angle < 2*M_PI; angle += 0.630) {
</pre>
<pre> frontCircle[i] = origin[0] + radius * cos(angle); // X
</pre>
<pre> frontCircle[i+1] = origin[1] + radius * sin(angle); // Y
</pre>
<pre> frontCircle[i+2] = 2.0; // Z, somewhere off behind the viewer
</pre>
<br />
<br />
<pre> rearCircle[i] = frontCircle[i];
</pre>
<pre> rearCircle[i+1] = frontCircle[i+1];
</pre>
<pre> rearCircle[i+2] = -2.0; // Off in front of us
</pre>
<pre> i += 3;
</pre>
<pre> }
</pre>
<br />
<br />
<pre> frontCircle[30] = frontCircle[0];
</pre>
<pre> frontCircle[31] = frontCircle[1];
</pre>
<pre> frontCircle[32] = frontCircle[2];
</pre>
<pre> rearCircle[30] = rearCircle[0];
</pre>
<pre> rearCircle[31] = rearCircle[1];
</pre>
<pre> rearCircle[32] = rearCircle[2];
</pre>
<br />
<br />
<pre> glPushMatrix();
</pre>
<pre> rota += 0.5;
</pre>
<br />
<br />
<pre> glTranslatef(0.0, 0.0, -10.0);
</pre>
<pre> glRotatef(rota, 0.0, 1.0, 0.0);
</pre>
<pre> glVertexPointer(3, GL_FLOAT, 0, frontCircle);
</pre>
<pre> glColor4f(0.0, 0.0, 1.0, 1.0);
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 0, 10);
</pre>
<br />
<br />
<pre> glVertexPointer(3, GL_FLOAT, 0, rearCircle);
</pre>
<pre> glColor4f(1.0, 0.0, 0.0, 1.0);
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 0, 10);
</pre>
<br />
<br />
<pre> GLfloat side[12];
</pre>
<pre> glVertexPointer(3, GL_FLOAT, 0, side);
</pre>
<pre> glColor4f(1.0, 1.0, 0.0, 1.0);
</pre>
<pre> // For each section we need to form a quad with the two front and two rear sets </pre>
<pre>//of vertices
</pre>
<pre> for (int i = 0; i < 10; i++) {
</pre>
<pre> side[0] = frontCircle[i*3];
</pre>
<pre> side[1] = frontCircle[i*3+1];
</pre>
<pre> side[2] = frontCircle[i*3+2];
</pre>
<br />
<br />
<pre> side[3] = frontCircle[i*3+3];
</pre>
<pre> side[4] = frontCircle[i*3+4];
</pre>
<pre> side[5] = frontCircle[i*3+5];
</pre>
<br />
<br />
<pre> side[6] = rearCircle[i*3];
</pre>
<pre> side[7] = rearCircle[i*3+1];
</pre>
<pre> side[8] = rearCircle[i*3+2];
</pre>
<br />
<br />
<pre> side[9] = rearCircle[i*3+3];
</pre>
<pre> side[10] = rearCircle[i*3+4];
</pre>
<pre> side[11] = rearCircle[i*3+5];
</pre>
<pre> glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
</pre>
<pre> }
</pre>
<pre> glPopMatrix();
</pre>
<br />
Add in a translate and a rotate to spin that bad boy and you’ll get:
<br />
<br />
<img alt="5_627_69dda3fe6fc9d4a.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_69dda3fe6fc9d4a.jpg" />
<br />
<br />
Obviously, you wouldn’t re-generate all the geometry every frame in a
“real world” situation but I just decided to make it all a bit easier
and drop it in there. Again, also all you need to do to make it smoother
is just to increase the quantity of “sections” which make up the
circle(s).
<br />
<br />
Notice the errors in the above image? You can see part of the red circle
which should be fully occluded and the yellow on the left of the blue
circle. I am assuming they are rounding errors which aren’t too much of a
problem. Personally, I wouldn’t bother fixing them because in a real
world situation, you’re going to have more than just a flipping cylinder
being rendered. If someone playing a game has the time to notice little
faults like that, then you’ve really done something wrong in your
game-play design.
<br />
<br />
Ellipses
<br />
These fellas are just as easy to draw. All you need to realise
about an ellipse is that it’s a circle with two radii. From memory,
they’re called the major axis and the minor axis rather than radius. I
tend not to think of them as a major or minor, typically just as an X
radius and a Y radius. So have a look at this:
<br />
<br />
<br />
To draw an ellipse, you do:
<br />
<br />
<br />
<pre> GLfloat xradius = 0.25;
</pre>
<pre> GLfloat yradius = 0.5;
</pre>
<br />
<br />
<pre> GLfloat point[2];
</pre>
<br />
<br />
<pre> glEnableClientState(GL_VERTEX_ARRAY);
</pre>
<pre> glVertexPointer(2, GL_FLOAT, 0, point);
</pre>
<pre> glColor4f(1.0, 1.0, 0.0, 1.0);
</pre>
<pre> glPointSize(2.0);
</pre>
<br />
<br />
<pre> for (float angle = 0; angle < 2*M_PI; angle += 0.1) {
</pre>
<pre> glPushMatrix();
</pre>
<pre> point[0] = cos(angle)*xradius;
</pre>
<pre> point[1] = sin(angle)*yradius;
</pre>
<pre> glTranslatef(point[0], point[1], 0.0);
</pre>
<pre> glDrawArrays(GL_POINTS, 0, 1);
</pre>
<pre> glPopMatrix();
</pre>
<pre> }
</pre>
<br />
Which gives you:
<br />
<br />
<img alt="5_627_d91508d048b45b3.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_d91508d048b45b3.jpg" />
<br />
<br />
So in other words, it’s just like drawing a circle except you have to deal with two radii.
<br />
<br />
Everything else applies just like we did for a circle.
<br />
<br />
That’s It for Today
<br />
I’ll leave it here for now. I’ll be back again in a couple of
days with something else. I didn’t bother with a final tutorial project
here because all you need to do is insert the code in the draw method in
the project above.
<br />
<br />
Until next time, hooroo
<br />
Simon Maurice
<br />
<br />
<br />
Copyright 2009 Simon Maurice. All Rights Reserved.
<br />
The code provided in these pages are for educational purposes
only and may not be used for commercial purposes without Simon Maurice’s
expressed permission in writing.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-3765795590291919148.post-83480272411278713352012-01-31T03:52:00.000-08:002012-01-31T04:12:39.184-08:00OpenGL ES 19 - So You Wanna Be an iPhone Games Programmer?<script type="text/javascript">
<!--
google_ad_client = "ca-pub-9566972421287894";
/* 728x15 LINK AD-1 */
google_ad_slot = "1092558380";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript">
</script>
<br />
<h1 class="firstHeading" style="color: #990000;">
OpenGL ES 19 - So You Wanna Be an iPhone Games Programmer? </h1>
<br />
<br />
<div style="color: #999999;">
<span style="color: #990000;"><b><span style="color: #cc0000;">Disclaimer:</span></b> <b><span style="color: #444444;">This post and the whole blog is just a copy of iPhone-OpenGL-ES-tutorial-Series (seems to have )written by Simon Maurice. This blog does NOT have a written permission or any kind of permission from the original author (Simon Maurice). Please use it at your own risk. If you have any complaints regarding any content of this blog, please let me know I will remove it right away. Just hoping that it would be helpful for people who are trying to learn OpenGL. Happy coding!</span></b></span> </div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<br /></div>
<div style="color: #999999;">
<b>Copyright 2009 Simon Maurice. All Rights Reserved.
</b></div>
<div style="color: #999999;">
The code provided in these pages are for educational purposes only and
may not be used for commercial purposes without Simon Maurice’s
expressed permission in writing. Information contained within this site
cannot be duplicated in any form without Simon Maurice’s expressed
permission in writing; this includes, but is not limited to, publishing
in printed format, reproduced on web pages, or other forms of electronic
distribution.
</div>
<br />
<a href="https://github.com/mauriceatron" target="_blank">Source codes can be found here </a><br />
<div style="color: #666666;">
<b><br /></b></div>
<b><span style="color: #666666;">Original tutorial starts from here onwards..</span></b><br />
<br />
Sorry for the corny title but back in 1990/91, Amiga Format magazine
had a series where Dave Jones, shared the code of how he wrote the first
level of the game Menace. Whilst I really didn’t learn much about the
coding side of the articles, I did learn a lot when it came down to how
he thought as a programmer and what decisions he made when it came to
actually implementing a game.
<br />
<br />
This tutorial sub-series is going to be a serious nod to Dave Jones and
to that great magazine for what they did back in the days. I’m going to
show you how to code a “Menace-like” game for the iPhone. Hence the
title of this post, I’m pretty sure Amiga format used to splash “So you
wanna be a games programmer” on their covers for one of their series
with
a pro game developer (they had several from memory). Like I said, this
is a nod to them.
<br />
<br />
From my last post, I said that I would do a A-to-Z of programming a 2D
shoot ‘em up. Well, that’s exactly what this set of tutorials is going
to be about. At the end of it, we should have a working game which can
be submitted to the App Store for release. Yes, that even means I’ll
show you how to create levels, game graphics, and deal with iPhone
issues such as what to do when the phone rings.
<br />
<br />
<img alt="5_627_f42a25b206b91ed.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_f42a25b206b91ed.jpg" />
<br />
<br />
<br />
Since this is going to be a “proper” project series, there really is going to be a few things you’re going to need. They are:
<br />
<br />
<br />
<pre>
</pre>
<pre> 1.An iPhone or iPod touch: Since most of the control will be via the </pre>
<pre>accelerometer, you can’t avoid having a device. I won’t do “click on screen” controls.</pre>
<pre> Well, maybe but I’m not promising anything.
</pre>
<pre> 2.A developer certificate from Apple: Yes, you’ve got to spend the US$99 to get </pre>
<pre>the finished code onto a device for testing. This goes back to the first requirement.
</pre>
<pre> 3.Needless to say, you’ll also need a Mac with Apple’s Xcode tools with iPhone </pre>
<pre>SDK 3.0 installed.
</pre>
<pre> 4.Apple’s X11 installed: we’ll be using the GIMP for some graphics work so </pre>
<pre>you’ll need X11. Sorry, I don’t do Photoshop. So yes, you’ll need the GIMP, Blender, </pre>
<pre>and a couple of other pieces of free software. Nothing that will cost you money to </pre>
<pre>get.
</pre>
<pre> 5.Music: this is an unknown at the moment. I think all you’ll need is GarageBand.</pre>
<pre> To be honest, I haven’t worked this out yet. Maybe I’ll use a MOD player library as</pre>
<pre> a nod to the Amiga.
</pre>
<br />
You won’t need everything at first. You’ll only need one and two once we
get to the accelerometer support. I know I’ve avoided that before
because I know not all of you have a device to test on. To do this
properly, you’re going to need one. Just go spend the money when the
time comes.
<br />
<br />
I’ll give you some warning one or two tutorials in advance on those cost-item requirements.
<br />
<img alt="5_627_775ac26a855607b.jpg" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_775ac26a855607b.jpg" />
<br />
BTW: Don’t ask me how to get the code onto a jailbroken iPhone. I
don’t go there. Just pay the $99 and code things properly. It’s part of
being a professional (even if you are working out of your bedroom).
<br />
<br />
<br />
This code will be released under the GNU GPL v3. You know that means
<br />
<br />
<img alt="5_627_368d600aa80ef2d.png" src="http://www.cocoachina.com/bbs/attachment/Fid_5/5_627_368d600aa80ef2d.png" />
<br />
<br />
if you
release something based on this code you need to release the source code
as well. If you don’t want to, then just contact me and we can probably
make a deal but I don’t think it should come to that. I really just
think it’s best if you release the source code as per the GNU GPL. I
used to think that I had to keep my source code secret. Back in the
days, I used to release Amiga demos as binary only but these days I
think different. There really is very little in a game engine that
another professional coder can’t figure out by looking at your game so I
don’t think there’s much point of treating most code as a national
secret.
<br />
<br />
I know I said I’d wait until I got my own game done but, in reality,
I’ve not got the time at the moment to do both a tutorial series and
work on my own things. I do have a life. So I think for now, I’m going
to do this as an open project and write it all in public so I can kill
two birds with one stone. Either that or I won’t bother as I am writing a
desktop Mac app at the moment which for me is a bit more interesting
because it’s not a graphics project. It’s actually something for my main
hobby: beer.
<br />
<br />
But that’s another story.
<br />
<br />
The Basic Game
<br />
The game that’s going to be created is a side scrolling shoot em
up. Like I said, it’s going to based on the Amiga game Menace as a nod
to Dave Jones’ Amiga format tutorial series which is the first that I
can remember seeing and taking it seriously. I haven’t got all the
details worked out yet but I’ve pilfered enough code from the other 2D
game engine I’m writing to get started and we’ll be able to finish this
in a couple of weeks.
<br />
<br />
For those of you who don’t know the game, there’s a youtube video
showing someone play the entire game through (AmigaLongPlay.com or
something like that). If you search for “Menace Amiga Longplay” you
should get it. That will show you the kind of game that we’re going to
develop.
<br />
<br />
Essentially, we’re going to use OpenGL ES 1.1 in an orthographic
projection. All this means is that depth is ignored so we’ll just render
everything using the painter’s algorithm where objects are drawn from
the rear to the front. We’ll use parallax scrolling to provide the
illusion of depth.
<br />
<br />
Of course, there will be sprites, lasers, explosions, collision
detection, point scoring, baddies; all the stuff you’d expect in a shoot
em up game. Now how can I forget to mention end of level bosses as
well!!
<br />
<br />
The Tutorial Series
<br />
I knew at the outset when I came up with the idea of doing a game
and releasing the source code, I didn’t really want to just chuck the
project file out there. I’d rather go through it and discuss the code.
This means more work for me but I think it will be a better learning
experience for those who are interested. Also, it means that if I don’t
get around to releasing the game, the code is not lost (not like the
other projects I’ve done over the years).
<br />
<br />
Essentially, there will be weekly posts. I’m going to post a new
tutorial every Monday night, Sydney time so I’m keeping everything
regular. That way I can work on this over the weekend, getting the posts
ready for Monday night. I’m not sure how many tutorials will make this
series up but I think it would be at least a dozen or so. Probably more.
<br />
<br />
I will do some screen casts. For the graphics development only but I’m
not sure how I’m going to make that happen at the moment. I don’t think
discussing code works in a screen cast but showing how to make graphics
in the GIMP does translate well.
<br />
<br />
Just on the graphics side, I’m not really that crash hot on graphics.
However, I do know how to do it after looking over the shoulders of real
graphics artists over the years. I just do the same things as they do,
just not as well so I think the techniques are correct, but let down by
my lack of artistic talent. Normally I do enough just for quick and
dirty prototyping, or for non-critical elements. Whilst my creations may
not turn out that good, with persistence or some artistic talent on
your part, you should be able to get something quite a bit better than
what I can create.
<br />
<br />
After some thought, I did decide to implement it in Objective-C, even
though I do prefer to code in plain old C. As much as I prefer not to
use OOP, this is an iPhone series and you need to know Objective-C at
some level to work on the iPhone and, besides that, many people like to
use OOP by choice. I’d rather present something in Objective-C rather
than the evil that is C++ which is a language I straight out refuse to
use.
<br />
<br />
So, for this series, Objective-C it is. For the next series, I think I might just do it mostly in C.
<br />
<br />
Having said that, I’m not going to make everything into a class. You do
see that all the time so there’s going to be no creating of a tile class
and then sub-classing half the planet etc. I’ll have to use some of
course but I’m not going to go crazy about it. I don’t really care what
anyone says; Objective-C class creation and messaging is more expensive
than a standard C function call so I want to keep all that to a minimum.
We’re shooting for 60fps here after all.
<br />
<br />
The Very Short Brief for the Game
<br />
I’m not going to write a design document. This will probably
evolve over time but let’s set the ground rules for what is absolutely
promised to be delivered for this title:
<br />
<br />
<br />
<pre>
</pre>
<pre> 1.A full game, with graphics and audio, all developed here in public. Code </pre>
<pre>released under the GNU GPL.
</pre>
<pre> 2.2D side scrolling game with parallax scrolling. Running at 60 frames per </pre>
<pre>second on the iPhone 3G. The iPod Touch and iPhone 3GS should be fine if the iPhone </pre>
<pre>3G can hit 60fps.
</pre>
<pre> 3.Fully handle the possibilities on the iPhone which means phone calls and other</pre>
<pre> events which may interrupt game play like battery warnings.
</pre>
<pre> 4.Correctness. We’ll write proper code which cleans up after itself etc. It must</pre>
<pre> be suitable for publishing in the App Store so that means only official API code </pre>
<pre>only. </pre>
<pre> </pre>
<pre></pre>
<pre> 5.We’ll produce a fun game. I hope.
</pre>
<pre> 6.Multiple levels loaded on the fly. Right now, I’m shooting for around 5 or 6 </pre>
<pre>but we can do one fully then only the differences for the other levels I guess. </pre>
<pre>We can work it out later. At least be able to handle multiple levels.
</pre>
<pre> 7.Saving games, whilst not normal for arcade games, this is recognition of the </pre>
<pre>iPhone user’s needs. Also pausing and other basic game functions.
</pre>
<pre> 8.A high score table. Gotta have that right?
</pre>
<pre> 9.No off the shelf game engines. I’ll tell you why below.
</pre>
<br />
So that’s the brief as it stands right now. The order in which things are implemented will be exposed over time.
<br />
<br />
Why do I say no game engines? Look there’s multiple reasons why not to
and of course “why to” reasons as well. Personally, I don’t believe in
them. I’m not going to bag them out here or their authors. In fact, I
praise the work of the guys doing open source engines for their
generosity towards the programming community and giving of their time to
do so.
<br />
<br />
I’m going this route because it’s what I want to do. I don’t just want
to bolt graphics onto someone else’s code and not truly understand
what’s happening in the background. I don’t just want to make a clone of
a clone of a clone (although you’re of course not just limited to that
with an engine).
<br />
<br />
I want to program and design. That’s what I want to do.
<br />
<br />
Like I’ve said before, this is only one man’s opinion. You’re free to
make up your own mind. I’ve chosen this route because it’s what I want
to do. If you chose the path I’m presenting here, then you’re right. If
you’d rather go the engine route, then you’re also right.
<br />
<br />
The only wrong route is to start one path and then chop and change every
time you strike a problem and think the other route might actually be
easier.
<br />
<br />
Suck it up big boy. Make a decision and stick to it.
<br />
<br />
Killing is My Business...
<br />
...and business is good. Yeah! The old Megadeth song!
<br />
<br />
I’ve gotta give the project a name and, without thinking too hard about
the title, I’m just going to call the project Draconia. Anyone who
played Menace will know why.
<br />
<br />
So, now, let us begin...
<br />
In the previous post, I discussed what we’re doing here in this
series. If you haven’t read it, it’s basic background material for this
little series on making a side scrolling 2D shoot ‘em up in the style of
Menace back on the Amiga.
<br />
<br />
As I recounted a couple of posts ago, I went straight from doing a 3D
game into a 2D shoot ‘em up when I realised my 3D concept wasn’t going
to translate into the iPhone’s world and that I went from zero to a
working scrolling 2D engine in a couple of hours. Those couple of hours
were just as frustrating as they were exciting because it was fun doing
things in the old ways. Although the lack of planar graphics with
hardware scrolling meant that I had to try and re-learn a few things
that I hadn’t used in years.
<br />
<br />
So, today I’m covering the base project, getting the background drawn, and then scrolling the background.
<br />
<br />
Creating the Base Project
<br />
All I did was to use Apple’s default OpenGL ES template and
delete the code for the spinning cube thing. Then I set up the project
to run in landscape mode with viewport the size of the iPhone’s display
because I thought that might be easier. Right now, having gone further, I
may change that to get some smoother effects at a later date. For now,
it just means that if we put a 16x16 unit quad on the display, it will
be 16x16 pixels in size when displayed on the iPhone’s screen.
<br />
<br />
I’ve covered that before I think so I’m just putting the starter project
here for your downloading pleasure. I think it’s all fairly self
explanatory.
<br />
<br />
The project download: Draconia Start.zip
<br />
<br />
I do want to point out a couple of things.
<br />
<br />
In the project build configuration, I’ve put in a GCC definition for
DEBUG whilst building a Debug configuration. This just allows me to do
things like:
<br />
<br />
<br />
<ol>
<li>ifdef DEBUG
</li>
</ol>
<pre> double startTime = CFAbsoluteTimeGetCurrent();
</pre>
<ol>
<li>endif
</li>
</ol>
<br />
////// Code goes here
<br />
<pre> frameCounter++;
</pre>
<ol>
<li>ifdef DEBUG
</li>
</ol>
<pre> double endTime = CFAbsoluteTimeGetCurrent();
</pre>
<pre> totalRenderingTime += (endTime - startTime);
</pre>
<ol>
<li>endif
</li>
</ol>
<br />
So on a release build, anything that I don’t want in a finished product is not included.
<br />
<br />
The above code is another thing. It’s from the drawView method. I’ve
just popped that in there so I can keep a track of frame rates. Like I
said in the last post, I’m shooting for 60fps so I’d like to know where
I’m at each build I do for device testing; just need to know how much
headroom I have for “special effects” or how far behind I am.
<br />
<br />
It the average frame rate gets logged to the console on exit. I did some
testing some time ago and found that using CFAbsoluteTimeGetCurrent()
was faster than using the Unix time calls so I’m assuming it’s getting
routed through there somehow. I didn’t look into it as it’s no major
drama.
<br />
<br />
I did decide to make the frame counter a global variable because since I
can keep a fixed frame rate, I can use that for animation times and
other things. Just like we did in the good old days in the vertical
blanking interrupt!
<br />
<br />
The other things I did was put the device into the landscape mode with
the home button to the right and hide the status bar just like I did
before.
<br />
<br />
I’ve made an executive decision not to handle rotation. I’ve found most
iPhone games don’t support it anyway and it will just keep our code
cleaner.
<br />
<br />
Just to make things really clear, this is the main part of the code which organises the OpenGL ES view to be in landscape mode:
<br />
<br />
glMatrixMode(GL_PROJECTION);
<br />
glLoadIdentity();
<br />
glRotatef(-90.0, 0.0, 0.0, 1.0);
<br />
glOrthof(0.0, 480.0, 0.0, 320.0, -1.0, 1.0);
<br />
<br />
Basically, what the above does is just rotate the current projection
around the X axis by 90º. Remember, OpenGL talks in degrees whereas the C
maths functions use radians.
<br />
<br />
The call to glOrthof() is where we set up the projection matrix. I think
last time I used different values for that but this time, we get the
range of visible X co-ordinate values as between 0 and 480, and the Y
co-ordinate values as between 0 and 320.
<br />
<br />
Therefore, the co-ordinates (0, 0) are the bottom left of the iPhone’s
display and (480, 320) are the top right. Again, I’ve covered that
before.
<br />
<br />
Drawing and Scrolling a Background
<br />
As I’ve mentioned, we’re using parallax scrolling in this game.
Parallax scrolling is where you draw multiple layers of “scenery” and
scroll them at different speeds to provide an illusion of depth. If you
don’t know what I mean by parallax scrolling, then head off to Wikipedia
and read this: <a class="external free" href="http://en.wikipedia.org/wiki/Parallax_scrolling" rel="nofollow" title="http://en.wikipedia.org/wiki/Parallax_scrolling">http://en.wikipedia.org/wiki/Parallax_scrolling</a>
<br />
<br />
Sorry to my international readers, that’s the English page. There’s a nice animation there which saves me creating one.
<br />
<br />
Now, we’re going to have ultimately three layers in the game: a
background, the layer with all the sprites (ie player, enemies, laser
shots etc), and a foreground layer. They will be drawn in that order
just like the painter’s algorithm.
<br />
<br />
The background in this first level we’re going to create is going to be
just a single texture repeated over and over again rendered using a tile
map. Whilst that may sound uninteresting, if you think back to classic
arcade games, many used this technique. It’s because the player’s eyes
are typically focused on himself, the enemies on screen, and the
foreground detail.
<br />
<br />
The background is important from the position that it has to be there.
Like mood music in a restaurant: it’s not important what’s playing, it
just needs to be at a soft volume, in the background. It just needs to
be there.
<br />
<br />
Creating a Background Texture
<br />
The first thing that we’ll do is to create the texture for the
background tile. Here’s the chance for me to introduce my screen casting
skills (or lack thereof) because I’m creating it in the GIMP. I think
doing things in the GIMP or Blender really lends itself to using the
screen casting approach. Code, on the other hand, I think works better
than text.
<br />
<br />
This is my first shot so it’s probably not very good (I haven’t recorded
at the time of typing) but it will show you the techniques that I would
use to create this.
<br />
<br />
Find that video here:
<br />
At this point the video is not up yet. It’s crappier than what I thought my my worst attempt would be!
<br />
<br />
I will post it, if not before the next tutorial, at the same time. So that’s Monday 17th August at the latest.
<br />
<br />
In the meantime, use the texture in the starter project.
<br />
<br />
If you haven’t got the GIMP or don’t feel confident yet in using it,
then the texture is in the starter project above or you can substitute
your own (because mine is pretty average).
<br />
<br />
First Class to Create: The Level Class
<br />
Like I said in the last post, I’m not going to create classes for
everything. I did decide to create a Level class which basically
equates to a game level.It will be this class which does most of the
rendering. Initially, I was just calling C functions so I didn’t create a
class for this. In this case, I think a class will work quite well,
especially as we can do some custom things for different levels.
<br />
<br />
First of all, right click on the Classes group and create a new
sub-class of NSObject and call the class “Level” (sorry, couldn’t think
of a more imaginative name).
<br />
<br />
So now, the background is just going to be an array or grid of tiles
which will be scrolled across the rear of the screen at a fixed rate. In
order to help you visualise what I did, have a look at this image.
<br />
<br />
<br />
<br />
This is a pretend view of the iPhone’s screen. The green gridlines
represent the tiles which will contain the texture we created earlier.
<br />
<br />
The first thing I want to point out right now is that I decided to treat
these as individual quads, just like I would have before. It’s quite
possible to draw these as triangle strips but under a tile based engine,
that’s not really going to cut it if I wanted more than a single
texture for the background. In fact, I did when I was first coding this.
It’s definitely required for the foreground.
<br />
<br />
Whilst drawing each as individual quads is definitely not the fastest
way, I’ll worry about optimising that if I need to. Remember, things
only have to be fast enough, not perfectly optimised. I have a rant
about optimisations below :)
<br />
<br />
Secondly, I decided to make the grid sized 64x64 pixels. That will be 64x64 units when we’re sending vertex arrays to the GPU.
<br />
<br />
Thirdly, note that the grid is 9 tiles wide meaning there are 2.5 grids
off the visible area to the right of the screen. It could only be 8
tiles wide but it’s 9 because I wanted the game engine to be a couple of
tiles ahead when scrolling as we will be using that far right tile as a
trigger for some events. All will be made clearer when it gets used;
it’s just planning ahead.
<br />
<br />
Finally, you can see the grid is only 4 tiles high leaving space at the
bottom. This is where we will put our console containing power levels,
score and the fire button.
<br />
<br />
The first thing we need to do is to create the vertex array for the tile
grid. Remember that we’re only doing the background at the moment. In
the Level.h file you just created, the first thing you need to add in
are a couple of OpenGL header files so you have access to the data types
and function prototypes. Add these lines:
<br />
<br />
<br />
<ol>
<li>import <OpenGLES/EAGL.h>
</li>
</ol>
<ol>
<li>import <OpenGLES/ES1/gl.h>
</li>
</ol>
<ol>
<li>import <OpenGLES/ES1/glext.h>
</li>
</ol>
<br />
Yes, I know I don’t need all 3, but if you just add in all 3, you’re making life easier on yourself if you extend this code.
<br />
<br />
Next, since I was experimenting at the time, I did create some defines
to allow some flexibility and store some constants. Here are the ones
for the background:
<br />
<br />
// First some defines on the background
<br />
<ol>
<li>define BACKGROUND_TILE_SIZE 64
</li>
</ol>
<ol>
<li>define BACKGROUND_COLUMNS 9
</li>
</ol>
<ol>
<li>define BACKGROUND_ROWS 4
</li>
</ol>
<ol>
<li>define BACKGROUND_SCROLL_SPEED 0.3
</li>
</ol>
<ol>
<li>define BACKGROUND_WRAP_X BACKGROUND_TILE_SIZE*(BACKGROUND_COLUMNS-1)-BACKGROUND_SCROLL_SPEED
</li>
</ol>
<ol>
<li>define BACKGROUND_TILE_COUNT BACKGROUND_ROWS*BACKGROUND_COLUMNS
</li>
</ol>
<br />
BACKGROUND_TILE_SIZE is the pixel size of each background tile. They are square so they are 64x64.
<br />
BACKGROUND_COLUMNS and BACKGROUND_ROWS are exactly that. The count of the number of rows and columns for the background.
<br />
BACKGROUND_SCROLL_SPEED is a constant which controls how many
pixels per frame the background scrolls. In other words 0.3 pixels per
1/60 of a second. This is where some jitter is caused by rounding errors
but I’d rather fix that later than tie myself into whole numbers now.
Yes, I already have a solution.
<br />
BACKGROUND_WRAP_X is the X co-ordinate in world space where we wrap the tiles to. I’ll cover this one in detail later.
<br />
BACKGROUND_TILE_COUNT is exactly that, how many quads there are in the background.
<br />
<br />
I went to all the trouble with those because I didn’t know how the tile
sizes etc would play out so that way I was able to keep things fairly
generic later on.
<br />
<br />
Now, in the class interface, add some instance variables like so:
<br />
<br />
<br />
<pre> GLfloat backVerts[BACKGROUND_TILE_COUNT*8]; // Vertex array for all the </pre>
<pre> //background tiles</pre>
<pre></pre>
<pre> GLuint backgroundTexture; // OpenGL holder for the texture ID
</pre>
<pre> GLbyte backSTs[8]; // Only need one quad as they are all the same.
</pre>
<br />
In the interests of late night laziness, I didn’t try and eradicate
duplicated vertices. I’m not that interested in trying to optimise the
engine at the moment so I just created an array to hold 4 vertices for
all the tiles (2 values per co-ordinate remember. Also, we don’t need to
worry about the texture co-ordinates for each quad, one set of texture
co-ordinates will be fine will be fine.
<br />
<br />
Finally, some methods:
<br />
<br />
- (id)initWithFile:(NSString *)filename;
<br />
- (void)scroll;
<br />
<pre> 1.
</pre>
<pre> -(void)drawBackground;
</pre>
- (void)loadTexture:(NSString *)fileName intoLocation:(GLuint)location;
<br />
<br />
initWithFile receives a filename which will eventually hold the level
information such as where the enemies are, how many there are, the
foreground tile map etc. scroll is just called to scroll the entire
level map (only background at the moment). drawBackground should not
require any explanation.
<br />
<br />
At some point through this series, I’ll move the texture management into
a centralised location to avoid 20 different loadTexture methods spread
across a bunch of classes. Centralised texture management is a Good
Thing(tm).
<br />
<br />
Creating the Background Vertex Array
<br />
I’ve just looked at my code again. Pointer arithmetic!! Someone’s
going to send me hate mail if I presented this. Truth is, I like
pointer arithmetic. It makes life so much easier and it’s only when you
don’t understand what you’re doing do you introduce bugs. Mind you, I’m
probably only good at it because I spent so many hours (years) writing
assembly language code (remember HiSoft Devpac Assembler? Go you good
thing!).
<br />
<br />
So, I’ll be presenting the non-pointer-arithmetic version here. It’s a
bit late and I should have written this code better but it works.
<br />
<br />
Into Level.m and go to initWithFile and create the method there just like this:
<br />
<br />
- (id)initWithFile:(NSString *)filename {
<br />
<pre> if (self = [super init]) {
</pre>
<br />
<br />
<pre> // Code for initialising the background
</pre>
<pre> GLfloat xpos = 0.0;
</pre>
<pre> GLfloat ypos = BACKGROUND_TILE_SIZE; // Offset the base to allow for the </pre>
<pre> //console
</pre>
<br />
<br />
<pre> for (int i = 0; i < BACKGROUND_COLUMNS; i++) {
</pre>
<pre> for (int j = 0; j < BACKGROUND_ROWS; j++) {
</pre>
<pre> int idx = i*BACKGROUND_ROWS*8 + (j*8); // Just to make the </pre>
<pre> //code neater
</pre>
<br />
<br />
<pre> backVerts[idx] = xpos; // Top left
</pre>
<pre> backVerts[idx+1] = ypos + BACKGROUND_TILE_SIZE;
</pre>
<pre> backVerts[idx+2] = xpos; // Bottom left
</pre>
<pre> backVerts[idx+3] = ypos;
</pre>
<pre> backVerts[idx+4] = xpos + BACKGROUND_TILE_SIZE; // Bottom right
</pre>
<pre> backVerts[idx+5] = ypos;
</pre>
<pre> backVerts[idx+6] = xpos + BACKGROUND_TILE_SIZE; // Top right
</pre>
<pre> backVerts[idx+7] = ypos + BACKGROUND_TILE_SIZE;
</pre>
<br />
<br />
<pre> ypos += BACK_GRID_SIZE; // Increase the y value so we create a tile </pre>
<pre> //above
</pre>
<pre> }
</pre>
<pre> ypos = BACKGROUND_TILE_SIZE; // Reset the Y value to the bottom </pre>
<pre> //(above the console)
</pre>
<pre> xpos += BACKGROUND_TILE_SIZE; // Move to the next column across
</pre>
<pre> }
</pre>
<br />
Basically what’s happening is that I’ve used two for loops. For each
column, I build one square for each row. In other words, I’m just
building the tile array starting with the tile that is at the bottom
left of the screen, then moving to the one above it etc. Then I do the
next column.
<br />
<br />
In other words (ie a picture), the vertices for the background tiles are calculated in this order:
<br />
<br />
<br />
<br />
Hope that makes sense!
<br />
<br />
Like I said, I’ve not bothered to worry about duplicate vertices etc at the moment. It’s not critical right now.
<br />
<br />
Then all we do is to load the texture and setup the texture co-ordinate array like so, and then complete the initialiser:
<br />
<br />
<br />
<pre> glGenTextures(1, &backgroundTexture);
</pre>
<pre> [self loadTexture:@"bluebackground.png" intoLocation:backgroundTexture];
</pre>
<br />
<br />
<pre> // Setup the texture co-ordinates (STs)
</pre>
<pre> backSTs[0] = 0;
</pre>
<pre> backSTs[1] = 1;
</pre>
<pre> backSTs[2] = 0;
</pre>
<pre> backSTs[3] = 0;
</pre>
<pre> backSTs[4] = 1;
</pre>
<pre> backSTs[5] = 0;
</pre>
<pre> backSTs[6] = 1;
</pre>
<pre> backSTs[7] = 1;
</pre>
<pre> }
</pre>
<pre> return self;
</pre>
}
<br />
<br />
<br />
Drawing the Background
<br />
This can be done without too much thought. Just a loop through each tile and drawing each tile individually.
<br />
<br />
- (void)drawBackground {
<br />
<br />
<br />
<pre> // Draw that bad boy
</pre>
<pre> glEnableClientState(GL_VERTEX_ARRAY);
</pre>
<pre> glEnableClientState(GL_TEXTURE_COORD_ARRAY);
</pre>
<pre> glEnable(GL_TEXTURE_2D);
</pre>
<br />
<br />
<pre> glTexCoordPointer(2, GL_BYTE, 0, backSTs);
</pre>
<pre> GLfloat *v = backVerts;
</pre>
<pre> glBindTexture(GL_TEXTURE_2D, backgroundTexture);
</pre>
<pre> for (int i = 0; i <= BACKGROUND_TILE_COUNT; i++) {
</pre>
<pre> glVertexPointer(2, GL_FLOAT, 0, v);
</pre>
<pre> glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
</pre>
<pre> v = &backVerts[i*8];
</pre>
<pre> }
</pre>
<br />
<br />
<pre> glDisable(GL_TEXTURE_2D);
</pre>
<pre> glDisableClientState(GL_VERTEX_ARRAY);
</pre>
<pre> glDisableClientState(GL_TEXTURE_COORD_ARRAY);
</pre>
}
<br />
<br />
There really is nothing new in there. You should be up to date with
these calls by now and, if not, go back over my original tutorials.
<br />
<br />
Before we leave here right now, we just need to copy and paste in the
loadTexture method. Here it is to save you guys looking it up:
<br />
<br />
- (void)loadTexture:(NSString *)fileName intoLocation:(GLuint)location {
<br />
<pre> CGImageRef textureImage = [UIImage imageNamed:fileName].CGImage;
</pre>
<br />
<br />
<pre> if (textureImage == nil) {
</pre>
<pre> NSLog(@"Could not open image: %@", fileName);
</pre>
<pre> return;
</pre>
<pre> }
</pre>
<br />
<br />
<pre> NSInteger texWidth = CGImageGetWidth(textureImage);
</pre>
<pre> NSInteger texHeight = CGImageGetHeight(textureImage);
</pre>
<br />
<br />
<pre> GLubyte *textureData = (GLubyte *)malloc(texWidth * texHeight * 4);
</pre>
<br />
<br />
<pre> CGContextRef textureContext = CGBitmapContextCreate(textureData,
</pre>
<pre> texWidth, texHeight,
</pre>
<pre> 8, texWidth * 4,
</pre>
<pre> CGImageGetColorSpace(textureImage),
</pre>
<pre> kCGImageAlphaPremultipliedLast);
</pre>
<br />
<br />
<pre> CGContextTranslateCTM(textureContext, 0, texHeight);
</pre>
<pre> CGContextScaleCTM(textureContext, 1.0, -1.0);
</pre>
<br />
<br />
<pre> CGContextDrawImage(textureContext, CGRectMake(0.0, 0.0, (float)texWidth, </pre>
<pre>(float)texHeight), textureImage);</pre>
<pre></pre>
<pre> CGContextRelease(textureContext);
</pre>
<br />
<br />
<pre> glBindTexture(GL_TEXTURE_2D, location);
</pre>
<pre> glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, </pre>
<pre>GL_RGBA, GL_UNSIGNED_BYTE, textureData);
</pre>
<br />
<br />
<pre> free(textureData);
</pre>
<br />
<br />
<pre> glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
</pre>
<pre> glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
</pre>
}
<br />
<br />
I’ll do something cooler with texture management later on.
<br />
<br />
Our First Look at the Background
<br />
Righto, now that we’ve done all that, let’s at least draw the
background to make sure that everything is fine at the moment. It’s
going to be easier to debug this without the scrolling code added in.
<br />
<br />
Switch over to EAGLView.h and add an import for Level.h at the top like so:
<br />
<br />
<br />
<ol>
<li>import "Level.h"
</li>
</ol>
<br />
Then add an instance variable for the level in the class’ interface:
<br />
<br />
Level *currentLevel;
<br />
<br />
Now into EAGLView.m.
<br />
In the initWithCoder method, add this at the end somewhere:
<br />
<br />
<br />
currentLevel = [[Level alloc] initWithFile:@"no file"];
<br />
<br />
That just creates the level. The filename is not important because we
haven’t created it yet. That will come later (I’ve done something
already for what I was developing and it’s actually been kinda fun to
work out a file format for a level).
<br />
<br />
Finally, in the drawView method, we can add the call to draw the background. So drawView will look like this:
<br />
<br />
- (void)drawView {
<br />
<br />
<br />
<ol>
<li>ifdef DEBUG
</li>
</ol>
<pre> double startTime = CFAbsoluteTimeGetCurrent();
</pre>
<ol>
<li>endif
</li>
</ol>
<br />
<br />
<pre> [EAGLContext setCurrentContext:context];
</pre>
<pre> glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
</pre>
<pre> glViewport(0, 0, backingWidth, backingHeight);
</pre>
<br />
<br />
<pre> [currentLevel drawBackground]; // Add this line here
</pre>
<br />
<br />
<pre> glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
</pre>
<pre> [context presentRenderbuffer:GL_RENDERBUFFER_OES];
</pre>
<br />
<br />
<pre> frameCounter++;
</pre>
<ol>
<li>ifdef DEBUG
</li>
</ol>
<pre> double endTime = CFAbsoluteTimeGetCurrent();
</pre>
<pre> totalRenderingTime += (endTime - startTime);
</pre>
<ol>
<li>endif
</li>
</ol>
<br />
<br />
}
<br />
<br />
Two more final things to do: add the texture to the project. Add the
CoreGraphics.framework to the project for loading the texture. See
tutorial #5 for both of these if you haven’t already.
<br />
<br />
Hit Build & Go and (after ignoring the warning about the missing
scroll method in Level.m), if I’ve done my job, you’ll get this:
<br />
<br />
<br />
<br />
It may not look exactly like that depending on the background texture I
include in the project. I’m already not liking this pattern and I think
it’s a little bright so I’m pretty sure that I’m going to change it.
<br />
<br />
Note the black space at the bottom. That’s where our console will go.
<br />
<br />
Scroll that Bad Boy
<br />
Now to implement the scroll functionality. This is where those
tiles over the right hand side of the display come into play. To
implement scrolling, I decided the quickest and easiest way to do so
would be to go through my vertex array for my background tiles and just
change the X co-ordinate for each tile, decreasing the X co-ordinate by
the BACKGROUND_SCROLL_SPEED value. Remember, the X co-ordinate 0 is at
the left hand side of the display.
<br />
<br />
After a few frames of scrolling, we would end up with this:
<br />
<br />
<br />
<br />
Notice that I’ve changed the colour of the first column of tiles. You need to keep an eye on these ones.
<br />
<br />
Now, when the X value of the leading vertices of those red tiles (ie the
left most vertices, they’re the leading edge) reach a value less than
-BACKGROUND_TILE_SIZE (negative value, that is -64 in this case), we now
know the tile is no longer visible. The grid will look like this:
<br />
<br />
<br />
So now what? Here’s what I decided to do. At this point in the
scrolling code when we detect that the leading edge of that tile is less
than the negative value of the tile size, we literally change the X
value of that tile to be at the rear of the grid.
<br />
<br />
So our code will do this to the red tiles:
<br />
<br />
<br />
<br />
That’s what’s happening when I refer to wrapping.
<br />
<br />
So, you can see I didn’t need 9 columns for the background but, like I
said, I am planning to use the current column as a trigger in some cases
and this allows me to have different triggers for the foreground to the
background if I want. The amount of memory and time to deal with those
extra two columns isn’t worth losing sleep over.
<br />
<br />
That’s the basic scroll loop, so let’s have a look at the code:
<br />
<br />
- (void)scroll {
<br />
<pre> // Now Scroll the background. We only scroll the background if we have needed to </pre>
<pre>//scroll the
</pre>
<pre> // foreground.
</pre>
<pre> for (int i = 0; i < BACKGROUND_TILE_COUNT; i++) {
</pre>
<pre> if (backVerts[i*8] < -BACK_GRID_SIZE) {
</pre>
<pre> backVerts[i*8] = BACKGROUND_WRAP_X;
</pre>
<pre> backVerts[i*8+2] = BACKGROUND_WRAP_X;
</pre>
<pre> backVerts[i*8+4] = BACKGROUND_WRAP_X + BACKGROUND_TILE_SIZE;
</pre>
<pre> backVerts[i*8+6] = BACKGROUND_WRAP_X + BACKGROUND_TILE_SIZE;
</pre>
<pre> } else {
</pre>
<pre> backVerts[i*8] -= BACKGROUND_SCROLL_SPEED;
</pre>
<pre> backVerts[i*8+2] -= BACKGROUND_SCROLL_SPEED;
</pre>
<pre> backVerts[i*8+4] -= BACKGROUND_SCROLL_SPEED;
</pre>
<pre> backVerts[i*8+6] -= BACKGROUND_SCROLL_SPEED;
</pre>
<pre> }
</pre>
<pre> }
</pre>
}
<br />
<br />
For the sake of simplicity, I just went through every tile. You can
optimise this if you like but we’re likely to later on. That’s the first
thing, it’s just a for loop. For each tile, we test to see if the
leading edge is far enough off the left hand side of the display. If it
is, we change the X co-ordinates only to the wrap co-ordinate, specified
by BACKGROUND_WRAP_X for the leading edge, and BACKGROUND_WRAP_X +
BACKGROUND_TILE_SIZE for the trailing edge of the tile (obviously, they
cannot be the same co-ordinate.
<br />
<br />
If you remember, we defined BACKGROUND_WRAP_X as the following:
<br />
<br />
<br />
<ol>
<li>define BACKGROUND_WRAP_X BACKGROUND_TILE_SIZE*(BACKGROUND_COLUMNS-1)-BACKGROUND_SCROLL_SPEED
</li>
</ol>
<br />
Our tile size is 64 and we have 9 columns. So to get the new leading
edge X co-ordinate, we just multiply the tile size by the column count
less one. ie 64 x (9-1) = 512. Now, since all the other tiles are
scrolling anyway, we still need to scroll the “wrapped” tile otherwise
we would get a gap between the tile that just wrapped and the tile
immediately before it.
<br />
<br />
The other half of the loop is executed when the leading edge isn’t
detected as being far enough off screen. The tile’s X co-ordinates are
just decrements by the scroll speed value.
<br />
<br />
Add that to the Level.m file and then switch back to EAGLView.m where you can add the following to the drawView method:
<br />
<br />
<br />
<pre> [currentLevel scroll];
</pre>
<pre> [currentLevel drawBackground];
</pre>
<br />
Do you scroll first, then draw second or draw first then scroll for the
next frame? It doesn’t matter. I’ve decided to scroll first, then draw.
Like I mentioned, new tiles create new triggers so I get the scroll out
of the way first.
<br />
<br />
Hit Build & Go and you should get the scrolling background:
<br />
<br />
<br />
<br />
Now of course this looks identical in static pictures!!
<br />
<br />
That is scrolling okay and looking smooth but if you put a texture up
there with say a border, you’d see some of the jitters I was talking
about. That’s all fixable but we’ll do it later after we’ve decided if
we need to optimise this code and how we can do it.
<br />
<br />
Some Cleanup Code. Yes, Really!
<br />
You guys who have followed these tutorials know that I haven’t
really written cleanup code. Well, since we are making a real game in
this, I can’t get away with that here. So we need to add a dealloc
method to the Level class to clean up the texture.
<br />
<br />
- (void)dealloc {
<br />
<pre> glDeleteTextures(1, &backgroundTexture);
</pre>
<pre> [super dealloc];
</pre>
}
<br />
<br />
I don’t think I’ve ever included a glDeleteTextures before!! :)
Remember you call glGenTextures() to create a texture “handle” within
OpenGL. Then, when we load the texture, OpenGL takes control of it so to
speak, so, when we’re done with that texture, we need to tell OpenGL to
release that memory that it’s controlling. We don’t have access to the
GPU’s memory so we can’t release it ourselves. That’s where
glDeleteTextures() tells OpenGL to free that memory up.
<br />
<br />
You don’t need to check that the texture is actually allocated. Anything
with a value of zero is ignored as the texture ID zero is never
allocated for us, they all start at 1 (depending on implementation of
course). Objective-C initialises all instance variables to nil or zero
(hopefully) so we don’t need to check that something is allocated there.
Just call it.
<br />
<br />
For now, we’ll clean up the level in the EAGLView dealloc method with a call like so:
<br />
<br />
[currentLevel release];
<br />
<br />
In reality, we’ll move that elsewhere later on when we add code for multiple levels.
<br />
<br />
Some Parting Comments Before Concluding
<br />
Look, I know I’ve said several times not to worry about
optimising the code just yet. The reason is because this tutorial is
aimed at those people who have emailed me and asked “how to make a tile
engine”. So this has so far been more of a concept than what it may turn
out to be. For these people, they need to get the concepts first, the
speed comes later.
<br />
<br />
If you already knew this stuff, then great. At some point I’m sure
something will come out of this that you didn’t know. If you’re in my
target audience, then chances are if I wrote 100% optimised code you may
not have learnt a thing except how to cut and paste.
<br />
<br />
For everyone though, I want to hit that speed barrier. I want it to fall
below 60fps which is what I’ve promised to deliver. Then I get to talk
about how I go through and optimise code. What steps do I take. What my
thought processes are. How it gets done. How to measure the results.
<br />
<br />
When you learn to optimise yourself by learning from someone else, you
can then take that knowledge and apply it elsewhere. If you get served
up 100% pre-optimised code but don’t really know how to optimise, when
are you going to learn? How will you learn?
<br />
<br />
We want to hit that barrier at some point. That’s how we learn.
<br />
<br />
Like I’ve said before, I present this information in a way that I think I
would like to learn and, in many ways, is how I learned.
<br />
<br />
Conclusion for this Tutorial
<br />
It’s actually at this point I’m going to switch from what I did
in that night a couple of weeks ago. Originally, I got parallax
scrolling working, then went in the hunt for some software for making a
tile map. I actually created the tiles and then used a hand coded array
which I’m not going to make you guys do.
<br />
<br />
In the next instalment, I’ll introduce some software I’ve found (free
software of course!) and go through creating a tile map for us to render
the foreground.
<br />
<br />
In the interim, here’s the completed project for now. This is where we’ll start from next time.
<br />
<br />
File download: Draconia01.zip
<br />
<br />
Until then, take care.
<br />
Hooroo!
<br />
Simon Maurice
<br />
<br />
Copyright 2009 Simon Maurice. All Rights Reserved.
<br />
The code provided in these pages is free software under the terms
of the GNU General Public Licence version 3. Please refer to this link
for the full licence: <a class="external free" href="http://www.gnu.org/licenses/gpl-3.0.txt" rel="nofollow" title="http://www.gnu.org/licenses/gpl-3.0.txt">http://www.gnu.org/licenses/gpl-3.0.txt</a>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-3765795590291919148.post-54940634820193056232012-01-10T04:07:00.000-08:002013-11-11T04:08:11.537-08:00Latest job openings Kerala<div dir="ltr" style="text-align: left;" trbidi="on">
<br />
<a href="http://www.keralajobs.co.in/">Jobs in Kerala</a><br />
<a href="http://www.calicutjobs.co.in/">Jobs in Calicut</a><br />
<a href="http://thrissurjobs.in/">Jobs in Thrissur</a><br />
<a href="http://www.cochinjobs.in/">Jobs in Cochin</a><br />
<a href="http://trivandrumjobs.co.in/">Jobs in Trivandrum</a><br />
<br />
<br />
<a href="http://www.keralajobs.co.in/search/all-fresher-trainee-jobs-in-kerala-india/0/0years/0/0years/0-experienced">Fresher Jobs Kerala</a><br />
<a href="http://www.calicutjobs.co.in/search/all-fresher-trainee-jobs-in-calicut-kozhikode/all/0/0years/0/0years/0-experienced">Fresher Jobs Calicut</a><br />
<a href="http://thrissurjobs.in/search/all-fresher-trainee-jobs-in-thrissur/all/0/0years/0/0years/0-experienced">Fresher Jobs Thrissur</a><br />
<a href="http://www.cochinjobs.in/search/all-fresher-trainee-jobs-in-cochin-kochi-ernakulam/all/0/0years/0/0years/0-experienced">Fresher Jobs Cochin</a><br />
<a href="http://trivandrumjobs.co.in/search/all-fresher-trainee-jobs-in-trivandrum-thiruvananthapuram/all/0/0years/0/0years/0-experienced">Fresher Jobs Trivandrum</a><br />
<br />
<br />
<a href="http://www.keralajobs.co.in/search/MBA-jobs-in-kerala-india/0/a/b/c/d">MBA Jobs Kerala</a><br />
<a href="http://www.calicutjobs.co.in/search/MBA-jobs-in-calicut-kozhikode/all/0/a/b/c/d">MBA Jobs Calicut</a><br />
<a href="http://thrissurjobs.in/search/MBA-jobs-in-thrissur/all/0/a/b/c/d">MBA Jobs Thrissu</a>r<br />
<a href="http://www.cochinjobs.in/search/MBA-jobs-in-cochin-kochi-ernakulam/all/0/a/b/c/d">MBA Jobs Cochin</a><br />
<a href="http://trivandrumjobs.co.in/search/MBA-jobs-in-trivandrum-thiruvananthapuram/all/0/a/b/c/d">MBA Jobs Trivandrum</a><br />
<br />
<br />
<a href="http://www.keralajobs.co.in/search/MCA-jobs-in-kerala-india/0/a/b/c/d">MCA Jobs Kerala</a><br />
<a href="http://www.calicutjobs.co.in/search/MCA-jobs-in-calicut-kozhikode/all/0/a/b/c/d">MCA Jobs Calicut</a><br />
<a href="http://thrissurjobs.in/search/MCA-jobs-in-thrissur/all/0/a/b/c/d">MCA Jobs Thrissur</a><br />
<a href="http://www.cochinjobs.in/search/MCA-jobs-in-cochin-kochi-ernakulam/all/0/a/b/c/d">MCA Jobs Cochin</a><br />
<a href="http://trivandrumjobs.co.in/search/MCA-jobs-in-trivandrum-thiruvananthapuram/all/0/a/b/c/d">MCA Jobs Trivandrum</a><br />
<br />
<br />
<a href="http://www.keralajobs.co.in/search/BTech-jobs-in-kerala-india/0/a/b/c/d">BTech Jobs Kerala</a><br />
<br />
<a href="http://www.calicutjobs.co.in/search/BTech-jobs-in-calicut-kozhikode/all/0/a/b/c/d">BTech Jobs Calicut</a><br />
<a href="http://thrissurjobs.in/search/BTech-jobs-in-thrissur/all/0/a/b/c/d">BTech Jobs Thrissur</a><br />
<a href="http://www.cochinjobs.in/search/BTech-jobs-in-cochin-kochi-ernakulam/all/0/a/b/c/d">BTech Jobs Cochin</a><br />
<a href="http://trivandrumjobs.co.in/search/BTech-jobs-in-trivandrum-thiruvananthapuram/all/0/a/b/c/d">BTech Jobs Trivandrum</a><br />
<br />
<a href="http://philipjobs.com/">Post jobs free kerala</a><br />
<a href="http://philipjobs.com/">Free resume kerala</a><br />
</div>
Unknownnoreply@blogger.com0