OpenGL ES From the Ground Up, Part 5: Living in a Material World

WEDNESDAY, MAY 13, 2009

OpenGL ES From the Ground Up, Part 5: Living in a Material World

Okay, in the last installment, we talked about lights, how to set them
up and what attributes they have. We also talked about the three components of light,diffuseambient, and specular. If you're not completely clear on those, you might want to review, as those components are
used heavily in defining materials.

Source image from Wikimedia Commons, modified as allowed by license.

As a starting point for this installment, we're going to use the Procedural Sphere project from this
posting
. We're switching to a sphere rather than continuing with the icosahedron that we've been using up to now because a sphere is the ideal shape for showing how the different components of lights and materials interact with each other.

What is Color

So, let's talk about what color is. This is probably a review for anybody who passed grade school art, but let's do it anyway. Why do things have color in the real world? What causes it?

Light that we can see is called the visible spectrum of light. We perceive different colors based on the wavelength
of that light. At one end of the visible spectrum, we have the higher-frequency, lower wavelength purples and blues, and at the other end, we have the lower-frequency, higher wavelength oranges and reds:

Electromagnetic waves that fall outside of this range - both higher and lower - are not "visible light", though it is something ofan artificial distinction since the only difference is the wavelength, frequency, and the ability of the human eye to perceive
it. However, that ability to perceive electromagnetic waves is everything, so we as far as OpenGL is concerned, the visible spectrum is all we care about.

Now, "white light" contains equal amounts of all the wavelengths. In other words, white light is white because it contains all (or at least most) of the frequencies of visible light. If you've ever experimented with a prism, you've seen this effect in action:

The prism refracts the white light so that light with different wavelengths is separated. This is also the process by which rainbows are created and ZOMG Ponies! No, not really on the ponies, but it's true about the rainbows.

Now, if you look at an object, and it appears blue, what is happening is that the object is absorbing most of the lower end of the visible range. The object is absorbing red, orange, yellow and green light. Depending on the shade of blue, it may also be absorbing
violet and some blue.

But most of the blue wavelengths are being reflected back towards your eye. Because some of the visible light has been absorbed, the light being reflect back to your eye is no longer white because it no longer contains all the wavelengths of the visible spectrum.

Simple enough, right? Let's see how this applies to OpenGL.

OpenGL Materials

To define a color for a material in OpenGL ES, we define how that material reflects light, just like how things work in the real world. If a material is set up to reflect red light, then it will appear red when shown under regular, white light. 

In OpenGL (at least when when using smooth shading and lights) materials don't have just a single color. We have the ability to specify exactly how the material reflects each of the three components of OpenGL lights (ambient, diffuse, and specular) separately.
We additionally have the ability to specify the material's emissive property, which we'll talk about a little later.

Specifying a Material

To create a material in OpenGL, we make one or more call to either glMaterialf() or glMaterialfv(). Similar to the way we specified lights in the last installment, we often
have to make multiple calls to these functions to fully define the material because each attribute or component has to be specified individually by making a separate call. Any component or attribute that is not specified defaults to zero or, in the case of
colors, to black.

The first parameter passed to either glMaterialf() or glMaterialfv() is always a GL_ENUM which specifies whether the material affects
the front, back, or both the front and back of polygons. There's actually no point in having this first argument in OpenGL ES other than for compatibility with OpenGL ES, because there's only one valid option: GL_FRONT_AND_BACK,
which simply indicates that materials are used for any polygon that is drawn. If you remember frompart
1
, triangles have a front and a back that is determined by their winding (order the vertices are drawn). By default, only the front of a triangle is drawn in OpenGL, but it's possible to make OpenGL draw the backside, or even to draw only the backs, and
regular OpenGL would let you specify different materials for front and back by passing either GL_FRONT, GL_BACK, or GL_FRONT_AND_BACK.
But, OpenGL ES doesn't support any option other thanGL_FRONT_AND_BACK, so just pass that always, 'kay?

The second parameter to glMaterialf() or glMaterialfv() is a GL_ENUM that identifies which component or attribute of the material
you are setting. These are the same values we passed into glLightfv(), like GL_AMBIENT, along with some new ones you'll see in a moment.

The final value is either a GL_FLOAT, or a pointer to an array of GL_FLOATs that contains the actual value for the attribute or component.

The most imoprtant components of a material that need to be set are the ambient and diffuse components, because they define how a material reflects the bulk of the light being shined on it. The code project we're using today has defined white light, much like
the light produced by the sun, or a light bulb in that it has all wavelengths or colors of light represented equally. If our light were not white, the appearance of our sphere would be different. Blue light reflecting off a red material, for example, would
produce a purple shade. For simplicity, we're going to work only with white light and you can feel free to experiment with changing the colors of the lights on your own to see how lights and materials interact. For the most part, they interact in OpenGL ES
the way they would in real life.

Here is what the project looks like when run, before adding any materials:

As you can see, we've got some ambient light and considerably more diffuse light.

Ambient and Diffuse

When talking about materials in OpenGL, we need to talk about the ambient and diffuse components at the same time because these two work together to define the perceived color of the object. Remember, diffuse light is the top part of the sphere in very first
picture in this posting (the brighter yellow), and ambient is the darker yellow part on the underside. How the material reflects these two components will determine what color you perceive the object to be. Now, the picture above could have been achieved in
more than one way. Likely, the yellow sphere reflects the ambient and diffuse properties in the same proportion, but the scene has less ambient light defined.

Probably 90% of the time or more, you will specify the ambient and diffuse parameters of a material exactly the same. By doing this, it becomes the amount of diffuse and ambient that determines the shading and appearance of the object. There is, in fact, a
way to specify a material's diffuse and ambient components at the same time with a single call to glMaterialfv(). Here is how we might declare our material to be a shade of blue:

    GLfloat ambientAndDiffuse[] = {0.0, 0.1, 0.9, 1.0};
    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, ambientAndDiffuse);

Just like with glColor4f(), setting a material dictates the way everything is drawn until another material is specified. If the code above is placed before our drawing code, and we now run the project, we're going
to see that our sphere has become a single shade of blue. The underside is still darker because our ambient light is not as strong as our diffuse light:

There may be times when you want more control and you want to specify how the material reflects the diffuse and ambient light separately. For example, we could do the following to create a material that reflects the blue from the ambient light but the red from
the diffuse light:

    GLfloat ambient[] = {0.0, 0.1, 0.9, 1.0};
    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, ambient);
    GLfloat diffuse[] = {0.9, 0.0, 0.1, 1.0};
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse);

If we run this, we get a very different appearance:

In this case, it looks like we're shining a colored light on the sphere, even though we're not. The reason that it looks that way is that we're reflecting the directional light differently than the ambient light. 

In most cases, if you want to give the appearance of colored lights, then create colored lights and specify your material colors usingGL_AMBIENT_AND_DIFFUSE. But, there are times when you might want to separate them
out to create special effects or to fake an isolated, colored spot light without incurring the overhead of an additional light. Remember: Every light you add increases the calculations that have to be performed every second, so it's not a bad idea to cheat
sometimes.

Specular and Shininess

You can also specify how your light reflects the specular component of the scene's lights separately from the diffuse and ambient components. This gives you the ability to control how bright the specular "hot spot" is. A separate parameter, called GL_SHININESS works
together with the material's specular component to define how big the specular hot spot is. If you do specify a GL_SPECULAR value for your material, you should also define its shininess. The higher the shininess,
the smaller the specular reflection, so the default value of 0.0 tends to completely overwhelm the diffuse component and generally looks bad.

Let's return to our blue sphere, and add a specular hot spot to it.

    GLfloat ambientAndDiffuse[] = {0.0, 0.1, 0.9, 1.0};
    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, ambientAndDiffuse);
    GLfloat specular[] = {0.3, 0.3, 0.3, 1.0};
    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular);
    glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 25.0);

We've used a dim white for our specular value. This may seem low, but the way specular is calculated, a little seems to go a long way. The quality of the specular component of the lights is multiplied by the material's specular component, and the resulting
light is concentrated in the area dictated by the material's shininess. Here is what the sphere looks like with the code above:

We now have a small circular area on the sphere that's reflecting more light. We can make this spot brighter by increasing either the specular component of our light or lights, or by increasing the shininesst of our material. We can also change the size of
the highlight by tweaking the shininess. The shinier the material, the more concentrated the specular highlight. If we change the shininess from 25.0 to 50.0, for example,
we get a much tighter hot spot, which gives the appearance of a more highly polished surface.

Here's a little warning, though. Part of the reason why I switched to spheres for this posting, and the reason why I used a relatively high vertex count for this sphere, is because specular highlights on low-poly objects don't look very good. Watch what happens
if I reduce the number of vertices in our sphere to a more game-friendly number:

The specular highlights tend to make the triangle edges stand out, and often specular highlights just don't look good for low-poly objects like those you'd use in a game. In regular OpenGL, there is something called a shader that can be used
to achieve good results with low-poly models, but the version of OpenGL ES on the iPhone does not have shaders. The only way you can generally make low-poly objects look good in games is by forgetting about the specular component altogether and using texture
mapping, something we'll look at starting in the next installment.

Emission

There's one last important attribute of materials that we need to look at, and it's called the emission (or sometimes the emissive) component. By setting the emission component, we make the material look like it is emitting light of the color we specify. Now,
it's not really emitting light. Nearby objects, for example, will not be affected by another object's emissive light. If you want to make an object like a light bulb that actually shines on other objects, you need to combine the emission component with an
actual light at the same location as the object because (and this is going to sound redundantly redundant) but in OpenGL ES, only lights actually emit light. But, the emission component can be used to give objects a nice glow.

We could, for example, add a nice green glowiness to our blue sphere, by adding this code:

    GLfloat emission[] = {0.0, 0.4, 0.0, 1.0};
    glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, emission);

This would give us a result that looks like this:

The emission component affects the entire material, so the value specified for GL_EMISSION is multiplied by any type of light that falls on a specific spot on the object. Notice that even the specular highlight in
the picture above has taken on a bit of a blue-green cast rather than being pure white. It's subtle in the brighter spots, and most noticeable in the bottom area where only ambient light is being reflected, but it affects everything.

That's All She Wrote

Well, I though long and hard about some kind of Madonna-reference or at least an 80's music reference for the conclusion here to tie it together with the posting's title, but I was completely unable to come up with anything even remotely witty, so I'm going
to have to just leave it here. 

In the next installment, we will look at texture mapping, which can be used both for two-dimensional sprites and also for giving three-dimensional objects more complex surfaces than materials allow for.

POSTED BY JEFF LAMARCHE AT 1:02
PM

LABELS: OPENGL ESTUTORIALS

时间: 2024-10-31 21:44:40

OpenGL ES From the Ground Up, Part 5: Living in a Material World的相关文章

OpenGL ES From the Ground Up, Part 1: Basic Concepts

FRIDAY, APRIL 17, 2009 OpenGL ES From the Ground Up, Part 1: Basic Concepts I've done a number of postings on programming OpenGL ES for the iPhone, but most of the posts I've done have been targeted at people who already know at least a little bit ab

OpenGL ES From the Ground Up, Part 4: Let There Be Light!

FRIDAY, MAY 1, 2009 OpenGL ES From the Ground Up, Part 4: Let There Be Light! Continuing on with OpenGL ES for the iPhone, let's talk about light. So far, we haven't done anything with light. Fortunately, OpenGL still lets us see what's going on if w

OpenGL ES From the Ground Up, Part 6: Textures and Texture Mapping

MONDAY, MAY 25, 2009 OpenGL ES From the Ground Up, Part 6: Textures and Texture Mapping An alternative to creating materials in OpenGL ES to define the color of a polygon is to map a texture onto that polygon. This is a handy options that can give yo

OpenGL ES From the Ground Up, Part 2: A Look at Simple Drawing

MONDAY, APRIL 20, 2009 OpenGL ES From the Ground Up, Part 2: A Look at Simple Drawing Okay, there's still a lot of theory to talk about, but before we spend too much time getting bogged down in complex math or difficult concepts, let's get our feet w

OpenGL ES From the Ground Up, Part 1 Addendum: Alphabet Soup

MONDAY, MAY 4, 2009 OpenGL ES From the Ground Up, Part 1 Addendum: Alphabet Soup One problem with blog postings is that they are self-edited. For my postings here, I have no external technical review and no copy editing done. Mostly, it's not too muc

从零开始学习OpenGL ES之六 – 纹理及纹理映射

转自:http://bbs.ityran.com/article-9-1.html [转载]从零开始学习OpenGL ES之六 – 纹理及纹理映射 2011-9-14 23:49| 发布者: Iven| 查看: 9206| 评论: 15 摘要: 图形图像, 编程,编程, OpenGL ES, 教程,OpenGL ES 3D 在OpenGL ES中另一种为多边形定义颜色创建材质的方法是将纹理映射到多边形.这是一种很实用的方法,它可以产生很漂亮的外观并节省大量的处理器时间.比如说,你想在游戏中造一个

第二章 你好三角形:一个OpenGL ES 2.0例子

介绍基本概念的OpenGL ES 2.0,我们首先从一个简单的例子.在这一章里,我们将展示什么是需要创建一个OpenGL ES 2.0一个三角形的项目..我们要编写的程序是最基本的例子,一个OpenGL ES 2.0应用程序,绘制几何.有数量的概念,我们将介绍在本章: 1.创建一个屏幕渲染表面与EGL. 2.加载片段着色器和定点. 3.创建程序的对象,附着顶点和片段着色器,连接程序对象. 4.设置窗口. 5.清除颜色缓冲. 6.渲染一个简单的例子. 7.使内容的颜色缓冲可见在EGL窗口表面. 事

Android开发之OpenGL ES 颜色

一.基础知识: 1.平滑着色(Smooth coloring): 将多个顶点的不同颜色混合在一起,创建出漂亮的色彩混合. 2.单调着色: 给图形涂上一种固定单一的颜色. 3.三角形定义的颜色数组(平滑着色): [java] int one = 0x10000; //三角形的顶点颜色值(r,g,b,a) private IntBuffer colorBuffer = IntBuffer.wrap(new int[]{ one,0,0,one, 0,one,0,one, 0,0,one,one, }

Android开发之OpenGL ES 画多边形

一.基础知识: OpenGL ES目前只支持三角形,但任何多边形都可拆分成多个三角形,所以无所谓这个限制的存在. 1.OpenGL中的坐标点: 每一个坐标点由(X, Y, Z)组成. 定义一个三角形的顶点数组: [java] int one = 0x10000; //三角形三个顶点 private IntBuffer triggerBuffer = IntBuffer.wrap(new int[]{ 0,one,0, //上顶点 -one,-one,0, //左下点 one,-one,0,});